emacs-diffs
[Top][All Lists]
Advanced

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

feature/named-lambdas 2e110c6c204 2/3: Merge branch 'master' into featur


From: Alan Mackenzie
Subject: feature/named-lambdas 2e110c6c204 2/3: Merge branch 'master' into feature/named-lambdas
Date: Sat, 7 Oct 2023 14:03:50 -0400 (EDT)

branch: feature/named-lambdas
commit 2e110c6c204a9c851f54e6a0c722057760d9073d
Merge: db8090ebf1b 37130fd500f
Author: Alan Mackenzie <acm@muc.de>
Commit: Alan Mackenzie <acm@muc.de>

    Merge branch 'master' into feature/named-lambdas
---
 .clangd                                            |    2 +-
 .gitignore                                         |    1 +
 .mailmap                                           |    6 +-
 CONTRIBUTE                                         |   11 +-
 ChangeLog.android                                  |    9 +-
 INSTALL                                            |    4 +-
 Makefile.in                                        |    6 +
 admin/MAINTAINERS                                  |   68 +
 admin/authors.el                                   |   10 +
 admin/make-tarball.txt                             |   20 +-
 admin/notes/bug-triage                             |   71 +-
 java/README => admin/notes/java                    |   97 +-
 admin/notes/multi-tty                              |    2 -
 admin/notes/tree-sitter/build-module/batch.sh      |    2 +
 admin/notes/tree-sitter/build-module/build.sh      |    3 +
 admin/notes/tree-sitter/performance                |   25 +
 admin/notes/unicode                                |   18 +-
 admin/unidata/BidiBrackets.txt                     |   10 +-
 admin/unidata/BidiMirroring.txt                    |    8 +-
 admin/unidata/Blocks.txt                           |    7 +-
 admin/unidata/IdnaMappingTable.txt                 |   23 +-
 admin/unidata/NormalizationTest.txt                |    6 +-
 admin/unidata/PropertyValueAliases.txt             |   38 +-
 admin/unidata/ScriptExtensions.txt                 |   47 +-
 admin/unidata/Scripts.txt                          |   14 +-
 admin/unidata/SpecialCasing.txt                    |    6 +-
 admin/unidata/UnicodeData.txt                      |    7 +
 admin/unidata/confusables.txt                      |   10 +-
 admin/unidata/copyright.html                       |   20 +-
 admin/unidata/emoji-data.txt                       |    6 +-
 admin/unidata/emoji-sequences.txt                  |   17 +-
 admin/unidata/emoji-test.txt                       |  337 ++-
 admin/unidata/emoji-variation-sequences.txt        | 1458 +++++-----
 admin/unidata/emoji-zwj-sequences.txt              |  144 +-
 admin/unidata/emoji-zwj.awk                        |    8 +-
 build-aux/gitlog-to-changelog                      |    2 +-
 build-aux/update-copyright                         |    2 +-
 configure.ac                                       |   24 +-
 doc/emacs/abbrevs.texi                             |   15 +-
 doc/emacs/ack.texi                                 |   16 +-
 doc/emacs/android.texi                             |  106 +-
 doc/emacs/building.texi                            |    7 +-
 doc/emacs/custom.texi                              |    2 +-
 doc/emacs/frames.texi                              |    7 +-
 doc/emacs/haiku.texi                               |  112 +-
 doc/emacs/input.texi                               |    7 +-
 doc/emacs/killing.texi                             |    2 +-
 doc/emacs/kmacro.texi                              |   19 +-
 doc/emacs/maintaining.texi                         |   13 +-
 doc/emacs/msdos.texi                               |    4 +-
 doc/emacs/text.texi                                |    9 +-
 doc/lispref/commands.texi                          |   27 +-
 doc/lispref/compile.texi                           |   16 +-
 doc/lispref/control.texi                           |   67 +-
 doc/lispref/display.texi                           |   22 +-
 doc/lispref/edebug.texi                            |    2 +
 doc/lispref/frames.texi                            |   48 +-
 doc/lispref/functions.texi                         |   13 +-
 doc/lispref/internals.texi                         |    2 +-
 doc/lispref/keymaps.texi                           |    2 +-
 doc/lispref/minibuf.texi                           |   25 +-
 doc/lispref/nonascii.texi                          |    4 +-
 doc/lispref/objects.texi                           |   15 +-
 doc/lispref/os.texi                                |   18 +-
 doc/lispref/parsing.texi                           |   64 +-
 doc/lispref/positions.texi                         |   15 +-
 doc/lispref/processes.texi                         |    6 +-
 doc/lispref/searching.texi                         |   11 +
 doc/lispref/sequences.texi                         |    3 +-
 doc/lispref/strings.texi                           |    2 +-
 doc/lispref/symbols.texi                           |   40 +-
 doc/lispref/text.texi                              |   11 +-
 doc/lispref/variables.texi                         |   18 +-
 doc/lispref/windows.texi                           |   22 +-
 doc/misc/autotype.texi                             |    8 +-
 doc/misc/efaq-w32.texi                             |   12 +-
 doc/misc/efaq.texi                                 |   53 +-
 doc/misc/eglot.texi                                |  159 +-
 doc/misc/erc.texi                                  |   11 +-
 doc/misc/ert.texi                                  |   23 +-
 doc/misc/eshell.texi                               |   41 +-
 doc/misc/flymake.texi                              |   15 +-
 doc/misc/gnus.texi                                 |    4 +-
 doc/misc/idlwave.texi                              |    2 +-
 doc/misc/mh-e.texi                                 |    3 +-
 doc/misc/modus-themes.org                          |  137 +-
 doc/misc/octave-mode.texi                          |    2 +-
 doc/misc/org.org                                   |    2 +-
 doc/misc/ses.texi                                  |    4 +-
 doc/misc/tramp.texi                                |   91 +-
 doc/misc/url.texi                                  |   34 -
 doc/misc/widget.texi                               | 2779 +++++++++++++++-----
 etc/DEBUG                                          |   93 +-
 etc/EGLOT-NEWS                                     |   17 +
 etc/ERC-NEWS                                       |   41 +-
 etc/NEWS                                           |  216 +-
 etc/NEWS.20                                        |    4 +-
 etc/NEWS.21                                        |   20 +-
 etc/NEWS.22                                        |    2 +-
 etc/NEWS.23                                        |    8 +-
 etc/NEWS.24                                        |   12 +-
 etc/NEWS.26                                        |    2 +-
 etc/NEWS.29                                        |   13 +
 etc/PROBLEMS                                       |   50 +-
 etc/TODO                                           |   55 +-
 etc/emacsclient.desktop                            |    2 +-
 etc/images/symbols/dot_large_16.pbm                |  Bin 0 -> 41 bytes
 etc/images/symbols/dot_large_16.svg                |    3 +
 etc/images/symbols/dot_medium_16.pbm               |  Bin 0 -> 41 bytes
 etc/images/symbols/dot_medium_16.svg               |    3 +
 etc/images/symbols/dot_small_16.pbm                |  Bin 0 -> 41 bytes
 etc/images/symbols/dot_small_16.svg                |    3 +
 etc/refcards/orgcard.tex                           |    2 +-
 etc/themes/modus-operandi-deuteranopia-theme.el    |   58 +-
 etc/themes/modus-operandi-theme.el                 |   56 +-
 etc/themes/modus-operandi-tinted-theme.el          |   56 +-
 etc/themes/modus-operandi-tritanopia-theme.el      |   60 +-
 etc/themes/modus-themes.el                         |  235 +-
 etc/themes/modus-vivendi-deuteranopia-theme.el     |   56 +-
 etc/themes/modus-vivendi-theme.el                  |   54 +-
 etc/themes/modus-vivendi-tinted-theme.el           |   54 +-
 etc/themes/modus-vivendi-tritanopia-theme.el       |   57 +-
 exec/exec.c                                        |    2 +-
 exec/trace.c                                       |   22 +-
 java/AndroidManifest.xml.in                        |   93 +-
 java/README                                        | 1027 +-------
 java/org/gnu/emacs/EmacsContextMenu.java           |    7 +
 java/org/gnu/emacs/EmacsDesktopNotification.java   |   37 +-
 java/org/gnu/emacs/EmacsDialog.java                |    8 +
 java/org/gnu/emacs/EmacsDrawLine.java              |    4 +-
 java/org/gnu/emacs/EmacsDrawRectangle.java         |    3 +-
 java/org/gnu/emacs/EmacsDrawable.java              |    1 +
 java/org/gnu/emacs/EmacsFontDriver.java            |   11 +-
 java/org/gnu/emacs/EmacsNative.java                |    4 +
 java/org/gnu/emacs/EmacsOpenActivity.java          |  122 +-
 java/org/gnu/emacs/EmacsPixmap.java                |   40 +-
 java/org/gnu/emacs/EmacsSdk11Clipboard.java        |    2 +-
 java/org/gnu/emacs/EmacsSdk23FontDriver.java       |   10 +-
 java/org/gnu/emacs/EmacsSdk7FontDriver.java        |  210 +-
 java/org/gnu/emacs/EmacsService.java               |   28 +-
 java/org/gnu/emacs/EmacsView.java                  |   37 +
 java/org/gnu/emacs/EmacsWindow.java                |   14 +-
 leim/Makefile.in                                   |    9 +-
 lib-src/emacsclient.c                              |   17 +-
 lisp/ChangeLog.12                                  |    2 +-
 lisp/align.el                                      |   52 +-
 lisp/{use-package => }/bind-key.el                 |    3 +
 lisp/bindings.el                                   |    2 +-
 lisp/button.el                                     |    2 +-
 lisp/calendar/appt.el                              |    8 +-
 lisp/calendar/diary-lib.el                         |    4 +-
 lisp/calendar/icalendar.el                         |    4 +-
 lisp/calendar/todo-mode.el                         |   60 +-
 lisp/cedet/cedet-global.el                         |    9 +-
 lisp/comint.el                                     |    7 +-
 lisp/completion.el                                 |   20 +-
 lisp/dired-aux.el                                  |   16 +
 lisp/dired-x.el                                    |    6 +-
 lisp/dired.el                                      |   80 +-
 lisp/doc-view.el                                   |   43 +-
 lisp/edmacro.el                                    |  115 +-
 lisp/elec-pair.el                                  |    2 +-
 lisp/emacs-lisp/advice.el                          |    2 +-
 lisp/emacs-lisp/byte-run.el                        |    7 +
 lisp/emacs-lisp/bytecomp.el                        |  278 +-
 lisp/emacs-lisp/checkdoc.el                        |   47 +-
 lisp/emacs-lisp/cl-lib.el                          |   12 +
 lisp/emacs-lisp/cl-macs.el                         |   75 +-
 lisp/emacs-lisp/cl-preloaded.el                    |    4 +-
 lisp/emacs-lisp/cl-print.el                        |   51 +-
 lisp/emacs-lisp/comp.el                            |   24 +-
 lisp/emacs-lisp/disass.el                          |   17 +-
 lisp/emacs-lisp/edebug.el                          |   52 +-
 lisp/emacs-lisp/eieio.el                           |    7 +-
 lisp/emacs-lisp/elint.el                           |   20 +-
 lisp/emacs-lisp/ert.el                             |   39 +-
 lisp/emacs-lisp/gv.el                              |   13 -
 lisp/emacs-lisp/macroexp.el                        |   48 +-
 lisp/emacs-lisp/map.el                             |    3 +
 lisp/emacs-lisp/oclosure.el                        |    1 +
 lisp/emacs-lisp/package-vc.el                      |   52 +-
 lisp/emacs-lisp/package.el                         |   29 +-
 lisp/emacs-lisp/pp.el                              |    2 +-
 lisp/emacs-lisp/rmc.el                             |    3 +-
 lisp/emacs-lisp/seq.el                             |   15 +-
 lisp/emacs-lisp/shorthands.el                      |    1 +
 lisp/emacs-lisp/syntax.el                          |  288 +-
 lisp/emacs-lisp/vtable.el                          |    5 +-
 lisp/epa-ks.el                                     |    1 +
 lisp/epg.el                                        |   56 +-
 lisp/erc/erc-backend.el                            |    6 +-
 lisp/erc/erc-button.el                             |   64 +-
 lisp/erc/erc-common.el                             |   17 +-
 lisp/erc/erc-compat.el                             |   15 +
 lisp/erc/erc-fill.el                               |    5 +-
 lisp/erc/erc-goodies.el                            |  276 +-
 lisp/erc/erc-ibuffer.el                            |   16 +-
 lisp/erc/erc-stamp.el                              |  101 +-
 lisp/erc/erc.el                                    |  213 +-
 lisp/eshell/em-basic.el                            |   32 +
 lisp/eshell/em-cmpl.el                             |    2 +-
 lisp/eshell/em-dirs.el                             |   15 +-
 lisp/eshell/em-glob.el                             |    4 +-
 lisp/eshell/em-pred.el                             |   24 +-
 lisp/eshell/em-prompt.el                           |   60 +-
 lisp/eshell/em-script.el                           |   32 +-
 lisp/eshell/em-smart.el                            |    2 +-
 lisp/eshell/esh-arg.el                             |    9 +-
 lisp/eshell/esh-cmd.el                             |  604 +++--
 lisp/eshell/esh-io.el                              |    9 +-
 lisp/eshell/esh-mode.el                            |    4 +-
 lisp/eshell/esh-proc.el                            |  177 +-
 lisp/eshell/esh-util.el                            |   76 +-
 lisp/eshell/esh-var.el                             |   59 +-
 lisp/eshell/eshell.el                              |    9 +-
 lisp/external-completion.el                        |    3 +
 lisp/faces.el                                      |   20 +-
 lisp/ffap.el                                       |    3 +-
 lisp/files.el                                      |   40 +-
 lisp/filesets.el                                   |   31 +-
 lisp/finder.el                                     |    5 +
 lisp/font-lock.el                                  |    4 +-
 lisp/frame.el                                      |    2 +-
 lisp/gnus/gnus-cloud.el                            |    1 +
 lisp/gnus/gnus-logic.el                            |   10 +-
 lisp/gnus/gnus-msg.el                              |   12 +-
 lisp/gnus/gnus-util.el                             |    8 +-
 lisp/gnus/message.el                               |   25 +-
 lisp/gnus/mm-decode.el                             |    6 +-
 lisp/help-fns.el                                   |    5 +-
 lisp/help.el                                       |   76 +-
 lisp/ibuf-macs.el                                  |    7 +-
 lisp/ibuffer.el                                    |   17 +-
 lisp/ido.el                                        |    3 +-
 lisp/ielm.el                                       |    3 +-
 lisp/image-mode.el                                 |    6 +-
 lisp/image.el                                      |    2 +-
 lisp/image/image-dired-dired.el                    |    1 +
 lisp/image/image-dired-external.el                 |    1 +
 lisp/image/image-dired-tags.el                     |    1 +
 lisp/image/image-dired-util.el                     |    1 +
 lisp/international/characters.el                   |   11 +-
 lisp/international/emoji.el                        |   43 +-
 lisp/international/ja-dic-cnv.el                   |    2 +
 lisp/international/mule-cmds.el                    |   26 +-
 lisp/international/ucs-normalize.el                |   14 +-
 lisp/keymap.el                                     |   12 +-
 lisp/leim/quail/indian.el                          |    4 +-
 lisp/leim/quail/latin-pre.el                       |    6 +-
 lisp/loadup.el                                     |    2 +-
 lisp/ls-lisp.el                                    |    8 +-
 lisp/mail/ietf-drums-date.el                       |    1 +
 lisp/mail/rmailsum.el                              |    2 +-
 lisp/mail/sendmail.el                              |   10 +-
 lisp/menu-bar.el                                   |   27 +-
 lisp/minibuffer.el                                 |  204 +-
 lisp/misearch.el                                   |  151 ++
 lisp/mouse.el                                      |   11 +-
 lisp/net/browse-url.el                             |   10 +-
 lisp/net/eudc-capf.el                              |   11 +-
 lisp/net/eudcb-ecomplete.el                        |   13 +-
 lisp/net/eudcb-macos-contacts.el                   |    3 +
 lisp/net/eudcb-mailabbrev.el                       |   13 +-
 lisp/net/eww.el                                    |   93 +-
 lisp/net/gnutls.el                                 |    2 +-
 lisp/net/mailcap.el                                |    2 +-
 lisp/net/newst-backend.el                          |    9 -
 lisp/net/newst-plainview.el                        |   40 +-
 lisp/net/ntlm.el                                   |    5 +-
 lisp/net/rcirc.el                                  |    1 +
 lisp/net/shr.el                                    |    5 +-
 lisp/net/sieve-mode.el                             |   28 +-
 lisp/net/soap-client.el                            |    3 +
 lisp/net/tramp-cmds.el                             |   81 +-
 lisp/net/tramp-container.el                        |  241 +-
 lisp/net/tramp-gvfs.el                             |    2 +-
 lisp/net/tramp-message.el                          |   33 +-
 lisp/net/tramp-sh.el                               |  126 +-
 lisp/net/tramp-sshfs.el                            |    4 +-
 lisp/net/tramp-sudoedit.el                         |    2 +-
 lisp/net/tramp.el                                  |   74 +-
 lisp/obsolete/landmark.el                          |    8 +-
 lisp/obsolete/url-ns.el                            |    5 +-
 lisp/org/ChangeLog.1                               |    2 +-
 lisp/org/ob-eshell.el                              |    9 +-
 lisp/org/ob-python.el                              |    2 +-
 lisp/org/oc-basic.el                               |    2 +-
 lisp/org/ol.el                                     |    4 +-
 lisp/org/org-element.el                            |   22 +-
 lisp/org/org-table.el                              |    4 +-
 lisp/org/org-version.el                            |    4 +-
 lisp/org/org.el                                    |   55 +-
 lisp/org/ox-html.el                                |    1 +
 lisp/outline.el                                    |    3 +
 lisp/pcomplete.el                                  |   14 +-
 lisp/pixel-scroll.el                               |   18 +-
 lisp/play/zone.el                                  |    2 +-
 lisp/plstore.el                                    |   63 +-
 lisp/progmodes/c-ts-common.el                      |    1 +
 lisp/progmodes/c-ts-mode.el                        |  117 +-
 lisp/progmodes/cc-awk.el                           |   16 +-
 lisp/progmodes/cc-defs.el                          |   81 +-
 lisp/progmodes/cc-engine.el                        |   52 +-
 lisp/progmodes/cc-mode.el                          |    4 +-
 lisp/progmodes/cperl-mode.el                       |   29 +-
 lisp/progmodes/csharp-mode.el                      |   12 +-
 lisp/progmodes/dockerfile-ts-mode.el               |    5 +-
 lisp/progmodes/ebnf2ps.el                          |    2 +-
 lisp/progmodes/eglot.el                            |  564 ++--
 lisp/progmodes/elisp-mode.el                       |    6 +-
 lisp/progmodes/elixir-ts-mode.el                   |   36 +-
 lisp/progmodes/flymake-proc.el                     |    5 +-
 lisp/progmodes/flymake.el                          |  251 +-
 lisp/progmodes/gdb-mi.el                           |   32 +-
 lisp/progmodes/gud.el                              |   22 +-
 lisp/progmodes/heex-ts-mode.el                     |    5 +-
 lisp/progmodes/hideshow.el                         |    1 +
 lisp/progmodes/idlwave.el                          |    7 +-
 lisp/progmodes/java-ts-mode.el                     |   66 +-
 lisp/progmodes/js.el                               |   43 +-
 lisp/progmodes/json-ts-mode.el                     |    4 +-
 lisp/progmodes/lua-ts-mode.el                      |  455 ++++
 lisp/progmodes/perl-mode.el                        |   36 +-
 lisp/progmodes/prog-mode.el                        |   11 +-
 lisp/progmodes/project.el                          |   15 +-
 lisp/progmodes/ps-mode.el                          |    6 +-
 lisp/progmodes/python.el                           |    3 +
 lisp/progmodes/ruby-ts-mode.el                     |   77 +-
 lisp/progmodes/rust-ts-mode.el                     |   24 +
 lisp/progmodes/sh-script.el                        |    9 +-
 lisp/progmodes/typescript-ts-mode.el               |   93 +-
 lisp/progmodes/xref.el                             |   43 +-
 lisp/ps-print.el                                   |   16 +-
 lisp/replace.el                                    |    6 +-
 lisp/saveplace.el                                  |   11 +-
 lisp/server.el                                     |    9 +-
 lisp/shell.el                                      |   22 +-
 lisp/simple.el                                     |   39 +-
 lisp/sort.el                                       |   13 +-
 lisp/sqlite-mode.el                                |   18 +-
 lisp/startup.el                                    |    2 +-
 lisp/strokes.el                                    |   25 +-
 lisp/subr.el                                       |   72 +-
 lisp/svg.el                                        |    3 +
 lisp/tab-bar.el                                    |   21 +-
 lisp/tab-line.el                                   |   88 +-
 lisp/tempo.el                                      |  185 +-
 lisp/term/android-win.el                           |    2 +-
 lisp/textmodes/dns-mode.el                         |    1 +
 lisp/textmodes/enriched.el                         |    2 +-
 lisp/textmodes/flyspell.el                         |   49 +-
 lisp/textmodes/glyphless-mode.el                   |    1 -
 lisp/textmodes/html-ts-mode.el                     |   19 +-
 lisp/textmodes/ispell.el                           |    4 +-
 lisp/textmodes/reftex-vars.el                      |    2 +-
 lisp/textmodes/reftex.el                           |   15 +-
 lisp/textmodes/sgml-mode.el                        |    4 +-
 lisp/textmodes/tex-mode.el                         |   18 +-
 lisp/tmm.el                                        |    3 +-
 lisp/touch-screen.el                               |   39 +-
 lisp/treesit.el                                    |  582 ++--
 lisp/uniquify.el                                   |    4 +-
 lisp/url/url-gw.el                                 |   17 +-
 lisp/url/url-vars.el                               |    2 +-
 lisp/use-package/use-package.el                    |    3 +
 lisp/vc/diff-mode.el                               |   35 +
 lisp/vc/ediff-util.el                              |   87 +-
 lisp/vc/emerge.el                                  |   30 +-
 lisp/vc/vc-annotate.el                             |   33 +-
 lisp/vc/vc-git.el                                  |   18 +-
 lisp/vc/vc-hg.el                                   |    5 +-
 lisp/vc/vc-hooks.el                                |    9 +
 lisp/vc/vc.el                                      |   24 +-
 lisp/wid-edit.el                                   |   26 +-
 lisp/window.el                                     |    8 +-
 make-dist                                          |    5 +-
 nextstep/Makefile.in                               |    4 +-
 nt/inc/ms-w32.h                                    |    2 +-
 src/ChangeLog.11                                   |    2 +-
 src/alloc.c                                        |  267 +-
 src/android-asset.h                                |   12 +-
 src/android.c                                      |  424 +--
 src/android.h                                      |    8 +-
 src/androidfns.c                                   |   29 +-
 src/androidfont.c                                  |    6 +-
 src/androidmenu.c                                  |  113 +-
 src/androidselect.c                                |   18 +-
 src/androidterm.c                                  |   41 +-
 src/androidvfs.c                                   |  114 +-
 src/buffer.c                                       |    2 +-
 src/buffer.h                                       |    6 +-
 src/callproc.c                                     |   20 +-
 src/chartab.c                                      |    3 +-
 src/data.c                                         |    6 +-
 src/fileio.c                                       |    2 +-
 src/fns.c                                          |   78 +-
 src/font.c                                         |  257 +-
 src/font.h                                         |   18 +-
 src/fontset.c                                      |   31 +-
 src/gtkutil.c                                      |    6 +-
 src/haiku_support.cc                               |    4 -
 src/haikufont.c                                    |    3 +-
 src/image.c                                        |   12 +
 src/itree.c                                        |    2 +-
 src/keyboard.c                                     |    5 +
 src/keymap.c                                       |   24 +-
 src/lisp.h                                         |   75 +-
 src/nsfns.m                                        |   30 +-
 src/nsmenu.m                                       |    8 +
 src/nsterm.h                                       |   13 +-
 src/nsterm.m                                       |   86 +-
 src/pdumper.c                                      |    6 +-
 src/process.c                                      |   32 +-
 src/regex-emacs.c                                  | 1290 +++++----
 src/regex-emacs.h                                  |    4 +
 src/search.c                                       |   41 +
 src/sfnt.c                                         |  236 +-
 src/sfnt.h                                         |   88 +
 src/sfntfont.c                                     |  174 +-
 src/sysdep.c                                       |   10 +-
 src/term.c                                         |   25 +-
 src/treesit.c                                      |  242 +-
 src/treesit.h                                      |   15 +-
 src/w32font.c                                      |    2 +-
 src/w32heap.c                                      |    2 +-
 src/xdisp.c                                        |   19 +-
 src/xfaces.c                                       |    7 +-
 src/xfns.c                                         |   10 +-
 src/xterm.c                                        |  201 +-
 test/infra/Dockerfile.emba                         |    1 +
 test/infra/test-jobs.yml                           |    1 +
 test/lisp/autorevert-tests.el                      |    2 +-
 test/lisp/emacs-lisp/benchmark-tests.el            |    4 +-
 test/lisp/emacs-lisp/byte-run-tests.el             |   32 +
 test/lisp/emacs-lisp/bytecomp-tests.el             |  116 +-
 test/lisp/emacs-lisp/cl-lib-tests.el               |   36 +-
 test/lisp/emacs-lisp/cl-print-tests.el             |   18 +-
 test/lisp/emacs-lisp/ert-tests.el                  |   14 +
 test/lisp/emacs-lisp/find-func-tests.el            |    2 +-
 test/lisp/emacs-lisp/package-tests.el              |   16 +
 .../erc/erc-scenarios-scrolltobottom-relaxed.el    |  140 +
 test/lisp/erc/erc-scenarios-scrolltobottom.el      |   66 +
 test/lisp/erc/erc-stamp-tests.el                   |   33 +
 test/lisp/erc/erc-tests.el                         |  255 +-
 .../base/assoc/bouncer-history/barnet.eld          |    4 +-
 .../base/assoc/bouncer-history/foonet.eld          |    4 +-
 .../lisp/erc/resources/base/assoc/bumped/again.eld |   10 +-
 .../erc/resources/base/assoc/bumped/foisted.eld    |   10 +-
 .../erc/resources/base/assoc/bumped/refoisted.eld  |    8 +-
 test/lisp/erc/resources/base/flood/soju.eld        |    2 +-
 .../erc/resources/base/netid/bouncer/barnet.eld    |    2 +-
 .../erc/resources/base/netid/bouncer/foonet.eld    |    2 +-
 .../erc/resources/base/reconnect/aborted-dupe.eld  |    2 +-
 test/lisp/erc/resources/base/reconnect/aborted.eld |    2 +-
 .../resources/base/renick/self/qual-chester.eld    |    2 +-
 .../erc/resources/base/renick/self/qual-tester.eld |    2 +-
 test/lisp/erc/resources/erc-d/erc-d-t.el           |    7 +-
 test/lisp/erc/resources/erc-d/erc-d-u.el           |    1 +
 test/lisp/erc/resources/erc-d/erc-d.el             |   52 +-
 .../resources/erc-d/resources/dynamic-barnet.eld   |    4 +-
 .../resources/erc-d/resources/dynamic-foonet.eld   |    2 +-
 test/lisp/erc/resources/erc-d/resources/linger.eld |    4 +-
 test/lisp/erc/resources/erc-scenarios-common.el    |  206 ++
 test/lisp/erc/resources/join/legacy/foonet.eld     |    2 +-
 test/lisp/erc/resources/scrolltobottom/help.eld    |   46 +
 test/lisp/eshell/em-prompt-tests.el                |  109 +-
 test/lisp/eshell/em-script-tests.el                |   13 +
 test/lisp/eshell/esh-cmd-tests.el                  |  119 +
 test/lisp/eshell/esh-io-tests.el                   |   11 +
 test/lisp/eshell/esh-proc-tests.el                 |   73 +-
 test/lisp/eshell/esh-util-tests.el                 |   30 +
 test/lisp/eshell/esh-var-tests.el                  |    8 +
 test/lisp/eshell/eshell-tests-helpers.el           |   37 +-
 test/lisp/eshell/eshell-tests.el                   |  108 +-
 test/lisp/filenotify-tests.el                      |    2 +-
 test/lisp/help-tests.el                            |   12 +-
 test/lisp/ibuffer-tests.el                         |    2 +-
 test/lisp/international/mule-tests.el              |   24 +
 test/lisp/international/ucs-normalize-tests.el     |  138 +-
 test/lisp/minibuffer-tests.el                      |   13 +
 test/lisp/net/mailcap-tests.el                     |   24 +
 test/lisp/net/network-stream-tests.el              |   20 +-
 test/lisp/net/tramp-tests.el                       |   96 +-
 .../cperl-mode-resources/cperl-bug-35925.pl        |   36 +
 test/lisp/progmodes/cperl-mode-tests.el            |   18 +
 test/lisp/progmodes/eglot-tests.el                 |   18 +-
 test/lisp/progmodes/elisp-mode-tests.el            |    8 +-
 .../progmodes/elixir-ts-mode-resources/indent.erts |   16 +
 test/lisp/progmodes/flymake-tests.el               |    3 +-
 .../progmodes/lua-ts-mode-resources/indent.erts    |  152 ++
 .../progmodes/lua-ts-mode-resources/movement.erts  |  553 ++++
 test/lisp/progmodes/lua-ts-mode-tests.el           |   36 +
 test/lisp/progmodes/perl-mode-tests.el             |   17 +
 test/lisp/progmodes/project-tests.el               |    1 +
 test/lisp/progmodes/python-tests.el                |    2 +-
 test/lisp/shadowfile-tests.el                      |   20 +-
 test/lisp/simple-tests.el                          |    6 +-
 test/lisp/term-tests.el                            |   20 +-
 test/lisp/thread-tests.el                          |    2 +-
 test/lisp/vc/vc-tests.el                           |    9 +-
 test/lisp/wid-edit-tests.el                        |   11 +
 test/manual/BidiCharacterTest.txt                  |    6 +-
 test/manual/scroll-tests.el                        |   10 +-
 test/misc/test-custom-libs.el                      |    2 +-
 test/src/buffer-tests.el                           |    1 +
 test/src/emacs-module-tests.el                     |    2 +-
 test/src/fileio-tests.el                           |    2 +-
 test/src/filelock-tests.el                         |   10 +-
 test/src/fns-tests.el                              |   20 +
 test/src/image-tests.el                            |    6 +-
 test/src/process-tests.el                          |    6 +-
 test/src/regex-emacs-tests.el                      |   41 +-
 test/src/regex-resources/PTESTS                    |    1 +
 test/src/treesit-tests.el                          |   14 +
 514 files changed, 17116 insertions(+), 8632 deletions(-)

diff --git a/.clangd b/.clangd
index 131d0af5843..469d33dfd03 100644
--- a/.clangd
+++ b/.clangd
@@ -1,5 +1,5 @@
 ---
 If:
-    PathMatch: "src/.*\.c"
+    PathMatch: "src/*.c"
 CompileFlags:
     Add: [-Wno-unused-macros, -include=config.h]
diff --git a/.gitignore b/.gitignore
index 0a68121009d..81abc4c90ff 100644
--- a/.gitignore
+++ b/.gitignore
@@ -127,6 +127,7 @@ lisp/cedet/semantic/grammar-wy.el
 lisp/eshell/esh-groups.el
 lisp/finder-inf.el
 lisp/leim/ja-dic/
+leim/small-ja-dic-option
 lisp/leim/leim-list.el
 lisp/leim/quail/4Corner.el
 lisp/leim/quail/ARRAY30.el
diff --git a/.mailmap b/.mailmap
index f14f086b2e5..5d80e4aa082 100644
--- a/.mailmap
+++ b/.mailmap
@@ -27,6 +27,7 @@ Andrew G Cohen <cohen@andy.bu.edu> <cohen@bu.edu>
 Arash Esbati <arash@gnu.org> <arash.esbati@gmail.com>
 Arash Esbati <arash@gnu.org> <esbati@gmx.de>
 Artur Malabarba <bruce.connor.am@gmail.com> <am12548@it055607.users.bris.ac.uk>
+Artur Malabarba <bruce.connor.am@gmail.com> Artur Malabarba <address@hidden>
 Bastien Guerry <bzg@gnu.org>
 Bastien Guerry <bzg@gnu.org> <bastien1@free.fr>
 Bastien Guerry <bzg@gnu.org> <bzg@altern.org>
@@ -60,7 +61,7 @@ Era Eriksson <era+emacs@iki.fi> <era+emacsbugs@iki.fi>
 Eric Ludlam <zappo@gnu.org>
 Eric Ludlam <zappo@gnu.org> <eric@siege-engine.com>
 Eric Ludlam <zappo@gnu.org> <ericludlam@gmail.com>
-Eric S. Raymond <esr@thyrsus.com>
+Eric S. Raymond <esr@thyrsus.com> <esr@snark.thyrsus.com>
 Etienne Prud’homme <e.e.f.prudhomme@gmail.com>
 Fabián Ezequiel Gallina <fgallina@gnu.org> <fgallina@cuca>
 Fabián Ezequiel Gallina <fgallina@gnu.org> <galli.87@gmail.com>
@@ -89,6 +90,7 @@ Joakim Verona <joakim@verona.se> <root@exodia.verona.se>
 John Wiegley <johnw@newartisans.com> <jwiegley@gmail.com>
 Jose A. Ortega Ruiz <jao@gnu.org>
 João Távora <joaotavora@gmail.com>
+João Távora <joaotavora@gmail.com> 
<capitaomorte@archlinux2022.linuxvmimages.local>
 Julien Danjou <julien@danjou.info> <jd@dex.adm.naquadah.org>
 Julien Danjou <julien@danjou.info> Julien Danjou <jd@abydos>
 Juri Linkov <juri@linkov.net> <juri@jurta.org>
@@ -121,6 +123,7 @@ Masatake YAMATO <yamato@redhat.com> <jet@gyve.org>
 Matt Armstrong <matt@rfc20.org> <marmstrong@google.com>
 Matt Armstrong <matt@rfc20.org> <matt@mdeb>
 Mattias Engdegård <mattiase@acm.org>
+Mattias Engdegård <mattiase@acm.org> <mattias.engdegard@gmail.com>
 Maxim Nikulin <manikulin@gmail.com>
 Michael Albinus <michael.albinus@gmx.de> <albinus@detlef>
 Michalis V <mvar.40k@gmail.com>
@@ -189,6 +192,7 @@ Wolfgang Scherer <wolfgang.scherer@gmx.de> 
<Wolfgang.Scherer@gmx.de>
 Xi Lu <lx@shellcodes.org>
 Xue Fuqiao <xfq.free@gmail.com> <xfq@gnu.org>
 Yilkal Argaw <yilkalargawworkneh@gmail.com>
+Yuan Fu <casouri@gmail.com> <yuan@debian-BULLSEYE-live-builder-AMD64>
 Yuuki Harano <masm+github@masm11.me> <masm@masm11.ddo.jp>
 Óscar Fuentes <ofv@wanadoo.es>
 İ. Göktuğ Kayaalp <self@gkayaalp.com>
diff --git a/CONTRIBUTE b/CONTRIBUTE
index 464b800adfb..68c41638942 100644
--- a/CONTRIBUTE
+++ b/CONTRIBUTE
@@ -120,9 +120,9 @@ Emacs version in which they will appear.  Likewise with 
defcustom's
 whose value is changed -- update their ':version' tag.
 
 Think about whether your change requires updating the manuals.  If you
-know it does not, mark the NEWS entry with "---".  If you know
-that *all* the necessary documentation updates have been made as part
-of your changes or those by others, mark the entry with "+++".
+know it does not, mark the NEWS entry with "---" before the entry.  If
+you know that *all* the necessary documentation updates have been made
+as part of your changes or those by others, mark the entry with "+++".
 Otherwise, do not mark it.
 
 If your change requires updating the manuals to document new
@@ -300,6 +300,11 @@ them right the first time, so here are guidelines for 
formatting them:
   blank ChangeLog entries from the diff being committed, then use
   'M-q' to combine and fill them.  See 'info "(emacs) Log Buffer"'.
 
+- If you use the third-party package Magit, you can use
+  'magit-generate-changelog' from the commit message buffer.
+  See also 'magit-add-change-log-entry' and
+  'magit-add-change-log-entry-other-window'.
+
 - Alternatively, you can use Emacs functions for ChangeLog files; see
   
https://www.gnu.org/software/emacs/manual/html_node/emacs/Change-Log-Commands.html
   or run 'info "(emacs)Change Log Commands"'.
diff --git a/ChangeLog.android b/ChangeLog.android
index f56c0469408..8cc66c4d7ea 100644
--- a/ChangeLog.android
+++ b/ChangeLog.android
@@ -90,10 +90,10 @@
 
 2023-08-02  Po Lu  <luangruo@yahoo.com>
 
-       * doc/emacs/android.texi (Android, What is Android?, Android
-       Startup, Android File System, Android Environment,Android
-       Windowing, Android Fonts, Android Troubleshooting): Improve
-       section titles.
+       * doc/emacs/android.texi (Android, What is Android?)
+       (Android Startup, Android File System, Android Environment)
+       (Android Windowing, Android Fonts, Android Troubleshooting):
+       Improve section titles.
        (Android Windowing): Describe the relation between keyboard
        modifiers reported by Android and those in key events.
 
@@ -203,6 +203,7 @@
        argument NAME.
 
        * src/android.c (android_init_emacs_service): Add new argument.
+
        * src/androidvfs.c (android_saf_delete_document)
        (android_saf_tree_rmdir, android_saf_file_unlink): Pass name of
        file being deleted to `deleteDocument'.
diff --git a/INSTALL b/INSTALL
index d09216739f6..477ac16ba71 100644
--- a/INSTALL
+++ b/INSTALL
@@ -495,12 +495,12 @@ shell such as Bash, which uses these variables:
 
   ./configure \
     CPPFLAGS='-I/foo/myinclude' LDFLAGS='-L/bar/mylib' \
-    CFLAGS='-O3' LIBS='-lfoo -lbar'
+    CFLAGS='-Og' LIBS='-lfoo -lbar'
 
 (this is all one shell command).  This tells 'configure' to instruct the
 preprocessor to look in the '/foo/myinclude' directory for header
 files (in addition to the standard directories), instruct the linker
-to look in '/bar/mylib' for libraries, pass the -O3 optimization
+to look in '/bar/mylib' for libraries, pass the -Og optimization
 switch to the compiler, and link against libfoo and libbar
 libraries in addition to the standard ones.
 
diff --git a/Makefile.in b/Makefile.in
index fdd9353e254..51a27cc1814 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -643,6 +643,7 @@ ifndef NO_BIN_LINK
        cd "$(DESTDIR)${bindir}" && $(LN_S_FILEONLY) "$(EMACSFULL)" "$(EMACS)"
 endif
 else
+       ${INSTALL_DATA} src/emacs.pdmp "$(DESTDIR)${libexecdir}/Emacs.pdmp"
        subdir=${ns_appresdir}/site-lisp && ${write_subdir}
        rm -rf ${ns_appresdir}/share
 endif
@@ -843,6 +844,9 @@ EMACS_ICON=emacs
 ifeq (${USE_STARTUP_NOTIFICATION},no)
 USE_STARTUP_NOTIFICATION_SED_CMD=-e "/^StartupNotify=true$$/d"
 endif
+ifeq ($(HAVE_PGTK),yes)
+USE_WAYLAND_DISPLAY_SED_CMD=-e "s/display=[^ ]*/reuse-frame/"
+endif
 install-etc:
        umask 022; ${MKDIR_P} "$(DESTDIR)${desktopdir}"
        tmp=etc/emacs.tmpdesktop; rm -f $${tmp}; \
@@ -857,6 +861,7 @@ install-etc:
        sed -e "/^Exec=/ s|emacsclient|${bindir}/$${client_name}|" \
          -e "/^Icon=emacs/ s/emacs/${EMACS_NAME}/" \
          $(USE_STARTUP_NOTIFICATION_SED_CMD) \
+         $(USE_WAYLAND_DISPLAY_SED_CMD) \
          ${srcdir}/etc/emacsclient.desktop > $${tmp}; \
        ${INSTALL_DATA} $${tmp} 
"$(DESTDIR)${desktopdir}/$${client_name}.desktop"; \
        rm -f $${tmp}
@@ -870,6 +875,7 @@ install-etc:
        client_name=`echo emacsclient | sed '$(TRANSFORM)'`${EXEEXT}; \
        sed -e "/^Exec=/ s|emacsclient|${bindir}/$${client_name}|" \
          -e "/^Icon=emacs/ s/emacs/${EMACS_NAME}/" \
+         $(USE_WAYLAND_DISPLAY_SED_CMD) \
          ${srcdir}/etc/emacsclient-mail.desktop > $${tmp}; \
        ${INSTALL_DATA} $${tmp} 
"$(DESTDIR)${desktopdir}/$${client_name}-mail.desktop"; \
        rm -f $${tmp}
diff --git a/admin/MAINTAINERS b/admin/MAINTAINERS
index cea1aa56cde..1801842bdcb 100644
--- a/admin/MAINTAINERS
+++ b/admin/MAINTAINERS
@@ -5,6 +5,11 @@ what parts of the Emacs distribution.  The areas can be defined
 "arbitrarily", but should provide fairly well-defined boundaries so
 that there are not too many ambiguities.
 
+The (co-)maintainers of Emacs are:
+
+       Eli Zaretskii <eliz@gnu.org>
+       Stefan Kangas <stefankangas@gmail.com>
+
 ==============================================================================
 1. Areas that someone wants to be maintaining (i.e. has a particularly
 keen interest in).  There's no need to list files where you are
@@ -160,6 +165,69 @@ Po Lu
 
        Haiku battery support in lisp/battery.el
 
+       The Android port:
+           src/android-asset.h
+           src/android.c
+           src/android-emacs.c
+           src/androidfns.c
+           src/androidfont.c
+           src/androidgui.h
+           src/android.h
+           src/androidmenu.c
+           src/androidselect.c
+           src/androidterm.c
+           src/androidterm.h
+           src/androidvfs.c
+           src/sfnt.c
+           src/sfntfont-android.c
+           src/sfntfont.c
+           src/sfntfont.h
+           src/sfnt.h
+           java/org/gnu/emacs/EmacsActivity.java
+           java/org/gnu/emacs/EmacsApplication.java
+           java/org/gnu/emacs/EmacsClipboard.java
+           java/org/gnu/emacs/EmacsContextMenu.java
+           java/org/gnu/emacs/EmacsCursor.java
+           java/org/gnu/emacs/EmacsDesktopNotification.java
+           java/org/gnu/emacs/EmacsDialogButtonLayout.java
+           java/org/gnu/emacs/EmacsDialog.java
+           java/org/gnu/emacs/EmacsDirectoryEntry.java
+           java/org/gnu/emacs/EmacsDocumentsProvider.java
+           java/org/gnu/emacs/EmacsDrawable.java
+           java/org/gnu/emacs/EmacsDrawLine.java
+           java/org/gnu/emacs/EmacsDrawPoint.java
+           java/org/gnu/emacs/EmacsDrawRectangle.java
+           java/org/gnu/emacs/EmacsFillPolygon.java
+           java/org/gnu/emacs/EmacsFillRectangle.java
+           java/org/gnu/emacs/EmacsFontDriver.java
+           java/org/gnu/emacs/EmacsGC.java
+           java/org/gnu/emacs/EmacsHandleObject.java
+           java/org/gnu/emacs/EmacsHolder.java
+           java/org/gnu/emacs/EmacsInputConnection.java
+           java/org/gnu/emacs/EmacsLauncherPreferencesActivity.java
+           java/org/gnu/emacs/EmacsMultitaskActivity.java
+           java/org/gnu/emacs/EmacsNative.java
+           java/org/gnu/emacs/EmacsNoninteractive.java
+           java/org/gnu/emacs/EmacsOpenActivity.java
+           java/org/gnu/emacs/EmacsPixmap.java
+           java/org/gnu/emacs/EmacsPreferencesActivity.java
+           java/org/gnu/emacs/EmacsSafThread.java
+           java/org/gnu/emacs/EmacsSdk11Clipboard.java
+           java/org/gnu/emacs/EmacsSdk23FontDriver.java
+           java/org/gnu/emacs/EmacsSdk7FontDriver.java
+           java/org/gnu/emacs/EmacsSdk8Clipboard.java
+           java/org/gnu/emacs/EmacsService.java
+           java/org/gnu/emacs/EmacsSurfaceView.java
+           java/org/gnu/emacs/EmacsThread.java
+           java/org/gnu/emacs/EmacsView.java
+           java/org/gnu/emacs/EmacsWindowAttachmentManager.java
+           java/org/gnu/emacs/EmacsWindow.java
+           java/org/gnu/emacs/R.java
+           m4/ndk-build.m4
+           cross
+
+       Android battery support in lisp/battery.el
+
 Jim Porter
        Eshell
            lisp/eshell/*
diff --git a/admin/authors.el b/admin/authors.el
index f1c988cc776..679ddf08085 100644
--- a/admin/authors.el
+++ b/admin/authors.el
@@ -90,6 +90,8 @@ files.")
     ("Etienne Prud’Homme" "Etienne Prud'Homme")
     ("Fabián Ezequiel Gallina" "Fabian Ezequiel Gallina" "Fabi.n E\\. Gallina")
     (nil "felix.*EmacsWiki")
+    (nil "felix\\.dick@web\\.de")
+    ("Felicián Németh" "Felician Nemeth")
     (nil "foudfou")
     ("Francis Litterio" "Fran Litterio")
     ("Francis J. Wright" "Dr Francis J. Wright" "Francis Wright")
@@ -98,6 +100,8 @@ files.")
     ("Frederic Pierresteguy" "Fred Pierresteguy")
     (nil "^FSF")
     ("Gerd Möllmann" "Gerd Moellmann")
+    (nil "haqle314")
+    ("Grégoire Jadi" "Gregoire Jadi")
     ("Hallvard B. Furuseth" "Hallvard B Furuseth" "Hallvard Furuseth")
     ("Hrvoje Nikšić" "Hrvoje Niksic")
     ("Ian Dunn" "^Ian D\\>")
@@ -109,6 +113,7 @@ files.")
     ("J. Alexander Branham" "Alex Branham")
     ("Jaeyoun Chung" "Jae-youn Chung" "Jae-you Chung" "Chung Jae-youn")
     ("Jan Djärv" "Jan D\\>" "Jan Djarv")
+    ("Jan Synáček" "Jan Synacek")
     ("João Távora" "João Tãvora")
     ("Jay K. Adams" "Jay Adams")
     ("J.D. Smith" "Jd Smith")
@@ -119,6 +124,7 @@ files.")
     ("Jeremy Bertram Maitin-Shepard" "Jeremy Maitin-Shepard")
     ("Jérémy Compostella" "Jeremy Compostella")
     ("Jimmy Aguilar Mena" "Ergus")
+    ("Jindřich Makovička" "Jindrich Makovicka")
     ("Johan Bockgård" "Johan Bockgard")
     ("John F. Carr" "John F Carr")
     ("John J Foerch" "John Foerch")
@@ -140,6 +146,10 @@ files.")
     ("Kenichi Handa" "Ken'ichi Handa" "Kenichi HANDA" "K\\. Handa")
     ("Kevin Greiner" "Kevin J. Greiner")
     ("Kim F. Storm" "Kim Storm")
+    ("Kjartan Óli Ágústsson" "Kjartan Oli Agustsson")
+    ;; The dash is a kludge, so this contributor is not ignored.
+    ("kobarity-" "kobarity@gmail\\.com")
+    ("Koen van Greevenbroek" "realcomplex" "koenvg@posteo\\.net")
     ("Kyle Jones" "Kyle E. Jones")
     ("Lars Magne Ingebrigtsen" "Lars Ingebrigtsen")
     (nil "LynX@bk.ru")
diff --git a/admin/make-tarball.txt b/admin/make-tarball.txt
index fddf8444067..505d3469d3c 100644
--- a/admin/make-tarball.txt
+++ b/admin/make-tarball.txt
@@ -205,7 +205,11 @@ General steps (for each step, check for possible errors):
     you need to repeat from step 4 onwards.  (You can commit the files
     from step 2 and 3 earlier to reduce the chance of this.)
 
-6.   ./make-dist --snapshot --no-compress
+6.  If there has been a change in who is the Emacs maintainer since
+    the last release, update doc/misc/ack.texi and admin/MAINTAINERS
+    to reflect this.  You can commit this separately.
+
+7.   ./make-dist --snapshot --no-compress
 
     Check the contents of the new tar with admin/diff-tar-files
     against the previous release (if this is the first pretest) or the
@@ -234,7 +238,7 @@ General steps (for each step, check for possible errors):
     The output of this command might be easier to compare to the
     tarball than the one you get from find.
 
-7.   tar xf emacs-NEW.tar; cd emacs-NEW
+8.   tar xf emacs-NEW.tar; cd emacs-NEW
      ./configure --prefix=/tmp/emacs && make check && make install
 
     Use 'script' or M-x compile to save the compilation log in
@@ -244,7 +248,7 @@ General steps (for each step, check for possible errors):
     M-x ediff.  Especially check that Info files aren't built, and that
     no autotools (autoconf etc) run.
 
-8.  You can now tag the release/pretest and push it together with the
+9.  You can now tag the release/pretest and push it together with the
     last commit:
 
      cd EMACS_ROOT_DIR && git tag -a TAG -m "Emacs TAG"
@@ -270,7 +274,7 @@ General steps (for each step, check for possible errors):
      git tag -a emacs-28.1-rc1 -m "Emacs 28.1 RC1"
      git tag -a emacs-28.1     -m "Emacs 28.1 release"
 
-9. Decide what compression schemes to offer.
+10. Decide what compression schemes to offer.
     For a release, at least gz and xz:
       gzip --best --no-name -c emacs-NEW.tar > emacs-NEW.tar.gz
       xz -c emacs-NEW.tar > emacs-NEW.tar.xz
@@ -314,14 +318,14 @@ General steps (for each step, check for possible errors):
     For a pretest, place the files in /incoming/alpha instead, so that
     they appear on https://alpha.gnu.org/.
 
-10. After five minutes, verify that the files are visible at
+11. After five minutes, verify that the files are visible at
     https://alpha.gnu.org/gnu/emacs/pretest/ for a pretest, or
     https://ftp.gnu.org/gnu/emacs/ for a release.
 
     Download them and check the signatures and SHA1/SHA256 checksums.
     Check they build (./configure --with-native-compilation).
 
-11. Send an announcement to: emacs-devel, and bcc: info-gnu-emacs@gnu.org.
+12. Send an announcement to: emacs-devel, and bcc: info-gnu-emacs@gnu.org.
     For a pretest, also bcc: platform-testers@gnu.org.
     For a release, also bcc: info-gnu@gnu.org.
     (The reason for using bcc: is to make it less likely that people
@@ -345,9 +349,9 @@ General steps (for each step, check for possible errors):
     (Use e.g. `M-x mml-secure-message-sign' in `message-mode' to sign
     an email.)
 
-12. After a release, update the Emacs pages as described below.
+13. After a release, update the Emacs pages as described below.
 
-13. After a release, bump the Emacs version on the release branch.
+14. After a release, bump the Emacs version on the release branch.
     There is no need to bump the version after a pretest; the version
     is bumped before the next pretest or release instead.
 
diff --git a/admin/notes/bug-triage b/admin/notes/bug-triage
index bee7242337d..6fad55dc1e3 100644
--- a/admin/notes/bug-triage
+++ b/admin/notes/bug-triage
@@ -1,10 +1,10 @@
 HOW TO TRIAGE EMACS BUGS  -*- outline -*-
 
-This document just describes the procedure of triaging bugs, for information on
-how to work with the bug tracker, see the bugtracker file in this same 
directory
-for the basics.  You can also install the debbugs ELPA package for access to 
M-x
-debbugs-gnu, an emacs interface to debbugs, and M-x debbugs-org, an emacs
-interface via org-mode.
+This document describes the procedure of triaging bugs.  For information on how
+to work with the bug tracker, see the file "bugtracker" in the same directory 
as
+this file for the basics.  You can also install the GNU ELPA package 'debbugs'
+for access to 'M-x debbugs-gnu', an Emacs interface to the debbugs bug tracker,
+and 'M-x debbugs-org', an Emacs interface via org-mode.
 
 * Bug backlog triage procedure
 
@@ -15,9 +15,10 @@ the ones that are not reproducible on the current release.
      calling debbugs-gnu-emacs-release-blocking-reports.  If you want
      to check this for another Emacs version but the next-to-be-released-one,
      use the "C-u" prefix.
-  1. After that, enter debbugs mode (either debbugs-gnu, debbugs-org, or via 
the
-     web browser), and accept the default list option of bugs that have 
severity
-     serious, important, or normal.
+  1. After that, enter debbugs mode (either using 'M-x debbugs-gnu',
+     'M-x debbugs-org', or via the web browser), and accept the
+     default list option of bugs that have severity "serious",
+     "important", or "normal".
   2. For each bug, we want to primarily make sure it is still
      reproducible.  A bug can and should stay open as long as it is
      still a bug and no one has fixed it.  The following is a
@@ -90,21 +91,51 @@ necessary information for others to act on.
 
 For each new bug, ask the following questions:
 
-  1. Is the bug report written in a way to be easy to reproduce (starts from
-     "emacs -Q", etc.)?  If not, ask the reporter to try and reproduce it on an
-     emacs without customization.
-  2. Is the bug report written against the latest emacs?  If not, try to
-     reproduce on the latest version, and if it can't be reproduced, ask the
-     reporter to try again with the latest version.
+  1. Is the bug report written in a way to be easy to reproduce
+     (starts from "emacs -Q", etc.)?  If not, ask the reporter to try
+     and reproduce it on an emacs without customization.
+  2. Is the bug report written against the latest emacs?  If not, try
+     to reproduce on the latest version, and if it can't be
+     reproduced, ask the reporter to try again with the latest
+     version.
   3. Is the bug the same as another bug?  If so, merge the bugs.
-  4. What is the priority of the bug?  Add a priority: serious, important,
-     normal, minor, or wishlist.
-  5. Who should be the owner?  This depends on what component the bug is part
-     of.  You can look at the admin/MAINTAINERS file (then you can just search
-     emacs-devel to match the name with an email address).
+  4. What is the priority of the bug?  Add a priority: "serious",
+     "important", "normal", "minor, or "wishlist".
+  5. Who should be the owner?  This depends on what component the bug
+     is part of.  You can look at the "Maintainer" comment header in
+     the relevant Lisp files.  If you can't find the name there, look
+     at admin/MAINTAINERS file (then you can just search emacs-devel
+     to match the name with an email address).
 
 In the debbugs-gnu buffer, bugs are marked in the "State" column
 according to the communication flow.  Red bugs mean that nobody has
-answered, these bugs need primary attention.  Green bugs flag that
+answered; these bugs need primary attention.  Green bugs flag that
 there is a recent communication about, and orange bugs flag that the
 bug hasn't been touched for at least two weeks.
+
+* Bugs in GNU ELPA and NonGNU ELPA packages
+
+The goal here is to ping the relevant maintainers, as Emacs core
+developers aren't always up-to-date with recent developments in all
+GNU ELPA packages, and can't do anything with reports about bugs in
+NonGNU ELPA packages.
+
+This is how we deal with them:
+
+  1. Bugs in GNU ELPA packages can always be reported to our bug
+     tracker, even if they are usually tracked by other means.  Search
+     for the maintainer of that package, e.g. on
+     https://elpa.gnu.org/packages and take note of their email
+     address.  Send a reply with an email body like "<name> is the
+     maintainer of <package>, so I'm copying them in here.", and
+     include their email address in Cc.
+  2. Bugs in NonGNU ELPA packages should be sent to their maintainers,
+     because we can't do anything to fix them.  If you suspect that
+     the bug is about a NonGNU ELPA package, it's usually polite to
+     ask the reporter if this is indeed the case (in case you
+     misunderstood something), and then to point them in the right
+     direction.  Such bugs can be closed once the confusion has been
+     resolved.
+  3. Bugs in third-party packages that are not in any of the above
+     repositories are handled in the same way as packages in NonGNU
+     ELPA.
diff --git a/java/README b/admin/notes/java
similarity index 90%
copy from java/README
copy to admin/notes/java
index e518e9fbb2f..125ac0aad67 100644
--- a/java/README
+++ b/admin/notes/java
@@ -1,28 +1,8 @@
-This directory holds the Java sources of the port of GNU Emacs to
-Android-like systems, along with files needed to create an application
-package out of them.  If you need to build this port, please read the
-file INSTALL in this directory.
+Installation instructions for Android
+Copyright (C) 2023 Free Software Foundation, Inc.
+See the end of the file for license conditions.
 
-The ``org/gnu/emacs'' subdirectory contains the Java sources under the
-``org.gnu.emacs'' package identifier.
-
-``AndroidManifest.xml'' contains a manifest describing the Java
-sources to the system.
-
-The ``res'' directory contains resources, mainly the Emacs icon and
-several ``boolean resources'' which are used as a form of conditional
-evaluation for manifest entries.
-
-`emacs.keystore' is the signing key used to build Emacs.  It is kept
-here, and we encourage all people redistributing Emacs to use this
-key.  It holds no security value, and otherwise it will be impossible
-to install different builds of Emacs on top of each other.
-
-Please keep the Java code indented with tabs and formatted according
-to the rules for C code in the GNU coding standards.  Always use
-C-style comments.
-
-======================================================================
+
 
 OVERVIEW OF JAVA
 
@@ -1046,3 +1026,72 @@ public class Foo
 {
   Object bar;
 };
+
+
+
+COMPATIBILITY
+
+There are three variables set within every Android application that
+extert influence over the set of Android systems it supports, and the
+measures it must take to function faithfully on each of those systems:
+the minimum API level, compile SDK version and target API level.
+
+The minimum API level is the earliest version of Android that is
+permitted to install and run the application.  For Emacs, this is
+established by detecting the __ANDROID_API__ preprocessor macro
+defined within the Android C compiler.
+
+Before Java code executes any Android API calls that are not present
+within Android 2.2 (API level 8), the lowest API level supported by
+Emacs as a whole, it must first check the value of the:
+
+  Build.VERSION.SDK_INT
+
+variable, which is always set to the API level of the system Emacs is
+presently installed within.  For example, before calling
+`dispatchKeyEventFromInputMethod', a function absent from Android 6.0
+(API level 23) or earlier, check:
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
+      view.imManager.dispatchKeyEventFromInputMethod (view, key);
+    else
+      {
+
+where `N' is a constant defined to 24.
+
+The compile SDK version is the version of the Android SDK headers Java
+code is compiled against.  Because Java does not provide conditional
+compilation constructs, Emacs can't be compiled with any version of
+these headers other than the version mentioned in `java/INSTALL', but
+the headers used do not affect the set of supported systems provided
+that the version checks illustrated above are performed where
+necessary.
+
+The target API level is a number within java/AndroidManifest.xml.in
+the system refers to when deciding whether to enable
+backwards-incompatible modifications to the behavior of various system
+APIs.  For any given Android version, backwards incompatible changes
+in that version will be disabled for applications whose target API
+levels don't exceed its own.
+
+The target API should nevertheless be updated to match every major
+Android update, as Google has stated their intentions to prohibit
+users from installing applications targeting ``out-of-date'' versions
+of Android, though this threat has hitherto been made good on.
+
+
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
diff --git a/admin/notes/multi-tty b/admin/notes/multi-tty
index d0b63f166fd..16f2bec4d80 100644
--- a/admin/notes/multi-tty
+++ b/admin/notes/multi-tty
@@ -480,8 +480,6 @@ THINGS TO DO
 
 ** Have a look at set_frame_matrix_frame.
 
-** Check if we got term-setup-hook right.
-
 ** I think tip_frame should be display-local.
 
 ** Check display reference count handling in x_create_tip_frame.
diff --git a/admin/notes/tree-sitter/build-module/batch.sh 
b/admin/notes/tree-sitter/build-module/batch.sh
index 1d4076564dc..9988d1eae4e 100755
--- a/admin/notes/tree-sitter/build-module/batch.sh
+++ b/admin/notes/tree-sitter/build-module/batch.sh
@@ -13,8 +13,10 @@ languages=(
     'go-mod'
     'heex'
     'html'
+    'java'
     'javascript'
     'json'
+    'lua'
     'python'
     'rust'
     'toml'
diff --git a/admin/notes/tree-sitter/build-module/build.sh 
b/admin/notes/tree-sitter/build-module/build.sh
index 0832875168b..969187b7f92 100755
--- a/admin/notes/tree-sitter/build-module/build.sh
+++ b/admin/notes/tree-sitter/build-module/build.sh
@@ -42,6 +42,9 @@ case "${lang}" in
     "heex")
         org="phoenixframework"
         ;;
+    "lua")
+        org="MunifTanjim"
+        ;;
     "typescript")
         sourcedir="tree-sitter-typescript/typescript/src"
         grammardir="tree-sitter-typescript/typescript"
diff --git a/admin/notes/tree-sitter/performance 
b/admin/notes/tree-sitter/performance
new file mode 100644
index 00000000000..23f84743ced
--- /dev/null
+++ b/admin/notes/tree-sitter/performance
@@ -0,0 +1,25 @@
+TREE-SITTER PERFORMANCE NOTES -*- org -*-
+
+* Facts
+
+Incremental parsing of a few characters worth of edit usually takes
+less than 0.1ms. If it takes longer than that, something is wrong.
+There’s one time where I found tree-sitter-c takes ~30ms to
+incremental parse. Updating to the latest version of tree-sitter-c
+solves it, so I didn’t investigate further.
+
+The ranges set for a parser doesn’t grow when you insert text into a
+range, so you have to update the ranges every time before
+parsing. Fortunately, changing ranges doesn’t invalidate incremental
+parsing, so there isn’t any performance lost in update ranges
+frequently.
+
+* Experiments
+
+Using regexp by default in treesit-simple-indent-rules seems wasteful,
+so I tried replacing all string-match-p to equal in
+treesit-simple-indent-presets, and indent xdisp.c for a comparison.
+Turns out using regexp by default is faster: regexp-based indent took
+45s and equal-based indent took 75s.
+
+I could be missing something, further experiments are welcome.
diff --git a/admin/notes/unicode b/admin/notes/unicode
index da4736c43c6..3748989e2fe 100644
--- a/admin/notes/unicode
+++ b/admin/notes/unicode
@@ -39,9 +39,9 @@ repository).
 
 Next, review the assignment of default values of the Bidi Class
 property to blocks in the file extracted/DerivedBidiClass.txt from the
-UCD (search for "unassigned" in that file).  Any changes should be
-reflected in the unidata-gen.el file, where it sets up the default
-values around line 210.
+UCD (search for "unassigned" and "@missing" in that file).  Any
+changes should be reflected in the unidata-gen.el file, where it sets
+up the default values around line 210.
 
 Then Emacs should be rebuilt for them to take effect.  Rebuilding
 Emacs updates several derived files elsewhere in the Emacs source
@@ -61,9 +61,10 @@ Next, review the changes in UnicodeData.txt vs the previous 
version
 used by Emacs.  Any changes, be it introduction of new scripts or
 addition of codepoints to existing scripts, might need corresponding
 changes in the data used for filling the category-table, case-table,
-and char-width-table.  The additional scripts should cause automatic
-updates in charscript.el, but it is a good idea to look at the results
-and see if any changes in admin/unidata/blocks.awk are required.
+and char-width-table in characters.el.  The additional scripts should
+cause automatic updates in charscript.el, but it is a good idea to
+look at the results and see if any changes in admin/unidata/blocks.awk
+are required.
 
 The setting of char-width-table around line 1200 of characters.el
 should be checked against the latest version of the Unicode file
@@ -93,6 +94,11 @@ might need to be updated because it knows about used and 
unused ranges
 of Unicode codepoints, which a new release of the Unicode Standard
 could change.
 
+The data used by ucs-normalize.el might need to be updated.
+Specifically, the values of 'ucs-normalize-composition-exclusions' and
+'check-range", defined at the beginning of ucs-normalize.el, should be
+verified against the latest Unicode data files.
+
 Next, test normalization functions against NormalizationTests.txt,
 in the test/ directory run:
 
diff --git a/admin/unidata/BidiBrackets.txt b/admin/unidata/BidiBrackets.txt
index e138e7f5bea..8cebea41544 100644
--- a/admin/unidata/BidiBrackets.txt
+++ b/admin/unidata/BidiBrackets.txt
@@ -1,6 +1,6 @@
-# BidiBrackets-15.0.0.txt
-# Date: 2022-05-03, 18:42:00 GMT [AG, LI, KW]
-# © 2022 Unicode®, Inc.
+# BidiBrackets-15.1.0.txt
+# Date: 2023-01-18
+# © 2023 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in 
the U.S. and other countries.
 # For terms of use, see https://www.unicode.org/terms_of_use.html
 #
@@ -12,11 +12,11 @@
 # This file is a normative contributory data file in the Unicode
 # Character Database.
 #
-# Bidi_Paired_Bracket is a normative property of type Miscellaneous,
+# Bidi_Paired_Bracket is a normative property
 # which establishes a mapping between characters that are treated as
 # bracket pairs by the Unicode Bidirectional Algorithm.
 #
-# Bidi_Paired_Bracket_Type is a normative property of type Enumeration,
+# Bidi_Paired_Bracket_Type is a normative property
 # which classifies characters into opening and closing paired brackets
 # for the purposes of the Unicode Bidirectional Algorithm.
 #
diff --git a/admin/unidata/BidiMirroring.txt b/admin/unidata/BidiMirroring.txt
index 5861d6e7f4b..7e58cc4d715 100644
--- a/admin/unidata/BidiMirroring.txt
+++ b/admin/unidata/BidiMirroring.txt
@@ -1,6 +1,6 @@
-# BidiMirroring-15.0.0.txt
-# Date: 2022-05-03, 18:47:00 GMT [KW, RP]
-# © 2022 Unicode®, Inc.
+# BidiMirroring-15.1.0.txt
+# Date: 2023-01-05
+# © 2023 Unicode®, Inc.
 # For terms of use, see https://www.unicode.org/terms_of_use.html
 #
 # Unicode Character Database
@@ -15,7 +15,7 @@
 # value, for which there is another Unicode character that typically has a 
glyph
 # that is the mirror image of the original character's glyph.
 #
-# The repertoire covered by the file is Unicode 15.0.0.
+# The repertoire covered by the file is Unicode 15.1.0.
 #
 # The file contains a list of lines with mappings from one code point
 # to another one for character-based mirroring.
diff --git a/admin/unidata/Blocks.txt b/admin/unidata/Blocks.txt
index 12684594c9f..8fa3eaad04a 100644
--- a/admin/unidata/Blocks.txt
+++ b/admin/unidata/Blocks.txt
@@ -1,6 +1,6 @@
-# Blocks-15.0.0.txt
-# Date: 2022-01-28, 20:58:00 GMT [KW]
-# © 2022 Unicode®, Inc.
+# Blocks-15.1.0.txt
+# Date: 2023-07-28, 15:47:20 GMT
+# © 2023 Unicode®, Inc.
 # For terms of use, see https://www.unicode.org/terms_of_use.html
 #
 # Unicode Character Database
@@ -352,6 +352,7 @@ FFF0..FFFF; Specials
 2B740..2B81F; CJK Unified Ideographs Extension D
 2B820..2CEAF; CJK Unified Ideographs Extension E
 2CEB0..2EBEF; CJK Unified Ideographs Extension F
+2EBF0..2EE5F; CJK Unified Ideographs Extension I
 2F800..2FA1F; CJK Compatibility Ideographs Supplement
 30000..3134F; CJK Unified Ideographs Extension G
 31350..323AF; CJK Unified Ideographs Extension H
diff --git a/admin/unidata/IdnaMappingTable.txt 
b/admin/unidata/IdnaMappingTable.txt
index e4c06117929..3bf6b2668a4 100644
--- a/admin/unidata/IdnaMappingTable.txt
+++ b/admin/unidata/IdnaMappingTable.txt
@@ -1,11 +1,11 @@
 # IdnaMappingTable.txt
-# Date: 2022-05-02, 19:29:26 GMT
-# © 2022 Unicode®, Inc.
+# Date: 2023-08-10, 22:32:27 GMT
+# © 2023 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in 
the U.S. and other countries.
 # For terms of use, see https://www.unicode.org/terms_of_use.html
 #
 # Unicode IDNA Compatible Preprocessing for UTS #46
-# Version: 15.0.0
+# Version: 15.1.0
 #
 # For documentation and usage, see https://www.unicode.org/reports/tr46
 #
@@ -2036,7 +2036,7 @@
 1E9A          ; mapped                 ; 0061 02BE     # 1.1  LATIN SMALL 
LETTER A WITH RIGHT HALF RING
 1E9B          ; mapped                 ; 1E61          # 2.0  LATIN SMALL 
LETTER LONG S WITH DOT ABOVE
 1E9C..1E9D    ; valid                                  # 5.1  LATIN SMALL 
LETTER LONG S WITH DIAGONAL STROKE..LATIN SMALL LETTER LONG S WITH HIGH STROKE
-1E9E          ; mapped                 ; 0073 0073     # 5.1  LATIN CAPITAL 
LETTER SHARP S
+1E9E          ; mapped                 ; 00DF          # 5.1  LATIN CAPITAL 
LETTER SHARP S
 1E9F          ; valid                                  # 5.1  LATIN SMALL 
LETTER DELTA
 1EA0          ; mapped                 ; 1EA1          # 1.1  LATIN CAPITAL 
LETTER A WITH DOT BELOW
 1EA1          ; valid                                  # 1.1  LATIN SMALL 
LETTER A WITH DOT BELOW
@@ -2565,11 +2565,7 @@
 222E          ; valid                  ;      ; NV8    # 1.1  CONTOUR INTEGRAL
 222F          ; mapped                 ; 222E 222E     # 1.1  SURFACE INTEGRAL
 2230          ; mapped                 ; 222E 222E 222E #1.1  VOLUME INTEGRAL
-2231..225F    ; valid                  ;      ; NV8    # 1.1  CLOCKWISE 
INTEGRAL..QUESTIONED EQUAL TO
-2260          ; disallowed_STD3_valid                  # 1.1  NOT EQUAL TO
-2261..226D    ; valid                  ;      ; NV8    # 1.1  IDENTICAL 
TO..NOT EQUIVALENT TO
-226E..226F    ; disallowed_STD3_valid                  # 1.1  NOT 
LESS-THAN..NOT GREATER-THAN
-2270..22F1    ; valid                  ;      ; NV8    # 1.1  NEITHER 
LESS-THAN NOR EQUAL TO..DOWN RIGHT DIAGONAL ELLIPSIS
+2231..22F1    ; valid                  ;      ; NV8    # 1.1  CLOCKWISE 
INTEGRAL..DOWN RIGHT DIAGONAL ELLIPSIS
 22F2..22FF    ; valid                  ;      ; NV8    # 3.2  ELEMENT OF WITH 
LONG HORIZONTAL STROKE..Z NOTATION BAG MEMBERSHIP
 2300          ; valid                  ;      ; NV8    # 1.1  DIAMETER SIGN
 2301          ; valid                  ;      ; NV8    # 3.0  ELECTRIC ARROW
@@ -3273,7 +3269,7 @@
 2FD5          ; mapped                 ; 9FA0          # 3.0  KANGXI RADICAL 
FLUTE
 2FD6..2FEF    ; disallowed                             # NA   
<reserved-2FD6>..<reserved-2FEF>
 2FF0..2FFB    ; disallowed                             # 3.0  IDEOGRAPHIC 
DESCRIPTION CHARACTER LEFT TO RIGHT..IDEOGRAPHIC DESCRIPTION CHARACTER OVERLAID
-2FFC..2FFF    ; disallowed                             # NA   
<reserved-2FFC>..<reserved-2FFF>
+2FFC..2FFF    ; disallowed                             # 15.1 IDEOGRAPHIC 
DESCRIPTION CHARACTER SURROUND FROM RIGHT..IDEOGRAPHIC DESCRIPTION CHARACTER 
ROTATION
 3000          ; disallowed_STD3_mapped ; 0020          # 1.1  IDEOGRAPHIC SPACE
 3001          ; valid                  ;      ; NV8    # 1.1  IDEOGRAPHIC COMMA
 3002          ; mapped                 ; 002E          # 1.1  IDEOGRAPHIC FULL 
STOP
@@ -3425,7 +3421,8 @@
 31BB..31BF    ; valid                                  # 13.0 BOPOMOFO FINAL 
LETTER G..BOPOMOFO LETTER AH
 31C0..31CF    ; valid                  ;      ; NV8    # 4.1  CJK STROKE 
T..CJK STROKE N
 31D0..31E3    ; valid                  ;      ; NV8    # 5.1  CJK STROKE 
H..CJK STROKE Q
-31E4..31EF    ; disallowed                             # NA   
<reserved-31E4>..<reserved-31EF>
+31E4..31EE    ; disallowed                             # NA   
<reserved-31E4>..<reserved-31EE>
+31EF          ; disallowed                             # 15.1 IDEOGRAPHIC 
DESCRIPTION CHARACTER SUBTRACTION
 31F0..31FF    ; valid                                  # 3.2  KATAKANA LETTER 
SMALL KU..KATAKANA LETTER SMALL RO
 3200          ; disallowed_STD3_mapped ; 0028 1100 0029 #1.1  PARENTHESIZED 
HANGUL KIYEOK
 3201          ; disallowed_STD3_mapped ; 0028 1102 0029 #1.1  PARENTHESIZED 
HANGUL NIEUN
@@ -8450,7 +8447,9 @@ FFFE..FFFF    ; disallowed                             # 
1.1  <noncharacter-FFFE
 2B820..2CEA1  ; valid                                  # 8.0  CJK UNIFIED 
IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEA1
 2CEA2..2CEAF  ; disallowed                             # NA   
<reserved-2CEA2>..<reserved-2CEAF>
 2CEB0..2EBE0  ; valid                                  # 10.0 CJK UNIFIED 
IDEOGRAPH-2CEB0..CJK UNIFIED IDEOGRAPH-2EBE0
-2EBE1..2F7FF  ; disallowed                             # NA   
<reserved-2EBE1>..<reserved-2F7FF>
+2EBE1..2EBEF  ; disallowed                             # NA   
<reserved-2EBE1>..<reserved-2EBEF>
+2EBF0..2EE5D  ; valid                                  # 15.1 CJK UNIFIED 
IDEOGRAPH-2EBF0..CJK UNIFIED IDEOGRAPH-2EE5D
+2EE5E..2F7FF  ; disallowed                             # NA   
<reserved-2EE5E>..<reserved-2F7FF>
 2F800         ; mapped                 ; 4E3D          # 3.1  CJK 
COMPATIBILITY IDEOGRAPH-2F800
 2F801         ; mapped                 ; 4E38          # 3.1  CJK 
COMPATIBILITY IDEOGRAPH-2F801
 2F802         ; mapped                 ; 4E41          # 3.1  CJK 
COMPATIBILITY IDEOGRAPH-2F802
diff --git a/admin/unidata/NormalizationTest.txt 
b/admin/unidata/NormalizationTest.txt
index e75b4801c9b..2e88574243d 100644
--- a/admin/unidata/NormalizationTest.txt
+++ b/admin/unidata/NormalizationTest.txt
@@ -1,6 +1,6 @@
-# NormalizationTest-15.0.0.txt
-# Date: 2022-04-02, 01:29:09 GMT
-# © 2022 Unicode®, Inc.
+# NormalizationTest-15.1.0.txt
+# Date: 2023-01-05, 20:34:44 GMT
+# © 2023 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in 
the U.S. and other countries.
 # For terms of use, see https://www.unicode.org/terms_of_use.html
 #
diff --git a/admin/unidata/PropertyValueAliases.txt 
b/admin/unidata/PropertyValueAliases.txt
index 9346fcf03ee..6d308108818 100644
--- a/admin/unidata/PropertyValueAliases.txt
+++ b/admin/unidata/PropertyValueAliases.txt
@@ -1,6 +1,6 @@
-# PropertyValueAliases-15.0.0.txt
-# Date: 2022-08-05, 23:42:17 GMT
-# © 2022 Unicode®, Inc.
+# PropertyValueAliases-15.1.0.txt
+# Date: 2023-08-07, 15:21:34 GMT
+# © 2023 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in 
the U.S. and other countries.
 # For terms of use, see https://www.unicode.org/terms_of_use.html
 #
@@ -91,6 +91,7 @@ age; 12.1                             ; V12_1
 age; 13.0                             ; V13_0
 age; 14.0                             ; V14_0
 age; 15.0                             ; V15_0
+age; 15.1                             ; V15_1
 age; NA                               ; Unassigned
 
 # Alphabetic (Alpha)
@@ -208,6 +209,7 @@ blk; CJK_Ext_E                        ; 
CJK_Unified_Ideographs_Extension_E
 blk; CJK_Ext_F                        ; CJK_Unified_Ideographs_Extension_F
 blk; CJK_Ext_G                        ; CJK_Unified_Ideographs_Extension_G
 blk; CJK_Ext_H                        ; CJK_Unified_Ideographs_Extension_H
+blk; CJK_Ext_I                        ; CJK_Unified_Ideographs_Extension_I
 blk; CJK_Radicals_Sup                 ; CJK_Radicals_Supplement
 blk; CJK_Strokes                      ; CJK_Strokes
 blk; CJK_Symbols                      ; CJK_Symbols_And_Punctuation
@@ -817,6 +819,21 @@ IDSB; Y                               ; Yes                
              ; T
 IDST; N                               ; No                               ; F   
                             ; False
 IDST; Y                               ; Yes                              ; T   
                             ; True
 
+# IDS_Unary_Operator (IDSU)
+
+IDSU; N                               ; No                               ; F   
                             ; False
+IDSU; Y                               ; Yes                              ; T   
                             ; True
+
+# ID_Compat_Math_Continue (ID_Compat_Math_Continue)
+
+ID_Compat_Math_Continue; N            ; No                               ; F   
                             ; False
+ID_Compat_Math_Continue; Y            ; Yes                              ; T   
                             ; True
+
+# ID_Compat_Math_Start (ID_Compat_Math_Start)
+
+ID_Compat_Math_Start; N               ; No                               ; F   
                             ; False
+ID_Compat_Math_Start; Y               ; Yes                              ; T   
                             ; True
+
 # ID_Continue (IDC)
 
 IDC; N                                ; No                               ; F   
                             ; False
@@ -836,6 +853,13 @@ IDS; Y                                ; Yes                
              ; T
 Ideo; N                               ; No                               ; F   
                             ; False
 Ideo; Y                               ; Yes                              ; T   
                             ; True
 
+# Indic_Conjunct_Break (InCB)
+
+InCB; Consonant                       ; Consonant
+InCB; Extend                          ; Extend
+InCB; Linker                          ; Linker
+InCB; None                            ; None
+
 # Indic_Positional_Category (InPC)
 
 InPC; Bottom                          ; Bottom
@@ -1074,7 +1098,10 @@ jt ; U                                ; Non_Joining
 # Line_Break (lb)
 
 lb ; AI                               ; Ambiguous
+lb ; AK                               ; Aksara
 lb ; AL                               ; Alphabetic
+lb ; AP                               ; Aksara_Prebase
+lb ; AS                               ; Aksara_Start
 lb ; B2                               ; Break_Both
 lb ; BA                               ; Break_After
 lb ; BB                               ; Break_Before
@@ -1112,6 +1139,8 @@ lb ; SA                               ; Complex_Context
 lb ; SG                               ; Surrogate
 lb ; SP                               ; Space
 lb ; SY                               ; Break_Symbols
+lb ; VF                               ; Virama_Final
+lb ; VI                               ; Virama
 lb ; WJ                               ; Word_Joiner
 lb ; XX                               ; Unknown
 lb ; ZW                               ; ZWSpace
@@ -1156,6 +1185,9 @@ NFKC_QC; M                            ; Maybe
 NFKC_QC; N                            ; No
 NFKC_QC; Y                            ; Yes
 
+# NFKC_Simple_Casefold (NFKC_SCF)
+
+
 # NFKD_Quick_Check (NFKD_QC)
 
 NFKD_QC; N                            ; No
diff --git a/admin/unidata/ScriptExtensions.txt 
b/admin/unidata/ScriptExtensions.txt
index 2f5a1727e33..23141fb8241 100644
--- a/admin/unidata/ScriptExtensions.txt
+++ b/admin/unidata/ScriptExtensions.txt
@@ -1,6 +1,6 @@
-# ScriptExtensions-15.0.0.txt
-# Date: 2022-02-02, 00:57:11 GMT
-# © 2022 Unicode®, Inc.
+# ScriptExtensions-15.1.0.txt
+# Date: 2023-02-01, 23:02:24 GMT
+# © 2023 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in 
the U.S. and other countries.
 # For terms of use, see https://www.unicode.org/terms_of_use.html
 #
@@ -136,20 +136,20 @@
 
 # ================================================
 
-# Script_Extensions=Arab Rohg
+# Script_Extensions=Arab Nkoo
 
-06D4          ; Arab Rohg # Po       ARABIC FULL STOP
+FD3E          ; Arab Nkoo # Pe       ORNATE LEFT PARENTHESIS
+FD3F          ; Arab Nkoo # Ps       ORNATE RIGHT PARENTHESIS
 
-# Total code points: 1
+# Total code points: 2
 
 # ================================================
 
-# Script_Extensions=Arab Nkoo
+# Script_Extensions=Arab Rohg
 
-FD3E          ; Arab Nkoo # Pe       ORNATE LEFT PARENTHESIS
-FD3F          ; Arab Nkoo # Ps       ORNATE RIGHT PARENTHESIS
+06D4          ; Arab Rohg # Po       ARABIC FULL STOP
 
-# Total code points: 2
+# Total code points: 1
 
 # ================================================
 
@@ -553,17 +553,17 @@ FF64..FF65    ; Bopo Hang Hani Hira Kana Yiii # Po   [2] 
HALFWIDTH IDEOGRAPHIC C
 
 # ================================================
 
-# Script_Extensions=Beng Deva Gran Knda Nand Orya Telu Tirh
+# Script_Extensions=Adlm Arab Mand Mani Ougr Phlp Rohg Sogd Syrc
 
-1CF2          ; Beng Deva Gran Knda Nand Orya Telu Tirh # Lo       VEDIC SIGN 
ARDHAVISARGA
+0640          ; Adlm Arab Mand Mani Ougr Phlp Rohg Sogd Syrc # Lm       ARABIC 
TATWEEL
 
 # Total code points: 1
 
 # ================================================
 
-# Script_Extensions=Adlm Arab Mand Mani Ougr Phlp Rohg Sogd Syrc
+# Script_Extensions=Beng Deva Gran Knda Mlym Nand Orya Sinh Telu Tirh
 
-0640          ; Adlm Arab Mand Mani Ougr Phlp Rohg Sogd Syrc # Lm       ARABIC 
TATWEEL
+1CF2          ; Beng Deva Gran Knda Mlym Nand Orya Sinh Telu Tirh # Lo       
VEDIC SIGN ARDHAVISARGA
 
 # Total code points: 1
 
@@ -572,10 +572,9 @@ FF64..FF65    ; Bopo Hang Hani Hira Kana Yiii # Po   [2] 
HALFWIDTH IDEOGRAPHIC C
 # Script_Extensions=Deva Dogr Gujr Guru Khoj Kthi Mahj Modi Sind Takr Tirh
 
 A836..A837    ; Deva Dogr Gujr Guru Khoj Kthi Mahj Modi Sind Takr Tirh # So   
[2] NORTH INDIC QUARTER MARK..NORTH INDIC PLACEHOLDER MARK
-A838          ; Deva Dogr Gujr Guru Khoj Kthi Mahj Modi Sind Takr Tirh # Sc    
   NORTH INDIC RUPEE MARK
 A839          ; Deva Dogr Gujr Guru Khoj Kthi Mahj Modi Sind Takr Tirh # So    
   NORTH INDIC QUANTITY MARK
 
-# Total code points: 4
+# Total code points: 3
 
 # ================================================
 
@@ -587,6 +586,14 @@ A839          ; Deva Dogr Gujr Guru Khoj Kthi Mahj Modi 
Sind Takr Tirh # So
 
 # ================================================
 
+# Script_Extensions=Deva Dogr Gujr Guru Khoj Kthi Mahj Modi Shrd Sind Takr Tirh
+
+A838          ; Deva Dogr Gujr Guru Khoj Kthi Mahj Modi Shrd Sind Takr Tirh # 
Sc       NORTH INDIC RUPEE MARK
+
+# Total code points: 1
+
+# ================================================
+
 # Script_Extensions=Beng Deva Gran Gujr Guru Knda Latn Mlym Orya Shrd Taml 
Telu Tirh
 
 0951          ; Beng Deva Gran Gujr Guru Knda Latn Mlym Orya Shrd Taml Telu 
Tirh # Mn       DEVANAGARI STRESS SIGN UDATTA
@@ -595,17 +602,17 @@ A839          ; Deva Dogr Gujr Guru Khoj Kthi Mahj Modi 
Sind Takr Tirh # So
 
 # ================================================
 
-# Script_Extensions=Deva Dogr Gujr Guru Khoj Knda Kthi Mahj Modi Nand Sind 
Takr Tirh
+# Script_Extensions=Deva Dogr Gujr Guru Khoj Knda Kthi Mahj Modi Nand Shrd 
Sind Takr Tirh
 
-A833..A835    ; Deva Dogr Gujr Guru Khoj Knda Kthi Mahj Modi Nand Sind Takr 
Tirh # No   [3] NORTH INDIC FRACTION ONE SIXTEENTH..NORTH INDIC FRACTION THREE 
SIXTEENTHS
+A833..A835    ; Deva Dogr Gujr Guru Khoj Knda Kthi Mahj Modi Nand Shrd Sind 
Takr Tirh # No   [3] NORTH INDIC FRACTION ONE SIXTEENTH..NORTH INDIC FRACTION 
THREE SIXTEENTHS
 
 # Total code points: 3
 
 # ================================================
 
-# Script_Extensions=Deva Dogr Gujr Guru Khoj Knda Kthi Mahj Mlym Modi Nand 
Sind Takr Tirh
+# Script_Extensions=Deva Dogr Gujr Guru Khoj Knda Kthi Mahj Mlym Modi Nand 
Shrd Sind Takr Tirh
 
-A830..A832    ; Deva Dogr Gujr Guru Khoj Knda Kthi Mahj Mlym Modi Nand Sind 
Takr Tirh # No   [3] NORTH INDIC FRACTION ONE QUARTER..NORTH INDIC FRACTION 
THREE QUARTERS
+A830..A832    ; Deva Dogr Gujr Guru Khoj Knda Kthi Mahj Mlym Modi Nand Shrd 
Sind Takr Tirh # No   [3] NORTH INDIC FRACTION ONE QUARTER..NORTH INDIC 
FRACTION THREE QUARTERS
 
 # Total code points: 3
 
diff --git a/admin/unidata/Scripts.txt b/admin/unidata/Scripts.txt
index 2b138bffb88..0b3f717cb20 100644
--- a/admin/unidata/Scripts.txt
+++ b/admin/unidata/Scripts.txt
@@ -1,6 +1,6 @@
-# Scripts-15.0.0.txt
-# Date: 2022-04-26, 23:15:02 GMT
-# © 2022 Unicode®, Inc.
+# Scripts-15.1.0.txt
+# Date: 2023-07-28, 16:01:07 GMT
+# © 2023 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in 
the U.S. and other countries.
 # For terms of use, see https://www.unicode.org/terms_of_use.html
 #
@@ -357,7 +357,7 @@
 2E5B          ; Common # Ps       BOTTOM HALF LEFT PARENTHESIS
 2E5C          ; Common # Pe       BOTTOM HALF RIGHT PARENTHESIS
 2E5D          ; Common # Pd       OBLIQUE HYPHEN
-2FF0..2FFB    ; Common # So  [12] IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO 
RIGHT..IDEOGRAPHIC DESCRIPTION CHARACTER OVERLAID
+2FF0..2FFF    ; Common # So  [16] IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO 
RIGHT..IDEOGRAPHIC DESCRIPTION CHARACTER ROTATION
 3000          ; Common # Zs       IDEOGRAPHIC SPACE
 3001..3003    ; Common # Po   [3] IDEOGRAPHIC COMMA..DITTO MARK
 3004          ; Common # So       JAPANESE INDUSTRIAL STANDARD SYMBOL
@@ -399,6 +399,7 @@
 3192..3195    ; Common # No   [4] IDEOGRAPHIC ANNOTATION ONE MARK..IDEOGRAPHIC 
ANNOTATION FOUR MARK
 3196..319F    ; Common # So  [10] IDEOGRAPHIC ANNOTATION TOP MARK..IDEOGRAPHIC 
ANNOTATION MAN MARK
 31C0..31E3    ; Common # So  [36] CJK STROKE T..CJK STROKE Q
+31EF          ; Common # So       IDEOGRAPHIC DESCRIPTION CHARACTER SUBTRACTION
 3220..3229    ; Common # No  [10] PARENTHESIZED IDEOGRAPH ONE..PARENTHESIZED 
IDEOGRAPH TEN
 322A..3247    ; Common # So  [30] PARENTHESIZED IDEOGRAPH MOON..CIRCLED 
IDEOGRAPH KOTO
 3248..324F    ; Common # No   [8] CIRCLED NUMBER TEN ON BLACK SQUARE..CIRCLED 
NUMBER EIGHTY ON BLACK SQUARE
@@ -629,7 +630,7 @@ FFFC..FFFD    ; Common # So   [2] OBJECT REPLACEMENT 
CHARACTER..REPLACEMENT CHAR
 E0001         ; Common # Cf       LANGUAGE TAG
 E0020..E007F  ; Common # Cf  [96] TAG SPACE..CANCEL TAG
 
-# Total code points: 8301
+# Total code points: 8306
 
 # ================================================
 
@@ -1593,11 +1594,12 @@ FA70..FAD9    ; Han # Lo [106] CJK COMPATIBILITY 
IDEOGRAPH-FA70..CJK COMPATIBILI
 2B740..2B81D  ; Han # Lo [222] CJK UNIFIED IDEOGRAPH-2B740..CJK UNIFIED 
IDEOGRAPH-2B81D
 2B820..2CEA1  ; Han # Lo [5762] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED 
IDEOGRAPH-2CEA1
 2CEB0..2EBE0  ; Han # Lo [7473] CJK UNIFIED IDEOGRAPH-2CEB0..CJK UNIFIED 
IDEOGRAPH-2EBE0
+2EBF0..2EE5D  ; Han # Lo [622] CJK UNIFIED IDEOGRAPH-2EBF0..CJK UNIFIED 
IDEOGRAPH-2EE5D
 2F800..2FA1D  ; Han # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK 
COMPATIBILITY IDEOGRAPH-2FA1D
 30000..3134A  ; Han # Lo [4939] CJK UNIFIED IDEOGRAPH-30000..CJK UNIFIED 
IDEOGRAPH-3134A
 31350..323AF  ; Han # Lo [4192] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED 
IDEOGRAPH-323AF
 
-# Total code points: 98408
+# Total code points: 99030
 
 # ================================================
 
diff --git a/admin/unidata/SpecialCasing.txt b/admin/unidata/SpecialCasing.txt
index 08d04fa9421..de08450a6b9 100644
--- a/admin/unidata/SpecialCasing.txt
+++ b/admin/unidata/SpecialCasing.txt
@@ -1,6 +1,6 @@
-# SpecialCasing-15.0.0.txt
-# Date: 2022-02-02, 23:35:52 GMT
-# © 2022 Unicode®, Inc.
+# SpecialCasing-15.1.0.txt
+# Date: 2023-01-05, 20:35:03 GMT
+# © 2023 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in 
the U.S. and other countries.
 # For terms of use, see https://www.unicode.org/terms_of_use.html
 #
diff --git a/admin/unidata/UnicodeData.txt b/admin/unidata/UnicodeData.txt
index ea963a7162c..bdcc41850d7 100644
--- a/admin/unidata/UnicodeData.txt
+++ b/admin/unidata/UnicodeData.txt
@@ -11231,6 +11231,10 @@
 2FF9;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM UPPER 
RIGHT;So;0;ON;;;;;N;;;;;
 2FFA;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM LOWER 
LEFT;So;0;ON;;;;;N;;;;;
 2FFB;IDEOGRAPHIC DESCRIPTION CHARACTER OVERLAID;So;0;ON;;;;;N;;;;;
+2FFC;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM RIGHT;So;0;ON;;;;;N;;;;;
+2FFD;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM LOWER 
RIGHT;So;0;ON;;;;;N;;;;;
+2FFE;IDEOGRAPHIC DESCRIPTION CHARACTER HORIZONTAL REFLECTION;So;0;ON;;;;;N;;;;;
+2FFF;IDEOGRAPHIC DESCRIPTION CHARACTER ROTATION;So;0;ON;;;;;N;;;;;
 3000;IDEOGRAPHIC SPACE;Zs;0;WS;<wide> 0020;;;;N;;;;;
 3001;IDEOGRAPHIC COMMA;Po;0;ON;;;;;N;;;;;
 3002;IDEOGRAPHIC FULL STOP;Po;0;ON;;;;;N;IDEOGRAPHIC PERIOD;;;;
@@ -11705,6 +11709,7 @@
 31E1;CJK STROKE HZZZG;So;0;ON;;;;;N;;;;;
 31E2;CJK STROKE PG;So;0;ON;;;;;N;;;;;
 31E3;CJK STROKE Q;So;0;ON;;;;;N;;;;;
+31EF;IDEOGRAPHIC DESCRIPTION CHARACTER SUBTRACTION;So;0;ON;;;;;N;;;;;
 31F0;KATAKANA LETTER SMALL KU;Lo;0;L;;;;;N;;;;;
 31F1;KATAKANA LETTER SMALL SI;Lo;0;L;;;;;N;;;;;
 31F2;KATAKANA LETTER SMALL SU;Lo;0;L;;;;;N;;;;;
@@ -34035,6 +34040,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 2CEA1;<CJK Ideograph Extension E, Last>;Lo;0;L;;;;;N;;;;;
 2CEB0;<CJK Ideograph Extension F, First>;Lo;0;L;;;;;N;;;;;
 2EBE0;<CJK Ideograph Extension F, Last>;Lo;0;L;;;;;N;;;;;
+2EBF0;<CJK Ideograph Extension I, First>;Lo;0;L;;;;;N;;;;;
+2EE5D;<CJK Ideograph Extension I, Last>;Lo;0;L;;;;;N;;;;;
 2F800;CJK COMPATIBILITY IDEOGRAPH-2F800;Lo;0;L;4E3D;;;;N;;;;;
 2F801;CJK COMPATIBILITY IDEOGRAPH-2F801;Lo;0;L;4E38;;;;N;;;;;
 2F802;CJK COMPATIBILITY IDEOGRAPH-2F802;Lo;0;L;4E41;;;;N;;;;;
diff --git a/admin/unidata/confusables.txt b/admin/unidata/confusables.txt
index 24b61d519af..5e056ed5a35 100644
--- a/admin/unidata/confusables.txt
+++ b/admin/unidata/confusables.txt
@@ -1,11 +1,11 @@
 # confusables.txt
-# Date: 2022-08-26, 16:49:08 GMT
-# © 2022 Unicode®, Inc.
+# Date: 2023-08-11, 17:46:40 GMT
+# © 2023 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in 
the U.S. and other countries.
 # For terms of use, see https://www.unicode.org/terms_of_use.html
 #
 # Unicode Security Mechanisms for UTS #39
-# Version: 15.0.0
+# Version: 15.1.0
 #
 # For documentation and usage, see https://www.unicode.org/reports/tr39
 #
@@ -349,8 +349,8 @@ A4FA ;      002E 002E ;     MA      # ( ꓺ → .. ) LISU 
LETTER TONE MYA CYA → FULL STOP, F
 
 A6F4 ; A6F3 A6F3 ;     MA      #* ( ꛴ → ꛳꛳ ) BAMUM COLON → BAMUM FULL STOP, 
BAMUM FULL STOP    #
 
-30FB ; 00B7 ;  MA      #* ( ・ → · ) KATAKANA MIDDLE DOT → MIDDLE DOT   # →•→
-FF65 ; 00B7 ;  MA      #* ( ・ → · ) HALFWIDTH KATAKANA MIDDLE DOT → MIDDLE DOT 
# →•→
+30FB ; 00B7 ;  MA      # ( ・ → · ) KATAKANA MIDDLE DOT → MIDDLE DOT    # →•→
+FF65 ; 00B7 ;  MA      # ( ・ → · ) HALFWIDTH KATAKANA MIDDLE DOT → MIDDLE DOT  
# →•→
 16EB ; 00B7 ;  MA      #* ( ᛫ → · ) RUNIC SINGLE PUNCTUATION → MIDDLE DOT      
#
 0387 ; 00B7 ;  MA      # ( · → · ) GREEK ANO TELEIA → MIDDLE DOT       #
 2E31 ; 00B7 ;  MA      #* ( ⸱ → · ) WORD SEPARATOR MIDDLE DOT → MIDDLE DOT     
#
diff --git a/admin/unidata/copyright.html b/admin/unidata/copyright.html
index 567c54e72ac..fe6dd16903e 100644
--- a/admin/unidata/copyright.html
+++ b/admin/unidata/copyright.html
@@ -13,7 +13,7 @@
 <title>Unicode Terms of Use</title>
 <link rel="stylesheet" type="text/css"
 
-href="https://www.unicode.org/webscripts/standard_styles.css";>
+href="http://www.unicode.org/webscripts/standard_styles.css";>
 
 <style type="text/css">
 pre {
@@ -32,8 +32,8 @@ pre {
       <td colspan="2">
       <table width="100%" border="0" cellpadding="0" cellspacing="0">
         <tr>
-          <td class="icon" style="width:38px; height:35px"><a 
href="https://www.unicode.org/";><img border="0"
-      src="https://www.unicode.org/webscripts/logo60s2.gif"; align="middle" 
alt="[Unicode]" width="34" height="33"></a></td>
+          <td class="icon" style="width:38px; height:35px"><a 
href="http://www.unicode.org/";><img border="0"
+      src="http://www.unicode.org/webscripts/logo60s2.gif"; align="middle" 
alt="[Unicode]" width="34" height="33"></a></td>
           <td class="icon" style="vertical-align:middle;"> &nbsp;<a class="bar"
           href="https://www.unicode.org/copyright.html";><font size="3">Terms 
of Use</font></a></td>
           <td class="bar"><a href="https://www.unicode.org/main.html"; 
class="bar">Tech Site</a>
@@ -112,13 +112,13 @@ pre {
 
             <p>For the general privacy policy governing access to this site, 
see
             the&nbsp;
-            <a href="https://www.unicode.org/policies/privacy_policy.html";>
+            <a href="http://www.unicode.org/policies/privacy_policy.html";>
             Unicode Privacy Policy</a>.</p>
 
             <ol type="A">
               <li><u><a name="1"></a>Unicode Copyright</u>
               <ol>
-                <li>Copyright © 1991-2022 Unicode, Inc. All rights 
reserved.</li>
+                <li>Copyright © 1991-2023 Unicode, Inc. All rights 
reserved.</li>
               </ol>
               </li>
 
@@ -158,7 +158,7 @@ http://site.icu-project.org/download/
                 specifications of rights and restrictions of use. For the book
                 editions (Unicode 5.0 and earlier), these are found on the back
                 of the
-                                <a 
href="https://www.unicode.org/versions/Unicode5.0.0/Title.pdf";>title 
page</a>.</li>
+                               <a 
href="http://www.unicode.org/versions/Unicode5.0.0/Title.pdf";>title 
page</a>.</li>
                 <li>
                 The Unicode PDF <a 
href="https://www.unicode.org/charts/";>online code charts</a> carry specific 
restrictions. Those restrictions are incorporated as the
                 first page of each PDF code chart.</li>
@@ -224,7 +224,7 @@ http://site.icu-project.org/download/
               <li><u><a name="5"></a>Trademarks &amp; Logos</u>
                 <ol>
                 <li>The Unicode Word Mark and the Unicode Logo are trademarks 
of Unicode, Inc.  “The Unicode Consortium” and “Unicode, Inc.” are trade names 
of Unicode, Inc.  Use of the information and materials found on this website 
indicates your acknowledgement of Unicode, Inc.’s exclusive worldwide rights in 
the Unicode Word Mark, the Unicode Logo, and the Unicode trade names.</li>
-<li><a href="https://www.unicode.org/policies/logo_policy.html";>The Unicode 
Consortium Name and Trademark Usage Policy</a> (“Trademark Policy”) are 
incorporated herein by reference and you agree to abide by the provisions of 
the Trademark Policy, which may be changed from time to time in the sole 
discretion of Unicode, Inc.</li>
+<li><a href="http://www.unicode.org/policies/logo_policy.html";>The Unicode 
Consortium Name and Trademark Usage Policy</a> (“Trademark Policy”) are 
incorporated herein by reference and you agree to abide by the provisions of 
the Trademark Policy, which may be changed from time to time in the sole 
discretion of Unicode, Inc.</li>
 <li>All third party trademarks referenced herein are the property of their 
respective owners.</li>
               </ol>
               </li>
@@ -270,15 +270,15 @@ http://site.icu-project.org/download/
               <center>
               <table cellspacing="0" cellpadding="0" border="0" id="table2">
                 <tr>
-                  <td><a href="https://www.unicode.org/copyright.html";>
-                  <img src="https://www.unicode.org/img/hb_notice.gif";
+                  <td><a href="http://www.unicode.org/copyright.html";>
+                  <img src="http://www.unicode.org/img/hb_notice.gif";
                   border="0" alt="Access to Copyright and terms of use"
                   width="216" height="50"></a></td>
                 </tr>
               </table>
 
                 <script language="Javascript" type="text/javascript"
-              src="https://www.unicode.org/webscripts/lastModified.js";>
+              src="http://www.unicode.org/webscripts/lastModified.js";>
                 </script>
 
               </center>
diff --git a/admin/unidata/emoji-data.txt b/admin/unidata/emoji-data.txt
index 7942fc89a35..ab9c04ff056 100644
--- a/admin/unidata/emoji-data.txt
+++ b/admin/unidata/emoji-data.txt
@@ -1,11 +1,11 @@
 # emoji-data.txt
-# Date: 2022-08-02, 00:26:10 GMT
-# © 2022 Unicode®, Inc.
+# Date: 2023-02-01, 02:22:54 GMT
+# © 2023 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in 
the U.S. and other countries.
 # For terms of use, see https://www.unicode.org/terms_of_use.html
 #
 # Emoji Data for UTS #51
-# Used with Emoji Version 15.0 and subsequent minor revisions (if any)
+# Used with Emoji Version 15.1 and subsequent minor revisions (if any)
 #
 # For documentation and usage, see https://www.unicode.org/reports/tr51
 #
diff --git a/admin/unidata/emoji-sequences.txt 
b/admin/unidata/emoji-sequences.txt
index ffd40668117..dfeae158edb 100644
--- a/admin/unidata/emoji-sequences.txt
+++ b/admin/unidata/emoji-sequences.txt
@@ -1,11 +1,11 @@
 # emoji-sequences.txt
-# Date: 2022-08-15, 23:13:41 GMT
-# © 2022 Unicode®, Inc.
+# Date: 2023-06-05, 21:39:54 GMT
+# © 2023 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in 
the U.S. and other countries.
 # For terms of use, see https://www.unicode.org/terms_of_use.html
 #
 # Emoji Sequence Data for UTS #51
-# Version: 15.0
+# Version: 15.1
 #
 # For documentation and usage, see https://www.unicode.org/reports/tr51
 #
@@ -38,7 +38,6 @@
 
 # Basic_Emoji
 
-
 231A..231B    ; Basic_Emoji                  ; watch..hourglass done           
                               # E0.6   [2] (⌚..⌛)
 23E9..23EC    ; Basic_Emoji                  ; fast-forward button..fast down 
button                          # E0.6   [4] (⏩..⏬)
 23F0          ; Basic_Emoji                  ; alarm clock                     
                               # E0.6   [1] (⏰)
@@ -534,7 +533,6 @@
 
 # Emoji_Keycap_Sequence
 
-
 0023 FE0F 20E3; Emoji_Keycap_Sequence        ; keycap: \x{23}                  
                               # E0.6   [1] (#️⃣)
 002A FE0F 20E3; Emoji_Keycap_Sequence        ; keycap: *                       
                               # E2.0   [1] (*️⃣)
 0030 FE0F 20E3; Emoji_Keycap_Sequence        ; keycap: 0                       
                               # E0.6   [1] (0️⃣)
@@ -553,8 +551,7 @@
 # ================================================
 
 # RGI_Emoji_Flag_Sequence: This list does not include deprecated or 
macroregion flags, except for UN and EU.
-# See Annex B of TR51 for more information.
-
+# See Annex B of UTS #51 for more information.
 
 1F1E6 1F1E8   ; RGI_Emoji_Flag_Sequence      ; flag: Ascension Island          
                               # E2.0   [1] (🇦🇨)
 1F1E6 1F1E9   ; RGI_Emoji_Flag_Sequence      ; flag: Andorra                   
                               # E2.0   [1] (🇦🇩)
@@ -787,7 +784,7 @@
 1F1F9 1F1F2   ; RGI_Emoji_Flag_Sequence      ; flag: Turkmenistan              
                               # E2.0   [1] (🇹🇲)
 1F1F9 1F1F3   ; RGI_Emoji_Flag_Sequence      ; flag: Tunisia                   
                               # E2.0   [1] (🇹🇳)
 1F1F9 1F1F4   ; RGI_Emoji_Flag_Sequence      ; flag: Tonga                     
                               # E2.0   [1] (🇹🇴)
-1F1F9 1F1F7   ; RGI_Emoji_Flag_Sequence      ; flag: Turkey                    
                               # E2.0   [1] (🇹🇷)
+1F1F9 1F1F7   ; RGI_Emoji_Flag_Sequence      ; flag: Türkiye                   
                               # E2.0   [1] (🇹🇷)
 1F1F9 1F1F9   ; RGI_Emoji_Flag_Sequence      ; flag: Trinidad & Tobago         
                               # E2.0   [1] (🇹🇹)
 1F1F9 1F1FB   ; RGI_Emoji_Flag_Sequence      ; flag: Tuvalu                    
                               # E2.0   [1] (🇹🇻)
 1F1F9 1F1FC   ; RGI_Emoji_Flag_Sequence      ; flag: Taiwan                    
                               # E2.0   [1] (🇹🇼)
@@ -819,8 +816,7 @@
 
 # ================================================
 
-# RGI_Emoji_Tag_Sequence: See Annex C of TR51 for more information.
-
+# RGI_Emoji_Tag_Sequence: See Annex C of UTS #51 for more information.
 
 1F3F4 E0067 E0062 E0065 E006E E0067 E007F; RGI_Emoji_Tag_Sequence; flag: 
England                              # E5.0   [1] (🏴󠁧󠁢󠁥󠁮󠁧󠁿)
 1F3F4 E0067 E0062 E0073 E0063 E0074 E007F; RGI_Emoji_Tag_Sequence; flag: 
Scotland                             # E5.0   [1] (🏴󠁧󠁢󠁳󠁣󠁴󠁿)
@@ -832,7 +828,6 @@
 
 # RGI_Emoji_Modifier_Sequence
 
-
 261D 1F3FB    ; RGI_Emoji_Modifier_Sequence  ; index pointing up: light skin 
tone                             # E1.0   [1] (☝🏻)
 261D 1F3FC    ; RGI_Emoji_Modifier_Sequence  ; index pointing up: medium-light 
skin tone                      # E1.0   [1] (☝🏼)
 261D 1F3FD    ; RGI_Emoji_Modifier_Sequence  ; index pointing up: medium skin 
tone                            # E1.0   [1] (☝🏽)
diff --git a/admin/unidata/emoji-test.txt b/admin/unidata/emoji-test.txt
index bc8b52c2fb4..1f50b23fa24 100644
--- a/admin/unidata/emoji-test.txt
+++ b/admin/unidata/emoji-test.txt
@@ -1,11 +1,11 @@
 # emoji-test.txt
-# Date: 2022-08-12, 20:24:39 GMT
-# © 2022 Unicode®, Inc.
+# Date: 2023-06-05, 21:39:54 GMT
+# © 2023 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in 
the U.S. and other countries.
 # For terms of use, see https://www.unicode.org/terms_of_use.html
 #
 # Emoji Keyboard/Display Test Data for UTS #51
-# Version: 15.0
+# Version: 15.1
 #
 # For documentation and usage, see https://www.unicode.org/reports/tr51
 #
@@ -93,6 +93,10 @@
 1F62E 200D 1F4A8                                       ; fully-qualified     # 
😮‍💨 E13.1 face exhaling
 1F925                                                  ; fully-qualified     # 
🤥 E3.0 lying face
 1FAE8                                                  ; fully-qualified     # 
🫨 E15.0 shaking face
+1F642 200D 2194 FE0F                                   ; fully-qualified     # 
🙂‍↔️ E15.1 head shaking horizontally
+1F642 200D 2194                                        ; minimally-qualified # 
🙂‍↔ E15.1 head shaking horizontally
+1F642 200D 2195 FE0F                                   ; fully-qualified     # 
🙂‍↕️ E15.1 head shaking vertically
+1F642 200D 2195                                        ; minimally-qualified # 
🙂‍↕ E15.1 head shaking vertically
 
 # subgroup: face-sleepy
 1F60C                                                  ; fully-qualified     # 
😌 E0.6 relieved face
@@ -244,8 +248,8 @@
 1F4AD                                                  ; fully-qualified     # 
💭 E1.0 thought balloon
 1F4A4                                                  ; fully-qualified     # 
💤 E0.6 ZZZ
 
-# Smileys & Emotion subtotal:          180
-# Smileys & Emotion subtotal:          180     w/o modifiers
+# Smileys & Emotion subtotal:          184
+# Smileys & Emotion subtotal:          184     w/o modifiers
 
 # group: People & Body
 
@@ -2065,6 +2069,66 @@
 1F6B6 1F3FE 200D 2640                                  ; minimally-qualified # 
🚶🏾‍♀ E4.0 woman walking: medium-dark skin tone
 1F6B6 1F3FF 200D 2640 FE0F                             ; fully-qualified     # 
🚶🏿‍♀️ E4.0 woman walking: dark skin tone
 1F6B6 1F3FF 200D 2640                                  ; minimally-qualified # 
🚶🏿‍♀ E4.0 woman walking: dark skin tone
+1F6B6 200D 27A1 FE0F                                   ; fully-qualified     # 
🚶‍➡️ E15.1 person walking facing right
+1F6B6 200D 27A1                                        ; minimally-qualified # 
🚶‍➡ E15.1 person walking facing right
+1F6B6 1F3FB 200D 27A1 FE0F                             ; fully-qualified     # 
🚶🏻‍➡️ E15.1 person walking facing right: light skin tone
+1F6B6 1F3FB 200D 27A1                                  ; minimally-qualified # 
🚶🏻‍➡ E15.1 person walking facing right: light skin tone
+1F6B6 1F3FC 200D 27A1 FE0F                             ; fully-qualified     # 
🚶🏼‍➡️ E15.1 person walking facing right: medium-light skin tone
+1F6B6 1F3FC 200D 27A1                                  ; minimally-qualified # 
🚶🏼‍➡ E15.1 person walking facing right: medium-light skin tone
+1F6B6 1F3FD 200D 27A1 FE0F                             ; fully-qualified     # 
🚶🏽‍➡️ E15.1 person walking facing right: medium skin tone
+1F6B6 1F3FD 200D 27A1                                  ; minimally-qualified # 
🚶🏽‍➡ E15.1 person walking facing right: medium skin tone
+1F6B6 1F3FE 200D 27A1 FE0F                             ; fully-qualified     # 
🚶🏾‍➡️ E15.1 person walking facing right: medium-dark skin tone
+1F6B6 1F3FE 200D 27A1                                  ; minimally-qualified # 
🚶🏾‍➡ E15.1 person walking facing right: medium-dark skin tone
+1F6B6 1F3FF 200D 27A1 FE0F                             ; fully-qualified     # 
🚶🏿‍➡️ E15.1 person walking facing right: dark skin tone
+1F6B6 1F3FF 200D 27A1                                  ; minimally-qualified # 
🚶🏿‍➡ E15.1 person walking facing right: dark skin tone
+1F6B6 200D 2640 FE0F 200D 27A1 FE0F                    ; fully-qualified     # 
🚶‍♀️‍➡️ E15.1 woman walking facing right
+1F6B6 200D 2640 200D 27A1 FE0F                         ; minimally-qualified # 
🚶‍♀‍➡️ E15.1 woman walking facing right
+1F6B6 200D 2640 FE0F 200D 27A1                         ; minimally-qualified # 
🚶‍♀️‍➡ E15.1 woman walking facing right
+1F6B6 200D 2640 200D 27A1                              ; minimally-qualified # 
🚶‍♀‍➡ E15.1 woman walking facing right
+1F6B6 1F3FB 200D 2640 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🚶🏻‍♀️‍➡️ E15.1 woman walking facing right: light skin tone
+1F6B6 1F3FB 200D 2640 200D 27A1 FE0F                   ; minimally-qualified # 
🚶🏻‍♀‍➡️ E15.1 woman walking facing right: light skin tone
+1F6B6 1F3FB 200D 2640 FE0F 200D 27A1                   ; minimally-qualified # 
🚶🏻‍♀️‍➡ E15.1 woman walking facing right: light skin tone
+1F6B6 1F3FB 200D 2640 200D 27A1                        ; minimally-qualified # 
🚶🏻‍♀‍➡ E15.1 woman walking facing right: light skin tone
+1F6B6 1F3FC 200D 2640 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🚶🏼‍♀️‍➡️ E15.1 woman walking facing right: medium-light skin tone
+1F6B6 1F3FC 200D 2640 200D 27A1 FE0F                   ; minimally-qualified # 
🚶🏼‍♀‍➡️ E15.1 woman walking facing right: medium-light skin tone
+1F6B6 1F3FC 200D 2640 FE0F 200D 27A1                   ; minimally-qualified # 
🚶🏼‍♀️‍➡ E15.1 woman walking facing right: medium-light skin tone
+1F6B6 1F3FC 200D 2640 200D 27A1                        ; minimally-qualified # 
🚶🏼‍♀‍➡ E15.1 woman walking facing right: medium-light skin tone
+1F6B6 1F3FD 200D 2640 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🚶🏽‍♀️‍➡️ E15.1 woman walking facing right: medium skin tone
+1F6B6 1F3FD 200D 2640 200D 27A1 FE0F                   ; minimally-qualified # 
🚶🏽‍♀‍➡️ E15.1 woman walking facing right: medium skin tone
+1F6B6 1F3FD 200D 2640 FE0F 200D 27A1                   ; minimally-qualified # 
🚶🏽‍♀️‍➡ E15.1 woman walking facing right: medium skin tone
+1F6B6 1F3FD 200D 2640 200D 27A1                        ; minimally-qualified # 
🚶🏽‍♀‍➡ E15.1 woman walking facing right: medium skin tone
+1F6B6 1F3FE 200D 2640 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🚶🏾‍♀️‍➡️ E15.1 woman walking facing right: medium-dark skin tone
+1F6B6 1F3FE 200D 2640 200D 27A1 FE0F                   ; minimally-qualified # 
🚶🏾‍♀‍➡️ E15.1 woman walking facing right: medium-dark skin tone
+1F6B6 1F3FE 200D 2640 FE0F 200D 27A1                   ; minimally-qualified # 
🚶🏾‍♀️‍➡ E15.1 woman walking facing right: medium-dark skin tone
+1F6B6 1F3FE 200D 2640 200D 27A1                        ; minimally-qualified # 
🚶🏾‍♀‍➡ E15.1 woman walking facing right: medium-dark skin tone
+1F6B6 1F3FF 200D 2640 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🚶🏿‍♀️‍➡️ E15.1 woman walking facing right: dark skin tone
+1F6B6 1F3FF 200D 2640 200D 27A1 FE0F                   ; minimally-qualified # 
🚶🏿‍♀‍➡️ E15.1 woman walking facing right: dark skin tone
+1F6B6 1F3FF 200D 2640 FE0F 200D 27A1                   ; minimally-qualified # 
🚶🏿‍♀️‍➡ E15.1 woman walking facing right: dark skin tone
+1F6B6 1F3FF 200D 2640 200D 27A1                        ; minimally-qualified # 
🚶🏿‍♀‍➡ E15.1 woman walking facing right: dark skin tone
+1F6B6 200D 2642 FE0F 200D 27A1 FE0F                    ; fully-qualified     # 
🚶‍♂️‍➡️ E15.1 man walking facing right
+1F6B6 200D 2642 200D 27A1 FE0F                         ; minimally-qualified # 
🚶‍♂‍➡️ E15.1 man walking facing right
+1F6B6 200D 2642 FE0F 200D 27A1                         ; minimally-qualified # 
🚶‍♂️‍➡ E15.1 man walking facing right
+1F6B6 200D 2642 200D 27A1                              ; minimally-qualified # 
🚶‍♂‍➡ E15.1 man walking facing right
+1F6B6 1F3FB 200D 2642 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🚶🏻‍♂️‍➡️ E15.1 man walking facing right: light skin tone
+1F6B6 1F3FB 200D 2642 200D 27A1 FE0F                   ; minimally-qualified # 
🚶🏻‍♂‍➡️ E15.1 man walking facing right: light skin tone
+1F6B6 1F3FB 200D 2642 FE0F 200D 27A1                   ; minimally-qualified # 
🚶🏻‍♂️‍➡ E15.1 man walking facing right: light skin tone
+1F6B6 1F3FB 200D 2642 200D 27A1                        ; minimally-qualified # 
🚶🏻‍♂‍➡ E15.1 man walking facing right: light skin tone
+1F6B6 1F3FC 200D 2642 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🚶🏼‍♂️‍➡️ E15.1 man walking facing right: medium-light skin tone
+1F6B6 1F3FC 200D 2642 200D 27A1 FE0F                   ; minimally-qualified # 
🚶🏼‍♂‍➡️ E15.1 man walking facing right: medium-light skin tone
+1F6B6 1F3FC 200D 2642 FE0F 200D 27A1                   ; minimally-qualified # 
🚶🏼‍♂️‍➡ E15.1 man walking facing right: medium-light skin tone
+1F6B6 1F3FC 200D 2642 200D 27A1                        ; minimally-qualified # 
🚶🏼‍♂‍➡ E15.1 man walking facing right: medium-light skin tone
+1F6B6 1F3FD 200D 2642 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🚶🏽‍♂️‍➡️ E15.1 man walking facing right: medium skin tone
+1F6B6 1F3FD 200D 2642 200D 27A1 FE0F                   ; minimally-qualified # 
🚶🏽‍♂‍➡️ E15.1 man walking facing right: medium skin tone
+1F6B6 1F3FD 200D 2642 FE0F 200D 27A1                   ; minimally-qualified # 
🚶🏽‍♂️‍➡ E15.1 man walking facing right: medium skin tone
+1F6B6 1F3FD 200D 2642 200D 27A1                        ; minimally-qualified # 
🚶🏽‍♂‍➡ E15.1 man walking facing right: medium skin tone
+1F6B6 1F3FE 200D 2642 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🚶🏾‍♂️‍➡️ E15.1 man walking facing right: medium-dark skin tone
+1F6B6 1F3FE 200D 2642 200D 27A1 FE0F                   ; minimally-qualified # 
🚶🏾‍♂‍➡️ E15.1 man walking facing right: medium-dark skin tone
+1F6B6 1F3FE 200D 2642 FE0F 200D 27A1                   ; minimally-qualified # 
🚶🏾‍♂️‍➡ E15.1 man walking facing right: medium-dark skin tone
+1F6B6 1F3FE 200D 2642 200D 27A1                        ; minimally-qualified # 
🚶🏾‍♂‍➡ E15.1 man walking facing right: medium-dark skin tone
+1F6B6 1F3FF 200D 2642 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🚶🏿‍♂️‍➡️ E15.1 man walking facing right: dark skin tone
+1F6B6 1F3FF 200D 2642 200D 27A1 FE0F                   ; minimally-qualified # 
🚶🏿‍♂‍➡️ E15.1 man walking facing right: dark skin tone
+1F6B6 1F3FF 200D 2642 FE0F 200D 27A1                   ; minimally-qualified # 
🚶🏿‍♂️‍➡ E15.1 man walking facing right: dark skin tone
+1F6B6 1F3FF 200D 2642 200D 27A1                        ; minimally-qualified # 
🚶🏿‍♂‍➡ E15.1 man walking facing right: dark skin tone
 1F9CD                                                  ; fully-qualified     # 
🧍 E12.0 person standing
 1F9CD 1F3FB                                            ; fully-qualified     # 
🧍🏻 E12.0 person standing: light skin tone
 1F9CD 1F3FC                                            ; fully-qualified     # 
🧍🏼 E12.0 person standing: medium-light skin tone
@@ -2125,60 +2189,228 @@
 1F9CE 1F3FE 200D 2640                                  ; minimally-qualified # 
🧎🏾‍♀ E12.0 woman kneeling: medium-dark skin tone
 1F9CE 1F3FF 200D 2640 FE0F                             ; fully-qualified     # 
🧎🏿‍♀️ E12.0 woman kneeling: dark skin tone
 1F9CE 1F3FF 200D 2640                                  ; minimally-qualified # 
🧎🏿‍♀ E12.0 woman kneeling: dark skin tone
+1F9CE 200D 27A1 FE0F                                   ; fully-qualified     # 
🧎‍➡️ E15.1 person kneeling facing right
+1F9CE 200D 27A1                                        ; minimally-qualified # 
🧎‍➡ E15.1 person kneeling facing right
+1F9CE 1F3FB 200D 27A1 FE0F                             ; fully-qualified     # 
🧎🏻‍➡️ E15.1 person kneeling facing right: light skin tone
+1F9CE 1F3FB 200D 27A1                                  ; minimally-qualified # 
🧎🏻‍➡ E15.1 person kneeling facing right: light skin tone
+1F9CE 1F3FC 200D 27A1 FE0F                             ; fully-qualified     # 
🧎🏼‍➡️ E15.1 person kneeling facing right: medium-light skin tone
+1F9CE 1F3FC 200D 27A1                                  ; minimally-qualified # 
🧎🏼‍➡ E15.1 person kneeling facing right: medium-light skin tone
+1F9CE 1F3FD 200D 27A1 FE0F                             ; fully-qualified     # 
🧎🏽‍➡️ E15.1 person kneeling facing right: medium skin tone
+1F9CE 1F3FD 200D 27A1                                  ; minimally-qualified # 
🧎🏽‍➡ E15.1 person kneeling facing right: medium skin tone
+1F9CE 1F3FE 200D 27A1 FE0F                             ; fully-qualified     # 
🧎🏾‍➡️ E15.1 person kneeling facing right: medium-dark skin tone
+1F9CE 1F3FE 200D 27A1                                  ; minimally-qualified # 
🧎🏾‍➡ E15.1 person kneeling facing right: medium-dark skin tone
+1F9CE 1F3FF 200D 27A1 FE0F                             ; fully-qualified     # 
🧎🏿‍➡️ E15.1 person kneeling facing right: dark skin tone
+1F9CE 1F3FF 200D 27A1                                  ; minimally-qualified # 
🧎🏿‍➡ E15.1 person kneeling facing right: dark skin tone
+1F9CE 200D 2640 FE0F 200D 27A1 FE0F                    ; fully-qualified     # 
🧎‍♀️‍➡️ E15.1 woman kneeling facing right
+1F9CE 200D 2640 200D 27A1 FE0F                         ; minimally-qualified # 
🧎‍♀‍➡️ E15.1 woman kneeling facing right
+1F9CE 200D 2640 FE0F 200D 27A1                         ; minimally-qualified # 
🧎‍♀️‍➡ E15.1 woman kneeling facing right
+1F9CE 200D 2640 200D 27A1                              ; minimally-qualified # 
🧎‍♀‍➡ E15.1 woman kneeling facing right
+1F9CE 1F3FB 200D 2640 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🧎🏻‍♀️‍➡️ E15.1 woman kneeling facing right: light skin tone
+1F9CE 1F3FB 200D 2640 200D 27A1 FE0F                   ; minimally-qualified # 
🧎🏻‍♀‍➡️ E15.1 woman kneeling facing right: light skin tone
+1F9CE 1F3FB 200D 2640 FE0F 200D 27A1                   ; minimally-qualified # 
🧎🏻‍♀️‍➡ E15.1 woman kneeling facing right: light skin tone
+1F9CE 1F3FB 200D 2640 200D 27A1                        ; minimally-qualified # 
🧎🏻‍♀‍➡ E15.1 woman kneeling facing right: light skin tone
+1F9CE 1F3FC 200D 2640 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🧎🏼‍♀️‍➡️ E15.1 woman kneeling facing right: medium-light skin tone
+1F9CE 1F3FC 200D 2640 200D 27A1 FE0F                   ; minimally-qualified # 
🧎🏼‍♀‍➡️ E15.1 woman kneeling facing right: medium-light skin tone
+1F9CE 1F3FC 200D 2640 FE0F 200D 27A1                   ; minimally-qualified # 
🧎🏼‍♀️‍➡ E15.1 woman kneeling facing right: medium-light skin tone
+1F9CE 1F3FC 200D 2640 200D 27A1                        ; minimally-qualified # 
🧎🏼‍♀‍➡ E15.1 woman kneeling facing right: medium-light skin tone
+1F9CE 1F3FD 200D 2640 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🧎🏽‍♀️‍➡️ E15.1 woman kneeling facing right: medium skin tone
+1F9CE 1F3FD 200D 2640 200D 27A1 FE0F                   ; minimally-qualified # 
🧎🏽‍♀‍➡️ E15.1 woman kneeling facing right: medium skin tone
+1F9CE 1F3FD 200D 2640 FE0F 200D 27A1                   ; minimally-qualified # 
🧎🏽‍♀️‍➡ E15.1 woman kneeling facing right: medium skin tone
+1F9CE 1F3FD 200D 2640 200D 27A1                        ; minimally-qualified # 
🧎🏽‍♀‍➡ E15.1 woman kneeling facing right: medium skin tone
+1F9CE 1F3FE 200D 2640 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🧎🏾‍♀️‍➡️ E15.1 woman kneeling facing right: medium-dark skin tone
+1F9CE 1F3FE 200D 2640 200D 27A1 FE0F                   ; minimally-qualified # 
🧎🏾‍♀‍➡️ E15.1 woman kneeling facing right: medium-dark skin tone
+1F9CE 1F3FE 200D 2640 FE0F 200D 27A1                   ; minimally-qualified # 
🧎🏾‍♀️‍➡ E15.1 woman kneeling facing right: medium-dark skin tone
+1F9CE 1F3FE 200D 2640 200D 27A1                        ; minimally-qualified # 
🧎🏾‍♀‍➡ E15.1 woman kneeling facing right: medium-dark skin tone
+1F9CE 1F3FF 200D 2640 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🧎🏿‍♀️‍➡️ E15.1 woman kneeling facing right: dark skin tone
+1F9CE 1F3FF 200D 2640 200D 27A1 FE0F                   ; minimally-qualified # 
🧎🏿‍♀‍➡️ E15.1 woman kneeling facing right: dark skin tone
+1F9CE 1F3FF 200D 2640 FE0F 200D 27A1                   ; minimally-qualified # 
🧎🏿‍♀️‍➡ E15.1 woman kneeling facing right: dark skin tone
+1F9CE 1F3FF 200D 2640 200D 27A1                        ; minimally-qualified # 
🧎🏿‍♀‍➡ E15.1 woman kneeling facing right: dark skin tone
+1F9CE 200D 2642 FE0F 200D 27A1 FE0F                    ; fully-qualified     # 
🧎‍♂️‍➡️ E15.1 man kneeling facing right
+1F9CE 200D 2642 200D 27A1 FE0F                         ; minimally-qualified # 
🧎‍♂‍➡️ E15.1 man kneeling facing right
+1F9CE 200D 2642 FE0F 200D 27A1                         ; minimally-qualified # 
🧎‍♂️‍➡ E15.1 man kneeling facing right
+1F9CE 200D 2642 200D 27A1                              ; minimally-qualified # 
🧎‍♂‍➡ E15.1 man kneeling facing right
+1F9CE 1F3FB 200D 2642 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🧎🏻‍♂️‍➡️ E15.1 man kneeling facing right: light skin tone
+1F9CE 1F3FB 200D 2642 200D 27A1 FE0F                   ; minimally-qualified # 
🧎🏻‍♂‍➡️ E15.1 man kneeling facing right: light skin tone
+1F9CE 1F3FB 200D 2642 FE0F 200D 27A1                   ; minimally-qualified # 
🧎🏻‍♂️‍➡ E15.1 man kneeling facing right: light skin tone
+1F9CE 1F3FB 200D 2642 200D 27A1                        ; minimally-qualified # 
🧎🏻‍♂‍➡ E15.1 man kneeling facing right: light skin tone
+1F9CE 1F3FC 200D 2642 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🧎🏼‍♂️‍➡️ E15.1 man kneeling facing right: medium-light skin tone
+1F9CE 1F3FC 200D 2642 200D 27A1 FE0F                   ; minimally-qualified # 
🧎🏼‍♂‍➡️ E15.1 man kneeling facing right: medium-light skin tone
+1F9CE 1F3FC 200D 2642 FE0F 200D 27A1                   ; minimally-qualified # 
🧎🏼‍♂️‍➡ E15.1 man kneeling facing right: medium-light skin tone
+1F9CE 1F3FC 200D 2642 200D 27A1                        ; minimally-qualified # 
🧎🏼‍♂‍➡ E15.1 man kneeling facing right: medium-light skin tone
+1F9CE 1F3FD 200D 2642 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🧎🏽‍♂️‍➡️ E15.1 man kneeling facing right: medium skin tone
+1F9CE 1F3FD 200D 2642 200D 27A1 FE0F                   ; minimally-qualified # 
🧎🏽‍♂‍➡️ E15.1 man kneeling facing right: medium skin tone
+1F9CE 1F3FD 200D 2642 FE0F 200D 27A1                   ; minimally-qualified # 
🧎🏽‍♂️‍➡ E15.1 man kneeling facing right: medium skin tone
+1F9CE 1F3FD 200D 2642 200D 27A1                        ; minimally-qualified # 
🧎🏽‍♂‍➡ E15.1 man kneeling facing right: medium skin tone
+1F9CE 1F3FE 200D 2642 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🧎🏾‍♂️‍➡️ E15.1 man kneeling facing right: medium-dark skin tone
+1F9CE 1F3FE 200D 2642 200D 27A1 FE0F                   ; minimally-qualified # 
🧎🏾‍♂‍➡️ E15.1 man kneeling facing right: medium-dark skin tone
+1F9CE 1F3FE 200D 2642 FE0F 200D 27A1                   ; minimally-qualified # 
🧎🏾‍♂️‍➡ E15.1 man kneeling facing right: medium-dark skin tone
+1F9CE 1F3FE 200D 2642 200D 27A1                        ; minimally-qualified # 
🧎🏾‍♂‍➡ E15.1 man kneeling facing right: medium-dark skin tone
+1F9CE 1F3FF 200D 2642 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🧎🏿‍♂️‍➡️ E15.1 man kneeling facing right: dark skin tone
+1F9CE 1F3FF 200D 2642 200D 27A1 FE0F                   ; minimally-qualified # 
🧎🏿‍♂‍➡️ E15.1 man kneeling facing right: dark skin tone
+1F9CE 1F3FF 200D 2642 FE0F 200D 27A1                   ; minimally-qualified # 
🧎🏿‍♂️‍➡ E15.1 man kneeling facing right: dark skin tone
+1F9CE 1F3FF 200D 2642 200D 27A1                        ; minimally-qualified # 
🧎🏿‍♂‍➡ E15.1 man kneeling facing right: dark skin tone
 1F9D1 200D 1F9AF                                       ; fully-qualified     # 
🧑‍🦯 E12.1 person with white cane
 1F9D1 1F3FB 200D 1F9AF                                 ; fully-qualified     # 
🧑🏻‍🦯 E12.1 person with white cane: light skin tone
 1F9D1 1F3FC 200D 1F9AF                                 ; fully-qualified     # 
🧑🏼‍🦯 E12.1 person with white cane: medium-light skin tone
 1F9D1 1F3FD 200D 1F9AF                                 ; fully-qualified     # 
🧑🏽‍🦯 E12.1 person with white cane: medium skin tone
 1F9D1 1F3FE 200D 1F9AF                                 ; fully-qualified     # 
🧑🏾‍🦯 E12.1 person with white cane: medium-dark skin tone
 1F9D1 1F3FF 200D 1F9AF                                 ; fully-qualified     # 
🧑🏿‍🦯 E12.1 person with white cane: dark skin tone
+1F9D1 200D 1F9AF 200D 27A1 FE0F                        ; fully-qualified     # 
🧑‍🦯‍➡️ E15.1 person with white cane facing right
+1F9D1 200D 1F9AF 200D 27A1                             ; minimally-qualified # 
🧑‍🦯‍➡ E15.1 person with white cane facing right
+1F9D1 1F3FB 200D 1F9AF 200D 27A1 FE0F                  ; fully-qualified     # 
🧑🏻‍🦯‍➡️ E15.1 person with white cane facing right: light skin tone
+1F9D1 1F3FB 200D 1F9AF 200D 27A1                       ; minimally-qualified # 
🧑🏻‍🦯‍➡ E15.1 person with white cane facing right: light skin tone
+1F9D1 1F3FC 200D 1F9AF 200D 27A1 FE0F                  ; fully-qualified     # 
🧑🏼‍🦯‍➡️ E15.1 person with white cane facing right: medium-light skin tone
+1F9D1 1F3FC 200D 1F9AF 200D 27A1                       ; minimally-qualified # 
🧑🏼‍🦯‍➡ E15.1 person with white cane facing right: medium-light skin tone
+1F9D1 1F3FD 200D 1F9AF 200D 27A1 FE0F                  ; fully-qualified     # 
🧑🏽‍🦯‍➡️ E15.1 person with white cane facing right: medium skin tone
+1F9D1 1F3FD 200D 1F9AF 200D 27A1                       ; minimally-qualified # 
🧑🏽‍🦯‍➡ E15.1 person with white cane facing right: medium skin tone
+1F9D1 1F3FE 200D 1F9AF 200D 27A1 FE0F                  ; fully-qualified     # 
🧑🏾‍🦯‍➡️ E15.1 person with white cane facing right: medium-dark skin tone
+1F9D1 1F3FE 200D 1F9AF 200D 27A1                       ; minimally-qualified # 
🧑🏾‍🦯‍➡ E15.1 person with white cane facing right: medium-dark skin tone
+1F9D1 1F3FF 200D 1F9AF 200D 27A1 FE0F                  ; fully-qualified     # 
🧑🏿‍🦯‍➡️ E15.1 person with white cane facing right: dark skin tone
+1F9D1 1F3FF 200D 1F9AF 200D 27A1                       ; minimally-qualified # 
🧑🏿‍🦯‍➡ E15.1 person with white cane facing right: dark skin tone
 1F468 200D 1F9AF                                       ; fully-qualified     # 
👨‍🦯 E12.0 man with white cane
 1F468 1F3FB 200D 1F9AF                                 ; fully-qualified     # 
👨🏻‍🦯 E12.0 man with white cane: light skin tone
 1F468 1F3FC 200D 1F9AF                                 ; fully-qualified     # 
👨🏼‍🦯 E12.0 man with white cane: medium-light skin tone
 1F468 1F3FD 200D 1F9AF                                 ; fully-qualified     # 
👨🏽‍🦯 E12.0 man with white cane: medium skin tone
 1F468 1F3FE 200D 1F9AF                                 ; fully-qualified     # 
👨🏾‍🦯 E12.0 man with white cane: medium-dark skin tone
 1F468 1F3FF 200D 1F9AF                                 ; fully-qualified     # 
👨🏿‍🦯 E12.0 man with white cane: dark skin tone
+1F468 200D 1F9AF 200D 27A1 FE0F                        ; fully-qualified     # 
👨‍🦯‍➡️ E15.1 man with white cane facing right
+1F468 200D 1F9AF 200D 27A1                             ; minimally-qualified # 
👨‍🦯‍➡ E15.1 man with white cane facing right
+1F468 1F3FB 200D 1F9AF 200D 27A1 FE0F                  ; fully-qualified     # 
👨🏻‍🦯‍➡️ E15.1 man with white cane facing right: light skin tone
+1F468 1F3FB 200D 1F9AF 200D 27A1                       ; minimally-qualified # 
👨🏻‍🦯‍➡ E15.1 man with white cane facing right: light skin tone
+1F468 1F3FC 200D 1F9AF 200D 27A1 FE0F                  ; fully-qualified     # 
👨🏼‍🦯‍➡️ E15.1 man with white cane facing right: medium-light skin tone
+1F468 1F3FC 200D 1F9AF 200D 27A1                       ; minimally-qualified # 
👨🏼‍🦯‍➡ E15.1 man with white cane facing right: medium-light skin tone
+1F468 1F3FD 200D 1F9AF 200D 27A1 FE0F                  ; fully-qualified     # 
👨🏽‍🦯‍➡️ E15.1 man with white cane facing right: medium skin tone
+1F468 1F3FD 200D 1F9AF 200D 27A1                       ; minimally-qualified # 
👨🏽‍🦯‍➡ E15.1 man with white cane facing right: medium skin tone
+1F468 1F3FE 200D 1F9AF 200D 27A1 FE0F                  ; fully-qualified     # 
👨🏾‍🦯‍➡️ E15.1 man with white cane facing right: medium-dark skin tone
+1F468 1F3FE 200D 1F9AF 200D 27A1                       ; minimally-qualified # 
👨🏾‍🦯‍➡ E15.1 man with white cane facing right: medium-dark skin tone
+1F468 1F3FF 200D 1F9AF 200D 27A1 FE0F                  ; fully-qualified     # 
👨🏿‍🦯‍➡️ E15.1 man with white cane facing right: dark skin tone
+1F468 1F3FF 200D 1F9AF 200D 27A1                       ; minimally-qualified # 
👨🏿‍🦯‍➡ E15.1 man with white cane facing right: dark skin tone
 1F469 200D 1F9AF                                       ; fully-qualified     # 
👩‍🦯 E12.0 woman with white cane
 1F469 1F3FB 200D 1F9AF                                 ; fully-qualified     # 
👩🏻‍🦯 E12.0 woman with white cane: light skin tone
 1F469 1F3FC 200D 1F9AF                                 ; fully-qualified     # 
👩🏼‍🦯 E12.0 woman with white cane: medium-light skin tone
 1F469 1F3FD 200D 1F9AF                                 ; fully-qualified     # 
👩🏽‍🦯 E12.0 woman with white cane: medium skin tone
 1F469 1F3FE 200D 1F9AF                                 ; fully-qualified     # 
👩🏾‍🦯 E12.0 woman with white cane: medium-dark skin tone
 1F469 1F3FF 200D 1F9AF                                 ; fully-qualified     # 
👩🏿‍🦯 E12.0 woman with white cane: dark skin tone
+1F469 200D 1F9AF 200D 27A1 FE0F                        ; fully-qualified     # 
👩‍🦯‍➡️ E15.1 woman with white cane facing right
+1F469 200D 1F9AF 200D 27A1                             ; minimally-qualified # 
👩‍🦯‍➡ E15.1 woman with white cane facing right
+1F469 1F3FB 200D 1F9AF 200D 27A1 FE0F                  ; fully-qualified     # 
👩🏻‍🦯‍➡️ E15.1 woman with white cane facing right: light skin tone
+1F469 1F3FB 200D 1F9AF 200D 27A1                       ; minimally-qualified # 
👩🏻‍🦯‍➡ E15.1 woman with white cane facing right: light skin tone
+1F469 1F3FC 200D 1F9AF 200D 27A1 FE0F                  ; fully-qualified     # 
👩🏼‍🦯‍➡️ E15.1 woman with white cane facing right: medium-light skin tone
+1F469 1F3FC 200D 1F9AF 200D 27A1                       ; minimally-qualified # 
👩🏼‍🦯‍➡ E15.1 woman with white cane facing right: medium-light skin tone
+1F469 1F3FD 200D 1F9AF 200D 27A1 FE0F                  ; fully-qualified     # 
👩🏽‍🦯‍➡️ E15.1 woman with white cane facing right: medium skin tone
+1F469 1F3FD 200D 1F9AF 200D 27A1                       ; minimally-qualified # 
👩🏽‍🦯‍➡ E15.1 woman with white cane facing right: medium skin tone
+1F469 1F3FE 200D 1F9AF 200D 27A1 FE0F                  ; fully-qualified     # 
👩🏾‍🦯‍➡️ E15.1 woman with white cane facing right: medium-dark skin tone
+1F469 1F3FE 200D 1F9AF 200D 27A1                       ; minimally-qualified # 
👩🏾‍🦯‍➡ E15.1 woman with white cane facing right: medium-dark skin tone
+1F469 1F3FF 200D 1F9AF 200D 27A1 FE0F                  ; fully-qualified     # 
👩🏿‍🦯‍➡️ E15.1 woman with white cane facing right: dark skin tone
+1F469 1F3FF 200D 1F9AF 200D 27A1                       ; minimally-qualified # 
👩🏿‍🦯‍➡ E15.1 woman with white cane facing right: dark skin tone
 1F9D1 200D 1F9BC                                       ; fully-qualified     # 
🧑‍🦼 E12.1 person in motorized wheelchair
 1F9D1 1F3FB 200D 1F9BC                                 ; fully-qualified     # 
🧑🏻‍🦼 E12.1 person in motorized wheelchair: light skin tone
 1F9D1 1F3FC 200D 1F9BC                                 ; fully-qualified     # 
🧑🏼‍🦼 E12.1 person in motorized wheelchair: medium-light skin tone
 1F9D1 1F3FD 200D 1F9BC                                 ; fully-qualified     # 
🧑🏽‍🦼 E12.1 person in motorized wheelchair: medium skin tone
 1F9D1 1F3FE 200D 1F9BC                                 ; fully-qualified     # 
🧑🏾‍🦼 E12.1 person in motorized wheelchair: medium-dark skin tone
 1F9D1 1F3FF 200D 1F9BC                                 ; fully-qualified     # 
🧑🏿‍🦼 E12.1 person in motorized wheelchair: dark skin tone
+1F9D1 200D 1F9BC 200D 27A1 FE0F                        ; fully-qualified     # 
🧑‍🦼‍➡️ E15.1 person in motorized wheelchair facing right
+1F9D1 200D 1F9BC 200D 27A1                             ; minimally-qualified # 
🧑‍🦼‍➡ E15.1 person in motorized wheelchair facing right
+1F9D1 1F3FB 200D 1F9BC 200D 27A1 FE0F                  ; fully-qualified     # 
🧑🏻‍🦼‍➡️ E15.1 person in motorized wheelchair facing right: light skin tone
+1F9D1 1F3FB 200D 1F9BC 200D 27A1                       ; minimally-qualified # 
🧑🏻‍🦼‍➡ E15.1 person in motorized wheelchair facing right: light skin tone
+1F9D1 1F3FC 200D 1F9BC 200D 27A1 FE0F                  ; fully-qualified     # 
🧑🏼‍🦼‍➡️ E15.1 person in motorized wheelchair facing right: medium-light skin 
tone
+1F9D1 1F3FC 200D 1F9BC 200D 27A1                       ; minimally-qualified # 
🧑🏼‍🦼‍➡ E15.1 person in motorized wheelchair facing right: medium-light skin tone
+1F9D1 1F3FD 200D 1F9BC 200D 27A1 FE0F                  ; fully-qualified     # 
🧑🏽‍🦼‍➡️ E15.1 person in motorized wheelchair facing right: medium skin tone
+1F9D1 1F3FD 200D 1F9BC 200D 27A1                       ; minimally-qualified # 
🧑🏽‍🦼‍➡ E15.1 person in motorized wheelchair facing right: medium skin tone
+1F9D1 1F3FE 200D 1F9BC 200D 27A1 FE0F                  ; fully-qualified     # 
🧑🏾‍🦼‍➡️ E15.1 person in motorized wheelchair facing right: medium-dark skin tone
+1F9D1 1F3FE 200D 1F9BC 200D 27A1                       ; minimally-qualified # 
🧑🏾‍🦼‍➡ E15.1 person in motorized wheelchair facing right: medium-dark skin tone
+1F9D1 1F3FF 200D 1F9BC 200D 27A1 FE0F                  ; fully-qualified     # 
🧑🏿‍🦼‍➡️ E15.1 person in motorized wheelchair facing right: dark skin tone
+1F9D1 1F3FF 200D 1F9BC 200D 27A1                       ; minimally-qualified # 
🧑🏿‍🦼‍➡ E15.1 person in motorized wheelchair facing right: dark skin tone
 1F468 200D 1F9BC                                       ; fully-qualified     # 
👨‍🦼 E12.0 man in motorized wheelchair
 1F468 1F3FB 200D 1F9BC                                 ; fully-qualified     # 
👨🏻‍🦼 E12.0 man in motorized wheelchair: light skin tone
 1F468 1F3FC 200D 1F9BC                                 ; fully-qualified     # 
👨🏼‍🦼 E12.0 man in motorized wheelchair: medium-light skin tone
 1F468 1F3FD 200D 1F9BC                                 ; fully-qualified     # 
👨🏽‍🦼 E12.0 man in motorized wheelchair: medium skin tone
 1F468 1F3FE 200D 1F9BC                                 ; fully-qualified     # 
👨🏾‍🦼 E12.0 man in motorized wheelchair: medium-dark skin tone
 1F468 1F3FF 200D 1F9BC                                 ; fully-qualified     # 
👨🏿‍🦼 E12.0 man in motorized wheelchair: dark skin tone
+1F468 200D 1F9BC 200D 27A1 FE0F                        ; fully-qualified     # 
👨‍🦼‍➡️ E15.1 man in motorized wheelchair facing right
+1F468 200D 1F9BC 200D 27A1                             ; minimally-qualified # 
👨‍🦼‍➡ E15.1 man in motorized wheelchair facing right
+1F468 1F3FB 200D 1F9BC 200D 27A1 FE0F                  ; fully-qualified     # 
👨🏻‍🦼‍➡️ E15.1 man in motorized wheelchair facing right: light skin tone
+1F468 1F3FB 200D 1F9BC 200D 27A1                       ; minimally-qualified # 
👨🏻‍🦼‍➡ E15.1 man in motorized wheelchair facing right: light skin tone
+1F468 1F3FC 200D 1F9BC 200D 27A1 FE0F                  ; fully-qualified     # 
👨🏼‍🦼‍➡️ E15.1 man in motorized wheelchair facing right: medium-light skin tone
+1F468 1F3FC 200D 1F9BC 200D 27A1                       ; minimally-qualified # 
👨🏼‍🦼‍➡ E15.1 man in motorized wheelchair facing right: medium-light skin tone
+1F468 1F3FD 200D 1F9BC 200D 27A1 FE0F                  ; fully-qualified     # 
👨🏽‍🦼‍➡️ E15.1 man in motorized wheelchair facing right: medium skin tone
+1F468 1F3FD 200D 1F9BC 200D 27A1                       ; minimally-qualified # 
👨🏽‍🦼‍➡ E15.1 man in motorized wheelchair facing right: medium skin tone
+1F468 1F3FE 200D 1F9BC 200D 27A1 FE0F                  ; fully-qualified     # 
👨🏾‍🦼‍➡️ E15.1 man in motorized wheelchair facing right: medium-dark skin tone
+1F468 1F3FE 200D 1F9BC 200D 27A1                       ; minimally-qualified # 
👨🏾‍🦼‍➡ E15.1 man in motorized wheelchair facing right: medium-dark skin tone
+1F468 1F3FF 200D 1F9BC 200D 27A1 FE0F                  ; fully-qualified     # 
👨🏿‍🦼‍➡️ E15.1 man in motorized wheelchair facing right: dark skin tone
+1F468 1F3FF 200D 1F9BC 200D 27A1                       ; minimally-qualified # 
👨🏿‍🦼‍➡ E15.1 man in motorized wheelchair facing right: dark skin tone
 1F469 200D 1F9BC                                       ; fully-qualified     # 
👩‍🦼 E12.0 woman in motorized wheelchair
 1F469 1F3FB 200D 1F9BC                                 ; fully-qualified     # 
👩🏻‍🦼 E12.0 woman in motorized wheelchair: light skin tone
 1F469 1F3FC 200D 1F9BC                                 ; fully-qualified     # 
👩🏼‍🦼 E12.0 woman in motorized wheelchair: medium-light skin tone
 1F469 1F3FD 200D 1F9BC                                 ; fully-qualified     # 
👩🏽‍🦼 E12.0 woman in motorized wheelchair: medium skin tone
 1F469 1F3FE 200D 1F9BC                                 ; fully-qualified     # 
👩🏾‍🦼 E12.0 woman in motorized wheelchair: medium-dark skin tone
 1F469 1F3FF 200D 1F9BC                                 ; fully-qualified     # 
👩🏿‍🦼 E12.0 woman in motorized wheelchair: dark skin tone
+1F469 200D 1F9BC 200D 27A1 FE0F                        ; fully-qualified     # 
👩‍🦼‍➡️ E15.1 woman in motorized wheelchair facing right
+1F469 200D 1F9BC 200D 27A1                             ; minimally-qualified # 
👩‍🦼‍➡ E15.1 woman in motorized wheelchair facing right
+1F469 1F3FB 200D 1F9BC 200D 27A1 FE0F                  ; fully-qualified     # 
👩🏻‍🦼‍➡️ E15.1 woman in motorized wheelchair facing right: light skin tone
+1F469 1F3FB 200D 1F9BC 200D 27A1                       ; minimally-qualified # 
👩🏻‍🦼‍➡ E15.1 woman in motorized wheelchair facing right: light skin tone
+1F469 1F3FC 200D 1F9BC 200D 27A1 FE0F                  ; fully-qualified     # 
👩🏼‍🦼‍➡️ E15.1 woman in motorized wheelchair facing right: medium-light skin tone
+1F469 1F3FC 200D 1F9BC 200D 27A1                       ; minimally-qualified # 
👩🏼‍🦼‍➡ E15.1 woman in motorized wheelchair facing right: medium-light skin tone
+1F469 1F3FD 200D 1F9BC 200D 27A1 FE0F                  ; fully-qualified     # 
👩🏽‍🦼‍➡️ E15.1 woman in motorized wheelchair facing right: medium skin tone
+1F469 1F3FD 200D 1F9BC 200D 27A1                       ; minimally-qualified # 
👩🏽‍🦼‍➡ E15.1 woman in motorized wheelchair facing right: medium skin tone
+1F469 1F3FE 200D 1F9BC 200D 27A1 FE0F                  ; fully-qualified     # 
👩🏾‍🦼‍➡️ E15.1 woman in motorized wheelchair facing right: medium-dark skin tone
+1F469 1F3FE 200D 1F9BC 200D 27A1                       ; minimally-qualified # 
👩🏾‍🦼‍➡ E15.1 woman in motorized wheelchair facing right: medium-dark skin tone
+1F469 1F3FF 200D 1F9BC 200D 27A1 FE0F                  ; fully-qualified     # 
👩🏿‍🦼‍➡️ E15.1 woman in motorized wheelchair facing right: dark skin tone
+1F469 1F3FF 200D 1F9BC 200D 27A1                       ; minimally-qualified # 
👩🏿‍🦼‍➡ E15.1 woman in motorized wheelchair facing right: dark skin tone
 1F9D1 200D 1F9BD                                       ; fully-qualified     # 
🧑‍🦽 E12.1 person in manual wheelchair
 1F9D1 1F3FB 200D 1F9BD                                 ; fully-qualified     # 
🧑🏻‍🦽 E12.1 person in manual wheelchair: light skin tone
 1F9D1 1F3FC 200D 1F9BD                                 ; fully-qualified     # 
🧑🏼‍🦽 E12.1 person in manual wheelchair: medium-light skin tone
 1F9D1 1F3FD 200D 1F9BD                                 ; fully-qualified     # 
🧑🏽‍🦽 E12.1 person in manual wheelchair: medium skin tone
 1F9D1 1F3FE 200D 1F9BD                                 ; fully-qualified     # 
🧑🏾‍🦽 E12.1 person in manual wheelchair: medium-dark skin tone
 1F9D1 1F3FF 200D 1F9BD                                 ; fully-qualified     # 
🧑🏿‍🦽 E12.1 person in manual wheelchair: dark skin tone
+1F9D1 200D 1F9BD 200D 27A1 FE0F                        ; fully-qualified     # 
🧑‍🦽‍➡️ E15.1 person in manual wheelchair facing right
+1F9D1 200D 1F9BD 200D 27A1                             ; minimally-qualified # 
🧑‍🦽‍➡ E15.1 person in manual wheelchair facing right
+1F9D1 1F3FB 200D 1F9BD 200D 27A1 FE0F                  ; fully-qualified     # 
🧑🏻‍🦽‍➡️ E15.1 person in manual wheelchair facing right: light skin tone
+1F9D1 1F3FB 200D 1F9BD 200D 27A1                       ; minimally-qualified # 
🧑🏻‍🦽‍➡ E15.1 person in manual wheelchair facing right: light skin tone
+1F9D1 1F3FC 200D 1F9BD 200D 27A1 FE0F                  ; fully-qualified     # 
🧑🏼‍🦽‍➡️ E15.1 person in manual wheelchair facing right: medium-light skin tone
+1F9D1 1F3FC 200D 1F9BD 200D 27A1                       ; minimally-qualified # 
🧑🏼‍🦽‍➡ E15.1 person in manual wheelchair facing right: medium-light skin tone
+1F9D1 1F3FD 200D 1F9BD 200D 27A1 FE0F                  ; fully-qualified     # 
🧑🏽‍🦽‍➡️ E15.1 person in manual wheelchair facing right: medium skin tone
+1F9D1 1F3FD 200D 1F9BD 200D 27A1                       ; minimally-qualified # 
🧑🏽‍🦽‍➡ E15.1 person in manual wheelchair facing right: medium skin tone
+1F9D1 1F3FE 200D 1F9BD 200D 27A1 FE0F                  ; fully-qualified     # 
🧑🏾‍🦽‍➡️ E15.1 person in manual wheelchair facing right: medium-dark skin tone
+1F9D1 1F3FE 200D 1F9BD 200D 27A1                       ; minimally-qualified # 
🧑🏾‍🦽‍➡ E15.1 person in manual wheelchair facing right: medium-dark skin tone
+1F9D1 1F3FF 200D 1F9BD 200D 27A1 FE0F                  ; fully-qualified     # 
🧑🏿‍🦽‍➡️ E15.1 person in manual wheelchair facing right: dark skin tone
+1F9D1 1F3FF 200D 1F9BD 200D 27A1                       ; minimally-qualified # 
🧑🏿‍🦽‍➡ E15.1 person in manual wheelchair facing right: dark skin tone
 1F468 200D 1F9BD                                       ; fully-qualified     # 
👨‍🦽 E12.0 man in manual wheelchair
 1F468 1F3FB 200D 1F9BD                                 ; fully-qualified     # 
👨🏻‍🦽 E12.0 man in manual wheelchair: light skin tone
 1F468 1F3FC 200D 1F9BD                                 ; fully-qualified     # 
👨🏼‍🦽 E12.0 man in manual wheelchair: medium-light skin tone
 1F468 1F3FD 200D 1F9BD                                 ; fully-qualified     # 
👨🏽‍🦽 E12.0 man in manual wheelchair: medium skin tone
 1F468 1F3FE 200D 1F9BD                                 ; fully-qualified     # 
👨🏾‍🦽 E12.0 man in manual wheelchair: medium-dark skin tone
 1F468 1F3FF 200D 1F9BD                                 ; fully-qualified     # 
👨🏿‍🦽 E12.0 man in manual wheelchair: dark skin tone
+1F468 200D 1F9BD 200D 27A1 FE0F                        ; fully-qualified     # 
👨‍🦽‍➡️ E15.1 man in manual wheelchair facing right
+1F468 200D 1F9BD 200D 27A1                             ; minimally-qualified # 
👨‍🦽‍➡ E15.1 man in manual wheelchair facing right
+1F468 1F3FB 200D 1F9BD 200D 27A1 FE0F                  ; fully-qualified     # 
👨🏻‍🦽‍➡️ E15.1 man in manual wheelchair facing right: light skin tone
+1F468 1F3FB 200D 1F9BD 200D 27A1                       ; minimally-qualified # 
👨🏻‍🦽‍➡ E15.1 man in manual wheelchair facing right: light skin tone
+1F468 1F3FC 200D 1F9BD 200D 27A1 FE0F                  ; fully-qualified     # 
👨🏼‍🦽‍➡️ E15.1 man in manual wheelchair facing right: medium-light skin tone
+1F468 1F3FC 200D 1F9BD 200D 27A1                       ; minimally-qualified # 
👨🏼‍🦽‍➡ E15.1 man in manual wheelchair facing right: medium-light skin tone
+1F468 1F3FD 200D 1F9BD 200D 27A1 FE0F                  ; fully-qualified     # 
👨🏽‍🦽‍➡️ E15.1 man in manual wheelchair facing right: medium skin tone
+1F468 1F3FD 200D 1F9BD 200D 27A1                       ; minimally-qualified # 
👨🏽‍🦽‍➡ E15.1 man in manual wheelchair facing right: medium skin tone
+1F468 1F3FE 200D 1F9BD 200D 27A1 FE0F                  ; fully-qualified     # 
👨🏾‍🦽‍➡️ E15.1 man in manual wheelchair facing right: medium-dark skin tone
+1F468 1F3FE 200D 1F9BD 200D 27A1                       ; minimally-qualified # 
👨🏾‍🦽‍➡ E15.1 man in manual wheelchair facing right: medium-dark skin tone
+1F468 1F3FF 200D 1F9BD 200D 27A1 FE0F                  ; fully-qualified     # 
👨🏿‍🦽‍➡️ E15.1 man in manual wheelchair facing right: dark skin tone
+1F468 1F3FF 200D 1F9BD 200D 27A1                       ; minimally-qualified # 
👨🏿‍🦽‍➡ E15.1 man in manual wheelchair facing right: dark skin tone
 1F469 200D 1F9BD                                       ; fully-qualified     # 
👩‍🦽 E12.0 woman in manual wheelchair
 1F469 1F3FB 200D 1F9BD                                 ; fully-qualified     # 
👩🏻‍🦽 E12.0 woman in manual wheelchair: light skin tone
 1F469 1F3FC 200D 1F9BD                                 ; fully-qualified     # 
👩🏼‍🦽 E12.0 woman in manual wheelchair: medium-light skin tone
 1F469 1F3FD 200D 1F9BD                                 ; fully-qualified     # 
👩🏽‍🦽 E12.0 woman in manual wheelchair: medium skin tone
 1F469 1F3FE 200D 1F9BD                                 ; fully-qualified     # 
👩🏾‍🦽 E12.0 woman in manual wheelchair: medium-dark skin tone
 1F469 1F3FF 200D 1F9BD                                 ; fully-qualified     # 
👩🏿‍🦽 E12.0 woman in manual wheelchair: dark skin tone
+1F469 200D 1F9BD 200D 27A1 FE0F                        ; fully-qualified     # 
👩‍🦽‍➡️ E15.1 woman in manual wheelchair facing right
+1F469 200D 1F9BD 200D 27A1                             ; minimally-qualified # 
👩‍🦽‍➡ E15.1 woman in manual wheelchair facing right
+1F469 1F3FB 200D 1F9BD 200D 27A1 FE0F                  ; fully-qualified     # 
👩🏻‍🦽‍➡️ E15.1 woman in manual wheelchair facing right: light skin tone
+1F469 1F3FB 200D 1F9BD 200D 27A1                       ; minimally-qualified # 
👩🏻‍🦽‍➡ E15.1 woman in manual wheelchair facing right: light skin tone
+1F469 1F3FC 200D 1F9BD 200D 27A1 FE0F                  ; fully-qualified     # 
👩🏼‍🦽‍➡️ E15.1 woman in manual wheelchair facing right: medium-light skin tone
+1F469 1F3FC 200D 1F9BD 200D 27A1                       ; minimally-qualified # 
👩🏼‍🦽‍➡ E15.1 woman in manual wheelchair facing right: medium-light skin tone
+1F469 1F3FD 200D 1F9BD 200D 27A1 FE0F                  ; fully-qualified     # 
👩🏽‍🦽‍➡️ E15.1 woman in manual wheelchair facing right: medium skin tone
+1F469 1F3FD 200D 1F9BD 200D 27A1                       ; minimally-qualified # 
👩🏽‍🦽‍➡ E15.1 woman in manual wheelchair facing right: medium skin tone
+1F469 1F3FE 200D 1F9BD 200D 27A1 FE0F                  ; fully-qualified     # 
👩🏾‍🦽‍➡️ E15.1 woman in manual wheelchair facing right: medium-dark skin tone
+1F469 1F3FE 200D 1F9BD 200D 27A1                       ; minimally-qualified # 
👩🏾‍🦽‍➡ E15.1 woman in manual wheelchair facing right: medium-dark skin tone
+1F469 1F3FF 200D 1F9BD 200D 27A1 FE0F                  ; fully-qualified     # 
👩🏿‍🦽‍➡️ E15.1 woman in manual wheelchair facing right: dark skin tone
+1F469 1F3FF 200D 1F9BD 200D 27A1                       ; minimally-qualified # 
👩🏿‍🦽‍➡ E15.1 woman in manual wheelchair facing right: dark skin tone
 1F3C3                                                  ; fully-qualified     # 
🏃 E0.6 person running
 1F3C3 1F3FB                                            ; fully-qualified     # 
🏃🏻 E1.0 person running: light skin tone
 1F3C3 1F3FC                                            ; fully-qualified     # 
🏃🏼 E1.0 person running: medium-light skin tone
@@ -2209,6 +2441,66 @@
 1F3C3 1F3FE 200D 2640                                  ; minimally-qualified # 
🏃🏾‍♀ E4.0 woman running: medium-dark skin tone
 1F3C3 1F3FF 200D 2640 FE0F                             ; fully-qualified     # 
🏃🏿‍♀️ E4.0 woman running: dark skin tone
 1F3C3 1F3FF 200D 2640                                  ; minimally-qualified # 
🏃🏿‍♀ E4.0 woman running: dark skin tone
+1F3C3 200D 27A1 FE0F                                   ; fully-qualified     # 
🏃‍➡️ E15.1 person running facing right
+1F3C3 200D 27A1                                        ; minimally-qualified # 
🏃‍➡ E15.1 person running facing right
+1F3C3 1F3FB 200D 27A1 FE0F                             ; fully-qualified     # 
🏃🏻‍➡️ E15.1 person running facing right: light skin tone
+1F3C3 1F3FB 200D 27A1                                  ; minimally-qualified # 
🏃🏻‍➡ E15.1 person running facing right: light skin tone
+1F3C3 1F3FC 200D 27A1 FE0F                             ; fully-qualified     # 
🏃🏼‍➡️ E15.1 person running facing right: medium-light skin tone
+1F3C3 1F3FC 200D 27A1                                  ; minimally-qualified # 
🏃🏼‍➡ E15.1 person running facing right: medium-light skin tone
+1F3C3 1F3FD 200D 27A1 FE0F                             ; fully-qualified     # 
🏃🏽‍➡️ E15.1 person running facing right: medium skin tone
+1F3C3 1F3FD 200D 27A1                                  ; minimally-qualified # 
🏃🏽‍➡ E15.1 person running facing right: medium skin tone
+1F3C3 1F3FE 200D 27A1 FE0F                             ; fully-qualified     # 
🏃🏾‍➡️ E15.1 person running facing right: medium-dark skin tone
+1F3C3 1F3FE 200D 27A1                                  ; minimally-qualified # 
🏃🏾‍➡ E15.1 person running facing right: medium-dark skin tone
+1F3C3 1F3FF 200D 27A1 FE0F                             ; fully-qualified     # 
🏃🏿‍➡️ E15.1 person running facing right: dark skin tone
+1F3C3 1F3FF 200D 27A1                                  ; minimally-qualified # 
🏃🏿‍➡ E15.1 person running facing right: dark skin tone
+1F3C3 200D 2640 FE0F 200D 27A1 FE0F                    ; fully-qualified     # 
🏃‍♀️‍➡️ E15.1 woman running facing right
+1F3C3 200D 2640 200D 27A1 FE0F                         ; minimally-qualified # 
🏃‍♀‍➡️ E15.1 woman running facing right
+1F3C3 200D 2640 FE0F 200D 27A1                         ; minimally-qualified # 
🏃‍♀️‍➡ E15.1 woman running facing right
+1F3C3 200D 2640 200D 27A1                              ; minimally-qualified # 
🏃‍♀‍➡ E15.1 woman running facing right
+1F3C3 1F3FB 200D 2640 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🏃🏻‍♀️‍➡️ E15.1 woman running facing right: light skin tone
+1F3C3 1F3FB 200D 2640 200D 27A1 FE0F                   ; minimally-qualified # 
🏃🏻‍♀‍➡️ E15.1 woman running facing right: light skin tone
+1F3C3 1F3FB 200D 2640 FE0F 200D 27A1                   ; minimally-qualified # 
🏃🏻‍♀️‍➡ E15.1 woman running facing right: light skin tone
+1F3C3 1F3FB 200D 2640 200D 27A1                        ; minimally-qualified # 
🏃🏻‍♀‍➡ E15.1 woman running facing right: light skin tone
+1F3C3 1F3FC 200D 2640 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🏃🏼‍♀️‍➡️ E15.1 woman running facing right: medium-light skin tone
+1F3C3 1F3FC 200D 2640 200D 27A1 FE0F                   ; minimally-qualified # 
🏃🏼‍♀‍➡️ E15.1 woman running facing right: medium-light skin tone
+1F3C3 1F3FC 200D 2640 FE0F 200D 27A1                   ; minimally-qualified # 
🏃🏼‍♀️‍➡ E15.1 woman running facing right: medium-light skin tone
+1F3C3 1F3FC 200D 2640 200D 27A1                        ; minimally-qualified # 
🏃🏼‍♀‍➡ E15.1 woman running facing right: medium-light skin tone
+1F3C3 1F3FD 200D 2640 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🏃🏽‍♀️‍➡️ E15.1 woman running facing right: medium skin tone
+1F3C3 1F3FD 200D 2640 200D 27A1 FE0F                   ; minimally-qualified # 
🏃🏽‍♀‍➡️ E15.1 woman running facing right: medium skin tone
+1F3C3 1F3FD 200D 2640 FE0F 200D 27A1                   ; minimally-qualified # 
🏃🏽‍♀️‍➡ E15.1 woman running facing right: medium skin tone
+1F3C3 1F3FD 200D 2640 200D 27A1                        ; minimally-qualified # 
🏃🏽‍♀‍➡ E15.1 woman running facing right: medium skin tone
+1F3C3 1F3FE 200D 2640 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🏃🏾‍♀️‍➡️ E15.1 woman running facing right: medium-dark skin tone
+1F3C3 1F3FE 200D 2640 200D 27A1 FE0F                   ; minimally-qualified # 
🏃🏾‍♀‍➡️ E15.1 woman running facing right: medium-dark skin tone
+1F3C3 1F3FE 200D 2640 FE0F 200D 27A1                   ; minimally-qualified # 
🏃🏾‍♀️‍➡ E15.1 woman running facing right: medium-dark skin tone
+1F3C3 1F3FE 200D 2640 200D 27A1                        ; minimally-qualified # 
🏃🏾‍♀‍➡ E15.1 woman running facing right: medium-dark skin tone
+1F3C3 1F3FF 200D 2640 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🏃🏿‍♀️‍➡️ E15.1 woman running facing right: dark skin tone
+1F3C3 1F3FF 200D 2640 200D 27A1 FE0F                   ; minimally-qualified # 
🏃🏿‍♀‍➡️ E15.1 woman running facing right: dark skin tone
+1F3C3 1F3FF 200D 2640 FE0F 200D 27A1                   ; minimally-qualified # 
🏃🏿‍♀️‍➡ E15.1 woman running facing right: dark skin tone
+1F3C3 1F3FF 200D 2640 200D 27A1                        ; minimally-qualified # 
🏃🏿‍♀‍➡ E15.1 woman running facing right: dark skin tone
+1F3C3 200D 2642 FE0F 200D 27A1 FE0F                    ; fully-qualified     # 
🏃‍♂️‍➡️ E15.1 man running facing right
+1F3C3 200D 2642 200D 27A1 FE0F                         ; minimally-qualified # 
🏃‍♂‍➡️ E15.1 man running facing right
+1F3C3 200D 2642 FE0F 200D 27A1                         ; minimally-qualified # 
🏃‍♂️‍➡ E15.1 man running facing right
+1F3C3 200D 2642 200D 27A1                              ; minimally-qualified # 
🏃‍♂‍➡ E15.1 man running facing right
+1F3C3 1F3FB 200D 2642 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🏃🏻‍♂️‍➡️ E15.1 man running facing right: light skin tone
+1F3C3 1F3FB 200D 2642 200D 27A1 FE0F                   ; minimally-qualified # 
🏃🏻‍♂‍➡️ E15.1 man running facing right: light skin tone
+1F3C3 1F3FB 200D 2642 FE0F 200D 27A1                   ; minimally-qualified # 
🏃🏻‍♂️‍➡ E15.1 man running facing right: light skin tone
+1F3C3 1F3FB 200D 2642 200D 27A1                        ; minimally-qualified # 
🏃🏻‍♂‍➡ E15.1 man running facing right: light skin tone
+1F3C3 1F3FC 200D 2642 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🏃🏼‍♂️‍➡️ E15.1 man running facing right: medium-light skin tone
+1F3C3 1F3FC 200D 2642 200D 27A1 FE0F                   ; minimally-qualified # 
🏃🏼‍♂‍➡️ E15.1 man running facing right: medium-light skin tone
+1F3C3 1F3FC 200D 2642 FE0F 200D 27A1                   ; minimally-qualified # 
🏃🏼‍♂️‍➡ E15.1 man running facing right: medium-light skin tone
+1F3C3 1F3FC 200D 2642 200D 27A1                        ; minimally-qualified # 
🏃🏼‍♂‍➡ E15.1 man running facing right: medium-light skin tone
+1F3C3 1F3FD 200D 2642 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🏃🏽‍♂️‍➡️ E15.1 man running facing right: medium skin tone
+1F3C3 1F3FD 200D 2642 200D 27A1 FE0F                   ; minimally-qualified # 
🏃🏽‍♂‍➡️ E15.1 man running facing right: medium skin tone
+1F3C3 1F3FD 200D 2642 FE0F 200D 27A1                   ; minimally-qualified # 
🏃🏽‍♂️‍➡ E15.1 man running facing right: medium skin tone
+1F3C3 1F3FD 200D 2642 200D 27A1                        ; minimally-qualified # 
🏃🏽‍♂‍➡ E15.1 man running facing right: medium skin tone
+1F3C3 1F3FE 200D 2642 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🏃🏾‍♂️‍➡️ E15.1 man running facing right: medium-dark skin tone
+1F3C3 1F3FE 200D 2642 200D 27A1 FE0F                   ; minimally-qualified # 
🏃🏾‍♂‍➡️ E15.1 man running facing right: medium-dark skin tone
+1F3C3 1F3FE 200D 2642 FE0F 200D 27A1                   ; minimally-qualified # 
🏃🏾‍♂️‍➡ E15.1 man running facing right: medium-dark skin tone
+1F3C3 1F3FE 200D 2642 200D 27A1                        ; minimally-qualified # 
🏃🏾‍♂‍➡ E15.1 man running facing right: medium-dark skin tone
+1F3C3 1F3FF 200D 2642 FE0F 200D 27A1 FE0F              ; fully-qualified     # 
🏃🏿‍♂️‍➡️ E15.1 man running facing right: dark skin tone
+1F3C3 1F3FF 200D 2642 200D 27A1 FE0F                   ; minimally-qualified # 
🏃🏿‍♂‍➡️ E15.1 man running facing right: dark skin tone
+1F3C3 1F3FF 200D 2642 FE0F 200D 27A1                   ; minimally-qualified # 
🏃🏿‍♂️‍➡ E15.1 man running facing right: dark skin tone
+1F3C3 1F3FF 200D 2642 200D 27A1                        ; minimally-qualified # 
🏃🏿‍♂‍➡ E15.1 man running facing right: dark skin tone
 1F483                                                  ; fully-qualified     # 
💃 E0.6 woman dancing
 1F483 1F3FB                                            ; fully-qualified     # 
💃🏻 E1.0 woman dancing: light skin tone
 1F483 1F3FC                                            ; fully-qualified     # 
💃🏼 E1.0 woman dancing: medium-light skin tone
@@ -3244,7 +3536,6 @@
 1F469 1F3FF 200D 2764 200D 1F469 1F3FE                 ; minimally-qualified # 
👩🏿‍❤‍👩🏾 E13.1 couple with heart: woman, woman, dark skin tone, medium-dark skin 
tone
 1F469 1F3FF 200D 2764 FE0F 200D 1F469 1F3FF            ; fully-qualified     # 
👩🏿‍❤️‍👩🏿 E13.1 couple with heart: woman, woman, dark skin tone
 1F469 1F3FF 200D 2764 200D 1F469 1F3FF                 ; minimally-qualified # 
👩🏿‍❤‍👩🏿 E13.1 couple with heart: woman, woman, dark skin tone
-1F46A                                                  ; fully-qualified     # 
👪 E0.6 family
 1F468 200D 1F469 200D 1F466                            ; fully-qualified     # 
👨‍👩‍👦 E2.0 family: man, woman, boy
 1F468 200D 1F469 200D 1F467                            ; fully-qualified     # 
👨‍👩‍👧 E2.0 family: man, woman, girl
 1F468 200D 1F469 200D 1F467 200D 1F466                 ; fully-qualified     # 
👨‍👩‍👧‍👦 E2.0 family: man, woman, girl, boy
@@ -3277,10 +3568,15 @@
 1F464                                                  ; fully-qualified     # 
👤 E0.6 bust in silhouette
 1F465                                                  ; fully-qualified     # 
👥 E1.0 busts in silhouette
 1FAC2                                                  ; fully-qualified     # 
🫂 E13.0 people hugging
+1F46A                                                  ; fully-qualified     # 
👪 E0.6 family
+1F9D1 200D 1F9D1 200D 1F9D2                            ; fully-qualified     # 
🧑‍🧑‍🧒 E15.1 family: adult, adult, child
+1F9D1 200D 1F9D1 200D 1F9D2 200D 1F9D2                 ; fully-qualified     # 
🧑‍🧑‍🧒‍🧒 E15.1 family: adult, adult, child, child
+1F9D1 200D 1F9D2                                       ; fully-qualified     # 
🧑‍🧒 E15.1 family: adult, child
+1F9D1 200D 1F9D2 200D 1F9D2                            ; fully-qualified     # 
🧑‍🧒‍🧒 E15.1 family: adult, child, child
 1F463                                                  ; fully-qualified     # 
👣 E0.6 footprints
 
-# People & Body subtotal:              2998
-# People & Body subtotal:              508     w/o modifiers
+# People & Body subtotal:              3290
+# People & Body subtotal:              560     w/o modifiers
 
 # group: Component
 
@@ -3395,6 +3691,7 @@
 1FABD                                                  ; fully-qualified     # 
🪽 E15.0 wing
 1F426 200D 2B1B                                        ; fully-qualified     # 
🐦‍⬛ E15.0 black bird
 1FABF                                                  ; fully-qualified     # 
🪿 E15.0 goose
+1F426 200D 1F525                                       ; fully-qualified     # 
🐦‍🔥 E15.1 phoenix
 
 # subgroup: animal-amphibian
 1F438                                                  ; fully-qualified     # 
🐸 E0.6 frog
@@ -3477,8 +3774,8 @@
 1FABA                                                  ; fully-qualified     # 
🪺 E14.0 nest with eggs
 1F344                                                  ; fully-qualified     # 
🍄 E0.6 mushroom
 
-# Animals & Nature subtotal:           159
-# Animals & Nature subtotal:           159     w/o modifiers
+# Animals & Nature subtotal:           160
+# Animals & Nature subtotal:           160     w/o modifiers
 
 # group: Food & Drink
 
@@ -3488,6 +3785,7 @@
 1F349                                                  ; fully-qualified     # 
🍉 E0.6 watermelon
 1F34A                                                  ; fully-qualified     # 
🍊 E0.6 tangerine
 1F34B                                                  ; fully-qualified     # 
🍋 E1.0 lemon
+1F34B 200D 1F7E9                                       ; fully-qualified     # 
🍋‍🟩 E15.1 lime
 1F34C                                                  ; fully-qualified     # 
🍌 E0.6 banana
 1F34D                                                  ; fully-qualified     # 
🍍 E0.6 pineapple
 1F96D                                                  ; fully-qualified     # 
🥭 E11.0 mango
@@ -3522,6 +3820,7 @@
 1F330                                                  ; fully-qualified     # 
🌰 E0.6 chestnut
 1FADA                                                  ; fully-qualified     # 
🫚 E15.0 ginger root
 1FADB                                                  ; fully-qualified     # 
🫛 E15.0 pea pod
+1F344 200D 1F7EB                                       ; fully-qualified     # 
🍄‍🟫 E15.1 brown mushroom
 
 # subgroup: food-prepared
 1F35E                                                  ; fully-qualified     # 
🍞 E0.6 bread
@@ -3633,8 +3932,8 @@
 1FAD9                                                  ; fully-qualified     # 
🫙 E14.0 jar
 1F3FA                                                  ; fully-qualified     # 
🏺 E1.0 amphora
 
-# Food & Drink subtotal:               135
-# Food & Drink subtotal:               135     w/o modifiers
+# Food & Drink subtotal:               137
+# Food & Drink subtotal:               137     w/o modifiers
 
 # group: Travel & Places
 
@@ -4321,6 +4620,8 @@
 2696                                                   ; unqualified         # 
⚖ E1.0 balance scale
 1F9AF                                                  ; fully-qualified     # 
🦯 E12.0 white cane
 1F517                                                  ; fully-qualified     # 
🔗 E0.6 link
+26D3 FE0F 200D 1F4A5                                   ; fully-qualified     # 
⛓️‍💥 E15.1 broken chain
+26D3 200D 1F4A5                                        ; unqualified         # 
⛓‍💥 E15.1 broken chain
 26D3 FE0F                                              ; fully-qualified     # 
⛓️ E0.7 chains
 26D3                                                   ; unqualified         # 
⛓ E0.7 chains
 1FA9D                                                  ; fully-qualified     # 
🪝 E13.0 hook
@@ -4389,8 +4690,8 @@
 1FAA7                                                  ; fully-qualified     # 
🪧 E13.0 placard
 1FAAA                                                  ; fully-qualified     # 
🪪 E14.0 identification card
 
-# Objects subtotal:            310
-# Objects subtotal:            310     w/o modifiers
+# Objects subtotal:            312
+# Objects subtotal:            312     w/o modifiers
 
 # group: Symbols
 
@@ -4979,7 +5280,7 @@
 1F1F9 1F1F2                                            ; fully-qualified     # 
🇹🇲 E2.0 flag: Turkmenistan
 1F1F9 1F1F3                                            ; fully-qualified     # 
🇹🇳 E2.0 flag: Tunisia
 1F1F9 1F1F4                                            ; fully-qualified     # 
🇹🇴 E2.0 flag: Tonga
-1F1F9 1F1F7                                            ; fully-qualified     # 
🇹🇷 E2.0 flag: Turkey
+1F1F9 1F1F7                                            ; fully-qualified     # 
🇹🇷 E2.0 flag: Türkiye
 1F1F9 1F1F9                                            ; fully-qualified     # 
🇹🇹 E2.0 flag: Trinidad & Tobago
 1F1F9 1F1FB                                            ; fully-qualified     # 
🇹🇻 E2.0 flag: Tuvalu
 1F1F9 1F1FC                                            ; fully-qualified     # 
🇹🇼 E2.0 flag: Taiwan
@@ -5016,9 +5317,9 @@
 # Flags subtotal:              275     w/o modifiers
 
 # Status Counts
-# fully-qualified : 3655
-# minimally-qualified : 827
-# unqualified : 242
+# fully-qualified : 3773
+# minimally-qualified : 1009
+# unqualified : 243
 # component : 9
 
 #EOF
diff --git a/admin/unidata/emoji-variation-sequences.txt 
b/admin/unidata/emoji-variation-sequences.txt
index f3396ada19d..d8a3c9f431d 100644
--- a/admin/unidata/emoji-variation-sequences.txt
+++ b/admin/unidata/emoji-variation-sequences.txt
@@ -1,723 +1,757 @@
 # emoji-variation-sequences.txt
-# Date: 2022-05-13, 21:54:24 GMT
-# © 2022 Unicode®, Inc.
+# Date: 2023-02-01, 02:22:54 GMT
+# © 2023 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in 
the U.S. and other countries.
 # For terms of use, see https://www.unicode.org/terms_of_use.html
 #
 # Emoji Variation Sequences for UTS #51
-# Used with Emoji Version 15.0 and subsequent minor revisions (if any)
+# Used with Emoji Version 15.1 and subsequent minor revisions (if any)
 #
 # For documentation and usage, see https://www.unicode.org/reports/tr51
 #
-0023 FE0E  ; text style;  # (1.1 #︎ ) NUMBER SIGN
-0023 FE0F  ; emoji style; # (1.1 #️ ) NUMBER SIGN
-002A FE0E  ; text style;  # (1.1 *︎ ) ASTERISK
-002A FE0F  ; emoji style; # (1.1 *️ ) ASTERISK
-0030 FE0E  ; text style;  # (1.1 0︎ ) DIGIT ZERO
-0030 FE0F  ; emoji style; # (1.1 0️ ) DIGIT ZERO
-0031 FE0E  ; text style;  # (1.1 1︎ ) DIGIT ONE
-0031 FE0F  ; emoji style; # (1.1 1️ ) DIGIT ONE
-0032 FE0E  ; text style;  # (1.1 2︎ ) DIGIT TWO
-0032 FE0F  ; emoji style; # (1.1 2️ ) DIGIT TWO
-0033 FE0E  ; text style;  # (1.1 3︎ ) DIGIT THREE
-0033 FE0F  ; emoji style; # (1.1 3️ ) DIGIT THREE
-0034 FE0E  ; text style;  # (1.1 4︎ ) DIGIT FOUR
-0034 FE0F  ; emoji style; # (1.1 4️ ) DIGIT FOUR
-0035 FE0E  ; text style;  # (1.1 5︎ ) DIGIT FIVE
-0035 FE0F  ; emoji style; # (1.1 5️ ) DIGIT FIVE
-0036 FE0E  ; text style;  # (1.1 6︎ ) DIGIT SIX
-0036 FE0F  ; emoji style; # (1.1 6️ ) DIGIT SIX
-0037 FE0E  ; text style;  # (1.1 7︎ ) DIGIT SEVEN
-0037 FE0F  ; emoji style; # (1.1 7️ ) DIGIT SEVEN
-0038 FE0E  ; text style;  # (1.1 8︎ ) DIGIT EIGHT
-0038 FE0F  ; emoji style; # (1.1 8️ ) DIGIT EIGHT
-0039 FE0E  ; text style;  # (1.1 9︎ ) DIGIT NINE
-0039 FE0F  ; emoji style; # (1.1 9️ ) DIGIT NINE
-00A9 FE0E  ; text style;  # (1.1 ©︎ ) COPYRIGHT SIGN
-00A9 FE0F  ; emoji style; # (1.1 ©️ ) COPYRIGHT SIGN
-00AE FE0E  ; text style;  # (1.1 ®︎ ) REGISTERED SIGN
-00AE FE0F  ; emoji style; # (1.1 ®️ ) REGISTERED SIGN
-203C FE0E  ; text style;  # (1.1 ‼︎ ) DOUBLE EXCLAMATION MARK
-203C FE0F  ; emoji style; # (1.1 ‼️ ) DOUBLE EXCLAMATION MARK
-2049 FE0E  ; text style;  # (3.0 ⁉︎ ) EXCLAMATION QUESTION MARK
-2049 FE0F  ; emoji style; # (3.0 ⁉️ ) EXCLAMATION QUESTION MARK
-2122 FE0E  ; text style;  # (1.1 ™︎ ) TRADE MARK SIGN
-2122 FE0F  ; emoji style; # (1.1 ™️ ) TRADE MARK SIGN
-2139 FE0E  ; text style;  # (3.0 ℹ︎ ) INFORMATION SOURCE
-2139 FE0F  ; emoji style; # (3.0 ℹ️ ) INFORMATION SOURCE
-2194 FE0E  ; text style;  # (1.1 ↔︎ ) LEFT RIGHT ARROW
-2194 FE0F  ; emoji style; # (1.1 ↔️ ) LEFT RIGHT ARROW
-2195 FE0E  ; text style;  # (1.1 ↕︎ ) UP DOWN ARROW
-2195 FE0F  ; emoji style; # (1.1 ↕️ ) UP DOWN ARROW
-2196 FE0E  ; text style;  # (1.1 ↖︎ ) NORTH WEST ARROW
-2196 FE0F  ; emoji style; # (1.1 ↖️ ) NORTH WEST ARROW
-2197 FE0E  ; text style;  # (1.1 ↗︎ ) NORTH EAST ARROW
-2197 FE0F  ; emoji style; # (1.1 ↗️ ) NORTH EAST ARROW
-2198 FE0E  ; text style;  # (1.1 ↘︎ ) SOUTH EAST ARROW
-2198 FE0F  ; emoji style; # (1.1 ↘️ ) SOUTH EAST ARROW
-2199 FE0E  ; text style;  # (1.1 ↙︎ ) SOUTH WEST ARROW
-2199 FE0F  ; emoji style; # (1.1 ↙️ ) SOUTH WEST ARROW
-21A9 FE0E  ; text style;  # (1.1 ↩︎ ) LEFTWARDS ARROW WITH HOOK
-21A9 FE0F  ; emoji style; # (1.1 ↩️ ) LEFTWARDS ARROW WITH HOOK
-21AA FE0E  ; text style;  # (1.1 ↪︎ ) RIGHTWARDS ARROW WITH HOOK
-21AA FE0F  ; emoji style; # (1.1 ↪️ ) RIGHTWARDS ARROW WITH HOOK
-231A FE0E  ; text style;  # (1.1 ⌚︎ ) WATCH
-231A FE0F  ; emoji style; # (1.1 ⌚️ ) WATCH
-231B FE0E  ; text style;  # (1.1 ⌛︎ ) HOURGLASS
-231B FE0F  ; emoji style; # (1.1 ⌛️ ) HOURGLASS
-2328 FE0E  ; text style;  # (1.1 ⌨︎ ) KEYBOARD
-2328 FE0F  ; emoji style; # (1.1 ⌨️ ) KEYBOARD
-23CF FE0E  ; text style;  # (4.0 ⏏︎ ) EJECT SYMBOL
-23CF FE0F  ; emoji style; # (4.0 ⏏️ ) EJECT SYMBOL
-23E9 FE0E  ; text style;  # (6.0 ⏩︎ ) BLACK RIGHT-POINTING DOUBLE TRIANGLE
-23E9 FE0F  ; emoji style; # (6.0 ⏩️ ) BLACK RIGHT-POINTING DOUBLE TRIANGLE
-23EA FE0E  ; text style;  # (6.0 ⏪︎ ) BLACK LEFT-POINTING DOUBLE TRIANGLE
-23EA FE0F  ; emoji style; # (6.0 ⏪️ ) BLACK LEFT-POINTING DOUBLE TRIANGLE
-23ED FE0E  ; text style;  # (6.0 ⏭︎ ) BLACK RIGHT-POINTING DOUBLE TRIANGLE 
WITH VERTICAL BAR
-23ED FE0F  ; emoji style; # (6.0 ⏭️ ) BLACK RIGHT-POINTING DOUBLE TRIANGLE 
WITH VERTICAL BAR
-23EE FE0E  ; text style;  # (6.0 ⏮︎ ) BLACK LEFT-POINTING DOUBLE TRIANGLE WITH 
VERTICAL BAR
-23EE FE0F  ; emoji style; # (6.0 ⏮️ ) BLACK LEFT-POINTING DOUBLE TRIANGLE WITH 
VERTICAL BAR
-23EF FE0E  ; text style;  # (6.0 ⏯︎ ) BLACK RIGHT-POINTING TRIANGLE WITH 
DOUBLE VERTICAL BAR
-23EF FE0F  ; emoji style; # (6.0 ⏯️ ) BLACK RIGHT-POINTING TRIANGLE WITH 
DOUBLE VERTICAL BAR
-23F1 FE0E  ; text style;  # (6.0 ⏱︎ ) STOPWATCH
-23F1 FE0F  ; emoji style; # (6.0 ⏱️ ) STOPWATCH
-23F2 FE0E  ; text style;  # (6.0 ⏲︎ ) TIMER CLOCK
-23F2 FE0F  ; emoji style; # (6.0 ⏲️ ) TIMER CLOCK
-23F3 FE0E  ; text style;  # (6.0 ⏳︎ ) HOURGLASS WITH FLOWING SAND
-23F3 FE0F  ; emoji style; # (6.0 ⏳️ ) HOURGLASS WITH FLOWING SAND
-23F8 FE0E  ; text style;  # (7.0 ⏸︎ ) DOUBLE VERTICAL BAR
-23F8 FE0F  ; emoji style; # (7.0 ⏸️ ) DOUBLE VERTICAL BAR
-23F9 FE0E  ; text style;  # (7.0 ⏹︎ ) BLACK SQUARE FOR STOP
-23F9 FE0F  ; emoji style; # (7.0 ⏹️ ) BLACK SQUARE FOR STOP
-23FA FE0E  ; text style;  # (7.0 ⏺︎ ) BLACK CIRCLE FOR RECORD
-23FA FE0F  ; emoji style; # (7.0 ⏺️ ) BLACK CIRCLE FOR RECORD
-24C2 FE0E  ; text style;  # (1.1 Ⓜ︎ ) CIRCLED LATIN CAPITAL LETTER M
-24C2 FE0F  ; emoji style; # (1.1 Ⓜ️ ) CIRCLED LATIN CAPITAL LETTER M
-25AA FE0E  ; text style;  # (1.1 ▪︎ ) BLACK SMALL SQUARE
-25AA FE0F  ; emoji style; # (1.1 ▪️ ) BLACK SMALL SQUARE
-25AB FE0E  ; text style;  # (1.1 ▫︎ ) WHITE SMALL SQUARE
-25AB FE0F  ; emoji style; # (1.1 ▫️ ) WHITE SMALL SQUARE
-25B6 FE0E  ; text style;  # (1.1 ▶︎ ) BLACK RIGHT-POINTING TRIANGLE
-25B6 FE0F  ; emoji style; # (1.1 ▶️ ) BLACK RIGHT-POINTING TRIANGLE
-25C0 FE0E  ; text style;  # (1.1 ◀︎ ) BLACK LEFT-POINTING TRIANGLE
-25C0 FE0F  ; emoji style; # (1.1 ◀️ ) BLACK LEFT-POINTING TRIANGLE
-25FB FE0E  ; text style;  # (3.2 ◻︎ ) WHITE MEDIUM SQUARE
-25FB FE0F  ; emoji style; # (3.2 ◻️ ) WHITE MEDIUM SQUARE
-25FC FE0E  ; text style;  # (3.2 ◼︎ ) BLACK MEDIUM SQUARE
-25FC FE0F  ; emoji style; # (3.2 ◼️ ) BLACK MEDIUM SQUARE
-25FD FE0E  ; text style;  # (3.2 ◽︎ ) WHITE MEDIUM SMALL SQUARE
-25FD FE0F  ; emoji style; # (3.2 ◽️ ) WHITE MEDIUM SMALL SQUARE
-25FE FE0E  ; text style;  # (3.2 ◾︎ ) BLACK MEDIUM SMALL SQUARE
-25FE FE0F  ; emoji style; # (3.2 ◾️ ) BLACK MEDIUM SMALL SQUARE
-2600 FE0E  ; text style;  # (1.1 ☀︎ ) BLACK SUN WITH RAYS
-2600 FE0F  ; emoji style; # (1.1 ☀️ ) BLACK SUN WITH RAYS
-2601 FE0E  ; text style;  # (1.1 ☁︎ ) CLOUD
-2601 FE0F  ; emoji style; # (1.1 ☁️ ) CLOUD
-2602 FE0E  ; text style;  # (1.1 ☂︎ ) UMBRELLA
-2602 FE0F  ; emoji style; # (1.1 ☂️ ) UMBRELLA
-2603 FE0E  ; text style;  # (1.1 ☃︎ ) SNOWMAN
-2603 FE0F  ; emoji style; # (1.1 ☃️ ) SNOWMAN
-2604 FE0E  ; text style;  # (1.1 ☄︎ ) COMET
-2604 FE0F  ; emoji style; # (1.1 ☄️ ) COMET
-260E FE0E  ; text style;  # (1.1 ☎︎ ) BLACK TELEPHONE
-260E FE0F  ; emoji style; # (1.1 ☎️ ) BLACK TELEPHONE
-2611 FE0E  ; text style;  # (1.1 ☑︎ ) BALLOT BOX WITH CHECK
-2611 FE0F  ; emoji style; # (1.1 ☑️ ) BALLOT BOX WITH CHECK
-2614 FE0E  ; text style;  # (4.0 ☔︎ ) UMBRELLA WITH RAIN DROPS
-2614 FE0F  ; emoji style; # (4.0 ☔️ ) UMBRELLA WITH RAIN DROPS
-2615 FE0E  ; text style;  # (4.0 ☕︎ ) HOT BEVERAGE
-2615 FE0F  ; emoji style; # (4.0 ☕️ ) HOT BEVERAGE
-2618 FE0E  ; text style;  # (4.1 ☘︎ ) SHAMROCK
-2618 FE0F  ; emoji style; # (4.1 ☘️ ) SHAMROCK
-261D FE0E  ; text style;  # (1.1 ☝︎ ) WHITE UP POINTING INDEX
-261D FE0F  ; emoji style; # (1.1 ☝️ ) WHITE UP POINTING INDEX
-2620 FE0E  ; text style;  # (1.1 ☠︎ ) SKULL AND CROSSBONES
-2620 FE0F  ; emoji style; # (1.1 ☠️ ) SKULL AND CROSSBONES
-2622 FE0E  ; text style;  # (1.1 ☢︎ ) RADIOACTIVE SIGN
-2622 FE0F  ; emoji style; # (1.1 ☢️ ) RADIOACTIVE SIGN
-2623 FE0E  ; text style;  # (1.1 ☣︎ ) BIOHAZARD SIGN
-2623 FE0F  ; emoji style; # (1.1 ☣️ ) BIOHAZARD SIGN
-2626 FE0E  ; text style;  # (1.1 ☦︎ ) ORTHODOX CROSS
-2626 FE0F  ; emoji style; # (1.1 ☦️ ) ORTHODOX CROSS
-262A FE0E  ; text style;  # (1.1 ☪︎ ) STAR AND CRESCENT
-262A FE0F  ; emoji style; # (1.1 ☪️ ) STAR AND CRESCENT
-262E FE0E  ; text style;  # (1.1 ☮︎ ) PEACE SYMBOL
-262E FE0F  ; emoji style; # (1.1 ☮️ ) PEACE SYMBOL
-262F FE0E  ; text style;  # (1.1 ☯︎ ) YIN YANG
-262F FE0F  ; emoji style; # (1.1 ☯️ ) YIN YANG
-2638 FE0E  ; text style;  # (1.1 ☸︎ ) WHEEL OF DHARMA
-2638 FE0F  ; emoji style; # (1.1 ☸️ ) WHEEL OF DHARMA
-2639 FE0E  ; text style;  # (1.1 ☹︎ ) WHITE FROWNING FACE
-2639 FE0F  ; emoji style; # (1.1 ☹️ ) WHITE FROWNING FACE
-263A FE0E  ; text style;  # (1.1 ☺︎ ) WHITE SMILING FACE
-263A FE0F  ; emoji style; # (1.1 ☺️ ) WHITE SMILING FACE
-2640 FE0E  ; text style;  # (1.1 ♀︎ ) FEMALE SIGN
-2640 FE0F  ; emoji style; # (1.1 ♀️ ) FEMALE SIGN
-2642 FE0E  ; text style;  # (1.1 ♂︎ ) MALE SIGN
-2642 FE0F  ; emoji style; # (1.1 ♂️ ) MALE SIGN
-2648 FE0E  ; text style;  # (1.1 ♈︎ ) ARIES
-2648 FE0F  ; emoji style; # (1.1 ♈️ ) ARIES
-2649 FE0E  ; text style;  # (1.1 ♉︎ ) TAURUS
-2649 FE0F  ; emoji style; # (1.1 ♉️ ) TAURUS
-264A FE0E  ; text style;  # (1.1 ♊︎ ) GEMINI
-264A FE0F  ; emoji style; # (1.1 ♊️ ) GEMINI
-264B FE0E  ; text style;  # (1.1 ♋︎ ) CANCER
-264B FE0F  ; emoji style; # (1.1 ♋️ ) CANCER
-264C FE0E  ; text style;  # (1.1 ♌︎ ) LEO
-264C FE0F  ; emoji style; # (1.1 ♌️ ) LEO
-264D FE0E  ; text style;  # (1.1 ♍︎ ) VIRGO
-264D FE0F  ; emoji style; # (1.1 ♍️ ) VIRGO
-264E FE0E  ; text style;  # (1.1 ♎︎ ) LIBRA
-264E FE0F  ; emoji style; # (1.1 ♎️ ) LIBRA
-264F FE0E  ; text style;  # (1.1 ♏︎ ) SCORPIUS
-264F FE0F  ; emoji style; # (1.1 ♏️ ) SCORPIUS
-2650 FE0E  ; text style;  # (1.1 ♐︎ ) SAGITTARIUS
-2650 FE0F  ; emoji style; # (1.1 ♐️ ) SAGITTARIUS
-2651 FE0E  ; text style;  # (1.1 ♑︎ ) CAPRICORN
-2651 FE0F  ; emoji style; # (1.1 ♑️ ) CAPRICORN
-2652 FE0E  ; text style;  # (1.1 ♒︎ ) AQUARIUS
-2652 FE0F  ; emoji style; # (1.1 ♒️ ) AQUARIUS
-2653 FE0E  ; text style;  # (1.1 ♓︎ ) PISCES
-2653 FE0F  ; emoji style; # (1.1 ♓️ ) PISCES
-265F FE0E  ; text style;  # (1.1 ♟︎ ) BLACK CHESS PAWN
-265F FE0F  ; emoji style; # (1.1 ♟️ ) BLACK CHESS PAWN
-2660 FE0E  ; text style;  # (1.1 ♠︎ ) BLACK SPADE SUIT
-2660 FE0F  ; emoji style; # (1.1 ♠️ ) BLACK SPADE SUIT
-2663 FE0E  ; text style;  # (1.1 ♣︎ ) BLACK CLUB SUIT
-2663 FE0F  ; emoji style; # (1.1 ♣️ ) BLACK CLUB SUIT
-2665 FE0E  ; text style;  # (1.1 ♥︎ ) BLACK HEART SUIT
-2665 FE0F  ; emoji style; # (1.1 ♥️ ) BLACK HEART SUIT
-2666 FE0E  ; text style;  # (1.1 ♦︎ ) BLACK DIAMOND SUIT
-2666 FE0F  ; emoji style; # (1.1 ♦️ ) BLACK DIAMOND SUIT
-2668 FE0E  ; text style;  # (1.1 ♨︎ ) HOT SPRINGS
-2668 FE0F  ; emoji style; # (1.1 ♨️ ) HOT SPRINGS
-267B FE0E  ; text style;  # (3.2 ♻︎ ) BLACK UNIVERSAL RECYCLING SYMBOL
-267B FE0F  ; emoji style; # (3.2 ♻️ ) BLACK UNIVERSAL RECYCLING SYMBOL
-267E FE0E  ; text style;  # (4.1 ♾︎ ) PERMANENT PAPER SIGN
-267E FE0F  ; emoji style; # (4.1 ♾️ ) PERMANENT PAPER SIGN
-267F FE0E  ; text style;  # (4.1 ♿︎ ) WHEELCHAIR SYMBOL
-267F FE0F  ; emoji style; # (4.1 ♿️ ) WHEELCHAIR SYMBOL
-2692 FE0E  ; text style;  # (4.1 ⚒︎ ) HAMMER AND PICK
-2692 FE0F  ; emoji style; # (4.1 ⚒️ ) HAMMER AND PICK
-2693 FE0E  ; text style;  # (4.1 ⚓︎ ) ANCHOR
-2693 FE0F  ; emoji style; # (4.1 ⚓️ ) ANCHOR
-2694 FE0E  ; text style;  # (4.1 ⚔︎ ) CROSSED SWORDS
-2694 FE0F  ; emoji style; # (4.1 ⚔️ ) CROSSED SWORDS
-2695 FE0E  ; text style;  # (4.1 ⚕︎ ) STAFF OF AESCULAPIUS
-2695 FE0F  ; emoji style; # (4.1 ⚕️ ) STAFF OF AESCULAPIUS
-2696 FE0E  ; text style;  # (4.1 ⚖︎ ) SCALES
-2696 FE0F  ; emoji style; # (4.1 ⚖️ ) SCALES
-2697 FE0E  ; text style;  # (4.1 ⚗︎ ) ALEMBIC
-2697 FE0F  ; emoji style; # (4.1 ⚗️ ) ALEMBIC
-2699 FE0E  ; text style;  # (4.1 ⚙︎ ) GEAR
-2699 FE0F  ; emoji style; # (4.1 ⚙️ ) GEAR
-269B FE0E  ; text style;  # (4.1 ⚛︎ ) ATOM SYMBOL
-269B FE0F  ; emoji style; # (4.1 ⚛️ ) ATOM SYMBOL
-269C FE0E  ; text style;  # (4.1 ⚜︎ ) FLEUR-DE-LIS
-269C FE0F  ; emoji style; # (4.1 ⚜️ ) FLEUR-DE-LIS
-26A0 FE0E  ; text style;  # (4.0 ⚠︎ ) WARNING SIGN
-26A0 FE0F  ; emoji style; # (4.0 ⚠️ ) WARNING SIGN
-26A1 FE0E  ; text style;  # (4.0 ⚡︎ ) HIGH VOLTAGE SIGN
-26A1 FE0F  ; emoji style; # (4.0 ⚡️ ) HIGH VOLTAGE SIGN
-26A7 FE0E  ; text style;  # (4.1 ⚧︎ ) MALE WITH STROKE AND MALE AND FEMALE SIGN
-26A7 FE0F  ; emoji style; # (4.1 ⚧️ ) MALE WITH STROKE AND MALE AND FEMALE SIGN
-26AA FE0E  ; text style;  # (4.1 ⚪︎ ) MEDIUM WHITE CIRCLE
-26AA FE0F  ; emoji style; # (4.1 ⚪️ ) MEDIUM WHITE CIRCLE
-26AB FE0E  ; text style;  # (4.1 ⚫︎ ) MEDIUM BLACK CIRCLE
-26AB FE0F  ; emoji style; # (4.1 ⚫️ ) MEDIUM BLACK CIRCLE
-26B0 FE0E  ; text style;  # (4.1 ⚰︎ ) COFFIN
-26B0 FE0F  ; emoji style; # (4.1 ⚰️ ) COFFIN
-26B1 FE0E  ; text style;  # (4.1 ⚱︎ ) FUNERAL URN
-26B1 FE0F  ; emoji style; # (4.1 ⚱️ ) FUNERAL URN
-26BD FE0E  ; text style;  # (5.2 ⚽︎ ) SOCCER BALL
-26BD FE0F  ; emoji style; # (5.2 ⚽️ ) SOCCER BALL
-26BE FE0E  ; text style;  # (5.2 ⚾︎ ) BASEBALL
-26BE FE0F  ; emoji style; # (5.2 ⚾️ ) BASEBALL
-26C4 FE0E  ; text style;  # (5.2 ⛄︎ ) SNOWMAN WITHOUT SNOW
-26C4 FE0F  ; emoji style; # (5.2 ⛄️ ) SNOWMAN WITHOUT SNOW
-26C5 FE0E  ; text style;  # (5.2 ⛅︎ ) SUN BEHIND CLOUD
-26C5 FE0F  ; emoji style; # (5.2 ⛅️ ) SUN BEHIND CLOUD
-26C8 FE0E  ; text style;  # (5.2 ⛈︎ ) THUNDER CLOUD AND RAIN
-26C8 FE0F  ; emoji style; # (5.2 ⛈️ ) THUNDER CLOUD AND RAIN
-26CF FE0E  ; text style;  # (5.2 ⛏︎ ) PICK
-26CF FE0F  ; emoji style; # (5.2 ⛏️ ) PICK
-26D1 FE0E  ; text style;  # (5.2 ⛑︎ ) HELMET WITH WHITE CROSS
-26D1 FE0F  ; emoji style; # (5.2 ⛑️ ) HELMET WITH WHITE CROSS
-26D3 FE0E  ; text style;  # (5.2 ⛓︎ ) CHAINS
-26D3 FE0F  ; emoji style; # (5.2 ⛓️ ) CHAINS
-26D4 FE0E  ; text style;  # (5.2 ⛔︎ ) NO ENTRY
-26D4 FE0F  ; emoji style; # (5.2 ⛔️ ) NO ENTRY
-26E9 FE0E  ; text style;  # (5.2 ⛩︎ ) SHINTO SHRINE
-26E9 FE0F  ; emoji style; # (5.2 ⛩️ ) SHINTO SHRINE
-26EA FE0E  ; text style;  # (5.2 ⛪︎ ) CHURCH
-26EA FE0F  ; emoji style; # (5.2 ⛪️ ) CHURCH
-26F0 FE0E  ; text style;  # (5.2 ⛰︎ ) MOUNTAIN
-26F0 FE0F  ; emoji style; # (5.2 ⛰️ ) MOUNTAIN
-26F1 FE0E  ; text style;  # (5.2 ⛱︎ ) UMBRELLA ON GROUND
-26F1 FE0F  ; emoji style; # (5.2 ⛱️ ) UMBRELLA ON GROUND
-26F2 FE0E  ; text style;  # (5.2 ⛲︎ ) FOUNTAIN
-26F2 FE0F  ; emoji style; # (5.2 ⛲️ ) FOUNTAIN
-26F3 FE0E  ; text style;  # (5.2 ⛳︎ ) FLAG IN HOLE
-26F3 FE0F  ; emoji style; # (5.2 ⛳️ ) FLAG IN HOLE
-26F4 FE0E  ; text style;  # (5.2 ⛴︎ ) FERRY
-26F4 FE0F  ; emoji style; # (5.2 ⛴️ ) FERRY
-26F5 FE0E  ; text style;  # (5.2 ⛵︎ ) SAILBOAT
-26F5 FE0F  ; emoji style; # (5.2 ⛵️ ) SAILBOAT
-26F7 FE0E  ; text style;  # (5.2 ⛷︎ ) SKIER
-26F7 FE0F  ; emoji style; # (5.2 ⛷️ ) SKIER
-26F8 FE0E  ; text style;  # (5.2 ⛸︎ ) ICE SKATE
-26F8 FE0F  ; emoji style; # (5.2 ⛸️ ) ICE SKATE
-26F9 FE0E  ; text style;  # (5.2 ⛹︎ ) PERSON WITH BALL
-26F9 FE0F  ; emoji style; # (5.2 ⛹️ ) PERSON WITH BALL
-26FA FE0E  ; text style;  # (5.2 ⛺︎ ) TENT
-26FA FE0F  ; emoji style; # (5.2 ⛺️ ) TENT
-26FD FE0E  ; text style;  # (5.2 ⛽︎ ) FUEL PUMP
-26FD FE0F  ; emoji style; # (5.2 ⛽️ ) FUEL PUMP
-2702 FE0E  ; text style;  # (1.1 ✂︎ ) BLACK SCISSORS
-2702 FE0F  ; emoji style; # (1.1 ✂️ ) BLACK SCISSORS
-2708 FE0E  ; text style;  # (1.1 ✈︎ ) AIRPLANE
-2708 FE0F  ; emoji style; # (1.1 ✈️ ) AIRPLANE
-2709 FE0E  ; text style;  # (1.1 ✉︎ ) ENVELOPE
-2709 FE0F  ; emoji style; # (1.1 ✉️ ) ENVELOPE
-270C FE0E  ; text style;  # (1.1 ✌︎ ) VICTORY HAND
-270C FE0F  ; emoji style; # (1.1 ✌️ ) VICTORY HAND
-270D FE0E  ; text style;  # (1.1 ✍︎ ) WRITING HAND
-270D FE0F  ; emoji style; # (1.1 ✍️ ) WRITING HAND
-270F FE0E  ; text style;  # (1.1 ✏︎ ) PENCIL
-270F FE0F  ; emoji style; # (1.1 ✏️ ) PENCIL
-2712 FE0E  ; text style;  # (1.1 ✒︎ ) BLACK NIB
-2712 FE0F  ; emoji style; # (1.1 ✒️ ) BLACK NIB
-2714 FE0E  ; text style;  # (1.1 ✔︎ ) HEAVY CHECK MARK
-2714 FE0F  ; emoji style; # (1.1 ✔️ ) HEAVY CHECK MARK
-2716 FE0E  ; text style;  # (1.1 ✖︎ ) HEAVY MULTIPLICATION X
-2716 FE0F  ; emoji style; # (1.1 ✖️ ) HEAVY MULTIPLICATION X
-271D FE0E  ; text style;  # (1.1 ✝︎ ) LATIN CROSS
-271D FE0F  ; emoji style; # (1.1 ✝️ ) LATIN CROSS
-2721 FE0E  ; text style;  # (1.1 ✡︎ ) STAR OF DAVID
-2721 FE0F  ; emoji style; # (1.1 ✡️ ) STAR OF DAVID
-2733 FE0E  ; text style;  # (1.1 ✳︎ ) EIGHT SPOKED ASTERISK
-2733 FE0F  ; emoji style; # (1.1 ✳️ ) EIGHT SPOKED ASTERISK
-2734 FE0E  ; text style;  # (1.1 ✴︎ ) EIGHT POINTED BLACK STAR
-2734 FE0F  ; emoji style; # (1.1 ✴️ ) EIGHT POINTED BLACK STAR
-2744 FE0E  ; text style;  # (1.1 ❄︎ ) SNOWFLAKE
-2744 FE0F  ; emoji style; # (1.1 ❄️ ) SNOWFLAKE
-2747 FE0E  ; text style;  # (1.1 ❇︎ ) SPARKLE
-2747 FE0F  ; emoji style; # (1.1 ❇️ ) SPARKLE
-2753 FE0E  ; text style;  # (6.0 ❓︎ ) BLACK QUESTION MARK ORNAMENT
-2753 FE0F  ; emoji style; # (6.0 ❓️ ) BLACK QUESTION MARK ORNAMENT
-2757 FE0E  ; text style;  # (5.2 ❗︎ ) HEAVY EXCLAMATION MARK SYMBOL
-2757 FE0F  ; emoji style; # (5.2 ❗️ ) HEAVY EXCLAMATION MARK SYMBOL
-2763 FE0E  ; text style;  # (1.1 ❣︎ ) HEAVY HEART EXCLAMATION MARK ORNAMENT
-2763 FE0F  ; emoji style; # (1.1 ❣️ ) HEAVY HEART EXCLAMATION MARK ORNAMENT
-2764 FE0E  ; text style;  # (1.1 ❤︎ ) HEAVY BLACK HEART
-2764 FE0F  ; emoji style; # (1.1 ❤️ ) HEAVY BLACK HEART
-27A1 FE0E  ; text style;  # (1.1 ➡︎ ) BLACK RIGHTWARDS ARROW
-27A1 FE0F  ; emoji style; # (1.1 ➡️ ) BLACK RIGHTWARDS ARROW
-2934 FE0E  ; text style;  # (3.2 ⤴︎ ) ARROW POINTING RIGHTWARDS THEN CURVING 
UPWARDS
-2934 FE0F  ; emoji style; # (3.2 ⤴️ ) ARROW POINTING RIGHTWARDS THEN CURVING 
UPWARDS
-2935 FE0E  ; text style;  # (3.2 ⤵︎ ) ARROW POINTING RIGHTWARDS THEN CURVING 
DOWNWARDS
-2935 FE0F  ; emoji style; # (3.2 ⤵️ ) ARROW POINTING RIGHTWARDS THEN CURVING 
DOWNWARDS
-2B05 FE0E  ; text style;  # (4.0 ⬅︎ ) LEFTWARDS BLACK ARROW
-2B05 FE0F  ; emoji style; # (4.0 ⬅️ ) LEFTWARDS BLACK ARROW
-2B06 FE0E  ; text style;  # (4.0 ⬆︎ ) UPWARDS BLACK ARROW
-2B06 FE0F  ; emoji style; # (4.0 ⬆️ ) UPWARDS BLACK ARROW
-2B07 FE0E  ; text style;  # (4.0 ⬇︎ ) DOWNWARDS BLACK ARROW
-2B07 FE0F  ; emoji style; # (4.0 ⬇️ ) DOWNWARDS BLACK ARROW
-2B1B FE0E  ; text style;  # (5.1 ⬛︎ ) BLACK LARGE SQUARE
-2B1B FE0F  ; emoji style; # (5.1 ⬛️ ) BLACK LARGE SQUARE
-2B1C FE0E  ; text style;  # (5.1 ⬜︎ ) WHITE LARGE SQUARE
-2B1C FE0F  ; emoji style; # (5.1 ⬜️ ) WHITE LARGE SQUARE
-2B50 FE0E  ; text style;  # (5.1 ⭐︎ ) WHITE MEDIUM STAR
-2B50 FE0F  ; emoji style; # (5.1 ⭐️ ) WHITE MEDIUM STAR
-2B55 FE0E  ; text style;  # (5.2 ⭕︎ ) HEAVY LARGE CIRCLE
-2B55 FE0F  ; emoji style; # (5.2 ⭕️ ) HEAVY LARGE CIRCLE
-3030 FE0E  ; text style;  # (1.1 〰︎ ) WAVY DASH
-3030 FE0F  ; emoji style; # (1.1 〰️ ) WAVY DASH
-303D FE0E  ; text style;  # (3.2 〽︎ ) PART ALTERNATION MARK
-303D FE0F  ; emoji style; # (3.2 〽️ ) PART ALTERNATION MARK
-3297 FE0E  ; text style;  # (1.1 ㊗︎ ) CIRCLED IDEOGRAPH CONGRATULATION
-3297 FE0F  ; emoji style; # (1.1 ㊗️ ) CIRCLED IDEOGRAPH CONGRATULATION
-3299 FE0E  ; text style;  # (1.1 ㊙︎ ) CIRCLED IDEOGRAPH SECRET
-3299 FE0F  ; emoji style; # (1.1 ㊙️ ) CIRCLED IDEOGRAPH SECRET
-1F004 FE0E ; text style;  # (5.1 🀄︎ ) MAHJONG TILE RED DRAGON
-1F004 FE0F ; emoji style; # (5.1 🀄️ ) MAHJONG TILE RED DRAGON
-1F170 FE0E ; text style;  # (6.0 🅰︎ ) NEGATIVE SQUARED LATIN CAPITAL LETTER A
-1F170 FE0F ; emoji style; # (6.0 🅰️ ) NEGATIVE SQUARED LATIN CAPITAL LETTER A
-1F171 FE0E ; text style;  # (6.0 🅱︎ ) NEGATIVE SQUARED LATIN CAPITAL LETTER B
-1F171 FE0F ; emoji style; # (6.0 🅱️ ) NEGATIVE SQUARED LATIN CAPITAL LETTER B
-1F17E FE0E ; text style;  # (6.0 🅾︎ ) NEGATIVE SQUARED LATIN CAPITAL LETTER O
-1F17E FE0F ; emoji style; # (6.0 🅾️ ) NEGATIVE SQUARED LATIN CAPITAL LETTER O
-1F17F FE0E ; text style;  # (5.2 🅿︎ ) NEGATIVE SQUARED LATIN CAPITAL LETTER P
-1F17F FE0F ; emoji style; # (5.2 🅿️ ) NEGATIVE SQUARED LATIN CAPITAL LETTER P
-1F202 FE0E ; text style;  # (6.0 🈂︎ ) SQUARED KATAKANA SA
-1F202 FE0F ; emoji style; # (6.0 🈂️ ) SQUARED KATAKANA SA
-1F21A FE0E ; text style;  # (5.2 🈚︎ ) SQUARED CJK UNIFIED IDEOGRAPH-7121
-1F21A FE0F ; emoji style; # (5.2 🈚️ ) SQUARED CJK UNIFIED IDEOGRAPH-7121
-1F22F FE0E ; text style;  # (5.2 🈯︎ ) SQUARED CJK UNIFIED IDEOGRAPH-6307
-1F22F FE0F ; emoji style; # (5.2 🈯️ ) SQUARED CJK UNIFIED IDEOGRAPH-6307
-1F237 FE0E ; text style;  # (6.0 🈷︎ ) SQUARED CJK UNIFIED IDEOGRAPH-6708
-1F237 FE0F ; emoji style; # (6.0 🈷️ ) SQUARED CJK UNIFIED IDEOGRAPH-6708
-1F30D FE0E ; text style;  # (6.0 🌍︎ ) EARTH GLOBE EUROPE-AFRICA
-1F30D FE0F ; emoji style; # (6.0 🌍️ ) EARTH GLOBE EUROPE-AFRICA
-1F30E FE0E ; text style;  # (6.0 🌎︎ ) EARTH GLOBE AMERICAS
-1F30E FE0F ; emoji style; # (6.0 🌎️ ) EARTH GLOBE AMERICAS
-1F30F FE0E ; text style;  # (6.0 🌏︎ ) EARTH GLOBE ASIA-AUSTRALIA
-1F30F FE0F ; emoji style; # (6.0 🌏️ ) EARTH GLOBE ASIA-AUSTRALIA
-1F315 FE0E ; text style;  # (6.0 🌕︎ ) FULL MOON SYMBOL
-1F315 FE0F ; emoji style; # (6.0 🌕️ ) FULL MOON SYMBOL
-1F31C FE0E ; text style;  # (6.0 🌜︎ ) LAST QUARTER MOON WITH FACE
-1F31C FE0F ; emoji style; # (6.0 🌜️ ) LAST QUARTER MOON WITH FACE
-1F321 FE0E ; text style;  # (7.0 🌡︎ ) THERMOMETER
-1F321 FE0F ; emoji style; # (7.0 🌡️ ) THERMOMETER
-1F324 FE0E ; text style;  # (7.0 🌤︎ ) WHITE SUN WITH SMALL CLOUD
-1F324 FE0F ; emoji style; # (7.0 🌤️ ) WHITE SUN WITH SMALL CLOUD
-1F325 FE0E ; text style;  # (7.0 🌥︎ ) WHITE SUN BEHIND CLOUD
-1F325 FE0F ; emoji style; # (7.0 🌥️ ) WHITE SUN BEHIND CLOUD
-1F326 FE0E ; text style;  # (7.0 🌦︎ ) WHITE SUN BEHIND CLOUD WITH RAIN
-1F326 FE0F ; emoji style; # (7.0 🌦️ ) WHITE SUN BEHIND CLOUD WITH RAIN
-1F327 FE0E ; text style;  # (7.0 🌧︎ ) CLOUD WITH RAIN
-1F327 FE0F ; emoji style; # (7.0 🌧️ ) CLOUD WITH RAIN
-1F328 FE0E ; text style;  # (7.0 🌨︎ ) CLOUD WITH SNOW
-1F328 FE0F ; emoji style; # (7.0 🌨️ ) CLOUD WITH SNOW
-1F329 FE0E ; text style;  # (7.0 🌩︎ ) CLOUD WITH LIGHTNING
-1F329 FE0F ; emoji style; # (7.0 🌩️ ) CLOUD WITH LIGHTNING
-1F32A FE0E ; text style;  # (7.0 🌪︎ ) CLOUD WITH TORNADO
-1F32A FE0F ; emoji style; # (7.0 🌪️ ) CLOUD WITH TORNADO
-1F32B FE0E ; text style;  # (7.0 🌫︎ ) FOG
-1F32B FE0F ; emoji style; # (7.0 🌫️ ) FOG
-1F32C FE0E ; text style;  # (7.0 🌬︎ ) WIND BLOWING FACE
-1F32C FE0F ; emoji style; # (7.0 🌬️ ) WIND BLOWING FACE
-1F336 FE0E ; text style;  # (7.0 🌶︎ ) HOT PEPPER
-1F336 FE0F ; emoji style; # (7.0 🌶️ ) HOT PEPPER
-1F378 FE0E ; text style;  # (6.0 🍸︎ ) COCKTAIL GLASS
-1F378 FE0F ; emoji style; # (6.0 🍸️ ) COCKTAIL GLASS
-1F37D FE0E ; text style;  # (7.0 🍽︎ ) FORK AND KNIFE WITH PLATE
-1F37D FE0F ; emoji style; # (7.0 🍽️ ) FORK AND KNIFE WITH PLATE
-1F393 FE0E ; text style;  # (6.0 🎓︎ ) GRADUATION CAP
-1F393 FE0F ; emoji style; # (6.0 🎓️ ) GRADUATION CAP
-1F396 FE0E ; text style;  # (7.0 🎖︎ ) MILITARY MEDAL
-1F396 FE0F ; emoji style; # (7.0 🎖️ ) MILITARY MEDAL
-1F397 FE0E ; text style;  # (7.0 🎗︎ ) REMINDER RIBBON
-1F397 FE0F ; emoji style; # (7.0 🎗️ ) REMINDER RIBBON
-1F399 FE0E ; text style;  # (7.0 🎙︎ ) STUDIO MICROPHONE
-1F399 FE0F ; emoji style; # (7.0 🎙️ ) STUDIO MICROPHONE
-1F39A FE0E ; text style;  # (7.0 🎚︎ ) LEVEL SLIDER
-1F39A FE0F ; emoji style; # (7.0 🎚️ ) LEVEL SLIDER
-1F39B FE0E ; text style;  # (7.0 🎛︎ ) CONTROL KNOBS
-1F39B FE0F ; emoji style; # (7.0 🎛️ ) CONTROL KNOBS
-1F39E FE0E ; text style;  # (7.0 🎞︎ ) FILM FRAMES
-1F39E FE0F ; emoji style; # (7.0 🎞️ ) FILM FRAMES
-1F39F FE0E ; text style;  # (7.0 🎟︎ ) ADMISSION TICKETS
-1F39F FE0F ; emoji style; # (7.0 🎟️ ) ADMISSION TICKETS
-1F3A7 FE0E ; text style;  # (6.0 🎧︎ ) HEADPHONE
-1F3A7 FE0F ; emoji style; # (6.0 🎧️ ) HEADPHONE
-1F3AC FE0E ; text style;  # (6.0 🎬︎ ) CLAPPER BOARD
-1F3AC FE0F ; emoji style; # (6.0 🎬️ ) CLAPPER BOARD
-1F3AD FE0E ; text style;  # (6.0 🎭︎ ) PERFORMING ARTS
-1F3AD FE0F ; emoji style; # (6.0 🎭️ ) PERFORMING ARTS
-1F3AE FE0E ; text style;  # (6.0 🎮︎ ) VIDEO GAME
-1F3AE FE0F ; emoji style; # (6.0 🎮️ ) VIDEO GAME
-1F3C2 FE0E ; text style;  # (6.0 🏂︎ ) SNOWBOARDER
-1F3C2 FE0F ; emoji style; # (6.0 🏂️ ) SNOWBOARDER
-1F3C4 FE0E ; text style;  # (6.0 🏄︎ ) SURFER
-1F3C4 FE0F ; emoji style; # (6.0 🏄️ ) SURFER
-1F3C6 FE0E ; text style;  # (6.0 🏆︎ ) TROPHY
-1F3C6 FE0F ; emoji style; # (6.0 🏆️ ) TROPHY
-1F3CA FE0E ; text style;  # (6.0 🏊︎ ) SWIMMER
-1F3CA FE0F ; emoji style; # (6.0 🏊️ ) SWIMMER
-1F3CB FE0E ; text style;  # (7.0 🏋︎ ) WEIGHT LIFTER
-1F3CB FE0F ; emoji style; # (7.0 🏋️ ) WEIGHT LIFTER
-1F3CC FE0E ; text style;  # (7.0 🏌︎ ) GOLFER
-1F3CC FE0F ; emoji style; # (7.0 🏌️ ) GOLFER
-1F3CD FE0E ; text style;  # (7.0 🏍︎ ) RACING MOTORCYCLE
-1F3CD FE0F ; emoji style; # (7.0 🏍️ ) RACING MOTORCYCLE
-1F3CE FE0E ; text style;  # (7.0 🏎︎ ) RACING CAR
-1F3CE FE0F ; emoji style; # (7.0 🏎️ ) RACING CAR
-1F3D4 FE0E ; text style;  # (7.0 🏔︎ ) SNOW CAPPED MOUNTAIN
-1F3D4 FE0F ; emoji style; # (7.0 🏔️ ) SNOW CAPPED MOUNTAIN
-1F3D5 FE0E ; text style;  # (7.0 🏕︎ ) CAMPING
-1F3D5 FE0F ; emoji style; # (7.0 🏕️ ) CAMPING
-1F3D6 FE0E ; text style;  # (7.0 🏖︎ ) BEACH WITH UMBRELLA
-1F3D6 FE0F ; emoji style; # (7.0 🏖️ ) BEACH WITH UMBRELLA
-1F3D7 FE0E ; text style;  # (7.0 🏗︎ ) BUILDING CONSTRUCTION
-1F3D7 FE0F ; emoji style; # (7.0 🏗️ ) BUILDING CONSTRUCTION
-1F3D8 FE0E ; text style;  # (7.0 🏘︎ ) HOUSE BUILDINGS
-1F3D8 FE0F ; emoji style; # (7.0 🏘️ ) HOUSE BUILDINGS
-1F3D9 FE0E ; text style;  # (7.0 🏙︎ ) CITYSCAPE
-1F3D9 FE0F ; emoji style; # (7.0 🏙️ ) CITYSCAPE
-1F3DA FE0E ; text style;  # (7.0 🏚︎ ) DERELICT HOUSE BUILDING
-1F3DA FE0F ; emoji style; # (7.0 🏚️ ) DERELICT HOUSE BUILDING
-1F3DB FE0E ; text style;  # (7.0 🏛︎ ) CLASSICAL BUILDING
-1F3DB FE0F ; emoji style; # (7.0 🏛️ ) CLASSICAL BUILDING
-1F3DC FE0E ; text style;  # (7.0 🏜︎ ) DESERT
-1F3DC FE0F ; emoji style; # (7.0 🏜️ ) DESERT
-1F3DD FE0E ; text style;  # (7.0 🏝︎ ) DESERT ISLAND
-1F3DD FE0F ; emoji style; # (7.0 🏝️ ) DESERT ISLAND
-1F3DE FE0E ; text style;  # (7.0 🏞︎ ) NATIONAL PARK
-1F3DE FE0F ; emoji style; # (7.0 🏞️ ) NATIONAL PARK
-1F3DF FE0E ; text style;  # (7.0 🏟︎ ) STADIUM
-1F3DF FE0F ; emoji style; # (7.0 🏟️ ) STADIUM
-1F3E0 FE0E ; text style;  # (6.0 🏠︎ ) HOUSE BUILDING
-1F3E0 FE0F ; emoji style; # (6.0 🏠️ ) HOUSE BUILDING
-1F3ED FE0E ; text style;  # (6.0 🏭︎ ) FACTORY
-1F3ED FE0F ; emoji style; # (6.0 🏭️ ) FACTORY
-1F3F3 FE0E ; text style;  # (7.0 🏳︎ ) WAVING WHITE FLAG
-1F3F3 FE0F ; emoji style; # (7.0 🏳️ ) WAVING WHITE FLAG
-1F3F5 FE0E ; text style;  # (7.0 🏵︎ ) ROSETTE
-1F3F5 FE0F ; emoji style; # (7.0 🏵️ ) ROSETTE
-1F3F7 FE0E ; text style;  # (7.0 🏷︎ ) LABEL
-1F3F7 FE0F ; emoji style; # (7.0 🏷️ ) LABEL
-1F408 FE0E ; text style;  # (6.0 🐈︎ ) CAT
-1F408 FE0F ; emoji style; # (6.0 🐈️ ) CAT
-1F415 FE0E ; text style;  # (6.0 🐕︎ ) DOG
-1F415 FE0F ; emoji style; # (6.0 🐕️ ) DOG
-1F41F FE0E ; text style;  # (6.0 🐟︎ ) FISH
-1F41F FE0F ; emoji style; # (6.0 🐟️ ) FISH
-1F426 FE0E ; text style;  # (6.0 🐦︎ ) BIRD
-1F426 FE0F ; emoji style; # (6.0 🐦️ ) BIRD
-1F43F FE0E ; text style;  # (7.0 🐿︎ ) CHIPMUNK
-1F43F FE0F ; emoji style; # (7.0 🐿️ ) CHIPMUNK
-1F441 FE0E ; text style;  # (7.0 👁︎ ) EYE
-1F441 FE0F ; emoji style; # (7.0 👁️ ) EYE
-1F442 FE0E ; text style;  # (6.0 👂︎ ) EAR
-1F442 FE0F ; emoji style; # (6.0 👂️ ) EAR
-1F446 FE0E ; text style;  # (6.0 👆︎ ) WHITE UP POINTING BACKHAND INDEX
-1F446 FE0F ; emoji style; # (6.0 👆️ ) WHITE UP POINTING BACKHAND INDEX
-1F447 FE0E ; text style;  # (6.0 👇︎ ) WHITE DOWN POINTING BACKHAND INDEX
-1F447 FE0F ; emoji style; # (6.0 👇️ ) WHITE DOWN POINTING BACKHAND INDEX
-1F448 FE0E ; text style;  # (6.0 👈︎ ) WHITE LEFT POINTING BACKHAND INDEX
-1F448 FE0F ; emoji style; # (6.0 👈️ ) WHITE LEFT POINTING BACKHAND INDEX
-1F449 FE0E ; text style;  # (6.0 👉︎ ) WHITE RIGHT POINTING BACKHAND INDEX
-1F449 FE0F ; emoji style; # (6.0 👉️ ) WHITE RIGHT POINTING BACKHAND INDEX
-1F44D FE0E ; text style;  # (6.0 👍︎ ) THUMBS UP SIGN
-1F44D FE0F ; emoji style; # (6.0 👍️ ) THUMBS UP SIGN
-1F44E FE0E ; text style;  # (6.0 👎︎ ) THUMBS DOWN SIGN
-1F44E FE0F ; emoji style; # (6.0 👎️ ) THUMBS DOWN SIGN
-1F453 FE0E ; text style;  # (6.0 👓︎ ) EYEGLASSES
-1F453 FE0F ; emoji style; # (6.0 👓️ ) EYEGLASSES
-1F46A FE0E ; text style;  # (6.0 👪︎ ) FAMILY
-1F46A FE0F ; emoji style; # (6.0 👪️ ) FAMILY
-1F47D FE0E ; text style;  # (6.0 👽︎ ) EXTRATERRESTRIAL ALIEN
-1F47D FE0F ; emoji style; # (6.0 👽️ ) EXTRATERRESTRIAL ALIEN
-1F4A3 FE0E ; text style;  # (6.0 💣︎ ) BOMB
-1F4A3 FE0F ; emoji style; # (6.0 💣️ ) BOMB
-1F4B0 FE0E ; text style;  # (6.0 💰︎ ) MONEY BAG
-1F4B0 FE0F ; emoji style; # (6.0 💰️ ) MONEY BAG
-1F4B3 FE0E ; text style;  # (6.0 💳︎ ) CREDIT CARD
-1F4B3 FE0F ; emoji style; # (6.0 💳️ ) CREDIT CARD
-1F4BB FE0E ; text style;  # (6.0 💻︎ ) PERSONAL COMPUTER
-1F4BB FE0F ; emoji style; # (6.0 💻️ ) PERSONAL COMPUTER
-1F4BF FE0E ; text style;  # (6.0 💿︎ ) OPTICAL DISC
-1F4BF FE0F ; emoji style; # (6.0 💿️ ) OPTICAL DISC
-1F4CB FE0E ; text style;  # (6.0 📋︎ ) CLIPBOARD
-1F4CB FE0F ; emoji style; # (6.0 📋️ ) CLIPBOARD
-1F4DA FE0E ; text style;  # (6.0 📚︎ ) BOOKS
-1F4DA FE0F ; emoji style; # (6.0 📚️ ) BOOKS
-1F4DF FE0E ; text style;  # (6.0 📟︎ ) PAGER
-1F4DF FE0F ; emoji style; # (6.0 📟️ ) PAGER
-1F4E4 FE0E ; text style;  # (6.0 📤︎ ) OUTBOX TRAY
-1F4E4 FE0F ; emoji style; # (6.0 📤️ ) OUTBOX TRAY
-1F4E5 FE0E ; text style;  # (6.0 📥︎ ) INBOX TRAY
-1F4E5 FE0F ; emoji style; # (6.0 📥️ ) INBOX TRAY
-1F4E6 FE0E ; text style;  # (6.0 📦︎ ) PACKAGE
-1F4E6 FE0F ; emoji style; # (6.0 📦️ ) PACKAGE
-1F4EA FE0E ; text style;  # (6.0 📪︎ ) CLOSED MAILBOX WITH LOWERED FLAG
-1F4EA FE0F ; emoji style; # (6.0 📪️ ) CLOSED MAILBOX WITH LOWERED FLAG
-1F4EB FE0E ; text style;  # (6.0 📫︎ ) CLOSED MAILBOX WITH RAISED FLAG
-1F4EB FE0F ; emoji style; # (6.0 📫️ ) CLOSED MAILBOX WITH RAISED FLAG
-1F4EC FE0E ; text style;  # (6.0 📬︎ ) OPEN MAILBOX WITH RAISED FLAG
-1F4EC FE0F ; emoji style; # (6.0 📬️ ) OPEN MAILBOX WITH RAISED FLAG
-1F4ED FE0E ; text style;  # (6.0 📭︎ ) OPEN MAILBOX WITH LOWERED FLAG
-1F4ED FE0F ; emoji style; # (6.0 📭️ ) OPEN MAILBOX WITH LOWERED FLAG
-1F4F7 FE0E ; text style;  # (6.0 📷︎ ) CAMERA
-1F4F7 FE0F ; emoji style; # (6.0 📷️ ) CAMERA
-1F4F9 FE0E ; text style;  # (6.0 📹︎ ) VIDEO CAMERA
-1F4F9 FE0F ; emoji style; # (6.0 📹️ ) VIDEO CAMERA
-1F4FA FE0E ; text style;  # (6.0 📺︎ ) TELEVISION
-1F4FA FE0F ; emoji style; # (6.0 📺️ ) TELEVISION
-1F4FB FE0E ; text style;  # (6.0 📻︎ ) RADIO
-1F4FB FE0F ; emoji style; # (6.0 📻️ ) RADIO
-1F4FD FE0E ; text style;  # (7.0 📽︎ ) FILM PROJECTOR
-1F4FD FE0F ; emoji style; # (7.0 📽️ ) FILM PROJECTOR
-1F508 FE0E ; text style;  # (6.0 🔈︎ ) SPEAKER
-1F508 FE0F ; emoji style; # (6.0 🔈️ ) SPEAKER
-1F50D FE0E ; text style;  # (6.0 🔍︎ ) LEFT-POINTING MAGNIFYING GLASS
-1F50D FE0F ; emoji style; # (6.0 🔍️ ) LEFT-POINTING MAGNIFYING GLASS
-1F512 FE0E ; text style;  # (6.0 🔒︎ ) LOCK
-1F512 FE0F ; emoji style; # (6.0 🔒️ ) LOCK
-1F513 FE0E ; text style;  # (6.0 🔓︎ ) OPEN LOCK
-1F513 FE0F ; emoji style; # (6.0 🔓️ ) OPEN LOCK
-1F549 FE0E ; text style;  # (7.0 🕉︎ ) OM SYMBOL
-1F549 FE0F ; emoji style; # (7.0 🕉️ ) OM SYMBOL
-1F54A FE0E ; text style;  # (7.0 🕊︎ ) DOVE OF PEACE
-1F54A FE0F ; emoji style; # (7.0 🕊️ ) DOVE OF PEACE
-1F550 FE0E ; text style;  # (6.0 🕐︎ ) CLOCK FACE ONE OCLOCK
-1F550 FE0F ; emoji style; # (6.0 🕐️ ) CLOCK FACE ONE OCLOCK
-1F551 FE0E ; text style;  # (6.0 🕑︎ ) CLOCK FACE TWO OCLOCK
-1F551 FE0F ; emoji style; # (6.0 🕑️ ) CLOCK FACE TWO OCLOCK
-1F552 FE0E ; text style;  # (6.0 🕒︎ ) CLOCK FACE THREE OCLOCK
-1F552 FE0F ; emoji style; # (6.0 🕒️ ) CLOCK FACE THREE OCLOCK
-1F553 FE0E ; text style;  # (6.0 🕓︎ ) CLOCK FACE FOUR OCLOCK
-1F553 FE0F ; emoji style; # (6.0 🕓️ ) CLOCK FACE FOUR OCLOCK
-1F554 FE0E ; text style;  # (6.0 🕔︎ ) CLOCK FACE FIVE OCLOCK
-1F554 FE0F ; emoji style; # (6.0 🕔️ ) CLOCK FACE FIVE OCLOCK
-1F555 FE0E ; text style;  # (6.0 🕕︎ ) CLOCK FACE SIX OCLOCK
-1F555 FE0F ; emoji style; # (6.0 🕕️ ) CLOCK FACE SIX OCLOCK
-1F556 FE0E ; text style;  # (6.0 🕖︎ ) CLOCK FACE SEVEN OCLOCK
-1F556 FE0F ; emoji style; # (6.0 🕖️ ) CLOCK FACE SEVEN OCLOCK
-1F557 FE0E ; text style;  # (6.0 🕗︎ ) CLOCK FACE EIGHT OCLOCK
-1F557 FE0F ; emoji style; # (6.0 🕗️ ) CLOCK FACE EIGHT OCLOCK
-1F558 FE0E ; text style;  # (6.0 🕘︎ ) CLOCK FACE NINE OCLOCK
-1F558 FE0F ; emoji style; # (6.0 🕘️ ) CLOCK FACE NINE OCLOCK
-1F559 FE0E ; text style;  # (6.0 🕙︎ ) CLOCK FACE TEN OCLOCK
-1F559 FE0F ; emoji style; # (6.0 🕙️ ) CLOCK FACE TEN OCLOCK
-1F55A FE0E ; text style;  # (6.0 🕚︎ ) CLOCK FACE ELEVEN OCLOCK
-1F55A FE0F ; emoji style; # (6.0 🕚️ ) CLOCK FACE ELEVEN OCLOCK
-1F55B FE0E ; text style;  # (6.0 🕛︎ ) CLOCK FACE TWELVE OCLOCK
-1F55B FE0F ; emoji style; # (6.0 🕛️ ) CLOCK FACE TWELVE OCLOCK
-1F55C FE0E ; text style;  # (6.0 🕜︎ ) CLOCK FACE ONE-THIRTY
-1F55C FE0F ; emoji style; # (6.0 🕜️ ) CLOCK FACE ONE-THIRTY
-1F55D FE0E ; text style;  # (6.0 🕝︎ ) CLOCK FACE TWO-THIRTY
-1F55D FE0F ; emoji style; # (6.0 🕝️ ) CLOCK FACE TWO-THIRTY
-1F55E FE0E ; text style;  # (6.0 🕞︎ ) CLOCK FACE THREE-THIRTY
-1F55E FE0F ; emoji style; # (6.0 🕞️ ) CLOCK FACE THREE-THIRTY
-1F55F FE0E ; text style;  # (6.0 🕟︎ ) CLOCK FACE FOUR-THIRTY
-1F55F FE0F ; emoji style; # (6.0 🕟️ ) CLOCK FACE FOUR-THIRTY
-1F560 FE0E ; text style;  # (6.0 🕠︎ ) CLOCK FACE FIVE-THIRTY
-1F560 FE0F ; emoji style; # (6.0 🕠️ ) CLOCK FACE FIVE-THIRTY
-1F561 FE0E ; text style;  # (6.0 🕡︎ ) CLOCK FACE SIX-THIRTY
-1F561 FE0F ; emoji style; # (6.0 🕡️ ) CLOCK FACE SIX-THIRTY
-1F562 FE0E ; text style;  # (6.0 🕢︎ ) CLOCK FACE SEVEN-THIRTY
-1F562 FE0F ; emoji style; # (6.0 🕢️ ) CLOCK FACE SEVEN-THIRTY
-1F563 FE0E ; text style;  # (6.0 🕣︎ ) CLOCK FACE EIGHT-THIRTY
-1F563 FE0F ; emoji style; # (6.0 🕣️ ) CLOCK FACE EIGHT-THIRTY
-1F564 FE0E ; text style;  # (6.0 🕤︎ ) CLOCK FACE NINE-THIRTY
-1F564 FE0F ; emoji style; # (6.0 🕤️ ) CLOCK FACE NINE-THIRTY
-1F565 FE0E ; text style;  # (6.0 🕥︎ ) CLOCK FACE TEN-THIRTY
-1F565 FE0F ; emoji style; # (6.0 🕥️ ) CLOCK FACE TEN-THIRTY
-1F566 FE0E ; text style;  # (6.0 🕦︎ ) CLOCK FACE ELEVEN-THIRTY
-1F566 FE0F ; emoji style; # (6.0 🕦️ ) CLOCK FACE ELEVEN-THIRTY
-1F567 FE0E ; text style;  # (6.0 🕧︎ ) CLOCK FACE TWELVE-THIRTY
-1F567 FE0F ; emoji style; # (6.0 🕧️ ) CLOCK FACE TWELVE-THIRTY
-1F56F FE0E ; text style;  # (7.0 🕯︎ ) CANDLE
-1F56F FE0F ; emoji style; # (7.0 🕯️ ) CANDLE
-1F570 FE0E ; text style;  # (7.0 🕰︎ ) MANTELPIECE CLOCK
-1F570 FE0F ; emoji style; # (7.0 🕰️ ) MANTELPIECE CLOCK
-1F573 FE0E ; text style;  # (7.0 🕳︎ ) HOLE
-1F573 FE0F ; emoji style; # (7.0 🕳️ ) HOLE
-1F574 FE0E ; text style;  # (7.0 🕴︎ ) MAN IN BUSINESS SUIT LEVITATING
-1F574 FE0F ; emoji style; # (7.0 🕴️ ) MAN IN BUSINESS SUIT LEVITATING
-1F575 FE0E ; text style;  # (7.0 🕵︎ ) SLEUTH OR SPY
-1F575 FE0F ; emoji style; # (7.0 🕵️ ) SLEUTH OR SPY
-1F576 FE0E ; text style;  # (7.0 🕶︎ ) DARK SUNGLASSES
-1F576 FE0F ; emoji style; # (7.0 🕶️ ) DARK SUNGLASSES
-1F577 FE0E ; text style;  # (7.0 🕷︎ ) SPIDER
-1F577 FE0F ; emoji style; # (7.0 🕷️ ) SPIDER
-1F578 FE0E ; text style;  # (7.0 🕸︎ ) SPIDER WEB
-1F578 FE0F ; emoji style; # (7.0 🕸️ ) SPIDER WEB
-1F579 FE0E ; text style;  # (7.0 🕹︎ ) JOYSTICK
-1F579 FE0F ; emoji style; # (7.0 🕹️ ) JOYSTICK
-1F587 FE0E ; text style;  # (7.0 🖇︎ ) LINKED PAPERCLIPS
-1F587 FE0F ; emoji style; # (7.0 🖇️ ) LINKED PAPERCLIPS
-1F58A FE0E ; text style;  # (7.0 🖊︎ ) LOWER LEFT BALLPOINT PEN
-1F58A FE0F ; emoji style; # (7.0 🖊️ ) LOWER LEFT BALLPOINT PEN
-1F58B FE0E ; text style;  # (7.0 🖋︎ ) LOWER LEFT FOUNTAIN PEN
-1F58B FE0F ; emoji style; # (7.0 🖋️ ) LOWER LEFT FOUNTAIN PEN
-1F58C FE0E ; text style;  # (7.0 🖌︎ ) LOWER LEFT PAINTBRUSH
-1F58C FE0F ; emoji style; # (7.0 🖌️ ) LOWER LEFT PAINTBRUSH
-1F58D FE0E ; text style;  # (7.0 🖍︎ ) LOWER LEFT CRAYON
-1F58D FE0F ; emoji style; # (7.0 🖍️ ) LOWER LEFT CRAYON
-1F590 FE0E ; text style;  # (7.0 🖐︎ ) RAISED HAND WITH FINGERS SPLAYED
-1F590 FE0F ; emoji style; # (7.0 🖐️ ) RAISED HAND WITH FINGERS SPLAYED
-1F5A5 FE0E ; text style;  # (7.0 🖥︎ ) DESKTOP COMPUTER
-1F5A5 FE0F ; emoji style; # (7.0 🖥️ ) DESKTOP COMPUTER
-1F5A8 FE0E ; text style;  # (7.0 🖨︎ ) PRINTER
-1F5A8 FE0F ; emoji style; # (7.0 🖨️ ) PRINTER
-1F5B1 FE0E ; text style;  # (7.0 🖱︎ ) THREE BUTTON MOUSE
-1F5B1 FE0F ; emoji style; # (7.0 🖱️ ) THREE BUTTON MOUSE
-1F5B2 FE0E ; text style;  # (7.0 🖲︎ ) TRACKBALL
-1F5B2 FE0F ; emoji style; # (7.0 🖲️ ) TRACKBALL
-1F5BC FE0E ; text style;  # (7.0 🖼︎ ) FRAME WITH PICTURE
-1F5BC FE0F ; emoji style; # (7.0 🖼️ ) FRAME WITH PICTURE
-1F5C2 FE0E ; text style;  # (7.0 🗂︎ ) CARD INDEX DIVIDERS
-1F5C2 FE0F ; emoji style; # (7.0 🗂️ ) CARD INDEX DIVIDERS
-1F5C3 FE0E ; text style;  # (7.0 🗃︎ ) CARD FILE BOX
-1F5C3 FE0F ; emoji style; # (7.0 🗃️ ) CARD FILE BOX
-1F5C4 FE0E ; text style;  # (7.0 🗄︎ ) FILE CABINET
-1F5C4 FE0F ; emoji style; # (7.0 🗄️ ) FILE CABINET
-1F5D1 FE0E ; text style;  # (7.0 🗑︎ ) WASTEBASKET
-1F5D1 FE0F ; emoji style; # (7.0 🗑️ ) WASTEBASKET
-1F5D2 FE0E ; text style;  # (7.0 🗒︎ ) SPIRAL NOTE PAD
-1F5D2 FE0F ; emoji style; # (7.0 🗒️ ) SPIRAL NOTE PAD
-1F5D3 FE0E ; text style;  # (7.0 🗓︎ ) SPIRAL CALENDAR PAD
-1F5D3 FE0F ; emoji style; # (7.0 🗓️ ) SPIRAL CALENDAR PAD
-1F5DC FE0E ; text style;  # (7.0 🗜︎ ) COMPRESSION
-1F5DC FE0F ; emoji style; # (7.0 🗜️ ) COMPRESSION
-1F5DD FE0E ; text style;  # (7.0 🗝︎ ) OLD KEY
-1F5DD FE0F ; emoji style; # (7.0 🗝️ ) OLD KEY
-1F5DE FE0E ; text style;  # (7.0 🗞︎ ) ROLLED-UP NEWSPAPER
-1F5DE FE0F ; emoji style; # (7.0 🗞️ ) ROLLED-UP NEWSPAPER
-1F5E1 FE0E ; text style;  # (7.0 🗡︎ ) DAGGER KNIFE
-1F5E1 FE0F ; emoji style; # (7.0 🗡️ ) DAGGER KNIFE
-1F5E3 FE0E ; text style;  # (7.0 🗣︎ ) SPEAKING HEAD IN SILHOUETTE
-1F5E3 FE0F ; emoji style; # (7.0 🗣️ ) SPEAKING HEAD IN SILHOUETTE
-1F5E8 FE0E ; text style;  # (7.0 🗨︎ ) LEFT SPEECH BUBBLE
-1F5E8 FE0F ; emoji style; # (7.0 🗨️ ) LEFT SPEECH BUBBLE
-1F5EF FE0E ; text style;  # (7.0 🗯︎ ) RIGHT ANGER BUBBLE
-1F5EF FE0F ; emoji style; # (7.0 🗯️ ) RIGHT ANGER BUBBLE
-1F5F3 FE0E ; text style;  # (7.0 🗳︎ ) BALLOT BOX WITH BALLOT
-1F5F3 FE0F ; emoji style; # (7.0 🗳️ ) BALLOT BOX WITH BALLOT
-1F5FA FE0E ; text style;  # (7.0 🗺︎ ) WORLD MAP
-1F5FA FE0F ; emoji style; # (7.0 🗺️ ) WORLD MAP
-1F610 FE0E ; text style;  # (6.0 😐︎ ) NEUTRAL FACE
-1F610 FE0F ; emoji style; # (6.0 😐️ ) NEUTRAL FACE
-1F687 FE0E ; text style;  # (6.0 🚇︎ ) METRO
-1F687 FE0F ; emoji style; # (6.0 🚇️ ) METRO
-1F68D FE0E ; text style;  # (6.0 🚍︎ ) ONCOMING BUS
-1F68D FE0F ; emoji style; # (6.0 🚍️ ) ONCOMING BUS
-1F691 FE0E ; text style;  # (6.0 🚑︎ ) AMBULANCE
-1F691 FE0F ; emoji style; # (6.0 🚑️ ) AMBULANCE
-1F694 FE0E ; text style;  # (6.0 🚔︎ ) ONCOMING POLICE CAR
-1F694 FE0F ; emoji style; # (6.0 🚔️ ) ONCOMING POLICE CAR
-1F698 FE0E ; text style;  # (6.0 🚘︎ ) ONCOMING AUTOMOBILE
-1F698 FE0F ; emoji style; # (6.0 🚘️ ) ONCOMING AUTOMOBILE
-1F6AD FE0E ; text style;  # (6.0 🚭︎ ) NO SMOKING SYMBOL
-1F6AD FE0F ; emoji style; # (6.0 🚭️ ) NO SMOKING SYMBOL
-1F6B2 FE0E ; text style;  # (6.0 🚲︎ ) BICYCLE
-1F6B2 FE0F ; emoji style; # (6.0 🚲️ ) BICYCLE
-1F6B9 FE0E ; text style;  # (6.0 🚹︎ ) MENS SYMBOL
-1F6B9 FE0F ; emoji style; # (6.0 🚹️ ) MENS SYMBOL
-1F6BA FE0E ; text style;  # (6.0 🚺︎ ) WOMENS SYMBOL
-1F6BA FE0F ; emoji style; # (6.0 🚺️ ) WOMENS SYMBOL
-1F6BC FE0E ; text style;  # (6.0 🚼︎ ) BABY SYMBOL
-1F6BC FE0F ; emoji style; # (6.0 🚼️ ) BABY SYMBOL
-1F6CB FE0E ; text style;  # (7.0 🛋︎ ) COUCH AND LAMP
-1F6CB FE0F ; emoji style; # (7.0 🛋️ ) COUCH AND LAMP
-1F6CD FE0E ; text style;  # (7.0 🛍︎ ) SHOPPING BAGS
-1F6CD FE0F ; emoji style; # (7.0 🛍️ ) SHOPPING BAGS
-1F6CE FE0E ; text style;  # (7.0 🛎︎ ) BELLHOP BELL
-1F6CE FE0F ; emoji style; # (7.0 🛎️ ) BELLHOP BELL
-1F6CF FE0E ; text style;  # (7.0 🛏︎ ) BED
-1F6CF FE0F ; emoji style; # (7.0 🛏️ ) BED
-1F6E0 FE0E ; text style;  # (7.0 🛠︎ ) HAMMER AND WRENCH
-1F6E0 FE0F ; emoji style; # (7.0 🛠️ ) HAMMER AND WRENCH
-1F6E1 FE0E ; text style;  # (7.0 🛡︎ ) SHIELD
-1F6E1 FE0F ; emoji style; # (7.0 🛡️ ) SHIELD
-1F6E2 FE0E ; text style;  # (7.0 🛢︎ ) OIL DRUM
-1F6E2 FE0F ; emoji style; # (7.0 🛢️ ) OIL DRUM
-1F6E3 FE0E ; text style;  # (7.0 🛣︎ ) MOTORWAY
-1F6E3 FE0F ; emoji style; # (7.0 🛣️ ) MOTORWAY
-1F6E4 FE0E ; text style;  # (7.0 🛤︎ ) RAILWAY TRACK
-1F6E4 FE0F ; emoji style; # (7.0 🛤️ ) RAILWAY TRACK
-1F6E5 FE0E ; text style;  # (7.0 🛥︎ ) MOTOR BOAT
-1F6E5 FE0F ; emoji style; # (7.0 🛥️ ) MOTOR BOAT
-1F6E9 FE0E ; text style;  # (7.0 🛩︎ ) SMALL AIRPLANE
-1F6E9 FE0F ; emoji style; # (7.0 🛩️ ) SMALL AIRPLANE
-1F6F0 FE0E ; text style;  # (7.0 🛰︎ ) SATELLITE
-1F6F0 FE0F ; emoji style; # (7.0 🛰️ ) SATELLITE
-1F6F3 FE0E ; text style;  # (7.0 🛳︎ ) PASSENGER SHIP
-1F6F3 FE0F ; emoji style; # (7.0 🛳️ ) PASSENGER SHIP
+0023 FE0E  ; text style;  # (1.1) NUMBER SIGN
+0023 FE0F  ; emoji style; # (1.1) NUMBER SIGN
+002A FE0E  ; text style;  # (1.1) ASTERISK
+002A FE0F  ; emoji style; # (1.1) ASTERISK
+0030 FE0E  ; text style;  # (1.1) DIGIT ZERO
+0030 FE0F  ; emoji style; # (1.1) DIGIT ZERO
+0031 FE0E  ; text style;  # (1.1) DIGIT ONE
+0031 FE0F  ; emoji style; # (1.1) DIGIT ONE
+0032 FE0E  ; text style;  # (1.1) DIGIT TWO
+0032 FE0F  ; emoji style; # (1.1) DIGIT TWO
+0033 FE0E  ; text style;  # (1.1) DIGIT THREE
+0033 FE0F  ; emoji style; # (1.1) DIGIT THREE
+0034 FE0E  ; text style;  # (1.1) DIGIT FOUR
+0034 FE0F  ; emoji style; # (1.1) DIGIT FOUR
+0035 FE0E  ; text style;  # (1.1) DIGIT FIVE
+0035 FE0F  ; emoji style; # (1.1) DIGIT FIVE
+0036 FE0E  ; text style;  # (1.1) DIGIT SIX
+0036 FE0F  ; emoji style; # (1.1) DIGIT SIX
+0037 FE0E  ; text style;  # (1.1) DIGIT SEVEN
+0037 FE0F  ; emoji style; # (1.1) DIGIT SEVEN
+0038 FE0E  ; text style;  # (1.1) DIGIT EIGHT
+0038 FE0F  ; emoji style; # (1.1) DIGIT EIGHT
+0039 FE0E  ; text style;  # (1.1) DIGIT NINE
+0039 FE0F  ; emoji style; # (1.1) DIGIT NINE
+00A9 FE0E  ; text style;  # (1.1) COPYRIGHT SIGN
+00A9 FE0F  ; emoji style; # (1.1) COPYRIGHT SIGN
+00AE FE0E  ; text style;  # (1.1) REGISTERED SIGN
+00AE FE0F  ; emoji style; # (1.1) REGISTERED SIGN
+203C FE0E  ; text style;  # (1.1) DOUBLE EXCLAMATION MARK
+203C FE0F  ; emoji style; # (1.1) DOUBLE EXCLAMATION MARK
+2049 FE0E  ; text style;  # (3.0) EXCLAMATION QUESTION MARK
+2049 FE0F  ; emoji style; # (3.0) EXCLAMATION QUESTION MARK
+2122 FE0E  ; text style;  # (1.1) TRADE MARK SIGN
+2122 FE0F  ; emoji style; # (1.1) TRADE MARK SIGN
+2139 FE0E  ; text style;  # (3.0) INFORMATION SOURCE
+2139 FE0F  ; emoji style; # (3.0) INFORMATION SOURCE
+2194 FE0E  ; text style;  # (1.1) LEFT RIGHT ARROW
+2194 FE0F  ; emoji style; # (1.1) LEFT RIGHT ARROW
+2195 FE0E  ; text style;  # (1.1) UP DOWN ARROW
+2195 FE0F  ; emoji style; # (1.1) UP DOWN ARROW
+2196 FE0E  ; text style;  # (1.1) NORTH WEST ARROW
+2196 FE0F  ; emoji style; # (1.1) NORTH WEST ARROW
+2197 FE0E  ; text style;  # (1.1) NORTH EAST ARROW
+2197 FE0F  ; emoji style; # (1.1) NORTH EAST ARROW
+2198 FE0E  ; text style;  # (1.1) SOUTH EAST ARROW
+2198 FE0F  ; emoji style; # (1.1) SOUTH EAST ARROW
+2199 FE0E  ; text style;  # (1.1) SOUTH WEST ARROW
+2199 FE0F  ; emoji style; # (1.1) SOUTH WEST ARROW
+21A9 FE0E  ; text style;  # (1.1) LEFTWARDS ARROW WITH HOOK
+21A9 FE0F  ; emoji style; # (1.1) LEFTWARDS ARROW WITH HOOK
+21AA FE0E  ; text style;  # (1.1) RIGHTWARDS ARROW WITH HOOK
+21AA FE0F  ; emoji style; # (1.1) RIGHTWARDS ARROW WITH HOOK
+231A FE0E  ; text style;  # (1.1) WATCH
+231A FE0F  ; emoji style; # (1.1) WATCH
+231B FE0E  ; text style;  # (1.1) HOURGLASS
+231B FE0F  ; emoji style; # (1.1) HOURGLASS
+2328 FE0E  ; text style;  # (1.1) KEYBOARD
+2328 FE0F  ; emoji style; # (1.1) KEYBOARD
+23CF FE0E  ; text style;  # (4.0) EJECT SYMBOL
+23CF FE0F  ; emoji style; # (4.0) EJECT SYMBOL
+23E9 FE0E  ; text style;  # (6.0) BLACK RIGHT-POINTING DOUBLE TRIANGLE
+23E9 FE0F  ; emoji style; # (6.0) BLACK RIGHT-POINTING DOUBLE TRIANGLE
+23EA FE0E  ; text style;  # (6.0) BLACK LEFT-POINTING DOUBLE TRIANGLE
+23EA FE0F  ; emoji style; # (6.0) BLACK LEFT-POINTING DOUBLE TRIANGLE
+23EB FE0E  ; text style;  # (6.0) BLACK UP-POINTING DOUBLE TRIANGLE
+23EB FE0F  ; emoji style; # (6.0) BLACK UP-POINTING DOUBLE TRIANGLE
+23EC FE0E  ; text style;  # (6.0) BLACK DOWN-POINTING DOUBLE TRIANGLE
+23EC FE0F  ; emoji style; # (6.0) BLACK DOWN-POINTING DOUBLE TRIANGLE
+23ED FE0E  ; text style;  # (6.0) BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH 
VERTICAL BAR
+23ED FE0F  ; emoji style; # (6.0) BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH 
VERTICAL BAR
+23EE FE0E  ; text style;  # (6.0) BLACK LEFT-POINTING DOUBLE TRIANGLE WITH 
VERTICAL BAR
+23EE FE0F  ; emoji style; # (6.0) BLACK LEFT-POINTING DOUBLE TRIANGLE WITH 
VERTICAL BAR
+23EF FE0E  ; text style;  # (6.0) BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE 
VERTICAL BAR
+23EF FE0F  ; emoji style; # (6.0) BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE 
VERTICAL BAR
+23F0 FE0E  ; text style;  # (6.0) ALARM CLOCK
+23F0 FE0F  ; emoji style; # (6.0) ALARM CLOCK
+23F1 FE0E  ; text style;  # (6.0) STOPWATCH
+23F1 FE0F  ; emoji style; # (6.0) STOPWATCH
+23F2 FE0E  ; text style;  # (6.0) TIMER CLOCK
+23F2 FE0F  ; emoji style; # (6.0) TIMER CLOCK
+23F3 FE0E  ; text style;  # (6.0) HOURGLASS WITH FLOWING SAND
+23F3 FE0F  ; emoji style; # (6.0) HOURGLASS WITH FLOWING SAND
+23F8 FE0E  ; text style;  # (7.0) DOUBLE VERTICAL BAR
+23F8 FE0F  ; emoji style; # (7.0) DOUBLE VERTICAL BAR
+23F9 FE0E  ; text style;  # (7.0) BLACK SQUARE FOR STOP
+23F9 FE0F  ; emoji style; # (7.0) BLACK SQUARE FOR STOP
+23FA FE0E  ; text style;  # (7.0) BLACK CIRCLE FOR RECORD
+23FA FE0F  ; emoji style; # (7.0) BLACK CIRCLE FOR RECORD
+24C2 FE0E  ; text style;  # (1.1) CIRCLED LATIN CAPITAL LETTER M
+24C2 FE0F  ; emoji style; # (1.1) CIRCLED LATIN CAPITAL LETTER M
+25AA FE0E  ; text style;  # (1.1) BLACK SMALL SQUARE
+25AA FE0F  ; emoji style; # (1.1) BLACK SMALL SQUARE
+25AB FE0E  ; text style;  # (1.1) WHITE SMALL SQUARE
+25AB FE0F  ; emoji style; # (1.1) WHITE SMALL SQUARE
+25B6 FE0E  ; text style;  # (1.1) BLACK RIGHT-POINTING TRIANGLE
+25B6 FE0F  ; emoji style; # (1.1) BLACK RIGHT-POINTING TRIANGLE
+25C0 FE0E  ; text style;  # (1.1) BLACK LEFT-POINTING TRIANGLE
+25C0 FE0F  ; emoji style; # (1.1) BLACK LEFT-POINTING TRIANGLE
+25FB FE0E  ; text style;  # (3.2) WHITE MEDIUM SQUARE
+25FB FE0F  ; emoji style; # (3.2) WHITE MEDIUM SQUARE
+25FC FE0E  ; text style;  # (3.2) BLACK MEDIUM SQUARE
+25FC FE0F  ; emoji style; # (3.2) BLACK MEDIUM SQUARE
+25FD FE0E  ; text style;  # (3.2) WHITE MEDIUM SMALL SQUARE
+25FD FE0F  ; emoji style; # (3.2) WHITE MEDIUM SMALL SQUARE
+25FE FE0E  ; text style;  # (3.2) BLACK MEDIUM SMALL SQUARE
+25FE FE0F  ; emoji style; # (3.2) BLACK MEDIUM SMALL SQUARE
+2600 FE0E  ; text style;  # (1.1) BLACK SUN WITH RAYS
+2600 FE0F  ; emoji style; # (1.1) BLACK SUN WITH RAYS
+2601 FE0E  ; text style;  # (1.1) CLOUD
+2601 FE0F  ; emoji style; # (1.1) CLOUD
+2602 FE0E  ; text style;  # (1.1) UMBRELLA
+2602 FE0F  ; emoji style; # (1.1) UMBRELLA
+2603 FE0E  ; text style;  # (1.1) SNOWMAN
+2603 FE0F  ; emoji style; # (1.1) SNOWMAN
+2604 FE0E  ; text style;  # (1.1) COMET
+2604 FE0F  ; emoji style; # (1.1) COMET
+260E FE0E  ; text style;  # (1.1) BLACK TELEPHONE
+260E FE0F  ; emoji style; # (1.1) BLACK TELEPHONE
+2611 FE0E  ; text style;  # (1.1) BALLOT BOX WITH CHECK
+2611 FE0F  ; emoji style; # (1.1) BALLOT BOX WITH CHECK
+2614 FE0E  ; text style;  # (4.0) UMBRELLA WITH RAIN DROPS
+2614 FE0F  ; emoji style; # (4.0) UMBRELLA WITH RAIN DROPS
+2615 FE0E  ; text style;  # (4.0) HOT BEVERAGE
+2615 FE0F  ; emoji style; # (4.0) HOT BEVERAGE
+2618 FE0E  ; text style;  # (4.1) SHAMROCK
+2618 FE0F  ; emoji style; # (4.1) SHAMROCK
+261D FE0E  ; text style;  # (1.1) WHITE UP POINTING INDEX
+261D FE0F  ; emoji style; # (1.1) WHITE UP POINTING INDEX
+2620 FE0E  ; text style;  # (1.1) SKULL AND CROSSBONES
+2620 FE0F  ; emoji style; # (1.1) SKULL AND CROSSBONES
+2622 FE0E  ; text style;  # (1.1) RADIOACTIVE SIGN
+2622 FE0F  ; emoji style; # (1.1) RADIOACTIVE SIGN
+2623 FE0E  ; text style;  # (1.1) BIOHAZARD SIGN
+2623 FE0F  ; emoji style; # (1.1) BIOHAZARD SIGN
+2626 FE0E  ; text style;  # (1.1) ORTHODOX CROSS
+2626 FE0F  ; emoji style; # (1.1) ORTHODOX CROSS
+262A FE0E  ; text style;  # (1.1) STAR AND CRESCENT
+262A FE0F  ; emoji style; # (1.1) STAR AND CRESCENT
+262E FE0E  ; text style;  # (1.1) PEACE SYMBOL
+262E FE0F  ; emoji style; # (1.1) PEACE SYMBOL
+262F FE0E  ; text style;  # (1.1) YIN YANG
+262F FE0F  ; emoji style; # (1.1) YIN YANG
+2638 FE0E  ; text style;  # (1.1) WHEEL OF DHARMA
+2638 FE0F  ; emoji style; # (1.1) WHEEL OF DHARMA
+2639 FE0E  ; text style;  # (1.1) WHITE FROWNING FACE
+2639 FE0F  ; emoji style; # (1.1) WHITE FROWNING FACE
+263A FE0E  ; text style;  # (1.1) WHITE SMILING FACE
+263A FE0F  ; emoji style; # (1.1) WHITE SMILING FACE
+2640 FE0E  ; text style;  # (1.1) FEMALE SIGN
+2640 FE0F  ; emoji style; # (1.1) FEMALE SIGN
+2642 FE0E  ; text style;  # (1.1) MALE SIGN
+2642 FE0F  ; emoji style; # (1.1) MALE SIGN
+2648 FE0E  ; text style;  # (1.1) ARIES
+2648 FE0F  ; emoji style; # (1.1) ARIES
+2649 FE0E  ; text style;  # (1.1) TAURUS
+2649 FE0F  ; emoji style; # (1.1) TAURUS
+264A FE0E  ; text style;  # (1.1) GEMINI
+264A FE0F  ; emoji style; # (1.1) GEMINI
+264B FE0E  ; text style;  # (1.1) CANCER
+264B FE0F  ; emoji style; # (1.1) CANCER
+264C FE0E  ; text style;  # (1.1) LEO
+264C FE0F  ; emoji style; # (1.1) LEO
+264D FE0E  ; text style;  # (1.1) VIRGO
+264D FE0F  ; emoji style; # (1.1) VIRGO
+264E FE0E  ; text style;  # (1.1) LIBRA
+264E FE0F  ; emoji style; # (1.1) LIBRA
+264F FE0E  ; text style;  # (1.1) SCORPIUS
+264F FE0F  ; emoji style; # (1.1) SCORPIUS
+2650 FE0E  ; text style;  # (1.1) SAGITTARIUS
+2650 FE0F  ; emoji style; # (1.1) SAGITTARIUS
+2651 FE0E  ; text style;  # (1.1) CAPRICORN
+2651 FE0F  ; emoji style; # (1.1) CAPRICORN
+2652 FE0E  ; text style;  # (1.1) AQUARIUS
+2652 FE0F  ; emoji style; # (1.1) AQUARIUS
+2653 FE0E  ; text style;  # (1.1) PISCES
+2653 FE0F  ; emoji style; # (1.1) PISCES
+265F FE0E  ; text style;  # (1.1) BLACK CHESS PAWN
+265F FE0F  ; emoji style; # (1.1) BLACK CHESS PAWN
+2660 FE0E  ; text style;  # (1.1) BLACK SPADE SUIT
+2660 FE0F  ; emoji style; # (1.1) BLACK SPADE SUIT
+2663 FE0E  ; text style;  # (1.1) BLACK CLUB SUIT
+2663 FE0F  ; emoji style; # (1.1) BLACK CLUB SUIT
+2665 FE0E  ; text style;  # (1.1) BLACK HEART SUIT
+2665 FE0F  ; emoji style; # (1.1) BLACK HEART SUIT
+2666 FE0E  ; text style;  # (1.1) BLACK DIAMOND SUIT
+2666 FE0F  ; emoji style; # (1.1) BLACK DIAMOND SUIT
+2668 FE0E  ; text style;  # (1.1) HOT SPRINGS
+2668 FE0F  ; emoji style; # (1.1) HOT SPRINGS
+267B FE0E  ; text style;  # (3.2) BLACK UNIVERSAL RECYCLING SYMBOL
+267B FE0F  ; emoji style; # (3.2) BLACK UNIVERSAL RECYCLING SYMBOL
+267E FE0E  ; text style;  # (4.1) PERMANENT PAPER SIGN
+267E FE0F  ; emoji style; # (4.1) PERMANENT PAPER SIGN
+267F FE0E  ; text style;  # (4.1) WHEELCHAIR SYMBOL
+267F FE0F  ; emoji style; # (4.1) WHEELCHAIR SYMBOL
+2692 FE0E  ; text style;  # (4.1) HAMMER AND PICK
+2692 FE0F  ; emoji style; # (4.1) HAMMER AND PICK
+2693 FE0E  ; text style;  # (4.1) ANCHOR
+2693 FE0F  ; emoji style; # (4.1) ANCHOR
+2694 FE0E  ; text style;  # (4.1) CROSSED SWORDS
+2694 FE0F  ; emoji style; # (4.1) CROSSED SWORDS
+2695 FE0E  ; text style;  # (4.1) STAFF OF AESCULAPIUS
+2695 FE0F  ; emoji style; # (4.1) STAFF OF AESCULAPIUS
+2696 FE0E  ; text style;  # (4.1) SCALES
+2696 FE0F  ; emoji style; # (4.1) SCALES
+2697 FE0E  ; text style;  # (4.1) ALEMBIC
+2697 FE0F  ; emoji style; # (4.1) ALEMBIC
+2699 FE0E  ; text style;  # (4.1) GEAR
+2699 FE0F  ; emoji style; # (4.1) GEAR
+269B FE0E  ; text style;  # (4.1) ATOM SYMBOL
+269B FE0F  ; emoji style; # (4.1) ATOM SYMBOL
+269C FE0E  ; text style;  # (4.1) FLEUR-DE-LIS
+269C FE0F  ; emoji style; # (4.1) FLEUR-DE-LIS
+26A0 FE0E  ; text style;  # (4.0) WARNING SIGN
+26A0 FE0F  ; emoji style; # (4.0) WARNING SIGN
+26A1 FE0E  ; text style;  # (4.0) HIGH VOLTAGE SIGN
+26A1 FE0F  ; emoji style; # (4.0) HIGH VOLTAGE SIGN
+26A7 FE0E  ; text style;  # (4.1) MALE WITH STROKE AND MALE AND FEMALE SIGN
+26A7 FE0F  ; emoji style; # (4.1) MALE WITH STROKE AND MALE AND FEMALE SIGN
+26AA FE0E  ; text style;  # (4.1) MEDIUM WHITE CIRCLE
+26AA FE0F  ; emoji style; # (4.1) MEDIUM WHITE CIRCLE
+26AB FE0E  ; text style;  # (4.1) MEDIUM BLACK CIRCLE
+26AB FE0F  ; emoji style; # (4.1) MEDIUM BLACK CIRCLE
+26B0 FE0E  ; text style;  # (4.1) COFFIN
+26B0 FE0F  ; emoji style; # (4.1) COFFIN
+26B1 FE0E  ; text style;  # (4.1) FUNERAL URN
+26B1 FE0F  ; emoji style; # (4.1) FUNERAL URN
+26BD FE0E  ; text style;  # (5.2) SOCCER BALL
+26BD FE0F  ; emoji style; # (5.2) SOCCER BALL
+26BE FE0E  ; text style;  # (5.2) BASEBALL
+26BE FE0F  ; emoji style; # (5.2) BASEBALL
+26C4 FE0E  ; text style;  # (5.2) SNOWMAN WITHOUT SNOW
+26C4 FE0F  ; emoji style; # (5.2) SNOWMAN WITHOUT SNOW
+26C5 FE0E  ; text style;  # (5.2) SUN BEHIND CLOUD
+26C5 FE0F  ; emoji style; # (5.2) SUN BEHIND CLOUD
+26C8 FE0E  ; text style;  # (5.2) THUNDER CLOUD AND RAIN
+26C8 FE0F  ; emoji style; # (5.2) THUNDER CLOUD AND RAIN
+26CE FE0E  ; text style;  # (6.0) OPHIUCHUS
+26CE FE0F  ; emoji style; # (6.0) OPHIUCHUS
+26CF FE0E  ; text style;  # (5.2) PICK
+26CF FE0F  ; emoji style; # (5.2) PICK
+26D1 FE0E  ; text style;  # (5.2) HELMET WITH WHITE CROSS
+26D1 FE0F  ; emoji style; # (5.2) HELMET WITH WHITE CROSS
+26D3 FE0E  ; text style;  # (5.2) CHAINS
+26D3 FE0F  ; emoji style; # (5.2) CHAINS
+26D4 FE0E  ; text style;  # (5.2) NO ENTRY
+26D4 FE0F  ; emoji style; # (5.2) NO ENTRY
+26E9 FE0E  ; text style;  # (5.2) SHINTO SHRINE
+26E9 FE0F  ; emoji style; # (5.2) SHINTO SHRINE
+26EA FE0E  ; text style;  # (5.2) CHURCH
+26EA FE0F  ; emoji style; # (5.2) CHURCH
+26F0 FE0E  ; text style;  # (5.2) MOUNTAIN
+26F0 FE0F  ; emoji style; # (5.2) MOUNTAIN
+26F1 FE0E  ; text style;  # (5.2) UMBRELLA ON GROUND
+26F1 FE0F  ; emoji style; # (5.2) UMBRELLA ON GROUND
+26F2 FE0E  ; text style;  # (5.2) FOUNTAIN
+26F2 FE0F  ; emoji style; # (5.2) FOUNTAIN
+26F3 FE0E  ; text style;  # (5.2) FLAG IN HOLE
+26F3 FE0F  ; emoji style; # (5.2) FLAG IN HOLE
+26F4 FE0E  ; text style;  # (5.2) FERRY
+26F4 FE0F  ; emoji style; # (5.2) FERRY
+26F5 FE0E  ; text style;  # (5.2) SAILBOAT
+26F5 FE0F  ; emoji style; # (5.2) SAILBOAT
+26F7 FE0E  ; text style;  # (5.2) SKIER
+26F7 FE0F  ; emoji style; # (5.2) SKIER
+26F8 FE0E  ; text style;  # (5.2) ICE SKATE
+26F8 FE0F  ; emoji style; # (5.2) ICE SKATE
+26F9 FE0E  ; text style;  # (5.2) PERSON WITH BALL
+26F9 FE0F  ; emoji style; # (5.2) PERSON WITH BALL
+26FA FE0E  ; text style;  # (5.2) TENT
+26FA FE0F  ; emoji style; # (5.2) TENT
+26FD FE0E  ; text style;  # (5.2) FUEL PUMP
+26FD FE0F  ; emoji style; # (5.2) FUEL PUMP
+2702 FE0E  ; text style;  # (1.1) BLACK SCISSORS
+2702 FE0F  ; emoji style; # (1.1) BLACK SCISSORS
+2705 FE0E  ; text style;  # (6.0) WHITE HEAVY CHECK MARK
+2705 FE0F  ; emoji style; # (6.0) WHITE HEAVY CHECK MARK
+2708 FE0E  ; text style;  # (1.1) AIRPLANE
+2708 FE0F  ; emoji style; # (1.1) AIRPLANE
+2709 FE0E  ; text style;  # (1.1) ENVELOPE
+2709 FE0F  ; emoji style; # (1.1) ENVELOPE
+270A FE0E  ; text style;  # (6.0) RAISED FIST
+270A FE0F  ; emoji style; # (6.0) RAISED FIST
+270B FE0E  ; text style;  # (6.0) RAISED HAND
+270B FE0F  ; emoji style; # (6.0) RAISED HAND
+270C FE0E  ; text style;  # (1.1) VICTORY HAND
+270C FE0F  ; emoji style; # (1.1) VICTORY HAND
+270D FE0E  ; text style;  # (1.1) WRITING HAND
+270D FE0F  ; emoji style; # (1.1) WRITING HAND
+270F FE0E  ; text style;  # (1.1) PENCIL
+270F FE0F  ; emoji style; # (1.1) PENCIL
+2712 FE0E  ; text style;  # (1.1) BLACK NIB
+2712 FE0F  ; emoji style; # (1.1) BLACK NIB
+2714 FE0E  ; text style;  # (1.1) HEAVY CHECK MARK
+2714 FE0F  ; emoji style; # (1.1) HEAVY CHECK MARK
+2716 FE0E  ; text style;  # (1.1) HEAVY MULTIPLICATION X
+2716 FE0F  ; emoji style; # (1.1) HEAVY MULTIPLICATION X
+271D FE0E  ; text style;  # (1.1) LATIN CROSS
+271D FE0F  ; emoji style; # (1.1) LATIN CROSS
+2721 FE0E  ; text style;  # (1.1) STAR OF DAVID
+2721 FE0F  ; emoji style; # (1.1) STAR OF DAVID
+2728 FE0E  ; text style;  # (6.0) SPARKLES
+2728 FE0F  ; emoji style; # (6.0) SPARKLES
+2733 FE0E  ; text style;  # (1.1) EIGHT SPOKED ASTERISK
+2733 FE0F  ; emoji style; # (1.1) EIGHT SPOKED ASTERISK
+2734 FE0E  ; text style;  # (1.1) EIGHT POINTED BLACK STAR
+2734 FE0F  ; emoji style; # (1.1) EIGHT POINTED BLACK STAR
+2744 FE0E  ; text style;  # (1.1) SNOWFLAKE
+2744 FE0F  ; emoji style; # (1.1) SNOWFLAKE
+2747 FE0E  ; text style;  # (1.1) SPARKLE
+2747 FE0F  ; emoji style; # (1.1) SPARKLE
+274C FE0E  ; text style;  # (6.0) CROSS MARK
+274C FE0F  ; emoji style; # (6.0) CROSS MARK
+274E FE0E  ; text style;  # (6.0) NEGATIVE SQUARED CROSS MARK
+274E FE0F  ; emoji style; # (6.0) NEGATIVE SQUARED CROSS MARK
+2753 FE0E  ; text style;  # (6.0) BLACK QUESTION MARK ORNAMENT
+2753 FE0F  ; emoji style; # (6.0) BLACK QUESTION MARK ORNAMENT
+2754 FE0E  ; text style;  # (6.0) WHITE QUESTION MARK ORNAMENT
+2754 FE0F  ; emoji style; # (6.0) WHITE QUESTION MARK ORNAMENT
+2755 FE0E  ; text style;  # (6.0) WHITE EXCLAMATION MARK ORNAMENT
+2755 FE0F  ; emoji style; # (6.0) WHITE EXCLAMATION MARK ORNAMENT
+2757 FE0E  ; text style;  # (5.2) HEAVY EXCLAMATION MARK SYMBOL
+2757 FE0F  ; emoji style; # (5.2) HEAVY EXCLAMATION MARK SYMBOL
+2763 FE0E  ; text style;  # (1.1) HEAVY HEART EXCLAMATION MARK ORNAMENT
+2763 FE0F  ; emoji style; # (1.1) HEAVY HEART EXCLAMATION MARK ORNAMENT
+2764 FE0E  ; text style;  # (1.1) HEAVY BLACK HEART
+2764 FE0F  ; emoji style; # (1.1) HEAVY BLACK HEART
+2795 FE0E  ; text style;  # (6.0) HEAVY PLUS SIGN
+2795 FE0F  ; emoji style; # (6.0) HEAVY PLUS SIGN
+2796 FE0E  ; text style;  # (6.0) HEAVY MINUS SIGN
+2796 FE0F  ; emoji style; # (6.0) HEAVY MINUS SIGN
+2797 FE0E  ; text style;  # (6.0) HEAVY DIVISION SIGN
+2797 FE0F  ; emoji style; # (6.0) HEAVY DIVISION SIGN
+27A1 FE0E  ; text style;  # (1.1) BLACK RIGHTWARDS ARROW
+27A1 FE0F  ; emoji style; # (1.1) BLACK RIGHTWARDS ARROW
+27B0 FE0E  ; text style;  # (6.0) CURLY LOOP
+27B0 FE0F  ; emoji style; # (6.0) CURLY LOOP
+27BF FE0E  ; text style;  # (6.0) DOUBLE CURLY LOOP
+27BF FE0F  ; emoji style; # (6.0) DOUBLE CURLY LOOP
+2934 FE0E  ; text style;  # (3.2) ARROW POINTING RIGHTWARDS THEN CURVING 
UPWARDS
+2934 FE0F  ; emoji style; # (3.2) ARROW POINTING RIGHTWARDS THEN CURVING 
UPWARDS
+2935 FE0E  ; text style;  # (3.2) ARROW POINTING RIGHTWARDS THEN CURVING 
DOWNWARDS
+2935 FE0F  ; emoji style; # (3.2) ARROW POINTING RIGHTWARDS THEN CURVING 
DOWNWARDS
+2B05 FE0E  ; text style;  # (4.0) LEFTWARDS BLACK ARROW
+2B05 FE0F  ; emoji style; # (4.0) LEFTWARDS BLACK ARROW
+2B06 FE0E  ; text style;  # (4.0) UPWARDS BLACK ARROW
+2B06 FE0F  ; emoji style; # (4.0) UPWARDS BLACK ARROW
+2B07 FE0E  ; text style;  # (4.0) DOWNWARDS BLACK ARROW
+2B07 FE0F  ; emoji style; # (4.0) DOWNWARDS BLACK ARROW
+2B1B FE0E  ; text style;  # (5.1) BLACK LARGE SQUARE
+2B1B FE0F  ; emoji style; # (5.1) BLACK LARGE SQUARE
+2B1C FE0E  ; text style;  # (5.1) WHITE LARGE SQUARE
+2B1C FE0F  ; emoji style; # (5.1) WHITE LARGE SQUARE
+2B50 FE0E  ; text style;  # (5.1) WHITE MEDIUM STAR
+2B50 FE0F  ; emoji style; # (5.1) WHITE MEDIUM STAR
+2B55 FE0E  ; text style;  # (5.2) HEAVY LARGE CIRCLE
+2B55 FE0F  ; emoji style; # (5.2) HEAVY LARGE CIRCLE
+3030 FE0E  ; text style;  # (1.1) WAVY DASH
+3030 FE0F  ; emoji style; # (1.1) WAVY DASH
+303D FE0E  ; text style;  # (3.2) PART ALTERNATION MARK
+303D FE0F  ; emoji style; # (3.2) PART ALTERNATION MARK
+3297 FE0E  ; text style;  # (1.1) CIRCLED IDEOGRAPH CONGRATULATION
+3297 FE0F  ; emoji style; # (1.1) CIRCLED IDEOGRAPH CONGRATULATION
+3299 FE0E  ; text style;  # (1.1) CIRCLED IDEOGRAPH SECRET
+3299 FE0F  ; emoji style; # (1.1) CIRCLED IDEOGRAPH SECRET
+1F004 FE0E ; text style;  # (5.1) MAHJONG TILE RED DRAGON
+1F004 FE0F ; emoji style; # (5.1) MAHJONG TILE RED DRAGON
+1F170 FE0E ; text style;  # (6.0) NEGATIVE SQUARED LATIN CAPITAL LETTER A
+1F170 FE0F ; emoji style; # (6.0) NEGATIVE SQUARED LATIN CAPITAL LETTER A
+1F171 FE0E ; text style;  # (6.0) NEGATIVE SQUARED LATIN CAPITAL LETTER B
+1F171 FE0F ; emoji style; # (6.0) NEGATIVE SQUARED LATIN CAPITAL LETTER B
+1F17E FE0E ; text style;  # (6.0) NEGATIVE SQUARED LATIN CAPITAL LETTER O
+1F17E FE0F ; emoji style; # (6.0) NEGATIVE SQUARED LATIN CAPITAL LETTER O
+1F17F FE0E ; text style;  # (5.2) NEGATIVE SQUARED LATIN CAPITAL LETTER P
+1F17F FE0F ; emoji style; # (5.2) NEGATIVE SQUARED LATIN CAPITAL LETTER P
+1F202 FE0E ; text style;  # (6.0) SQUARED KATAKANA SA
+1F202 FE0F ; emoji style; # (6.0) SQUARED KATAKANA SA
+1F21A FE0E ; text style;  # (5.2) SQUARED CJK UNIFIED IDEOGRAPH-7121
+1F21A FE0F ; emoji style; # (5.2) SQUARED CJK UNIFIED IDEOGRAPH-7121
+1F22F FE0E ; text style;  # (5.2) SQUARED CJK UNIFIED IDEOGRAPH-6307
+1F22F FE0F ; emoji style; # (5.2) SQUARED CJK UNIFIED IDEOGRAPH-6307
+1F237 FE0E ; text style;  # (6.0) SQUARED CJK UNIFIED IDEOGRAPH-6708
+1F237 FE0F ; emoji style; # (6.0) SQUARED CJK UNIFIED IDEOGRAPH-6708
+1F30D FE0E ; text style;  # (6.0) EARTH GLOBE EUROPE-AFRICA
+1F30D FE0F ; emoji style; # (6.0) EARTH GLOBE EUROPE-AFRICA
+1F30E FE0E ; text style;  # (6.0) EARTH GLOBE AMERICAS
+1F30E FE0F ; emoji style; # (6.0) EARTH GLOBE AMERICAS
+1F30F FE0E ; text style;  # (6.0) EARTH GLOBE ASIA-AUSTRALIA
+1F30F FE0F ; emoji style; # (6.0) EARTH GLOBE ASIA-AUSTRALIA
+1F315 FE0E ; text style;  # (6.0) FULL MOON SYMBOL
+1F315 FE0F ; emoji style; # (6.0) FULL MOON SYMBOL
+1F31C FE0E ; text style;  # (6.0) LAST QUARTER MOON WITH FACE
+1F31C FE0F ; emoji style; # (6.0) LAST QUARTER MOON WITH FACE
+1F321 FE0E ; text style;  # (7.0) THERMOMETER
+1F321 FE0F ; emoji style; # (7.0) THERMOMETER
+1F324 FE0E ; text style;  # (7.0) WHITE SUN WITH SMALL CLOUD
+1F324 FE0F ; emoji style; # (7.0) WHITE SUN WITH SMALL CLOUD
+1F325 FE0E ; text style;  # (7.0) WHITE SUN BEHIND CLOUD
+1F325 FE0F ; emoji style; # (7.0) WHITE SUN BEHIND CLOUD
+1F326 FE0E ; text style;  # (7.0) WHITE SUN BEHIND CLOUD WITH RAIN
+1F326 FE0F ; emoji style; # (7.0) WHITE SUN BEHIND CLOUD WITH RAIN
+1F327 FE0E ; text style;  # (7.0) CLOUD WITH RAIN
+1F327 FE0F ; emoji style; # (7.0) CLOUD WITH RAIN
+1F328 FE0E ; text style;  # (7.0) CLOUD WITH SNOW
+1F328 FE0F ; emoji style; # (7.0) CLOUD WITH SNOW
+1F329 FE0E ; text style;  # (7.0) CLOUD WITH LIGHTNING
+1F329 FE0F ; emoji style; # (7.0) CLOUD WITH LIGHTNING
+1F32A FE0E ; text style;  # (7.0) CLOUD WITH TORNADO
+1F32A FE0F ; emoji style; # (7.0) CLOUD WITH TORNADO
+1F32B FE0E ; text style;  # (7.0) FOG
+1F32B FE0F ; emoji style; # (7.0) FOG
+1F32C FE0E ; text style;  # (7.0) WIND BLOWING FACE
+1F32C FE0F ; emoji style; # (7.0) WIND BLOWING FACE
+1F336 FE0E ; text style;  # (7.0) HOT PEPPER
+1F336 FE0F ; emoji style; # (7.0) HOT PEPPER
+1F378 FE0E ; text style;  # (6.0) COCKTAIL GLASS
+1F378 FE0F ; emoji style; # (6.0) COCKTAIL GLASS
+1F37D FE0E ; text style;  # (7.0) FORK AND KNIFE WITH PLATE
+1F37D FE0F ; emoji style; # (7.0) FORK AND KNIFE WITH PLATE
+1F393 FE0E ; text style;  # (6.0) GRADUATION CAP
+1F393 FE0F ; emoji style; # (6.0) GRADUATION CAP
+1F396 FE0E ; text style;  # (7.0) MILITARY MEDAL
+1F396 FE0F ; emoji style; # (7.0) MILITARY MEDAL
+1F397 FE0E ; text style;  # (7.0) REMINDER RIBBON
+1F397 FE0F ; emoji style; # (7.0) REMINDER RIBBON
+1F399 FE0E ; text style;  # (7.0) STUDIO MICROPHONE
+1F399 FE0F ; emoji style; # (7.0) STUDIO MICROPHONE
+1F39A FE0E ; text style;  # (7.0) LEVEL SLIDER
+1F39A FE0F ; emoji style; # (7.0) LEVEL SLIDER
+1F39B FE0E ; text style;  # (7.0) CONTROL KNOBS
+1F39B FE0F ; emoji style; # (7.0) CONTROL KNOBS
+1F39E FE0E ; text style;  # (7.0) FILM FRAMES
+1F39E FE0F ; emoji style; # (7.0) FILM FRAMES
+1F39F FE0E ; text style;  # (7.0) ADMISSION TICKETS
+1F39F FE0F ; emoji style; # (7.0) ADMISSION TICKETS
+1F3A7 FE0E ; text style;  # (6.0) HEADPHONE
+1F3A7 FE0F ; emoji style; # (6.0) HEADPHONE
+1F3AC FE0E ; text style;  # (6.0) CLAPPER BOARD
+1F3AC FE0F ; emoji style; # (6.0) CLAPPER BOARD
+1F3AD FE0E ; text style;  # (6.0) PERFORMING ARTS
+1F3AD FE0F ; emoji style; # (6.0) PERFORMING ARTS
+1F3AE FE0E ; text style;  # (6.0) VIDEO GAME
+1F3AE FE0F ; emoji style; # (6.0) VIDEO GAME
+1F3C2 FE0E ; text style;  # (6.0) SNOWBOARDER
+1F3C2 FE0F ; emoji style; # (6.0) SNOWBOARDER
+1F3C4 FE0E ; text style;  # (6.0) SURFER
+1F3C4 FE0F ; emoji style; # (6.0) SURFER
+1F3C6 FE0E ; text style;  # (6.0) TROPHY
+1F3C6 FE0F ; emoji style; # (6.0) TROPHY
+1F3CA FE0E ; text style;  # (6.0) SWIMMER
+1F3CA FE0F ; emoji style; # (6.0) SWIMMER
+1F3CB FE0E ; text style;  # (7.0) WEIGHT LIFTER
+1F3CB FE0F ; emoji style; # (7.0) WEIGHT LIFTER
+1F3CC FE0E ; text style;  # (7.0) GOLFER
+1F3CC FE0F ; emoji style; # (7.0) GOLFER
+1F3CD FE0E ; text style;  # (7.0) RACING MOTORCYCLE
+1F3CD FE0F ; emoji style; # (7.0) RACING MOTORCYCLE
+1F3CE FE0E ; text style;  # (7.0) RACING CAR
+1F3CE FE0F ; emoji style; # (7.0) RACING CAR
+1F3D4 FE0E ; text style;  # (7.0) SNOW CAPPED MOUNTAIN
+1F3D4 FE0F ; emoji style; # (7.0) SNOW CAPPED MOUNTAIN
+1F3D5 FE0E ; text style;  # (7.0) CAMPING
+1F3D5 FE0F ; emoji style; # (7.0) CAMPING
+1F3D6 FE0E ; text style;  # (7.0) BEACH WITH UMBRELLA
+1F3D6 FE0F ; emoji style; # (7.0) BEACH WITH UMBRELLA
+1F3D7 FE0E ; text style;  # (7.0) BUILDING CONSTRUCTION
+1F3D7 FE0F ; emoji style; # (7.0) BUILDING CONSTRUCTION
+1F3D8 FE0E ; text style;  # (7.0) HOUSE BUILDINGS
+1F3D8 FE0F ; emoji style; # (7.0) HOUSE BUILDINGS
+1F3D9 FE0E ; text style;  # (7.0) CITYSCAPE
+1F3D9 FE0F ; emoji style; # (7.0) CITYSCAPE
+1F3DA FE0E ; text style;  # (7.0) DERELICT HOUSE BUILDING
+1F3DA FE0F ; emoji style; # (7.0) DERELICT HOUSE BUILDING
+1F3DB FE0E ; text style;  # (7.0) CLASSICAL BUILDING
+1F3DB FE0F ; emoji style; # (7.0) CLASSICAL BUILDING
+1F3DC FE0E ; text style;  # (7.0) DESERT
+1F3DC FE0F ; emoji style; # (7.0) DESERT
+1F3DD FE0E ; text style;  # (7.0) DESERT ISLAND
+1F3DD FE0F ; emoji style; # (7.0) DESERT ISLAND
+1F3DE FE0E ; text style;  # (7.0) NATIONAL PARK
+1F3DE FE0F ; emoji style; # (7.0) NATIONAL PARK
+1F3DF FE0E ; text style;  # (7.0) STADIUM
+1F3DF FE0F ; emoji style; # (7.0) STADIUM
+1F3E0 FE0E ; text style;  # (6.0) HOUSE BUILDING
+1F3E0 FE0F ; emoji style; # (6.0) HOUSE BUILDING
+1F3ED FE0E ; text style;  # (6.0) FACTORY
+1F3ED FE0F ; emoji style; # (6.0) FACTORY
+1F3F3 FE0E ; text style;  # (7.0) WAVING WHITE FLAG
+1F3F3 FE0F ; emoji style; # (7.0) WAVING WHITE FLAG
+1F3F5 FE0E ; text style;  # (7.0) ROSETTE
+1F3F5 FE0F ; emoji style; # (7.0) ROSETTE
+1F3F7 FE0E ; text style;  # (7.0) LABEL
+1F3F7 FE0F ; emoji style; # (7.0) LABEL
+1F408 FE0E ; text style;  # (6.0) CAT
+1F408 FE0F ; emoji style; # (6.0) CAT
+1F415 FE0E ; text style;  # (6.0) DOG
+1F415 FE0F ; emoji style; # (6.0) DOG
+1F41F FE0E ; text style;  # (6.0) FISH
+1F41F FE0F ; emoji style; # (6.0) FISH
+1F426 FE0E ; text style;  # (6.0) BIRD
+1F426 FE0F ; emoji style; # (6.0) BIRD
+1F43F FE0E ; text style;  # (7.0) CHIPMUNK
+1F43F FE0F ; emoji style; # (7.0) CHIPMUNK
+1F441 FE0E ; text style;  # (7.0) EYE
+1F441 FE0F ; emoji style; # (7.0) EYE
+1F442 FE0E ; text style;  # (6.0) EAR
+1F442 FE0F ; emoji style; # (6.0) EAR
+1F446 FE0E ; text style;  # (6.0) WHITE UP POINTING BACKHAND INDEX
+1F446 FE0F ; emoji style; # (6.0) WHITE UP POINTING BACKHAND INDEX
+1F447 FE0E ; text style;  # (6.0) WHITE DOWN POINTING BACKHAND INDEX
+1F447 FE0F ; emoji style; # (6.0) WHITE DOWN POINTING BACKHAND INDEX
+1F448 FE0E ; text style;  # (6.0) WHITE LEFT POINTING BACKHAND INDEX
+1F448 FE0F ; emoji style; # (6.0) WHITE LEFT POINTING BACKHAND INDEX
+1F449 FE0E ; text style;  # (6.0) WHITE RIGHT POINTING BACKHAND INDEX
+1F449 FE0F ; emoji style; # (6.0) WHITE RIGHT POINTING BACKHAND INDEX
+1F44D FE0E ; text style;  # (6.0) THUMBS UP SIGN
+1F44D FE0F ; emoji style; # (6.0) THUMBS UP SIGN
+1F44E FE0E ; text style;  # (6.0) THUMBS DOWN SIGN
+1F44E FE0F ; emoji style; # (6.0) THUMBS DOWN SIGN
+1F453 FE0E ; text style;  # (6.0) EYEGLASSES
+1F453 FE0F ; emoji style; # (6.0) EYEGLASSES
+1F46A FE0E ; text style;  # (6.0) FAMILY
+1F46A FE0F ; emoji style; # (6.0) FAMILY
+1F47D FE0E ; text style;  # (6.0) EXTRATERRESTRIAL ALIEN
+1F47D FE0F ; emoji style; # (6.0) EXTRATERRESTRIAL ALIEN
+1F4A3 FE0E ; text style;  # (6.0) BOMB
+1F4A3 FE0F ; emoji style; # (6.0) BOMB
+1F4B0 FE0E ; text style;  # (6.0) MONEY BAG
+1F4B0 FE0F ; emoji style; # (6.0) MONEY BAG
+1F4B3 FE0E ; text style;  # (6.0) CREDIT CARD
+1F4B3 FE0F ; emoji style; # (6.0) CREDIT CARD
+1F4BB FE0E ; text style;  # (6.0) PERSONAL COMPUTER
+1F4BB FE0F ; emoji style; # (6.0) PERSONAL COMPUTER
+1F4BF FE0E ; text style;  # (6.0) OPTICAL DISC
+1F4BF FE0F ; emoji style; # (6.0) OPTICAL DISC
+1F4CB FE0E ; text style;  # (6.0) CLIPBOARD
+1F4CB FE0F ; emoji style; # (6.0) CLIPBOARD
+1F4DA FE0E ; text style;  # (6.0) BOOKS
+1F4DA FE0F ; emoji style; # (6.0) BOOKS
+1F4DF FE0E ; text style;  # (6.0) PAGER
+1F4DF FE0F ; emoji style; # (6.0) PAGER
+1F4E4 FE0E ; text style;  # (6.0) OUTBOX TRAY
+1F4E4 FE0F ; emoji style; # (6.0) OUTBOX TRAY
+1F4E5 FE0E ; text style;  # (6.0) INBOX TRAY
+1F4E5 FE0F ; emoji style; # (6.0) INBOX TRAY
+1F4E6 FE0E ; text style;  # (6.0) PACKAGE
+1F4E6 FE0F ; emoji style; # (6.0) PACKAGE
+1F4EA FE0E ; text style;  # (6.0) CLOSED MAILBOX WITH LOWERED FLAG
+1F4EA FE0F ; emoji style; # (6.0) CLOSED MAILBOX WITH LOWERED FLAG
+1F4EB FE0E ; text style;  # (6.0) CLOSED MAILBOX WITH RAISED FLAG
+1F4EB FE0F ; emoji style; # (6.0) CLOSED MAILBOX WITH RAISED FLAG
+1F4EC FE0E ; text style;  # (6.0) OPEN MAILBOX WITH RAISED FLAG
+1F4EC FE0F ; emoji style; # (6.0) OPEN MAILBOX WITH RAISED FLAG
+1F4ED FE0E ; text style;  # (6.0) OPEN MAILBOX WITH LOWERED FLAG
+1F4ED FE0F ; emoji style; # (6.0) OPEN MAILBOX WITH LOWERED FLAG
+1F4F7 FE0E ; text style;  # (6.0) CAMERA
+1F4F7 FE0F ; emoji style; # (6.0) CAMERA
+1F4F9 FE0E ; text style;  # (6.0) VIDEO CAMERA
+1F4F9 FE0F ; emoji style; # (6.0) VIDEO CAMERA
+1F4FA FE0E ; text style;  # (6.0) TELEVISION
+1F4FA FE0F ; emoji style; # (6.0) TELEVISION
+1F4FB FE0E ; text style;  # (6.0) RADIO
+1F4FB FE0F ; emoji style; # (6.0) RADIO
+1F4FD FE0E ; text style;  # (7.0) FILM PROJECTOR
+1F4FD FE0F ; emoji style; # (7.0) FILM PROJECTOR
+1F508 FE0E ; text style;  # (6.0) SPEAKER
+1F508 FE0F ; emoji style; # (6.0) SPEAKER
+1F50D FE0E ; text style;  # (6.0) LEFT-POINTING MAGNIFYING GLASS
+1F50D FE0F ; emoji style; # (6.0) LEFT-POINTING MAGNIFYING GLASS
+1F512 FE0E ; text style;  # (6.0) LOCK
+1F512 FE0F ; emoji style; # (6.0) LOCK
+1F513 FE0E ; text style;  # (6.0) OPEN LOCK
+1F513 FE0F ; emoji style; # (6.0) OPEN LOCK
+1F549 FE0E ; text style;  # (7.0) OM SYMBOL
+1F549 FE0F ; emoji style; # (7.0) OM SYMBOL
+1F54A FE0E ; text style;  # (7.0) DOVE OF PEACE
+1F54A FE0F ; emoji style; # (7.0) DOVE OF PEACE
+1F550 FE0E ; text style;  # (6.0) CLOCK FACE ONE OCLOCK
+1F550 FE0F ; emoji style; # (6.0) CLOCK FACE ONE OCLOCK
+1F551 FE0E ; text style;  # (6.0) CLOCK FACE TWO OCLOCK
+1F551 FE0F ; emoji style; # (6.0) CLOCK FACE TWO OCLOCK
+1F552 FE0E ; text style;  # (6.0) CLOCK FACE THREE OCLOCK
+1F552 FE0F ; emoji style; # (6.0) CLOCK FACE THREE OCLOCK
+1F553 FE0E ; text style;  # (6.0) CLOCK FACE FOUR OCLOCK
+1F553 FE0F ; emoji style; # (6.0) CLOCK FACE FOUR OCLOCK
+1F554 FE0E ; text style;  # (6.0) CLOCK FACE FIVE OCLOCK
+1F554 FE0F ; emoji style; # (6.0) CLOCK FACE FIVE OCLOCK
+1F555 FE0E ; text style;  # (6.0) CLOCK FACE SIX OCLOCK
+1F555 FE0F ; emoji style; # (6.0) CLOCK FACE SIX OCLOCK
+1F556 FE0E ; text style;  # (6.0) CLOCK FACE SEVEN OCLOCK
+1F556 FE0F ; emoji style; # (6.0) CLOCK FACE SEVEN OCLOCK
+1F557 FE0E ; text style;  # (6.0) CLOCK FACE EIGHT OCLOCK
+1F557 FE0F ; emoji style; # (6.0) CLOCK FACE EIGHT OCLOCK
+1F558 FE0E ; text style;  # (6.0) CLOCK FACE NINE OCLOCK
+1F558 FE0F ; emoji style; # (6.0) CLOCK FACE NINE OCLOCK
+1F559 FE0E ; text style;  # (6.0) CLOCK FACE TEN OCLOCK
+1F559 FE0F ; emoji style; # (6.0) CLOCK FACE TEN OCLOCK
+1F55A FE0E ; text style;  # (6.0) CLOCK FACE ELEVEN OCLOCK
+1F55A FE0F ; emoji style; # (6.0) CLOCK FACE ELEVEN OCLOCK
+1F55B FE0E ; text style;  # (6.0) CLOCK FACE TWELVE OCLOCK
+1F55B FE0F ; emoji style; # (6.0) CLOCK FACE TWELVE OCLOCK
+1F55C FE0E ; text style;  # (6.0) CLOCK FACE ONE-THIRTY
+1F55C FE0F ; emoji style; # (6.0) CLOCK FACE ONE-THIRTY
+1F55D FE0E ; text style;  # (6.0) CLOCK FACE TWO-THIRTY
+1F55D FE0F ; emoji style; # (6.0) CLOCK FACE TWO-THIRTY
+1F55E FE0E ; text style;  # (6.0) CLOCK FACE THREE-THIRTY
+1F55E FE0F ; emoji style; # (6.0) CLOCK FACE THREE-THIRTY
+1F55F FE0E ; text style;  # (6.0) CLOCK FACE FOUR-THIRTY
+1F55F FE0F ; emoji style; # (6.0) CLOCK FACE FOUR-THIRTY
+1F560 FE0E ; text style;  # (6.0) CLOCK FACE FIVE-THIRTY
+1F560 FE0F ; emoji style; # (6.0) CLOCK FACE FIVE-THIRTY
+1F561 FE0E ; text style;  # (6.0) CLOCK FACE SIX-THIRTY
+1F561 FE0F ; emoji style; # (6.0) CLOCK FACE SIX-THIRTY
+1F562 FE0E ; text style;  # (6.0) CLOCK FACE SEVEN-THIRTY
+1F562 FE0F ; emoji style; # (6.0) CLOCK FACE SEVEN-THIRTY
+1F563 FE0E ; text style;  # (6.0) CLOCK FACE EIGHT-THIRTY
+1F563 FE0F ; emoji style; # (6.0) CLOCK FACE EIGHT-THIRTY
+1F564 FE0E ; text style;  # (6.0) CLOCK FACE NINE-THIRTY
+1F564 FE0F ; emoji style; # (6.0) CLOCK FACE NINE-THIRTY
+1F565 FE0E ; text style;  # (6.0) CLOCK FACE TEN-THIRTY
+1F565 FE0F ; emoji style; # (6.0) CLOCK FACE TEN-THIRTY
+1F566 FE0E ; text style;  # (6.0) CLOCK FACE ELEVEN-THIRTY
+1F566 FE0F ; emoji style; # (6.0) CLOCK FACE ELEVEN-THIRTY
+1F567 FE0E ; text style;  # (6.0) CLOCK FACE TWELVE-THIRTY
+1F567 FE0F ; emoji style; # (6.0) CLOCK FACE TWELVE-THIRTY
+1F56F FE0E ; text style;  # (7.0) CANDLE
+1F56F FE0F ; emoji style; # (7.0) CANDLE
+1F570 FE0E ; text style;  # (7.0) MANTELPIECE CLOCK
+1F570 FE0F ; emoji style; # (7.0) MANTELPIECE CLOCK
+1F573 FE0E ; text style;  # (7.0) HOLE
+1F573 FE0F ; emoji style; # (7.0) HOLE
+1F574 FE0E ; text style;  # (7.0) MAN IN BUSINESS SUIT LEVITATING
+1F574 FE0F ; emoji style; # (7.0) MAN IN BUSINESS SUIT LEVITATING
+1F575 FE0E ; text style;  # (7.0) SLEUTH OR SPY
+1F575 FE0F ; emoji style; # (7.0) SLEUTH OR SPY
+1F576 FE0E ; text style;  # (7.0) DARK SUNGLASSES
+1F576 FE0F ; emoji style; # (7.0) DARK SUNGLASSES
+1F577 FE0E ; text style;  # (7.0) SPIDER
+1F577 FE0F ; emoji style; # (7.0) SPIDER
+1F578 FE0E ; text style;  # (7.0) SPIDER WEB
+1F578 FE0F ; emoji style; # (7.0) SPIDER WEB
+1F579 FE0E ; text style;  # (7.0) JOYSTICK
+1F579 FE0F ; emoji style; # (7.0) JOYSTICK
+1F587 FE0E ; text style;  # (7.0) LINKED PAPERCLIPS
+1F587 FE0F ; emoji style; # (7.0) LINKED PAPERCLIPS
+1F58A FE0E ; text style;  # (7.0) LOWER LEFT BALLPOINT PEN
+1F58A FE0F ; emoji style; # (7.0) LOWER LEFT BALLPOINT PEN
+1F58B FE0E ; text style;  # (7.0) LOWER LEFT FOUNTAIN PEN
+1F58B FE0F ; emoji style; # (7.0) LOWER LEFT FOUNTAIN PEN
+1F58C FE0E ; text style;  # (7.0) LOWER LEFT PAINTBRUSH
+1F58C FE0F ; emoji style; # (7.0) LOWER LEFT PAINTBRUSH
+1F58D FE0E ; text style;  # (7.0) LOWER LEFT CRAYON
+1F58D FE0F ; emoji style; # (7.0) LOWER LEFT CRAYON
+1F590 FE0E ; text style;  # (7.0) RAISED HAND WITH FINGERS SPLAYED
+1F590 FE0F ; emoji style; # (7.0) RAISED HAND WITH FINGERS SPLAYED
+1F5A5 FE0E ; text style;  # (7.0) DESKTOP COMPUTER
+1F5A5 FE0F ; emoji style; # (7.0) DESKTOP COMPUTER
+1F5A8 FE0E ; text style;  # (7.0) PRINTER
+1F5A8 FE0F ; emoji style; # (7.0) PRINTER
+1F5B1 FE0E ; text style;  # (7.0) THREE BUTTON MOUSE
+1F5B1 FE0F ; emoji style; # (7.0) THREE BUTTON MOUSE
+1F5B2 FE0E ; text style;  # (7.0) TRACKBALL
+1F5B2 FE0F ; emoji style; # (7.0) TRACKBALL
+1F5BC FE0E ; text style;  # (7.0) FRAME WITH PICTURE
+1F5BC FE0F ; emoji style; # (7.0) FRAME WITH PICTURE
+1F5C2 FE0E ; text style;  # (7.0) CARD INDEX DIVIDERS
+1F5C2 FE0F ; emoji style; # (7.0) CARD INDEX DIVIDERS
+1F5C3 FE0E ; text style;  # (7.0) CARD FILE BOX
+1F5C3 FE0F ; emoji style; # (7.0) CARD FILE BOX
+1F5C4 FE0E ; text style;  # (7.0) FILE CABINET
+1F5C4 FE0F ; emoji style; # (7.0) FILE CABINET
+1F5D1 FE0E ; text style;  # (7.0) WASTEBASKET
+1F5D1 FE0F ; emoji style; # (7.0) WASTEBASKET
+1F5D2 FE0E ; text style;  # (7.0) SPIRAL NOTE PAD
+1F5D2 FE0F ; emoji style; # (7.0) SPIRAL NOTE PAD
+1F5D3 FE0E ; text style;  # (7.0) SPIRAL CALENDAR PAD
+1F5D3 FE0F ; emoji style; # (7.0) SPIRAL CALENDAR PAD
+1F5DC FE0E ; text style;  # (7.0) COMPRESSION
+1F5DC FE0F ; emoji style; # (7.0) COMPRESSION
+1F5DD FE0E ; text style;  # (7.0) OLD KEY
+1F5DD FE0F ; emoji style; # (7.0) OLD KEY
+1F5DE FE0E ; text style;  # (7.0) ROLLED-UP NEWSPAPER
+1F5DE FE0F ; emoji style; # (7.0) ROLLED-UP NEWSPAPER
+1F5E1 FE0E ; text style;  # (7.0) DAGGER KNIFE
+1F5E1 FE0F ; emoji style; # (7.0) DAGGER KNIFE
+1F5E3 FE0E ; text style;  # (7.0) SPEAKING HEAD IN SILHOUETTE
+1F5E3 FE0F ; emoji style; # (7.0) SPEAKING HEAD IN SILHOUETTE
+1F5E8 FE0E ; text style;  # (7.0) LEFT SPEECH BUBBLE
+1F5E8 FE0F ; emoji style; # (7.0) LEFT SPEECH BUBBLE
+1F5EF FE0E ; text style;  # (7.0) RIGHT ANGER BUBBLE
+1F5EF FE0F ; emoji style; # (7.0) RIGHT ANGER BUBBLE
+1F5F3 FE0E ; text style;  # (7.0) BALLOT BOX WITH BALLOT
+1F5F3 FE0F ; emoji style; # (7.0) BALLOT BOX WITH BALLOT
+1F5FA FE0E ; text style;  # (7.0) WORLD MAP
+1F5FA FE0F ; emoji style; # (7.0) WORLD MAP
+1F610 FE0E ; text style;  # (6.0) NEUTRAL FACE
+1F610 FE0F ; emoji style; # (6.0) NEUTRAL FACE
+1F687 FE0E ; text style;  # (6.0) METRO
+1F687 FE0F ; emoji style; # (6.0) METRO
+1F68D FE0E ; text style;  # (6.0) ONCOMING BUS
+1F68D FE0F ; emoji style; # (6.0) ONCOMING BUS
+1F691 FE0E ; text style;  # (6.0) AMBULANCE
+1F691 FE0F ; emoji style; # (6.0) AMBULANCE
+1F694 FE0E ; text style;  # (6.0) ONCOMING POLICE CAR
+1F694 FE0F ; emoji style; # (6.0) ONCOMING POLICE CAR
+1F698 FE0E ; text style;  # (6.0) ONCOMING AUTOMOBILE
+1F698 FE0F ; emoji style; # (6.0) ONCOMING AUTOMOBILE
+1F6AD FE0E ; text style;  # (6.0) NO SMOKING SYMBOL
+1F6AD FE0F ; emoji style; # (6.0) NO SMOKING SYMBOL
+1F6B2 FE0E ; text style;  # (6.0) BICYCLE
+1F6B2 FE0F ; emoji style; # (6.0) BICYCLE
+1F6B9 FE0E ; text style;  # (6.0) MENS SYMBOL
+1F6B9 FE0F ; emoji style; # (6.0) MENS SYMBOL
+1F6BA FE0E ; text style;  # (6.0) WOMENS SYMBOL
+1F6BA FE0F ; emoji style; # (6.0) WOMENS SYMBOL
+1F6BC FE0E ; text style;  # (6.0) BABY SYMBOL
+1F6BC FE0F ; emoji style; # (6.0) BABY SYMBOL
+1F6CB FE0E ; text style;  # (7.0) COUCH AND LAMP
+1F6CB FE0F ; emoji style; # (7.0) COUCH AND LAMP
+1F6CD FE0E ; text style;  # (7.0) SHOPPING BAGS
+1F6CD FE0F ; emoji style; # (7.0) SHOPPING BAGS
+1F6CE FE0E ; text style;  # (7.0) BELLHOP BELL
+1F6CE FE0F ; emoji style; # (7.0) BELLHOP BELL
+1F6CF FE0E ; text style;  # (7.0) BED
+1F6CF FE0F ; emoji style; # (7.0) BED
+1F6E0 FE0E ; text style;  # (7.0) HAMMER AND WRENCH
+1F6E0 FE0F ; emoji style; # (7.0) HAMMER AND WRENCH
+1F6E1 FE0E ; text style;  # (7.0) SHIELD
+1F6E1 FE0F ; emoji style; # (7.0) SHIELD
+1F6E2 FE0E ; text style;  # (7.0) OIL DRUM
+1F6E2 FE0F ; emoji style; # (7.0) OIL DRUM
+1F6E3 FE0E ; text style;  # (7.0) MOTORWAY
+1F6E3 FE0F ; emoji style; # (7.0) MOTORWAY
+1F6E4 FE0E ; text style;  # (7.0) RAILWAY TRACK
+1F6E4 FE0F ; emoji style; # (7.0) RAILWAY TRACK
+1F6E5 FE0E ; text style;  # (7.0) MOTOR BOAT
+1F6E5 FE0F ; emoji style; # (7.0) MOTOR BOAT
+1F6E9 FE0E ; text style;  # (7.0) SMALL AIRPLANE
+1F6E9 FE0F ; emoji style; # (7.0) SMALL AIRPLANE
+1F6F0 FE0E ; text style;  # (7.0) SATELLITE
+1F6F0 FE0F ; emoji style; # (7.0) SATELLITE
+1F6F3 FE0E ; text style;  # (7.0) PASSENGER SHIP
+1F6F3 FE0F ; emoji style; # (7.0) PASSENGER SHIP
 
-#Total sequences: 354
+#Total sequences: 371
 
 #EOF
diff --git a/admin/unidata/emoji-zwj-sequences.txt 
b/admin/unidata/emoji-zwj-sequences.txt
index 4125bec62e2..25f8b6154b5 100644
--- a/admin/unidata/emoji-zwj-sequences.txt
+++ b/admin/unidata/emoji-zwj-sequences.txt
@@ -1,11 +1,11 @@
 # emoji-zwj-sequences.txt
-# Date: 2022-05-06, 16:14:52 GMT
-# © 2022 Unicode®, Inc.
+# Date: 2023-06-05, 20:04:50 GMT
+# © 2023 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in 
the U.S. and other countries.
 # For terms of use, see https://www.unicode.org/terms_of_use.html
 #
 # Emoji ZWJ Sequences for UTS #51
-# Version: 15.0
+# Version: 15.1
 #
 # For documentation and usage, see https://www.unicode.org/reports/tr51
 #
@@ -269,6 +269,10 @@
 1F469 1F3FF 200D 1F91D 200D 1F469 1F3FD     ; RGI_Emoji_ZWJ_Sequence  ; women 
holding hands: dark skin tone, medium skin tone          # E12.0  [1] (👩🏿‍🤝‍👩🏽)
 1F469 1F3FF 200D 1F91D 200D 1F469 1F3FE     ; RGI_Emoji_ZWJ_Sequence  ; women 
holding hands: dark skin tone, medium-dark skin tone     # E12.0  [1] (👩🏿‍🤝‍👩🏾)
 1F9D1 200D 1F91D 200D 1F9D1                 ; RGI_Emoji_ZWJ_Sequence  ; people 
holding hands                                           # E12.0  [1] (🧑‍🤝‍🧑)
+1F9D1 200D 1F9D1 200D 1F9D2                 ; RGI_Emoji_ZWJ_Sequence  ; 
family: adult, adult, child                                    # E15.1  [1] 
(🧑‍🧑‍🧒)
+1F9D1 200D 1F9D1 200D 1F9D2 200D 1F9D2      ; RGI_Emoji_ZWJ_Sequence  ; 
family: adult, adult, child, child                             # E15.1  [1] 
(🧑‍🧑‍🧒‍🧒)
+1F9D1 200D 1F9D2                            ; RGI_Emoji_ZWJ_Sequence  ; 
family: adult, child                                           # E15.1  [1] 
(🧑‍🧒)
+1F9D1 200D 1F9D2 200D 1F9D2                 ; RGI_Emoji_ZWJ_Sequence  ; 
family: adult, child, child                                    # E15.1  [1] 
(🧑‍🧒‍🧒)
 1F9D1 1F3FB 200D 2764 FE0F 200D 1F48B 200D 1F9D1 1F3FC; 
RGI_Emoji_ZWJ_Sequence; kiss: person, person, light skin tone, medium-light 
skin tone #E13.1[1] (🧑🏻‍❤️‍💋‍🧑🏼)
 1F9D1 1F3FB 200D 2764 FE0F 200D 1F48B 200D 1F9D1 1F3FD; 
RGI_Emoji_ZWJ_Sequence; kiss: person, person, light skin tone, medium skin tone 
#E13.1  [1] (🧑🏻‍❤️‍💋‍🧑🏽)
 1F9D1 1F3FB 200D 2764 FE0F 200D 1F48B 200D 1F9D1 1F3FE; 
RGI_Emoji_ZWJ_Sequence; kiss: person, person, light skin tone, medium-dark skin 
tone #E13.1[1] (🧑🏻‍❤️‍💋‍🧑🏾)
@@ -277,7 +281,6 @@
 1F9D1 1F3FB 200D 2764 FE0F 200D 1F9D1 1F3FD ; RGI_Emoji_ZWJ_Sequence  ; couple 
with heart: person, person, light skin tone, medium skin tone #E13.1[1] 
(🧑🏻‍❤️‍🧑🏽)
 1F9D1 1F3FB 200D 2764 FE0F 200D 1F9D1 1F3FE ; RGI_Emoji_ZWJ_Sequence  ; couple 
with heart: person, person, light skin tone, medium-dark skin tone #E13.1[1] 
(🧑🏻‍❤️‍🧑🏾)
 1F9D1 1F3FB 200D 2764 FE0F 200D 1F9D1 1F3FF ; RGI_Emoji_ZWJ_Sequence  ; couple 
with heart: person, person, light skin tone, dark skin tone #E13.1[1] (🧑🏻‍❤️‍🧑🏿)
-1F9D1 1F3FB 200D 1F384                      ; RGI_Emoji_ZWJ_Sequence  ; mx 
claus: light skin tone                                      # E13.0  [1] (🧑🏻‍🎄)
 1F9D1 1F3FB 200D 1F91D 200D 1F9D1 1F3FB     ; RGI_Emoji_ZWJ_Sequence  ; people 
holding hands: light skin tone                          # E12.0  [1] (🧑🏻‍🤝‍🧑🏻)
 1F9D1 1F3FB 200D 1F91D 200D 1F9D1 1F3FC     ; RGI_Emoji_ZWJ_Sequence  ; people 
holding hands: light skin tone, medium-light skin tone  # E12.1  [1] (🧑🏻‍🤝‍🧑🏼)
 1F9D1 1F3FB 200D 1F91D 200D 1F9D1 1F3FD     ; RGI_Emoji_ZWJ_Sequence  ; people 
holding hands: light skin tone, medium skin tone        # E12.1  [1] (🧑🏻‍🤝‍🧑🏽)
@@ -291,7 +294,6 @@
 1F9D1 1F3FC 200D 2764 FE0F 200D 1F9D1 1F3FD ; RGI_Emoji_ZWJ_Sequence  ; couple 
with heart: person, person, medium-light skin tone, medium skin tone #E13.1[1] 
(🧑🏼‍❤️‍🧑🏽)
 1F9D1 1F3FC 200D 2764 FE0F 200D 1F9D1 1F3FE ; RGI_Emoji_ZWJ_Sequence  ; couple 
with heart: person, person, medium-light skin tone, medium-dark skin tone 
#E13.1[1] (🧑🏼‍❤️‍🧑🏾)
 1F9D1 1F3FC 200D 2764 FE0F 200D 1F9D1 1F3FF ; RGI_Emoji_ZWJ_Sequence  ; couple 
with heart: person, person, medium-light skin tone, dark skin tone #E13.1[1] 
(🧑🏼‍❤️‍🧑🏿)
-1F9D1 1F3FC 200D 1F384                      ; RGI_Emoji_ZWJ_Sequence  ; mx 
claus: medium-light skin tone                               # E13.0  [1] (🧑🏼‍🎄)
 1F9D1 1F3FC 200D 1F91D 200D 1F9D1 1F3FB     ; RGI_Emoji_ZWJ_Sequence  ; people 
holding hands: medium-light skin tone, light skin tone  # E12.0  [1] (🧑🏼‍🤝‍🧑🏻)
 1F9D1 1F3FC 200D 1F91D 200D 1F9D1 1F3FC     ; RGI_Emoji_ZWJ_Sequence  ; people 
holding hands: medium-light skin tone                   # E12.0  [1] (🧑🏼‍🤝‍🧑🏼)
 1F9D1 1F3FC 200D 1F91D 200D 1F9D1 1F3FD     ; RGI_Emoji_ZWJ_Sequence  ; people 
holding hands: medium-light skin tone, medium skin tone # E12.1  [1] (🧑🏼‍🤝‍🧑🏽)
@@ -305,7 +307,6 @@
 1F9D1 1F3FD 200D 2764 FE0F 200D 1F9D1 1F3FC ; RGI_Emoji_ZWJ_Sequence  ; couple 
with heart: person, person, medium skin tone, medium-light skin tone #E13.1[1] 
(🧑🏽‍❤️‍🧑🏼)
 1F9D1 1F3FD 200D 2764 FE0F 200D 1F9D1 1F3FE ; RGI_Emoji_ZWJ_Sequence  ; couple 
with heart: person, person, medium skin tone, medium-dark skin tone #E13.1[1] 
(🧑🏽‍❤️‍🧑🏾)
 1F9D1 1F3FD 200D 2764 FE0F 200D 1F9D1 1F3FF ; RGI_Emoji_ZWJ_Sequence  ; couple 
with heart: person, person, medium skin tone, dark skin tone #E13.1[1] 
(🧑🏽‍❤️‍🧑🏿)
-1F9D1 1F3FD 200D 1F384                      ; RGI_Emoji_ZWJ_Sequence  ; mx 
claus: medium skin tone                                     # E13.0  [1] (🧑🏽‍🎄)
 1F9D1 1F3FD 200D 1F91D 200D 1F9D1 1F3FB     ; RGI_Emoji_ZWJ_Sequence  ; people 
holding hands: medium skin tone, light skin tone        # E12.0  [1] (🧑🏽‍🤝‍🧑🏻)
 1F9D1 1F3FD 200D 1F91D 200D 1F9D1 1F3FC     ; RGI_Emoji_ZWJ_Sequence  ; people 
holding hands: medium skin tone, medium-light skin tone # E12.0  [1] (🧑🏽‍🤝‍🧑🏼)
 1F9D1 1F3FD 200D 1F91D 200D 1F9D1 1F3FD     ; RGI_Emoji_ZWJ_Sequence  ; people 
holding hands: medium skin tone                         # E12.0  [1] (🧑🏽‍🤝‍🧑🏽)
@@ -319,7 +320,6 @@
 1F9D1 1F3FE 200D 2764 FE0F 200D 1F9D1 1F3FC ; RGI_Emoji_ZWJ_Sequence  ; couple 
with heart: person, person, medium-dark skin tone, medium-light skin tone 
#E13.1[1] (🧑🏾‍❤️‍🧑🏼)
 1F9D1 1F3FE 200D 2764 FE0F 200D 1F9D1 1F3FD ; RGI_Emoji_ZWJ_Sequence  ; couple 
with heart: person, person, medium-dark skin tone, medium skin tone #E13.1[1] 
(🧑🏾‍❤️‍🧑🏽)
 1F9D1 1F3FE 200D 2764 FE0F 200D 1F9D1 1F3FF ; RGI_Emoji_ZWJ_Sequence  ; couple 
with heart: person, person, medium-dark skin tone, dark skin tone #E13.1[1] 
(🧑🏾‍❤️‍🧑🏿)
-1F9D1 1F3FE 200D 1F384                      ; RGI_Emoji_ZWJ_Sequence  ; mx 
claus: medium-dark skin tone                                # E13.0  [1] (🧑🏾‍🎄)
 1F9D1 1F3FE 200D 1F91D 200D 1F9D1 1F3FB     ; RGI_Emoji_ZWJ_Sequence  ; people 
holding hands: medium-dark skin tone, light skin tone   # E12.0  [1] (🧑🏾‍🤝‍🧑🏻)
 1F9D1 1F3FE 200D 1F91D 200D 1F9D1 1F3FC     ; RGI_Emoji_ZWJ_Sequence  ; people 
holding hands: medium-dark skin tone, medium-light skin tone #E12.0[1] (🧑🏾‍🤝‍🧑🏼)
 1F9D1 1F3FE 200D 1F91D 200D 1F9D1 1F3FD     ; RGI_Emoji_ZWJ_Sequence  ; people 
holding hands: medium-dark skin tone, medium skin tone  # E12.0  [1] (🧑🏾‍🤝‍🧑🏽)
@@ -333,7 +333,6 @@
 1F9D1 1F3FF 200D 2764 FE0F 200D 1F9D1 1F3FC ; RGI_Emoji_ZWJ_Sequence  ; couple 
with heart: person, person, dark skin tone, medium-light skin tone #E13.1[1] 
(🧑🏿‍❤️‍🧑🏼)
 1F9D1 1F3FF 200D 2764 FE0F 200D 1F9D1 1F3FD ; RGI_Emoji_ZWJ_Sequence  ; couple 
with heart: person, person, dark skin tone, medium skin tone #E13.1[1] 
(🧑🏿‍❤️‍🧑🏽)
 1F9D1 1F3FF 200D 2764 FE0F 200D 1F9D1 1F3FE ; RGI_Emoji_ZWJ_Sequence  ; couple 
with heart: person, person, dark skin tone, medium-dark skin tone #E13.1[1] 
(🧑🏿‍❤️‍🧑🏾)
-1F9D1 1F3FF 200D 1F384                      ; RGI_Emoji_ZWJ_Sequence  ; mx 
claus: dark skin tone                                       # E13.0  [1] (🧑🏿‍🎄)
 1F9D1 1F3FF 200D 1F91D 200D 1F9D1 1F3FB     ; RGI_Emoji_ZWJ_Sequence  ; people 
holding hands: dark skin tone, light skin tone          # E12.0  [1] (🧑🏿‍🤝‍🧑🏻)
 1F9D1 1F3FF 200D 1F91D 200D 1F9D1 1F3FC     ; RGI_Emoji_ZWJ_Sequence  ; people 
holding hands: dark skin tone, medium-light skin tone   # E12.0  [1] (🧑🏿‍🤝‍🧑🏼)
 1F9D1 1F3FF 200D 1F91D 200D 1F9D1 1F3FD     ; RGI_Emoji_ZWJ_Sequence  ; people 
holding hands: dark skin tone, medium skin tone         # E12.0  [1] (🧑🏿‍🤝‍🧑🏽)
@@ -360,12 +359,18 @@
 1FAF1 1F3FF 200D 1FAF2 1F3FD                ; RGI_Emoji_ZWJ_Sequence  ; 
handshake: dark skin tone, medium skin tone                    # E14.0  [1] 
(🫱🏿‍🫲🏽)
 1FAF1 1F3FF 200D 1FAF2 1F3FE                ; RGI_Emoji_ZWJ_Sequence  ; 
handshake: dark skin tone, medium-dark skin tone               # E14.0  [1] 
(🫱🏿‍🫲🏾)
 
-# Total elements: 332
+# Total elements: 331
 
 # ================================================
 
 # RGI_Emoji_ZWJ_Sequence: Role
 
+1F3C3 200D 27A1 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; person 
running facing right                                    # E15.1  [1] (🏃‍➡️)
+1F3C3 1F3FB 200D 27A1 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; person 
running facing right: light skin tone                   # E15.1  [1] (🏃🏻‍➡️)
+1F3C3 1F3FC 200D 27A1 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; person 
running facing right: medium-light skin tone            # E15.1  [1] (🏃🏼‍➡️)
+1F3C3 1F3FD 200D 27A1 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; person 
running facing right: medium skin tone                  # E15.1  [1] (🏃🏽‍➡️)
+1F3C3 1F3FE 200D 27A1 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; person 
running facing right: medium-dark skin tone             # E15.1  [1] (🏃🏾‍➡️)
+1F3C3 1F3FF 200D 27A1 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; person 
running facing right: dark skin tone                    # E15.1  [1] (🏃🏿‍➡️)
 1F468 200D 2695 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; man 
health worker                                              # E4.0   [1] (👨‍⚕️)
 1F468 200D 2696 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; man 
judge                                                      # E4.0   [1] (👨‍⚖️)
 1F468 200D 2708 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; man 
pilot                                                      # E4.0   [1] (👨‍✈️)
@@ -384,8 +389,11 @@
 1F468 200D 1F680                            ; RGI_Emoji_ZWJ_Sequence  ; man 
astronaut                                                  # E4.0   [1] (👨‍🚀)
 1F468 200D 1F692                            ; RGI_Emoji_ZWJ_Sequence  ; man 
firefighter                                                # E4.0   [1] (👨‍🚒)
 1F468 200D 1F9AF                            ; RGI_Emoji_ZWJ_Sequence  ; man 
with white cane                                            # E12.0  [1] (👨‍🦯)
+1F468 200D 1F9AF 200D 27A1 FE0F             ; RGI_Emoji_ZWJ_Sequence  ; man 
with white cane facing right                               # E15.1  [1] (👨‍🦯‍➡️)
 1F468 200D 1F9BC                            ; RGI_Emoji_ZWJ_Sequence  ; man in 
motorized wheelchair                                    # E12.0  [1] (👨‍🦼)
+1F468 200D 1F9BC 200D 27A1 FE0F             ; RGI_Emoji_ZWJ_Sequence  ; man in 
motorized wheelchair facing right                       # E15.1  [1] (👨‍🦼‍➡️)
 1F468 200D 1F9BD                            ; RGI_Emoji_ZWJ_Sequence  ; man in 
manual wheelchair                                       # E12.0  [1] (👨‍🦽)
+1F468 200D 1F9BD 200D 27A1 FE0F             ; RGI_Emoji_ZWJ_Sequence  ; man in 
manual wheelchair facing right                          # E15.1  [1] (👨‍🦽‍➡️)
 1F468 1F3FB 200D 2695 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
health worker: light skin tone                             # E4.0   [1] (👨🏻‍⚕️)
 1F468 1F3FB 200D 2696 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
judge: light skin tone                                     # E4.0   [1] (👨🏻‍⚖️)
 1F468 1F3FB 200D 2708 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
pilot: light skin tone                                     # E4.0   [1] (👨🏻‍✈️)
@@ -404,8 +412,11 @@
 1F468 1F3FB 200D 1F680                      ; RGI_Emoji_ZWJ_Sequence  ; man 
astronaut: light skin tone                                 # E4.0   [1] (👨🏻‍🚀)
 1F468 1F3FB 200D 1F692                      ; RGI_Emoji_ZWJ_Sequence  ; man 
firefighter: light skin tone                               # E4.0   [1] (👨🏻‍🚒)
 1F468 1F3FB 200D 1F9AF                      ; RGI_Emoji_ZWJ_Sequence  ; man 
with white cane: light skin tone                           # E12.0  [1] (👨🏻‍🦯)
+1F468 1F3FB 200D 1F9AF 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; man 
with white cane facing right: light skin tone              # E15.1  [1] 
(👨🏻‍🦯‍➡️)
 1F468 1F3FB 200D 1F9BC                      ; RGI_Emoji_ZWJ_Sequence  ; man in 
motorized wheelchair: light skin tone                   # E12.0  [1] (👨🏻‍🦼)
+1F468 1F3FB 200D 1F9BC 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; man in 
motorized wheelchair facing right: light skin tone      # E15.1  [1] (👨🏻‍🦼‍➡️)
 1F468 1F3FB 200D 1F9BD                      ; RGI_Emoji_ZWJ_Sequence  ; man in 
manual wheelchair: light skin tone                      # E12.0  [1] (👨🏻‍🦽)
+1F468 1F3FB 200D 1F9BD 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; man in 
manual wheelchair facing right: light skin tone         # E15.1  [1] (👨🏻‍🦽‍➡️)
 1F468 1F3FC 200D 2695 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
health worker: medium-light skin tone                      # E4.0   [1] (👨🏼‍⚕️)
 1F468 1F3FC 200D 2696 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
judge: medium-light skin tone                              # E4.0   [1] (👨🏼‍⚖️)
 1F468 1F3FC 200D 2708 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
pilot: medium-light skin tone                              # E4.0   [1] (👨🏼‍✈️)
@@ -424,8 +435,11 @@
 1F468 1F3FC 200D 1F680                      ; RGI_Emoji_ZWJ_Sequence  ; man 
astronaut: medium-light skin tone                          # E4.0   [1] (👨🏼‍🚀)
 1F468 1F3FC 200D 1F692                      ; RGI_Emoji_ZWJ_Sequence  ; man 
firefighter: medium-light skin tone                        # E4.0   [1] (👨🏼‍🚒)
 1F468 1F3FC 200D 1F9AF                      ; RGI_Emoji_ZWJ_Sequence  ; man 
with white cane: medium-light skin tone                    # E12.0  [1] (👨🏼‍🦯)
+1F468 1F3FC 200D 1F9AF 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; man 
with white cane facing right: medium-light skin tone       # E15.1  [1] 
(👨🏼‍🦯‍➡️)
 1F468 1F3FC 200D 1F9BC                      ; RGI_Emoji_ZWJ_Sequence  ; man in 
motorized wheelchair: medium-light skin tone            # E12.0  [1] (👨🏼‍🦼)
+1F468 1F3FC 200D 1F9BC 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; man in 
motorized wheelchair facing right: medium-light skin tone #E15.1 [1] (👨🏼‍🦼‍➡️)
 1F468 1F3FC 200D 1F9BD                      ; RGI_Emoji_ZWJ_Sequence  ; man in 
manual wheelchair: medium-light skin tone               # E12.0  [1] (👨🏼‍🦽)
+1F468 1F3FC 200D 1F9BD 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; man in 
manual wheelchair facing right: medium-light skin tone  # E15.1  [1] (👨🏼‍🦽‍➡️)
 1F468 1F3FD 200D 2695 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
health worker: medium skin tone                            # E4.0   [1] (👨🏽‍⚕️)
 1F468 1F3FD 200D 2696 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
judge: medium skin tone                                    # E4.0   [1] (👨🏽‍⚖️)
 1F468 1F3FD 200D 2708 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
pilot: medium skin tone                                    # E4.0   [1] (👨🏽‍✈️)
@@ -444,8 +458,11 @@
 1F468 1F3FD 200D 1F680                      ; RGI_Emoji_ZWJ_Sequence  ; man 
astronaut: medium skin tone                                # E4.0   [1] (👨🏽‍🚀)
 1F468 1F3FD 200D 1F692                      ; RGI_Emoji_ZWJ_Sequence  ; man 
firefighter: medium skin tone                              # E4.0   [1] (👨🏽‍🚒)
 1F468 1F3FD 200D 1F9AF                      ; RGI_Emoji_ZWJ_Sequence  ; man 
with white cane: medium skin tone                          # E12.0  [1] (👨🏽‍🦯)
+1F468 1F3FD 200D 1F9AF 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; man 
with white cane facing right: medium skin tone             # E15.1  [1] 
(👨🏽‍🦯‍➡️)
 1F468 1F3FD 200D 1F9BC                      ; RGI_Emoji_ZWJ_Sequence  ; man in 
motorized wheelchair: medium skin tone                  # E12.0  [1] (👨🏽‍🦼)
+1F468 1F3FD 200D 1F9BC 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; man in 
motorized wheelchair facing right: medium skin tone     # E15.1  [1] (👨🏽‍🦼‍➡️)
 1F468 1F3FD 200D 1F9BD                      ; RGI_Emoji_ZWJ_Sequence  ; man in 
manual wheelchair: medium skin tone                     # E12.0  [1] (👨🏽‍🦽)
+1F468 1F3FD 200D 1F9BD 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; man in 
manual wheelchair facing right: medium skin tone        # E15.1  [1] (👨🏽‍🦽‍➡️)
 1F468 1F3FE 200D 2695 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
health worker: medium-dark skin tone                       # E4.0   [1] (👨🏾‍⚕️)
 1F468 1F3FE 200D 2696 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
judge: medium-dark skin tone                               # E4.0   [1] (👨🏾‍⚖️)
 1F468 1F3FE 200D 2708 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
pilot: medium-dark skin tone                               # E4.0   [1] (👨🏾‍✈️)
@@ -464,8 +481,11 @@
 1F468 1F3FE 200D 1F680                      ; RGI_Emoji_ZWJ_Sequence  ; man 
astronaut: medium-dark skin tone                           # E4.0   [1] (👨🏾‍🚀)
 1F468 1F3FE 200D 1F692                      ; RGI_Emoji_ZWJ_Sequence  ; man 
firefighter: medium-dark skin tone                         # E4.0   [1] (👨🏾‍🚒)
 1F468 1F3FE 200D 1F9AF                      ; RGI_Emoji_ZWJ_Sequence  ; man 
with white cane: medium-dark skin tone                     # E12.0  [1] (👨🏾‍🦯)
+1F468 1F3FE 200D 1F9AF 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; man 
with white cane facing right: medium-dark skin tone        # E15.1  [1] 
(👨🏾‍🦯‍➡️)
 1F468 1F3FE 200D 1F9BC                      ; RGI_Emoji_ZWJ_Sequence  ; man in 
motorized wheelchair: medium-dark skin tone             # E12.0  [1] (👨🏾‍🦼)
+1F468 1F3FE 200D 1F9BC 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; man in 
motorized wheelchair facing right: medium-dark skin tone #E15.1  [1] (👨🏾‍🦼‍➡️)
 1F468 1F3FE 200D 1F9BD                      ; RGI_Emoji_ZWJ_Sequence  ; man in 
manual wheelchair: medium-dark skin tone                # E12.0  [1] (👨🏾‍🦽)
+1F468 1F3FE 200D 1F9BD 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; man in 
manual wheelchair facing right: medium-dark skin tone   # E15.1  [1] (👨🏾‍🦽‍➡️)
 1F468 1F3FF 200D 2695 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
health worker: dark skin tone                              # E4.0   [1] (👨🏿‍⚕️)
 1F468 1F3FF 200D 2696 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
judge: dark skin tone                                      # E4.0   [1] (👨🏿‍⚖️)
 1F468 1F3FF 200D 2708 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
pilot: dark skin tone                                      # E4.0   [1] (👨🏿‍✈️)
@@ -484,8 +504,11 @@
 1F468 1F3FF 200D 1F680                      ; RGI_Emoji_ZWJ_Sequence  ; man 
astronaut: dark skin tone                                  # E4.0   [1] (👨🏿‍🚀)
 1F468 1F3FF 200D 1F692                      ; RGI_Emoji_ZWJ_Sequence  ; man 
firefighter: dark skin tone                                # E4.0   [1] (👨🏿‍🚒)
 1F468 1F3FF 200D 1F9AF                      ; RGI_Emoji_ZWJ_Sequence  ; man 
with white cane: dark skin tone                            # E12.0  [1] (👨🏿‍🦯)
+1F468 1F3FF 200D 1F9AF 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; man 
with white cane facing right: dark skin tone               # E15.1  [1] 
(👨🏿‍🦯‍➡️)
 1F468 1F3FF 200D 1F9BC                      ; RGI_Emoji_ZWJ_Sequence  ; man in 
motorized wheelchair: dark skin tone                    # E12.0  [1] (👨🏿‍🦼)
+1F468 1F3FF 200D 1F9BC 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; man in 
motorized wheelchair facing right: dark skin tone       # E15.1  [1] (👨🏿‍🦼‍➡️)
 1F468 1F3FF 200D 1F9BD                      ; RGI_Emoji_ZWJ_Sequence  ; man in 
manual wheelchair: dark skin tone                       # E12.0  [1] (👨🏿‍🦽)
+1F468 1F3FF 200D 1F9BD 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; man in 
manual wheelchair facing right: dark skin tone          # E15.1  [1] (👨🏿‍🦽‍➡️)
 1F469 200D 2695 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; woman 
health worker                                            # E4.0   [1] (👩‍⚕️)
 1F469 200D 2696 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; woman 
judge                                                    # E4.0   [1] (👩‍⚖️)
 1F469 200D 2708 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; woman 
pilot                                                    # E4.0   [1] (👩‍✈️)
@@ -504,8 +527,11 @@
 1F469 200D 1F680                            ; RGI_Emoji_ZWJ_Sequence  ; woman 
astronaut                                                # E4.0   [1] (👩‍🚀)
 1F469 200D 1F692                            ; RGI_Emoji_ZWJ_Sequence  ; woman 
firefighter                                              # E4.0   [1] (👩‍🚒)
 1F469 200D 1F9AF                            ; RGI_Emoji_ZWJ_Sequence  ; woman 
with white cane                                          # E12.0  [1] (👩‍🦯)
+1F469 200D 1F9AF 200D 27A1 FE0F             ; RGI_Emoji_ZWJ_Sequence  ; woman 
with white cane facing right                             # E15.1  [1] (👩‍🦯‍➡️)
 1F469 200D 1F9BC                            ; RGI_Emoji_ZWJ_Sequence  ; woman 
in motorized wheelchair                                  # E12.0  [1] (👩‍🦼)
+1F469 200D 1F9BC 200D 27A1 FE0F             ; RGI_Emoji_ZWJ_Sequence  ; woman 
in motorized wheelchair facing right                     # E15.1  [1] (👩‍🦼‍➡️)
 1F469 200D 1F9BD                            ; RGI_Emoji_ZWJ_Sequence  ; woman 
in manual wheelchair                                     # E12.0  [1] (👩‍🦽)
+1F469 200D 1F9BD 200D 27A1 FE0F             ; RGI_Emoji_ZWJ_Sequence  ; woman 
in manual wheelchair facing right                        # E15.1  [1] (👩‍🦽‍➡️)
 1F469 1F3FB 200D 2695 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
health worker: light skin tone                           # E4.0   [1] (👩🏻‍⚕️)
 1F469 1F3FB 200D 2696 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
judge: light skin tone                                   # E4.0   [1] (👩🏻‍⚖️)
 1F469 1F3FB 200D 2708 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
pilot: light skin tone                                   # E4.0   [1] (👩🏻‍✈️)
@@ -524,8 +550,11 @@
 1F469 1F3FB 200D 1F680                      ; RGI_Emoji_ZWJ_Sequence  ; woman 
astronaut: light skin tone                               # E4.0   [1] (👩🏻‍🚀)
 1F469 1F3FB 200D 1F692                      ; RGI_Emoji_ZWJ_Sequence  ; woman 
firefighter: light skin tone                             # E4.0   [1] (👩🏻‍🚒)
 1F469 1F3FB 200D 1F9AF                      ; RGI_Emoji_ZWJ_Sequence  ; woman 
with white cane: light skin tone                         # E12.0  [1] (👩🏻‍🦯)
+1F469 1F3FB 200D 1F9AF 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; woman 
with white cane facing right: light skin tone            # E15.1  [1] (👩🏻‍🦯‍➡️)
 1F469 1F3FB 200D 1F9BC                      ; RGI_Emoji_ZWJ_Sequence  ; woman 
in motorized wheelchair: light skin tone                 # E12.0  [1] (👩🏻‍🦼)
+1F469 1F3FB 200D 1F9BC 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; woman 
in motorized wheelchair facing right: light skin tone    # E15.1  [1] (👩🏻‍🦼‍➡️)
 1F469 1F3FB 200D 1F9BD                      ; RGI_Emoji_ZWJ_Sequence  ; woman 
in manual wheelchair: light skin tone                    # E12.0  [1] (👩🏻‍🦽)
+1F469 1F3FB 200D 1F9BD 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; woman 
in manual wheelchair facing right: light skin tone       # E15.1  [1] (👩🏻‍🦽‍➡️)
 1F469 1F3FC 200D 2695 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
health worker: medium-light skin tone                    # E4.0   [1] (👩🏼‍⚕️)
 1F469 1F3FC 200D 2696 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
judge: medium-light skin tone                            # E4.0   [1] (👩🏼‍⚖️)
 1F469 1F3FC 200D 2708 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
pilot: medium-light skin tone                            # E4.0   [1] (👩🏼‍✈️)
@@ -544,8 +573,11 @@
 1F469 1F3FC 200D 1F680                      ; RGI_Emoji_ZWJ_Sequence  ; woman 
astronaut: medium-light skin tone                        # E4.0   [1] (👩🏼‍🚀)
 1F469 1F3FC 200D 1F692                      ; RGI_Emoji_ZWJ_Sequence  ; woman 
firefighter: medium-light skin tone                      # E4.0   [1] (👩🏼‍🚒)
 1F469 1F3FC 200D 1F9AF                      ; RGI_Emoji_ZWJ_Sequence  ; woman 
with white cane: medium-light skin tone                  # E12.0  [1] (👩🏼‍🦯)
+1F469 1F3FC 200D 1F9AF 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; woman 
with white cane facing right: medium-light skin tone     # E15.1  [1] (👩🏼‍🦯‍➡️)
 1F469 1F3FC 200D 1F9BC                      ; RGI_Emoji_ZWJ_Sequence  ; woman 
in motorized wheelchair: medium-light skin tone          # E12.0  [1] (👩🏼‍🦼)
+1F469 1F3FC 200D 1F9BC 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; woman 
in motorized wheelchair facing right: medium-light skin tone #E15.1[1] (👩🏼‍🦼‍➡️)
 1F469 1F3FC 200D 1F9BD                      ; RGI_Emoji_ZWJ_Sequence  ; woman 
in manual wheelchair: medium-light skin tone             # E12.0  [1] (👩🏼‍🦽)
+1F469 1F3FC 200D 1F9BD 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; woman 
in manual wheelchair facing right: medium-light skin tone #E15.1  [1] (👩🏼‍🦽‍➡️)
 1F469 1F3FD 200D 2695 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
health worker: medium skin tone                          # E4.0   [1] (👩🏽‍⚕️)
 1F469 1F3FD 200D 2696 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
judge: medium skin tone                                  # E4.0   [1] (👩🏽‍⚖️)
 1F469 1F3FD 200D 2708 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
pilot: medium skin tone                                  # E4.0   [1] (👩🏽‍✈️)
@@ -564,8 +596,11 @@
 1F469 1F3FD 200D 1F680                      ; RGI_Emoji_ZWJ_Sequence  ; woman 
astronaut: medium skin tone                              # E4.0   [1] (👩🏽‍🚀)
 1F469 1F3FD 200D 1F692                      ; RGI_Emoji_ZWJ_Sequence  ; woman 
firefighter: medium skin tone                            # E4.0   [1] (👩🏽‍🚒)
 1F469 1F3FD 200D 1F9AF                      ; RGI_Emoji_ZWJ_Sequence  ; woman 
with white cane: medium skin tone                        # E12.0  [1] (👩🏽‍🦯)
+1F469 1F3FD 200D 1F9AF 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; woman 
with white cane facing right: medium skin tone           # E15.1  [1] (👩🏽‍🦯‍➡️)
 1F469 1F3FD 200D 1F9BC                      ; RGI_Emoji_ZWJ_Sequence  ; woman 
in motorized wheelchair: medium skin tone                # E12.0  [1] (👩🏽‍🦼)
+1F469 1F3FD 200D 1F9BC 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; woman 
in motorized wheelchair facing right: medium skin tone   # E15.1  [1] (👩🏽‍🦼‍➡️)
 1F469 1F3FD 200D 1F9BD                      ; RGI_Emoji_ZWJ_Sequence  ; woman 
in manual wheelchair: medium skin tone                   # E12.0  [1] (👩🏽‍🦽)
+1F469 1F3FD 200D 1F9BD 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; woman 
in manual wheelchair facing right: medium skin tone      # E15.1  [1] (👩🏽‍🦽‍➡️)
 1F469 1F3FE 200D 2695 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
health worker: medium-dark skin tone                     # E4.0   [1] (👩🏾‍⚕️)
 1F469 1F3FE 200D 2696 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
judge: medium-dark skin tone                             # E4.0   [1] (👩🏾‍⚖️)
 1F469 1F3FE 200D 2708 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
pilot: medium-dark skin tone                             # E4.0   [1] (👩🏾‍✈️)
@@ -584,8 +619,11 @@
 1F469 1F3FE 200D 1F680                      ; RGI_Emoji_ZWJ_Sequence  ; woman 
astronaut: medium-dark skin tone                         # E4.0   [1] (👩🏾‍🚀)
 1F469 1F3FE 200D 1F692                      ; RGI_Emoji_ZWJ_Sequence  ; woman 
firefighter: medium-dark skin tone                       # E4.0   [1] (👩🏾‍🚒)
 1F469 1F3FE 200D 1F9AF                      ; RGI_Emoji_ZWJ_Sequence  ; woman 
with white cane: medium-dark skin tone                   # E12.0  [1] (👩🏾‍🦯)
+1F469 1F3FE 200D 1F9AF 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; woman 
with white cane facing right: medium-dark skin tone      # E15.1  [1] (👩🏾‍🦯‍➡️)
 1F469 1F3FE 200D 1F9BC                      ; RGI_Emoji_ZWJ_Sequence  ; woman 
in motorized wheelchair: medium-dark skin tone           # E12.0  [1] (👩🏾‍🦼)
+1F469 1F3FE 200D 1F9BC 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; woman 
in motorized wheelchair facing right: medium-dark skin tone #E15.1[1] (👩🏾‍🦼‍➡️)
 1F469 1F3FE 200D 1F9BD                      ; RGI_Emoji_ZWJ_Sequence  ; woman 
in manual wheelchair: medium-dark skin tone              # E12.0  [1] (👩🏾‍🦽)
+1F469 1F3FE 200D 1F9BD 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; woman 
in manual wheelchair facing right: medium-dark skin tone # E15.1  [1] (👩🏾‍🦽‍➡️)
 1F469 1F3FF 200D 2695 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
health worker: dark skin tone                            # E4.0   [1] (👩🏿‍⚕️)
 1F469 1F3FF 200D 2696 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
judge: dark skin tone                                    # E4.0   [1] (👩🏿‍⚖️)
 1F469 1F3FF 200D 2708 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
pilot: dark skin tone                                    # E4.0   [1] (👩🏿‍✈️)
@@ -604,14 +642,30 @@
 1F469 1F3FF 200D 1F680                      ; RGI_Emoji_ZWJ_Sequence  ; woman 
astronaut: dark skin tone                                # E4.0   [1] (👩🏿‍🚀)
 1F469 1F3FF 200D 1F692                      ; RGI_Emoji_ZWJ_Sequence  ; woman 
firefighter: dark skin tone                              # E4.0   [1] (👩🏿‍🚒)
 1F469 1F3FF 200D 1F9AF                      ; RGI_Emoji_ZWJ_Sequence  ; woman 
with white cane: dark skin tone                          # E12.0  [1] (👩🏿‍🦯)
+1F469 1F3FF 200D 1F9AF 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; woman 
with white cane facing right: dark skin tone             # E15.1  [1] (👩🏿‍🦯‍➡️)
 1F469 1F3FF 200D 1F9BC                      ; RGI_Emoji_ZWJ_Sequence  ; woman 
in motorized wheelchair: dark skin tone                  # E12.0  [1] (👩🏿‍🦼)
+1F469 1F3FF 200D 1F9BC 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; woman 
in motorized wheelchair facing right: dark skin tone     # E15.1  [1] (👩🏿‍🦼‍➡️)
 1F469 1F3FF 200D 1F9BD                      ; RGI_Emoji_ZWJ_Sequence  ; woman 
in manual wheelchair: dark skin tone                     # E12.0  [1] (👩🏿‍🦽)
+1F469 1F3FF 200D 1F9BD 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; woman 
in manual wheelchair facing right: dark skin tone        # E15.1  [1] (👩🏿‍🦽‍➡️)
+1F6B6 200D 27A1 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; person 
walking facing right                                    # E15.1  [1] (🚶‍➡️)
+1F6B6 1F3FB 200D 27A1 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; person 
walking facing right: light skin tone                   # E15.1  [1] (🚶🏻‍➡️)
+1F6B6 1F3FC 200D 27A1 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; person 
walking facing right: medium-light skin tone            # E15.1  [1] (🚶🏼‍➡️)
+1F6B6 1F3FD 200D 27A1 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; person 
walking facing right: medium skin tone                  # E15.1  [1] (🚶🏽‍➡️)
+1F6B6 1F3FE 200D 27A1 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; person 
walking facing right: medium-dark skin tone             # E15.1  [1] (🚶🏾‍➡️)
+1F6B6 1F3FF 200D 27A1 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; person 
walking facing right: dark skin tone                    # E15.1  [1] (🚶🏿‍➡️)
+1F9CE 200D 27A1 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; person 
kneeling facing right                                   # E15.1  [1] (🧎‍➡️)
+1F9CE 1F3FB 200D 27A1 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; person 
kneeling facing right: light skin tone                  # E15.1  [1] (🧎🏻‍➡️)
+1F9CE 1F3FC 200D 27A1 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; person 
kneeling facing right: medium-light skin tone           # E15.1  [1] (🧎🏼‍➡️)
+1F9CE 1F3FD 200D 27A1 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; person 
kneeling facing right: medium skin tone                 # E15.1  [1] (🧎🏽‍➡️)
+1F9CE 1F3FE 200D 27A1 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; person 
kneeling facing right: medium-dark skin tone            # E15.1  [1] (🧎🏾‍➡️)
+1F9CE 1F3FF 200D 27A1 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; person 
kneeling facing right: dark skin tone                   # E15.1  [1] (🧎🏿‍➡️)
 1F9D1 200D 2695 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; health 
worker                                                  # E12.1  [1] (🧑‍⚕️)
 1F9D1 200D 2696 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; judge  
                                                        # E12.1  [1] (🧑‍⚖️)
 1F9D1 200D 2708 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; pilot  
                                                        # E12.1  [1] (🧑‍✈️)
 1F9D1 200D 1F33E                            ; RGI_Emoji_ZWJ_Sequence  ; farmer 
                                                        # E12.1  [1] (🧑‍🌾)
 1F9D1 200D 1F373                            ; RGI_Emoji_ZWJ_Sequence  ; cook   
                                                        # E12.1  [1] (🧑‍🍳)
 1F9D1 200D 1F37C                            ; RGI_Emoji_ZWJ_Sequence  ; person 
feeding baby                                            # E13.0  [1] (🧑‍🍼)
+1F9D1 200D 1F384                            ; RGI_Emoji_ZWJ_Sequence  ; mx 
claus                                                       # E13.0  [1] (🧑‍🎄)
 1F9D1 200D 1F393                            ; RGI_Emoji_ZWJ_Sequence  ; 
student                                                        # E12.1  [1] 
(🧑‍🎓)
 1F9D1 200D 1F3A4                            ; RGI_Emoji_ZWJ_Sequence  ; singer 
                                                        # E12.1  [1] (🧑‍🎤)
 1F9D1 200D 1F3A8                            ; RGI_Emoji_ZWJ_Sequence  ; artist 
                                                        # E12.1  [1] (🧑‍🎨)
@@ -624,14 +678,18 @@
 1F9D1 200D 1F680                            ; RGI_Emoji_ZWJ_Sequence  ; 
astronaut                                                      # E12.1  [1] 
(🧑‍🚀)
 1F9D1 200D 1F692                            ; RGI_Emoji_ZWJ_Sequence  ; 
firefighter                                                    # E12.1  [1] 
(🧑‍🚒)
 1F9D1 200D 1F9AF                            ; RGI_Emoji_ZWJ_Sequence  ; person 
with white cane                                         # E12.1  [1] (🧑‍🦯)
+1F9D1 200D 1F9AF 200D 27A1 FE0F             ; RGI_Emoji_ZWJ_Sequence  ; person 
with white cane facing right                            # E15.1  [1] (🧑‍🦯‍➡️)
 1F9D1 200D 1F9BC                            ; RGI_Emoji_ZWJ_Sequence  ; person 
in motorized wheelchair                                 # E12.1  [1] (🧑‍🦼)
+1F9D1 200D 1F9BC 200D 27A1 FE0F             ; RGI_Emoji_ZWJ_Sequence  ; person 
in motorized wheelchair facing right                    # E15.1  [1] (🧑‍🦼‍➡️)
 1F9D1 200D 1F9BD                            ; RGI_Emoji_ZWJ_Sequence  ; person 
in manual wheelchair                                    # E12.1  [1] (🧑‍🦽)
+1F9D1 200D 1F9BD 200D 27A1 FE0F             ; RGI_Emoji_ZWJ_Sequence  ; person 
in manual wheelchair facing right                       # E15.1  [1] (🧑‍🦽‍➡️)
 1F9D1 1F3FB 200D 2695 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; health 
worker: light skin tone                                 # E12.1  [1] (🧑🏻‍⚕️)
 1F9D1 1F3FB 200D 2696 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; judge: 
light skin tone                                         # E12.1  [1] (🧑🏻‍⚖️)
 1F9D1 1F3FB 200D 2708 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; pilot: 
light skin tone                                         # E12.1  [1] (🧑🏻‍✈️)
 1F9D1 1F3FB 200D 1F33E                      ; RGI_Emoji_ZWJ_Sequence  ; 
farmer: light skin tone                                        # E12.1  [1] 
(🧑🏻‍🌾)
 1F9D1 1F3FB 200D 1F373                      ; RGI_Emoji_ZWJ_Sequence  ; cook: 
light skin tone                                          # E12.1  [1] (🧑🏻‍🍳)
 1F9D1 1F3FB 200D 1F37C                      ; RGI_Emoji_ZWJ_Sequence  ; person 
feeding baby: light skin tone                           # E13.0  [1] (🧑🏻‍🍼)
+1F9D1 1F3FB 200D 1F384                      ; RGI_Emoji_ZWJ_Sequence  ; mx 
claus: light skin tone                                      # E13.0  [1] (🧑🏻‍🎄)
 1F9D1 1F3FB 200D 1F393                      ; RGI_Emoji_ZWJ_Sequence  ; 
student: light skin tone                                       # E12.1  [1] 
(🧑🏻‍🎓)
 1F9D1 1F3FB 200D 1F3A4                      ; RGI_Emoji_ZWJ_Sequence  ; 
singer: light skin tone                                        # E12.1  [1] 
(🧑🏻‍🎤)
 1F9D1 1F3FB 200D 1F3A8                      ; RGI_Emoji_ZWJ_Sequence  ; 
artist: light skin tone                                        # E12.1  [1] 
(🧑🏻‍🎨)
@@ -644,14 +702,18 @@
 1F9D1 1F3FB 200D 1F680                      ; RGI_Emoji_ZWJ_Sequence  ; 
astronaut: light skin tone                                     # E12.1  [1] 
(🧑🏻‍🚀)
 1F9D1 1F3FB 200D 1F692                      ; RGI_Emoji_ZWJ_Sequence  ; 
firefighter: light skin tone                                   # E12.1  [1] 
(🧑🏻‍🚒)
 1F9D1 1F3FB 200D 1F9AF                      ; RGI_Emoji_ZWJ_Sequence  ; person 
with white cane: light skin tone                        # E12.1  [1] (🧑🏻‍🦯)
+1F9D1 1F3FB 200D 1F9AF 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; person 
with white cane facing right: light skin tone           # E15.1  [1] (🧑🏻‍🦯‍➡️)
 1F9D1 1F3FB 200D 1F9BC                      ; RGI_Emoji_ZWJ_Sequence  ; person 
in motorized wheelchair: light skin tone                # E12.1  [1] (🧑🏻‍🦼)
+1F9D1 1F3FB 200D 1F9BC 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; person 
in motorized wheelchair facing right: light skin tone   # E15.1  [1] (🧑🏻‍🦼‍➡️)
 1F9D1 1F3FB 200D 1F9BD                      ; RGI_Emoji_ZWJ_Sequence  ; person 
in manual wheelchair: light skin tone                   # E12.1  [1] (🧑🏻‍🦽)
+1F9D1 1F3FB 200D 1F9BD 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; person 
in manual wheelchair facing right: light skin tone      # E15.1  [1] (🧑🏻‍🦽‍➡️)
 1F9D1 1F3FC 200D 2695 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; health 
worker: medium-light skin tone                          # E12.1  [1] (🧑🏼‍⚕️)
 1F9D1 1F3FC 200D 2696 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; judge: 
medium-light skin tone                                  # E12.1  [1] (🧑🏼‍⚖️)
 1F9D1 1F3FC 200D 2708 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; pilot: 
medium-light skin tone                                  # E12.1  [1] (🧑🏼‍✈️)
 1F9D1 1F3FC 200D 1F33E                      ; RGI_Emoji_ZWJ_Sequence  ; 
farmer: medium-light skin tone                                 # E12.1  [1] 
(🧑🏼‍🌾)
 1F9D1 1F3FC 200D 1F373                      ; RGI_Emoji_ZWJ_Sequence  ; cook: 
medium-light skin tone                                   # E12.1  [1] (🧑🏼‍🍳)
 1F9D1 1F3FC 200D 1F37C                      ; RGI_Emoji_ZWJ_Sequence  ; person 
feeding baby: medium-light skin tone                    # E13.0  [1] (🧑🏼‍🍼)
+1F9D1 1F3FC 200D 1F384                      ; RGI_Emoji_ZWJ_Sequence  ; mx 
claus: medium-light skin tone                               # E13.0  [1] (🧑🏼‍🎄)
 1F9D1 1F3FC 200D 1F393                      ; RGI_Emoji_ZWJ_Sequence  ; 
student: medium-light skin tone                                # E12.1  [1] 
(🧑🏼‍🎓)
 1F9D1 1F3FC 200D 1F3A4                      ; RGI_Emoji_ZWJ_Sequence  ; 
singer: medium-light skin tone                                 # E12.1  [1] 
(🧑🏼‍🎤)
 1F9D1 1F3FC 200D 1F3A8                      ; RGI_Emoji_ZWJ_Sequence  ; 
artist: medium-light skin tone                                 # E12.1  [1] 
(🧑🏼‍🎨)
@@ -664,14 +726,18 @@
 1F9D1 1F3FC 200D 1F680                      ; RGI_Emoji_ZWJ_Sequence  ; 
astronaut: medium-light skin tone                              # E12.1  [1] 
(🧑🏼‍🚀)
 1F9D1 1F3FC 200D 1F692                      ; RGI_Emoji_ZWJ_Sequence  ; 
firefighter: medium-light skin tone                            # E12.1  [1] 
(🧑🏼‍🚒)
 1F9D1 1F3FC 200D 1F9AF                      ; RGI_Emoji_ZWJ_Sequence  ; person 
with white cane: medium-light skin tone                 # E12.1  [1] (🧑🏼‍🦯)
+1F9D1 1F3FC 200D 1F9AF 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; person 
with white cane facing right: medium-light skin tone    # E15.1  [1] (🧑🏼‍🦯‍➡️)
 1F9D1 1F3FC 200D 1F9BC                      ; RGI_Emoji_ZWJ_Sequence  ; person 
in motorized wheelchair: medium-light skin tone         # E12.1  [1] (🧑🏼‍🦼)
+1F9D1 1F3FC 200D 1F9BC 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; person 
in motorized wheelchair facing right: medium-light skin tone #E15.1[1] (🧑🏼‍🦼‍➡️)
 1F9D1 1F3FC 200D 1F9BD                      ; RGI_Emoji_ZWJ_Sequence  ; person 
in manual wheelchair: medium-light skin tone            # E12.1  [1] (🧑🏼‍🦽)
+1F9D1 1F3FC 200D 1F9BD 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; person 
in manual wheelchair facing right: medium-light skin tone #E15.1 [1] (🧑🏼‍🦽‍➡️)
 1F9D1 1F3FD 200D 2695 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; health 
worker: medium skin tone                                # E12.1  [1] (🧑🏽‍⚕️)
 1F9D1 1F3FD 200D 2696 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; judge: 
medium skin tone                                        # E12.1  [1] (🧑🏽‍⚖️)
 1F9D1 1F3FD 200D 2708 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; pilot: 
medium skin tone                                        # E12.1  [1] (🧑🏽‍✈️)
 1F9D1 1F3FD 200D 1F33E                      ; RGI_Emoji_ZWJ_Sequence  ; 
farmer: medium skin tone                                       # E12.1  [1] 
(🧑🏽‍🌾)
 1F9D1 1F3FD 200D 1F373                      ; RGI_Emoji_ZWJ_Sequence  ; cook: 
medium skin tone                                         # E12.1  [1] (🧑🏽‍🍳)
 1F9D1 1F3FD 200D 1F37C                      ; RGI_Emoji_ZWJ_Sequence  ; person 
feeding baby: medium skin tone                          # E13.0  [1] (🧑🏽‍🍼)
+1F9D1 1F3FD 200D 1F384                      ; RGI_Emoji_ZWJ_Sequence  ; mx 
claus: medium skin tone                                     # E13.0  [1] (🧑🏽‍🎄)
 1F9D1 1F3FD 200D 1F393                      ; RGI_Emoji_ZWJ_Sequence  ; 
student: medium skin tone                                      # E12.1  [1] 
(🧑🏽‍🎓)
 1F9D1 1F3FD 200D 1F3A4                      ; RGI_Emoji_ZWJ_Sequence  ; 
singer: medium skin tone                                       # E12.1  [1] 
(🧑🏽‍🎤)
 1F9D1 1F3FD 200D 1F3A8                      ; RGI_Emoji_ZWJ_Sequence  ; 
artist: medium skin tone                                       # E12.1  [1] 
(🧑🏽‍🎨)
@@ -684,14 +750,18 @@
 1F9D1 1F3FD 200D 1F680                      ; RGI_Emoji_ZWJ_Sequence  ; 
astronaut: medium skin tone                                    # E12.1  [1] 
(🧑🏽‍🚀)
 1F9D1 1F3FD 200D 1F692                      ; RGI_Emoji_ZWJ_Sequence  ; 
firefighter: medium skin tone                                  # E12.1  [1] 
(🧑🏽‍🚒)
 1F9D1 1F3FD 200D 1F9AF                      ; RGI_Emoji_ZWJ_Sequence  ; person 
with white cane: medium skin tone                       # E12.1  [1] (🧑🏽‍🦯)
+1F9D1 1F3FD 200D 1F9AF 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; person 
with white cane facing right: medium skin tone          # E15.1  [1] (🧑🏽‍🦯‍➡️)
 1F9D1 1F3FD 200D 1F9BC                      ; RGI_Emoji_ZWJ_Sequence  ; person 
in motorized wheelchair: medium skin tone               # E12.1  [1] (🧑🏽‍🦼)
+1F9D1 1F3FD 200D 1F9BC 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; person 
in motorized wheelchair facing right: medium skin tone  # E15.1  [1] (🧑🏽‍🦼‍➡️)
 1F9D1 1F3FD 200D 1F9BD                      ; RGI_Emoji_ZWJ_Sequence  ; person 
in manual wheelchair: medium skin tone                  # E12.1  [1] (🧑🏽‍🦽)
+1F9D1 1F3FD 200D 1F9BD 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; person 
in manual wheelchair facing right: medium skin tone     # E15.1  [1] (🧑🏽‍🦽‍➡️)
 1F9D1 1F3FE 200D 2695 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; health 
worker: medium-dark skin tone                           # E12.1  [1] (🧑🏾‍⚕️)
 1F9D1 1F3FE 200D 2696 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; judge: 
medium-dark skin tone                                   # E12.1  [1] (🧑🏾‍⚖️)
 1F9D1 1F3FE 200D 2708 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; pilot: 
medium-dark skin tone                                   # E12.1  [1] (🧑🏾‍✈️)
 1F9D1 1F3FE 200D 1F33E                      ; RGI_Emoji_ZWJ_Sequence  ; 
farmer: medium-dark skin tone                                  # E12.1  [1] 
(🧑🏾‍🌾)
 1F9D1 1F3FE 200D 1F373                      ; RGI_Emoji_ZWJ_Sequence  ; cook: 
medium-dark skin tone                                    # E12.1  [1] (🧑🏾‍🍳)
 1F9D1 1F3FE 200D 1F37C                      ; RGI_Emoji_ZWJ_Sequence  ; person 
feeding baby: medium-dark skin tone                     # E13.0  [1] (🧑🏾‍🍼)
+1F9D1 1F3FE 200D 1F384                      ; RGI_Emoji_ZWJ_Sequence  ; mx 
claus: medium-dark skin tone                                # E13.0  [1] (🧑🏾‍🎄)
 1F9D1 1F3FE 200D 1F393                      ; RGI_Emoji_ZWJ_Sequence  ; 
student: medium-dark skin tone                                 # E12.1  [1] 
(🧑🏾‍🎓)
 1F9D1 1F3FE 200D 1F3A4                      ; RGI_Emoji_ZWJ_Sequence  ; 
singer: medium-dark skin tone                                  # E12.1  [1] 
(🧑🏾‍🎤)
 1F9D1 1F3FE 200D 1F3A8                      ; RGI_Emoji_ZWJ_Sequence  ; 
artist: medium-dark skin tone                                  # E12.1  [1] 
(🧑🏾‍🎨)
@@ -704,14 +774,18 @@
 1F9D1 1F3FE 200D 1F680                      ; RGI_Emoji_ZWJ_Sequence  ; 
astronaut: medium-dark skin tone                               # E12.1  [1] 
(🧑🏾‍🚀)
 1F9D1 1F3FE 200D 1F692                      ; RGI_Emoji_ZWJ_Sequence  ; 
firefighter: medium-dark skin tone                             # E12.1  [1] 
(🧑🏾‍🚒)
 1F9D1 1F3FE 200D 1F9AF                      ; RGI_Emoji_ZWJ_Sequence  ; person 
with white cane: medium-dark skin tone                  # E12.1  [1] (🧑🏾‍🦯)
+1F9D1 1F3FE 200D 1F9AF 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; person 
with white cane facing right: medium-dark skin tone     # E15.1  [1] (🧑🏾‍🦯‍➡️)
 1F9D1 1F3FE 200D 1F9BC                      ; RGI_Emoji_ZWJ_Sequence  ; person 
in motorized wheelchair: medium-dark skin tone          # E12.1  [1] (🧑🏾‍🦼)
+1F9D1 1F3FE 200D 1F9BC 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; person 
in motorized wheelchair facing right: medium-dark skin tone #E15.1[1] (🧑🏾‍🦼‍➡️)
 1F9D1 1F3FE 200D 1F9BD                      ; RGI_Emoji_ZWJ_Sequence  ; person 
in manual wheelchair: medium-dark skin tone             # E12.1  [1] (🧑🏾‍🦽)
+1F9D1 1F3FE 200D 1F9BD 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; person 
in manual wheelchair facing right: medium-dark skin tone #E15.1  [1] (🧑🏾‍🦽‍➡️)
 1F9D1 1F3FF 200D 2695 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; health 
worker: dark skin tone                                  # E12.1  [1] (🧑🏿‍⚕️)
 1F9D1 1F3FF 200D 2696 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; judge: 
dark skin tone                                          # E12.1  [1] (🧑🏿‍⚖️)
 1F9D1 1F3FF 200D 2708 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; pilot: 
dark skin tone                                          # E12.1  [1] (🧑🏿‍✈️)
 1F9D1 1F3FF 200D 1F33E                      ; RGI_Emoji_ZWJ_Sequence  ; 
farmer: dark skin tone                                         # E12.1  [1] 
(🧑🏿‍🌾)
 1F9D1 1F3FF 200D 1F373                      ; RGI_Emoji_ZWJ_Sequence  ; cook: 
dark skin tone                                           # E12.1  [1] (🧑🏿‍🍳)
 1F9D1 1F3FF 200D 1F37C                      ; RGI_Emoji_ZWJ_Sequence  ; person 
feeding baby: dark skin tone                            # E13.0  [1] (🧑🏿‍🍼)
+1F9D1 1F3FF 200D 1F384                      ; RGI_Emoji_ZWJ_Sequence  ; mx 
claus: dark skin tone                                       # E13.0  [1] (🧑🏿‍🎄)
 1F9D1 1F3FF 200D 1F393                      ; RGI_Emoji_ZWJ_Sequence  ; 
student: dark skin tone                                        # E12.1  [1] 
(🧑🏿‍🎓)
 1F9D1 1F3FF 200D 1F3A4                      ; RGI_Emoji_ZWJ_Sequence  ; 
singer: dark skin tone                                         # E12.1  [1] 
(🧑🏿‍🎤)
 1F9D1 1F3FF 200D 1F3A8                      ; RGI_Emoji_ZWJ_Sequence  ; 
artist: dark skin tone                                         # E12.1  [1] 
(🧑🏿‍🎨)
@@ -724,10 +798,13 @@
 1F9D1 1F3FF 200D 1F680                      ; RGI_Emoji_ZWJ_Sequence  ; 
astronaut: dark skin tone                                      # E12.1  [1] 
(🧑🏿‍🚀)
 1F9D1 1F3FF 200D 1F692                      ; RGI_Emoji_ZWJ_Sequence  ; 
firefighter: dark skin tone                                    # E12.1  [1] 
(🧑🏿‍🚒)
 1F9D1 1F3FF 200D 1F9AF                      ; RGI_Emoji_ZWJ_Sequence  ; person 
with white cane: dark skin tone                         # E12.1  [1] (🧑🏿‍🦯)
+1F9D1 1F3FF 200D 1F9AF 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; person 
with white cane facing right: dark skin tone            # E15.1  [1] (🧑🏿‍🦯‍➡️)
 1F9D1 1F3FF 200D 1F9BC                      ; RGI_Emoji_ZWJ_Sequence  ; person 
in motorized wheelchair: dark skin tone                 # E12.1  [1] (🧑🏿‍🦼)
+1F9D1 1F3FF 200D 1F9BC 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; person 
in motorized wheelchair facing right: dark skin tone    # E15.1  [1] (🧑🏿‍🦼‍➡️)
 1F9D1 1F3FF 200D 1F9BD                      ; RGI_Emoji_ZWJ_Sequence  ; person 
in manual wheelchair: dark skin tone                    # E12.1  [1] (🧑🏿‍🦽)
+1F9D1 1F3FF 200D 1F9BD 200D 27A1 FE0F       ; RGI_Emoji_ZWJ_Sequence  ; person 
in manual wheelchair facing right: dark skin tone       # E15.1  [1] (🧑🏿‍🦽‍➡️)
 
-# Total elements: 360
+# Total elements: 438
 
 # ================================================
 
@@ -746,17 +823,29 @@
 26F9 FE0F 200D 2640 FE0F                    ; RGI_Emoji_ZWJ_Sequence  ; woman 
bouncing ball                                            # E4.0   [1] (⛹️‍♀️)
 26F9 FE0F 200D 2642 FE0F                    ; RGI_Emoji_ZWJ_Sequence  ; man 
bouncing ball                                              # E4.0   [1] (⛹️‍♂️)
 1F3C3 200D 2640 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; woman 
running                                                  # E4.0   [1] (🏃‍♀️)
+1F3C3 200D 2640 FE0F 200D 27A1 FE0F         ; RGI_Emoji_ZWJ_Sequence  ; woman 
running facing right                                     # E15.1  [1] (🏃‍♀️‍➡️)
 1F3C3 200D 2642 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; man 
running                                                    # E4.0   [1] (🏃‍♂️)
+1F3C3 200D 2642 FE0F 200D 27A1 FE0F         ; RGI_Emoji_ZWJ_Sequence  ; man 
running facing right                                       # E15.1  [1] 
(🏃‍♂️‍➡️)
 1F3C3 1F3FB 200D 2640 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
running: light skin tone                                 # E4.0   [1] (🏃🏻‍♀️)
+1F3C3 1F3FB 200D 2640 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; woman 
running facing right: light skin tone                    # E15.1  [1] (🏃🏻‍♀️‍➡️)
 1F3C3 1F3FB 200D 2642 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
running: light skin tone                                   # E4.0   [1] (🏃🏻‍♂️)
+1F3C3 1F3FB 200D 2642 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; man 
running facing right: light skin tone                      # E15.1  [1] 
(🏃🏻‍♂️‍➡️)
 1F3C3 1F3FC 200D 2640 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
running: medium-light skin tone                          # E4.0   [1] (🏃🏼‍♀️)
+1F3C3 1F3FC 200D 2640 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; woman 
running facing right: medium-light skin tone             # E15.1  [1] (🏃🏼‍♀️‍➡️)
 1F3C3 1F3FC 200D 2642 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
running: medium-light skin tone                            # E4.0   [1] (🏃🏼‍♂️)
+1F3C3 1F3FC 200D 2642 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; man 
running facing right: medium-light skin tone               # E15.1  [1] 
(🏃🏼‍♂️‍➡️)
 1F3C3 1F3FD 200D 2640 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
running: medium skin tone                                # E4.0   [1] (🏃🏽‍♀️)
+1F3C3 1F3FD 200D 2640 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; woman 
running facing right: medium skin tone                   # E15.1  [1] (🏃🏽‍♀️‍➡️)
 1F3C3 1F3FD 200D 2642 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
running: medium skin tone                                  # E4.0   [1] (🏃🏽‍♂️)
+1F3C3 1F3FD 200D 2642 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; man 
running facing right: medium skin tone                     # E15.1  [1] 
(🏃🏽‍♂️‍➡️)
 1F3C3 1F3FE 200D 2640 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
running: medium-dark skin tone                           # E4.0   [1] (🏃🏾‍♀️)
+1F3C3 1F3FE 200D 2640 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; woman 
running facing right: medium-dark skin tone              # E15.1  [1] (🏃🏾‍♀️‍➡️)
 1F3C3 1F3FE 200D 2642 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
running: medium-dark skin tone                             # E4.0   [1] (🏃🏾‍♂️)
+1F3C3 1F3FE 200D 2642 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; man 
running facing right: medium-dark skin tone                # E15.1  [1] 
(🏃🏾‍♂️‍➡️)
 1F3C3 1F3FF 200D 2640 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
running: dark skin tone                                  # E4.0   [1] (🏃🏿‍♀️)
+1F3C3 1F3FF 200D 2640 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; woman 
running facing right: dark skin tone                     # E15.1  [1] (🏃🏿‍♀️‍➡️)
 1F3C3 1F3FF 200D 2642 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
running: dark skin tone                                    # E4.0   [1] (🏃🏿‍♂️)
+1F3C3 1F3FF 200D 2642 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; man 
running facing right: dark skin tone                       # E15.1  [1] 
(🏃🏿‍♂️‍➡️)
 1F3C4 200D 2640 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; woman 
surfing                                                  # E4.0   [1] (🏄‍♀️)
 1F3C4 200D 2642 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; man 
surfing                                                    # E4.0   [1] (🏄‍♂️)
 1F3C4 1F3FB 200D 2640 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
surfing: light skin tone                                 # E4.0   [1] (🏄🏻‍♀️)
@@ -1036,17 +1125,29 @@
 1F6B5 1F3FF 200D 2640 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
mountain biking: dark skin tone                          # E4.0   [1] (🚵🏿‍♀️)
 1F6B5 1F3FF 200D 2642 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
mountain biking: dark skin tone                            # E4.0   [1] (🚵🏿‍♂️)
 1F6B6 200D 2640 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; woman 
walking                                                  # E4.0   [1] (🚶‍♀️)
+1F6B6 200D 2640 FE0F 200D 27A1 FE0F         ; RGI_Emoji_ZWJ_Sequence  ; woman 
walking facing right                                     # E15.1  [1] (🚶‍♀️‍➡️)
 1F6B6 200D 2642 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; man 
walking                                                    # E4.0   [1] (🚶‍♂️)
+1F6B6 200D 2642 FE0F 200D 27A1 FE0F         ; RGI_Emoji_ZWJ_Sequence  ; man 
walking facing right                                       # E15.1  [1] 
(🚶‍♂️‍➡️)
 1F6B6 1F3FB 200D 2640 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
walking: light skin tone                                 # E4.0   [1] (🚶🏻‍♀️)
+1F6B6 1F3FB 200D 2640 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; woman 
walking facing right: light skin tone                    # E15.1  [1] (🚶🏻‍♀️‍➡️)
 1F6B6 1F3FB 200D 2642 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
walking: light skin tone                                   # E4.0   [1] (🚶🏻‍♂️)
+1F6B6 1F3FB 200D 2642 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; man 
walking facing right: light skin tone                      # E15.1  [1] 
(🚶🏻‍♂️‍➡️)
 1F6B6 1F3FC 200D 2640 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
walking: medium-light skin tone                          # E4.0   [1] (🚶🏼‍♀️)
+1F6B6 1F3FC 200D 2640 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; woman 
walking facing right: medium-light skin tone             # E15.1  [1] (🚶🏼‍♀️‍➡️)
 1F6B6 1F3FC 200D 2642 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
walking: medium-light skin tone                            # E4.0   [1] (🚶🏼‍♂️)
+1F6B6 1F3FC 200D 2642 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; man 
walking facing right: medium-light skin tone               # E15.1  [1] 
(🚶🏼‍♂️‍➡️)
 1F6B6 1F3FD 200D 2640 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
walking: medium skin tone                                # E4.0   [1] (🚶🏽‍♀️)
+1F6B6 1F3FD 200D 2640 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; woman 
walking facing right: medium skin tone                   # E15.1  [1] (🚶🏽‍♀️‍➡️)
 1F6B6 1F3FD 200D 2642 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
walking: medium skin tone                                  # E4.0   [1] (🚶🏽‍♂️)
+1F6B6 1F3FD 200D 2642 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; man 
walking facing right: medium skin tone                     # E15.1  [1] 
(🚶🏽‍♂️‍➡️)
 1F6B6 1F3FE 200D 2640 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
walking: medium-dark skin tone                           # E4.0   [1] (🚶🏾‍♀️)
+1F6B6 1F3FE 200D 2640 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; woman 
walking facing right: medium-dark skin tone              # E15.1  [1] (🚶🏾‍♀️‍➡️)
 1F6B6 1F3FE 200D 2642 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
walking: medium-dark skin tone                             # E4.0   [1] (🚶🏾‍♂️)
+1F6B6 1F3FE 200D 2642 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; man 
walking facing right: medium-dark skin tone                # E15.1  [1] 
(🚶🏾‍♂️‍➡️)
 1F6B6 1F3FF 200D 2640 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
walking: dark skin tone                                  # E4.0   [1] (🚶🏿‍♀️)
+1F6B6 1F3FF 200D 2640 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; woman 
walking facing right: dark skin tone                     # E15.1  [1] (🚶🏿‍♀️‍➡️)
 1F6B6 1F3FF 200D 2642 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
walking: dark skin tone                                    # E4.0   [1] (🚶🏿‍♂️)
+1F6B6 1F3FF 200D 2642 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; man 
walking facing right: dark skin tone                       # E15.1  [1] 
(🚶🏿‍♂️‍➡️)
 1F926 200D 2640 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; woman 
facepalming                                              # E4.0   [1] (🤦‍♀️)
 1F926 200D 2642 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; man 
facepalming                                                # E4.0   [1] (🤦‍♂️)
 1F926 1F3FB 200D 2640 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
facepalming: light skin tone                             # E4.0   [1] (🤦🏻‍♀️)
@@ -1170,17 +1271,29 @@
 1F9CD 1F3FF 200D 2640 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
standing: dark skin tone                                 # E12.0  [1] (🧍🏿‍♀️)
 1F9CD 1F3FF 200D 2642 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
standing: dark skin tone                                   # E12.0  [1] (🧍🏿‍♂️)
 1F9CE 200D 2640 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; woman 
kneeling                                                 # E12.0  [1] (🧎‍♀️)
+1F9CE 200D 2640 FE0F 200D 27A1 FE0F         ; RGI_Emoji_ZWJ_Sequence  ; woman 
kneeling facing right                                    # E15.1  [1] (🧎‍♀️‍➡️)
 1F9CE 200D 2642 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; man 
kneeling                                                   # E12.0  [1] (🧎‍♂️)
+1F9CE 200D 2642 FE0F 200D 27A1 FE0F         ; RGI_Emoji_ZWJ_Sequence  ; man 
kneeling facing right                                      # E15.1  [1] 
(🧎‍♂️‍➡️)
 1F9CE 1F3FB 200D 2640 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
kneeling: light skin tone                                # E12.0  [1] (🧎🏻‍♀️)
+1F9CE 1F3FB 200D 2640 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; woman 
kneeling facing right: light skin tone                   # E15.1  [1] (🧎🏻‍♀️‍➡️)
 1F9CE 1F3FB 200D 2642 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
kneeling: light skin tone                                  # E12.0  [1] (🧎🏻‍♂️)
+1F9CE 1F3FB 200D 2642 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; man 
kneeling facing right: light skin tone                     # E15.1  [1] 
(🧎🏻‍♂️‍➡️)
 1F9CE 1F3FC 200D 2640 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
kneeling: medium-light skin tone                         # E12.0  [1] (🧎🏼‍♀️)
+1F9CE 1F3FC 200D 2640 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; woman 
kneeling facing right: medium-light skin tone            # E15.1  [1] (🧎🏼‍♀️‍➡️)
 1F9CE 1F3FC 200D 2642 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
kneeling: medium-light skin tone                           # E12.0  [1] (🧎🏼‍♂️)
+1F9CE 1F3FC 200D 2642 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; man 
kneeling facing right: medium-light skin tone              # E15.1  [1] 
(🧎🏼‍♂️‍➡️)
 1F9CE 1F3FD 200D 2640 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
kneeling: medium skin tone                               # E12.0  [1] (🧎🏽‍♀️)
+1F9CE 1F3FD 200D 2640 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; woman 
kneeling facing right: medium skin tone                  # E15.1  [1] (🧎🏽‍♀️‍➡️)
 1F9CE 1F3FD 200D 2642 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
kneeling: medium skin tone                                 # E12.0  [1] (🧎🏽‍♂️)
+1F9CE 1F3FD 200D 2642 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; man 
kneeling facing right: medium skin tone                    # E15.1  [1] 
(🧎🏽‍♂️‍➡️)
 1F9CE 1F3FE 200D 2640 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
kneeling: medium-dark skin tone                          # E12.0  [1] (🧎🏾‍♀️)
+1F9CE 1F3FE 200D 2640 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; woman 
kneeling facing right: medium-dark skin tone             # E15.1  [1] (🧎🏾‍♀️‍➡️)
 1F9CE 1F3FE 200D 2642 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
kneeling: medium-dark skin tone                            # E12.0  [1] (🧎🏾‍♂️)
+1F9CE 1F3FE 200D 2642 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; man 
kneeling facing right: medium-dark skin tone               # E15.1  [1] 
(🧎🏾‍♂️‍➡️)
 1F9CE 1F3FF 200D 2640 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; woman 
kneeling: dark skin tone                                 # E12.0  [1] (🧎🏿‍♀️)
+1F9CE 1F3FF 200D 2640 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; woman 
kneeling facing right: dark skin tone                    # E15.1  [1] (🧎🏿‍♀️‍➡️)
 1F9CE 1F3FF 200D 2642 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; man 
kneeling: dark skin tone                                   # E12.0  [1] (🧎🏿‍♂️)
+1F9CE 1F3FF 200D 2642 FE0F 200D 27A1 FE0F   ; RGI_Emoji_ZWJ_Sequence  ; man 
kneeling facing right: dark skin tone                      # E15.1  [1] 
(🧎🏿‍♂️‍➡️)
 1F9CF 200D 2640 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; deaf 
woman                                                     # E12.0  [1] (🧏‍♀️)
 1F9CF 200D 2642 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; deaf 
man                                                       # E12.0  [1] (🧏‍♂️)
 1F9CF 1F3FB 200D 2640 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; deaf 
woman: light skin tone                                    # E12.0  [1] (🧏🏻‍♀️)
@@ -1306,7 +1419,7 @@
 1F9DF 200D 2640 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; woman 
zombie                                                   # E5.0   [1] (🧟‍♀️)
 1F9DF 200D 2642 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; man 
zombie                                                     # E5.0   [1] (🧟‍♂️)
 
-# Total elements: 572
+# Total elements: 608
 
 # ================================================
 
@@ -1391,21 +1504,26 @@
 
 # RGI_Emoji_ZWJ_Sequence: Other
 
+26D3 FE0F 200D 1F4A5                        ; RGI_Emoji_ZWJ_Sequence  ; broken 
chain                                                   # E15.1  [1] (⛓️‍💥)
 2764 FE0F 200D 1F525                        ; RGI_Emoji_ZWJ_Sequence  ; heart 
on fire                                                  # E13.1  [1] (❤️‍🔥)
 2764 FE0F 200D 1FA79                        ; RGI_Emoji_ZWJ_Sequence  ; 
mending heart                                                  # E13.1  [1] 
(❤️‍🩹)
+1F344 200D 1F7EB                            ; RGI_Emoji_ZWJ_Sequence  ; brown 
mushroom                                                 # E15.1  [1] (🍄‍🟫)
+1F34B 200D 1F7E9                            ; RGI_Emoji_ZWJ_Sequence  ; lime   
                                                        # E15.1  [1] (🍋‍🟩)
 1F3F3 FE0F 200D 26A7 FE0F                   ; RGI_Emoji_ZWJ_Sequence  ; 
transgender flag                                               # E13.0  [1] 
(🏳️‍⚧️)
 1F3F3 FE0F 200D 1F308                       ; RGI_Emoji_ZWJ_Sequence  ; 
rainbow flag                                                   # E4.0   [1] 
(🏳️‍🌈)
 1F3F4 200D 2620 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; pirate 
flag                                                    # E11.0  [1] (🏴‍☠️)
 1F408 200D 2B1B                             ; RGI_Emoji_ZWJ_Sequence  ; black 
cat                                                      # E13.0  [1] (🐈‍⬛)
 1F415 200D 1F9BA                            ; RGI_Emoji_ZWJ_Sequence  ; 
service dog                                                    # E12.0  [1] 
(🐕‍🦺)
 1F426 200D 2B1B                             ; RGI_Emoji_ZWJ_Sequence  ; black 
bird                                                     # E15.0  [1] (🐦‍⬛)
+1F426 200D 1F525                            ; RGI_Emoji_ZWJ_Sequence  ; 
phoenix                                                        # E15.1  [1] 
(🐦‍🔥)
 1F43B 200D 2744 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; polar 
bear                                                     # E13.0  [1] (🐻‍❄️)
 1F441 FE0F 200D 1F5E8 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; eye in 
speech bubble                                           # E2.0   [1] (👁️‍🗨️)
 1F62E 200D 1F4A8                            ; RGI_Emoji_ZWJ_Sequence  ; face 
exhaling                                                  # E13.1  [1] (😮‍💨)
 1F635 200D 1F4AB                            ; RGI_Emoji_ZWJ_Sequence  ; face 
with spiral eyes                                          # E13.1  [1] (😵‍💫)
 1F636 200D 1F32B FE0F                       ; RGI_Emoji_ZWJ_Sequence  ; face 
in clouds                                                 # E13.1  [1] (😶‍🌫️)
-1F9D1 200D 1F384                            ; RGI_Emoji_ZWJ_Sequence  ; mx 
claus                                                       # E13.0  [1] (🧑‍🎄)
+1F642 200D 2194 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; head 
shaking horizontally                                      # E15.1  [1] (🙂‍↔️)
+1F642 200D 2195 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; head 
shaking vertically                                        # E15.1  [1] (🙂‍↕️)
 
-# Total elements: 14
+# Total elements: 19
 
 #EOF
diff --git a/admin/unidata/emoji-zwj.awk b/admin/unidata/emoji-zwj.awk
index 53170214d8a..fc4a2edd559 100644
--- a/admin/unidata/emoji-zwj.awk
+++ b/admin/unidata/emoji-zwj.awk
@@ -100,14 +100,14 @@ END {
          vec[codepoint] = vec[codepoint] "\n\"\\N{U+" trigger_codepoints[trig] 
"}\\N{U+FE0F}\""
      }
 
-     print "(dolist (elt `("
+     print "(dolist (elt (eval-when-compile `("
 
      for (elt in ch)
     {
-        print "(#x" elt " .\n,(eval-when-compile (regexp-opt\n'(\n" vec[elt]
-        print "\"\\N{U+" elt "}\\N{U+FE0E}\"\n\"\\N{U+" elt 
"}\\N{U+FE0F}\"\n))))"
+        print "(#x" elt " .\n,(regexp-opt\n'(\n" vec[elt]
+        print "\"\\N{U+" elt "}\\N{U+FE0E}\"\n\"\\N{U+" elt 
"}\\N{U+FE0F}\"\n)))"
     }
-     print "))"
+     print ")))"
      print "  (set-char-table-range composition-function-table"
      print "                        (car elt)"
      print "                        (nconc (char-table-range 
composition-function-table (car elt))"
diff --git a/build-aux/gitlog-to-changelog b/build-aux/gitlog-to-changelog
index 43e4a37adf4..ceb1f0cf320 100755
--- a/build-aux/gitlog-to-changelog
+++ b/build-aux/gitlog-to-changelog
@@ -20,7 +20,7 @@
 #
 # Written by Jim Meyering
 
-# This is a prologue that allows to run a perl script as an executable
+# This is a prologue that allows running a perl script as an executable
 # on systems that are compliant to a POSIX version before POSIX:2017.
 # On such systems, the usual invocation of an executable through execlp()
 # or execvp() fails with ENOEXEC if it is a script that does not start
diff --git a/build-aux/update-copyright b/build-aux/update-copyright
index 0343eaa72c1..cdc3f3b5988 100755
--- a/build-aux/update-copyright
+++ b/build-aux/update-copyright
@@ -123,7 +123,7 @@
 #   5. Set UPDATE_COPYRIGHT_HOLDER if the copyright holder is other
 #      than "Free Software Foundation, Inc.".
 
-# This is a prologue that allows to run a perl script as an executable
+# This is a prologue that allows running a perl script as an executable
 # on systems that are compliant to a POSIX version before POSIX:2017.
 # On such systems, the usual invocation of an executable through execlp()
 # or execvp() fails with ENOEXEC if it is a script that does not start
diff --git a/configure.ac b/configure.ac
index f92339225b5..9ae0dec3867 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1664,13 +1664,13 @@ AC_ARG_ENABLE([gcc-warnings],
    # however, if there is also a .tarball-version file it is probably
    # just a release imported into Git for patch management.
    gl_gcc_warnings=no
-   if test -d "$srcdir"/.git && test ! -f "$srcdir"/.tarball-version; then
-      # Clang typically identifies itself as GCC 4.2 or something similar
-      # even if it is recent enough to accept the warnings we enable.
-      AS_IF([test "$emacs_cv_clang" = yes],
-         [gl_gcc_warnings=warn-only],
-         [gl_GCC_VERSION_IFELSE([5], [3], [gl_gcc_warnings=warn-only])])
-   fi])
+   AS_IF([test -d "$srcdir"/.git || test -f "$srcdir"/.git],
+     [AS_IF([test -f "$srcdir"/.tarball-version], [],
+       # Clang typically identifies itself as GCC 4.2 or something similar
+       # even if it is recent enough to accept the warnings we enable.
+       [AS_IF([test "$emacs_cv_clang" = yes],
+          [gl_gcc_warnings=warn-only],
+          [gl_GCC_VERSION_IFELSE([5], [3], [gl_gcc_warnings=warn-only])])])])])
 
 NATIVE_COMPILATION_AOT=no
 AC_ARG_WITH([native-compilation],
@@ -1713,7 +1713,6 @@ AS_IF([test $gl_gcc_warnings = no],
   AS_IF([test "$emacs_cv_clang" = yes],
    [
      # Turn off some warnings if supported.
-     gl_WARN_ADD([-Wno-switch])
      gl_WARN_ADD([-Wno-pointer-sign])
      gl_WARN_ADD([-Wno-string-plus-int])
      gl_WARN_ADD([-Wno-unknown-attributes])
@@ -5171,8 +5170,9 @@ if test "${with_native_compilation}" != "no"; then
     case "${opsys}" in
       # mingw32 loads the library dynamically.
       mingw32) ;;
-      # OpenBSD doesn't have libdl, all the functions are in libc
-      netbsd|openbsd)
+      # Neither NetBSD, OpenBSD nor Haiku have libdl, with all dynamic
+      # linker functions placed within libc.
+      netbsd|openbsd|haiku)
         LIBGCCJIT_LIBS="-lgccjit" ;;
       darwin)
         LIBGCCJIT_CFLAGS="${MAC_CFLAGS}"
@@ -6263,7 +6263,7 @@ AC_FUNC_FORK
 
 dnl AC_CHECK_FUNCS_ONCE wouldn’t be right for snprintf, which needs
 dnl the current CFLAGS etc.
-AC_CHECK_FUNCS([snprintf])
+AC_CHECK_FUNCS([snprintf open_memstream])
 
 dnl posix_spawn.  The chdir and setsid functionality is relatively
 dnl recent, so we check for it specifically.
@@ -6513,7 +6513,7 @@ case $opsys in
 esac
 
 case $opsys in
-  gnu-* | solaris | cygwin )
+  gnu-* | android | solaris | cygwin )
     dnl FIXME Can't we test if this exists (eg /proc/$$)?
     AC_DEFINE([HAVE_PROCFS], [1], [Define if you have the /proc filesystem.])
   ;;
diff --git a/doc/emacs/abbrevs.texi b/doc/emacs/abbrevs.texi
index 3678377e9b5..6c171d47427 100644
--- a/doc/emacs/abbrevs.texi
+++ b/doc/emacs/abbrevs.texi
@@ -275,6 +275,8 @@ Edit a list of abbrevs; you can add, alter or remove 
definitions.
 
 @example
 @var{various other tables@dots{}}
+(python-mode-skeleton-abbrev-table)
+"class" (sys)      0 "" python-skeleton-class
 (lisp-mode-abbrev-table)
 "ks"          0    "keymap-set"
 (global-abbrev-table)
@@ -297,11 +299,14 @@ keeps track of this to help you see which abbrevs you 
actually use, so
 that you can eliminate those that you don't use often.  The string at
 the end of the line is the expansion.
 
-  Some abbrevs are marked with @samp{(sys)}.  These @dfn{system abbrevs}
-(@pxref{Abbrevs,,, elisp, The Emacs Lisp Reference Manual}) are
-pre-defined by various modes, and are not saved to your abbrev file.
-To disable a system abbrev, define an abbrev of the same name that
-expands to itself, and save it to your abbrev file.
+  Some abbrevs are marked with @samp{(sys)}.  These @dfn{system
+abbrevs} (@pxref{Abbrevs,,, elisp, The Emacs Lisp Reference Manual})
+are pre-defined by various modes, and are not saved to your abbrev
+file.  To disable a system abbrev, define an abbrev of the same name
+that expands to itself, and save it to your abbrev file.  The system
+abbrevs have an associated hook function, which is called to perform
+the abbrev expansion; the name of that function follows the abbrev
+expansion in the buffer shown by @code{list-abbrevs}.
 
 @findex edit-abbrevs
 @kindex C-c C-c @r{(Edit Abbrevs)}
diff --git a/doc/emacs/ack.texi b/doc/emacs/ack.texi
index 4a8a2a24377..483ea3306a3 100644
--- a/doc/emacs/ack.texi
+++ b/doc/emacs/ack.texi
@@ -525,7 +525,7 @@ browser to display a URL.
 
 @item
 Lars Magne Ingebrigtsen was the Emacs (co-)maintainer from Emacs 27.2
-onwards.  He did a major redesign of the Gnus news-reader and wrote
+to 29.1.  He did a major redesign of the Gnus news-reader and wrote
 many of its parts.  Several of these are now general components of
 Emacs, including: @file{dns.el} for Domain Name Service lookups;
 @file{format-spec.el} for formatting arbitrary format strings;
@@ -590,6 +590,9 @@ control system.
 Tomoji Kagatani implemented @file{smtpmail.el}, used for sending out
 mail with SMTP.
 
+@item
+Stefan Kangas was the Emacs (co-)maintainer from 29.2 onwards.
+
 @item
 Ivan Kanis wrote @file{vc-hg.el}, support for the Mercurial version
 control system.
@@ -902,6 +905,11 @@ Takahashi Naoto co-wrote @file{quail.el} (q.v.), and wrote
 Thomas Neumann and Eric S. Raymond wrote @file{make-mode.el},
 a mode for editing makefiles.
 
+@item
+Thien-Thi Nguyen wrote the @samp{xpm}, @samp{gnugo}, and
+@samp{ascii-art-to-unicode} packages.  He also made substantial
+contributions to many others, such as @file{vc.el}.
+
 @item
 Thien-Thi Nguyen and Dan Nicolaescu wrote @file{hideshow.el}, a minor
 mode for selectively displaying blocks of text.
@@ -1379,9 +1387,9 @@ Rodney Whitby and Reto Zimmermann wrote 
@file{vhdl-mode.el}, a major
 mode for editing VHDL source code.
 
 @item
-John Wiegley was the Emacs maintainer from Emacs 25 onwards.  He wrote
-@file{align.el}, a set of commands for aligning text according to
-regular-expression based rules; @file{isearchb.el} for fast buffer
+John Wiegley was the Emacs (co-)maintainer from Emacs 25 to 29.1.  He
+wrote @file{align.el}, a set of commands for aligning text according
+to regular-expression based rules; @file{isearchb.el} for fast buffer
 switching; @file{timeclock.el}, a package for keeping track of time
 spent on projects; the Bahá'í calendar support; @file{pcomplete.el}, a
 programmable completion facility; @file{remember.el}, a mode for
diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi
index 5e7ff0e4bb3..9f3cca2b137 100644
--- a/doc/emacs/android.texi
+++ b/doc/emacs/android.texi
@@ -129,8 +129,7 @@ buffer.
   Since there is no other way to start the @command{emacsclient}
 program (@pxref{Emacs Server}) from another Android program, Emacs
 provides a wrapper around the @command{emacsclient} program, which is
-registered with the system as an application that can open all text
-files.
+registered with the system as an application that can open any file.
 
   When that wrapper is selected as the program with which to open a
 file, it invokes @command{emacsclient} with the options
@@ -154,6 +153,18 @@ directory, or try to open files in it yourself.
 case such files are copied to a temporary directory before being
 opened.
 
+@cindex ``org-protocol'' links, android
+  In addition to opening ordinary text files, Emacs also registers its
+@command{emacsclient} wrapper as a program capable of opening
+``org-protocol'' links (@pxref{Protocols,,,org, The Org Manual}).
+
+@cindex ``mailto'' links, android
+  Furthermore, the wrapper is also registered as a program capable of
+sending mail to @code{mailto} URIs; when it is invoked to open such a
+URL, it calls the function @code{message-mailto} with that URI as its
+first argument.  This feature does not function when the Emacs server
+is not already running.
+
 @node Android File System
 @section What Files Emacs Can Access on Android
 @cindex /assets directory, android
@@ -293,7 +304,7 @@ provide, placing their files within the directory
 @file{/content/storage}.
 
 @findex android-request-directory-access
-  Before Emacs is granted access to any of these directories, it must
+  Before Emacs is granted access to one of these directories, it must
 first request the right to access it.  This is done by running the
 command (@pxref{M-x}) @code{android-request-directory-access}, which
 displays a file selection dialog.
@@ -434,10 +445,36 @@ list of such troublesome manufacturers and sometimes 
workarounds at
 
   Android also defines a permissions system that determines what
 system services Emacs is allowed to access.  Programs must specify
-what permissions they want; what then happens depends on the version
-of Android being used:
+what permissions they want; what then happens is then subject to the
+version of Android being used:
 
 @itemize @bullet
+@item
+Under more or less recent releases of Android, such as Android 6.0 and
+later, Emacs only receives the following permissions upon
+installation:
+
+@itemize @minus
+@item
+@code{android.permission.VIBRATE}
+@item
+@code{android.permission.ACCESS_NETWORK_STATE}
+@item
+@code{android.permission.INTERNET}
+@item
+@code{android.permission.SET_WALLPAPER}
+@item
+@code{android.permission.NFC}
+@item
+@code{android.permission.TRANSMIT_IR}
+@item
+@code{android.permission.WAKE_LOCK}
+@end itemize
+
+Other permissions must be granted by the user through the system
+settings application.  Consult the manufacturer of your device for
+more details, as how to do this varies by device.
+
 @item
 On Android 5.1 and earlier, Emacs automatically receives the following
 permissions it has requested upon being installed:
@@ -458,6 +495,10 @@ permissions it has requested upon being installed:
 @item
 @code{android.permission.SET_WALLPAPER}
 @item
+@code{android.permission.READ_CALENDAR}
+@item
+@code{android.permission.WRITE_CALENDAR}
+@item
 @code{android.permission.READ_EXTERNAL_STORAGE}
 @item
 @code{android.permission.WRITE_EXTERNAL_STORAGE}
@@ -498,31 +539,6 @@ permissions it has requested upon being installed:
 While most of these permissions are left unused by Emacs itself, they
 are declared by Emacs as they could be useful for other programs; for
 example, the permission to access contacts may be useful for EUDC.
-
-@item
-On Android 6.0 and later, Emacs only receives the following
-permissions upon installation:
-
-@itemize @minus
-@item
-@code{android.permission.VIBRATE}
-@item
-@code{android.permission.ACCESS_NETWORK_STATE}
-@item
-@code{android.permission.INTERNET}
-@item
-@code{android.permission.SET_WALLPAPER}
-@item
-@code{android.permission.NFC}
-@item
-@code{android.permission.TRANSMIT_IR}
-@item
-@code{android.permission.WAKE_LOCK}
-@end itemize
-
-Other permissions must be granted by the user through the system
-settings application.  Consult the manufacturer of your device for
-more details, as how to do this varies by device.
 @end itemize
 
 @node Android Windowing
@@ -640,16 +656,14 @@ volume buttons in that case.
 @cindex dialog boxes, android
   Emacs is unable to display dialog boxes (@pxref{Dialog Boxes}) while
 it does not have the input focus on Android 6.0 or later.  If this is
-important to you, this ability can be restored by granting Emacs
-permission to display over other programs.  Normally, this can be done
-from the:
+important to you, this capability can be restored by granting Emacs
+permission to display over other programs.  On most systems, this can
+be done from the following Settings menu:
 
 @example
 System -> Apps -> Emacs -> More -> Display over other apps
 @end example
 
-menu in the system settings, but this procedure may vary by device.
-
 @cindex keyboard modifiers, android
   There is a direct relation between physical modifier keys and Emacs
 modifiers (@pxref{Modifier Keys}) reported within key events, subject
@@ -663,6 +677,28 @@ in Emacs.
 modifier: it is referred to as @key{SYM} on Android keyboards and
 within the Settings keymap menu.
 
+@vindex android-intercept-control-space
+@cindex @kbd{C-SPC} interception, android
+  Android input methods have a penchant for irritating users by
+silently discarding key sequences containing @kbd{C-SPC} during the
+event filtering process, that they normally have no real application
+for such key sequences notwithstanding.  By default, Emacs intercepts
+these key sequences before they can be filtered by the input method.
+
+  If this proves unwanted (for instance, if the input method treats
+@kbd{C-SPC} as a shortcut key for switching languages), it can be
+disabled by setting the variable
+@code{android-intercept-control-space} to @code{nil}.
+
+@vindex android-keyboard-bell-duration
+@cindex keyboard bell, android
+  The keyboard bell installed within Android systems takes the form of
+a vibrating element that is activated for a number of milliseconds
+whenever the bell is rung.  The duration of this vibration can be
+customized through altering the variable
+@code{android-keyboard-bell-duration} to any value between @code{10}
+and @code{1000}.
+
 @node Android Fonts
 @section Font Backends and Selection under Android
 @cindex fonts, android
diff --git a/doc/emacs/building.texi b/doc/emacs/building.texi
index f82b605598e..d6610099460 100644
--- a/doc/emacs/building.texi
+++ b/doc/emacs/building.texi
@@ -701,7 +701,6 @@ to recompile and restart the program.
 @cindex GUD Tooltip mode
 @cindex mode, GUD Tooltip
 @findex gud-tooltip-mode
-@vindex gud-tooltip-echo-area
   GUD Tooltip mode is a global minor mode that adds tooltip support to
 GUD@.  To toggle this mode, type @kbd{M-x gud-tooltip-mode}.  It is
 disabled by default.  If enabled, you can move the mouse pointer over a
@@ -713,10 +712,8 @@ you can tell Emacs more explicitly what expression to 
evaluate by
 dragging the mouse over the expression, then leaving the mouse inside
 the marked area.  The GUD Tooltip mode takes effect in the GUD
 interaction buffer, and in all source buffers with major modes listed
-in the variable @code{gud-tooltip-modes}.  If the variable
-@code{gud-tooltip-echo-area} is non-@code{nil}, or if you turned off
-the tooltip mode, values are shown in the echo area instead of a
-tooltip.
+in the variable @code{gud-tooltip-modes}.  If you turned off the
+tooltip mode, values are shown in the echo area instead of a tooltip.
 
   When using GUD Tooltip mode with @kbd{M-x gud-gdb}, displaying an
 expression's value in GDB can sometimes expand a macro, potentially
diff --git a/doc/emacs/custom.texi b/doc/emacs/custom.texi
index 456e2087217..23dcc44a928 100644
--- a/doc/emacs/custom.texi
+++ b/doc/emacs/custom.texi
@@ -626,7 +626,7 @@ button.
 the theme file and asks if you really want to load it.  Because
 loading a Custom theme can execute arbitrary Lisp code, you should
 only say yes if you know that the theme is safe; in that case, Emacs
-offers to remember in the future that the theme is safe(this is done
+offers to remember in the future that the theme is safe (this is done
 by saving the theme file's SHA-256 hash to the variable
 @code{custom-safe-themes}; if you want to treat all themes as safe,
 change its value to @code{t}).  Themes that come with Emacs (in the
diff --git a/doc/emacs/frames.texi b/doc/emacs/frames.texi
index a968c2a97c5..2a9567a7bcd 100644
--- a/doc/emacs/frames.texi
+++ b/doc/emacs/frames.texi
@@ -1408,9 +1408,10 @@ the number of tabs created on each frame.
 
 @findex toggle-frame-tab-bar
   To toggle the use of the Tab Bar only on the selected frame, type
-@kbd{M-x toggle-frame-tab-bar}.  This command allows to enable the
-display of the Tab Bar on some frames and disable it on others,
-regardless of the values of @code{tab-bar-mode} and @code{tab-bar-show}.
+@kbd{M-x toggle-frame-tab-bar}.  This command facilitates selectively
+enabling the Tab Bar on some frames while keeping it disabled on
+others, irrespective of the values of @code{tab-bar-mode} and
+@code{tab-bar-show}.
 
 @kindex C-x t
   The prefix key @kbd{C-x t} is analogous to @kbd{C-x 5}.
diff --git a/doc/emacs/haiku.texi b/doc/emacs/haiku.texi
index 1506bc8f912..0bb216c14ae 100644
--- a/doc/emacs/haiku.texi
+++ b/doc/emacs/haiku.texi
@@ -8,52 +8,55 @@
   Haiku is a Unix-like operating system that originated as a
 re-implementation of the operating system BeOS.
 
-  This section describes the peculiarities of using Emacs built with
-the Application Kit, the windowing system native to Haiku.  The
-oddities described here do not apply to using Emacs on Haiku built
-without windowing support, or built with X11.
+  This appendix describes the peculiarities of using Emacs built with
+the Application Kit, the windowing system indigenous to Haiku.  The
+idiosyncracies illustrated here do not apply to Emacs on Haiku built
+without windowing support, or configured with X11.
 
 @menu
 * Haiku Basics::        Basic Emacs usage and installation under Haiku.
-* Haiku Fonts::         The various options for displaying fonts on Haiku.
+* Haiku Fonts::         Various options for displaying fonts on Haiku.
 @end menu
 
 @node Haiku Basics
-@section Installation and usage peculiarities under Haiku
+@section Haiku Installation and Startup
 @cindex haiku application
 @cindex haiku installation
 
-  Emacs installs two separate executables under Haiku; it is up to the
-user to decide which one suits him best: A regular executable, with
-the lowercase name @code{emacs}, and a binary containing
-Haiku-specific application metadata, with the name @code{Emacs}.
-
-@cindex launching Emacs from the tracker
-@cindex tty Emacs in haiku
-  If you are launching Emacs from the Tracker, or want to make the
-Tracker open files using Emacs, you should use the binary named
-@code{Emacs}; if you are going to use Emacs in the terminal, or wish
-to launch separate instances of Emacs, or do not care for the
-aforementioned system integration features, use the binary named
-@code{emacs} instead.
+  When Emacs is installed under Haiku, two executables are copied to
+the binaries directory, which are identical save for some identifying
+file-system metadata.  The first is a normal Emacs executable,
+@file{emacs}, whereas the second, @file{Emacs}, incorporates an icon
+and an application ``signature'' that abets the system in attributing
+both file types and open frames to it, thereby enabling it to receive
+file type assignments, and thus to open files directly from the
+Tracker.
+
+  Several file attributes are set within @file{Emacs} that prompt the
+system to permit only a single copy to run at any given time.  This
+invariant is verified upon the establishment of a display connection,
+and is enforced by terminating any Emacs process that attempts to
+create a display connection when one is already present.
+
+  For this and other reasons, @file{Emacs} is appropriate for starting
+a GUI session of Emacs, while @file{emacs} should be used for other
+types of Emacs sessions.
 
 @cindex modifier keys and system keymap (Haiku)
-@cindex haiku keymap
-  On Haiku, unusual modifier keys such as the Hyper key are
-unsupported.  By default, the super key corresponds with the option
-key defined by the operating system, the meta key with the command
-key, the control key with the system control key, and the shift key
-with the system shift key.  On a standard PC keyboard, Haiku should
-map these keys to positions familiar to those using a GNU system, but
-this may require some adjustment to your system's configuration to
-work.
-
-  It is impossible to type accented characters using the system super
-key map.
-
-  You can customize the correspondence between modifier keys known to
-the system, and those known to Emacs.  The variables that allow for
-that are described below.
+  Emacs is incapable of receiving unusual modifier keys such as
+@kbd{Hyper} under Haiku, or to receive accented characters produced
+from the system Super key map.
+
+  By default, the @key{Super} modifier is reported when the Option key
+defined by the operating system is depressed.  Analogously, the
+@key{Meta} modifier is assigned to the Command key, and of course
+@key{Control} to the system Control key and @key{Shift} to the system
+Shift key.  On a standard PC keyboard, Haiku should map these keys to
+positions familiar to those using a GNU system, but this may require
+some adjustment to your system's configuration to work.
+
+  You can customize the relation between modifier keys known to the
+system and those known to Emacs by means of the variables below.
 
 @cindex modifier key customization (Haiku)
 @table @code
@@ -86,25 +89,22 @@ instead.
 @cindex tooltips (haiku)
 @cindex haiku tooltips
   On Haiku, Emacs defaults to using the system tooltip mechanism.
-This usually leads to more responsive tooltips, but the tooltips will
-not be able to display text properties or faces.  If you need those
-features, customize the variable @code{use-system-tooltips} to the
-@code{nil} value, and Emacs will use its own implementation of
-tooltips.
+Tooltips thus generated are sometimes more responsive, but will not be
+able to display text properties or faces.  If you need those features,
+customize the variable @code{use-system-tooltips} to @code{nil} value,
+whereupon Emacs will use its own implementation of tooltips instead.
 
 @cindex X resources on Haiku
-  Unlike the X window system, Haiku does not have a system-wide
-resource database.  Since many important options are specified via
-X resources (@pxref{X Resources}), an emulation is provided: upon
+  Unlike the X window system, Haiku does not provide a system-wide
+resource database.  Since many important options are specified via X
+resources (@pxref{X Resources}), an emulation is provided: upon
 startup, Emacs will load a file named @file{GNU Emacs} inside the user
 configuration directory (normally @file{/boot/home/config/settings}),
 which should be a flattened system message where keys and values are
 both strings, and correspond to attributes and their values
-respectively.
-
-You can create such a file with the @command{xmlbmessage} tool.
+respectively.  Such a file may be created with the
+@command{xmlbmessage} tool.
 
-@subsection What to do when Emacs crashes
 @cindex crashes, Haiku
 @cindex haiku debugger
 @vindex haiku-debug-on-fatal-error
@@ -115,18 +115,18 @@ attach the report generated by the system debugger when 
reporting a
 bug.
 
 @node Haiku Fonts
-@section Font and font backend selection on Haiku
+@section Font Backends and Selection under Haiku
 @cindex font backend selection (Haiku)
 
-  Emacs, when built with Haiku windowing support, can be built with
-several different font backends.  You can specify font backends by
-specifying @kbd{-xrm Emacs.fontBackend:BACKEND} on the command line
-used to invoke Emacs, where @kbd{BACKEND} is one of the backends
-specified below, or on a per-frame basis by changing the
-@code{font-backend} frame parameter.
+  Emacs supports several different font backends when built with Haiku
+windowing support, though the subset supported is subject to the list
+of dependencies present and enabled when Emacs was configured.  You
+can specify which font backends to utilize by providing @w{@code{-xrm
+Emacs.fontBackend:@var{backend}}} on the command line used to invoke
+Emacs, where @var{backend} is one of the backends listed below, or on
+a per-frame basis by changing the @code{font-backend} frame parameter.
 
   Two of these backends, @code{ftcr} and @code{ftcrhb} are identical
 to their counterparts on the X Window System.  There is also a
 Haiku-specific backend named @code{haiku}, that uses the App Server to
-draw fonts, but does not at present support display of color font and
-emoji.
+draw fonts, but presently cannot display color fonts or Emoji.
diff --git a/doc/emacs/input.texi b/doc/emacs/input.texi
index 4f49ca3cece..db0e88a1c9c 100644
--- a/doc/emacs/input.texi
+++ b/doc/emacs/input.texi
@@ -145,9 +145,10 @@ of a hardware button that is always present on the device 
results in
 Emacs quitting.  @xref{Quitting}.
 
 @vindex x-quit-keysym
-  Which button is used to do this depends on the window system in use:
-on X, it is defined in the variable @code{x-quit-keysym}, and on
-Android, it is always the volume down button.
+  The button afforded such special treatment varies; under X, no such
+button exists by default, but one can be configured through the
+variable @code{x-quit-keysym}, whereas under Android it is always the
+volume down buttons.
 
 @cindex text conversion, keyboards
   Most input methods designed to work with on-screen keyboards perform
diff --git a/doc/emacs/killing.texi b/doc/emacs/killing.texi
index 47e0b5e37ae..4d5c9ecc7b2 100644
--- a/doc/emacs/killing.texi
+++ b/doc/emacs/killing.texi
@@ -308,7 +308,7 @@ way to move or copy text is to kill it and then yank it 
elsewhere.
 Yank the last kill into the buffer, at point (@code{yank}).
 @item M-y
 Either replace the text just yanked with an earlier batch of killed
-text (@code{yank-pop}), or allow to select from the list of
+text (@code{yank-pop}), or allow selecting from the list of
 previously-killed batches of text.  @xref{Earlier Kills}.
 @item C-M-w
 Cause the following command, if it is a kill command, to append to the
diff --git a/doc/emacs/kmacro.texi b/doc/emacs/kmacro.texi
index fc1402b489d..05095bc68b8 100644
--- a/doc/emacs/kmacro.texi
+++ b/doc/emacs/kmacro.texi
@@ -515,6 +515,19 @@ editing it.  Type @kbd{C-h m} once in that buffer to 
display details
 of how to edit the macro.  When you are finished editing, type
 @kbd{C-c C-c}.
 
+@findex edmacro-insert-key
+@findex edmacro-set-macro-to-region-lines
+  @code{edmacro-mode}, the major mode used by
+@code{kmacro-edit-macro}, provides commands for more easily editing
+the formatted macro.  Use @kbd{C-c C-q} (@code{edmacro-insert-key}) to
+insert the next key sequence that you type into the buffer using the
+correct format, similar to @kbd{C-q} (@code{quoted-insert}).  Use
+@kbd{C-c C-r} (@code{edmacro-set-macro-to-region-lines}) to replace
+the macro's text with the text in the region.  If the region does not
+begin at the start of a line or if it does not end at the end of a
+line, the region is extended to include complete lines.  If the region
+ends at the beginning of a line, that final line is excluded.
+
 @findex edit-kbd-macro
 @kindex C-x C-k e
   You can edit a named keyboard macro or a macro bound to a key by typing
@@ -523,9 +536,13 @@ keyboard input that you would use to invoke the 
macro---@kbd{C-x e} or
 @kbd{M-x @var{name}} or some other key sequence.
 
 @findex kmacro-edit-lossage
+@vindex edmacro-reverse-macro-lines
 @kindex C-x C-k l
   You can edit the last 300 keystrokes as a macro by typing
-@kbd{C-x C-k l} (@code{kmacro-edit-lossage}).
+@kbd{C-x C-k l} (@code{kmacro-edit-lossage}).  By default,
+your most recent keystrokes are listed at the bottom of the buffer.
+To list a macro's key sequences in reverse order, set
+@code{edmacro-reverse-macro-lines} to @code{t}.
 
 @node Keyboard Macro Step-Edit
 @section Stepwise Editing a Keyboard Macro
diff --git a/doc/emacs/maintaining.texi b/doc/emacs/maintaining.texi
index 2dad70d3d13..5f9a5d89bf3 100644
--- a/doc/emacs/maintaining.texi
+++ b/doc/emacs/maintaining.texi
@@ -447,9 +447,10 @@ merge-based version control system, a @samp{-} character 
indicates
 that the work file is unmodified, and @samp{:} indicates that it has
 been modified.  @samp{!} indicates that the file contains conflicts as
 result of a recent merge operation (@pxref{Merging}), or that the file
-was removed from the version control.  Finally, @samp{?} means that
-the file is under version control, but is missing from the working
-tree.
+was removed from the version control, or that it is versioned but also
+@dfn{ignored}, something that usually should not happen (@pxref{VC
+Ignore}).  Finally, @samp{?} means that the file is under version
+control, but is missing from the working tree.
 
   In a lock-based system, @samp{-} indicates an unlocked file, and
 @samp{:} a locked file; if the file is locked by another user (for
@@ -1803,7 +1804,7 @@ prompt you for the project directory.
 @vindex vc-directory-exclusion-list
   The command @kbd{C-x p f} (@code{project-find-file}) is a convenient
 way of visiting files (@pxref{Visiting}) that belong to the current
-project.  Unlike @kbd{C-x C-f}, this command doesn't require to type
+project.  Unlike @kbd{C-x C-f}, this command doesn't require typing
 the full file name of the file to visit, you can type only the file's
 base name (i.e., omit the leading directories).  In addition, the
 completion candidates considered by the command include only the files
@@ -2657,8 +2658,8 @@ definitions have tag names like @samp{operator+}.  If you 
specify the
 @samp{--class-qualify} option, tags for variables and functions in
 classes are named @samp{@var{class}::@var{variable}} and
 @samp{@var{class}::@var{function}}.  By default, class methods and
-members are not class-qualified, which allows to identify their names in
-the sources more accurately.
+members are not class-qualified, which facilitates identifying their
+names in the sources more accurately.
 
 @item
 In Java code, tags include all the constructs recognized in C++, plus
diff --git a/doc/emacs/msdos.texi b/doc/emacs/msdos.texi
index 52561ec9a33..8401a2aa596 100644
--- a/doc/emacs/msdos.texi
+++ b/doc/emacs/msdos.texi
@@ -412,10 +412,10 @@ this option can be one of the following symbols:
 @itemx nil
 Emulate @sc{gnu} systems; this is the default.  This sets
 @code{ls-lisp-ignore-case} and @code{ls-lisp-dirs-first} to
-@code{nil}, and @code{ls-lisp-verbosity} to @code{(links uid gid)}.
+@code{nil}, and @code{ls-lisp-verbosity} to @code{(links uid gid modes)}.
 @item UNIX
 Emulate Unix systems.  Like @code{GNU}, but sets
-@code{ls-lisp-verbosity} to @code{(links uid)}.
+@code{ls-lisp-verbosity} to @code{(links uid modes)}.
 @item MacOS
 Emulate macOS@.  Sets @code{ls-lisp-ignore-case} to @code{t}, and
 @code{ls-lisp-dirs-first} and @code{ls-lisp-verbosity} to @code{nil}.
diff --git a/doc/emacs/text.texi b/doc/emacs/text.texi
index 3d3f2562617..6f57bae8fef 100644
--- a/doc/emacs/text.texi
+++ b/doc/emacs/text.texi
@@ -1024,10 +1024,11 @@ addition to ellipsis, to show that a section is hidden. 
 Clicking the
 mouse on the button toggles display of the section.  If the value of
 this variable is @code{insert}, the buttons are inserted directly into
 the buffer text, so @key{RET} on the button will also toggle display
-of the section, like a mouse click does.  If the value is
-@code{in-margins}, Outline minor mode will use the window margins to
-indicate that a section is hidden.  The buttons are customizable as icons
-(@pxref{Icons}).
+of the section, like a mouse click does.  Using the value @code{insert}
+is not recommended in editable buffers because it modifies them.
+If the value is @code{in-margins}, Outline minor mode will use the
+window margins to indicate that a section is hidden.  The buttons are
+customizable as icons (@pxref{Icons}).
 
 @vindex outline-minor-mode-cycle
   If the @code{outline-minor-mode-cycle} user option is
diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi
index 78fc43d0daf..6601135cb85 100644
--- a/doc/lispref/commands.texi
+++ b/doc/lispref/commands.texi
@@ -1854,20 +1854,19 @@ represented in Lisp as lists.  The lists record both 
the starting mouse
 position and the final position, like this:
 
 @example
-(@var{event-type}
- (@var{window1} START-POSITION)
- (@var{window2} END-POSITION))
+(@var{event-type} @var{start-position} @var{end-position})
 @end example
 
 For a drag event, the name of the symbol @var{event-type} contains the
 prefix @samp{drag-}.  For example, dragging the mouse with button 2
 held down generates a @code{drag-mouse-2} event.  The second and third
-elements of the event give the starting and ending position of the
-drag, as mouse position lists (@pxref{Click Events}).  You can access
-the second element of any mouse event in the same way.  However, the
-drag event may end outside the boundaries of the frame that was
-initially selected.  In that case, the third element's position list
-contains that frame in place of a window.
+elements of the event, @var{start-position} and @var{end-position} in
+the foregoing illustration, are set to the start and end positions of
+the drag as mouse position lists (@pxref{Click Events}).  You can
+access the second element of any mouse event in the same way.
+However, the drag event may end outside the boundaries of the frame
+that was initially selected.  In that case, the third element's
+position list contains that frame in place of a window.
 
 The @samp{drag-} prefix follows the modifier key prefixes such as
 @samp{C-} and @samp{M-}.
@@ -2762,16 +2761,6 @@ the @code{track-mouse} macro, that produces an event 
like this:
 (mouse-movement (#<frame *ielm* 0x102849a30> nil (563 . 205) 532301936))
 @end smallexample
 
-To handle a SIGUSR1 signal, define an interactive function, and
-bind it to the @code{signal usr1} event sequence:
-
-@smallexample
-(defun usr1-handler ()
-  (interactive)
-  (message "Got USR1 signal"))
-(keymap-global-set "<signal> <usr1>" 'usr1-handler)
-@end smallexample
-
 @node Classifying Events
 @subsection Classifying Events
 @cindex event type
diff --git a/doc/lispref/compile.texi b/doc/lispref/compile.texi
index a51691bddcf..0dab03eb7ba 100644
--- a/doc/lispref/compile.texi
+++ b/doc/lispref/compile.texi
@@ -422,7 +422,7 @@ execution of the compiled file.  For example,
 @lisp
 (eval-when-compile
   (unless (fboundp 'some-new-thing)
-    (defmacro 'some-new-thing ()
+    (defmacro some-new-thing ()
       (compatibility code))))
 @end lisp
 
@@ -801,9 +801,9 @@ Compilation, the previous chapter}, Emacs can also 
optionally compile
 Lisp function definitions into a true compiled code, known as
 @dfn{native code}.  This feature uses the @file{libgccjit} library,
 which is part of the GCC distribution, and requires that Emacs be
-built with support for using that library.  It also requires to have
-GCC and Binutils (the assembler and linker) available on your system
-for you to be able to native-compile Lisp code.
+built with support for using that library.  It also requires GCC and
+Binutils (the assembler and linker) to be available on your system for
+you to be able to native-compile Lisp code.
 
 @vindex native-compile@r{, a Lisp feature}
   To determine whether the current Emacs process can produce and load
@@ -831,9 +831,9 @@ produced by earlier or later Emacs versions; native 
compilation of the
 same Lisp code by a different Emacs version will usually produce a
 natively-compiled library under a unique file name that only that
 version of Emacs will be able to load.  However, the use of unique
-file names allows to have in the same directory several versions of
-the same Lisp library natively-compiled by several different versions
-of Emacs.
+file names enables several versions of the same Lisp library
+natively-compiled by several different versions of Emacs to be placed
+within the same directory.
 
 @vindex no-native-compile
   A non-@code{nil} file-local variable binding of
@@ -1116,7 +1116,7 @@ functions provided by the @file{.elc} file.
 @end defvar
 
 @cindex trampolines, in native compilation
-  Setting the value of @code{native-comp-jit-compilation} to@code{nil}
+  Setting the value of @code{native-comp-jit-compilation} to @code{nil}
 disables JIT native compilation.  However, even when JIT native
 compilation is disabled, Emacs might still need to start asynchronous
 native compilation subprocesses to produce @dfn{trampolines}.  To
diff --git a/doc/lispref/control.texi b/doc/lispref/control.texi
index 3aee9dd80e4..dc5b9507efc 100644
--- a/doc/lispref/control.texi
+++ b/doc/lispref/control.texi
@@ -36,13 +36,14 @@ evaluated sequentially.  You can use macros to define your 
own control
 structure constructs (@pxref{Macros}).
 
 @menu
-* Sequencing::             Evaluation in textual order.
-* Conditionals::           @code{if}, @code{cond}, @code{when}, @code{unless}.
-* Combining Conditions::   @code{and}, @code{or}, @code{not}, and friends.
+* Sequencing::                  Evaluation in textual order.
+* Conditionals::                @code{if}, @code{cond}, @code{when}, 
@code{unless}.
+* Combining Conditions::        @code{and}, @code{or}, @code{not}, and friends.
 * Pattern-Matching Conditional::  How to use @code{pcase} and friends.
-* Iteration::              @code{while} loops.
-* Generators::             Generic sequences and coroutines.
-* Nonlocal Exits::         Jumping out of a sequence.
+* Iteration::                   @code{while} loops.
+* Generators::                  Generic sequences and coroutines.
+* Nonlocal Exits::              Jumping out of a sequence.
+* Conditional Compilation::     A facility like C's #if.
 @end menu
 
 @node Sequencing
@@ -1280,11 +1281,11 @@ fail, @code{pcase} will immediately return @code{nil} 
without calling
 @code{message}.
 
 Extraction of multiple values stored in an object is known as
-@dfn{destructuring}.  Using @code{pcase} patterns allows to perform
-@dfn{destructuring binding}, which is similar to a local binding
-(@pxref{Local Variables}), but gives values to multiple elements of
-a variable by extracting those values from an object of compatible
-structure.
+@dfn{destructuring}.  Using @code{pcase} patterns allows you to
+perform @dfn{destructuring binding}, which is similar to a local
+binding (@pxref{Local Variables}), but gives values to multiple
+elements of a variable by extracting those values from an object of
+compatible structure.
 
 The macros described in this section use @code{pcase} patterns to
 perform destructuring binding.  The condition of the object to be of
@@ -2467,3 +2468,47 @@ quit, and the quit happens immediately after the function
 @code{ftp-setup-buffer} returns but before the variable @code{process} is
 set, the process will not be killed.  There is no easy way to fix this bug,
 but at least it is very unlikely.
+
+@node Conditional Compilation
+@section Conditional Compilation
+
+  There will be times when you want certain code to be compiled only
+when a certain condition holds.  This is particularly the case when
+maintaining Emacs packages; to keep the package compatible with older
+versions of Emacs you may need to use a function or variable which has
+become obsolete in the current version of Emacs.
+
+  You could just use a conditional form to select the old or new form
+at run time, but this tends to output annoying warning messages about
+the obsolete function/variable.  For such situations, the macro
+@code{static-if} comes in handy.  It is patterned after the special
+form @code{if} (@pxref{Conditionals}).
+
+  To use this facility for an older version of Emacs, copy the source
+for @code{static-if} from the Emacs source file @file{lisp/subr.el}
+into your package.
+
+@defmac static-if condition then-form else-forms...
+Test @var{condition} at macro-expansion time.  If its value is
+non-@code{nil}, expand the macro to @var{then-form}, otherwise expand
+it to @var{else-forms} enclosed in a @code{progn}.  @var{else-forms}
+may be empty.
+
+Here is an example of its use from CC Mode, which prevents a
+@code{defadvice} form being compiled in newer versions of Emacs:
+@example
+@group
+(static-if (boundp 'comment-line-break-function)
+    (progn)
+  (defvar c-inside-line-break-advice nil)
+  (defadvice indent-new-comment-line (around c-line-break-advice
+                                             activate preactivate)
+    "Call `c-indent-new-comment-line' if in CC Mode."
+    (if (or c-inside-line-break-advice
+            (not c-buffer-is-cc-mode))
+        ad-do-it
+      (let ((c-inside-line-break-advice t))
+        (c-indent-new-comment-line (ad-get-arg 0))))))
+@end group
+@end example
+@end defmac
diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index 0331c48fa6b..4dbb4afb20d 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -1821,8 +1821,8 @@ where the @var{primary} value is used as described above, 
and
 @var{secondary} is the fallback value used when @var{primary} and the
 nesting considerations fail to resolve the precedence between
 overlays.  In particular, priority value @w{@code{(nil . @var{n})}},
-with @var{n} a positive integer, allows to have the overlays ordered
-by priority when necessary without completely overriding other
+with @var{n} a positive integer, enables you to have the overlays
+ordered by priority when necessary without completely overriding other
 overlays.
 
 Currently, all overlays take priority over text properties.
@@ -4254,14 +4254,20 @@ key-attribute pairs may be omitted from the list if 
they are not
 specified by @var{font}.
 @end defun
 
-@defun font-xlfd-name font &optional fold-wildcards
+@defun font-xlfd-name font &optional fold-wildcards long-xlfds
 This function returns the XLFD (X Logical Font Descriptor), a string,
 matching @var{font}.  @xref{Fonts,,, emacs, The GNU Emacs Manual}, for
-information about XLFDs.  If the name is too long for an XLFD (which
-can contain at most 255 characters), the function returns @code{nil}.
+information about XLFDs.
 
 If the optional argument @var{fold-wildcards} is non-@code{nil},
 consecutive wildcards in the XLFD are folded into one.
+
+If the optional argument @var{long-xlfds} is omitted or @code{nil},
+then the function returns @code{nil} if the XLFD would exceed 255
+characters in length; this is for compatibility with the X protocol,
+which mandates that XLFDs are restricted to that length.  If
+@var{long-xlfds} is non-@code{nil}, this restriction is lifted, and
+the function can return XLFDs of any length.
 @end defun
 
 The following two functions return important information about a font.
@@ -8725,8 +8731,8 @@ hexadecimal notation.
 Display a box containing that string.  The string should contain at
 most 6 @acronym{ASCII} characters.  As an exception, if the string
 includes just one character, on text-mode terminals that character
-will be displayed without a box; this allows to handle such
-``acronyms'' as a replacement character for characters that cannot be
+will be displayed without a box; this enables treating such
+``acronyms'' as replacement characters for characters that cannot be
 displayed by the terminal.
 
 @item a cons cell @code{(@var{graphical} . @var{text})}
@@ -9022,7 +9028,7 @@ Bidirectionality'' class implementation of the 
@acronym{UBA},
 consistent with the requirements of the Unicode Standard v9.0.  Note,
 however, that the way Emacs displays continuation lines when text
 direction is opposite to the base paragraph direction deviates from
-the UBA, which requires to perform line wrapping before reordering
+the UBA, which requires performing line wrapping before reordering
 text for display.
 
 @defvar bidi-display-reordering
diff --git a/doc/lispref/edebug.texi b/doc/lispref/edebug.texi
index c5be3a40d2c..7cf7ee51751 100644
--- a/doc/lispref/edebug.texi
+++ b/doc/lispref/edebug.texi
@@ -1289,6 +1289,8 @@ examples):
 @item sexp
 A single unevaluated Lisp object, which is not instrumented.
 @c an "expression" is not necessarily intended for evaluation.
+If the macro evaluates an argument at macro-expansion time, you should
+use @code{sexp} for it rather than @code{form}.
 
 @item form
 A single evaluated expression, which is instrumented.  If your macro
diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi
index 853ca5ae69e..5d6e1809286 100644
--- a/doc/lispref/frames.texi
+++ b/doc/lispref/frames.texi
@@ -606,7 +606,7 @@ frames (@pxref{Child Frames}) and @code{undecorated} or
 Outer borders are never shown on text terminal frames and on frames
 generated by GTK+ routines.  On MS-Windows, the outer border is emulated
 with the help of a one pixel wide external border.  Non-toolkit builds
-on X allow to change the color of the outer border by setting the
+on X allow changing the color of the outer border by setting the
 @code{border-color} frame parameter (@pxref{Layout Parameters}).
 
 @item Title Bar
@@ -1000,12 +1000,12 @@ Negative parameter values position the right edge of 
the outer frame by
 frame's native rectangle) and the bottom edge by @var{-y} pixels up from
 the bottom edge of the screen (or the parent frame's native rectangle).
 
-Note that negative values do not permit to align the right or bottom
+Note that negative values do not permit aligning the right or bottom
 edge of @var{frame} exactly at the right or bottom edge of its display
-or parent frame.  Neither do they allow to specify a position that does
+or parent frame.  Neither do they allow specifying a position that does
 not lie within the edges of the display or parent frame.  The frame
 parameters @code{left} and @code{top} (@pxref{Position Parameters})
-allow to do that, but may still fail to provide good results for the
+allow doing that, but may still fail to provide good results for the
 initial or a new frame.
 
 This function has no effect on text terminal frames.
@@ -1742,7 +1742,7 @@ Geometry}) of the frame, in characters.  Normally, the 
functions that
 establish a frame's initial width or resize a frame horizontally make
 sure that all the frame's windows, vertical scroll bars, fringes,
 margins and vertical dividers can be displayed.  This parameter, if
-non-@code{nil} allows to make a frame narrower than that with the
+non-@code{nil} enables making a frame narrower than that with the
 consequence that any components that do not fit will be clipped by the
 window manager.
 
@@ -1753,7 +1753,7 @@ Geometry}) of the frame, in characters.  Normally, the 
functions that
 establish a frame's initial size or resize a frame make sure that all
 the frame's windows, horizontal scroll bars and dividers, mode and
 header lines, the echo area and the internal menu and tool bar can be
-displayed.  This parameter, if non-@code{nil} allows to make a frame
+displayed.  This parameter, if non-@code{nil} enables making a frame
 smaller than that with the consequence that any components that do not
 fit will be clipped by the window manager.
 
@@ -1814,14 +1814,14 @@ the first time.
 
 @vindex fit-frame-to-buffer-margins@r{, a frame parameter}
 @item fit-frame-to-buffer-margins
-This parameter allows to override the value of the option
-@code{fit-frame-to-buffer-margins} when fitting this frame to the buffer
-of its root window with @code{fit-frame-to-buffer} (@pxref{Resizing
-Windows}).
+This parameter enables overriding the value of the option
+@code{fit-frame-to-buffer-margins} when fitting this frame to the
+buffer of its root window with @code{fit-frame-to-buffer}
+(@pxref{Resizing Windows}).
 
 @vindex fit-frame-to-buffer-sizes@r{, a frame parameter}
 @item fit-frame-to-buffer-sizes
-This parameter allows to override the value of the option
+This parameter enables overriding the value of the option
 @code{fit-frame-to-buffer-sizes} when fitting this frame to the buffer
 of its root window with @code{fit-frame-to-buffer} (@pxref{Resizing
 Windows}).
@@ -1902,13 +1902,13 @@ to not draw bottom dividers.
 @vindex menu-bar-lines@r{, a frame parameter}
 @item menu-bar-lines
 The number of lines to allocate at the top of the frame for a menu bar
-(@pxref{Menu Bar}).  The default is one if Menu Bar mode is enabled and
-zero otherwise.  @xref{Menu Bars,,,emacs, The GNU Emacs Manual}.  For an
-external menu bar (@pxref{Frame Layout}), this value remains unchanged
-even when the menu bar wraps to two or more lines.  In that case, the
-@code{menu-bar-size} value returned by @code{frame-geometry}
-(@pxref{Frame Geometry}) allows to derive whether the menu bar actually
-occupies one or more lines.
+(@pxref{Menu Bar}).  The default is one if Menu Bar mode is enabled
+and zero otherwise.  @xref{Menu Bars,,,emacs, The GNU Emacs Manual}.
+For an external menu bar (@pxref{Frame Layout}), this value remains
+unchanged even when the menu bar wraps to two or more lines.  In that
+case, the @code{menu-bar-size} value returned by @code{frame-geometry}
+(@pxref{Frame Geometry}) enables you to establish whether the menu bar
+actually occupies one or more lines.
 
 @vindex tool-bar-lines@r{, a frame parameter}
 @item tool-bar-lines
@@ -3433,15 +3433,15 @@ parameter indicates the number of pixels where the 
frame @dfn{snaps} at
 the respective edge or corner of its parent frame.
 
   There are two ways to drag an entire child frame with the mouse: The
-@code{drag-with-mode-line} parameter, if non-@code{nil}, allows to drag
-a frame without minibuffer window (@pxref{Minibuffer Windows}) via the
-mode line area of its bottommost window.  The
-@code{drag-with-header-line} parameter, if non-@code{nil}, allows to
-drag the frame via the header line area of its topmost window.
+@code{drag-with-mode-line} parameter, if non-@code{nil}, enables
+dragging a frame without minibuffer window (@pxref{Minibuffer
+Windows}) via the mode line area of its bottommost window.  The
+@code{drag-with-header-line} parameter, if non-@code{nil}, enables
+dragging the frame via the header line area of its topmost window.
 
   In order to give a child frame a draggable header or mode line, the
 window parameters @code{mode-line-format} and @code{header-line-format}
-are handy (@pxref{Window Parameters}).  These allow to remove an
+are handy (@pxref{Window Parameters}).  These allow removing an
 unwanted mode line (when @code{drag-with-header-line} is chosen) and to
 remove mouse-sensitive areas which might interfere with frame dragging.
 
diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi
index 53525e6b386..236b823e7e6 100644
--- a/doc/lispref/functions.texi
+++ b/doc/lispref/functions.texi
@@ -1994,9 +1994,16 @@ advice.  Advice can also cause confusion in debugging, 
if the person doing the
 debugging does not notice or remember that the function has been modified
 by advice.
 
-  For these reasons, advice should be reserved for the cases where you
-cannot modify a function's behavior in any other way.  If it is
-possible to do the same thing via a hook, that is preferable
+  Note that the problems are not due to advice per se, but to the act
+of modifying a named function.  It is even more problematic to modify
+a named function via lower-level primitives like @code{fset},
+@code{defalias}, or @code{cl-letf}.  From that point of view, advice
+is the better way to modify a named function because it keeps track of
+the modifications, so they can be listed and undone.
+
+  Modifying a named function should be reserved for
+the cases where you cannot modify Emacs' behavior in any other way.
+If it is possible to do the same thing via a hook, that is preferable
 (@pxref{Hooks}).  If you simply want to change what a particular key
 does, it may be better to write a new command, and remap the old
 command's key bindings to the new one (@pxref{Remapping Commands}).
diff --git a/doc/lispref/internals.texi b/doc/lispref/internals.texi
index 0951e60433a..1fba683223e 100644
--- a/doc/lispref/internals.texi
+++ b/doc/lispref/internals.texi
@@ -1567,7 +1567,7 @@ return values from module functions.  For this purpose, 
the module
 Emacs Lisp objects communicated via the @acronym{API}; it is the
 functional equivalent of the @code{Lisp_Object} type used in Emacs C
 primitives (@pxref{Writing Emacs Primitives}).  This section describes
-the parts of the module @acronym{API} that allow to create
+the parts of the module @acronym{API} that allow creating
 @code{emacs_value} objects corresponding to basic Lisp data types, and
 how to access from C data in @code{emacs_value} objects that
 correspond to Lisp objects.
diff --git a/doc/lispref/keymaps.texi b/doc/lispref/keymaps.texi
index 99bc10677cd..d4b1c4f2b3e 100644
--- a/doc/lispref/keymaps.texi
+++ b/doc/lispref/keymaps.texi
@@ -90,7 +90,7 @@ Manual}.
 (kbd "C-x C-f") @result{} "\C-x\C-f"
 (kbd "C-x 4 C-f") @result{} "\C-x4\C-f"
 (kbd "X") @result{} "X"
-(kbd "RET") @result{} "\^M"
+(kbd "RET") @result{} "^M"
 (kbd "C-c SPC") @result{} "\C-c@ "
 (kbd "<f1> SPC") @result{} [f1 32]
 (kbd "C-M-<down>") @result{} [C-M-down]
diff --git a/doc/lispref/minibuf.texi b/doc/lispref/minibuf.texi
index 8ff5c14055e..be4d7e37261 100644
--- a/doc/lispref/minibuf.texi
+++ b/doc/lispref/minibuf.texi
@@ -374,6 +374,7 @@ key, so the text properties are only preserved on the last 
three
 characters.
 @end defvar
 
+@vindex minibuffer-mode-map
 @defvar minibuffer-local-map
 This
 @anchor{Definition of minibuffer-local-map}
@@ -414,6 +415,9 @@ default, it makes the following bindings:
 @code{file-cache-minibuffer-complete}
 @end ignore
 @end table
+
+@noindent
+The variable @code{minibuffer-mode-map} is an alias for this variable.
 @end defvar
 
 @defun read-no-blanks-input prompt &optional initial inherit-input-method
@@ -2669,7 +2673,7 @@ variable instead (@pxref{Echo Area Customization}).
 
 The option @code{resize-mini-windows} does not affect the behavior of
 minibuffer-only frames (@pxref{Frame Layout}).  The following option
-allows to automatically resize such frames as well.
+enables automatically resizing such frames as well.
 
 @defopt resize-mini-frames
 If this is @code{nil}, minibuffer-only frames are never resized
@@ -2877,3 +2881,22 @@ This is the major mode used in inactive minibuffers.  It 
uses
 keymap @code{minibuffer-inactive-mode-map}.  This can be useful
 if the minibuffer is in a separate frame.  @xref{Minibuffers and Frames}.
 @end deffn
+
+@deffn Command minibuffer-regexp-mode
+This minor mode makes editing regular expressions in the minibuffer
+more convenient.  It highlight parens via @code{show-paren-mode} and
+@code{blink-matching-paren} in a user-friendly way, avoids reporting
+false paren mismatches, and makes sexp navigation more intuitive.
+@end deffn
+
+By default, only certain minibuffer prompts automatically activate the
+convenience features of @code{minibuffer-regexp-mode} when the
+minibuffer becomes active.  This list of prompts can be customized via
+@code{minibuffer-regexp-prompts}.
+
+@defopt minibuffer-regexp-prompts
+This variable holds the list of regular expressions for activating the
+features of @code{minibuffer-regexp-mode} in the minibuffer.  The
+mode's features will be activated only if the minibuffer prompt
+matches one of the regular expressions in the list.
+@end defopt
diff --git a/doc/lispref/nonascii.texi b/doc/lispref/nonascii.texi
index 7e5590ed8ec..c290632a48b 100644
--- a/doc/lispref/nonascii.texi
+++ b/doc/lispref/nonascii.texi
@@ -133,7 +133,7 @@ This function is similar to @code{position-bytes}, but 
instead of byte
 position in the current buffer it returns the offset from the
 beginning of the current buffer's file of the byte that corresponds to
 the given character @var{position} in the buffer.  The conversion
-requires to know how the text is encoded in the buffer's file; this is
+requires knowing how the text is encoded in the buffer's file; this is
 what the @var{coding-system} argument is for, defaulting to the value
 of @code{buffer-file-coding-system}.  The optional argument
 @var{quality} specifies how accurate the result should be; it should
@@ -1181,7 +1181,7 @@ the text to be written cannot be safely encoded using the 
coding system
 specified by this variable, these operations select an alternative
 encoding by calling the function @code{select-safe-coding-system}
 (@pxref{User-Chosen Coding Systems}).  If selecting a different encoding
-requires to ask the user to specify a coding system,
+requires asking the user to specify a coding system,
 @code{buffer-file-coding-system} is updated to the newly selected coding
 system.
 
diff --git a/doc/lispref/objects.texi b/doc/lispref/objects.texi
index ad079e0d63a..9febcbefa33 100644
--- a/doc/lispref/objects.texi
+++ b/doc/lispref/objects.texi
@@ -82,7 +82,9 @@ it does not make sense to enter objects of these types as 
constants in
 a Lisp program.  These objects are printed in @dfn{hash notation},
 which consists of the characters @samp{#<}, a descriptive string
 (typically the type name followed by the name of the object), and a
-closing @samp{>}.  For example:
+closing @samp{>}.  (This is called ``hash notation'' because it begins
+with the @samp{#} character, known as ``hash'' or ``number sign'').
+For example:
 
 @example
 (current-buffer)
@@ -2206,6 +2208,10 @@ and the same non-fixnum numeric type, then they might or 
might not be
 the same object, and @code{eq} returns @code{t} or @code{nil}
 depending on whether the Lisp interpreter created one object or two.
 
+If @var{object1} or @var{object2} is a symbol with position, @code{eq}
+regards it as its bare symbol when @code{symbols-with-pos-enabled} is
+non-@code{nil} (@pxref{Symbols with Position}).
+
 @example
 @group
 (eq 'foo 'foo)
@@ -2363,6 +2369,13 @@ same sequence of character codes and all these codes are 
in the range
 The @code{equal} function recursively compares the contents of objects
 if they are integers, strings, markers, vectors, bool-vectors,
 byte-code function objects, char-tables, records, or font objects.
+
+If @var{object1} or @var{object2} is a symbol with position,
+@code{equal} regards it as its bare symbol when
+@code{symbols-with-pos-enabled} is non-@code{nil}.  Otherwise
+@code{equal} compares two symbols with position by recursively
+comparing their components.  @xref{Symbols with Position}.
+
 Other objects are considered @code{equal} only if they are @code{eq}.
 For example, two distinct buffers are never considered @code{equal},
 even if their textual contents are the same.
diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi
index 3c704970cda..5400d492f0a 100644
--- a/doc/lispref/os.texi
+++ b/doc/lispref/os.texi
@@ -3220,15 +3220,25 @@ These have the same meaning as they do when used in 
calls to
 @code{notifications-notify}.
 
 @item :urgency @var{urgency}
+The set of values for @var{urgency} is the same as with
+@code{notifications-notify}, but the urgency applies to all
+notifications displayed with the defined @var{group}, except under
+Android 7.1 and earlier.
+
 @item :group @var{group}
-These two parameters are ignored under Android 7.1 and earlier
-versions of the system.  The set of values for @var{urgency} is the
-same as with @code{notifications-notify}, but the urgency applies to
-all notifications displayed with the defined @var{group}.
+@var{group} is a string that designates a category to which the
+notification sent will belong.  This category is reproduced within the
+system's notification settings menus, but is ignored under Android 7.1
+and earlier.
 
 If @var{group} is nil or not present within @var{params}, it is
 replaced by the string @samp{"Desktop Notifications"}.
 
+Callers should provide one stable combination of @var{urgency} and
+@var{group} for each kind of notification they send, given that the
+system may elect to disregard @var{urgency} if it does not match that
+of any notification previously delivered to @var{group}.
+
 @item :icon @var{icon}
 This parameter controls the symbolic icon the notification will be
 displayed with.  Its value is a string designating an icon within the
diff --git a/doc/lispref/parsing.texi b/doc/lispref/parsing.texi
index 36b3c19df01..bac5a864bf8 100644
--- a/doc/lispref/parsing.texi
+++ b/doc/lispref/parsing.texi
@@ -400,14 +400,17 @@ when deciding whether to enable tree-sitter features.
 
 @cindex creating tree-sitter parsers
 @cindex tree-sitter parser, creating
-@defun treesit-parser-create language &optional buffer no-reuse
+@defun treesit-parser-create language &optional buffer no-reuse tag
 Create a parser for the specified @var{buffer} and @var{language}
-(@pxref{Language Grammar}).  If @var{buffer} is omitted or
-@code{nil}, it stands for the current buffer.
+(@pxref{Language Grammar}), with @var{tag}.  If @var{buffer} is
+omitted or @code{nil}, it stands for the current buffer.
 
 By default, this function reuses a parser if one already exists for
-@var{language} in @var{buffer}, but if @var{no-reuse} is
-non-@code{nil}, this function always creates a new parser.
+@var{language} with @var{tag} in @var{buffer}, but if @var{no-reuse}
+is non-@code{nil}, this function always creates a new parser.
+
+@var{tag} can be any symbol except @code{t}, and defaults to
+@code{nil}.  Different parsers can have the same tag.
 
 If that buffer is an indirect buffer, its base buffer is used instead.
 That is, indirect buffers use their base buffer's parsers.  If the
@@ -450,11 +453,17 @@ internal parser list.  Every time a change is made to the 
buffer,
 Emacs updates parsers in this list so they can update their syntax
 tree incrementally.
 
-@defun treesit-parser-list &optional buffer
-This function returns the parser list of @var{buffer}.  If
-@var{buffer} is @code{nil} or omitted, it defaults to the current
-buffer.  If that buffer is an indirect buffer, its base buffer is used
-instead.  That is, indirect buffers use their base buffer's parsers.
+@defun treesit-parser-list &optional buffer language tag
+This function returns the parser list of @var{buffer}, filtered by
+@var{language} and @var{tag}.  If @var{buffer} is @code{nil} or
+omitted, it defaults to the current buffer.  If that buffer is an
+indirect buffer, its base buffer is used instead.  That is, indirect
+buffers use their base buffer's parsers.
+
+If @var{language} is non-@var{nil}, only include parsers for that
+language, and only include parsers with @var{tag}.  @var{tag} defaults
+to @code{nil}.  If @var{tag} is @code{t}, include parsers in the
+returned list regardless of their tag.
 @end defun
 
 @defun treesit-parser-delete parser
@@ -1706,6 +1715,20 @@ If @var{query} is a tree-sitter query, it should be 
preceded by two
 specifies the embedded language, and the @code{:host} keyword
 specifies the host language.
 
+@cindex local parser
+If the query is given the @code{:local} keyword whose value is
+@code{t}, the range set by this query has a dedicated local parser;
+otherwise the range shares a parser with other ranges for the same
+language.
+
+By default, a parser sees its ranges as a continuum, rather than
+treating them as separate independent segments.  Therefore, if the
+embedded ranges are semantically independent segments, they should be
+processed by local parsers, described below.
+
+Local parser set to a range can be retrieved by
+@code{treesit-local-parsers-at} and @code{treesit-local-parsers-on}.
+
 @code{treesit-update-ranges} uses @var{query} to figure out how to set
 the ranges for parsers for the embedded language.  It queries
 @var{query} in a host language parser, computes the ranges which the
@@ -1741,6 +1764,25 @@ language of the buffer text at @var{pos}.  This variable 
is used by
 @code{treesit-language-at}.
 @end defvar
 
+@defun treesit-local-parsers-at &optional pos language
+This function returns all the local parsers at @var{pos} in the
+current buffer.  @var{pos} defaults to point.
+
+Local parsers are those which only parse a limited region marked by an
+overlay with a non-@code{nil} @code{treesit-parser} property.  If
+@var{language} is non-@code{nil}, only return parsers for that
+language.
+@end defun
+
+@defun treesit-local-parsers-on &optional beg end language
+This function is the same as @code{treesit-local-parsers-at}, but it
+returns the local parsers in the range between @var{beg} and @var{end}
+instead of at point.
+
+@var{beg} and @var{end} default to the entire accessible portion of
+the buffer.
+@end defun
+
 @node Tree-sitter Major Modes
 @section Developing major modes with tree-sitter
 @cindex major mode, developing with tree-sitter
@@ -1835,6 +1877,8 @@ add-log functions used by @code{add-log-current-defun}.
 If @code{treesit-simple-imenu-settings} (@pxref{Imenu}) is
 non-@code{nil}, it sets up Imenu.
 @end itemize
+
+@c TODO: Add treesit-thing-settings stuff once we finalize it.
 @end defun
 
 For more information on these built-in tree-sitter features,
diff --git a/doc/lispref/positions.texi b/doc/lispref/positions.texi
index 98cf81be107..5ff89e992d9 100644
--- a/doc/lispref/positions.texi
+++ b/doc/lispref/positions.texi
@@ -872,36 +872,25 @@ defuns.  If the value is @code{nested}, navigation 
functions recognize
 nested defuns.
 @end defvar
 
-@defvar treesit-sentence-type-regexp
-The value of this variable is a regexp matching the node type of sentence
-nodes.  (For ``node'' and ``node type'', @pxref{Parsing Program Source}.)
-@end defvar
-
 @findex treesit-forward-sentence
 @findex forward-sentence
 @findex backward-sentence
 If Emacs is compiled with tree-sitter, it can use the tree-sitter
 parser information to move across syntax constructs.  Since what
 exactly is considered a sentence varies between languages, a major
-mode should set @code{treesit-sentence-type-regexp} to determine that.
+mode should set @code{treesit-thing-settings} to determine that.
 Then the mode can get navigation-by-sentence functionality for free,
 by using @code{forward-sentence} and
 @code{backward-sentence}(@pxref{Moving by Sentences,,, emacs, The
 extensible self-documenting text editor}).
 
-@defvar treesit-sexp-type-regexp
-The value of this variable is a regexp matching the node type of sexp
-nodes.  (For ``node'' and ``node type'', @pxref{Parsing Program
-Source}.)
-@end defvar
-
 @findex treesit-forward-sexp
 @findex forward-sexp@r{, and tree-sitter}
 @findex backward-sexp@r{, and tree-sitter}
 If Emacs is compiled with tree-sitter, it can use the tree-sitter
 parser information to move across syntax constructs.  Since what
 exactly is considered a sexp varies between languages, a major mode
-should set @code{treesit-sexp-type-regexp} to determine that.  Then
+should set @code{treesit-thing-settings} to determine that.  Then
 the mode can get navigation-by-sexp functionality for free, by using
 @code{forward-sexp} and @code{backward-sexp}(@pxref{Moving by
 Sentences,,, emacs, The extensible self-documenting text editor}).
diff --git a/doc/lispref/processes.texi b/doc/lispref/processes.texi
index e055d043c91..df5e2139237 100644
--- a/doc/lispref/processes.texi
+++ b/doc/lispref/processes.texi
@@ -134,7 +134,7 @@ but all the 3 primitives allow optionally to direct the 
standard error
 stream to a different destination.
 
 @cindex program arguments
-  All three of the subprocess-creating functions allow to specify
+  All three of the subprocess-creating functions allow specifying
 command-line arguments for the process to run. For @code{call-process}
 and @code{call-process-region}, these come in the form of a
 @code{&rest} argument, @var{args}.  For @code{make-process}, both the
@@ -520,7 +520,7 @@ This user option indicates whether a call of 
@code{process-file}
 returns a string describing the signal interrupting a remote process.
 
 When a process returns an exit code greater than 128, it is
-interpreted as a signal.  @code{process-file} requires to return a
+interpreted as a signal.  @code{process-file} requires returning a
 string describing this signal.
 
 Since there are processes violating this rule, returning exit codes
@@ -681,7 +681,7 @@ This function is the basic low-level primitive for starting
 asynchronous subprocesses.  It returns a process object representing
 the subprocess.  Compared to the more high-level @code{start-process},
 described below, it takes keyword arguments, is more flexible, and
-allows to specify process filters and sentinels in a single call.
+enables you to specify process filters and sentinels in a single call.
 
 The arguments @var{args} are a list of keyword/argument pairs.
 Omitting a keyword is always equivalent to specifying it with value
diff --git a/doc/lispref/searching.texi b/doc/lispref/searching.texi
index 66b33316faa..cb269fcacc5 100644
--- a/doc/lispref/searching.texi
+++ b/doc/lispref/searching.texi
@@ -1972,6 +1972,17 @@ advice, don't be afraid of performing the matching in 
multiple
 function calls, each using a simpler regexp where backtracking can
 more easily be contained.
 
+@defun re--describe-compiled regexp &optional raw
+To help diagnose problems in your regexps or in the regexp engine
+itself, this function returns a string describing the compiled
+form of @var{regexp}.  To make sense of it, it can be necessary
+to read at least the description of the @code{re_opcode_t} type in the
+@code{src/regex-emacs.c} file in Emacs' source code.
+
+It is currently able to give a meaningful description only if Emacs
+was compiled with @code{--enable-checking}.
+@end defun
+
 @node Regexp Search
 @section Regular Expression Searching
 @cindex regular expression searching
diff --git a/doc/lispref/sequences.texi b/doc/lispref/sequences.texi
index dd5b723b479..c9c6bb31350 100644
--- a/doc/lispref/sequences.texi
+++ b/doc/lispref/sequences.texi
@@ -1645,7 +1645,8 @@ Refers to the element for character @var{char}
 
 @item @code{(@var{from} . @var{to})}
 A cons cell refers to all the characters in the inclusive range
-@samp{[@var{from}..@var{to}]}.
+@samp{[@var{from}..@var{to}]}.  In this case, the function returns the
+value for the character specified by @var{from}.
 @end table
 @end defun
 
diff --git a/doc/lispref/strings.texi b/doc/lispref/strings.texi
index 3d86a87516b..7d11db49def 100644
--- a/doc/lispref/strings.texi
+++ b/doc/lispref/strings.texi
@@ -754,7 +754,7 @@ ignores case differences.
 @defun string-search needle haystack &optional start-pos
 Return the position of the first instance of @var{needle} in
 @var{haystack}, both of which are strings.  If @var{start-pos} is
-non-@code{nil}, start searching from that position in @var{needle}.
+non-@code{nil}, start searching from that position in @var{haystack}.
 Return @code{nil} if no match was found.
 This function only considers the characters in the strings when doing
 the comparison; text properties are ignored.  Matching is always
diff --git a/doc/lispref/symbols.texi b/doc/lispref/symbols.texi
index 34db0caf3a8..1f3b677d7fb 100644
--- a/doc/lispref/symbols.texi
+++ b/doc/lispref/symbols.texi
@@ -783,10 +783,15 @@ Symbol forms whose names start with @samp{#_} are not 
transformed.
 
 @cindex bare symbol
 A @dfn{symbol with position} is a symbol, the @dfn{bare symbol},
-together with an unsigned integer called the @dfn{position}.  These
-objects are intended for use by the byte compiler, which records in
-them the position of each symbol occurrence and uses those positions
-in warning and error messages.
+together with an unsigned integer called the @dfn{position}.  Symbols
+with position don't themselves have entries in the obarray (though
+their bare symbols do; @pxref{Creating Symbols}).
+
+Symbols with position are for the use of the byte compiler, which
+records in them the position of each symbol occurrence and uses those
+positions in warning and error messages.  They shouldn't normally be
+used otherwise.  Doing so can cause unexpected results with basic
+Emacs functions such as @code{eq} and @code{equal}.
 
 The printed representation of a symbol with position uses the hash
 notation outlined in @ref{Printed Representation}.  It looks like
@@ -798,11 +803,20 @@ the compiled Lisp file.
 
 For most purposes, when the flag variable
 @code{symbols-with-pos-enabled} is non-@code{nil}, symbols with
-positions behave just as bare symbols do.  For example, @samp{(eq
-#<symbol foo at 12345> foo)} has a value @code{t} when that variable
-is set (but @code{nil} when it isn't set).  Most of the time in Emacs this
-variable is @code{nil}, but the byte compiler binds it to @code{t}
-when it runs.
+positions behave just as their bare symbols would.  For example,
+@samp{(eq #<symbol foo at 12345> foo)} has a value @code{t} when the
+variable is set; likewise, @code{equal} will treat a symbol with
+position argument as its bare symbol.
+
+When @code{symbols-with-pos-enabled} is @code{nil}, any symbols with
+position continue to exist, but do not behave as symbols, or have the
+other useful properties outlined in the previous paragraph.  @code{eq}
+returns @code{t} when given identical arguments, and @code{equal}
+returns @code{t} when given arguments with @code{equal} components.
+
+Most of the time in Emacs @code{symbols-with-pos-enabled} is
+@code{nil}, but the byte compiler and the native compiler bind it to
+@code{t} when they run.
 
 Typically, symbols with position are created by the byte compiler
 calling the reader function @code{read-positioning-symbols}
@@ -810,17 +824,17 @@ calling the reader function 
@code{read-positioning-symbols}
 @code{position-symbol}.
 
 @defvar symbols-with-pos-enabled
-When this variable is non-@code{nil}, symbols with position behave
+When this variable is non-@code{nil}, a symbol with position behaves
 like the contained bare symbol.  Emacs runs a little more slowly in
 this case.
 @end defvar
 
 @defvar print-symbols-bare
-When bound to non-@code{nil}, the Lisp printer prints only the bare symbol of
-a symbol with position, ignoring the position.
+When bound to non-@code{nil}, the Lisp printer prints only the bare
+symbol of a symbol with position, ignoring the position.
 @end defvar
 
-@defun symbol-with-pos-p symbol.
+@defun symbol-with-pos-p symbol
 This function returns @code{t} if @var{symbol} is a symbol with
 position, @code{nil} otherwise.
 @end defun
diff --git a/doc/lispref/text.texi b/doc/lispref/text.texi
index 4ffd5126e7e..45436cd2bc6 100644
--- a/doc/lispref/text.texi
+++ b/doc/lispref/text.texi
@@ -2808,22 +2808,21 @@ indentation in the text.
 @deffn Command back-to-indentation
 @comment !!SourceFile simple.el
 This command moves point to the first non-whitespace character in the
-current line (which is the line in which point is located).  It returns
-@code{nil}.
+current line (which is the line in which point is located).
 @end deffn
 
 @deffn Command backward-to-indentation &optional arg
 @comment !!SourceFile simple.el
 This command moves point backward @var{arg} lines and then to the
-first nonblank character on that line.  It returns @code{nil}.
-If @var{arg} is omitted or @code{nil}, it defaults to 1.
+first nonblank character on that line.  If @var{arg} is omitted or
+@code{nil}, it defaults to 1.
 @end deffn
 
 @deffn Command forward-to-indentation &optional arg
 @comment !!SourceFile simple.el
 This command moves point forward @var{arg} lines and then to the first
-nonblank character on that line.  It returns @code{nil}.
-If @var{arg} is omitted or @code{nil}, it defaults to 1.
+nonblank character on that line.  If @var{arg} is omitted or
+@code{nil}, it defaults to 1.
 @end deffn
 
 @node Case Changes
diff --git a/doc/lispref/variables.texi b/doc/lispref/variables.texi
index 93930d17587..dea35a04d4f 100644
--- a/doc/lispref/variables.texi
+++ b/doc/lispref/variables.texi
@@ -678,15 +678,15 @@ which are being phased out.)
 it as safe or risky; see @ref{File Local Variables}.
 
   When defining and initializing a variable that holds a complicated
-value (such as a keymap with bindings in it), it's best to put the
+value (such as a syntax table for a major mode), it's best to put the
 entire computation of the value into the @code{defvar}, like this:
 
 @example
-(defvar my-mode-map
-  (let ((map (make-sparse-keymap)))
-    (keymap-set map "C-c C-a" 'my-command)
+(defvar my-major-mode-syntax-table
+  (let ((table (make-syntax-table)))
+    (modify-syntax-entry ?# "<" table)
     @dots{}
-    map)
+    table)
   @var{docstring})
 @end example
 
@@ -696,9 +696,9 @@ loading the file, the variable is either still 
uninitialized or
 initialized properly, never in-between.  If it is still uninitialized,
 reloading the file will initialize it properly.  Second, reloading the
 file once the variable is initialized will not alter it; that is
-important if the user has run hooks to alter part of the contents
-(such as, to rebind keys).  Third, evaluating the @code{defvar} form
-with @kbd{C-M-x} will reinitialize the map completely.
+important if the user has changed its value.  Third, evaluating the
+@code{defvar} form with @kbd{C-M-x} will reinitialize the variable
+completely.
 
 @node Accessing Variables
 @section Accessing Variable Values
@@ -1891,7 +1891,7 @@ This makes its global value shadowed by the binding;
 @code{default-value} will then return the value from that binding, not
 the global value, and @code{set-default} will be prevented from
 setting the global value (it will change the let-bound value instead).
-The following two functions allow to reference the global value even
+The following two functions allow referencing the global value even
 if it's shadowed by a let-binding.
 
 @cindex top-level default value
diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi
index a49e63e49de..22c1b307252 100644
--- a/doc/lispref/windows.texi
+++ b/doc/lispref/windows.texi
@@ -2847,8 +2847,8 @@ the left edge coordinate of the reference window.  Its 
left edge
 coordinate would equal the left edge coordinate of the frame's new
 root window.
 
-Four special values for @code{direction} entries allow to implicitly
-specify the selected frame's main window as the reference window:
+Four special values for @code{direction} entries allow implicitly
+specifying the selected frame's main window as the reference window:
 @code{leftmost}, @code{top}, @code{rightmost} and @code{bottom}.  This
 means that instead of, for example, @w{@code{(direction . left)
 (window . main)}} one can just specify @w{@code{(direction
@@ -3759,7 +3759,7 @@ preferred way regardless of whether the display is also 
guided by an
 @code{display-buffer-alist} differs from customizing
 @code{display-buffer-base-action} in two major aspects: it is stronger
 because it overrides the @var{action} argument of
-@code{display-buffer}, and it allows to explicitly specify the
+@code{display-buffer}, and it enables you to explicitly specify the
 affected buffers.  In fact, displaying other buffers is not affected
 in any way by a customization for @file{*foo*}.  For example,
 
@@ -4596,7 +4596,7 @@ window and a number of side windows surrounding that main 
window.  The
 main window is either a ``normal'' live window or specifies the area
 containing all the normal windows.
 
-   In their most simple form of use, side windows allow to display
+   In their most simple form of use, side windows allow displaying
 specific buffers always in the same area of a frame.  Hence they can
 be regarded as a generalization of the concept provided by
 @code{display-buffer-at-bottom} (@pxref{Buffer Display Action
@@ -6150,11 +6150,11 @@ up-to-date.
 @section Mouse Window Auto-selection
 @cindex window auto-selection
 @cindex auto-selection of window
-The following option allows to automatically select the window under the
-mouse pointer.  This accomplishes a policy similar to that of window
-managers that give focus to a frame (and thus trigger its subsequent
-selection) whenever the mouse pointer enters its window-system window
-(@pxref{Input Focus}).
+The following option enables automatically selecting the window under
+the mouse pointer.  This accomplishes a policy similar to that of
+window managers that give focus to a frame (and thus trigger its
+subsequent selection) whenever the mouse pointer enters its
+window-system window (@pxref{Input Focus}).
 
 @defopt mouse-autoselect-window
 If this variable is non-@code{nil}, Emacs will try to automatically
@@ -6188,7 +6188,7 @@ and never deselects the active minibuffer window.
 
 Mouse auto-selection can be used to emulate a focus follows mouse policy
 for child frames (@pxref{Child Frames}) which usually are not tracked by
-the window manager.  This requires to set the value of
+the window manager.  This requires setting the value of
 @code{focus-follows-mouse} (@pxref{Input Focus}) to a non-@code{nil}
 value.  If the value of @code{focus-follows-mouse} is @code{auto-raise},
 entering a child frame with the mouse will raise it automatically above
@@ -6341,7 +6341,7 @@ and/or two columns.
 @end defun
 
 The functions @code{window-state-get} and @code{window-state-put} also
-allow to exchange the contents of two live windows.  The following
+allow exchanging the contents of two live windows.  The following
 function does precisely that:
 
 @deffn Command window-swap-states &optional window-1 window-2 size
diff --git a/doc/misc/autotype.texi b/doc/misc/autotype.texi
index 4110b60a774..8617db16ca0 100644
--- a/doc/misc/autotype.texi
+++ b/doc/misc/autotype.texi
@@ -276,10 +276,10 @@ empty file is visited.  This is accomplished by putting
 @code{auto-insert-alist}.  The @sc{car} of each element of this list
 is either a mode name, making the element applicable when a buffer is
 in that mode, or a string, which is a regexp matched against a
-buffer's file name (the latter allows to distinguish between different
-kinds of files that have the same mode in Emacs).  The @sc{car} of an
-element may also be a cons cell, consisting of mode name or regexp, as
-above, and an additional descriptive string.
+buffer's file name (the latter enables you to distinguish between
+different kinds of files that have the same mode in Emacs).  The
+@sc{car} of an element may also be a cons cell, consisting of mode
+name or regexp, as above, and an additional descriptive string.
 
   When a matching element is found, the @sc{cdr} says what to do.  It may
 be a string, which is a file name, whose contents are to be inserted, if
diff --git a/doc/misc/efaq-w32.texi b/doc/misc/efaq-w32.texi
index 71d4b4e1fc2..de9354500df 100644
--- a/doc/misc/efaq-w32.texi
+++ b/doc/misc/efaq-w32.texi
@@ -1168,15 +1168,15 @@ binary and the Ctrl-M characters are significant.
 @subsection CR/LF translation by file system
 @cindex line ends, determining by filesystem
 @cindex binary files, determining by filesystem
-@vindex untranslated-filesystem-list
-@findex add-untranslated-filesystem
-@findex remove-untranslated-filesystem
+@vindex w32-untranslated-filesystem-list
+@findex w32-add-untranslated-filesystem
+@findex w32-remove-untranslated-filesystem
 
-The variable @code{untranslated-filesystem-list} defines whole
+The variable @code{w32-untranslated-filesystem-list} defines whole
 directory trees that should not have CR/LF autodetection performed on
 them.  The list can be manipulated with the functions
-@code{add-untranslated-filesystem} and
-@code{remove-untranslated-filesystem}.  With auto-detection in
+@code{w32-add-untranslated-filesystem} and
+@code{w32-remove-untranslated-filesystem}.  With auto-detection in
 recent versions of Emacs, this is seldom useful for existing files,
 but can still be used to influence the choice of line ends for newly
 created files.
diff --git a/doc/misc/efaq.texi b/doc/misc/efaq.texi
index 0a0c375d273..2fc8e60d400 100644
--- a/doc/misc/efaq.texi
+++ b/doc/misc/efaq.texi
@@ -1121,8 +1121,8 @@ feature, when you need to insert a single special 
character.
 allows selection of one of the previous kills.
 
 @item
-New minor mode @code{repeat-mode} allows to repeat commands with fewer
-keystrokes.
+New minor mode @code{repeat-mode} enables repeating commands with
+fewer keystrokes.
 
 @item
 Among the many internal changes in this release, we would like to
@@ -3301,12 +3301,12 @@ Emacs has an inherent fixed limitation on the size of 
buffers.  This
 limit is stricter than the maximum size of objects supported by other
 programs on the same architecture.
 
-The maximum buffer size on 32-bit machines is 512 MBytes.  If Emacs
-was built using the @code{--with-wide-int} flag, the maximum buffer
-size on 32-bit machines is 2 GB.
+The maximum buffer size on 64-bit machines is 2.3 exabytes
+(@code{most-positive-fixnum}).
 
-Emacs compiled on a 64-bit machine can handle much larger buffers; up
-to @code{most-positive-fixnum} (2.3 exabytes).
+Emacs compiled on a 32-bit machine can handle buffers up to 512
+MBytes.  If Emacs was built using the @code{--with-wide-int} flag, the
+maximum buffer size on 32-bit machines is 2 GB.
 
 Due to things like decoding of multibyte characters, you can only
 visit files with a size that is roughly half the buffer size limit.
@@ -3839,13 +3839,28 @@ description of what they do and how they should be used.
 
 The easiest way to add more features to your Emacs is to use the
 command @kbd{M-x list-packages}.  This contacts the
-@uref{https://elpa.gnu.org, GNU ELPA} (``Emacs Lisp Package Archive'')
-server and fetches the list of additional packages that it offers.
-These are GNU packages that are available for use with Emacs, but are
-distributed separately from Emacs itself, for reasons of space, etc.
-You can browse the resulting @file{*Packages*} buffer to see what is
-available, and then Emacs can automatically download and install the
-packages that you select.  @xref{Packages,,, emacs, The GNU Emacs Manual}.
+@uref{https://elpa.gnu.org, GNU ELPA} and
+@uref{https://elpa.nongnu.org, NonGNU ELPA} (``Emacs Lisp Package
+Archive'') servers and fetches the list of additional packages that
+they offer.  You can browse the resulting @file{*Packages*} buffer to
+see what is available, and then Emacs can automatically download and
+install the packages that you select.  @xref{Packages,,, emacs, The
+GNU Emacs Manual}.
+
+GNU ELPA contains GNU packages that are available for use with Emacs,
+but are distributed separately from Emacs itself, for reasons of
+space, etc.  NonGNU ELPA contains a selection of third-party packages
+that can not be included in GNU ELPA because their copyright has not
+yet been assigned to the Free Software Foundation.@footnote{For more
+information, see @uref{https://www.gnu.org/licenses/why-assign.html,
+Why the FSF Gets Copyright Assignments from Contributors}.}
+
+The @uref{https://lists.gnu.org/mailman/listinfo/gnu-emacs-sources,
+GNU Emacs sources mailing list} is automatically sent an email when a
+new version of a GNU ELPA or NonGNU ELPA package is
+released.@footnote{It used to be an official place where people could
+post or announce their extensions to Emacs.  That is still allowed,
+but exceedingly rare these days.}
 
 There are other Emacs Lisp package archives.  To use additional
 archives, you can customize the @code{package-archives} variable.
@@ -3855,23 +3870,19 @@ correctness and safety of the code, or they may give 
only cursory
 attention.
 
 Also, packages hosted on these other archives may encourage or require
-you to install and use other nonfree programs.  Unless you can verify
+you to install and use nonfree programs.  Unless you can verify
 that a package is free software, and that it functions without
 installing any nonfree software, we recommend for your freedom's sake
 that you stay away from it.
 
-The @uref{https://lists.gnu.org/mailman/listinfo/gnu-emacs-sources,
-GNU Emacs sources mailing list} is an official place where people can
-post or announce their extensions to Emacs.
-
 The @uref{https://emacswiki.org, Emacs Wiki} contains pointers to some
 additional extensions.  @uref{https://wikemacs.org, WikEmacs} is an
 alternative wiki for Emacs.
 
 It is impossible for us to list here all the sites that offer Emacs
 Lisp packages.  If you are interested in a specific feature, then
-after checking Emacs itself and GNU ELPA, a web search is often the
-best way to find results.
+after checking Emacs itself, GNU ELPA, and NonGNU ELPA, a web search
+is often the best way to find results.
 
 @node Spell-checkers
 @section Spell-checkers
diff --git a/doc/misc/eglot.texi b/doc/misc/eglot.texi
index 6eb212ca841..3c1d44cd363 100644
--- a/doc/misc/eglot.texi
+++ b/doc/misc/eglot.texi
@@ -99,6 +99,7 @@ This manual documents how to configure, use, and customize 
Eglot.
 * Using Eglot::                 Important Eglot commands and variables.
 * Customizing Eglot::           Eglot customization and advanced features.
 * Advanced server configuration::  Fine-tune a specific language server
+* Extending Eglot::             Writing Eglot extensions in Elisp
 * Troubleshooting Eglot::       Troubleshooting and reporting bugs.
 * GNU Free Documentation License::  The license for this manual.
 * Index::
@@ -831,12 +832,14 @@ last buffer managed by it is killed.  @xref{Shutting Down 
LSP Servers}.
 The default is @code{nil}; if you want to shut down a server, use
 @kbd{M-x eglot-shutdown} (@pxref{Eglot Commands}).
 
-@item eglot-confirm-server-initiated-edits
+@item eglot-confirm-server-edits
 Various Eglot commands and code actions result in the language server
 sending editing commands to Emacs.  If this option's value is
-non-@code{nil} (the default), Eglot will ask for confirmation before
-performing edits initiated by the server or edits whose scope affects
-buffers other than the one where the user initiated the request.
+non-@code{nil}, Eglot will ask for confirmation before performing
+edits proposed by the language server.  This option's value can be
+crafted to require this confirmation for specific commands or only
+when the edit affects files not yet visited by the user.  Consult this
+option's docstring for more information.
 
 @item eglot-ignored-server-capabilities
 This variable's value is a list of language server capabilities that
@@ -1262,6 +1265,154 @@ is serialized by Eglot to the following JSON text:
 @}
 @end example
 
+@node Extending Eglot
+@chapter Extending Eglot
+
+Sometimes it may be useful to extend existing Eglot functionality
+using Elisp its public methods.  A good example of when this need may
+arise is adding support for a custom LSP protocol extension only
+implemented by a specific server.
+
+The best source of documentation for this is probably Eglot source
+code itself, particularly the section marked ``API''.
+
+Most of the functionality is implemented with Common-Lisp style
+generic functions (@pxref{Generics,,,eieio,EIEIO}) that can be easily
+extended or overridden.  The Eglot code itself is an example on how to
+do this.
+
+The following is a relatively simple example that adds support for the
+@code{inactiveRegions} experimental feature introduced in version 17
+of the @command{clangd} C/C++ language server++.
+
+Summarily, the feature works by first having the server detect the
+Eglot's advertisement of the @code{inactiveRegions} client capability
+during startup, whereupon the language server will report a list of
+regions of inactive code for each buffer.  This is usually code
+surrounded by C/C++ @code{#ifdef} macros that the preprocessor removes
+based on compile-time information.
+
+The language server reports the regions by periodically sending a
+@code{textDocument/inactiveRegions} notification for each managed
+buffer (@pxref{Eglot and Buffers}). Normally, unknown server
+notifications are ignored by Eglot, but we're going change that.
+
+Both the announcement of the client capability and the handling of the
+new notification is done by adding methods to generic functions.
+
+@itemize @bullet
+@item
+The first method extends @code{eglot-client-capabilities} using a
+simple heuristic to detect if current server is @command{clangd} and
+enables the @code{inactiveRegion} capability.
+
+@lisp
+(cl-defmethod eglot-client-capabilities :around (server)
+  (let ((base (cl-call-next-method)))
+    (when (cl-find "clangd" (process-command
+                              (jsonrpc--process server))
+                   :test #'string-match)
+      (setf (cl-getf (cl-getf base :textDocument)
+                     :inactiveRegionsCapabilities)
+            '(:inactiveRegions t)))
+    base))
+@end lisp
+
+Notice we use an internal function of the @code{jsonrpc.el} library,
+and a regexp search to detect @command{clangd}.  An alternative would
+be to define a new EIEIO subclass of @code{eglot-lsp-server}, maybe
+called @code{eglot-clangd}, so that the method would be simplified:
+
+@lisp
+(cl-defmethod eglot-client-capabilities :around ((_s eglot-clangd))
+  (let ((base (cl-call-next-method)))
+    (setf (cl-getf (cl-getf base :textDocument)
+                     :inactiveRegionsCapabilities)
+            '(:inactiveRegions t))))
+@end lisp
+
+However, this would require that users tweak
+@code{eglot-server-program} to tell Eglot instantiate such sub-classes
+instead of the generic @code{eglot-lsp-server} (@pxref{Setting Up LSP
+Servers}). For the purposes of this particular demonstration, we're
+going to use the more hacky regexp route which doesn't require that.
+
+Note, however, that detecting server versions before announcing new
+capabilities is generally not needed, as both server and client are
+required by LSP to ignore unknown capabilities advertised by their
+counterparts.
+
+@item
+The second method implements @code{eglot-handle-notification} to
+process the server notification for the LSP method
+@code{textDocument/inactiveRegions}.  For each region received it
+creates an overlay applying the @code{shadow} face to the region.
+Overlays are recreated every time a new notification of this kind is
+received.
+
+To learn about how @command{clangd}'s special JSONRPC notification
+message is structured in detail you could consult that server's
+documentation.  Another possibility is to evaluate the first
+capability-announcing method, reconnect to the server and peek in the
+events buffer (@pxref{Eglot Commands, eglot-events-buffer}).  You
+could find something like:
+
+@lisp
+[server-notification] Mon Sep  4 01:10:04 2023:
+(:jsonrpc "2.0" :method "textDocument/inactiveRegions" :params
+          (:textDocument
+           (:uri "file:///path/to/file.cpp")
+           :regions
+           [(:start (:character 0 :line 18)
+             :end (:character 58 :line 19))
+            (:start (:character 0 :line 36)
+             :end (:character 1 :line 38))]))
+@end lisp
+
+This reveals that the @code{textDocument/inactiveRegions} notification
+contains a @code{:textDocument} property to designate the managed
+buffer and an array of LSP regions under the @code{:regions} property.
+Notice how the message (originally in JSON format), is represented as
+Elisp plists (@pxref{JSONRPC objects in Elisp}).
+
+The Eglot generic function machinery will automatically destructure
+the incoming message, so these two properties can simply be added to
+the new method's lambda list as @code{&key} arguments.  Also, the
+@code{eglot-uri-to-path} and @code{eglot-range-region} may be used to
+easily parse the LSP @code{:uri} and @code{:start ... :end ...}
+objects to obtain Emacs objects for file names and positions.
+
+The remainder of the implementation consists of standard Elisp
+techniques to loop over arrays, manage buffers and overlays.
+
+@lisp
+(defvar-local eglot-clangd-inactive-region-overlays '())
+
+(cl-defmethod eglot-handle-notification
+  (_server (_method (eql textDocument/inactiveRegions))
+           &key regions textDocument &allow-other-keys)
+  (if-let* ((path (expand-file-name (eglot-uri-to-path
+                                     (cl-getf textDocument :uri))))
+            (buffer (find-buffer-visiting path)))
+      (with-current-buffer buffer
+        (mapc #'delete-overlay eglot-clangd-inactive-region-overlays)
+        (cl-loop
+         for r across regions
+         for (beg . end) = (eglot-range-region r)
+         for ov = (make-overlay beg end)
+         do
+         (overlay-put ov 'face 'shadow)
+         (push ov eglot-clangd-inactive-region-overlays)))))
+@end lisp
+
+@end itemize
+
+After evaluating these two additions and reconnecting to the
+@command{clangd} language server (version 17), the result will be that
+all the inactive code in the buffer will be nicely grayed out using
+the LSP server knowledge about current compile time preprocessor
+defines.
+
 @node Troubleshooting Eglot
 @chapter Troubleshooting Eglot
 @cindex troubleshooting Eglot
diff --git a/doc/misc/erc.texi b/doc/misc/erc.texi
index 6d7785a9b54..3297d8b17f0 100644
--- a/doc/misc/erc.texi
+++ b/doc/misc/erc.texi
@@ -1803,10 +1803,11 @@ Observe that ERC supplies the names of buffer-display 
options as
 the symbols @samp{erc-tls} or @samp{url}, the full lineup of which are
 listed below.
 
-In this second example, the user writes three predicates that somewhat
-resemble the ``@code{display-buffer}-like'' function above.  These too
-look for @var{action} alist keys sharing the names of buffer-display
-options (and, in one case, a module's minor mode).
+In this second example, for Emacs 29 and above, the user writes three
+predicates that somewhat resemble the ``@code{display-buffer}-like''
+function above.  These too look for @var{action} alist keys sharing
+the names of ERC's buffer-display options (and, in one case, a
+module's minor mode).
 
 @lisp
 (defun my-erc-disp-entry-p (_ action)
@@ -1821,7 +1822,7 @@ options (and, in one case, a module's minor mode).
 
 (defun my-erc-disp-chan-p (_ action)
   (or (assq 'erc-autojoin-mode action)
-      (and (memq (cdr (assq 'erc-buffer-display alist)) 'JOIN)
+      (and (eq (cdr (assq 'erc-buffer-display action)) 'JOIN)
            (member (erc-default-target) '("#emacs" "#fsf")))))
 @end lisp
 
diff --git a/doc/misc/ert.texi b/doc/misc/ert.texi
index a6b62a058f2..f4a072cf2bc 100644
--- a/doc/misc/ert.texi
+++ b/doc/misc/ert.texi
@@ -658,11 +658,30 @@ versions, specific architectures, etc.:
 @cindex skipping tests
 @cindex test preconditions
 @cindex preconditions of a test
+@findex skip-when
+@findex skip-unless
 Sometimes, it doesn't make sense to run a test due to missing
 preconditions.  A required Emacs feature might not be compiled in, the
 function to be tested could call an external binary which might not be
-available on the test machine, you name it.  In this case, the macro
-@code{skip-unless} could be used to skip the test:
+available on the test machine, you name it.  In this case, the macros
+@code{skip-when} or @code{skip-unless} could be used to skip the
+test.@footnote{The @code{skip-when} macro was added in Emacs 30.1.  If
+you need your tests to be compatible with older versions of Emacs, use
+@code{skip-unless} instead.}
+
+@noindent
+For example, this test is skipped on MS-Windows and macOS:
+
+@lisp
+(ert-deftest test-gnu-linux ()
+  "A test that is not relevant on MS-Windows and macOS."
+  (skip-when (memq system-type '(windows-nt ns))
+  ...))
+@end lisp
+
+@noindent
+This test is skipped if the feature @samp{dbusbind} is not present in
+the running Emacs:
 
 @lisp
 (ert-deftest test-dbus ()
diff --git a/doc/misc/eshell.texi b/doc/misc/eshell.texi
index f8f60bae13a..cc94f610615 100644
--- a/doc/misc/eshell.texi
+++ b/doc/misc/eshell.texi
@@ -234,6 +234,9 @@ the foreground.  That said, background processes invoked 
from Eshell
 can be controlled the same way as any other background process in
 Emacs.
 
+If a command exits abnormally, Eshell will display its exit code
+in the next prompt.
+
 @subsection Command form
 Command form looks much the same as in other shells.  A command
 consists of arguments separated by spaces; the first argument is the
@@ -334,7 +337,8 @@ As with other shells, you can escape special characters and 
spaces by
 prefixing the character with a backslash (@samp{\}), or by surrounding
 the string with apostrophes (@samp{''}) or double quotes (@samp{""}).
 This is needed especially for file names with special characters like
-pipe (@samp{|}), which could be part of remote file names.
+pipe (@samp{|}) or square brackets (@samp{[} or @samp{]}), which could
+be part of remote file names.
 
 When you escape a character with @samp{\} outside of any quotes, the
 result is the literal character immediately following it.  For
@@ -619,10 +623,23 @@ environment.
 @item eshell-debug
 @cmindex eshell-debug
 Toggle debugging information for Eshell itself.  You can pass this
-command the argument @code{errors} to enable/disable Eshell trapping
-errors when evaluating commands, or the argument @code{commands} to
-show/hide command execution progress in the buffer @code{*eshell last
-cmd*}.
+command one or more of the following arguments:
+
+@itemize @bullet
+
+@item
+@code{error}, to enable/disable Eshell trapping errors when
+evaluating commands;
+
+@item
+@code{form}, to show/hide Eshell command form manipulation in the
+buffer @code{*eshell last cmd*}; or
+
+@item
+@code{process}, to show/hide external process events in the buffer
+@code{*eshell last cmd*}.
+
+@end itemize
 
 @item exit
 @cmindex exit
@@ -1368,9 +1385,15 @@ Concatenate the string representation of each value.
 
 @node Dollars Expansion
 @section Dollars Expansion
-Eshell has different @code{$} expansion syntax from other shells.  There
-are some similarities, but don't let these lull you into a false sense
-of familiarity.
+Like in many other shells, you can use @code{$} expansions to insert
+various values into your Eshell invocations.  While Eshell's @code{$}
+expansion syntax has some similarities to the syntax from other
+shells, there are also many differences.  Don't let these similarities
+lull you into a false sense of familiarity.
+
+When using command form (@pxref{Invocation}), Eshell will ignore any
+leading nil values, so if @var{foo} is @code{nil}, @samp{$@var{foo}
+echo hello} is equivalent to @samp{echo hello}.
 
 @table @code
 
@@ -2545,8 +2568,6 @@ A special associate array, which can take references of 
the form
 @samp{$=[REGEXP]}.  It indexes into the directory ring.
 @end table
 
-@item Eshell scripts can't execute in the background
-
 @item Support zsh's ``Parameter Expansion'' syntax, i.e., 
@samp{$@{@var{name}:-@var{val}@}}
 
 @item Create a mode @code{eshell-browse}
diff --git a/doc/misc/flymake.texi b/doc/misc/flymake.texi
index 237f0702778..e0cb79aa546 100644
--- a/doc/misc/flymake.texi
+++ b/doc/misc/flymake.texi
@@ -59,12 +59,11 @@ types of diagnostics.
 
 To learn about using Flymake, @pxref{Using Flymake}.
 
-Flymake is designed to be easily extended to support new backends via
-an Elisp interface.  @xref{Extending Flymake}.
-
-Historically, Flymake used to accept diagnostics from a single
-backend.  Although obsolete, it is still functional.  To learn how to
-use and customize it, @pxref{The legacy Proc backend}.
+When the Emacs LSP support mode Eglot is enabled, Flymake will use
+that as an additional back-end.  @xref{Eglot Features,,, eglot, Eglot:
+The Emacs LSP Client}  Flymake is also designed to be easily extended
+to support new backends via an Elisp interface.  @xref{Extending
+Flymake}.
 
 @ifnottex
 @insertcopying
@@ -93,6 +92,10 @@ already setup this hook for you, by adding @dfn{backend 
functions} to
 @code{flymake-diagnostic-functions}.  If you know Elisp you may also
 write your own Flymake backend functions.  @xref{Backend functions}.
 
+When the Emacs LSP support mode Eglot is enabled, Flymake will use
+that as an additional back-end automatically.  @xref{Eglot Features,,,
+eglot, Eglot: The Emacs LSP Client}
+
 @menu
 * Starting Flymake::
 * Finding diagnostics::
diff --git a/doc/misc/gnus.texi b/doc/misc/gnus.texi
index f017b011d71..7ebd82c5bed 100644
--- a/doc/misc/gnus.texi
+++ b/doc/misc/gnus.texi
@@ -24526,8 +24526,8 @@ that is needed.  It can also contain @samp{(@var{addr} 
@var{string}
 @var{amount})} cells, where the @var{string} is the string to use
 (normally the email address or newsgroup name is used).
 
-@item hashcash-path
-@vindex hashcash-path
+@item hashcash-program
+@vindex hashcash-program
 Where the @code{hashcash} binary is installed.  This variable should
 be automatically set by @code{executable-find}, but if it's @code{nil}
 (usually because the @code{hashcash} binary is not in your path)
diff --git a/doc/misc/idlwave.texi b/doc/misc/idlwave.texi
index 10fc4c85c7b..5577105ab2b 100644
--- a/doc/misc/idlwave.texi
+++ b/doc/misc/idlwave.texi
@@ -3186,7 +3186,7 @@ the expression printed by IDL.
 
 @defopt idlwave-shell-output-face
 The face for @code{idlwave-shell-output-overlay}.
-Allows to choose the font, color and other properties for the most
+Allows you to choose the font, color and other properties for the most
 recent output of IDL when examining an expression."
 @end defopt
 
diff --git a/doc/misc/mh-e.texi b/doc/misc/mh-e.texi
index caee0ed2c70..75788f18f03 100644
--- a/doc/misc/mh-e.texi
+++ b/doc/misc/mh-e.texi
@@ -1479,7 +1479,6 @@ Binding} of @samp{m}.
 
 @cindex @command{emacsclient}
 @cindex @command{xbuffy}
-@cindex @samp{gnuserv}
 @cindex Unix commands, @command{emacsclient}
 @cindex Unix commands, @command{xbuffy}
 
@@ -8920,7 +8919,7 @@ Bill Wohler, August 2008
 @c LocalWords: Baushke Bcc BBN Beranek bogofilter bogofilter's
 @c LocalWords: cmd CMU contrib cron
 @c LocalWords: DesBrisay Dcc devel dir dired docstring filll forw
-@c LocalWords: GECOS Gildea Gildea's Ginnean GnuCash goto gnuserv htm
+@c LocalWords: GECOS Gildea Gildea's Ginnean GnuCash goto htm
 @c LocalWords: ImageMagick inbox ispell keychain
 @c LocalWords: Larus licensor LocalWords lookup lpr
 @c LocalWords: makeinfo mairix mbox mh mhbuild mhl mhpath mlisp
diff --git a/doc/misc/modus-themes.org b/doc/misc/modus-themes.org
index ae760624b6f..5a53426dfee 100644
--- a/doc/misc/modus-themes.org
+++ b/doc/misc/modus-themes.org
@@ -4,9 +4,9 @@
 #+language: en
 #+options: ':t toc:nil author:t email:t num:t
 #+startup: content
-#+macro: stable-version 4.2.0
-#+macro: release-date 2023-05-30
-#+macro: development-version 4.3.0-dev
+#+macro: stable-version 4.3.0
+#+macro: release-date 2023-09-19
+#+macro: development-version 4.4.0-dev
 #+macro: file @@texinfo:@file{@@$1@@texinfo:}@@
 #+macro: space @@texinfo:@: @@
 #+macro: kbd @@texinfo:@kbd{@@$1@@texinfo:}@@
@@ -581,11 +581,11 @@ Possible values:
 All theme user options take effect when a theme is loaded.  Any
 subsequent changes require the theme to be reloaded.
 
-When this variable has a non-nil value, any change made via the Custom
+When this variable has a non-~nil~ value, any change made via the Custom
 UI or related functions such as ~customize-set-variable~ and ~setopt~
 (Emacs 29), will trigger a reload automatically.
 
-With a nil value, changes to user options have no further consequences:
+With a ~nil~ value, changes to user options have no further consequences:
 the user must manually reload the theme 
([[#h:3f3c3728-1b34-437d-9d0c-b110f5b161a9][Enable and load]]).
 
 ** Option for disabling other themes while loading Modus
@@ -605,20 +605,20 @@ Possible values:
 1. ~nil~
 2. ~t~ (default)
 
-When the value is non-nil, the commands ~modus-themes-toggle~ and
+When the value is non-~nil~, the commands ~modus-themes-toggle~ and
 ~modus-themes-select~, as well as the ~modus-themes-load-theme~
 function, will disable all other themes while loading the specified
 Modus theme.  This is done to ensure that Emacs does not blend two or
 more themes: such blends lead to awkward results that undermine the
 work of the designer.
 
-When the value is nil, the aforementioned commands and function will
+When the value is ~nil~, the aforementioned commands and function will
 only disable other themes within the Modus collection.
 
 This option is provided because Emacs themes are not necessarily
 limited to colors/faces: they can consist of an arbitrary set of
 customizations.  Users who use such customization bundles must set
-this variable to a nil value.
+this variable to a ~nil~ value.
 
 ** Option for more bold constructs
 :properties:
@@ -794,7 +794,7 @@ followed by a description of the particularities:
 #+end_src
 
 The ~matches~ key refers to the highlighted characters that correspond
-to the user's input.  When its properties are nil or an empty list,
+to the user's input.  When its properties are ~nil~ or an empty list,
 matching characters in the user interface will have a bold weight and
 a colored foreground.  The list of properties may include any of the
 following symbols regardless of the order they may appear in:
@@ -810,7 +810,7 @@ following symbols regardless of the order they may appear 
in:
 
 The ~selection~ key applies to the current line or currently matched
 candidate, depending on the specifics of the user interface.  When its
-properties are nil or an empty list, it has a subtle gray background,
+properties are ~nil~ or an empty list, it has a subtle gray background,
 a bold weight, and the base foreground value for the text.  The list
 of properties it accepts is as follows (order is not significant):
 
@@ -861,10 +861,10 @@ Possible values:
 2. ~gray-background~
 3. ~tinted-background~
 
-Nil (the default) means that the block has no background of its own:
-it uses the one that applies to the rest of the buffer.  In this case,
-the delimiter lines have a gray color for their text, making them look
-exactly like all other Org properties.
+Option ~nil~ (the default) means that the block has no background of
+its own: it uses the one that applies to the rest of the buffer.
+In this case, the delimiter lines have a gray color for their text,
+making them look exactly like all other Org properties.
 
 Option ~gray-background~ applies a subtle gray background to the
 block's contents.  It also affects the begin and end lines of the
@@ -883,9 +883,9 @@ For this to take effect, the Org buffer needs to be 
restarted with
 ~org-mode-restart~.
 
 Code blocks use their major mode's fontification (syntax highlighting)
-only when the variable ~org-src-fontify-natively~ is non-nil.  While
+only when the variable ~org-src-fontify-natively~ is non-~nil~.  While
 quote/verse blocks require setting
-~org-fontify-quote-and-verse-blocks~ to a non-nil value.
+~org-fontify-quote-and-verse-blocks~ to a non-~nil~ value.
 
 [[#h:f44cc6e3-b0f1-4a5e-8a90-9e48fa557b50][Update Org block delimiter 
fontification]].
 
@@ -2730,7 +2730,7 @@ If the value is the name of another color entry in the 
palette (so a
 mapping), this function recurs until it finds the underlying color
 value.
 
-With an optional =OVERRIDES= argument as a non-nil value, it accounts
+With an optional =OVERRIDES= argument as a non-~nil~ value, it accounts
 for palette overrides.  Else it reads only the default palette.
 
 [[#h:34c7a691-19bb-4037-8d2f-67a07edab150][Option for palette overrides]].
@@ -2884,7 +2884,7 @@ above:
 #+end_src
 
 The reason we no longer provide this option is because it depends on a
-non-nil value for ~x-underline-at-descent-line~.  That variable
+non-~nil~ value for ~x-underline-at-descent-line~.  That variable
 affects ALL underlines, including those of links.  The effect is
 intrusive and looks awkard in prose.
 
@@ -3150,20 +3150,20 @@ have something like this:
 #+end_src
 
 You could then use a variant of the following to inherit from a face
-that uses the styles you want and also to preserve the properties
-applied by the ~org-todo~ face (in case there is a difference between the
-two):
+that uses the styles you want and also to preserve the attributes
+applied by the ~org-todo~ face (in case there is a difference between
+the two):
 
 #+begin_src emacs-lisp
 (setq org-todo-keyword-faces
-      '(("MEET" . '(bold org-todo))
-        ("STUDY" . '(warning org-todo))
-        ("WRITE" . '(shadow org-todo))))
+      '(("MEET" . (:inherit (bold org-todo)))
+        ("STUDY" . (:inherit (warning org-todo)))
+        ("WRITE" . (:inherit (shadow org-todo)))))
 #+end_src
 
 This will refashion the keywords you specify, while letting the other
-items in ~org-todo-keywords~ use their original styles (which are defined
-in the ~org-todo~ and ~org-done~ faces).
+items in ~org-todo-keywords~ use their original styles, which are
+defined in the ~org-todo~ and ~org-done~ faces.
 
 If you want back the defaults, try specifying just the ~org-todo~ face:
 
@@ -3174,24 +3174,27 @@ If you want back the defaults, try specifying just the 
~org-todo~ face:
         ("WRITE" . org-todo)))
 #+end_src
 
-When you inherit from multiple faces, you need to quote the list as
+Or set ~org-todo-keyword-faces~ to ~nil~.
+
+When you inherit from multiple faces, you need to do it the way it is
 shown further above.  The order is significant: the first entry is
-applied on top of the second, overriding any properties that are
-explicitly set for both of them: any property that is not specified is
-not overridden, so, for example, if ~org-todo~ has a background and a
-foreground, while ~font-lock-type-face~ only has a foreground, the merged
-face will include the background of the former and the foreground of the
-latter.  If you do not want to blend multiple faces, you do not need a
-quoted list.  A pattern of =keyword . face= will suffice.
+applied on top of the second, overriding any attributes that are
+explicitly set for both of them: any attribute that is not specified
+is not overridden, so, for example, if ~org-todo~ has a background and
+a foreground, while ~font-lock-type-face~ only has a foreground, the
+merged face will include the background of the former and the
+foreground of the latter.  If you do not want to blend multiple faces,
+you only specify one by name without parentheses or an =:inherit=
+keyword.  A pattern of =keyword . face= will suffice.
 
 Both approaches can be used simultaneously, as illustrated in this
 configuration of the priority cookies:
 
 #+begin_src emacs-lisp
 (setq org-priority-faces
-      '((?A . '(bold org-priority))
+      '((?A . (:inherit (bold org-priority)))
         (?B . org-priority)
-        (?C . '(shadow org-priority))))
+        (?C . (:inherit (shadow org-priority)))))
 #+end_src
 
 To find all the faces that are loaded in your current Emacs session, use
@@ -3636,6 +3639,10 @@ need to (provided they understand the implications).
 :CUSTOM_ID: h:43bcb5d0-e25f-470f-828c-662cee9e21f1
 :END:
 
+[ UPDATE 2023-06-25: Instead of following these instructions, you can
+  simply install my ~spacious-padding~ package from GNU ELPA.  It
+  implements the padding and provides relevant user options. ]
+
 By default, Emacs frames try to maximize the number of characters that
 fit in the current visible portion of the buffer.  Users may prefer to
 have some extra padding instead.  This can make Emacs frames look more
@@ -3847,6 +3854,7 @@ have lots of extensions, so the "full support" may not be 
100% true…
 + avy
 + bbdb
 + binder
++ breadcrumb
 + bongo
 + boon
 + bookmark
@@ -3864,11 +3872,11 @@ have lots of extensions, so the "full support" may not 
be 100% true…
 + completions
 + consult
 + corfu
++ corfu-candidate-overlay
 + corfu-quick
 + counsel*
 + cperl-mode
 + crontab-mode
-+ css-mode
 + csv-mode
 + ctrlf
 + custom (what you get with {{{kbd(M-x customize)}}})
@@ -3905,6 +3913,7 @@ have lots of extensions, so the "full support" may not be 
100% true…
 + epa
 + erc
 + ert
++ erts-mode
 + eshell
 + eshell-fringe-status
 + evil* (evil-mode)
@@ -3951,6 +3960,7 @@ have lots of extensions, so the "full support" may not be 
100% true…
 + isearch, occur, etc.
 + ivy*
 + ivy-posframe
++ japanese-holidays
 + jira (org-jira)
 + jit-spell
 + jinx
@@ -3975,6 +3985,10 @@ have lots of extensions, so the "full support" may not 
be 100% true…
 + mpdel
 + mu4e
 + multiple-cursors
++ nerd-icons
++ nerd-icons-completion
++ nerd-icons-dired
++ nerd-icons-ibuffer
 + neotree
 + notmuch
 + num3-mode
@@ -4038,7 +4052,6 @@ have lots of extensions, so the "full support" may not be 
100% true…
 + suggest
 + switch-window
 + swiper
-+ sx
 + symbol-overlay
 + syslog-mode
 + tab-bar-mode
@@ -4099,6 +4112,7 @@ supported by the themes.
 + bufler
 + counsel-notmuch
 + counsel-org-capture-string
++ css-mode
 + dashboard (emacs-dashboard)
 + define-word
 + denote
@@ -4722,7 +4736,7 @@ Consult the doc string of ~shr-use-colors~.
 By default, packages that build on top of the Simple HTML Remember
 (~shr~) use proportionately spaced fonts.  This is controlled by the
 user option ~shr-use-fonts~, which is set to non-~nil~ by default.  To
-use the standard font instead, set that variable to nil.
+use the standard font instead, set that variable to ~nil~.
 
 [[#h:defcf4fc-8fa8-4c29-b12e-7119582cc929][Font configurations for Org and 
others]].
 
@@ -4859,7 +4873,7 @@ consider including (or equivalent) this in their setup:
       goto-address-mail-mouse-face 'highlight)
 #+end_src
 
-My personal preference is to set ~goto-address-mail-face~ to nil, as
+My personal preference is to set ~goto-address-mail-face~ to ~nil~, as
 it otherwise adds too much visual noise to the buffer (email addresses
 stand out more, due to the use of the uncommon =@= character but also
 because they are often enclosed in angled brackets).
@@ -5345,8 +5359,9 @@ The Modus themes are a collective effort.  Every bit of 
work matters.
   Matthew Stevenson, Mauro Aranda, Nacho Barrientos, Nicolas De
   Jaeghere, Paul David, Philip Kaludercic, Pierre Téchoueyres, Rudolf
   Adamkovič, Sergey Nichiporchik, Shreyas Ragavan, Stefan Kangas,
-  Stephen Gildea, Steve Downey, Tomasz Hołubowicz, Utkarsh Singh,
-  Vincent Murphy, Xinglu Chen, Yuanchen Xie, okamsn.
+  Stephen Berman, Stephen Gildea, Steve Downey, Tomasz Hołubowicz,
+  Utkarsh Singh, Vincent Murphy, Xinglu Chen, Yuanchen Xie, fluentpwn,
+  okamsn.
 
 + Ideas and user feedback :: Aaron Jensen, Adam Porter, Adam Spiers,
   Adrian Manea, Aleksei Pirogov, Alex Griffin, Alex Koen, Alex
@@ -5363,25 +5378,25 @@ The Modus themes are a collective effort.  Every bit of 
work matters.
   Ferguson, Jeremy Friesen, Jerry Zhang, Johannes Grødem, John Haman,
   Jonas Collberg, Jorge Morais, Joshua O'Connor, Julio C. Villasante,
   Kenta Usami, Kevin Fleming, Kévin Le Gouguec, Kevin Kainan Li,
-  Kostadin Ninev, Laith Bahodi, Len Trigg, Lennart C. Karssen, Luis
-  Miguel Castañeda, Magne Hov, Manuel Giraud, Manuel Uberti, Mark
-  Bestley, Mark Burton, Mark Simpson, Marko Kocic, Markus Beppler,
-  Matt Armstrong, Matthias Fuchs, Mattias Engdegård, Mauro Aranda,
-  Maxime Tréca, Michael Goldenberg, Morgan Smith, Morgan Willcock,
-  Murilo Pereira, Nicky van Foreest, Nicolas De Jaeghere, Nicolas
-  Semrau, Oliver Epper, Pablo Stafforini, Paul Poloskov, Pengji Zhang,
-  Pete Kazmier, Peter Wu, Philip Kaludercic, Pierre Téchoueyres,
-  Przemysław Kryger, Robert Hepple, Roman Rudakov, Russell Sim, Ryan
-  Phillips, Rytis Paškauskas, Rudolf Adamkovič, Sam Kleinman, Samuel
-  Culpepper, Saša Janiška, Shreyas Ragavan, Simon Pugnet, Steve
-  Downey, Tassilo Horn, Thanos Apollo, Thibaut Verron, Thomas
-  Heartman, Togan Muftuoglu, Tony Zorman, Trey Merkley, Tomasz
-  Hołubowicz, Toon Claes, Uri Sharf, Utkarsh Singh, Vincent Foley,
-  Zoltan Kiraly.  As well as users: Ben, CsBigDataHub1, Emacs Contrib,
-  Eugene, Fourchaux, Fredrik, Moesasji, Nick, Summer Emacs, TheBlob42,
-  TitusMu, Trey, bepolymathe, bit9tream, bangedorrunt, derek-upham,
-  doolio, fleimgruber, gitrj95, iSeeU, jixiuf, okamsn, pRot0ta1p,
-  soaringbird, tumashu, wakamenod.
+  Kostadin Ninev, Laith Bahodi, Lasse Lindner, Len Trigg, Lennart
+  C. Karssen, Luis Miguel Castañeda, Magne Hov, Manuel Giraud, Manuel
+  Uberti, Mark Bestley, Mark Burton, Mark Simpson, Marko Kocic, Markus
+  Beppler, Matt Armstrong, Matthias Fuchs, Mattias Engdegård, Mauro
+  Aranda, Maxime Tréca, Michael Goldenberg, Morgan Smith, Morgan
+  Willcock, Murilo Pereira, Nicky van Foreest, Nicolas De Jaeghere,
+  Nicolas Semrau, Olaf Meeuwissen, Oliver Epper, Pablo Stafforini,
+  Paul Poloskov, Pengji Zhang, Pete Kazmier, Peter Wu, Philip
+  Kaludercic, Pierre Téchoueyres, Przemysław Kryger, Robert Hepple,
+  Roman Rudakov, Russell Sim, Ryan Phillips, Rytis Paškauskas, Rudolf
+  Adamkovič, Sam Kleinman, Samuel Culpepper, Saša Janiška, Shreyas
+  Ragavan, Simon Pugnet, Steve Downey, Tassilo Horn, Thanos Apollo,
+  Thibaut Verron, Thomas Heartman, Togan Muftuoglu, Tony Zorman, Trey
+  Merkley, Tomasz Hołubowicz, Toon Claes, Uri Sharf, Utkarsh Singh,
+  Vincent Foley, Zoltan Kiraly.  As well as users: Ben, CsBigDataHub1,
+  Emacs Contrib, Eugene, Fourchaux, Fredrik, Moesasji, Nick, Summer
+  Emacs, TheBlob42, TitusMu, Trey, bepolymathe, bit9tream,
+  bangedorrunt, derek-upham, doolio, fleimgruber, gitrj95, iSeeU,
+  jixiuf, okamsn, pRot0ta1p, soaringbird, tumashu, wakamenod.
 
 + Packaging :: Basil L.{{{space()}}} Contovounesios, Eli Zaretskii,
   Glenn Morris, Mauro Aranda, Richard Stallman, Stefan Kangas (core
diff --git a/doc/misc/octave-mode.texi b/doc/misc/octave-mode.texi
index 9f82af45203..0a599f64516 100644
--- a/doc/misc/octave-mode.texi
+++ b/doc/misc/octave-mode.texi
@@ -421,7 +421,7 @@ when Octave is waiting for input, or done sending output.
 
 @bye
 
-@c TODO Update
+@c TODO Update (and change gnuserv to emacsclient)
 
 @c @node Using the Emacs Info Reader for Octave
 @c @chapter Using the Emacs Info Reader for Octave
diff --git a/doc/misc/org.org b/doc/misc/org.org
index a4ce53cc6cb..9721807a185 100644
--- a/doc/misc/org.org
+++ b/doc/misc/org.org
@@ -9431,7 +9431,7 @@ the estimated effort of an entry (see [[*Effort 
Estimates]]).
 #+vindex: org-agenda-effort-filter-preset
 #+vindex: org-agenda-regexp-filter-preset
 Agenda built-in or custom commands are statically defined.  Agenda
-filters and limits allow to flexibly narrow down the list of agenda
+filters and limits allow flexibly narrowing down the list of agenda
 entries.
 
 /Filters/ only change the visibility of items, are very fast and are
diff --git a/doc/misc/ses.texi b/doc/misc/ses.texi
index f63bffe2a9f..cfca9c25ebb 100644
--- a/doc/misc/ses.texi
+++ b/doc/misc/ses.texi
@@ -188,7 +188,7 @@ the end-points, e.g.:
 @emph{list} of values.  This allows for more complex possibilities.)
 
 Alternatively you can use the @code{!} modifier of @code{ses-range} to
-remove blank cells from the returned list, which allows to use
+remove blank cells from the returned list, which enables using
 @code{+} instead of @code{ses+}:
 
 @lisp
@@ -1007,7 +1007,7 @@ are some useful functions to call from your formulas:
 @item (ses-delete-blanks &rest @var{args})
 Returns a list from which all blank cells (value is either @code{nil}
 or '*skip*) have been deleted. Order of args is reverted. Please note
-that @code{ses-range} has a @code{!} modifier that allows to remove
+that @code{ses-range} has a @code{!} modifier that enables removing
 blanks, so it is possible to write:
 @lisp
 (ses-range A1 A5 !)
diff --git a/doc/misc/tramp.texi b/doc/misc/tramp.texi
index 6189ef2d41d..64d47515978 100644
--- a/doc/misc/tramp.texi
+++ b/doc/misc/tramp.texi
@@ -1094,11 +1094,11 @@ decode programs.
 @cindex method @option{sudoedit}
 @cindex @option{sudoedit} method
 
-The @option{sudoedit} method allows to edit a file as a different user
-on the local host.  You could regard this as @value{tramp}'s
+The @option{sudoedit} method facilitates editing a file as a different
+user on the local host.  You could regard this as @value{tramp}'s
 implementation of the @command{sudoedit}.  Contrary to the
 @option{sudo} method, all magic file name functions are implemented by
-single @command{sudo @dots{}}  commands.  The purpose is to make
+single @command{sudo @dots{}} commands.  The purpose is to make
 editing such a file as secure as possible; there must be no session
 running in the Emacs background which could be attacked from inside
 Emacs.
@@ -1383,7 +1383,7 @@ possible, @value{tramp} emulates those operations 
otherwise.
 @cindex @option{rclone} method
 
 @vindex tramp-rclone-program
-The program @command{rclone} allows to access different system
+The program @command{rclone} enables accessing different system
 storages in the cloud, see @uref{https://rclone.org/} for a list of
 supported systems.  If the @command{rclone} program isn't found in
 your @env{PATH} environment variable, you can tell @value{tramp} its
@@ -2366,7 +2366,7 @@ connection information}.  If you want, for example, use
 @end group
 @end lisp
 
-This works only for connection methods which allow to override the
+This works only for connection methods which allow overriding the
 remote login shell, like @option{sshx} or @option{plink}.  See
 @ref{Inline methods} and @ref{External methods} for connection methods
 which support this.
@@ -2436,8 +2436,10 @@ which may not be the same as the local login shell 
prompt,
 @value{tramp} sets a similar default value for both prompts.
 
 @item @code{tramp-password-prompt-regexp}
+@item @code{tramp-otp-password-prompt-regexp}
 @item @code{tramp-wrong-passwd-regexp}
 @vindex tramp-password-prompt-regexp
+@vindex tramp-otp-password-prompt-regexp
 @vindex tramp-wrong-passwd-regexp
 
 @value{tramp} uses @code{tramp-password-prompt-regexp} to
@@ -2471,6 +2473,10 @@ This user option is, by default, initialized from
 is usually more convenient to add new passphrases to that user option
 instead of altering this user option.
 
+The user option @code{tramp-otp-password-prompt-regexp} has a similar
+purpose, but for one-time passwords.  Those passwords are not cached
+by @value{tramp} for reuse.
+
 Similar localization may be necessary for handling wrong password
 prompts, for which @value{tramp} uses @code{tramp-wrong-passwd-regexp}.
 
@@ -3704,6 +3710,47 @@ host name of the previous hop is reused.  Therefore, the 
following
 file name is equivalent to the previous example:
 @samp{@trampfn{ssh@value{postfixhop}remotehost|su,,}}.
 
+@defopt tramp-completion-multi-hop-methods
+When this list includes the last method in a multi-hop connection, the
+remote host will be queried for a list of completion candidates.  This
+can, for example, provide a list of running docker or podman
+containers on the remote host.
+
+@lisp
+(customize-set-variable 'tramp-completion-multi-hop-methods
+ `(,tramp-docker-method ,tramp-podman-method))
+@end lisp
+@end defopt
+
+A common use case for ad-hoc specifications is to visit a file or a
+directory with proper permissions, for example with the @option{sudo}
+method.  The command @code{tramp-revert-buffer-with-sudo} supports
+this.
+
+@deffn Command tramp-revert-buffer-with-sudo
+This command shows the current buffer with @option{sudo} permissions.
+The buffer must either visit a file, or a directory
+(@code{dired-mode}).
+@end deffn
+
+@defopt tramp-file-name-with-method
+The method @code{tramp-revert-buffer-with-sudo} shows an alternate
+buffer.  It defaults to @code{sudo}, other valid methods are
+@code{su}, @code{doas}, and @code{ksu}.
+
+@lisp
+(customize-set-variable 'tramp-file-name-with-method "doas")
+@end lisp
+@end defopt
+
+These methods apply the user @samp{root} as default.  If another user
+shall be taken, add a proper rule to the user option
+@code{tramp-default-user-alist} (@pxref{Default User}):
+
+@lisp
+(add-to-list 'tramp-default-user-alist '("sudo" "remotehost" "admin"))
+@end lisp
+
 
 @node Home directories
 @section Expanding @file{~} to home directory
@@ -5252,30 +5299,11 @@ How to get notified after @value{tramp} completes file 
transfers?
 Make Emacs beep after reading from or writing to the remote host with
 the following code in @file{~/.emacs}.
 
+@vindex tramp-handle-write-region-hook
+@vindex tramp-handle-file-local-copy-hook
 @lisp
-@group
-(defadvice tramp-handle-write-region
-  (after tramp-write-beep-advice activate)
-  "Make @value{tramp} beep after writing a file."
-  (interactive)
-  (beep))
-@end group
-
-@group
-(defadvice tramp-handle-do-copy-or-rename-file
-  (after tramp-copy-beep-advice activate)
-  "Make @value{tramp} beep after copying a file."
-  (interactive)
-  (beep))
-@end group
-
-@group
-(defadvice tramp-handle-insert-file-contents
-  (after tramp-insert-beep-advice activate)
-  "Make @value{tramp} beep after inserting a file."
-  (interactive)
-  (beep))
-@end group
+(add-hook 'tramp-handle-write-region-hook 'beep)
+(add-hook 'tramp-handle-file-local-copy-hook 'beep)
 @end lisp
 
 
@@ -5528,9 +5556,8 @@ minibuffer:
 @end group
 
 @group
-(defadvice minibuffer-complete
-  (before my-minibuffer-complete activate)
-  (expand-abbrev))
+(advice-add 'minibuffer-complete
+ :before 'expand-abbrev)
 @end group
 @end lisp
 
@@ -5727,6 +5754,8 @@ If you find the cleanup disturbing, because the file 
names in
 two forms in your @file{~/.emacs} after loading the @code{tramp} and
 @code{recentf} packages:
 
+@vindex tramp-cleanup-connection-hook
+@vindex tramp-cleanup-all-connections-hook
 @lisp
 @group
 (remove-hook
diff --git a/doc/misc/url.texi b/doc/misc/url.texi
index b5a6cb0e6a1..e6636e32507 100644
--- a/doc/misc/url.texi
+++ b/doc/misc/url.texi
@@ -1149,40 +1149,6 @@ If this variable is non-@code{nil} new network 
connections are never
 opened by the URL library.
 @end defvar
 
-@c @node Broken hostname resolution
-@c @subsection Broken Hostname Resolution
-
-@c @cindex hostname resolver
-@c @cindex resolver, hostname
-@c Some C libraries do not include the hostname resolver routines in
-@c their static libraries.  If Emacs was linked statically, and was not
-@c linked with the resolver libraries, it will not be able to get to any
-@c machines off the local network.  This is characterized by being able
-@c to reach someplace with a raw ip number, but not its hostname
-@c (@url{https://129.79.254.191/} works, but
-@c @url{https://www.cs.indiana.edu/} doesn't).  This used to happen on
-@c SunOS4 and Ultrix, but is now probably now rare.  If Emacs can't be
-@c rebuilt linked against the resolver library, it can use the external
-@c @command{nslookup} program instead.
-
-@c @defopt url-gateway-broken-resolution
-@c @cindex @code{nslookup} program
-@c @cindex program, @code{nslookup}
-@c If non-@code{nil}, this variable says to use the program specified by
-@c @code{url-gateway-nslookup-program} program to do hostname resolution.
-@c @end defopt
-
-@c @defopt url-gateway-nslookup-program
-@c The name of the program to do hostname lookup if Emacs can't do it
-@c directly.  This program should expect a single argument on the command
-@c line---the hostname to resolve---and should produce output similar to
-@c the standard Unix @command{nslookup} program:
-@c @example
-@c Name: www.cs.indiana.edu
-@c Address: 129.79.254.191
-@c @end example
-@c @end defopt
-
 @node History
 @section History
 
diff --git a/doc/misc/widget.texi b/doc/misc/widget.texi
index 13b37ab5b54..eb411f29c5c 100644
--- a/doc/misc/widget.texi
+++ b/doc/misc/widget.texi
@@ -49,14 +49,16 @@ modify this GNU manual.''
 * Introduction::
 * User Interface::
 * Programming Example::
+* Widgets Basics::
 * Setting Up the Buffer::
-* Basic Types::
-* Sexp Types::
-* Widget Properties::
+* Working with Widgets::
+* Widgets and the Buffer::
+* Widget Gallery::
 * Defining New Widgets::
-* Widget Browser::
+* Inspecting Widgets::
 * Widget Minor Mode::
 * Utilities::
+* Customization::
 * Widget Wishlist::
 * GNU Free Documentation License::
 * Index::
@@ -68,7 +70,7 @@ modify this GNU manual.''
 Most graphical user interface toolkits provide a number of standard
 user interface controls (sometimes known as ``widgets'' or ``gadgets'').
 Emacs doesn't really support anything like this, except for an
-incredibly powerful text ``widget.''  On the other hand, Emacs does
+incredibly powerful text ``widget''.  On the other hand, Emacs does
 provide the necessary primitives to implement many other widgets
 within a text buffer.  The @code{widget} package simplifies this task.
 
@@ -85,13 +87,13 @@ Like link, but intended for stand-alone buttons.
 @item editable-field
 An editable text field.  It can be either variable or fixed length.
 @item menu-choice
-Allows the user to choose one of multiple options from a menu, each
-option is itself a widget.  Only the selected option will be visible in
-the buffer.
+Allows the user to choose one of multiple options from a menu, where
+each option is itself a widget.  Only the selected option is visible
+in the buffer.
 @item radio-button-choice
 Allows the user to choose one of multiple options by activating radio
-buttons.  The options are implemented as widgets.  All options will be
-visible in the buffer.
+buttons.  The options are implemented as widgets.  All options are
+visible in the buffer, with the selected one marked as chosen.
 @item item
 A simple constant widget intended to be used in the @code{menu-choice} and
 @code{radio-button-choice} widgets.
@@ -137,9 +139,9 @@ Editing happens in the buffer, not in the mini-buffer.
 Packages using the library get a uniform look, making them easier for
 the user to learn.
 @item
-As support for embedded graphics improve, the widget library will be
+As support for embedded graphics improve, the Widget library will be
 extended to use the GUI features.  This means that your code using the
-widget library will also use the new graphic features automatically.
+Widget library will also use the new graphic features automatically.
 @end enumerate
 
 @node User Interface
@@ -201,12 +203,6 @@ middle of another field is prohibited.
 
 Editable text fields are created by the @code{editable-field} widget.
 
-@strong{Warning:} In an @code{editable-field} widget, the editable
-field must not be adjacent to another widget---that won't work.
-You must put some text in between.  Either make this text part of
-the @code{editable-field} widget itself, or insert it with
-@code{widget-insert}.
-
 The @code{:format} keyword is useful for generating the necessary
 text; for instance, if you give it a value of @code{"Name: %v "},
 the @samp{Name: } part will provide the necessary separating text
@@ -215,17 +211,9 @@ separating text after the field.  If you don't include the
 @code{:size} keyword, the field will extend to the end of the
 line, and the terminating newline will provide separation after.
 
-@strong{Warning:} In an @code{editable-field} widget, the @samp{%v} escape
-must be preceded by some other text in the @code{:format} string
-(if specified).
-
 The editing text fields are highlighted with the
 @code{widget-field-face} face, making them easy to find.
 
-@deffn Face widget-field-face
-Face used for other editing fields.
-@end deffn
-
 @section Buttons
 
 @cindex widget buttons
@@ -233,24 +221,9 @@ Face used for other editing fields.
 Some portions of the buffer have an associated @dfn{action}, which can
 be @dfn{invoked} by a standard key or mouse command.  These portions
 are called @dfn{buttons}.  The default commands for activating a button
-are:
-
-@table @kbd
-@item @key{RET}
-@deffn Command widget-button-press @var{pos} &optional @var{event}
-Invoke the button at @var{pos}, defaulting to point.
-If point is not located on a button, invoke the binding in
-@code{widget-global-map} (by default the global map).
-@end deffn
-
-@kindex mouse-2 @r{(on button widgets})
-@item mouse-2
-@deffn Command widget-button-click @var{event}
-Invoke the button at the location of the mouse pointer.  If the mouse
-pointer is located in an editable text field, invoke the binding in
-@code{widget-global-map} (by default the global map).
-@end deffn
-@end table
+are @code{widget-button-press} and @code{widget-button-click}.  The
+user typically interacts with the buttons with a key, like @key{RET},
+or with the mouse buttons.
 
 There are several different kind of buttons, all of which are present in
 the example:
@@ -286,33 +259,15 @@ main difference from the @code{link} widget is that the 
buttons will be
 displayed as GUI buttons when possible.
 @end table
 
-To make them easier to locate, buttons are emphasized in the buffer.
-
-@deffn Face widget-button-face
-Face used for buttons.
-@end deffn
-
-@defopt widget-mouse-face
-Face used for highlighting a button when the mouse pointer moves across
-it.
-@end defopt
+To make them easier to locate, buttons are emphasized in the buffer
+with a distinctive face, like @code{widget-button-face} or
+@code{widget-mouse-face}.
 
 @section Navigation
 
 You can use all the normal Emacs commands to move around in a form
-buffer, plus you will have these additional commands:
-
-@table @kbd
-@item @key{TAB}
-@deffn Command widget-forward &optional count
-Move point @var{count} buttons or editing fields forward.
-@end deffn
-@item @kbd{M-@key{TAB}}
-@itemx @kbd{S-@key{TAB}}
-@deffn Command widget-backward &optional count
-Move point @var{count} buttons or editing fields backward.
-@end deffn
-@end table
+buffer, plus you will have these additional commands to navigate from
+widget to widget: @code{widget-forward} and @code{widget-backward}.
 
 @node Programming Example
 @chapter Programming Example
@@ -414,315 +369,1050 @@ Interface}).
   (widget-setup))
 @end lisp
 
+@node Widgets Basics
+@chapter Widgets Basics
+@cindex widget object
+The Widget Library deals with widgets objects.  A widget object has
+properties whose value may be anything, be it numbers, strings,
+symbols, functions, etc.  Those properties are referred to as keywords
+and are responsible for the way a widget is represented in a buffer,
+and control the way a user or a program can interact with it.
+
+@cindex widget inheritance
+The library defines several widget types, and gives you a way to
+define new types as well.  In addition, widgets can derive from other
+types, creating a sort of widget inheritance.  In fact, all widgets
+defined in the Widget Library share a common parent, the @dfn{default}
+widget.  In this manual, when we talk about a default behavior, we
+usually mean the behavior as defined by this @code{default} widget.
+@xref{Widget Gallery}, for a description of each defined widget.
+
+Defining a new type that derives from a previous one is not mandatory
+to create widgets that work very different from a specified type.
+When creating a widget, you can override any default property,
+including functions, that control the widget.  That is, you can
+specialize a widget on creation, without having to define it as a new
+type of widget.
+
+In addition to the function for defining a widget, this library
+provides functions to create widgets, query and change its properties,
+respond to user events and destroy them.  The following sections
+describe them.
+
+@cindex widget value
+One important property of a widget is its @dfn{value}.  All widgets
+may have a value,  which is stored in a so-called @dfn{internal format}.
+For the rest of Emacs, the widget presents its value in a so-called
+@dfn{external format}.  Both formats can be equal or different, and
+each widget is responsible for defining how the conversion between
+each format should happen.
+
+@c FIXME: Briefly describe inline widgets?
+@c The inline concept is described elsewhere, and it's difficult to
+@c describe.
+
+The value property is an important property for almost all widgets,
+and perhaps more important for @code{editable-field} widgets.  This
+type of widgets allow the user to edit them via the usual editing
+commands in Emacs.  They can also be edited programmatically.
+@strong{Important:} You @emph{must} call @code{widget-setup} after
+modifying the value of a widget before the user is allowed to edit the
+widget again.  It is enough to call @code{widget-setup} once if you
+modify multiple widgets.  This is currently only necessary if the widget
+contains an editing field, but may be necessary for other widgets in the
+future.
+
+@cindex widget properties
+If your application needs to associate some information with the widget
+objects, for example a reference to the item being edited, it can be
+done with the @code{widget-put} and @code{widget-get} functions.  The
+property names, as shown, are keywords, so they must begin with a
+@samp{:}.
+
 @node Setting Up the Buffer
 @chapter Setting Up the Buffer
+@cindex widget creation, widget conversion
+To show the widgets in a buffer, you have to create them.  Widget
+creation is actually a two-step process: conversion and creation per
+se.  With simple projects, usually the conversion step isn't really
+important, and you only care about widget creation, so feel free to
+skip the conversion description until you really need to know it.
+
+Widget conversion is the process that involves taking a widget
+specification and transforming it into a @dfn{widget} object, suitable
+to be created, queried and manipulated with other widget functions.
+Widget creation is the process that takes a widget object and actually
+inserts it in the buffer.
+
+The simplest function to create a widget is @code{widget-create}, which
+gets a widget specification and returns a widget object.
+
+@defun widget-create type [ keyword argument ]@dots{} args
+Create and return a widget of type @var{type}, converting it.
+
+@var{type} is a symbol that specifies a widget type.  @var{keyword}
+may be one of the properties supported by the widget type, and
+@var{argument} specify the value for that property.  These keyword
+arguments can be used to overwrite the keyword arguments that are part
+of @var{type} by default, as well as to provide other properties not
+present in @var{type} by default.  @var{args} holds additional
+information for the creation of @var{type} and each widget type is
+responsible for handling that information in a specific way.
+
+The syntax for the @var{type} argument is described in @ref{Widget
+Gallery}, and in more detail in every widget where it's relevant.
+@end defun
+
+There are other functions for creating widgets, useful when you work
+with composite widgets.  That is, widgets that are part of other
+widgets.
 
-Widgets are created with @code{widget-create}, which returns a
-@dfn{widget} object.  This object can be queried and manipulated by
-other widget functions, until it is deleted with @code{widget-delete}.
-After the widgets have been created, @code{widget-setup} must be called
-to enable them.
+@defun widget-create-child-and-convert parent type &rest args
+Create a widget of type @var{type} as a child of @var{parent}.
 
-@defun widget-create type [ keyword argument ]@dots{}
-Create and return a widget of type @var{type}.
-The syntax for the @var{type} argument is described in @ref{Basic Types}.
+Before creating it, converts @var{type} using the keyword arguments
+provided in @var{args}.
+@c FIXME: Is this description useful?
+Adds the @code{:indent} property, unless it is already present, and
+sets it to the sum of the values of: @code{:indent} and @code{:offset}
+from @var{parent} and @code{:extra-offset} from @var{type}.
 
-The keyword arguments can be used to overwrite the keyword arguments
-that are part of @var{type}.
+Returns a widget object, with the property @code{:parent} set to
+@var{PARENT}.
 @end defun
 
-@defun widget-delete widget
-Delete @var{widget} and remove it from the buffer.
+@defun widget-create-child parent type
+Create a widget of type @var{type} as a child of @var{parent}.
+
+This function is like @code{widget-create-child-and-convert} but it
+doesn't convert @var{type}, so it expects an already converted widget.
 @end defun
 
+@defun widget-create-child-value parent type value
+Create a widget of type @var{type} as a child of @var{parent} with
+value @var{value}.
+
+This function is like @code{widget-create-child}, but it lets you
+specify a value for the widget.
+
+Converts @var{value} to the internal format, as specified by
+@var{type}, and stores it into the @code{:value} property of @var{type}.
+That means, @var{value} should be in the external format, as
+specified by @var{type}.
+@end defun
+
+All these creating functions described here use the function stored in
+the @code{:create} property.  So, to modify the creation logic for a
+widget, you can provide a different @code{:create} function.
+
+When you're done creating widgets and you're ready for the user to
+interact with the buffer, use the function @code{widget-setup}.
+
 @defun widget-setup
-Set up a buffer to support widgets.
+Setup the current buffer, so that editable widgets can be edited.
 
 This should be called after creating all the widgets and before allowing
 the user to edit them.
 @end defun
 
-If you want to insert text outside the widgets in the form, the
-recommended way to do that is with @code{widget-insert}.
+As mentioned, all these functions return a widget object.  That widget
+object can be queried and manipulated with widget functions that
+take widgets as arguments, until deleting it with the widgets
+functions available to delete widgets.  Even if you don't save the
+returned widget object, you still can interact programmatically with
+the widget.  @xref{Working with Widgets}.
 
-@defun widget-insert
-Insert the arguments, either strings or characters, at point.
-The inserted text will be read-only.
+@defun widget-delete widget
+Delete the widget @var{widget} and remove it from the buffer.
 @end defun
 
-There is a standard widget keymap which you might find useful.
+@defun widget-children-value-delete widget
+Delete all children and buttons in widget @var{widget}.
 
-@findex widget-button-press
-@findex widget-button-click
-@defvr Const widget-keymap
-@key{TAB} and @kbd{C-@key{TAB}} are bound to @code{widget-forward} and
-@code{widget-backward}, respectively.  @key{RET} and @kbd{mouse-2}
-are bound to @code{widget-button-press} and
-@code{widget-button-click}.
-@end defvr
+This function does not delete @var{widget} itself, only the widgets
+stored in the @code{:children} and @code{:buttons} properties.  It
+also sets those properties to @code{nil}.
+@end defun
 
-@defvar widget-global-map
-Keymap used by @code{widget-button-press} and @code{widget-button-click}
-when not on a button.  By default this is @code{global-map}.
-@end defvar
+As with the creation mechanism, the function stored in @code{:delete}
+controls the deletion mechanism for a widget.
 
-@node Basic Types
-@chapter Basic Types
+Additionally, the library provides a way to make a copy of a widget.
 
-This is the general syntax of a type specification:
+@defun widget-copy widget
+Makes a copy of widget @var{widget} and returns it.
 
-@example
-@var{name} ::= (@var{name} [@var{keyword} @var{argument}]... @var{args})
-     |   @var{name}
-@end example
+It uses the function stored in the @code{:copy} property of @var{widget}
+and returns the widget that that function returns.
+@end defun
 
-Where, @var{name} is a widget name, @var{keyword} is the name of a
-property, @var{argument} is the value of the property, and @var{args}
-are interpreted in a widget specific way.
+As discussed, there is a conversion step when creating a widget.  To
+do the conversion without actually creating the widget, you can use
+the @code{widget-convert} function.
 
-@cindex keyword arguments
-The following keyword arguments apply to all widgets:
+@defun widget-convert type &rest args
+Convert @var{type} to a widget object, using keyword arguments @var{args}.
 
-@table @code
-@cindex internal format
-@cindex external format
-@vindex value@r{ keyword}
-@item :value
-The initial value for widgets of this type.  Typically, a widget
-represents its value in two formats: external and internal.  The
-external format is the value as the rest of Emacs sees it, and the
-internal format is a representation that the widget defines and uses
-in a widget specific way.
+Returns a widget object, suitable for creation.  It calls the function
+stored in the @code{:convert-widget} property, after putting into the
+@code{:args} property the arguments that the widget in question needs.
+If @var{type} has a @code{:value} property, either originally or after
+doing the conversion, this function converts the value stored in
+@code{:value} to the internal format, and stores it into @code{:value}.
+@end defun
 
-Both formats might be the same for certain widgets and might differ
-for others, and there is no guarantee about which format the value
-stored in the @code{:value} property has.  However, when creating a
-widget or defining a new one (@pxref{Defining New Widgets}), the
-@code{:value} should be in the external format.
+Apart from only creating widgets in the buffer, It's useful to have
+plain text.  For inserting text, the recommended way is with the
+@code{widget-insert} function.
 
-@vindex format@r{ keyword}
-@item :format
-This string will be inserted in the buffer when you create a widget.
-The following @samp{%} escapes are available:
+@defun widget-insert &rest args
+Insert @var{args}, either strings or characters, at point.
 
-@table @samp
-@item %[
-@itemx %]
-The text inside will be marked as a button.
+Uses @code{insert} to perform the insertion, passing @var{args} as
+argument.  @xref{Insertion,,,elisp, the Emacs Lisp Reference Manual},
+for more information about @var{args}.
 
-By default, the text will be shown in @code{widget-button-face}, and
-surrounded by brackets.
+The resulting text will be read-only.
+@end defun
 
-@defopt widget-button-prefix
-String to prefix buttons.
-@end defopt
+@node Working with Widgets
+@chapter Working with Widgets
+This section covers the more important functions needed to query and
+manipulate widgets in a generic way.  Widgets may have additional
+functions for interacting with them, those are described in the
+description for each widget.  @xref{Widget Gallery}.
 
-@defopt widget-button-suffix
-String to suffix buttons.
-@end defopt
+@defun widgetp widget
+Non-@code{nil} if @var{widget} is a widget.
+@end defun
 
-@item %@{
-@itemx %@}
-The text inside will be displayed with the face specified by
-@code{:sample-face}.
+@defun widget-type widget
+Return the type of widget @var{widget}, a symbol.
 
-@item %v
-This will be replaced with the buffer representation of the widget's
-value.  What this is depends on the widget type.
+This function is useful to find out which kind of widget @var{widget}
+represents, i.e., the name of the widget type when the widget
+was created.
+@end defun
 
-@strong{Warning:} In an @code{editable-field} widget, the @samp{%v} escape
-must be preceded by some other text in the format string (if specified).
+@defun widget-member widget property
+Non-@code{nil} if widget @var{widget} has a value (even @code{nil}) for
+property @var{property}.
+@end defun
 
-@item %d
-Insert the string specified by @code{:doc} here.
+@defun widget-get widget property
+For widget @var{widget}, return the value of the property @var{property}.
 
-@item %h
-Like @samp{%d}, with the following modifications: If the documentation
-string is more than one line, it will add a button which will toggle
-between showing only the first line, and showing the full text.
-Furthermore, if there is no @code{:doc} property in the widget, it will
-instead examine the @code{:documentation-property} property.  If it is a
-lambda expression, it will be called with the widget's value as an
-argument, and the result will be used as the documentation text.
+@var{property} should be a keyword, and the value is what was last set by
+@code{widget-put} for @var{property}.
+@end defun
 
-@item %t
-Insert the string specified by @code{:tag} here, or the @code{princ}
-representation of the value if there is no tag.
+@defun widget-put widget property value
+For widget @var{widget}, set the property @var{property} to @var{value}.
+@var{property} should be a keyword, while @var{value} can be anything.
+@end defun
 
-@item %%
-Insert a literal @samp{%}.
-@end table
+@defun widget-at &optional pos
+Return the widget at position @var{pos}, or at point if @var{pos} is 
@code{nil}.
+@end defun
 
-@vindex button-face@r{ keyword}
-@item :button-face
-Face used to highlight text inside %[ %] in the format.
+@defun widget-field-at pos
+Return the widget field at position POS, or @code{nil} if there is none.
+@end defun
 
-@vindex button-prefix@r{ keyword}
-@vindex button-suffix@r{ keyword}
-@item :button-prefix
-@itemx :button-suffix
-Text around %[ %] in the format.
+@defun widget-apply widget property &rest args
+Apply the function stored in @var{property} to @var{widget}, passing @var{args}
+as additional arguments to the function.
 
-These can be
-@table @emph
-@item nil
-No text is inserted.
+Returns the result of that function call.
+@end defun
 
-@item a string
-The string is inserted literally.
+@defun widget-value widget
+Return the current value contained in @var{widget}.
 
-@item a symbol
-The value of the symbol is expanded according to this table.
-@end table
+Note that the value returned by this function might differ from what's
+stored in the @code{:value} property of @var{widget}.  This is because
+this function extracts the current value of @var{widget} from the
+buffer, taking editions into account.
 
-@vindex doc@r{ keyword}
-@item :doc
-The string inserted by the @samp{%d} escape in the format
-string.
+The value returned is in the external format, after getting it with
+the @code{:value-get} function.
 
-@vindex tag@r{ keyword}
-@item :tag
-The string inserted by the @samp{%t} escape in the format
-string.
+It is an error to call this function on an uninitialized widget.
+@end defun
 
-@vindex tag-glyph@r{ keyword}
-@item :tag-glyph
-Name of image to use instead of the string specified by @code{:tag} on
-Emacsen that supports it.
+@defun widget-value-set widget value
+Set the value contained in @var{widget} to @var{value}.
 
-@vindex help-echo@r{ keyword}
-@item :help-echo
-Specifies how to display a message whenever you move to the widget with
-either @code{widget-forward} or @code{widget-backward} or move the mouse
-over it (using the standard @code{help-echo} mechanism).  The argument
-is either a string to display, a function of one argument, the widget,
-which should return a string to display, or a form that evaluates to
-such a string.
+Converts @var{value} to the internal format, and then sets it by
+applying the @code{:value-set} function.
 
-@vindex follow-link@r{ keyword}
-@item :follow-link
-Specifies how to interpret a @key{mouse-1} click on the widget.
-@xref{Clickable Text,, Defining Clickable Text, elisp, the Emacs Lisp 
Reference Manual}.
+It is an error to call this function with an invalid @var{value}, that
+is, a value that @var{widget} cannot represent.
+@end defun
 
-@vindex indent@r{ keyword}
-@item :indent
-An integer indicating the absolute number of spaces to indent children
-of this widget.
+@defun widget-default-get widget
+Return the default external value of widget @var{widget}.
 
-@vindex offset@r{ keyword}
-@item :offset
-An integer indicating how many extra spaces to add to the widget's
-grandchildren compared to this widget.
+The default value is the one stored in @code{:value} or the result of
+applying the @code{:default-get} function to the arguments of
+@var{widget}, as stored in @code{:args}.  A value of @code{nil} is
+ignored by default, so in order for a widget to respect @code{nil} as
+a value, it has to override the @code{:default-get} function.
+@end defun
 
-@vindex extra-offset@r{ keyword}
-@item :extra-offset
-An integer indicating how many extra spaces to add to the widget's
-children compared to this widget.
+@defun widget-type-default-get widget
+Convert the @code{:type} attribute in @var{widget} and return its
+default value.
+@end defun
 
-@vindex notify@r{ keyword}
-@item :notify
-A function called each time the widget or a nested widget is changed.
-The function is called with two or three arguments.  The first argument
-is the widget itself, the second argument is the widget that was
-changed, and the third argument is the event leading to the change, if
-any.
+@defun widget-child-value-get widget
+Return the value of the first member of @code{:children} in
+@var{widget}.
+@end defun
 
-@vindex menu-tag@r{ keyword}
-@item :menu-tag
-Tag used in the menu when the widget is used as an option in a
-@code{menu-choice} widget.
+@defun widget-child-value-inline widget
+Return the inline value of the first member of @code{:children} in
+@var{widget}.
 
-@vindex menu-tag-get@r{ keyword}
-@item :menu-tag-get
-Function used for finding the tag when the widget is used as an option
-in a @code{menu-choice} widget.  By default, the tag used will be either the
-@code{:menu-tag} or @code{:tag} property if present, or the @code{princ}
-representation of the @code{:value} property if not.
+The inline value is whatever the function stored in
+@code{:value-inline} returns.
+@end defun
 
-@vindex match@r{ keyword}
-@item :match
-Should be a function called with two arguments, the widget and an
-external value, and should return non-@code{nil} if the widget can
-represent the specified value.
+@defun widget-type-value-create widget
+Create a child widget for @var{widget}, of type stored in
+@code{:type}.
 
-@vindex validate@r{ keyword}
-@item :validate
-A function which takes a widget as an argument, and returns @code{nil}
-if the widget's current value is valid for the widget.  Otherwise it
-should return the widget containing the invalid data, and set that
-widget's @code{:error} property to a string explaining the error.
+Creates the child widget taking the value from the @code{:value}
+property and stores the newly created widget in the @code{:children}
+property of @var{widget}.
+
+The value stored in @code{:type} should be an unconverted widget
+type.
+@end defun
 
-The following predefined function can be used:
+@defun widget-value-convert-widget widget
+Initializes the @code{:value} property of @var{widget} from
+@code{:args}.
 
-@defun widget-children-validate widget
-All the @code{:children} of @var{widget} must be valid.
+Sets @code{:args} to @code{nil} and returns the modified widget
+@var{widget}.
 @end defun
 
-@vindex tab-order@r{ keyword}
-@item :tab-order
-Specify the order in which widgets are traversed with
-@code{widget-forward} or @code{widget-backward}.  This is only partially
-implemented.
+@defun widget-value-value-get widget
+Return the value stored in @code{:value} for widget @var{widget}.
 
-@enumerate a
-@item
-Widgets with tabbing order @code{-1} are ignored.
+This is different to getting the current value for @var{widget} with
+@code{widget-value}, since that function extracts the value from the
+buffer.
+@end defun
 
-@item
-(Unimplemented) When on a widget with tabbing order @var{n}, go to the
-next widget in the buffer with tabbing order @var{n+1} or @code{nil},
-whichever comes first.
+@defun widget-apply-action widget &optional event
+Apply the function stored in @code{:action} to @var{widget}, in
+response to @var{event}.
 
-@item
-When on a widget with no tabbing order specified, go to the next widget
+It is an error to call this function with an inactive widget.
+@end defun
+
+@defun widget-parent-action widget &optional event
+Tell @code{:parent} of @var{widget} to handle @var{event}.
+
+Optional @var{event} is the event that triggered the action.
+@end defun
+
+@defun widget-child-validate widget
+Check that the first member of @code{:children} in @var{widget} is valid.
+
+To be valid means that the widget value passes the checks that the
+function stored in @code{:validate} makes.
+@end defun
+
+@defun widget-children-validate widget
+Check that all @code{:children} in @var{widget} are valid.
+
+Returns @code{nil} on success, or the first child that isn't valid.
+@end defun
+
+@defun widget-type-match widget value
+Return non-@code{nil} if @var{VALUE} matches the value for the
+@code{:type} widget.
+
+As with the other type functions, the widget stored in @code{:type}
+should be an unconverted widget.
+@end defun
+
+@defun widget-types-copy widget
+Copy the @code{:args} value in @var{widget} and store them in @code{:args}.
+
+Makes the copies by calling @code{widget-copy} on each element present
+in @code{:args}.  Returns the modified widget @var{widget}.
+@end defun
+
+@defun widget-types-convert-widget widget
+Convert the @code{:args} value in @var{widget} and store them in
+@code{args}.
+
+Returns the modified widget @var{widget}.
+@end defun
+
+@node Widgets and the Buffer
+@chapter Widgets and the Buffer
+This chapter describes commands that are specific to buffers that
+contain widgets.
+
+@cindex widget keybindings
+@defvr Const widget-keymap
+Keymap containing useful bindings for buffers containing widgets.
+
+Binds @key{TAB} and @kbd{C-@key{TAB}} to @code{widget-forward} and
+@code{widget-backward}, respectively.  It also binds @key{RET} to
+@code{widget-button-press} and @kbd{down-mouse-1} and
+@kbd{down-mouse-2} to @code{widget-button-click}.
+@end defvr
+
+There's also a keymap for events that the Widget library doesn't need
+to handle.
+
+@defvar widget-global-map
+Keymap used by @code{widget-button-press} and @code{widget-button-click}
+when not on a button.  By default this is @code{global-map}.
+@end defvar
+
+In addition to these two keymaps, each widget might define a keymap of
+its own, active when events happen at that widget.
+
+@cindex widget navigation
+The following navigation commands are available:
+
+@table @kbd
+@item @key{TAB}
+@deffn Command widget-forward &optional count
+Move point @var{count} buttons or editing fields forward.
+@end deffn
+@item @kbd{M-@key{TAB}}
+@itemx @kbd{S-@key{TAB}}
+@deffn Command widget-backward &optional count
+Move point @var{count} buttons or editing fields backward.
+@end deffn
+@end table
+
+
+When editing an @code{editable-field} widget, the following commands
+are available:
+
+@table @kbd
+@item @key{C-e}
+@deffn Command widget-end-of-line
+Move point to the end of field or end of line, whichever is first.
+@end deffn
+
+@item @kbd{C-k}
+@deffn Command widget-kill-line
+Kill to end of field or end of line, whichever is first.
+@end deffn
+
+@item @kbd{M-TAB}
+@deffn Command widget-complete
+Complete the content of the editable field at point.
+@end deffn
+
+@item @kbd{C-m}
+@deffn Command widget-field-activate
+Invoke the editable field at point.
+@end deffn
+@end table
+
+The following two are commands that can execute widget actions.
+@table @kbd
+@item @key{RET}
+@findex widget-button-press
+@deffn Command widget-button-press @var{pos} &optional @var{event}
+Invoke the button at @var{pos}, defaulting to point.
+
+Invocation means to run the function stored in the @code{:action}
+property.
+
+If point is not located on a button, invoke the binding in
+@code{widget-global-map} (by default the global map).
+@end deffn
+
+@kindex mouse-2 @r{(on button widgets})
+@item mouse-2
+@findex widget-button-click
+@deffn Command widget-button-click @var{event}
+Invoke the button at the location of the mouse pointer.
+
+If the mouse pointer is located in an editable text field, invoke the
+binding in @code{widget-global-map} (by default the global map).
+
+In case the mouse-click is on a widget, calls the function stored in
+the @code{:mouse-down-action} property.
+@end deffn
+@end table
+
+@node Widget Gallery
+@chapter Widget Gallery
+@cindex widget syntax
+All widgets can be created from a type specification.  The general
+syntax of a type specification is:
+
+@c FIXME: Add BNF reference here? If yes, what reference?
+@example
+@var{name} ::= (@var{name} [@var{keyword} @var{argument}]... @var{args})
+     |   @var{name}
+@end example
+
+Where @var{name} is a widget name, as defined with
+@code{define-widget}, @var{keyword} is the name of a property and
+@var{argument} is the value for that property, and @var{args} are
+interpreted in a widget specific way.  @xref{Defining New Widgets}.
+
+@menu
+* Basic Types::
+* Sexp Types::
+@end menu
+
+@node Basic Types
+@section Basic Types
+
+@menu
+* default::
+* item::
+* link::
+* url-link::
+* info-link::
+* function-link::
+* variable-link::
+* face-link::
+* file-link::
+* emacs-library-link::
+* emacs-commentary-link::
+* push-button::
+* editable-field::
+* text::
+* menu-choice::
+* radio-button-choice::
+* choice-item::
+* toggle::
+* radio-button-toggle::
+* checkbox::
+* checklist::
+* editable-list::
+* group::
+* documentation-string::
+@end menu
+
+@node default
+@subsection The @code{default} Widget
+@findex default@r{ widget}
+The most basic widget in the Widget Library is the @dfn{default}
+widget.  It provides the basic behavior for all other widgets, and all
+its properties are present by default in derived widgets.  You're
+seldom (if ever) going to effectively create a default widget, but
+here we describe its properties and behavior, so that we can describe
+other widgets only by mentioning the properties and behavior those
+other widgets specialize.
+
+@deffn Widget default
+Widget used as a base for other widgets.
+
+It provides most of the functionality that is referred to as ``by
+default'' in this text.  If you want to define a new widget from
+scratch, use the @code{default} widget as its base.
+@end deffn
+
+@cindex keyword arguments
+The following keyword arguments apply to all widgets:
+
+@table @code
+@vindex create@r{ keyword}
+@item :create
+Function to create a widget from scratch.
+
+The function takes one argument, a widget type, and creates a widget
+of that type, inserts it in the buffer, and returns a widget object.
+
+By default, it inserts the widget at point, using the format provided
+in the @code{:format} property.
+
+@vindex delete@r{ keyword}
+@item :delete
+Function to delete a widget.
+
+The function should take one argument, a widget, and should remove all
+traces of the widget from the buffer.
+
+The default value is:
+
+@defun widget-default-delete widget
+Remove @var{widget} from the buffer.
+Delete all @code{:children} and @code{:buttons} in @var{widget}.
+@end defun
+
+In most cases you should not change this value, but instead use
+@code{:value-delete} to make any additional cleanup.
+
+@cindex internal format
+@cindex external format
+@vindex value@r{ keyword}
+@item :value
+The initial value for widgets of this type.
+
+Typically, a widget represents its value in two formats: external and
+internal.  The external format is the value as the rest of Emacs sees
+it, and the internal format is a representation that the widget
+defines and uses in a widget specific way.
+
+Both formats might be the same for certain widgets and might differ
+for others, and there is no guarantee about which format the value
+stored in the @code{:value} property has.  However, when creating a
+widget or defining a new one (@pxref{Defining New Widgets}), the
+@code{:value} should be in the external format.
+
+@vindex value-to-internal@r{ keyword}
+@item :value-to-internal
+Function to convert the value to the internal format.
+
+The function takes two arguments, a widget and an external value, and
+returns the internal value.  The function is called on the present
+@code{:value} when the widget is created, and on any value set later
+with @code{widget-value-set}.
+
+@vindex value-to-external@r{ keyword}
+@item :value-to-external
+Function to convert the value to the external format.
+
+The function takes two arguments, a widget and an internal value, and
+returns the value in the external format.
+
+@vindex value-create@r{ keyword}
+@item :value-create
+Function to expand the @samp{%v} escape in the format string.
+
+It will be called with the widget as its argument and should insert a
+representation of the widget's value in the buffer.
+
+@vindex value-delete@r{ keyword}
+@item :value-delete
+A function that should remove the representation of the widget's value
+from the buffer.
+
+It will be called with the widget as its argument.  It doesn't have to
+remove the text, but it should release markers and delete nested widgets
+if these are not listed in @code{:children} or @code{:buttons}.
+
+By default, it's a no-op.
+
+@vindex value-get@r{ keyword}
+@item :value-get
+Function to extract the value of a widget, as it is displayed in the
+buffer.
+
+@vindex value-set@r{ keyword}
+@item :value-set
+Function that takes a widget and a value as arguments, and recreates
+it.
+
+The value must already be in the internal format for widget.  By
+default, it deletes the widget with the @code{:delete} function and
+creates it again with the @code{:create} function.
+
+@vindex value-inline@r{ keyword}
+@item :value-inline
+Function that takes a widget and returns its value, inlined.
+
+Inlined means that if the widget is not inline (i.e., its
+@code{:inline} property is @code{nil}), the return value is wrapped in
+a list.
+
+@vindex default-get@r{ keyword}
+@item :default-get
+Function that takes a widget and returns its default value.
+
+By default, it just returns the value stored in @code{:value}.
+
+@vindex format@r{ keyword}
+@item :format
+This string will be inserted in the buffer when you create a widget.
+The following @samp{%} escapes are available:
+
+@table @samp
+@item %[
+@itemx %]
+The text inside will be marked as a button.
+
+By default, the text will be shown in @code{widget-button-face}, and
+surrounded by brackets.
+
+@item %@{
+@itemx %@}
+The text inside will be displayed with the face specified by
+@code{:sample-face}.
+
+@item %v
+This will be replaced with the buffer representation of the widget's
+value.  What this is depends on the widget type.
+
+@item %d
+Insert the string specified by @code{:doc} here.
+
+@item %h
+Like @samp{%d}, with the following modifications: If the documentation
+string is more than one line, it will add a button which will toggle
+between showing only the first line, and showing the full text.
+Furthermore, if there is no @code{:doc} property in the widget, it will
+instead examine the @code{:documentation-property} property.  If it is a
+lambda expression, it will be called with the widget's value as an
+argument, and the result will be used as the documentation text.
+
+@item %t
+Insert the string specified by @code{:tag} here, or the @code{princ}
+representation of the value if there is no tag.
+
+@item %%
+Insert a literal @samp{%}.
+@end table
+
+@vindex button-face@r{ keyword}
+@item :button-face
+Face used to highlight text inside %[ %] in the format.
+
+@vindex button-prefix@r{ keyword}
+@vindex button-suffix@r{ keyword}
+@item :button-prefix
+@itemx :button-suffix
+Strings used as prefix and suffix for widgets that are buttons.
+
+By default, the values are @code{widget-button-prefix} and
+@code{widget-button-suffix}.
+
+Text around %[ %] in the format.
+
+These can be
+@table @emph
+@item nil
+No text is inserted.
+
+@item a string
+The string is inserted literally.
+
+@item a symbol
+The value of the symbol is expanded according to this table.
+@end table
+
+@vindex doc@r{ keyword}
+@item :doc
+The string inserted by the @samp{%d} escape in the format
+string.
+
+@vindex tag@r{ keyword}
+@item :tag
+The string inserted by the @samp{%t} escape in the format
+string.
+
+@vindex tag-glyph@r{ keyword}
+@item :tag-glyph
+Name of image to use instead of the string specified by @code{:tag} on
+Emacsen that supports it.
+
+@vindex help-echo@r{ keyword}
+@item :help-echo
+Specifies how to display a message whenever you move to the widget with
+either @code{widget-forward} or @code{widget-backward} or move the mouse
+over it (using the standard @code{help-echo} mechanism).
+
+The value is either a string to display, or a function of one
+argument, the widget.  If a function, it should return a string to
+display, or a form that evaluates to such a string.
+
+@vindex follow-link@r{ keyword}
+@item :follow-link
+Specifies how to interpret a @key{mouse-1} click on the widget.
+@xref{Clickable Text,, Defining Clickable Text, elisp, the Emacs Lisp 
Reference Manual}.
+
+@vindex indent@r{ keyword}
+@item :indent
+An integer indicating the absolute number of spaces to indent children
+of this widget.  Its value might be @code{nil} too, which corresponds
+to a value of 0.
+
+The default @code{:create} functions and the functions that create the
+value per se use this property as a rudimentary layout mechanism for
+the widgets.
+
+@vindex offset@r{ keyword}
+@item :offset
+An integer indicating how many extra spaces to add to the widget's
+grandchildren compared to this widget.
+
+@vindex extra-offset@r{ keyword}
+@item :extra-offset
+An integer indicating how many extra spaces to add to the widget's
+children compared to this widget.
+
+@vindex menu-tag@r{ keyword}
+@item :menu-tag
+Tag used in the menu when the widget is used as an option in a
+@code{menu-choice} widget.
+
+@vindex menu-tag-get@r{ keyword}
+@item :menu-tag-get
+Function that takes a widget and returns the tag when the widget is
+used as an option in a @code{menu-choice} widget.
+
+By default, the tag used will be either the @code{:menu-tag} or
+@code{:tag} property if present, or the @code{princ} representation of
+the @code{:value} property if not.
+
+@vindex match@r{ keyword}
+@item :match
+Should be a function called with two arguments, the widget and an
+external value, and should return non-@code{nil} if the widget can
+represent the specified value.
+
+@vindex validate@r{ keyword}
+@item :validate
+A function which takes a widget as an argument, and returns @code{nil}
+if the widget's current value is valid for the widget.
+
+Otherwise, it should return the widget containing the invalid data,
+and set that widget's @code{:error} property to a string explaining
+the error.
+
+By default, it always returns @code{nil}.
+
+@vindex tab-order@r{ keyword}
+@item :tab-order
+Specify the order in which widgets are traversed with
+@code{widget-forward} or @code{widget-backward}.  This is only partially
+implemented.
+@enumerate a
+@item
+Widgets with tabbing order @code{-1} are ignored.
+
+@item
+(Unimplemented) When on a widget with tabbing order @var{n}, go to the
+next widget in the buffer with tabbing order @var{n+1} or @code{nil},
+whichever comes first.
+
+@item
+When on a widget with no tabbing order specified, go to the next widget
 in the buffer with a positive tabbing order, or @code{nil}
 @end enumerate
 
-@vindex parent@r{ keyword}
-@item :parent
-The parent of a nested widget (e.g., a @code{menu-choice} item or an
-element of a @code{editable-list} widget).
+@vindex parent@r{ keyword}
+@item :parent
+The parent of a nested widget (e.g., a @code{menu-choice} item or an
+element of a @code{editable-list} widget).
+
+@vindex sibling-args@r{ keyword}
+@item :sibling-args
+This keyword is only used for members of a @code{radio-button-choice} or
+@code{checklist}.  The value should be a list of extra keyword
+arguments, which will be used when creating the @code{radio-button} or
+@code{checkbox} associated with this item.
+
+@vindex completions-function@r{ keyword}
+@item :completions-function
+Function that takes a widget and returns completion data for that
+widget, like @code{completion-at-point-functions} would.
+@xref{Completion,,,elisp, the Emacs Lisp Reference Manual}.  It's
+used by @code{editable-field} widgets to provide completions.
+
+By default, it looks into the property @code{:completions}, which
+should be a completion table.  If @code{:completions} is @code{nil},
+then it calls the function stored either in the @code{:complete} or
+@code{:complete-function} property.
+
+@vindex format-handler@r{ keyword}
+@item :format-handler
+Function to handle unknown @samp{%} escapes in the format string.
+
+It takes a widget and the character that follows the @samp{%} as
+arguments.  You can set this to allow your widget to handle
+non-standard escapes in your own specialized widgets.
+
+@findex widget-default-format-handler
+You should end up calling @code{widget-default-format-handler} to handle
+unknown escape sequences, which will handle the @samp{%h} and any future
+escape sequences, as well as give an error for unknown escapes.
+
+@vindex button-face-get@r{ keyword}
+@item :button-face-get
+Function to return the face used to fontify a widget button.
+
+Takes a widget and returns an appropriate face for the widget.  By
+default, it either returns the face stored in the @code{:button-face}
+property, or calls the @code{:button-face-get} function from the
+parent of the widget, if it has one.
+
+@vindex mouse-face-get@r{ keyword}
+@item :mouse-face-get
+Function to return the face used to fontify a widget when the mouse
+pointer hovers over it.
+
+Takes a widget and returns an appropriate face.  By default, it either
+returns the face stored in the @code{:mouse-face} property, or calls
+the @code{:button-face-get} function from the parent of the widget, if
+it has one.
+
+@vindex copy@r{ keyword}
+@item :copy
+Function to deep copy a widget type.
+
+It takes a shallow copy of the widget type as an argument (made by
+@code{copy-sequence}), and returns a deep copy.  The purpose of this
+is to avoid having different instances of combined widgets share
+nested attributes.
+
+Its value by default is @code{identity}.
+
+@vindex active@r{ keyword}
+@item :active
+Function that takes a widget and returns @code{t} if it is active.
+
+A widget might be effectively always active, if its
+@code{:always-active} property is @code{t}.
+
+@cindex active widget
+@cindex inactive widget
+@cindex activate a widget
+@cindex deactivate a widget
+Widgets can be in two states: active, which means they are modifiable by
+the user, or inactive, which means they cannot be modified by the user.
+You can query or set the state with the following code:
+
+@lisp
+;; Examine if @var{widget} is active or not.
+(if (widget-apply @var{widget} :active)
+    (message "Widget is active.")
+  (message "Widget is inactive.")
+
+;; Make @var{widget} inactive.
+(widget-apply @var{widget} :deactivate)
+
+;; Make @var{widget} active.
+(widget-apply @var{widget} :activate)
+@end lisp
+
+A widget is inactive if it, or any of its ancestors (found by
+following the @code{:parent} link), have been deactivated.  To make sure
+a widget is really active, you must therefore activate both it and
+all its ancestors.
+
+@lisp
+(while widget
+  (widget-apply widget :activate)
+  (setq widget (widget-get widget :parent)))
+@end lisp
+
+You can check if a widget has been made inactive by examining the value
+of the @code{:inactive} keyword.  If this is non-@code{nil}, the widget itself
+has been deactivated.  This is different from using the @code{:active}
+keyword, in that the latter tells you if the widget @strong{or} any of
+its ancestors have been deactivated.  Do not attempt to set the
+@code{:inactive} keyword directly.  Use the @code{:activate}
+@code{:deactivate} functions instead.
+
+@vindex activate@r{ keyword}
+@item :activate
+Function that takes a widget and makes it active for user
+modifications.
+
+@vindex deactivate@r{ keyword}
+@item :deactivate
+Function that takes a widget and makes it inactive for user
+modifications.
+
+@vindex action@r{ keyword}
+@item :action
+Function that takes a widget and optionally an event, and handles a
+user initiated event.
+
+By default, uses the @code{:notify} function to notify the widget's
+parent about the event.
+
+@vindex mouse-down-action@r{ keyword}
+@item :mouse-down-action
+Function that takes a widget and optionally an event, and handles a
+mouse click on the widget.
+
+By default, it does nothing.
+
+@vindex notify@r{ keyword}
+@item :notify
+A function called each time the widget or a nested widget is changed.
+
+The function is called with two or three arguments.  The first argument
+is the widget itself, the second argument is the widget that was
+changed, and the third argument is the event leading to the change, if
+any.
+
+By default, it passes the notification to the widget's parent.
+
+@vindex prompt-value@r{ keyword}
+@item :prompt-value
+Function to prompt for a value in the minibuffer.
+
+The function should take four arguments, a widget, a prompt (a
+string), a value and a boolean, and should return a value for the
+widget, entered by the user.
+
+The prompt is the prompt to use.  The value is the default value to
+use, unless the fourtha argument is non-@code{nil}, in which case
+there is no default value.
+
+The function should read the value using the method most natural for
+this widget, and does not have to check that it matches.
+@end table
+
+@node item
+@subsection The @code{item} Widget
+@findex item@r{ widget}
+
+Syntax:
+
+@example
+@var{type} ::= (item [@var{keyword} @var{argument}]... @var{value})
+@end example
 
-@vindex sibling-args@r{ keyword}
-@item :sibling-args
-This keyword is only used for members of a @code{radio-button-choice} or
-@code{checklist}.  The value should be a list of extra keyword
-arguments, which will be used when creating the @code{radio-button} or
-@code{checkbox} associated with this item.
+A useful widget that holds a constant value, and can be included in
+other widgets.  Its super is the @code{default} widget.
 
-@end table
+As can be seen in the syntax, the @code{item} widget is one of the
+widget that handles the @var{args} argument to @code{widget-create} in
+a specific way.  If present, @var{value} is used to initialize the
+@code{:value} property.  When created, it inserts the value as a
+string in the buffer.
 
-@deffn {User Option} widget-image-directory
-Directory where Widget should look for images.
-Widget will look here for a file with the same name as specified for the
-image, with either a @file{.xpm} (if supported) or @file{.xbm} extension.
-@end deffn
+By default, it has the following properties:
 
-@deffn{User Option} widget-image-enable
-If non-@code{nil}, allow images to appear on displays where they are supported.
-@end deffn
+@table @code
+@item :convert-widget
+The function that allows it to handle @var{value}.
 
+@item :value-create
+Prints the representation of @code{:value} in the buffer.
 
-@menu
-* link::
-* url-link::
-* info-link::
-* push-button::
-* editable-field::
-* text::
-* menu-choice::
-* radio-button-choice::
-* item::
-* choice-item::
-* toggle::
-* checkbox::
-* checklist::
-* editable-list::
-* group::
-@end menu
+@item :value-get
+Returns the value stored in @code{:value}.
+
+@item :match
+A value matches the @code{item} widget if it's @code{equal} to its
+@code{:value}.
+
+@item :match-inline
+Inline values match the @code{item} widget if @code{:value} is a
+sublist of values.
+
+@item :action
+The @code{item} widget notifies itself of an event.
+
+@item :format
+By default, the @code{item} widget inserts its tag in the buffer.
+@end table
 
 @node link
-@section The @code{link} Widget
+@subsection The @code{link} Widget
 @findex link@r{ widget}
 
 Syntax:
@@ -731,22 +1421,42 @@ Syntax:
 @var{type} ::= (link [@var{keyword} @var{argument}]...  [ @var{value} ])
 @end example
 
+A widget to represent an embedded link.  Its super is the @code{item}
+widget.
+
 The @var{value}, if present, is used to initialize the @code{:value}
 property.  The value should be a string, which will be inserted in the
 buffer.
 
-By default the link will be shown in brackets.
+By default, it has the following properties:
 
-@defopt widget-link-prefix
-String to prefix links.
-@end defopt
+@table @code
+@item :button-prefix
+The value of @code{widget-link-prefix}.
 
-@defopt widget-link-suffix
-String to suffix links.
-@end defopt
+@item :button-suffix
+The value of @code{widget-link-suffix}.
+
+@item :keymap
+A custom keymap for the link widget, so that it can respond to mouse clicks.
+
+@item :follow-link
+This property allows the link to respect the value of
+@code{mouse-1-click-follows-link}.  @xref{Clickable Text,,,elisp, the Emacs 
Lisp Reference Manual}.
+
+@item :format
+Buttonizes the link, to make it clickable.
+
+If you override this property, you should make sure to provide the
+@samp{%[} and @samp{%]} escape sequences, so that the link is
+clickable.
+
+@end table
+
+By default the link will be shown in brackets.
 
 @node url-link
-@section The @code{url-link} Widget
+@subsection The @code{url-link} Widget
 @findex url-link@r{ widget}
 
 Syntax:
@@ -755,12 +1465,14 @@ Syntax:
 @var{type} ::= (url-link [@var{keyword} @var{argument}]...  @var{url})
 @end example
 
-@findex browse-url-browser-function@r{, and @code{url-link} widget}
-When this link is invoked, the @acronym{WWW} browser specified by
-@code{browse-url-browser-function} will be called with @var{url}.
+A widget to represent a link to a web page.  Its super is the
+@code{link} widget.
+
+It overrides the @code{:action} property to open up the @var{url}
+specified.
 
 @node info-link
-@section The @code{info-link} Widget
+@subsection The @code{info-link} Widget
 @findex info-link@r{ widget}
 
 Syntax:
@@ -769,11 +1481,104 @@ Syntax:
 @var{type} ::= (info-link [@var{keyword} @var{argument}]...  @var{address})
 @end example
 
-When this link is invoked, the built-in Info reader is started on
-@var{address}.
+A widget to represent a link to an info file.  Its super is the
+@code{link} widget.
+
+It overrides the @code{:action} property, to a function to start the
+built-in Info reader on @var{address}, when invoked.
+
+@node function-link
+@subsection The @code{function-link} Widget
+@findex function-link@r{ widget}
+Syntax:
+
+@example
+@var{type} ::= (function-link [@var{keyword} @var{argument}]...  
@var{function})
+@end example
+
+A widget to represent a link to an Emacs function.  Its super is the
+@code{link} widget.
+
+It overrides the @code{:action} property, to a function to describe
+@var{function}.
+
+@node variable-link
+@subsection The @code{variable-link} Widget
+@findex variable-link@r{ widget}
+Syntax:
+
+@example
+@var{type} ::= (variable-link [@var{keyword} @var{argument}]...  @var{var})
+@end example
+
+A widget to represent a link to an Emacs variable.  Its super is the
+@code{link} widget.
+
+It overrides the @code{:action} property, to a function to describe
+@var{var}.
+
+@node face-link
+@subsection The @code{face-link} Widget
+@findex face-link@r{ widget}
+Syntax:
+
+@example
+@var{type} ::= (face-link [@var{keyword} @var{argument}]...  @var{face})
+@end example
+
+A widget to represent a link to an Emacs face.  Its super is the
+@code{link} widget.
+
+It overrides the @code{:action} property, to a function to describe
+@var{face}.
+
+@node file-link
+@subsection The @code{file-link} Widget
+@findex file-link@r{ widget}
+Syntax:
+
+@example
+@var{type} ::= (file-link [@var{keyword} @var{argument}]...  @var{file})
+@end example
+
+A widget to represent a link to a file.  Its super is the
+@code{link} widget.
+
+It overrides the @code{:action} property, to a function to find the file
+@var{file}.
+
+@node emacs-library-link
+@subsection The @code{emacs-library-link} Widget
+@findex emacs-library-link@r{ widget}
+Syntax:
+
+@example
+@var{type} ::= (emacs-library-link [@var{keyword} @var{argument}]...  
@var{file})
+@end example
+
+A widget to represent a link to an Emacs Lisp file.  Its super is the
+@code{link} widget.
+
+It overrides the @code{:action} property, to a function to find the file
+@var{file}.
+
+@node emacs-commentary-link
+@subsection The @code{emacs-commentary-link} Widget
+@findex emacs-commentary-link@r{ widget}
+Syntax:
+
+@example
+@var{type} ::= (emacs-commentary-link [@var{keyword} @var{argument}]...  
@var{file})
+@end example
+
+A widget to represent a link to the Comment section of an Emacs Lisp
+file.  Its super is the @code{link} widget.
+
+It overrides the @code{:action} property, to a function to find the file
+@var{file} and put point in the Comment section.
 
 @node  push-button
-@section The @code{push-button} Widget
+@subsection The @code{push-button} Widget
 @findex push-button@r{ widget}
 
 Syntax:
@@ -782,22 +1587,37 @@ Syntax:
 @var{type} ::= (push-button [@var{keyword} @var{argument}]...  [ @var{value} ])
 @end example
 
+A widget that acts as a pushable button.  Its super is the @code{item}
+widget.
+
 The @var{value}, if present, is used to initialize the @code{:value}
 property.  The value should be a string, which will be inserted in the
 buffer.
 
-By default the tag will be shown in brackets.
+By default, it has the following properties:
+@table @code
+@item :button-prefix
+The empty string.
 
-@defopt widget-push-button-prefix
-String to prefix push buttons.
-@end defopt
+@item :button-suffix
+The empty string.
 
-@defopt widget-push-button-suffix
-String to suffix push buttons.
-@end defopt
+@item :value-create
+Inserts a representation of the ``on'' and ``off'' states for the push
+button.
+
+The representation might be an image, stored in the @code{:tag-glyph}
+property, or text.  If it is text, it might be the value of the
+@code{:tag} property, or the @code{:value} of the widget, surrounded
+with @code{widget-push-button-prefix} and
+@code{widget-push-button-suffix}.  @xref{Customization}.
+
+@item :format
+Buttonizes the widget, to make it clickable.
+@end table
 
 @node editable-field
-@section The @code{editable-field} Widget
+@subsection The @code{editable-field} Widget
 @findex editable-field@r{ widget}
 
 Syntax:
@@ -806,56 +1626,137 @@ Syntax:
 @var{type} ::= (editable-field [@var{keyword} @var{argument}]... [ @var{value} 
])
 @end example
 
+A widget that can be edited by the user.  Its super is the
+@code{default} widget.
+
 The @var{value}, if present, is used to initialize the @code{:value}
 property.  The value should be a string, which will be inserted in the
-field.  This widget will match all string values.
+field.  If not present, @code{:value} is the empty string.
+
+@strong{Warning:} In an @code{editable-field} widget, the editable
+field must not be adjacent to another widget---that won't work.
+You must put some text in between.  Either make this text part of
+the @code{editable-field} widget itself, or insert it with
+@code{widget-insert}.
 
-The following extra properties are recognized:
+This widget either overrides or adds the following properties:
 
 @table @code
+@item :convert-widget
+Just like the @code{item} widget, this function allows it to
+initialize @code{:value} from @var{value}.
+
+@vindex keymap@r{ keyword}
+@vindex widget-field-keymap
+@item :keymap
+Keymap used in the editable field.
+
+The default value is @code{widget-field-keymap}, which allows the user
+to use all the normal editing commands, even if the buffer's major
+mode suppresses some of them.  Pressing @key{RET} invokes the function
+specified by @code{:action}.
+
+@item :format
+By default, it specifies to insert only the widget's value.
+
+@strong{Warning:} In an @code{editable-field} widget, the @samp{%v} escape
+must be preceded by some other text in the @code{:format} string (if
+specified).
+
 @vindex size@r{ keyword}
 @item :size
-The width of the editable field.@*
+The width of the editable field.
+
 By default the field will reach to the end of the line.
 
 @vindex value-face@r{ keyword}
 @item :value-face
-Face used for highlighting the editable field.  Default is
-@code{widget-field-face}, see @ref{User Interface}.
+Face used for highlighting the editable field.
+
+Default is @code{widget-field-face}, @pxref{User Interface}.
 
 @vindex secret@r{ keyword}
 @item :secret
-Character used to display the value.  You can set this to, e.g., @code{?*}
-if the field contains a password or other secret information.  By
-default, this is @code{nil}, and the value is not secret.
+Character used to display the value.
+
+You can set this to, e.g., @code{?*} if the field contains a password
+or other secret information.  By default, this is @code{nil}, and the
+value is not secret.
 
 @vindex valid-regexp@r{ keyword}
 @item :valid-regexp
 By default the @code{:validate} function will match the content of the
-field with the value of this attribute.  The default value is @code{""}
-which matches everything.
+field with the value of this attribute.
 
-@vindex keymap@r{ keyword}
-@vindex widget-field-keymap
-@item :keymap
-Keymap used in the editable field.  The default value is
-@code{widget-field-keymap}, which allows you to use all the normal
-editing commands, even if the buffer's major mode suppresses some of
-them.  Pressing @key{RET} invokes the function specified by
-@code{:action}.
+The default value is @code{""} which matches everything.
+
+@item :validate
+Returns @code{nil} if the current value of the widget matches the
+@code{:valid-regexp} value.
+
+@item :prompt-internal
+A function to read a value for widget, used by the
+@code{:prompt-value} function.
+
+@item :prompt-history
+A variable that holds the history of field minibuffer edits.
+
+@item :prompt-value
+A function that uses the @code{:prompt-internal} function and the
+@code{:prompt-history} value to prompt for a string, and retun the
+user response in the external format.
+
+@item :action
+When invoked, moves point to the next field.
+
+@item :value-create
+Function that takes care of creating the widget, respecting its
+@code{:size} and @code{:value}.
+
+@item :value-set
+Function to use to modify programatically the current value of the
+widget.
+
+@item :value-delete
+Function that removes the widget so it cannot be edited anymore.
+
+@item :value-get
+Function to return the current text in the widget.
+
+It takes an optional argument, @var{no-truncate}.  If
+@var{no-truncate} is nil, truncates trailing spaces.
+
+@item :match
+Function that makes the widget match any string value.
 @end table
 
 @node text
-@section The @code{text} Widget
+@subsection The @code{text} Widget
 @findex text@r{ widget}
 
+Syntax:
+
+@example
+@var{type} ::= (text [@var{keyword} @var{argument}]... [ @var{value} ])
+@end example
+
+A widget just like the @code{editable-field} widget, but intended for
+multiline text fields.  Its super is the @code{editable-field} widget.
+
+It overrides the following properties:
+
+@table @code
+@item :format
+By default, prints a tag and the value.
+
 @vindex widget-text-keymap
-This is just like @code{editable-field}, but intended for multiline text
-fields.  The default @code{:keymap} is @code{widget-text-keymap}, which
-does not rebind the @key{RET} key.
+@item :keymap
+The default is @code{widget-text-keymap}, which does not rebind the
+@key{RET} key.
+@end table
 
 @node menu-choice
-@section The @code{menu-choice} Widget
+@subsection The @code{menu-choice} Widget
 @findex menu-choice@r{ widget}
 
 Syntax:
@@ -864,21 +1765,37 @@ Syntax:
 @var{type} ::= (menu-choice [@var{keyword} @var{argument}]... @var{type} ... )
 @end example
 
+A widget to represent a menu of options.  Its super is the
+@code{default} widget.
+
 The @var{type} argument represents each possible choice.  The widget's
-value will be that of the chosen @var{type} argument.  This widget will
-match any value matching at least one of the specified @var{type}
-arguments.
+value will be that of the chosen @var{type} argument.
+
+It either overrides or adds the following properties:
 
 @table @code
+@item :convert-widget
+A function that takes care of converting each possible choice.
+
+@item :copy
+A function to copy each possible choice.
+
+@item :format
+By default, buttonize the tag and show the value.
+
 @vindex void@r{ keyword}
 @item :void
 Widget type used as a fallback when the value does not match any of the
 specified @var{type} arguments.
 
+By default this is an @code{item} widget.
+
 @vindex case-fold@r{ keyword}
 @item :case-fold
-Set this to @code{nil} if you don't want to ignore case when prompting for a
-choice through the minibuffer.
+If @code{nil} don't ignore case when prompting for a choice through
+the minibuffer.
+
+By default, its value is @code{t}.
 
 @vindex children@r{ keyword}
 @item :children
@@ -892,10 +1809,54 @@ The current chosen type.
 @vindex args@r{ keyword}
 @item :args
 The list of types.
+
+@item :value-create
+The function that inserts the current value for the widget.
+
+It inserts the first choice that matches, as with the @code{:match}
+function, the value of the widget.
+
+@item :value-get
+Returns the value of the first child for the widget (see the
+description for @code{:children} above).
+
+@item :value-inline
+Returns the inline value of the first child for the widget.
+
+@item :default-get
+The default value for this widget is the default value for the first
+choice, in case @code{:value} is missing.
+
+This means that if you want a specific default value for the
+@code{menu-choice} widget, you should either pass a @code{:value}
+property when creating it, or arrange the choices so that the first
+one can hold your desired default value.
+
+@item :mouse-down-action
+A function that takes care of showing a menu, if possible and desired.
+
+@item :action
+A function that takes care of getting a new choice for the widget.
+
+Depending on the number of choices available, it may show a menu or
+just toggle the choices, or even do nothing at all.
+
+After getting the choice, it recreates the widget and notifies it.
+
+@item :validate
+Returns @code{nil} if the widget's value is a valid choice.
+
+@item :match
+This widget will match any value matching at least one of the
+specified @var{type} arguments.
+
+@item :match-inline
+A function that returns non-@code{nil} if the values match the widget,
+taking into account the @code{:inline} property.
 @end table
 
 @node radio-button-choice
-@section The @code{radio-button-choice} Widget
+@subsection The @code{radio-button-choice} Widget
 @findex radio-button-choice@r{ widget}
 
 Syntax:
@@ -904,14 +1865,28 @@ Syntax:
 @var{type} ::= (radio-button-choice [@var{keyword} @var{argument}]...  
@var{type} ... )
 @end example
 
-The component types specify the choices, with one radio button for
+A widget to represent a choice from multiple options.  Its super is
+the @code{default} widget.
+
+The component @var{types} specify the choices, with one radio button for
 each.  The widget's value will be that of the chosen @var{type}
-argument.  This widget matches any value that matches at least one of
-the specified @var{type} arguments.
+argument.
 
-The following extra properties are recognized.
+It overrides the following properties:
 
 @table @code
+@item :convert-widget
+As other composite widgets, a function that takes care of converting
+each available choice.
+
+@item :copy
+A function to copy each available choice.
+
+@item :action
+A function that checks if any radio button was pressed and activates
+the pressed one, possibly deactivating an old one.  Then, it notifies
+itself.
+
 @vindex entry-format@r{ keyword}
 @item :entry-format
 This string will be inserted for each entry in the list.
@@ -925,6 +1900,9 @@ Replace with the radio button.
 Insert a literal @samp{%}.
 @end table
 
+@item :format
+By default, it inserts its value.
+
 @vindex button-args@r{ keyword}
 @item :button-args
 A list of keywords to pass to the radio buttons.  Useful for setting,
@@ -940,42 +1918,55 @@ The widgets representing each type.
 
 @vindex choice@r{ keyword}
 @item :choice
-The current chosen type
+The current chosen type.
 
 @vindex args@r{ keyword}
 @item :args
 The list of types.
-@end table
 
-You can add extra radio button items to a @code{radio-button-choice}
-widget after it has been created with the function
-@code{widget-radio-add-item}.
+@item :value-create
+A function to insert all available choices.
 
-@defun widget-radio-add-item widget type
-Add to @code{radio-button-choice} widget @var{widget} a new radio button
-item of type @var{type}.
-@end defun
+@item :value-get
+Returns the value for the chosen widget.
 
-Please note that such items added after the @code{radio-button-choice}
-widget has been created will @strong{not} be properly destructed when
-you call @code{widget-delete}.
+@item :value-set
+A function to set the value to one of its available options.
 
-@node item
-@section The @code{item} Widget
-@findex item@r{ widget}
+@item :value-inline
+A function that returns the inline value of the child widget.
 
-Syntax:
+@item :offset
+By default, this widget has an offset of 4.
 
-@example
-@var{item} ::= (item [@var{keyword} @var{argument}]... @var{value})
-@end example
+@item :validate
+The widget validates if the current value is valid for one of its
+children.
+
+@item :match
+This widget matches any value that matches at least one of
+the specified @var{type} arguments.
+
+@item :match-inline
+Like the @code{:match} function, but taking into account inline
+values.
+@end table
+
+You can add extra radio button items to a @code{radio-button-choice}
+widget after it has been created with the function
+@code{widget-radio-add-item}.
+
+@defun widget-radio-add-item widget type
+Add to @code{radio-button-choice} widget @var{widget} a new radio button
+item of type @var{type}.
+@end defun
 
-The @var{value}, if present, is used to initialize the @code{:value}
-property.  The value should be a string, which will be inserted in the
-buffer.  This widget will only match the specified value.
+Please note that such items added after the @code{radio-button-choice}
+widget has been created will @strong{not} be properly destructed when
+you call @code{widget-delete}.
 
 @node choice-item
-@section The @code{choice-item} Widget
+@subsection The @code{choice-item} Widget
 @findex choice-item@r{ widget}
 
 Syntax:
@@ -984,14 +1975,26 @@ Syntax:
 @var{item} ::= (choice-item [@var{keyword} @var{argument}]... @var{value})
 @end example
 
+A widget to represent a choice in a @code{menu-choice} widget.  Its
+super is the @code{item} widget.
+
 The @var{value}, if present, is used to initialize the @code{:value}
-property.  The value should be a string, which will be inserted in the
-buffer as a button.  Activating the button of a @code{choice-item} is
-equivalent to activating the parent widget.  This widget will only match
-the specified value.
+property.
+
+It overrides the following properties:
+
+@table @code
+@item :action
+Activating the button of a @code{choice-item} is equivalent to
+activating the parent widget.
+
+@item :format
+By default, it buttonizes the tag (i.e., its value) and adds a newline
+character at the end of the widget.
+@end table
 
 @node toggle
-@section The @code{toggle} Widget
+@subsection The @code{toggle} Widget
 @findex toggle@r{ widget}
 
 Syntax:
@@ -1000,43 +2003,136 @@ Syntax:
 @var{type} ::= (toggle [@var{keyword} @var{argument}]...)
 @end example
 
+A widget that can toggle between two states.  Its super is the
+@code{item} widget.
+
 The widget has two possible states, @samp{on} and @samp{off}, which
 correspond to a @code{t} or @code{nil} value, respectively.
 
-The following extra properties are recognized:
+It either overrides or adds the following properties:
 
 @table @code
+@item :format
+By default, it buttonizes the value and adds a newline at the end of
+the widget.
+
 @item :on
 A string representing the @samp{on} state.  By default the string
 @samp{on}.
+
 @item :off
 A string representing the @samp{off} state.  By default the string
 @samp{off}.
+
 @vindex on-glyph@r{ keyword}
 @item :on-glyph
 Name of a glyph to be used instead of the @samp{:on} text string, on
 emacsen that supports this.
+
 @vindex off-glyph@r{ keyword}
 @item :off-glyph
 Name of a glyph to be used instead of the @samp{:off} text string, on
 emacsen that supports this.
+
+@item :value-create
+A function for creating the widget's value, according to its
+@samp{:on} or @samp{:off} state.
+
+@item :action
+Function to toggle the state of the widget.  After toggling, it
+notifies itself.
+
+@item :match
+This widget matches anything.
+@end table
+
+@node radio-button-toggle
+@subsection The @code{radio-button-toggle} Widget
+@findex radio-button-toggle@r{ widget}
+Syntax:
+
+@example
+@var{type} ::= (radio-button-toggle [@var{keyword} @var{argument}]...)
+@end example
+
+A toggle to use in the @code{radio} widget.
+
+It overrides the following properties:
+
+@table @code
+@item :button-prefix
+The empty string.
+
+@item :button-suffix
+The empty string.
+
+@item :on
+The string ``(*)'', to represent the @samp{on} state.
+
+@item :off
+The string ``( )'', to represent the @samp{off} state.
+
+@item :on-glyph
+The name of an image to represent the @samp{on} state.
+
+@item :off-glpyh
+The name of an image to represent the @samp{off} state.
+
+@item :format
+By default, it buttonizes its value.
+
+@item :notify
+A function to notify its parent.
 @end table
 
 @node checkbox
-@section The @code{checkbox} Widget
+@subsection The @code{checkbox} Widget
 @findex checkbox@r{ widget}
 
-This widget has two possible states, @samp{selected} and
-@samp{unselected}, which corresponds to a @code{t} or @code{nil} value.
-
 Syntax:
 
 @example
 @var{type} ::= (checkbox [@var{keyword} @var{argument}]...)
 @end example
 
+A widget to represent a toggle widget, with a checkbox.  Its super is
+the @code{toggle} widget.
+
+This widget has two possible states, @samp{selected} and
+@samp{unselected}, which corresponds to a @code{t} or @code{nil}
+value, respectively.
+
+It either overrides or adds the following properties:
+
+@table @code
+@item :button-prefix
+The empty string.
+
+@item :button-suffix
+The empty string.
+
+@item :format
+By default, buttonizes the value.
+
+@item :on
+By default, the string ``[X]''.
+
+@item :off
+By default, the string ``[ ]''.
+
+@item :on-glyph
+The name of the image to use when the state is @samp{on}.
+
+@item :off-glyph
+The name of the image to use when the state is @samp{off}.
+
+@item :action
+A function that toggles the checkbox, notifies the parents and in the
+@samp{on} state, activates its siblings.
+@end table
+
 @node checklist
-@section The @code{checklist} Widget
+@subsection The @code{checklist} Widget
 @findex checklist@r{ widget}
 
 Syntax:
@@ -1045,14 +2141,26 @@ Syntax:
 @var{type} ::= (checklist [@var{keyword} @var{argument}]...  @var{type} ... )
 @end example
 
+A widget to represent a multiplice choice.  Its super is the
+@code{default} widget.
+
 The @var{type} arguments represent each checklist item.  The widget's
 value will be a list containing the values of all checked @var{type}
-arguments.  The checklist widget will match a list whose elements all
-match at least one of the specified @var{type} arguments.
+arguments.
 
-The following extra properties are recognized:
+It either overrides or adds the following properties:
 
 @table @code
+@item :convert-widget
+As other composite widgets, a function that takes care of converting
+each checklist item.
+
+@item :copy
+A function to copy each checklist item.
+
+@item :format
+By default, it inserts its value.
+
 @vindex entry-format@r{ keyword}
 @item :entry-format
 This string will be inserted for each entry in the list.
@@ -1066,14 +2174,6 @@ Replace with the checkbox.
 Insert a literal @samp{%}.
 @end table
 
-@vindex greedy@r{ keyword}
-@item :greedy
-Usually a checklist will only match if the items are in the exact
-sequence given in the specification.  By setting @code{:greedy} to
-non-@code{nil}, it will allow the items to come in any sequence.
-However, if you extract the value they will be in the sequence given
-in the checklist, i.e., the original sequence is forgotten.
-
 @vindex button-args@r{ keyword}
 @item :button-args
 A list of keywords to pass to the checkboxes.  Useful for setting,
@@ -1090,10 +2190,35 @@ The widgets representing each type.
 @vindex args@r{ keyword}
 @item :args
 The list of types.
+
+@item :value-create
+The function that takes care of inserting all values.
+
+@item :value-get
+A function that returns all values of selected items.
+
+@item :validate
+A function that ensures all selected children are valid.
+
+@item :match
+The checklist widget will match a list whose elements all
+match at least one of the specified @var{type} arguments.
+
+@item :match-inline
+Like the @code{:match} function, but taking into account the
+@code{:inline} property.
+
+@vindex greedy@r{ keyword}
+@item :greedy
+Usually a checklist will only match if the items are in the exact
+sequence given in the specification.  By setting @code{:greedy} to
+non-@code{nil}, it will allow the items to come in any sequence.
+However, if you extract the value they will be in the sequence given
+in the checklist, i.e., the original sequence is forgotten.
 @end table
 
 @node editable-list
-@section The @code{editable-list} Widget
+@subsection The @code{editable-list} Widget
 @findex editable-list@r{ widget}
 
 Syntax:
@@ -1102,12 +2227,19 @@ Syntax:
 @var{type} ::= (editable-list [@var{keyword} @var{argument}]... @var{type})
 @end example
 
-The value is a list, where each member represents one widget of type
-@var{type}.
+A widget that can hold a variable list of widgets of the same type,
+represented by @var{type}.  Its super is the @code{default} widget.
 
-The following extra properties are recognized:
+It either overrides or adds the following properties:
 
 @table @code
+@item :convert-widget
+As other composite widgets, a function that takes care of converting
+each type in @var{type}.
+
+@item :copy
+A function to copy the types given in @var{type}.
+
 @vindex entry-format@r{ keyword}
 @item :entry-format
 This string will be inserted for each entry in the list.
@@ -1117,9 +2249,9 @@ The following @samp{%} escapes are available:
 This will be replaced with the buffer representation of the @var{type}
 widget.
 @item %i
-Insert the @b{[INS]} button.
+Insert the @b{[INS]} button, a widget of type @code{insert-button}.
 @item %d
-Insert the @b{[DEL]} button.
+Insert the @b{[DEL]} button, a widget of type @code{delete-button}.
 @item %%
 Insert a literal @samp{%}.
 @end table
@@ -1140,6 +2272,18 @@ A list of keyword arguments to pass to the trailing 
insert button.
 @item :buttons
 The widgets representing the insert and delete buttons.
 
+@item :format
+By default, insert its value and at the and adds an insert button.
+
+This is useful so that new elements can be added to the list upon user
+request.
+
+@item :format-handler
+A function that recognize the escape for inserting an insert button.
+
+@item :offset
+By default, this widget has an offset of 12.
+
 @vindex children@r{ keyword}
 @item :children
 The widgets representing the elements of the list.
@@ -1147,24 +2291,124 @@ The widgets representing the elements of the list.
 @vindex args@r{ keyword}
 @item :args
 List whose @sc{car} is the type of the list elements.
+
+@item :insert-before
+Function to insert a new widget as a child of the @code{editable-list}
+widget.
+
+This function inserts a recently deleted child, if there is one.  That
+is useful, so that the user can move elements in a list easily.  If
+there is not a recently deleted child, it inserts a child with its
+default value.
+
+@item :delete-at
+Function to delete a child from the widget, and store it into the
+@code{:last-deleted} list, so that it can be reinserted when the
+@code{:insert-before} function executes.
+
+@item :value-create
+The function that takes care of inserting all values.
+
+@item :value-get
+Function that returns a list with the value of the child widgets.
+
+@item :validate
+This widget validates if all children validate.
+
+@item :match
+To match, the value must be a list and all the list members must match
+the specified @var{type}.
+
+@item :match-inline
+Like the @code{:match} function, but taking into account inline
+values and widgets.
 @end table
 
 @node group
-@section The @code{group} Widget
+@subsection The @code{group} Widget
 @findex group@r{ widget}
 
-This widget simply group other widgets together.
-
 Syntax:
 
 @example
 @var{type} ::= (group [@var{keyword} @var{argument}]... @var{type}...)
 @end example
 
-The value is a list, with one member for each @var{type}.
+A widget to group other widgets.  Its super is the @code{default}
+widget.
+
+Its value is a list, with one member for each @var{type}.
+
+It overrides the following properties:
+
+@table @code
+@item :convert-widget
+As other composite widgets, a function that takes care of converting
+each widget in @var{type}.
+
+@item :copy
+A function to copy the types given in @var{type}.
+
+@item :format
+By default, displays a newline character and its value.
+
+@item :value-create
+A function to create each of its components.
+
+@item :value-get
+The same function used by the @code{editable-list} widget.
+
+@item :default-get
+A function that returns a list whose members are the default values of
+each widget it groups.
+
+@item :validate
+This widget validates if all of its children validate.
+
+@item :match
+This widget matches a value that matches each of its components.
+
+@item :match-inline
+As @code{:match}, but taking into account widgets and values that are
+inline.
+@end table
+
+@node documentation-string
+@subsection The @code{documentation-string} Widget
+@findex documentation-string@r{ widget}
+Syntax:
+
+@example
+@var{type} ::= (documentation-string [@var{keyword} @var{argument}]... 
@var{value})
+@end example
+
+A widget to represent a documentation string.  Its super is the
+@code{item} widget.
+
+It either overrides or adds the following properties:
+
+@table @code
+@item :format
+By default, insert its value.
+
+@item :value-create
+Function to insert a documentation string, possibly hiding part of the
+documentation if its large.
+
+To show or hide the rest of the documentation, uses a
+@code{visibility} widget.
+
+@item :action
+Function to toggle showing the documentation upon an event.
+
+@item :visibility-widget
+A symbol, the type of the widget to use for the visibility widget.
+
+This is, by default, the symbol @code{visibility}.
+@end table
 
 @node Sexp Types
-@chapter Sexp Types
+@section Sexp Types
 @cindex sexp types
 
 A number of widgets for editing @dfn{s-expressions} (Lisp types), sexp
@@ -1179,7 +2423,7 @@ categories described in this section.
 @end menu
 
 @node constants
-@section The Constant Widgets
+@subsection The Constant Widgets
 @cindex constant widgets
 
 The @code{const} widget can contain any Lisp expression, but the user is
@@ -1192,19 +2436,26 @@ The syntax for the @code{const} widget is:
 @var{type} ::= (const [@var{keyword} @var{argument}]...  [ @var{value} ])
 @end example
 
-The @var{value}, if present, is used to initialize the @code{:value}
-property and can be any s-expression.
+Its super is the @code{item} widget.  The @var{value}, if present, is
+used to initialize the @code{:value} property and can be any
+s-expression.
 
 @deffn Widget const
 This will display any valid s-expression in an immutable part of the
 buffer.
+
+It overrides the @code{:prompt-value} function, to avoid prompting and
+just return the widget's value.
 @end deffn
 
 There are two variations of the @code{const} widget, namely
 @code{variable-item} and @code{function-item}.  These should contain a
-symbol with a variable or function binding.  The major difference from
-the @code{const} widget is that they will allow the user to see the
-variable or function documentation for the symbol.
+symbol with a variable or function binding, respectively.  The major
+difference from the @code{const} widget is that they will allow the
+user to see the variable or function documentation for the symbol.
+
+This is accomplished via using the @samp{%h} format escape, and adding
+an appropiate @code{:documentation-property} function for each widget.
 
 @deffn Widget variable-item
 An immutable symbol that is bound as a variable.
@@ -1215,7 +2466,7 @@ An immutable symbol that is bound as a function.
 @end deffn
 
 @node generic
-@section Generic Sexp Widget
+@subsection Generic Sexp Widget
 @cindex generic sexp widget
 
 The @code{sexp} widget can contain any Lisp expression, and allows the
@@ -1228,23 +2479,42 @@ The syntax for the @code{sexp} widget is:
 @end example
 
 @deffn Widget sexp
-This will allow you to edit any valid s-expression in an editable buffer
-field.
+This widget represents an editable field that's useful to edit any
+valid s-expression.
 
 The @code{sexp} widget takes the same keyword arguments as the
 @code{editable-field} widget.  @xref{editable-field}.
+
+Its default value is @code{nil}.
+@end deffn
+
+@deffn Widget restricted-sexp
+A widget to edit Lisp expressions restricted to certain values or
+types.  Its super is the @code{sexp} widget.
+
+It works just like the sexp widget, but it overrides the @code{:match}
+function to match for certain values.  To use this widget, either you
+must define a @code{:match} function or give a
+@code{:match-alternatives} property.  The @code{:match-alternatives}
+property holds a list of predicate functions to call when checking if
+a given value matches the widget.  Each predicate function will be
+called with one argument, the value to be matched, and should return
+non-@code{nil} on success.
+
+As an example, the @code{integer} widget overrides
+@code{:match-alternatives} to @code{(integerp)}.
 @end deffn
 
 @node atoms
-@section Atomic Sexp Widgets
+@subsection Atomic Sexp Widgets
 @cindex atomic sexp widget
 
 The atoms are s-expressions that do not consist of other s-expressions.
 For example, a string, a file name, or a symbol are atoms, while a list
 is a composite type.  You can edit the value of an atom with the
-following widgets.
+widgets described in this section.
 
-The syntax for all the atoms are:
+The syntax for all the atoms is:
 
 @example
 @var{type} ::= (@var{construct} [@var{keyword} @var{argument}]...  [ 
@var{value} ])
@@ -1252,68 +2522,151 @@ The syntax for all the atoms are:
 
 The @var{value}, if present, is used to initialize the @code{:value}
 property and must be an expression of the same type as the widget.
-That is, the string widget can only be initialized with a string.
+That is, for example, the string widget can only be initialized with a
+string.
 
 All the atom widgets take the same keyword arguments as the
 @code{editable-field} widget.  @xref{editable-field}.
 
 @deffn Widget string
-Allows you to edit a string in an editable field.
+An editable field widget that can represent any Lisp string.
+
+It offers completion via the ispell library and the @code{:complete}
+property.
 @end deffn
 
 @deffn Widget regexp
-Allows you to edit a regular expression in an editable field.
+An editable field widget that can represent a regular expression.
+
+Overrides the @code{:match} and the @code{:validate} properties to
+check that the value is a valid regexp.
 @end deffn
 
 @deffn Widget character
-Allows you to enter a character in an editable field.
+An editable field widget that can represent a character.
+
+The character widget represents some characters (like the newline
+character) in a special manner, to make it easier for the user to see
+what's the content of the character field.
 @end deffn
 
 @deffn Widget file
-Allows you to edit a file name in an editable field.
+A widget for editing file names.
 
 Keywords:
 @table @code
+@item :completions
+Offers file name completion to the user.
+
+@item :prompt-value
+A function to read a file name from the minibuffer.
+
 @vindex must-match@r{ keyword}
 @item :must-match
-If this is set to non-@code{nil}, only existing file names will be
-allowed in the minibuffer.
+If this is set to non-@code{nil}, only existing file names are allowed
+when prompting for a value in the minibuffer.
+
+@item :match
+The widget matches if the value is a string, and the file whose name
+is that string is an existing file, or if @code{:must-match} is
+@code{nil}.
+
+@item :validate
+The widget is valid if its value matches.
+
 @end table
 @end deffn
 
 @deffn Widget directory
-Allows you to edit a directory name in an editable field.
-Similar to the @code{file} widget.
+A widget for editing directory names.
+
+Its super is the @code{file} widget, and it overrides the
+@code{:completions} property, to offer completions only for
+directories.
 @end deffn
 
 @deffn Widget symbol
-Allows you to edit a Lisp symbol in an editable field.
+A widget for editing a Lisp symbol.
+
+Its value by default is @code{nil}.
 @end deffn
 
 @deffn Widget function
-Allows you to edit a lambda expression, or a function name with completion.
+A widget for editing a lambda expression, or a function name, offering
+completion.  Its super is the @code{restricted-sexp} widget.
 @end deffn
 
 @deffn Widget variable
-Allows you to edit a variable name, with completion.
+A widget for editing variable names, offering completion.  Its super
+is the @code{symbol} widget.
 @end deffn
 
 @deffn Widget integer
-Allows you to edit an integer in an editable field.
+A widget for editing integers in an editable field.  Its super is the
+@code{restricted-sexp} widget.
+
+It has a default @code{:value} of 0.
+@end deffn
+
+@deffn Widget natnum
+A widget for editing non-negative integers.  Its super is the
+@code{restricted-sexp} widget.
+
+It has a default @code{:value} of 0.
+@end deffn
+
+@deffn Widget float
+A widget for editing a floating point number.  Its super is the
+@code{restricted-sexp} widget.
+
+It has a default @code{:value} of 0.0.
 @end deffn
 
 @deffn Widget number
-Allows you to edit a number in an editable field.
+A widget for editing a number, either floating point or integer.  Its
+super is the @code{restricted-sexp} widget.
+
+It has a default @code{:value} of 0.0.
 @end deffn
 
 @deffn Widget boolean
-Allows you to edit a boolean.  In Lisp this means a variable which is
-either @code{nil} meaning false, or non-@code{nil} meaning true.
+A widget for editing a boolean value.  Its super is the @code{toggle}
+widget.
+
+Its value may be @code{nil}, meaning false, or non-@code{nil}, meaning
+true.
+@end deffn
+
+@deffn Widget color
+A widget to edit a color name.
+
+In addition, shows a sample that shows the selected color, if any.
 @end deffn
 
+@deffn Widget other
+A widget useful as the last item in a @code{choice} widget, since it
+matches any value.
+
+Its super is the @code{sexp} widget, and its @code{:value} is
+@code{other}, by default.
+@end deffn
+
+@deffn Widget coding-system
+A widget that can represent a coding system name, offering
+completions.  @xref{Coding Systems,,,elisp, the Emacs Lisp Reference
+Manual}.  Its super is the @code{symbol} widget.
+
+It has a default value of @code{undecided}.
+@end deffn
+
+@deffn Widget key
+A widget to represent a key sequence.
+
+It uses a special keymap as the @code{:keymap}.
+@end deffn
 
 @node composite
-@section Composite Sexp Widgets
+@subsection Composite Sexp Widgets
 @cindex composite sexp widgets
 
 The syntax for the composite widget construct is:
@@ -1327,6 +2680,9 @@ where each @var{component} must be a widget type.  Each 
component widget
 will be displayed in the buffer, and will be editable by the user.
 
 @deffn Widget cons
+A widget to edit cons-cell values.  Its super is the @code{group}
+widget.
+
 The value of a @code{cons} widget must be a cons-cell whose @sc{car}
 and @sc{cdr} have two specified types.  It uses this syntax:
 
@@ -1336,8 +2692,10 @@ and @sc{cdr} have two specified types.  It uses this 
syntax:
 @end deffn
 
 @deffn Widget choice
-The value matched by a @code{choice} widget must have one of a fixed
-set of types.  The widget's syntax is as follows:
+A widget to hold a value of one of a fixed set of types.  Its super is
+the @code{menu-choice} widget.
+
+The widget's syntax is as follows:
 
 @example
 @var{type} ::= (choice [@var{keyword} @var{argument}]...  @var{type} ... )
@@ -1345,9 +2703,19 @@ set of types.  The widget's syntax is as follows:
 
 The value of a @code{choice} widget can be anything that matches any of the
 @var{types}.
+
+This widget only displays the widget that corresponds to the current
+choice.
+@end deffn
+
+@deffn Widget radio
+A widget to hold a value of one of a fixed set of options.  Its super is
+the @code{radio-button-choice} widget.
 @end deffn
 
 @deffn Widget list
+A widget to edit a list value.  Its super is the @code{group} widget.
+
 The value of a @code{list} widget must be a list whose element types
 match the specified component types:
 
@@ -1355,15 +2723,18 @@ match the specified component types:
 @var{type} ::= (list [@var{keyword} @var{argument}]...  
@var{component-type}...)
 @end example
 
-Thus, @code{(list string number)} matches lists of two elements,
-the first being a string and the second being a number.
+Thus, for example, @code{(list string number)} matches lists of two
+elements, the first being a string and the second being a number.
 @end deffn
 
 @deffn Widget vector
+A widget to edit a vector value.  Its super is the @code{group}
+widget.
+
 The @code{vector} widget is like the @code{list} widget but matches
-vectors instead of lists.  Thus, @code{(vector string number)} matches
-vectors of two elements, the first being a string and the second being
-a number.
+vectors instead of lists.  Thus, for example, @code{(vector string
+number)} matches vectors of two elements, the first being a string and
+the second being a number.
 @end deffn
 
 The above suffice for specifying fixed size lists and vectors.  To get
@@ -1392,126 +2763,74 @@ certainly hard to implement, so instead of confusing 
you more by
 trying to explain it here, I'll just suggest you meditate over it for
 a while.
 
-@deffn Widget set
-Specifies a type whose values are the lists whose elements all belong
-to a given set.  The order of elements of the list is not significant.
-Here's the syntax:
-
-@example
-@var{type} ::= (set [@var{keyword} @var{argument}]...  @var{permitted-element} 
... )
-@end example
-
-Use @code{const} to specify each permitted element, like this:
-@code{(set (const a) (const b))}.
-@end deffn
-
-@deffn Widget repeat
-Specifies a list of any number of elements that fit a certain type.
-
-@example
-@var{type} ::= (repeat [@var{keyword} @var{argument}]...  @var{type})
-@end example
-@end deffn
-
-@node Widget Properties
-@chapter Properties
-@cindex properties of widgets
-@cindex widget properties
-
-You can examine or set the value of a widget by using the widget object
-that was returned by @code{widget-create}.
-
-@defun widget-value widget
-Return the current value contained in @var{widget}.
-It is an error to call this function on an uninitialized widget.
-@end defun
-
-@defun widget-value-set widget value
-Set the value contained in @var{widget} to @var{value}.
-It is an error to call this function with an invalid @var{value}.
-@end defun
-
-@strong{Important:} You @emph{must} call @code{widget-setup} after
-modifying the value of a widget before the user is allowed to edit the
-widget again.  It is enough to call @code{widget-setup} once if you
-modify multiple widgets.  This is currently only necessary if the widget
-contains an editing field, but may be necessary for other widgets in the
-future.
+@deffn Widget set
+A widget to hold a list of members from a fixed set.  Its super is the
+@code{checklist} widget.
 
-If your application needs to associate some information with the widget
-objects, for example a reference to the item being edited, it can be
-done with @code{widget-put} and @code{widget-get}.  The property names
-must begin with a @samp{:}.
+Its value is a list where the elements all belong to a given set.  The
+order of elements of the list is not significant.
 
-@defun widget-put widget property value
-In @var{widget} set @var{property} to @var{value}.
-@var{property} should be a symbol, while @var{value} can be anything.
-@end defun
+Here's the syntax:
 
-@defun widget-get widget property
-In @var{widget} return the value for @var{property}.
-@var{property} should be a symbol, the value is what was last set by
-@code{widget-put} for @var{property}.
-@end defun
+@example
+@var{type} ::= (set [@var{keyword} @var{argument}]...  @var{permitted-element} 
... )
+@end example
 
-@defun widget-member widget property
-Non-@code{nil} if @var{widget} has a value (even @code{nil}) for
-property @var{property}.
-@end defun
+Use @code{const} to specify each permitted element, like this:
+@code{(set (const a) (const b))}.
+@end deffn
 
-@defun widget-apply widget property &rest args
-Apply the value of @var{property} to @var{widget}, passing @var{args}
-as additional arguments to the function.  Return the result of that
-function call.
-@end defun
+@deffn Widget repeat
+Specifies a list of any number of elements that fit a certain type.
+Its super is the @code{editable-list} widget.
 
-Occasionally it can be useful to know which kind of widget you have,
-i.e., the name of the widget type you gave when the widget was created.
+@example
+@var{type} ::= (repeat [@var{keyword} @var{argument}]...  @var{type})
+@end example
+@end deffn
 
-@defun widget-type widget
-Return the name of @var{widget}, a symbol.
-@end defun
+@deffn Widget plist
+A widget to edit property lists.  Its super is the @code{list} widget.
 
-@cindex active widget
-@cindex inactive widget
-@cindex activate a widget
-@cindex deactivate a widget
-Widgets can be in two states: active, which means they are modifiable by
-the user, or inactive, which means they cannot be modified by the user.
-You can query or set the state with the following code:
+It recognizes the following properties:
 
-@lisp
-;; Examine if @var{widget} is active or not.
-(if (widget-apply @var{widget} :active)
-    (message "Widget is active.")
-  (message "Widget is inactive.")
+@table @code
+@item :options
+A given set of recommended key-value values for the @code{plist}
+widget.  Each option shows up as a checklist item.
 
-;; Make @var{widget} inactive.
-(widget-apply @var{widget} :deactivate)
+@item :key-type
+The widget type to use for the plist keys.  By default, it uses the
+@code{symbol} widget.
 
-;; Make @var{widget} active.
-(widget-apply @var{widget} :activate)
-@end lisp
+@item :value-type
+The widget type to use for the plist values.  By default, it uses the
+@code{sexp} widget.
+@end table
+@end deffn
 
-A widget is inactive if it, or any of its ancestors (found by
-following the @code{:parent} link), have been deactivated.  To make sure
-a widget is really active, you must therefore activate both it and
-all its ancestors.
+@deffn Widget alist
+A widget to edit association lists.  Its super is the @code{list}
+widget.
 
-@lisp
-(while widget
-  (widget-apply widget :activate)
-  (setq widget (widget-get widget :parent)))
-@end lisp
+It recognizes the same properties that the @code{plist} widget, with
+the difference that the @code{:key-type} uses by default a @code{sexp}
+widget.
+@end deffn
 
-You can check if a widget has been made inactive by examining the value
-of the @code{:inactive} keyword.  If this is non-@code{nil}, the widget itself
-has been deactivated.  This is different from using the @code{:active}
-keyword, in that the latter tells you if the widget @strong{or} any of
-its ancestors have been deactivated.  Do not attempt to set the
-@code{:inactive} keyword directly.  Use the @code{:activate}
-@code{:deactivate} keywords instead.
+Most composite widgets do not allow for recursion.  That is, none of
+the contained widgets may be of the same type that is currently being
+defined.  To allow for this kind of widgets, there's the @code{lazy}
+widget.
+
+@deffn Widget lazy
+A base widget for recursive data structures.  Its super is the
+@code{default} widget.
 
+When instantiated, it contains a single inferior widget of the widget
+type specified in the @code{:type} property.  Its value is the same as
+the value of this inferior widget.
+@end deffn
 
 @node Defining New Widgets
 @chapter Defining New Widgets
@@ -1520,19 +2839,27 @@ its ancestors have been deactivated.  Do not attempt to 
set the
 
 You can define specialized widgets with @code{define-widget}.  It allows
 you to create a shorthand for more complex widgets, including specifying
-component widgets and new default values for the keyword
-arguments.
+component widgets and new default values for the keyword arguments.
 
 @defun define-widget name class doc &rest args
-Define a new widget type named @var{name} from @code{class}.
+Define a new widget type named @var{name} that derives from @var{class}.
 
-@var{name} and class should both be symbols, @code{class} should be one
-of the existing widget types.
+@var{name} and @var{class} should both be symbols, and @var{class}
+should be one of the existing widget types.
 
 The third argument @var{doc} is a documentation string for the widget.
 
-After the new widget has been defined, the following two calls will
-create identical widgets:
+@var{args} should be key-value pairs, overriding keyword values of
+@var{class}, or adding new recognized keywords for @var{name}.
+
+Usually, you'll want to derive from an existing widget type, like the
+@code{editable-field} widget, or the @code{default} widget, but it's
+also possible to derive from nothing, by passing a value of @code{nil}
+as @var{class}.  Note that if you do this, you're entirely responsible
+for defining a whole new default behavior for your widgets.
+
+After using this function, the following two calls will create
+identical widgets:
 
 @itemize @bullet
 @item
@@ -1555,170 +2882,67 @@ in the @code{widget-type} property of @var{name}, 
which is what
 If you only want to specify defaults for keywords with no complex
 conversions, you can use @code{identity} as your conversion function.
 
-The following additional keyword arguments are useful when defining new
-widgets:
+When defining new widgets, the @code{:convert-widget} property might
+be useful:
+
 @table @code
 @vindex convert-widget@r{ keyword}
 @item :convert-widget
 Function to convert a widget type before creating a widget of that
-type.  It takes a widget type as an argument, and returns the converted
-widget type.  When a widget is created, this function is called for the
-widget type and all the widget's parent types, most derived first.
-
-The following predefined functions can be used here:
-
-@defun widget-types-convert-widget widget
-Convert @code{:args} as widget types in @var{widget}.
-@end defun
-
-@defun widget-value-convert-widget widget
-Initialize @code{:value} from @code{:args} in @var{widget}.
-@end defun
-
-@vindex copy@r{ keyword}
-@item :copy
-Function to deep copy a widget type.  It takes a shallow copy of the
-widget type as an argument (made by @code{copy-sequence}), and returns a
-deep copy.  The purpose of this is to avoid having different instances
-of combined widgets share nested attributes.
-
-The following predefined functions can be used here:
-
-@defun widget-types-copy widget
-Copy @code{:args} as widget types in @var{widget}.
-@end defun
-
-@vindex value-to-internal@r{ keyword}
-@item :value-to-internal
-Function to convert the value to the internal format.  The function
-takes two arguments, a widget and an external value, and returns the
-internal value.  The function is called on the present @code{:value}
-when the widget is created, and on any value set later with
-@code{widget-value-set}.
-
-@vindex value-to-external@r{ keyword}
-@item :value-to-external
-Function to convert the value to the external format.  The function
-takes two arguments, a widget and an internal value, and returns the
-external value.
-
-@vindex create@r{ keyword}
-@item :create
-Function to create a widget from scratch.  The function takes one
-argument, a widget type, and creates a widget of that type, inserts it
-in the buffer, and returns a widget object.
-
-@vindex delete@r{ keyword}
-@item :delete
-Function to delete a widget.  The function takes one argument, a widget,
-and should remove all traces of the widget from the buffer.
-
-The default value is:
-
-@defun widget-default-delete widget
-Remove @var{widget} from the buffer.
-Delete all @code{:children} and @code{:buttons} in @var{widget}.
-@end defun
-
-In most cases you should not change this value, but instead use
-@code{:value-delete} to make any additional cleanup.
-
-@vindex value-create@r{ keyword}
-@item :value-create
-Function to expand the @samp{%v} escape in the format string.  It will
-be called with the widget as its argument and should insert a
-representation of the widget's value in the buffer.
-
-Nested widgets should be listed in @code{:children} or @code{:buttons}
-to make sure they are automatically deleted.
-
-@vindex value-delete@r{ keyword}
-@item :value-delete
-Should remove the representation of the widget's value from the buffer.
-It will be called with the widget as its argument.  It doesn't have to
-remove the text, but it should release markers and delete nested widgets
-if these are not listed in @code{:children} or @code{:buttons}.
-
-@vindex value-get@r{ keyword}
-@item :value-get
-Function to extract the value of a widget, as it is displayed in the
-buffer.
-
-The following predefined function can be used here:
-
-@defun widget-value-value-get widget
-Return the @code{:value} property of @var{widget}.
-@end defun
-
-@vindex format-handler@r{ keyword}
-@item :format-handler
-Function to handle unknown @samp{%} escapes in the format string.  It
-will be called with the widget and the character that follows the
-@samp{%} as arguments.  You can set this to allow your widget to handle
-non-standard escapes.
-
-@findex widget-default-format-handler
-You should end up calling @code{widget-default-format-handler} to handle
-unknown escape sequences, which will handle the @samp{%h} and any future
-escape sequences, as well as give an error for unknown escapes.
-
-@vindex action@r{ keyword}
-@item :action
-Function to handle user initiated events.  By default, @code{:notify}
-the parent.
+type.
 
-The following predefined function can be used here:
-
-@defun widget-parent-action widget &optional event
-Tell @code{:parent} of @var{widget} to handle the @code{:action}.
-Optional @var{event} is the event that triggered the action.
-@end defun
+It takes a widget type as an argument, and returns the converted
+widget type.  When a widget is created, this function is called for
+the widget type and all the widget's parent types, most derived first.
 
-@vindex prompt-value@r{ keyword}
-@item :prompt-value
-Function to prompt for a value in the minibuffer.  The function should
-take four arguments, @var{widget}, @var{prompt}, @var{value}, and
-@var{unbound} and should return a value for widget entered by the user.
-@var{prompt} is the prompt to use.  @var{value} is the default value to
-use, unless @var{unbound} is non-@code{nil}, in which case there is no default
-value.  The function should read the value using the method most natural
-for this widget, and does not have to check that it matches.
+The predefined functions @code{widget-types-convert-widget} and
+@code{widget-value-convert-widget} can be used here.
 @end table
 
-If you want to define a new widget from scratch, use the @code{default}
-widget as its base.
-
-@deffn Widget default
-Widget used as a base for other widgets.
-
-It provides most of the functionality that is referred to as ``by
-default'' in this text.
-@end deffn
-
-@node Widget Browser
-@chapter Widget Browser
+@node Inspecting Widgets
+@chapter Inspecting Widgets
 @cindex widget browser
 
-There is a separate package to browse widgets.  This is intended to help
-programmers who want to examine the content of a widget.  The browser
-shows the value of each keyword, but uses links for certain keywords
-such as @samp{:parent}, which avoids printing cyclic structures.
+There is a separate package to browse widgets, in
+@samp{wid-browse.el}.  This is intended to help programmers who want
+to examine the content of a widget.  The browser shows the value of
+each keyword, but uses links for certain keywords such as
+@samp{:parent}, which avoids printing cyclic structures.
 
 @deffn Command widget-browse @var{widget}
 Create a widget browser for @var{widget}.
+
 When called interactively, prompt for @var{widget}.
 @end deffn
 
 @deffn Command widget-browse-other-window @var{widget}
 Create a widget browser for @var{widget} and show it in another window.
+
 When called interactively, prompt for @var{widget}.
 @end deffn
 
 @deffn Command widget-browse-at @var{pos}
 Create a widget browser for the widget at @var{pos}.
+
 When called interactively, use the position of point.
 @end deffn
 
+In addition, there's a function to describe the widget at point.
+
+@deffn Command widget-describe &optional widget-or-pos
+Describe the widget at point.
+
+When called from Lisp, @var{widget-or-pos} might be the widget to
+describe or a buffer position where a widget is present.  If
+@var{widget-or-pos} is @code{nil}, the widget to describe is the
+widget at point.
+
+This command sets up a help buffer for providing information about the
+widget, mainly its @code{:action} and @code{:mouse-down-action}
+functions, and provides links to describe it in more detail using the
+@code{widget-browse} commands described above.
+@end deffn
+
 @node  Widget Minor Mode
 @chapter Widget Minor Mode
 @cindex widget minor mode
@@ -1740,14 +2964,24 @@ Keymap used in @code{widget-minor-mode}.
 @chapter Utilities
 @cindex utility functions for widgets
 
+Here we describe some utility functions that don't really have a place
+earlier in this manual.
+
 @defun widget-prompt-value widget prompt [ value unbound ]
 Prompt for a value matching @var{widget}, using @var{prompt}.
 The current value is assumed to be @var{value}, unless @var{unbound} is
 non-@code{nil}.
+
+Converts @var{widget} before prompting, and for prompting it uses the
+@code{:prompt-value} function.  This function returns the user
+``answer'', and it's an error if that answer doesn't match the widget,
+as with the @code{:match} function.
+
+If the answer matches the widget, returns the answer.
 @end defun
 
 @defun widget-get-sibling widget
-Get the item which @var{widget} is assumed to toggle.
+Get the item which @var{widget} should toggle.
 This is only meaningful for radio buttons or checkboxes in a list.
 @end defun
 
@@ -1773,6 +3007,142 @@ the form (@var{name} . @var{value}) (i.e., the 
simplified format),
 then the return value is the @var{value} of the chosen element.
 @end defun
 
+@defun widget-image-find image
+Create a graphical button from @var{image}, an image or a file name
+sans extension.
+
+If @var{image} is a file name, the file should be in
+@code{widget-image-directory}, or in a place where @code{find-image}
+will find it.
+@end defun
+
+@defun widget-image-insert widget tag image
+As part of @var{widget}, insert the text @var{tag} or, if supported,
+the image @var{image}.
+
+@var{image} should be as described in @code{widget-image-find}.
+@end defun
+
+@defun widget-echo-help pos
+Display help-echo text for the widget at @var{pos}.
+
+Uses the value of @code{:help-echo}.  If it is a function, it calls it
+to get a string.  Otherwise, it @code{eval}s it.
+@end defun
+
+@node  Customization
+@chapter Customization
+This chapter is about the customization options for the Widget
+library, for the end user.
+
+@deffn Face widget-field-face
+Face used for other editing fields.
+@end deffn
+
+@deffn Face widget-button-face
+Face used for buttons.
+@end deffn
+
+@defopt widget-mouse-face
+Face used for highlighting a button when the mouse pointer moves
+across it.
+
+The default value is @code{highlight}.
+@end defopt
+
+@defopt widget-image-directory
+Directory where Widget should look for images.
+
+Widget will look here for a file with the same name as specified for the
+image, with either a @file{.xpm} (if supported) or @file{.xbm} extension.
+@end defopt
+
+@defopt widget-image-enable
+If non-@code{nil}, allow images to appear on displays where they are supported.
+@end defopt
+
+@defopt widget-image-conversion
+An alist to convert symbols from image formats to file name suffixes.
+
+Each element is a cons cell (@var{format} . @var{suffix}), where
+@var{format} is a symbol that represents an image format and
+@var{suffix} is its correspondent suffix.
+@end defopt
+
+@defopt widget-button-prefix
+String to prefix buttons.
+@end defopt
+
+@defopt widget-button-suffix
+String to suffix buttons.
+@end defopt
+
+@defopt widget-push-button-prefix
+String to prefix push buttons.
+@end defopt
+
+@defopt widget-push-button-suffix
+String to suffix push buttons.
+@end defopt
+
+@defopt widget-link-prefix
+String to prefix links.
+@end defopt
+
+@defopt widget-link-suffix
+String to suffix links.
+@end defopt
+
+@defopt widget-choice-toggle
+If non-@code{nil}, toggle when there are just two options.
+
+By default, its value is @code{nil}.
+@end defopt
+
+@defopt widget-documentation-links
+If non-@code{nil}, add hyperlinks to documentation strings.
+@end defopt
+
+@defopt widget-documentation-link-regexp
+A regexp that matches potential links in documentation strings.  The
+link itself should match to the first group.
+@end defopt
+
+@defopt widget-documentation-link-p
+A predicate function to test if a string is useful as a link.  The
+function is called with one argument, a string, and should return
+non-@code{nil} if there should be a link for that string.
+
+By default, the value is @code{intern-soft}.
+@end defopt
+
+@defopt widget-documentation-link-type
+A symbol that represents a widget type to use for links in
+documentation strings.
+
+By default, the value is @code{documentation-link}.
+@end defopt
+
+@defopt widget-menu-max-size
+Maximum size for a popup menu.  By default, its value is 40.
+
+If a function ask you to choose from a menu that is larger than this
+value, it will use the minibuffer.
+@end defopt
+
+@defopt widget-menu-max-shortcuts
+Largest number of items for which it works to choose one with a
+character.
+
+For a larger number, use the minibuffer.
+@end defopt
+
+@defopt widget-menu-minibuffer-flag
+Whether to use the minibuffer to ask for a choice.
+
+If @code{nil}, the default, read a single character.
+@end defopt
+
 @node  Widget Wishlist
 @chapter Wishlist
 @cindex todo
@@ -1808,15 +3178,6 @@ Find a way to disable mouse highlight for inactive 
widgets.
 @item
 Find a way to make glyphs look inactive.
 
-@item
-Add @code{property-list} widget.
-
-@item
-Add @code{association-list} widget.
-
-@item
-Add @code{key-binding} widget.
-
 @item
 Add @code{widget} widget for editing widget specifications.
 
diff --git a/etc/DEBUG b/etc/DEBUG
index 6455152bd50..5aeb38c6460 100644
--- a/etc/DEBUG
+++ b/etc/DEBUG
@@ -472,6 +472,16 @@ and, assuming that "xtype" says that args[0] is a symbol:
 
 ** Debugging Emacs redisplay problems
 
+The Emacs display code includes special debugging code, but it is normally
+disabled.  Configuring Emacs with --enable-checking='yes,glyphs' enables it.
+
+Building Emacs like that activates many assertions which scrutinize display
+code operation more than Emacs does normally.  (To see the code which tests
+these assertions, look for calls to the 'eassert' macros.)  Any assertion that
+is reported to fail should be investigated.  Redisplay problems that cause
+aborts or segfaults in production builds of Emacs will many times be caught by
+these assertions before they cause a crash.
+
 If you configured Emacs with --enable-checking='glyphs', you can use redisplay
 tracing facilities from a running Emacs session.
 
@@ -481,21 +491,18 @@ code paths taken by the display engine under various 
conditions, especially if
 some redisplay optimizations produce wrong results.  (You know that redisplay
 optimizations might be involved if "M-x redraw-display RET", or even just
 typing "M-x", causes Emacs to correct the bad display.)  Since the cursor
-blinking feature triggers periodic redisplay cycles, we recommend disabling
-'blink-cursor-mode' before invoking 'trace-redisplay', so that you have less
-clutter in the trace.  You can also have up to 30 last trace messages dumped to
-standard error by invoking the 'dump-redisplay-history' command.
+blinking feature and ElDoc trigger periodic redisplay cycles, we recommend
+disabling 'blink-cursor-mode' and 'global-eldoc-mode' before invoking
+'trace-redisplay', so that you have less clutter in the trace.  You can also
+have up to 30 last trace messages dumped to standard error by invoking the
+'dump-redisplay-history' command.
 
 To find the code paths which were taken by the display engine, search xdisp.c
 for the trace messages you see.
 
 The command 'dump-glyph-matrix' is useful for producing on standard error
 stream a full dump of the selected window's glyph matrix.  See the function's
-doc string for more details.  If you are debugging redisplay issues in
-text-mode frames, you may find the command 'dump-frame-glyph-matrix' useful.
-
-Other commands useful for debugging redisplay are 'dump-glyph-row' and
-'dump-tool-bar-row'.
+doc string for more details.
 
 If you run Emacs under GDB, you can print the contents of any glyph matrix by
 just calling that function with the matrix as its argument.  For example, the
@@ -507,13 +514,11 @@ whose pointer is in 'w':
 (The second argument 2 tells dump_glyph_matrix to print the glyphs in
 a long form.)
 
-The Emacs display code includes special debugging code, but it is normally
-disabled.  Configuring Emacs with --enable-checking='yes,glyphs' enables it.
+If you are debugging redisplay issues in text-mode frames, you may find the
+command 'dump-frame-glyph-matrix' useful.
 
-Building Emacs like that activates many assertions which scrutinize
-display code operation more than Emacs does normally.  (To see the
-code which tests these assertions, look for calls to the 'eassert'
-macros.)  Any assertion that is reported to fail should be investigated.
+Other commands useful for debugging redisplay are 'dump-glyph-row' and
+'dump-tool-bar-row'.
 
 When you debug display problems running emacs under X, you can use
 the 'ff' command to flush all pending display updates to the screen.
@@ -535,36 +540,40 @@ object of the relevant type as argument.  For example, 
'pgrowx' dumps all
 glyphs in its argument, which must be of type 'struct glyph_row'.
 
 Since redisplay is performed by Emacs very frequently, you need to place your
-breakpoints cleverly to avoid hitting them all the time, when the issue you are
-debugging did not (yet) happen.  Here are some useful techniques for that:
-
- . Put a breakpoint at 'Fredraw_display' before running Emacs.  Then do
-   whatever is required to reproduce the bad display, and invoke "M-x
-   redraw-display".  The debugger will kick in, and you can set or enable
-   breakpoints in strategic places, knowing that the bad display will be
+breakpoints cleverly to avoid hitting them all the time, when the issue you
+are debugging did not (yet) happen.  Here are some useful techniques for that:
+
+ . Put a breakpoint at 'Frecenter' or 'Fredraw_display' before running Emacs.
+   Then do whatever is required to reproduce the bad display, and type C-l or
+   "M-x redraw-display" just before invoking the last action that reproduces
+   the bug.  The debugger will kick in, and you can set or enable breakpoints
+   in strategic places, knowing that the bad display will happen soon.  With a
+   breakpoint at 'Fredraw_display', you can even reproduce the bug and invoke
+   "M-x redraw-display" afterwards, knowing that the bad display will be
    redrawn from scratch.
 
- . For debugging incorrect cursor position, a good place to put a breakpoint is
-   in 'set_cursor_from_row'.  The first time this function is called as part of
-   'redraw-display', Emacs is redrawing the minibuffer window, which is usually
-   not what you want; type "continue" to get to the call you want.  In general,
-   always make sure 'set_cursor_from_row' is called for the right window and
-   buffer by examining the value of w->contents: it should be the buffer whose
-   display you are debugging.
+ . For debugging incorrect cursor position, a good place to put a breakpoint
+   is in 'set_cursor_from_row'.  The first time this function is called as
+   part of 'redraw-display', Emacs is redrawing the minibuffer window, which
+   is usually not what you want; type "continue" to get to the call you want.
+   In general, always make sure 'set_cursor_from_row' is called for the right
+   window and buffer by examining the value of w->contents: it should be the
+   buffer whose display you are debugging.
 
  . 'set_cursor_from_row' is also a good place to look at the contents of a
    screen line (a.k.a. "glyph row"), by means of the 'pgrow' GDB command.  Of
    course, you need first to make sure the cursor is on the screen line which
-   you want to investigate.  If you have set a breakpoint in 'Fredraw_display',
-   as advised above, move cursor to that line before invoking 'redraw-display'.
+   you want to investigate.  If you have set a breakpoint in 'Fredraw_display'
+   or 'Frecenter', as advised above, move cursor to that line before invoking
+   these commands.
 
  . If the problem happens only at some specific buffer position or for some
-   specific rarely-used character, you can make your breakpoints conditional on
-   those values.  The display engine maintains the buffer and string position
-   it is processing in the it->current member; for example, the buffer
-   character position is in it->current.pos.charpos.  Most redisplay functions
-   accept a pointer to a 'struct it' object as their argument, so you can make
-   conditional breakpoints in those functions, like this:
+   specific rarely-used character, you can make your breakpoints conditional
+   on those values.  The display engine maintains the buffer and string
+   position it is processing in the it->current member; for example, the
+   buffer character position is in it->current.pos.charpos.  Most redisplay
+   functions accept a pointer to a 'struct it' object as their argument, so
+   you can make conditional breakpoints in those functions, like this:
 
     (gdb) break x_produce_glyphs if it->current.pos.charpos == 1234
 
@@ -578,6 +587,16 @@ debugging did not (yet) happen.  Here are some useful 
techniques for that:
    GET_FROM_IMAGE for displaying an image, etc.  See 'enum it_method' in
    dispextern.h for the full list of values.
 
+ . When the display engine is processing a 'display' text property or an
+   overlay string, it pushes on the iterator stack the state variables
+   describing its iteration of buffer text, then reinitializes the iterator
+   object for processing the property or overlay.  The it->sp ("stack
+   pointer") member, if it is greater than zero, means the iterator's stack
+   was pushed at least once.  You can therefore condition your breakpoints on
+   the value of it->sp being positive or being of a certain positive value, to
+   debug display problems that happen only with display properties or
+   overlays.
+
 ** Debugging problems with native-compiled Lisp.
 
 When you encounter problems specific to native-compilation of Lisp, we
diff --git a/etc/EGLOT-NEWS b/etc/EGLOT-NEWS
index 01f0498eb81..f5f78ccd483 100644
--- a/etc/EGLOT-NEWS
+++ b/etc/EGLOT-NEWS
@@ -20,6 +20,16 @@ https://github.com/joaotavora/eglot/issues/1234.
 
 * Changes in upcoming Eglot
 
+** Diff previews of edits and new variable 'eglot-confirm-server-edits'
+
+The variable 'eglot-confirm-server-edits' replaces the obsolete
+'eglot-confirm-server-initiated-edits' and brings about a new
+confirmation model, making it possible to have only certain commands
+require user confirmation.  The type of confirmation has also been
+enhanced.  In particular it allows a temporary 'diff-mode' buffer to
+display the proposed changes, so the user can apply them one by one.
+See bug#60338.
+
 ** Optimized file-watching capability
 
 Some servers, like the Pyright language server, issue too many file
@@ -33,6 +43,13 @@ For 'newline' commands, Eglot sometimes sent the wrong 
character code
 to the server.  Also made this feature less chatty in the mode-line
 and messages buffer.
 
+** Improve mouse invocation of code actions
+
+When invoking code actions by middle clicking with the mouse on
+Flymake diagnostics, it was often the case that Eglot didn't request
+code actions correctly and thus no actions were offered to the user.
+This has been fixed. github#1295
+
 
 * Changes in Eglot 1.15 (29/4/2023)
 
diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS
index 7ee55982b17..fadd97b65df 100644
--- a/etc/ERC-NEWS
+++ b/etc/ERC-NEWS
@@ -178,6 +178,15 @@ been restored with a slightly revised role contingent on a 
few
 assumptions explained in its doc string.  For clarity, it has been
 renamed 'erc-ensure-target-buffer-on-privmsg'.
 
+** Module 'scrolltobottom' can attempt to be more aggressive.
+Enabling the experimental option 'erc-scrolltobottom-all' tells
+'scrolltobottom' to be more vigilant about staking down the input area
+and to do so in all ERC windows.  The dependent option
+'erc-scrolltobottom-relaxed', also experimental, makes ERC's prompt
+stationary wherever it happens to reside instead of forcing it to the
+bottom of a window.  That is, new input appears above the prompt,
+scrolling existing messages upward to compensate.
+
 ** Subtle changes in two fundamental faces.
 Users of the default theme may notice that 'erc-action-face' and
 'erc-notice-face' now appear slightly less bold on systems supporting
@@ -203,11 +212,24 @@ continued integration.  With the existing design, merely 
loading the
 library 'erc-log' caused 'truncate' to start writing logs, possibly
 against a user's wishes.
 
+** The function 'erc-echo-timestamp' is now a command.
+The option 'erc-echo-timestamps' (plural) has always enabled the
+contextual printing of timestamps to the echo area when moving between
+messages in an ERC buffer.  Similar functionality is now available on
+demand by invoking the newly interactive function 'erc-echo-timestamp'
+atop any message.  The new companion option 'erc-echo-timestamp-zone'
+determines the default timezone when not specified with a prefix
+argument.
+
+** Option 'erc-warn-about-blank-lines' is more informative.
+Enabled by default, this option now produces more useful feedback
+whenever ERC rejects prompt input containing whitespace-only lines.
+When paired with option 'erc-send-whitespace-lines', ERC echoes a
+tally of blank lines padded and trailing blanks culled.
+
 ** Miscellaneous UX changes.
 Some minor quality-of-life niceties have finally made their way to
-ERC.  For example, the function 'erc-echo-timestamp' is now
-interactive and can be invoked on any message to view its timestamp in
-the echo area.  Fool visibility has become togglable with the new
+ERC.  For example, fool visibility has become togglable with the new
 command 'erc-match-toggle-hidden-fools'.  The 'button' module's
 'erc-button-previous' now moves to the beginning instead of the end of
 buttons.  A new command, 'erc-news', can be invoked to visit this very
@@ -239,9 +261,9 @@ property of the same name has been retained and now has a 
value of
 Built-in and third-party modules rely on certain hooks for adjusting
 incoming and outgoing messages upon insertion.  And some modules only
 want to do so after others have done their damage.  Traditionally,
-this required various hacks and finagling to achieve.  And while this
-release makes an effort to load modules in a more consistent order,
-that alone isn't enough to ensure similar predictability among
+this has required various hacks and finagling to achieve.  And while
+this release makes an effort to load modules in a more consistent
+order, that alone isn't enough to ensure similar predictability among
 essential members of important hooks.
 
 Luckily, ERC now leverages a feature introduced in Emacs 27, "hook
@@ -255,12 +277,17 @@ the first two, 'erc-button-add-buttons' and 'erc-fill', 
which have
 been swapped with respect to their previous places in recent ERC
 versions.
 
+ERC also provisionally reserves the same depth interval for
+'erc-insert-pre-hook' and possibly other, similar hooks, but will
+continue to modify non-ERC hooks locally whenever possible, especially
+in new code.
+
 *** ERC now manages timestamp-related properties a bit differently.
 For starters, the 'cursor-sensor-functions' property no longer
 contains unique closures and thus no longer proves effective for
 traversing messages.  To compensate, a new property, 'erc-timestamp',
 now spans message bodies but not the newlines delimiting them.  Also
-affecting the `stamp' module is the deprecation of the function
+affecting the 'stamp' module is the deprecation of the function
 'erc-insert-aligned' and its removal from client code.  Additionally,
 the module now merges its 'invisible' property with existing ones and
 includes all white space around stamps when doing so.
diff --git a/etc/NEWS b/etc/NEWS
index 9a98db8c83a..b3c7d3a8693 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -24,6 +24,7 @@ applies, and please also update docstrings as needed.
 
 * Installation Changes in Emacs 30.1
 
++++
 ** Emacs has been ported to the Android operating system.
 This requires Emacs to be compiled on another computer.  The Android
 NDK, SDK, and a suitable Java compiler must also be installed.
@@ -43,9 +44,24 @@ ALSA on these operating systems instead.
 
 * Startup Changes in Emacs 30.1
 
+** On GNU/Linux, Emacs is now the default application for 'org-protocol'.
+Org mode provides a way to quickly capture bookmarks, notes, and links
+using 'emacsclient':
+
+    emacsclient "org-protocol://store-link?url=URL&title=TITLE"
+
+Previously, users had to manually configure their GNU/Linux desktop
+environment to open 'org-protocol' links in Emacs.  These links should
+now open in Emacs automatically, as the "emacsclient.desktop" file now
+arranges for Emacs to be the default application for the 'org-protocol'
+URI scheme.  See the Org mode manual, Info node "(org) Protocols" for
+more details.
+
 
 * Changes in Emacs 30.1
 
+** Emacs now supports Unicode Standard version 15.1.
+
 ** Help
 
 *** 'describe-function' shows function inferred type when available.
@@ -53,9 +69,9 @@ For native compiled Lisp functions 'describe-function' prints 
(after
 the signature) the automatically inferred function type as well.
 
 ---
-** New user option 'describe-bindings-outline-rules'.
+*** New user option 'describe-bindings-outline-rules'.
 This user option controls outline visibility in the output buffer of
-'describe-bindings' when 'describe-bindings-outline' in non-nil.
+'describe-bindings' when 'describe-bindings-outline' is non-nil.
 
 ** X selection requests are now handled much faster and asynchronously.
 This means it should be less necessary to disable the likes of
@@ -128,6 +144,14 @@ can use this to distinguish between buffers visiting files 
with the
 same base name that belong to different projects by using the provided
 transform function 'project-uniquify-dirname-transform'.
 
+** 'insert-directory-program' is now a defcustom.
+
+** 'insert-directory-program' prefers "gls" on *BSD and macOS.
+On *BSD and macOS systems, this user option now defaults to the "gls"
+executable, if it exists.  This should remove the need to change its
+value when installing GNU coreutils using something like ports or
+Homebrew.
+
 ** cl-print
 
 *** You can expand the "..." truncation everywhere.
@@ -152,6 +176,13 @@ right-aligned to is controlled by the new user option
 It can be used to add, remove and reorder functions that change
 the appearance of every tab on the tab bar.
 
+** Miscellaneous
+
+---
+*** New face 'appt-notification' for 'appt-display-mode-line'.
+It can be used to customize the look of the appointment notification
+displayed on the mode line when 'appt-display-mode-line' is non-nil.
+
 
 * Editing Changes in Emacs 30.1
 
@@ -235,8 +266,9 @@ functions in CJK locales.
 
 * Changes in Specialized Modes and Packages in Emacs 30.1
 
+** gdb-mi
 ---
-** Variable order and truncation can now be configured in 'gdb-many-windows'.
+*** Variable order and truncation can now be configured in 'gdb-many-windows'.
 The new user option 'gdb-locals-table-row-config' allows users to
 configure the order and max length of various properties in the local
 variables buffer when using 'gdb-many-windows'.
@@ -251,6 +283,13 @@ If you want to get back the old behavior, set the user 
option to the value
     (setopt gdb-locals-table-row-config
             `((type . 0) (name . 0) (value . ,gdb-locals-value-limit)))
 
+---
+*** New user option 'gdb-display-io-buffer'.
+If this is nil, "M-x gdb" will neither create nor display a separate
+buffer for the I/O of the program being debugged, but will instead
+redirect the program's interaction to the GDB execution buffer.  The
+default is t, to preserve previous behavior.
+
 ** Grep
 
 *** New user option 'grep-use-headings'.
@@ -282,6 +321,30 @@ This allows changing which type of whitespace changes are 
ignored when
 regenerating hunks with 'diff-ignore-whitespace-hunk'.  Defaults to
 the previously hard-coded "-b".
 
+*** New command 'diff-apply-buffer' bound to 'C-c RET a'.
+It applies the diff in the entire diff buffer and
+saves all modified file buffers.
+
+** Isearch and Replace
+
+*** New command 'replace-regexp-as-diff'.
+It reads a regexp to search for and a string to replace with, then
+displays a buffer with replacements as diffs.  After reviewing the
+changes in the output buffer you can apply the replacements as
+a patch to the current file buffer.  There are also new commands
+'multi-file-replace-regexp-as-diff' that shows as diffs replacements
+in a list of specified files, and 'dired-do-replace-regexp-as-diff'
+that shows as diffs replacements in the marked files in Dired.
+
+** Dired
+
+---
+*** New user option 'dired-movement-style'.
+When non-nil, make 'dired-next-line' and 'dired-previous-line' skip
+empty lines.  It also controls how to move point when encountering a
+boundary (e.g., if every line is visible, invoking 'dired-next-line'
+at the last line will move to the first line).  The default is nil.
+
 ** Ediff
 
 ---
@@ -355,6 +418,9 @@ this to your configuration:
 
     (keymap-set eshell-mode-map "<home>" #'eshell-bol-ignoring-prompt)
 
+This also means you no longer need to adjust 'eshell-prompt-regexp'
+when customizing your Eshell prompt.
+
 ---
 *** You can now properly unload Eshell.
 Calling '(unload-feature 'eshell)' no longer signals an error, and now
@@ -370,6 +436,9 @@ to load the edited aliases.
 Running 'rgrep' in Eshell now uses the Emacs grep facility instead of
 calling external rgrep.
 
++++
+*** If a command exits abnormally, the Eshell prompt now shows its exit code.
+
 ** Pcomplete
 
 ---
@@ -423,8 +492,8 @@ sandboxes provided by Flatpak.
 The host name for Kubernetes connections can be of kind [CONTAINER.]POD,
 in order to specify a dedicated container.  If there is just the pod
 name, the first container in the pod is taken.  The new user options
-'tramp-kubernetes-context' and 'tramp-kubernetes-namespace' allow to
-access pods with different context or namespace but the default one.
+'tramp-kubernetes-context' and 'tramp-kubernetes-namespace' allow
+accessing pods with different context or namespace but the default one.
 
 +++
 *** Rename 'tramp-use-ssh-controlmaster-options' to 
'tramp-use-connection-share'.
@@ -437,7 +506,7 @@ sessions, respectively.
 
 +++
 *** New command 'tramp-cleanup-some-buffers'.
-It allows to kill only selected remote buffers, controlled by user
+It kills only a subset of opened remote buffers, subject to the user
 option 'tramp-cleanup-some-buffers-hook'.
 
 +++
@@ -453,6 +522,20 @@ This macro could wrap code which handles local files only. 
 Due to the
 temporary deactivation of remote files, it results in a slightly
 improved performance of file name handling in Emacs.
 
++++
+*** New user option 'tramp-completion-multi-hop-methods'.
+It contains a list of connection methods for which completion should
+be attempted at the end of a multi-hop chain.  This allows completion
+candidates to include a list of, for example, containers running on a
+remote docker host.
+
++++
+*** New command 'tramp-revert-buffer-with-sudo'.
+It reverts the current buffer to visit with "sudo" permissions.  The
+buffer must either visit a file, or it must run 'dired-mode'.  Another
+method but "sudo" can be configured with user option
+'tramp-file-name-with-method'.
+
 ** EWW
 
 +++
@@ -602,9 +685,10 @@ This keyword enables the user to install packages using 
'package-vc'.
 *** New commands for reading mailing lists.
 The new Rmail commands 'rmail-mailing-list-post',
 'rmail-mailing-list-unsubscribe', 'rmail-mailing-list-help', and
-'rmail-mailing-list-archive' allow to, respectively, post to,
-unsubscribe from, request help about, and browse the archives, of the
-mailing list from which the current email message was delivered.
+'rmail-mailing-list-archive' allow, respectively, posting to,
+unsubscribing from, requesting help about, and browsing the archives
+of, the mailing list from which the current email message was
+delivered.
 
 ** Dictionary
 
@@ -699,7 +783,7 @@ of the accessibility of remote files can now time out if
 ** Notifications
 
 +++
-*** Allow to use Icon Naming Specification for ':app-icon'.
+*** Allow using Icon Naming Specification for ':app-icon'.
 You can use a symbol as the value for ':app-icon' to provide icon name
 without specifying a file, like this:
 
@@ -711,6 +795,44 @@ without specifying a file, like this:
 *** New user option 'image-dired-thumb-naming'.
 You can now configure how a thumbnail is named using this option.
 
+** ERT
+
+*** New macro `skip-when' to skip 'ert-deftest' tests.
+This can help avoid some awkward skip conditions.  For example
+'(skip-unless (not noninteractive))' can be changed to the easier
+to read '(skip-when noninteractive)'.
+
+** Checkdoc
+
+---
+*** New checkdock warning if not using lexical-binding.
+Checkdoc now warns if the first line of an Emacs Lisp file does not
+end with a "-*- lexical-binding: t -*-" cookie.  Customize the user
+option 'checkdoc-lexical-binding-flag' to nil to disable this warning.
+
+** URL
+
++++
+*** 'url-gateway-broken-resolution' is now obsolete.
+This option was intended for use on SunOS 4.x and Ultrix systems,
+neither of which have been supported by Emacs since version 23.1.
+The user option 'url-gateway-nslookup-program' and the function
+'url-gateway-nslookup-host' are consequently also obsolete.
+
++++
+** Edmacro
+
+*** New command 'edmacro-set-macro-to-region-lines'.
+Bound to 'C-c C-r', this command replaces the macro text with the
+lines of the region.  If needed, the region is extended to include
+whole lines.  If the region ends at the beginning of a line, that last
+line is excluded.
+
+*** New user option 'edmacro-reverse-macro-lines'.
+When this is non-nil, the lines of key sequences are displayed with
+the most recent line fist.  This is can be useful when working with
+macros with many lines, such as from 'kmacro-edit-lossage'.
+
 
 * New Modes and Packages in Emacs 30.1
 
@@ -730,6 +852,17 @@ A major mode based on the tree-sitter library for editing 
HEEx files.
 A major mode based on the tree-sitter library for editing Elixir
 files.
 
++++
+** New global minor mode 'minibuffer-regexp-mode'.
+This is a minor mode for editing regular expressions in the minibuffer.
+It highlights parens via ‘show-paren-mode’ and ‘blink-matching-paren’ in
+a user-friendly way, avoids reporting alleged paren mismatches and makes
+sexp navigation more intuitive.
+
+---
+*** New major mode 'lua-ts-mode'.
+A major mode based on the tree-sitter library for editing Lua files.
+
 ---
 ** The highly accessible Modus themes collection has eight items.
 The 'modus-operandi' and 'modus-vivendi' are the main themes that have
@@ -753,9 +886,18 @@ the current project.
 The look of the key prompt in the project switcher has been changed
 slightly.  To get the previous one, set this option to 'brackets'.
 
+*** 'project-try-vc' tries harder to find the responsible VCS.
+When 'project-vc-extra-root-markers' is non-nil, and causes
+subdirectory project to be detected which is not a VCS root, we now
+additionally traverse the parent directories until a VCS root is found
+(if any), so that the ignore rules for that repository are used, and
+the file listing's performance is still optimized.
+
 
 * Incompatible Lisp Changes in Emacs 30.1
 
+** 'post-gc-hook' runs after updating 'gcs-done' and `'gcs-elapsed'.
+
 ---
 ** The escape sequence '\x' not followed by hex digits is now an error.
 Previously, '\x' without at least one hex digit denoted character code
@@ -834,10 +976,32 @@ Use 'define-minor-mode' and 
'define-globalized-minor-mode' instead.
 
 * Lisp Changes in Emacs 30.1
 
+** New function 're--describe-compiled' to see the innards of a regexp.
+If you compiled with '--enable-checking', you can use this to help debug
+either your regexp performance problems or the regexp engine.
+
++++
+** XLFDs are no longer restricted to 255 characters.
+'font-xlfd-name' now returns an XLFD even if it is greater than 255
+characters in length, provided that the LONG_XLFDs argument is true.
+
+Other features in Emacs which employ XLFDs have been modified to
+produce and understand XLFDs larger than 255 characters.
+
 ** 'defadvice' is marked as obsolete.
 See the "(elisp) Porting Old Advice" node for help converting them
 to use 'advice-add' or 'define-advice' instead.
 
+** 'cl-old-struct-compat-mode' is marked as obsolete.
+You may need to recompile our code if it was compiled with Emacs < 24.3.
+
++++
+** New macro 'static-if' for conditional evaluation of code.
+This macro hides a form from the evaluator or byte-compiler based on a
+compile-time condition.  This is handy for avoiding byte-compilation
+warnings about code that will never actually run under some
+conditions.
+
 +++
 ** Desktop notifications are now supported on the Haiku operating system.
 The new function 'haiku-notifications-notify' provides a subset of the
@@ -919,24 +1083,17 @@ Major modes can now set this variable to customize the 
behavior of the
 The previous implementation of 'forward-sentence' is moved into its
 own function, to be bound by 'forward-sentence-function'.
 
-*** New buffer-local variable 'treesit-sentence-type-regexp'.
-Similarly to 'treesit-defun-type-regexp', this variable is used to
-define "sentences" in tree-sitter enabled modes.
-
 *** New function 'treesit-forward-sentence'.
-All tree-sitter enabled modes that define 'treesit-sentence-type-regexp'
-now set 'forward-sentence-function' to call 'treesit-forward-sentence'.
+All tree-sitter enabled modes that define 'sentence' in
+'treesit-thing-settings' now set 'forward-sentence-function' to call
+'treesit-forward-sentence'.
 
 ** Functions and variables to move by program sexps
 
-*** New buffer-local variable 'treesit-sexp-type-regexp'.
-Similarly to 'treesit-defun-type-regexp', this variable is used to
-define "sexps" in tree-sitter enabled modes.
-
 *** New function 'treesit-forward-sexp'.
 Tree-sitter conditionally sets 'forward-sexp-function' for major modes
-that have defined 'treesit-sexp-type-regexp' to enable sexp-related
-motion commands.
+that have defined 'sexp' in 'treesit-thing-settings' to enable
+sexp-related motion commands.
 
 ** New or changed byte-compilation warnings
 
@@ -1014,6 +1171,21 @@ simplified away.
 This warning can be suppressed using 'with-suppressed-warnings' with
 the warning name 'suspicious'.
 
+---
+*** Warn about useless trailing 'cond' clauses.
+The compiler now warns when a 'cond' form contains clauses following a
+default (unconditional) clause.  Example:
+
+    (cond ((= x 0) (say "none"))
+          (t (say "some"))
+          (say "goodbye"))
+
+Such a clause will never be executed but is likely to be a mistake,
+perhaps due to misplaced brackets.
+
+This warning can be suppressed using 'with-suppressed-warnings' with
+the warning name 'suspicious'.
+
 ---
 *** Warn about mutation of constant values.
 The compiler now warns about code that modifies program constants in
diff --git a/etc/NEWS.20 b/etc/NEWS.20
index 8143cfcf3cd..ade83eb3dd0 100644
--- a/etc/NEWS.20
+++ b/etc/NEWS.20
@@ -1250,7 +1250,7 @@ for large documents), you can reuse these buffers by 
setting
 
 *** References to external documents.
 
-The LaTeX package 'xr' allows to cross-reference labels in external
+The LaTeX package 'xr' allows cross-referencing labels in external
 documents.  RefTeX can provide information about the external
 documents as well.  To use this feature, set up the \externaldocument
 macros required by the 'xr' package and rescan the document with
@@ -3260,7 +3260,7 @@ can connect to an Emacs server started by a non-root user.
 it to return immediately without waiting for you to "finish" the
 buffer in Emacs.
 
-*** The new option --alternate-editor allows to specify an editor to
+*** The new option --alternate-editor allows specifying an editor to
 use if Emacs is not running.  The environment variable
 ALTERNATE_EDITOR can be used for the same effect; the command line
 option takes precedence.
diff --git a/etc/NEWS.21 b/etc/NEWS.21
index e68d7fe8fae..89cac46e248 100644
--- a/etc/NEWS.21
+++ b/etc/NEWS.21
@@ -1424,7 +1424,7 @@ digest message.
 *** The new user option 'rmail-automatic-folder-directives' specifies
 in which folder to put messages automatically.
 
-*** The new function 'rmail-redecode-body' allows to fix a message
+*** The new function 'rmail-redecode-body' allows fixing a message
 with non-ASCII characters if Emacs happens to decode it incorrectly
 due to missing or malformed "charset=" header.
 
@@ -1437,7 +1437,7 @@ use the -f option when sending mail.
 ** The Rmail command 'o' ('rmail-output-to-rmail-file') now writes the
 current message in the internal 'emacs-mule' encoding, rather than in
 the encoding taken from the variable 'buffer-file-coding-system'.
-This allows to save messages whose characters cannot be safely encoded
+This allows saving messages whose characters cannot be safely encoded
 by the buffer's coding system, and makes sure the message will be
 displayed correctly when you later visit the target Rmail file.
 
@@ -1465,7 +1465,7 @@ other than 'emacs-mule', you can customize the variable
     sorted *Index* buffer which looks like the final index.  Entries
     can be edited from that buffer.
 
-*** Label and citation key selection now allow to select several
+*** Label and citation key selection now allow selecting several
     items and reference them together (use 'm' to mark items, 'a' or
     'A' to use all marked entries).
 
@@ -1804,7 +1804,7 @@ to phrases and to highlight entire lines containing a 
match.
 *** The new package zone.el plays games with Emacs' display when
 Emacs is idle.
 
-*** The new package tildify.el allows to add hard spaces or other text
+*** The new package tildify.el allows adding hard spaces or other text
 fragments in accordance with the current major mode.
 
 *** The new package xml.el provides a simple but generic XML
@@ -1826,7 +1826,7 @@ provides an alternative interface to VC-dired for CVS.  
It comes with
 'log-view-mode' to view RCS and SCCS logs and 'log-edit-mode' used to
 enter check-in log messages.
 
-*** The new package called 'woman' allows to browse Unix man pages
+*** The new package called 'woman' allows browsing Unix man pages
 without invoking external programs.
 
 The command `M-x woman' formats manual pages entirely in Emacs Lisp
@@ -2011,8 +2011,8 @@ recent file list can be displayed:
 - sorted by file paths, file names, ascending or descending.
 - showing paths relative to the current default-directory
 
-The 'recentf-filter-changer' menu filter function allows to
-dynamically change the menu appearance.
+The 'recentf-filter-changer' menu filter function allows
+dynamically changing the menu appearance.
 
 *** 'elide-head' provides a mechanism for eliding boilerplate header
 text.
@@ -2139,7 +2139,7 @@ new command M-x strokes-list-strokes.
 ** Hexl contains a new command 'hexl-insert-hex-string' which inserts
 a string of hexadecimal numbers read from the mini-buffer.
 
-** Hexl mode allows to insert non-ASCII characters.
+** Hexl mode allows inserting non-ASCII characters.
 
 The non-ASCII characters are encoded using the same encoding as the
 file you are visiting in Hexl mode.
@@ -2369,7 +2369,7 @@ allows the animated display of strings.
 ** The new function 'interactive-form' can be used to obtain the
 interactive form of a function.
 
-** The keyword :set-after in defcustom allows to specify dependencies
+** The keyword :set-after in defcustom allows specifying dependencies
 between custom options.  Example:
 
   (defcustom default-input-method nil
@@ -3629,7 +3629,7 @@ Each face can specify the following display attributes:
    13. Whether or not a box should be drawn around characters, its
    color, the width of the box lines, and 3D appearance.
 
-Faces are frame-local by nature because Emacs allows to define the
+Faces are frame-local by nature because Emacs allows defining the
 same named face (face names are symbols) differently for different
 frames.  Each frame has an alist of face definitions for all named
 faces.  The value of a named face in such an alist is a Lisp vector
diff --git a/etc/NEWS.22 b/etc/NEWS.22
index 804dab00859..7021fce52ed 100644
--- a/etc/NEWS.22
+++ b/etc/NEWS.22
@@ -2408,7 +2408,7 @@ called with a prefix argument.  Related new options are
 
 The new command 'reftex-create-bibtex-file' creates a BibTeX database
 with all entries referenced in the current document.  The keys "e" and
-"E" allow to produce a BibTeX database file from entries marked in a
+"E" allow producing a BibTeX database file from entries marked in a
 citation selection buffer.
 
 The command 'reftex-citation' uses the word in the buffer before the
diff --git a/etc/NEWS.23 b/etc/NEWS.23
index 22408197f7d..7ac91e6165f 100644
--- a/etc/NEWS.23
+++ b/etc/NEWS.23
@@ -1200,7 +1200,7 @@ of the region to comment, rather than the leftmost column.
 *** The new commands 'pp-macroexpand-expression' and
 'pp-macroexpand-last-sexp' pretty-print macro expansions.
 
-*** The new command 'set-file-modes' allows to set file's mode bits.
+*** The new command 'set-file-modes' allows setting file's mode bits.
 The mode bits can be specified in symbolic notation, like with GNU
 Coreutils, in addition to an octal number.  'chmod' is a new
 convenience alias for this function.
@@ -1540,7 +1540,7 @@ authentication respectively.
 *** New macro 'with-help-window' should set up help windows better
 than 'with-output-to-temp-buffer' with 'print-help-return-message'.
 
-*** New option 'help-window-select' permits to customize whether help
+*** New option 'help-window-select' permits customizing whether help
 window shall be automatically selected when invoking help.
 
 *** New variable 'help-window-point-marker' permits one to specify a new
@@ -1670,7 +1670,7 @@ Previously, this information was hidden.
 ** TeX modes
 
 *** New option 'latex-indent-within-escaped-parens'
-permits to customize indentation of LaTeX environments delimited
+permits customizing indentation of LaTeX environments delimited
 by escaped parens.
 
 ** T-mouse Mode
@@ -1726,7 +1726,7 @@ and Bzr.  VC will now pass a multiple-file commit to 
these systems as
 a single changeset.
 
 *** 'vc-dir' is a new command that displays file names and their VC
-status.  It allows to apply various VC operations to a file, a
+status.  It allows applying various VC operations to a file, a
 directory or a set of files/directories.
 
 *** VC switches are no longer appended, rather the first non-nil value is used.
diff --git a/etc/NEWS.24 b/etc/NEWS.24
index 1e1206d058f..160674f5918 100644
--- a/etc/NEWS.24
+++ b/etc/NEWS.24
@@ -872,7 +872,7 @@ name and arguments.
 
 ** Tramp
 
-*** New connection method "adb", which allows to access Android
+*** New connection method "adb", which allows accessing Android
 devices by the Android Debug Bridge.  The variable 'tramp-adb-program'
 can be used to adapt the path of the "adb" program, if needed.
 
@@ -2703,12 +2703,12 @@ specified by 'display-buffer-fallback-action'.
 display actions, taking precedence over 'display-buffer-base-action'.
 
 *** New option 'window-combination-limit'.
-The new option 'window-combination-limit' allows to return the space
+The new option 'window-combination-limit' allows returning the space
 obtained for resizing or creating a window more reliably to the window
 from which such space was obtained.
 
 *** New option 'window-combination-resize'.
-The new option 'window-combination-resize' allows to split a window that
+The new option 'window-combination-resize' allows splitting a window that
 otherwise cannot be split because it's too small by stealing space from
 other windows in the same combination.  Subsequent resizing or deletion
 of the window will resize all windows in the same combination as well.
@@ -2721,7 +2721,7 @@ frame, or quitting a window showing a buffer in a frame 
of its own.
 These maximize and minimize the size of a window within its frame.
 
 *** New commands 'switch-to-prev-buffer' and 'switch-to-next-buffer'.
-These functions allow to navigate through the live buffers that have
+These functions allow navigating through the live buffers that have
 been shown in a specific window.
 
 ** Minibuffer changes
@@ -3496,7 +3496,7 @@ and 'window-body-height' are provided.
 For each window you can specify a parameter to override the default
 behavior of a number of functions like 'split-window', 'delete-window'
 and 'delete-other-windows'.  The variable 'ignore-window-parameters'
-allows to ignore processing such parameters.
+allows ignoring processing such parameters.
 
 *** New semantics of third argument of 'split-window'.
 The third argument of 'split-window' has been renamed to SIDE and can be
@@ -3554,7 +3554,7 @@ are user-customizable variables.
 See the docstring of 'display-buffer' for details.
 
 *** New functions 'window-state-get' and 'window-state-put'.
-These functions allow to save and restore the state of an arbitrary
+These functions allow saving and restoring the state of an arbitrary
 frame or window as an Elisp object.
 
 ** Completion
diff --git a/etc/NEWS.26 b/etc/NEWS.26
index 29eee5eb4a2..fb13733f45c 100644
--- a/etc/NEWS.26
+++ b/etc/NEWS.26
@@ -38,7 +38,7 @@ in its NEWS.)
 
 ** Installing Emacs now installs the emacs-module.h file.
 The emacs-module.h file is now installed in the system-wide include
-directory as part of the Emacs installation.  This allows to build
+directory as part of the Emacs installation.  This allows building
 Emacs modules outside of the Emacs source tree.
 
 
diff --git a/etc/NEWS.29 b/etc/NEWS.29
index e74cbee4a53..1b3532b5657 100644
--- a/etc/NEWS.29
+++ b/etc/NEWS.29
@@ -27,6 +27,19 @@ applies, and please also update docstrings as needed.
 
 * Startup Changes in Emacs 29.2
 
+** On GNU/Linux, Emacs is now the default application for 'org-protocol'.
+Org mode provides a way to quickly capture bookmarks, notes, and links
+using 'emacsclient':
+
+    emacsclient "org-protocol://store-link?url=URL&title=TITLE"
+
+Previously, users had to manually configure their GNU/Linux desktop
+environment to open 'org-protocol' links in Emacs.  These links should
+now open in Emacs automatically, as the "emacsclient.desktop" file now
+arranges for Emacs to be the default application for the 'org-protocol'
+URI scheme.  See the Org mode manual, Info node "(org) Protocols" for
+more details.
+
 
 * Changes in Emacs 29.2
 
diff --git a/etc/PROBLEMS b/etc/PROBLEMS
index c139f25e086..dfc34a4eaca 100644
--- a/etc/PROBLEMS
+++ b/etc/PROBLEMS
@@ -441,6 +441,17 @@ environment.
 
 ** Keyboard problems
 
+*** PGTK build of Emacs running on Wayland doesn't recognize Hyper modifier
+
+If you arrange for the Wayland compositor to send the Hyper key
+modifier (e.g., via XKB customizations), the Hyper modifier will still
+not be reported to Emacs.
+
+The reason is that GDK 3.x doesn't recognize the Hyper key modifier.
+Since GDK 3.x is no longer developed, this bug in GDK will probably
+never be solved.  And the Emacs PGTK build cannot yet support GTK4,
+where this problem is reportedly solved.
+
 *** Unable to enter the M-| key on some German keyboards.
 Some users have reported that M-| suffers from "keyboard ghosting".
 This can't be fixed by Emacs, as the keypress never gets passed to it
@@ -523,6 +534,15 @@ is to downgrade to a version of GnuPG older than 2.4.1 
(or, in the
 future, upgrade to a newer version which solves the problem, when such
 a fixed version becomes available).
 
+*** Emacs running on WSL receives stray characters as input.
+
+For example, you could see Emacs inserting 'z' characters even though
+nothing is typed on the keyboard, and even if you unplug the keyboard.
+
+The reason is a bug in the WSL X server's handling of key-press and
+key-repeat events.  A workaround is to use the Cygwin or native
+MS-Windows build of Emacs instead.
+
 ** Problems with hostname resolution
 
 *** Emacs does not know your host's fully-qualified domain name.
@@ -816,7 +836,7 @@ On many systems, it is possible to set LD_LIBRARY_PATH in 
your
 environment to specify additional directories where shared libraries
 can be found.
 
-Other systems allow to set LD_RUN_PATH in a similar way, but before
+Other systems allow setting LD_RUN_PATH in a similar way, but before
 Emacs is linked.  With LD_RUN_PATH set, the linker will include a
 specified run-time search path in the executable.
 
@@ -1638,6 +1658,18 @@ to normal, do
 
   (set-scroll-bar-mode 'left)
 
+*** Redisplay with scaled images is slow in Emacs built with Cairo.
+
+Cairo expends a noticeable amount of CPU time displaying large images
+with applied transforms.  These images most frequently appear within
+EWW buffers or in Image Mode buffers after executing the image scaling
+commands `i +' or `i -', and their presence incurs a performance
+penalty of hundereds of milliseconds to seconds upon redisplay.  The
+remedy is to build Emacs without Cairo after verifying the XRender
+extension is present on your X server and its headers are present on
+your system, in which case Emacs will use XRender to efficiently
+perform image transforms within the X server.
+
 *** Error messages about undefined colors on X.
 
 The messages might say something like this:
@@ -2508,7 +2540,7 @@ keyboard; printing that file on a PostScript printer will 
show what
 keys can serve as Meta.
 
 The 'xkeycaps' also shows a visual representation of the current
-keyboard settings.  It also allows to modify them.
+keyboard settings.  It also allows modifying them.
 
 *** GNU/Linux: slow startup on Linux-based GNU systems.
 
@@ -2762,7 +2794,7 @@ one, you could use the following workarounds:
     directory to that new home directory.
   . Move all the *.eln files from ~/.emacs.d/eln-cache to a directory
     out of the C:\Users tree, and customize Emacs to use that
-    directory for *.eln files.  This requires to call the function
+    directory for *.eln files.  This requires calling the function
     startup-redirect-eln-cache in your init file, to force Emacs to
     write *.eln files compiled at run time to that directory.
   . Delete all *.eln files in your ~/.emacs.d/eln-cache directory, and
@@ -3494,6 +3526,18 @@ points the test font attempts to hide.
 Since this behavior does not influence the display of real fonts, no
 action will be taken to address this problem.
 
+** Some other font's instruction code produces undesirable results.
+
+Executing instruction code is not a strict requirement for producing
+correct display results from most current fonts.  If a font's
+instruction code produces results that are merely unpleasing, but not
+incorrect, then the font was presumably not designed for Emacs's
+scaler.  If its uninstructed glyphs are satisfactory (such as if your
+screen resolution is high to the extent that scaling artifacts prove
+invisible), disable instruction code execution by appending its family
+name to the variable 'sfnt-uninstructable-font-regexp', then
+restarting Emacs.
+
 ** CJK text does not display in Emacs, but does in other programs.
 
 When inserting CJK text into a buffer or visiting a file containing
diff --git a/etc/TODO b/etc/TODO
index a918f496863..2292f100ac4 100644
--- a/etc/TODO
+++ b/etc/TODO
@@ -419,41 +419,6 @@ modify them.  Also, when properties are looked up during 
redisplay, we
 generally can't run Elisp code, whereas we generally can do that when
 properties are added.
 
-*** Move overlays to intervals.c
-Currently overlays are implemented as (two) sorted singly linked lists (one
-for overlays_before some position and one for overlay_after that
-position, for some quirky definition of "before" and "after").
-The function 'overlay-recenter' changes the position used for the split
-(and is called internally in various situations).
-
-Each overlay is itself implemented with two markers (which keep track of
-the overlay-start and overlay-end).  Markers are implemented as
-a non-sorted singly linked list of markers.  So every text
-insertion/deletion requires O(N) time, where N is the number of markers
-since we have to go down that list to update those markers that are
-affected by the modification.
-
-You can start in src/buffer.[ch], maybe grepping for overlays_before for
-a starting point.
-
-Text-properties, OTOH, are implemented with a (mostly) balanced binary
-tree.  This is implemented in src/intervals.[ch].
-
-So we'd like to change overlays so that they don't use markers (and we
-don't keep them in two sorted singly-linked lists) any more.  Instead,
-we'll store them inside the balanced binary tree used for
-text-properties.  I think we can use the "augmented tree" approach
-described in https://en.wikipedia.org/wiki/Interval_tree.
-
-To ease up debugging during development, I'd guess the implementation
-would first add the new stuff, keeping the old stuff (i.e. add to
-Lisp_Overlay whichever fields are needed for the new code, while keeping
-the old ones, add needed overlay fields to the intervals tree, but keep
-the old fields, the overlays_before etc...).  This way, you can add
-consistency checks that make sure the new code computes the same results
-as the old code.  And once that works well, we can remove the old code
-and old fields.
-
 ** Implement Unicode-compliant display of "default-ignorable" characters
 See the "Characters Ignored for Display" section of paragraph 5.21 in
 the Unicode Standard for the details.
@@ -473,6 +438,24 @@ wrapping of long lines under 'visual-line-mode'.  The 
algorithm for
 selecting the wrap point may also need be changed to break at the soft
 hyphen.
 
+** Support external rules for indentation
+This should teach Emacs to read indentation rules from a file and use
+them in preference to the user customizations and the built-in
+defaults.  An example of such rule files is '.clang-format', see
+
+  https://clang.llvm.org/docs/ClangFormatStyleOptions.html
+
+As a minimum, there should be a command, a variant of indent-region,
+which could be told to use the rules from such a file, and should then
+reformat the region of source code according to the rules.
+
+The next step is to use these rules during editing of files residing
+in a directory that has such an indentation-rules spec in it.
+
+For some discussion and implementation ideas (including possibly using
+LSP), see the thread starting at
+https://lists.gnu.org/archive/html/emacs-devel/2023-09/msg00609.html
+
 ** FFI (foreign function interface)
 See eg https://lists.gnu.org/r/emacs-devel/2013-10/msg00246.html
 
@@ -1246,7 +1229,7 @@ Necessary for indirect buffers to work?
 
 *** Locating schemas
 
-**** Should 'rng-validate-mode' allow to specify a schema?
+**** Should 'rng-validate-mode' allow specifying a schema?
 Give the user an opportunity to specify a schema if there is currently
 none?  Or should it at least give a hint to the user how to specify a
 non-vacuous schema?
diff --git a/etc/emacsclient.desktop b/etc/emacsclient.desktop
index a9f840c7033..4395d3b02bc 100644
--- a/etc/emacsclient.desktop
+++ b/etc/emacsclient.desktop
@@ -2,7 +2,7 @@
 Name=Emacs (Client)
 GenericName=Text Editor
 Comment=Edit text
-MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;
+MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;x-scheme-handler/org-protocol;
 Exec=sh -c "if [ -n \\"\\$*\\" ]; then exec emacsclient --alternate-editor= 
--display=\\"\\$DISPLAY\\" \\"\\$@\\"; else exec emacsclient 
--alternate-editor= --create-frame; fi" sh %F
 Icon=emacs
 Type=Application
diff --git a/etc/images/symbols/dot_large_16.pbm 
b/etc/images/symbols/dot_large_16.pbm
new file mode 100644
index 00000000000..03154adb813
Binary files /dev/null and b/etc/images/symbols/dot_large_16.pbm differ
diff --git a/etc/images/symbols/dot_large_16.svg 
b/etc/images/symbols/dot_large_16.svg
new file mode 100644
index 00000000000..dcc8eee380b
--- /dev/null
+++ b/etc/images/symbols/dot_large_16.svg
@@ -0,0 +1,3 @@
+<svg width="16" height="16" viewBox="0 0 16 16"  
xmlns="http://www.w3.org/2000/svg";>
+<circle cx="8" cy="8" r="6" />
+</svg>
diff --git a/etc/images/symbols/dot_medium_16.pbm 
b/etc/images/symbols/dot_medium_16.pbm
new file mode 100644
index 00000000000..d5af22f50c0
Binary files /dev/null and b/etc/images/symbols/dot_medium_16.pbm differ
diff --git a/etc/images/symbols/dot_medium_16.svg 
b/etc/images/symbols/dot_medium_16.svg
new file mode 100644
index 00000000000..18250ef12c6
--- /dev/null
+++ b/etc/images/symbols/dot_medium_16.svg
@@ -0,0 +1,3 @@
+<svg width="16" height="16" viewBox="0 0 16 16"  
xmlns="http://www.w3.org/2000/svg";>
+<circle cx="8" cy="8" r="4" />
+</svg>
diff --git a/etc/images/symbols/dot_small_16.pbm 
b/etc/images/symbols/dot_small_16.pbm
new file mode 100644
index 00000000000..6feef99ef53
Binary files /dev/null and b/etc/images/symbols/dot_small_16.pbm differ
diff --git a/etc/images/symbols/dot_small_16.svg 
b/etc/images/symbols/dot_small_16.svg
new file mode 100644
index 00000000000..1d6a279b5dc
--- /dev/null
+++ b/etc/images/symbols/dot_small_16.svg
@@ -0,0 +1,3 @@
+<svg width="16" height="16" viewBox="0 0 16 16"  
xmlns="http://www.w3.org/2000/svg";>
+<circle cx="8" cy="8" r="2" />
+</svg>
diff --git a/etc/refcards/orgcard.tex b/etc/refcards/orgcard.tex
index dd8cae5ce5e..62ba687c19f 100644
--- a/etc/refcards/orgcard.tex
+++ b/etc/refcards/orgcard.tex
@@ -1,5 +1,5 @@
 % Reference Card for Org Mode
-\def\orgversionnumber{9.6.7}
+\def\orgversionnumber{9.6.9}
 \def\versionyear{2023}          % latest update
 \input emacsver.tex
 
diff --git a/etc/themes/modus-operandi-deuteranopia-theme.el 
b/etc/themes/modus-operandi-deuteranopia-theme.el
index 5817d8f674c..6a2105a1c4d 100644
--- a/etc/themes/modus-operandi-deuteranopia-theme.el
+++ b/etc/themes/modus-operandi-deuteranopia-theme.el
@@ -43,6 +43,7 @@
                (require-theme 'modus-themes t))
     (require 'modus-themes))
 
+;;;###theme-autoload
   (deftheme modus-operandi-deuteranopia
     "Deuteranopia-optimized theme with a white background.
 This variant is optimized for users with red-green color
@@ -50,14 +51,17 @@ deficiency (deuteranopia).  It conforms with the highest
 legibility standard for color contrast between background and
 foreground in any given piece of text, which corresponds to a
 minimum contrast in relative luminance of 7:1 (WCAG AAA
-standard).")
+standard)."
+    :background-mode 'light
+    :kind 'color-scheme
+    :family 'modus)
 
   (defconst modus-operandi-deuteranopia-palette
     '(
 ;;; Basic values
 
       (bg-main          "#ffffff")
-      (bg-dim           "#f0f0f0")
+      (bg-dim           "#f2f2f2")
       (fg-main          "#000000")
       (fg-dim           "#595959")
       (fg-alt           "#193668")
@@ -155,7 +159,7 @@ standard).")
 ;;; Special purpose
 
       (bg-completion       "#c0deff")
-      (bg-hover            "#97dfed")
+      (bg-hover            "#b2e4dc")
       (bg-hover-secondary  "#f5d0a0")
       (bg-hl-line          "#dae5ec")
       (bg-region           "#bdbdbd")
@@ -198,7 +202,7 @@ standard).")
 
       (bg-removed         "#f4f099")
       (bg-removed-faint   "#f6f6b7")
-      (bg-removed-refine  "#f0e56f")
+      (bg-removed-refine  "#ede06f")
       (bg-removed-fringe  "#c0b200")
       (fg-removed         "#553d00")
       (fg-removed-intense "#7f6f00")
@@ -284,6 +288,7 @@ standard).")
       (date-deadline yellow-warmer)
       (date-event fg-alt)
       (date-holiday yellow-warmer)
+      (date-holiday-other blue)
       (date-now blue-faint)
       (date-range fg-alt)
       (date-scheduled yellow-cooler)
@@ -367,6 +372,48 @@ standard).")
       (fg-space border)
       (bg-space-err bg-yellow-intense)
 
+;;;; Terminal mappings
+
+      (bg-term-black           "black")
+      (fg-term-black           "black")
+      (bg-term-black-bright    "gray35")
+      (fg-term-black-bright    "gray35")
+
+      (bg-term-red             red)
+      (fg-term-red             red)
+      (bg-term-red-bright      red-warmer)
+      (fg-term-red-bright      red-warmer)
+
+      (bg-term-green           green)
+      (fg-term-green           green)
+      (bg-term-green-bright    green-cooler)
+      (fg-term-green-bright    green-cooler)
+
+      (bg-term-yellow          yellow)
+      (fg-term-yellow          yellow)
+      (bg-term-yellow-bright   yellow-warmer)
+      (fg-term-yellow-bright   yellow-warmer)
+
+      (bg-term-blue            blue)
+      (fg-term-blue            blue)
+      (bg-term-blue-bright     blue-warmer)
+      (fg-term-blue-bright     blue-warmer)
+
+      (bg-term-magenta         magenta)
+      (fg-term-magenta         magenta)
+      (bg-term-magenta-bright  magenta-cooler)
+      (fg-term-magenta-bright  magenta-cooler)
+
+      (bg-term-cyan            cyan)
+      (fg-term-cyan            cyan)
+      (bg-term-cyan-bright     cyan-cooler)
+      (fg-term-cyan-bright     cyan-cooler)
+
+      (bg-term-white           "gray65")
+      (fg-term-white           "gray65")
+      (bg-term-white-bright    "white")
+      (fg-term-white-bright    "white")
+
 ;;;; Heading mappings
 
       (fg-heading-0 cyan-cooler)
@@ -436,7 +483,4 @@ represents."
 
   (provide-theme 'modus-operandi-deuteranopia))
 
-;;;###theme-autoload
-(put 'modus-operandi-deuteranopia 'theme-properties '(:background-mode light 
:kind color-scheme :family modus))
-
 ;;; modus-operandi-deuteranopia-theme.el ends here
diff --git a/etc/themes/modus-operandi-theme.el 
b/etc/themes/modus-operandi-theme.el
index 9a69e3290b7..b9f9ee3834a 100644
--- a/etc/themes/modus-operandi-theme.el
+++ b/etc/themes/modus-operandi-theme.el
@@ -43,19 +43,23 @@
                (require-theme 'modus-themes t))
     (require 'modus-themes))
 
+;;;###theme-autoload
   (deftheme modus-operandi
     "Elegant, highly legible theme with a white background.
 Conforms with the highest legibility standard for color contrast
 between background and foreground in any given piece of text,
 which corresponds to a minimum contrast in relative luminance of
-7:1 (WCAG AAA standard).")
+7:1 (WCAG AAA standard)."
+    :background-mode 'light
+    :kind 'color-scheme
+    :family 'modus)
 
   (defconst modus-operandi-palette
     '(
 ;;; Basic values
 
       (bg-main          "#ffffff")
-      (bg-dim           "#f0f0f0")
+      (bg-dim           "#f2f2f2")
       (fg-main          "#000000")
       (fg-dim           "#595959")
       (fg-alt           "#193668")
@@ -153,7 +157,7 @@ which corresponds to a minimum contrast in relative 
luminance of
 ;;; Special purpose
 
       (bg-completion       "#c0deff")
-      (bg-hover            "#94d4ff")
+      (bg-hover            "#b2e4dc")
       (bg-hover-secondary  "#f5d0a0")
       (bg-hl-line          "#dae5ec")
       (bg-region           "#bdbdbd")
@@ -282,6 +286,7 @@ which corresponds to a minimum contrast in relative 
luminance of
       (date-deadline red)
       (date-event fg-alt)
       (date-holiday red-cooler)
+      (date-holiday-other blue)
       (date-now fg-main)
       (date-range fg-alt)
       (date-scheduled yellow-warmer)
@@ -365,6 +370,48 @@ which corresponds to a minimum contrast in relative 
luminance of
       (fg-space border)
       (bg-space-err bg-red-intense)
 
+;;;; Terminal mappings
+
+      (bg-term-black           "black")
+      (fg-term-black           "black")
+      (bg-term-black-bright    "gray35")
+      (fg-term-black-bright    "gray35")
+
+      (bg-term-red             red)
+      (fg-term-red             red)
+      (bg-term-red-bright      red-warmer)
+      (fg-term-red-bright      red-warmer)
+
+      (bg-term-green           green)
+      (fg-term-green           green)
+      (bg-term-green-bright    green-cooler)
+      (fg-term-green-bright    green-cooler)
+
+      (bg-term-yellow          yellow)
+      (fg-term-yellow          yellow)
+      (bg-term-yellow-bright   yellow-warmer)
+      (fg-term-yellow-bright   yellow-warmer)
+
+      (bg-term-blue            blue)
+      (fg-term-blue            blue)
+      (bg-term-blue-bright     blue-warmer)
+      (fg-term-blue-bright     blue-warmer)
+
+      (bg-term-magenta         magenta)
+      (fg-term-magenta         magenta)
+      (bg-term-magenta-bright  magenta-cooler)
+      (fg-term-magenta-bright  magenta-cooler)
+
+      (bg-term-cyan            cyan)
+      (fg-term-cyan            cyan)
+      (bg-term-cyan-bright     cyan-cooler)
+      (fg-term-cyan-bright     cyan-cooler)
+
+      (bg-term-white           "gray65")
+      (fg-term-white           "gray65")
+      (bg-term-white-bright    "white")
+      (fg-term-white-bright    "white")
+
 ;;;; Heading mappings
 
       (fg-heading-0 cyan-cooler)
@@ -434,7 +481,4 @@ represents."
 
   (provide-theme 'modus-operandi))
 
-;;;###theme-autoload
-(put 'modus-operandi 'theme-properties '(:background-mode light :kind 
color-scheme :family modus))
-
 ;;; modus-operandi-theme.el ends here
diff --git a/etc/themes/modus-operandi-tinted-theme.el 
b/etc/themes/modus-operandi-tinted-theme.el
index 341a7d29e84..e66a030650c 100644
--- a/etc/themes/modus-operandi-tinted-theme.el
+++ b/etc/themes/modus-operandi-tinted-theme.el
@@ -42,19 +42,23 @@
                (require-theme 'modus-themes t))
     (require 'modus-themes))
 
+;;;###theme-autoload
   (deftheme modus-operandi-tinted
     "Elegant, highly legible theme with a light ochre background.
 Conforms with the highest legibility standard for color contrast
 between background and foreground in any given piece of text,
 which corresponds to a minimum contrast in relative luminance of
-7:1 (WCAG AAA standard).")
+7:1 (WCAG AAA standard)."
+    :background-mode 'light
+    :kind 'color-scheme
+    :family 'modus)
 
   (defconst modus-operandi-tinted-palette
     '(
 ;;; Basic values
 
       (bg-main          "#fbf7f0")
-      (bg-dim           "#ede7db")
+      (bg-dim           "#efe9dd")
       (fg-main          "#000000")
       (fg-dim           "#595959")
       (fg-alt           "#193668")
@@ -152,7 +156,7 @@ which corresponds to a minimum contrast in relative 
luminance of
 ;;; Special purpose
 
       (bg-completion       "#f0c1cf")
-      (bg-hover            "#94d4ff")
+      (bg-hover            "#b2e4dc")
       (bg-hover-secondary  "#f5d0a0")
       (bg-hl-line          "#f1d5d0")
       (bg-region           "#c2bcb5")
@@ -281,6 +285,7 @@ which corresponds to a minimum contrast in relative 
luminance of
       (date-deadline red)
       (date-event fg-alt)
       (date-holiday red-cooler)
+      (date-holiday-other blue)
       (date-now fg-main)
       (date-range fg-alt)
       (date-scheduled yellow-warmer)
@@ -364,6 +369,48 @@ which corresponds to a minimum contrast in relative 
luminance of
       (fg-space border)
       (bg-space-err bg-red-intense)
 
+;;;; Terminal mappings
+
+      (bg-term-black           "black")
+      (fg-term-black           "black")
+      (bg-term-black-bright    "gray35")
+      (fg-term-black-bright    "gray35")
+
+      (bg-term-red             red)
+      (fg-term-red             red)
+      (bg-term-red-bright      red-warmer)
+      (fg-term-red-bright      red-warmer)
+
+      (bg-term-green           green)
+      (fg-term-green           green)
+      (bg-term-green-bright    green-cooler)
+      (fg-term-green-bright    green-cooler)
+
+      (bg-term-yellow          yellow)
+      (fg-term-yellow          yellow)
+      (bg-term-yellow-bright   yellow-warmer)
+      (fg-term-yellow-bright   yellow-warmer)
+
+      (bg-term-blue            blue)
+      (fg-term-blue            blue)
+      (bg-term-blue-bright     blue-warmer)
+      (fg-term-blue-bright     blue-warmer)
+
+      (bg-term-magenta         magenta)
+      (fg-term-magenta         magenta)
+      (bg-term-magenta-bright  magenta-cooler)
+      (fg-term-magenta-bright  magenta-cooler)
+
+      (bg-term-cyan            cyan)
+      (fg-term-cyan            cyan)
+      (bg-term-cyan-bright     cyan-cooler)
+      (fg-term-cyan-bright     cyan-cooler)
+
+      (bg-term-white           "gray65")
+      (fg-term-white           "gray65")
+      (bg-term-white-bright    "white")
+      (fg-term-white-bright    "white")
+
 ;;;; Heading mappings
 
       (fg-heading-0 cyan-cooler)
@@ -433,7 +480,4 @@ represents."
 
   (provide-theme 'modus-operandi-tinted))
 
-;;;###theme-autoload
-(put 'modus-operandi-tinted 'theme-properties '(:background-mode light :kind 
color-scheme :family modus))
-
 ;;; modus-operandi-tinted-theme.el ends here
diff --git a/etc/themes/modus-operandi-tritanopia-theme.el 
b/etc/themes/modus-operandi-tritanopia-theme.el
index 5d143fa7514..b7e9aa99748 100644
--- a/etc/themes/modus-operandi-tritanopia-theme.el
+++ b/etc/themes/modus-operandi-tritanopia-theme.el
@@ -43,6 +43,7 @@
                (require-theme 'modus-themes t))
     (require 'modus-themes))
 
+;;;###theme-autoload
   (deftheme modus-operandi-tritanopia
     "Tritanopia-optimized theme with a white background.
 This variant is optimized for users with blue-yellow color
@@ -50,14 +51,17 @@ deficiency (tritanopia).  It conforms with the highest
 legibility standard for color contrast between background and
 foreground in any given piece of text, which corresponds to a
 minimum contrast in relative luminance of 7:1 (WCAG AAA
-standard).")
+standard)."
+    :background-mode 'light
+    :kind 'color-scheme
+    :family 'modus)
 
   (defconst modus-operandi-tritanopia-palette
     '(
 ;;; Basic values
 
       (bg-main          "#ffffff")
-      (bg-dim           "#f0f0f0")
+      (bg-dim           "#f2f2f2")
       (fg-main          "#000000")
       (fg-dim           "#595959")
       (fg-alt           "#193668")
@@ -161,9 +165,9 @@ standard).")
       (bg-region           "#bdbdbd")
       (fg-region           "#000000")
 
-      (bg-char-0 "#ff8a5f")
-      (bg-char-1 "#bf7aff")
-      (bg-char-2 "#7fe0e0")
+      (bg-char-0 "#ff908f")
+      (bg-char-1 "#bfbfff")
+      (bg-char-2 "#5fcfdf")
 
       (bg-mode-line-active        "#afe0f2")
       (fg-mode-line-active        "#0f0f0f")
@@ -284,6 +288,7 @@ standard).")
       (date-deadline red)
       (date-event fg-alt)
       (date-holiday red)
+      (date-holiday-other cyan)
       (date-now fg-main)
       (date-range fg-alt)
       (date-scheduled magenta)
@@ -367,6 +372,48 @@ standard).")
       (fg-space border)
       (bg-space-err bg-red-intense)
 
+;;;; Terminal mappings
+
+      (bg-term-black           "black")
+      (fg-term-black           "black")
+      (bg-term-black-bright    "gray35")
+      (fg-term-black-bright    "gray35")
+
+      (bg-term-red             red)
+      (fg-term-red             red)
+      (bg-term-red-bright      red-warmer)
+      (fg-term-red-bright      red-warmer)
+
+      (bg-term-green           green)
+      (fg-term-green           green)
+      (bg-term-green-bright    green-cooler)
+      (fg-term-green-bright    green-cooler)
+
+      (bg-term-yellow          yellow)
+      (fg-term-yellow          yellow)
+      (bg-term-yellow-bright   yellow-warmer)
+      (fg-term-yellow-bright   yellow-warmer)
+
+      (bg-term-blue            blue)
+      (fg-term-blue            blue)
+      (bg-term-blue-bright     blue-warmer)
+      (fg-term-blue-bright     blue-warmer)
+
+      (bg-term-magenta         magenta)
+      (fg-term-magenta         magenta)
+      (bg-term-magenta-bright  magenta-cooler)
+      (fg-term-magenta-bright  magenta-cooler)
+
+      (bg-term-cyan            cyan)
+      (fg-term-cyan            cyan)
+      (bg-term-cyan-bright     cyan-cooler)
+      (fg-term-cyan-bright     cyan-cooler)
+
+      (bg-term-white           "gray65")
+      (fg-term-white           "gray65")
+      (bg-term-white-bright    "white")
+      (fg-term-white-bright    "white")
+
 ;;;; Heading mappings
 
       (fg-heading-0 cyan-cooler)
@@ -436,7 +483,4 @@ represents."
 
   (provide-theme 'modus-operandi-tritanopia))
 
-;;;###theme-autoload
-(put 'modus-operandi-tritanopia 'theme-properties '(:background-mode light 
:kind color-scheme :family modus))
-
 ;;; modus-operandi-tritanopia-theme.el ends here
diff --git a/etc/themes/modus-themes.el b/etc/themes/modus-themes.el
index 43c10043bc5..34130a05515 100644
--- a/etc/themes/modus-themes.el
+++ b/etc/themes/modus-themes.el
@@ -6,7 +6,7 @@
 ;; Maintainer: Modus-Themes Development <~protesilaos/modus-themes@lists.sr.ht>
 ;; URL: https://git.sr.ht/~protesilaos/modus-themes
 ;; Mailing-List: https://lists.sr.ht/~protesilaos/modus-themes
-;; Version: 4.2.0
+;; Version: 4.3.0
 ;; Package-Requires: ((emacs "27.1"))
 ;; Keywords: faces, theme, accessibility
 
@@ -841,8 +841,6 @@ represents."
 
 (defvar modus-themes-preset-overrides-faint
   '((bg-completion       bg-inactive)
-    (bg-hover            bg-cyan-subtle)
-    (bg-hover-secondary  bg-magenta-subtle)
     (bg-hl-line          bg-dim)
     (bg-paren-match      bg-cyan-subtle)
     (bg-region           bg-active)
@@ -997,9 +995,9 @@ Info node `(modus-themes) Option for palette overrides'.")
 
     (prose-block red-faint)
     (prose-done green-intense)
-    (prose-metadata cyan-faint)
+    (prose-metadata magenta-faint)
     (prose-metadata-value blue-cooler)
-    (prose-table cyan)
+    (prose-table blue)
     (prose-todo red-intense)
 
     (fg-heading-0 blue-cooler)
@@ -1031,7 +1029,7 @@ Info node `(modus-themes) Option for palette overrides'.")
     (overline-heading-6 yellow-cooler)
     (overline-heading-7 red-cooler)
     (overline-heading-8 magenta))
-  "Preset for palette overrides with faint coloration.
+  "Preset for palette overrides with intense coloration.
 
 This changes many parts of the theme to make them look more
 colorful/intense.  Many background colors are accented and
@@ -1111,7 +1109,7 @@ Info node `(modus-themes) Option for palette overrides'.")
     (fnname magenta-cooler)
     (keyword magenta-warmer)
     (preprocessor red-cooler)
-    (string olive)
+    (string green-warmer)
     (type cyan-cooler)
     (variable cyan)
     (rx-construct blue-cooler)
@@ -1322,7 +1320,20 @@ symbol, which is safe when used as a face attribute's 
value."
 
 (defun modus-themes--annotate-theme (theme)
   "Return completion annotation for THEME."
-  (format " -- %s" (car (split-string (get (intern theme) 
'theme-documentation) "\\."))))
+  (when-let ((symbol (intern-soft theme))
+             (doc-string (get symbol 'theme-documentation)))
+    (format " -- %s" (car (split-string doc-string "\\.")))))
+
+(defun modus-themes--completion-table (category candidates)
+  "Pass appropriate metadata CATEGORY to completion CANDIDATES."
+  (lambda (string pred action)
+    (if (eq action 'metadata)
+        `(metadata (category . ,category))
+      (complete-with-action action candidates string pred))))
+
+(defun modus-themes--completion-table-candidates ()
+  "Render `modus-themes--list-known-themes' as completion with theme category."
+  (modus-themes--completion-table 'theme (modus-themes--list-known-themes)))
 
 (defun modus-themes--select-prompt ()
   "Minibuffer prompt to select a Modus theme."
@@ -1330,7 +1341,7 @@ symbol, which is safe when used as a face attribute's 
value."
     (intern
      (completing-read
       "Select Modus theme: "
-      (modus-themes--list-known-themes)
+      (modus-themes--completion-table-candidates)
       nil t nil
       'modus-themes--select-theme-history))))
 
@@ -1344,12 +1355,13 @@ Disable other themes per 
`modus-themes-disable-other-themes'."
 
 (defun modus-themes--toggle-theme-p ()
   "Return non-nil if `modus-themes-to-toggle' are valid."
-  (mapc (lambda (theme)
-          (if (or (memq theme modus-themes-items)
-                  (memq theme (modus-themes--list-known-themes)))
-              theme
-            (user-error "`%s' is not part of `modus-themes-items'" theme)))
-        modus-themes-to-toggle))
+  (mapc
+   (lambda (theme)
+     (if (or (memq theme modus-themes-items)
+             (memq theme (modus-themes--list-known-themes)))
+         theme
+       (user-error "`%s' is not part of `modus-themes-items'" theme)))
+   modus-themes-to-toggle))
 
 ;;;###autoload
 (defun modus-themes-toggle ()
@@ -1364,9 +1376,7 @@ Disable other themes per 
`modus-themes-disable-other-themes'."
   (if-let* ((themes (modus-themes--toggle-theme-p))
             (one (car themes))
             (two (cadr themes)))
-      (if (eq (car custom-enabled-themes) one)
-          (modus-themes-load-theme two)
-        (modus-themes-load-theme one))
+      (modus-themes-load-theme (if (eq (car custom-enabled-themes) one) two 
one))
     (modus-themes-load-theme (modus-themes--select-prompt))))
 
 (defun modus-themes--list-colors-render (buffer theme &optional mappings &rest 
_)
@@ -1424,7 +1434,8 @@ Helper function for `modus-themes-list-colors'."
         (completion-extra-properties `(:annotation-function 
,#'modus-themes--annotate-theme)))
     (completing-read
      (format "Use palette from theme [%s]: " def)
-     (modus-themes--list-known-themes) nil t nil
+     (modus-themes--completion-table-candidates)
+     nil t nil
      'modus-themes--list-colors-prompt-history def)))
 
 (defun modus-themes-list-colors (theme &optional mappings)
@@ -1552,20 +1563,22 @@ Optional OL is the color of an overline."
          (style (or key (alist-get t modus-themes-headings)))
          (style-listp (listp style))
          (properties style)
-         (var (when (memq 'variable-pitch properties) 'variable-pitch))
+         (var (when (and style-listp (memq 'variable-pitch properties)) 
'variable-pitch))
          (weight (when style-listp (modus-themes--weight style))))
-    (list :inherit
-          (cond
-           ;; `no-bold' is for backward compatibility because we cannot
-           ;; deprecate a variable's value.
-           ((or weight (memq 'no-bold properties))
-            var)
-           (var (append (list 'bold) (list var)))
-           ('bold))
+    (list :inherit (cond
+                    ((not style-listp) 'bold)
+                    ;; `no-bold' is for backward compatibility because we 
cannot
+                    ;; deprecate a variable's value.
+                    ((or weight (memq 'no-bold properties))
+                     var)
+                    (var (append (list 'bold) (list var)))
+                    (t 'bold))
           :background (or bg 'unspecified)
           :foreground fg
           :overline (or ol 'unspecified)
-          :height (modus-themes--property-lookup properties 'height #'floatp 
'unspecified)
+          :height (if style-listp
+                      (modus-themes--property-lookup properties 'height 
#'floatp 'unspecified)
+                    'unspecified)
           :weight (or weight 'unspecified))))
 
 (defun modus-themes--org-block (fg bg)
@@ -1747,6 +1760,8 @@ FG and BG are the main colors."
     `(tool-bar ((,c :background ,bg-dim :foreground ,fg-main)))
     `(vertical-border ((,c :foreground ,border)))
 ;;;;; basic and/or ungrouped styles
+    `(appt-notification ((,c :inherit error)))
+    `(blink-matching-paren-highlight-offscreen ((,c :background 
,bg-paren-match)))
     `(bold ((,c :weight bold)))
     `(bold-italic ((,c :inherit (bold italic))))
     `(underline ((,c :underline ,fg-dim)))
@@ -1824,9 +1839,9 @@ FG and BG are the main colors."
     `(agda2-highlight-unsolved-meta-face ((,c :inherit 
modus-themes-lang-warning)))
 ;;;;; all-the-icons
     `(all-the-icons-blue ((,c :foreground ,blue-cooler)))
-    `(all-the-icons-blue-warmer ((,c :foreground ,blue-warmer)))
-    `(all-the-icons-cyan ((,c :foreground ,cyan-intense)))
-    `(all-the-icons-cyan-warmer ((,c :foreground ,cyan-warmer)))
+    `(all-the-icons-blue-alt ((,c :foreground ,blue-warmer)))
+    `(all-the-icons-cyan ((,c :foreground ,cyan)))
+    `(all-the-icons-cyan-alt ((,c :foreground ,cyan-warmer)))
     `(all-the-icons-dblue ((,c :foreground ,blue-faint)))
     `(all-the-icons-dcyan ((,c :foreground ,cyan-faint)))
     `(all-the-icons-dgreen ((,c :foreground ,green-faint)))
@@ -1834,7 +1849,7 @@ FG and BG are the main colors."
     `(all-the-icons-dorange ((,c :foreground ,red-faint)))
     `(all-the-icons-dpink ((,c :foreground ,magenta-faint)))
     `(all-the-icons-dpurple ((,c :foreground ,magenta-cooler)))
-    `(all-the-icons-dred ((,c :foreground ,red-faint)))
+    `(all-the-icons-dred ((,c :foreground ,red)))
     `(all-the-icons-dsilver ((,c :foreground ,cyan-faint)))
     `(all-the-icons-dyellow ((,c :foreground ,yellow-faint)))
     `(all-the-icons-green ((,c :foreground ,green)))
@@ -1845,12 +1860,18 @@ FG and BG are the main colors."
     `(all-the-icons-lorange ((,c :foreground ,red-warmer)))
     `(all-the-icons-lpink ((,c :foreground ,magenta)))
     `(all-the-icons-lpurple ((,c :foreground ,magenta-faint)))
-    `(all-the-icons-lred ((,c :foreground ,red)))
+    `(all-the-icons-lred ((,c :foreground ,red-faint)))
+    `(all-the-icons-lsilver ((,c :foreground "gray50")))
     `(all-the-icons-lyellow ((,c :foreground ,yellow-warmer)))
-    `(all-the-icons-maroon ((,c :foreground ,yellow-cooler)))
-    `(all-the-icons-red ((,c :foreground ,red-intense)))
-    `(all-the-icons-red-warmer ((,c :foreground ,red-cooler)))
-    `(all-the-icons-yellow ((,c :foreground ,yellow-intense)))
+    `(all-the-icons-maroon ((,c :foreground ,magenta)))
+    `(all-the-icons-orange ((,c :foreground ,yellow-warmer)))
+    `(all-the-icons-pink ((,c :foreground ,magenta-warmer)))
+    `(all-the-icons-purple ((,c :foreground ,magenta-cooler)))
+    `(all-the-icons-purple-alt ((,c :foreground ,blue-warmer)))
+    `(all-the-icons-red ((,c :foreground ,red)))
+    `(all-the-icons-red-alt ((,c :foreground ,red-cooler)))
+    `(all-the-icons-silver ((,c :foreground "gray50")))
+    `(all-the-icons-yellow ((,c :foreground ,yellow)))
 ;;;;; all-the-icons-dired
     `(all-the-icons-dired-dir-face ((,c :foreground ,cyan-faint)))
 ;;;;; all-the-icons-ibuffer
@@ -1865,23 +1886,23 @@ FG and BG are the main colors."
     `(annotate-highlight-secondary ((,c :background ,bg-magenta-subtle 
:underline ,magenta-intense)))
 ;;;;; ansi-color
     ;; Those are in Emacs28.
-    `(ansi-color-black ((,c :background "black" :foreground "black")))
-    `(ansi-color-blue ((,c :background ,blue :foreground ,blue)))
+    `(ansi-color-black ((,c :background ,bg-term-black :foreground 
,fg-term-black)))
+    `(ansi-color-blue ((,c :background ,bg-term-blue :foreground 
,fg-term-blue)))
     `(ansi-color-bold ((,c :inherit bold)))
-    `(ansi-color-bright-black ((,c :background "gray35" :foreground "gray35")))
-    `(ansi-color-bright-blue ((,c :background ,blue-warmer :foreground 
,blue-warmer)))
-    `(ansi-color-bright-cyan ((,c :background ,cyan-cooler :foreground 
,cyan-cooler)))
-    `(ansi-color-bright-green ((,c :background ,green-cooler :foreground 
,green-cooler)))
-    `(ansi-color-bright-magenta ((,c :background ,magenta-cooler :foreground 
,magenta-cooler)))
-    `(ansi-color-bright-red ((,c :background ,red-warmer :foreground 
,red-warmer)))
-    `(ansi-color-bright-white ((,c :background "white" :foreground "white")))
-    `(ansi-color-bright-yellow ((,c :background ,yellow-warmer :foreground 
,yellow-warmer)))
-    `(ansi-color-cyan ((,c :background ,cyan :foreground ,cyan)))
-    `(ansi-color-green ((,c :background ,green :foreground ,green)))
-    `(ansi-color-magenta ((,c :background ,magenta :foreground ,magenta)))
-    `(ansi-color-red ((,c :background ,red :foreground ,red)))
-    `(ansi-color-white ((,c :background "gray65" :foreground "gray65")))
-    `(ansi-color-yellow ((,c :background ,yellow :foreground ,yellow)))
+    `(ansi-color-bright-black ((,c :background ,bg-term-black-bright 
:foreground ,fg-term-black-bright)))
+    `(ansi-color-bright-blue ((,c :background ,bg-term-blue-bright :foreground 
,fg-term-blue-bright)))
+    `(ansi-color-bright-cyan ((,c :background ,bg-term-cyan-bright :foreground 
,fg-term-cyan-bright)))
+    `(ansi-color-bright-green ((,c :background ,bg-term-green-bright 
:foreground ,fg-term-green-bright)))
+    `(ansi-color-bright-magenta ((,c :background ,bg-term-magenta-bright 
:foreground ,fg-term-magenta-bright)))
+    `(ansi-color-bright-red ((,c :background ,bg-term-red-bright :foreground 
,fg-term-red-bright)))
+    `(ansi-color-bright-white ((,c :background ,bg-term-white-bright 
:foreground ,fg-term-white-bright)))
+    `(ansi-color-bright-yellow ((,c :background ,bg-term-yellow-bright 
:foreground ,fg-term-yellow-bright)))
+    `(ansi-color-cyan ((,c :background ,bg-term-cyan :foreground 
,fg-term-cyan)))
+    `(ansi-color-green ((,c :background ,bg-term-green :foreground 
,fg-term-green)))
+    `(ansi-color-magenta ((,c :background ,bg-term-magenta :foreground 
,fg-term-magenta)))
+    `(ansi-color-red ((,c :background ,bg-term-red :foreground ,fg-term-red)))
+    `(ansi-color-white ((,c :background ,bg-term-white :foreground 
,fg-term-white)))
+    `(ansi-color-yellow ((,c :background ,bg-term-yellow :foreground 
,fg-term-yellow)))
 ;;;;; anzu
     `(anzu-match-1 ((,c :inherit modus-themes-subtle-cyan)))
     `(anzu-match-2 ((,c :inherit modus-themes-search-current)))
@@ -1932,6 +1953,10 @@ FG and BG are the main colors."
     `(binder-sidebar-marked ((,c :inherit modus-themes-mark-sel)))
     `(binder-sidebar-missing ((,c :inherit modus-themes-mark-del)))
     `(binder-sidebar-tags ((,c :foreground ,variable)))
+;;;;; breadcrumb
+    `(breadcrumb-face ((,c :foreground ,fg-alt)))
+    `(breadcrumb-imenu-leaf-face ((,c :inherit bold :foreground 
,modeline-info))) ; same as `which-func'
+    `(breadcrumb-project-leaf-face ((,c :inherit bold)))
 ;;;;; bongo
     `(bongo-album-title (( )))
     `(bongo-artist ((,c :foreground ,accent-0)))
@@ -2009,7 +2034,7 @@ FG and BG are the main colors."
     `(change-log-name ((,c :foreground ,name)))
     `(log-edit-header ((,c :inherit bold)))
     `(log-edit-headers-separator ((,c :height 1 :background ,border :extend 
t)))
-    `(log-edit-summary ((,c :inherit bold :foreground ,blue)))
+    `(log-edit-summary ((,c :inherit success)))
     `(log-edit-unknown-header ((,c :inherit shadow)))
     `(log-view-commit-body (( )))
     `(log-view-file ((,c :inherit bold)))
@@ -2084,6 +2109,8 @@ FG and BG are the main colors."
     `(corfu-bar ((,c :background ,fg-dim)))
     `(corfu-border ((,c :background ,bg-active)))
     `(corfu-default ((,c :background ,bg-dim)))
+;;;;; corfu-candidate-overlay
+    `(corfu-candidate-overlay-face ((t :inherit shadow)))
 ;;;;; corfu-quick
     `(corfu-quick1 ((,c :inherit bold :background ,bg-char-0)))
     `(corfu-quick2 ((,c :inherit bold :background ,bg-char-1)))
@@ -2104,9 +2131,6 @@ FG and BG are the main colors."
     `(crontab-month ((,c :foreground ,constant)))
     `(crontab-week-day ((,c :foreground ,variable)))
     `(crontab-predefined ((,c :foreground ,string)))
-;;;;; css-mode
-    `(css-property ((,c :inherit font-lock-type-face)))
-    `(css-selector ((,c :inherit font-lock-keyword-face)))
 ;;;;; csv-mode
     `(csv-separator-face ((,c :foreground ,red-intense)))
 ;;;;; ctrlf
@@ -2413,6 +2437,11 @@ FG and BG are the main colors."
 ;;;;; ert
     `(ert-test-result-expected ((,c :inherit modus-themes-prominent-note)))
     `(ert-test-result-unexpected ((,c :inherit modus-themes-prominent-error)))
+;;;;; erts-mode
+    `(erts-mode-end-test ((,c :inherit error)))
+    `(erts-mode-specification-name ((,c :inherit bold)))
+    `(erts-mode-specification-value ((,c :foreground ,string)))
+    `(erts-mode-start-test ((,c :inherit success)))
 ;;;;; eshell
     `(eshell-ls-archive ((,c :foreground ,accent-2)))
     `(eshell-ls-backup ((,c :inherit shadow)))
@@ -2524,7 +2553,7 @@ FG and BG are the main colors."
     `(git-commit-keyword ((,c :foreground ,keyword)))
     `(git-commit-nonempty-second-line ((,c :inherit error)))
     `(git-commit-overlong-summary ((,c :inherit warning)))
-    `(git-commit-summary ((,c :inherit bold :foreground ,blue)))
+    `(git-commit-summary ((,c :inherit success)))
 ;;;;; git-gutter
     `(git-gutter:added ((,c :background ,bg-added-fringe)))
     `(git-gutter:deleted ((,c :background ,bg-removed-fringe)))
@@ -2799,6 +2828,8 @@ FG and BG are the main colors."
 ;;;;; ivy-posframe
     `(ivy-posframe-border ((,c :background ,border)))
     `(ivy-posframe-cursor ((,c :background ,fg-main :foreground ,bg-main)))
+;;;;; japanese-holidays
+    `(japanese-holiday-saturday ((,c :foreground ,date-holiday-other)))
 ;;;;; jira (org-jira)
     `(jiralib-comment-face ((,c :background ,bg-inactive)))
     `(jiralib-comment-header-face ((,c :inherit bold)))
@@ -3127,6 +3158,50 @@ FG and BG are the main colors."
     `(mc/cursor-bar-face ((,c :height 1 :foreground ,fg-main :background 
,bg-main)))
     `(mc/cursor-face ((,c :inverse-video t)))
     `(mc/region-face ((,c :inherit region)))
+;;;;; nerd-icons
+    `(nerd-icons-blue ((,c :foreground ,blue-cooler)))
+    `(nerd-icons-blue-alt ((,c :foreground ,blue-warmer)))
+    `(nerd-icons-cyan ((,c :foreground ,cyan)))
+    `(nerd-icons-cyan-alt ((,c :foreground ,cyan-warmer)))
+    `(nerd-icons-dblue ((,c :foreground ,blue-faint)))
+    `(nerd-icons-dcyan ((,c :foreground ,cyan-faint)))
+    `(nerd-icons-dgreen ((,c :foreground ,green-faint)))
+    `(nerd-icons-dmaroon ((,c :foreground ,magenta-faint)))
+    `(nerd-icons-dorange ((,c :foreground ,red-faint)))
+    `(nerd-icons-dpink ((,c :foreground ,magenta-faint)))
+    `(nerd-icons-dpurple ((,c :foreground ,magenta-cooler)))
+    `(nerd-icons-dred ((,c :foreground ,red)))
+    `(nerd-icons-dsilver ((,c :foreground ,cyan-faint)))
+    `(nerd-icons-dyellow ((,c :foreground ,yellow-faint)))
+    `(nerd-icons-green ((,c :foreground ,green)))
+    `(nerd-icons-lblue ((,c :foreground ,blue-cooler)))
+    `(nerd-icons-lcyan ((,c :foreground ,cyan)))
+    `(nerd-icons-lgreen ((,c :foreground ,green-warmer)))
+    `(nerd-icons-lmaroon ((,c :foreground ,magenta-warmer)))
+    `(nerd-icons-lorange ((,c :foreground ,red-warmer)))
+    `(nerd-icons-lpink ((,c :foreground ,magenta)))
+    `(nerd-icons-lpurple ((,c :foreground ,magenta-faint)))
+    `(nerd-icons-lred ((,c :foreground ,red-faint)))
+    `(nerd-icons-lsilver ((,c :foreground "gray50")))
+    `(nerd-icons-lyellow ((,c :foreground ,yellow-warmer)))
+    `(nerd-icons-maroon ((,c :foreground ,magenta)))
+    `(nerd-icons-orange ((,c :foreground ,yellow-warmer)))
+    `(nerd-icons-pink ((,c :foreground ,magenta-warmer)))
+    `(nerd-icons-purple ((,c :foreground ,magenta-cooler)))
+    `(nerd-icons-purple-alt ((,c :foreground ,blue-warmer)))
+    `(nerd-icons-red ((,c :foreground ,red)))
+    `(nerd-icons-red-alt ((,c :foreground ,red-cooler)))
+    `(nerd-icons-silver ((,c :foreground "gray50")))
+    `(nerd-icons-yellow ((,c :foreground ,yellow)))
+;;;;; nerd-icons-completion
+    `(nerd-icons-completion-dir-face ((,c :foreground ,cyan-faint)))
+;;;;; nerd-icons-dired
+    `(nerd-icons-dired-dir-face ((,c :foreground ,cyan-faint)))
+;;;;; nerd-icons-ibuffer
+    `(nerd-icons-ibuffer-dir-face ((,c :foreground ,cyan-faint)))
+    `(nerd-icons-ibuffer-file-face ((,c :foreground ,blue-faint)))
+    `(nerd-icons-ibuffer-mode-face ((,c :foreground ,cyan)))
+    `(nerd-icons-ibuffer-size-face ((,c :foreground ,cyan-cooler)))
 ;;;;; neotree
     `(neo-banner-face ((,c :foreground ,accent-0)))
     `(neo-button-face ((,c :inherit button)))
@@ -3247,7 +3322,7 @@ FG and BG are the main colors."
     `(org-date ((,c :inherit modus-themes-fixed-pitch :foreground 
,date-common)))
     `(org-date-selected ((,c :foreground ,date-common :inverse-video t)))
     `(org-document-info ((,c :foreground ,prose-metadata-value)))
-    `(org-document-info-keyword ((,c :foreground ,prose-metadata)))
+    `(org-document-info-keyword ((,c :inherit modus-themes-fixed-pitch 
:foreground ,prose-metadata)))
     `(org-document-title ((,c :inherit modus-themes-heading-0)))
     `(org-done ((,c :foreground ,prose-done)))
     `(org-drawer ((,c :inherit modus-themes-fixed-pitch :foreground 
,prose-metadata)))
@@ -3707,16 +3782,19 @@ FG and BG are the main colors."
     `(terraform--resource-name-face ((,c :foreground ,keyword)))
     `(terraform--resource-type-face ((,c :foreground ,type)))
 ;;;;; term
+    ;; NOTE 2023-08-10: `term-color-black' and `term-color-white' use
+    ;; the "bright" semantic color mappings to make sure they are
+    ;; distinct from `term'.
     `(term ((,c :background ,bg-main :foreground ,fg-main)))
     `(term-bold ((,c :inherit bold)))
-    `(term-color-black ((,c :background "gray35" :foreground "gray35")))
-    `(term-color-blue ((,c :background ,blue :foreground ,blue)))
-    `(term-color-cyan ((,c :background ,cyan :foreground ,cyan)))
-    `(term-color-green ((,c :background ,green :foreground ,green)))
-    `(term-color-magenta ((,c :background ,magenta :foreground ,magenta)))
-    `(term-color-red ((,c :background ,red :foreground ,red)))
-    `(term-color-white ((,c :background "gray65" :foreground "gray65")))
-    `(term-color-yellow ((,c :background ,yellow :foreground ,yellow)))
+    `(term-color-black ((,c :background ,bg-term-black-bright :foreground 
,fg-term-black-bright)))
+    `(term-color-blue ((,c :background ,bg-term-blue :foreground 
,fg-term-blue)))
+    `(term-color-cyan ((,c :background ,bg-term-cyan :foreground 
,fg-term-cyan)))
+    `(term-color-green ((,c :background ,bg-term-green :foreground 
,fg-term-green)))
+    `(term-color-magenta ((,c :background ,bg-term-magenta :foreground 
,fg-term-magenta)))
+    `(term-color-red ((,c :background ,bg-term-red :foreground ,fg-term-red)))
+    `(term-color-white ((,c :background ,bg-term-white-bright :foreground 
,fg-term-white-bright)))
+    `(term-color-yellow ((,c :background ,bg-term-yellow :foreground 
,fg-term-yellow)))
     `(term-underline ((,c :underline t)))
 ;;;;; textsec
     `(textsec-suspicious (( )))
@@ -3847,17 +3925,20 @@ FG and BG are the main colors."
     `(vr/match-1 ((,c :inherit modus-themes-intense-yellow)))
     `(vr/match-separator-face ((,c :inherit bold :background ,bg-active)))
 ;;;;; vterm
-    `(vterm-color-black ((,c :background "gray35" :foreground "black")))
-    `(vterm-color-blue ((,c :background ,blue-warmer :foreground ,blue)))
-    `(vterm-color-cyan ((,c :background ,cyan-cooler :foreground ,cyan)))
+    ;; NOTE 2023-08-10: `vterm-color-black' and `vterm-color-white'
+    ;; use the "bright" semantic color mappings to make sure they are
+    ;; distinct from `vterm-color-default'.
+    `(vterm-color-black ((,c :background ,bg-term-black :foreground 
,fg-term-black)))
+    `(vterm-color-blue ((,c :background ,bg-term-blue :foreground 
,fg-term-blue)))
+    `(vterm-color-cyan ((,c :background ,bg-term-cyan :foreground 
,fg-term-cyan)))
     `(vterm-color-default ((,c :background ,bg-main :foreground ,fg-main)))
-    `(vterm-color-green ((,c :background ,green-cooler :foreground ,green)))
+    `(vterm-color-green ((,c :background ,bg-term-green :foreground 
,fg-term-green)))
     `(vterm-color-inverse-video ((,c :background ,bg-main :inverse-video t)))
-    `(vterm-color-magenta ((,c :background ,magenta-cooler :foreground 
,magenta)))
-    `(vterm-color-red ((,c :background ,red-warmer :foreground ,red)))
+    `(vterm-color-magenta ((,c :background ,bg-term-magenta :foreground 
,fg-term-magenta)))
+    `(vterm-color-red ((,c :background ,bg-term-red :foreground ,fg-term-red)))
     `(vterm-color-underline ((,c :underline t)))
-    `(vterm-color-white ((,c :background "white" :foreground "gray65")))
-    `(vterm-color-yellow ((,c :background ,yellow-warmer :foreground ,yellow)))
+    `(vterm-color-white ((,c :background ,bg-term-white :foreground 
,fg-term-white)))
+    `(vterm-color-yellow ((,c :background ,bg-term-yellow :foreground 
,fg-term-yellow)))
 ;;;;; vundo
     `(vundo-default ((,c :inherit shadow)))
     `(vundo-highlight ((,c :inherit (bold vundo-node) :foreground ,red)))
@@ -3941,7 +4022,7 @@ FG and BG are the main colors."
     `(wgrep-file-face ((,c :foreground ,fg-alt)))
     `(wgrep-reject-face ((,c :inherit error)))
 ;;;;; which-function-mode
-    `(which-func ((,c :inherit bold :foreground ,modeline-info)))
+    `(which-func ((,c :inherit bold :foreground ,modeline-info))) ; same as 
`breadcrumb-imenu-leaf-face'
 ;;;;; which-key
     `(which-key-command-description-face ((,c :foreground ,fg-main)))
     `(which-key-group-description-face ((,c :foreground ,keyword)))
diff --git a/etc/themes/modus-vivendi-deuteranopia-theme.el 
b/etc/themes/modus-vivendi-deuteranopia-theme.el
index 60c3c62b38f..6de293ad5bc 100644
--- a/etc/themes/modus-vivendi-deuteranopia-theme.el
+++ b/etc/themes/modus-vivendi-deuteranopia-theme.el
@@ -42,6 +42,7 @@
                (require-theme 'modus-themes t))
     (require 'modus-themes))
 
+;;;###theme-autoload
   (deftheme modus-vivendi-deuteranopia
     "Deuteranopia-optimized theme with a black background.
 This variant is optimized for users with red-green color
@@ -49,7 +50,10 @@ deficiency (deuteranopia).  It conforms with the highest
 legibility standard for color contrast between background and
 foreground in any given piece of text, which corresponds to a
 minimum contrast in relative luminance of 7:1 (WCAG AAA
-standard).")
+standard)."
+    :background-mode 'dark
+    :kind 'color-scheme
+    :family 'modus)
 
   (defconst modus-vivendi-deuteranopia-palette
     '(
@@ -154,7 +158,7 @@ standard).")
 ;;; Special purpose
 
       (bg-completion       "#2f447f")
-      (bg-hover            "#004f70")
+      (bg-hover            "#45605e")
       (bg-hover-secondary  "#654a39")
       (bg-hl-line          "#2f3849")
       (bg-region           "#5a5a5a")
@@ -197,7 +201,7 @@ standard).")
 
       (bg-removed         "#3d3d00")
       (bg-removed-faint   "#281f00")
-      (bg-removed-refine  "#515100")
+      (bg-removed-refine  "#555500")
       (bg-removed-fringe  "#d0c03f")
       (fg-removed         "#d4d48f")
       (fg-removed-intense "#d0b05f")
@@ -283,6 +287,7 @@ standard).")
       (date-deadline yellow-warmer)
       (date-event fg-alt)
       (date-holiday yellow-warmer)
+      (date-holiday-other blue)
       (date-now blue-faint)
       (date-range fg-alt)
       (date-scheduled yellow-cooler)
@@ -366,6 +371,48 @@ standard).")
       (fg-space border)
       (bg-space-err bg-yellow-intense)
 
+;;;; Terminal mappings
+
+      (bg-term-black           "black")
+      (fg-term-black           "black")
+      (bg-term-black-bright    "gray35")
+      (fg-term-black-bright    "gray35")
+
+      (bg-term-red             red)
+      (fg-term-red             red)
+      (bg-term-red-bright      red-warmer)
+      (fg-term-red-bright      red-warmer)
+
+      (bg-term-green           green)
+      (fg-term-green           green)
+      (bg-term-green-bright    green-cooler)
+      (fg-term-green-bright    green-cooler)
+
+      (bg-term-yellow          yellow)
+      (fg-term-yellow          yellow)
+      (bg-term-yellow-bright   yellow-warmer)
+      (fg-term-yellow-bright   yellow-warmer)
+
+      (bg-term-blue            blue)
+      (fg-term-blue            blue)
+      (bg-term-blue-bright     blue-warmer)
+      (fg-term-blue-bright     blue-warmer)
+
+      (bg-term-magenta         magenta)
+      (fg-term-magenta         magenta)
+      (bg-term-magenta-bright  magenta-cooler)
+      (fg-term-magenta-bright  magenta-cooler)
+
+      (bg-term-cyan            cyan)
+      (fg-term-cyan            cyan)
+      (bg-term-cyan-bright     cyan-cooler)
+      (fg-term-cyan-bright     cyan-cooler)
+
+      (bg-term-white           "gray65")
+      (fg-term-white           "gray65")
+      (bg-term-white-bright    "white")
+      (fg-term-white-bright    "white")
+
 ;;;; Heading mappings
 
       (fg-heading-0 cyan-cooler)
@@ -435,7 +482,4 @@ represents."
 
   (provide-theme 'modus-vivendi-deuteranopia))
 
-;;;###theme-autoload
-(put 'modus-vivendi-deuteranopia 'theme-properties '(:background-mode dark 
:kind color-scheme :family modus))
-
 ;;; modus-vivendi-deuteranopia-theme.el ends here
diff --git a/etc/themes/modus-vivendi-theme.el 
b/etc/themes/modus-vivendi-theme.el
index 70dd0f78675..b193a96524f 100644
--- a/etc/themes/modus-vivendi-theme.el
+++ b/etc/themes/modus-vivendi-theme.el
@@ -42,12 +42,16 @@
                (require-theme 'modus-themes t))
     (require 'modus-themes))
 
+;;;###theme-autoload
   (deftheme modus-vivendi
     "Elegant, highly legible theme with a black background.
 Conforms with the highest legibility standard for color contrast
 between background and foreground in any given piece of text,
 which corresponds to a minimum contrast in relative luminance of
-7:1 (WCAG AAA standard).")
+7:1 (WCAG AAA standard)."
+    :background-mode 'dark
+    :kind 'color-scheme
+    :family 'modus)
 
   (defconst modus-vivendi-palette
     '(
@@ -152,7 +156,7 @@ which corresponds to a minimum contrast in relative 
luminance of
 ;;; Special purpose
 
       (bg-completion       "#2f447f")
-      (bg-hover            "#004f70")
+      (bg-hover            "#45605e")
       (bg-hover-secondary  "#654a39")
       (bg-hl-line          "#2f3849")
       (bg-region           "#5a5a5a")
@@ -281,6 +285,7 @@ which corresponds to a minimum contrast in relative 
luminance of
       (date-deadline red)
       (date-event fg-alt)
       (date-holiday red-cooler)
+      (date-holiday-other blue)
       (date-now fg-main)
       (date-range fg-alt)
       (date-scheduled yellow-warmer)
@@ -364,6 +369,48 @@ which corresponds to a minimum contrast in relative 
luminance of
       (fg-space border)
       (bg-space-err bg-red-intense)
 
+;;;; Terminal mappings
+
+      (bg-term-black           "black")
+      (fg-term-black           "black")
+      (bg-term-black-bright    "gray35")
+      (fg-term-black-bright    "gray35")
+
+      (bg-term-red             red)
+      (fg-term-red             red)
+      (bg-term-red-bright      red-warmer)
+      (fg-term-red-bright      red-warmer)
+
+      (bg-term-green           green)
+      (fg-term-green           green)
+      (bg-term-green-bright    green-cooler)
+      (fg-term-green-bright    green-cooler)
+
+      (bg-term-yellow          yellow)
+      (fg-term-yellow          yellow)
+      (bg-term-yellow-bright   yellow-warmer)
+      (fg-term-yellow-bright   yellow-warmer)
+
+      (bg-term-blue            blue)
+      (fg-term-blue            blue)
+      (bg-term-blue-bright     blue-warmer)
+      (fg-term-blue-bright     blue-warmer)
+
+      (bg-term-magenta         magenta)
+      (fg-term-magenta         magenta)
+      (bg-term-magenta-bright  magenta-cooler)
+      (fg-term-magenta-bright  magenta-cooler)
+
+      (bg-term-cyan            cyan)
+      (fg-term-cyan            cyan)
+      (bg-term-cyan-bright     cyan-cooler)
+      (fg-term-cyan-bright     cyan-cooler)
+
+      (bg-term-white           "gray65")
+      (fg-term-white           "gray65")
+      (bg-term-white-bright    "white")
+      (fg-term-white-bright    "white")
+
 ;;;; Heading mappings
 
       (fg-heading-0 cyan-cooler)
@@ -434,7 +481,4 @@ represents."
 
   (provide-theme 'modus-vivendi))
 
-;;;###theme-autoload
-(put 'modus-vivendi 'theme-properties '(:background-mode dark :kind 
color-scheme :family modus))
-
 ;;; modus-vivendi-theme.el ends here
diff --git a/etc/themes/modus-vivendi-tinted-theme.el 
b/etc/themes/modus-vivendi-tinted-theme.el
index b6443bdf6cc..bf72f88d8e0 100644
--- a/etc/themes/modus-vivendi-tinted-theme.el
+++ b/etc/themes/modus-vivendi-tinted-theme.el
@@ -42,12 +42,16 @@
                (require-theme 'modus-themes t))
     (require 'modus-themes))
 
+;;;###theme-autoload
   (deftheme modus-vivendi-tinted
     "Elegant, highly legible theme with a night sky background.
 Conforms with the highest legibility standard for color contrast
 between background and foreground in any given piece of text,
 which corresponds to a minimum contrast in relative luminance of
-7:1 (WCAG AAA standard).")
+7:1 (WCAG AAA standard)."
+    :background-mode 'dark
+    :kind 'color-scheme
+    :family 'modus)
 
   (defconst modus-vivendi-tinted-palette
     '(
@@ -146,7 +150,7 @@ which corresponds to a minimum contrast in relative 
luminance of
 ;;; Special purpose
 
       (bg-completion       "#483d8a")
-      (bg-hover            "#004f70")
+      (bg-hover            "#45605e")
       (bg-hover-secondary  "#654a39")
       (bg-hl-line          "#303a6f")
       (bg-region           "#555a66")
@@ -281,6 +285,7 @@ which corresponds to a minimum contrast in relative 
luminance of
       (date-deadline red)
       (date-event fg-alt)
       (date-holiday red-cooler)
+      (date-holiday-other blue)
       (date-now fg-main)
       (date-range fg-alt)
       (date-scheduled yellow-warmer)
@@ -364,6 +369,48 @@ which corresponds to a minimum contrast in relative 
luminance of
       (bg-space unspecified)
       (fg-space border)
 
+;;;; Terminal mappings
+
+      (bg-term-black           "black")
+      (fg-term-black           "black")
+      (bg-term-black-bright    "gray35")
+      (fg-term-black-bright    "gray35")
+
+      (bg-term-red             red)
+      (fg-term-red             red)
+      (bg-term-red-bright      red-warmer)
+      (fg-term-red-bright      red-warmer)
+
+      (bg-term-green           green)
+      (fg-term-green           green)
+      (bg-term-green-bright    green-cooler)
+      (fg-term-green-bright    green-cooler)
+
+      (bg-term-yellow          yellow)
+      (fg-term-yellow          yellow)
+      (bg-term-yellow-bright   yellow-warmer)
+      (fg-term-yellow-bright   yellow-warmer)
+
+      (bg-term-blue            blue)
+      (fg-term-blue            blue)
+      (bg-term-blue-bright     blue-warmer)
+      (fg-term-blue-bright     blue-warmer)
+
+      (bg-term-magenta         magenta)
+      (fg-term-magenta         magenta)
+      (bg-term-magenta-bright  magenta-cooler)
+      (fg-term-magenta-bright  magenta-cooler)
+
+      (bg-term-cyan            cyan)
+      (fg-term-cyan            cyan)
+      (bg-term-cyan-bright     cyan-cooler)
+      (fg-term-cyan-bright     cyan-cooler)
+
+      (bg-term-white           "gray65")
+      (fg-term-white           "gray65")
+      (bg-term-white-bright    "white")
+      (fg-term-white-bright    "white")
+
 ;;;; Heading mappings
 
       (fg-heading-0 cyan-cooler)
@@ -433,7 +480,4 @@ represents."
 
   (provide-theme 'modus-vivendi-tinted))
 
-;;;###theme-autoload
-(put 'modus-vivendi-tinted 'theme-properties '(:background-mode dark :kind 
color-scheme :family modus))
-
 ;;; modus-vivendi-tinted-theme.el ends here
diff --git a/etc/themes/modus-vivendi-tritanopia-theme.el 
b/etc/themes/modus-vivendi-tritanopia-theme.el
index 0d09989e2a7..d808d0250fc 100644
--- a/etc/themes/modus-vivendi-tritanopia-theme.el
+++ b/etc/themes/modus-vivendi-tritanopia-theme.el
@@ -6,6 +6,7 @@
 ;; Maintainer: Modus-Themes Development <~protesilaos/modus-themes@lists.sr.ht>
 ;; URL: https://git.sr.ht/~protesilaos/modus-themes
 ;; Mailing-List: https://lists.sr.ht/~protesilaos/modus-themes
+;; Keywords: faces, theme, accessibility
 
 ;; This file is part of GNU Emacs.
 
@@ -42,6 +43,7 @@
                (require-theme 'modus-themes t))
     (require 'modus-themes))
 
+;;;###theme-autoload
   (deftheme modus-vivendi-tritanopia
     "Tritanopia-optimized theme with a black background.
 This variant is optimized for users with blue-yellow color
@@ -49,7 +51,10 @@ deficiency (tritanopia).  It conforms with the highest
 legibility standard for color contrast between background and
 foreground in any given piece of text, which corresponds to a
 minimum contrast in relative luminance of 7:1 (WCAG AAA
-standard).")
+standard)."
+    :background-mode 'dark
+    :kind 'color-scheme
+    :family 'modus)
 
   (defconst modus-vivendi-tritanopia-palette
     '(
@@ -161,8 +166,8 @@ standard).")
       (fg-region           "#ffffff")
 
       (bg-char-0 "#922a00")
-      (bg-char-1 "#4f3f7f")
-      (bg-char-2 "#00709f")
+      (bg-char-1 "#00709f")
+      (bg-char-2 "#5f3faf")
 
       (bg-mode-line-active        "#003c52")
       (fg-mode-line-active        "#f0f0f0")
@@ -283,6 +288,7 @@ standard).")
       (date-deadline red)
       (date-event fg-alt)
       (date-holiday red-intense)
+      (date-holiday-other cyan-warmer)
       (date-now fg-main)
       (date-range fg-alt)
       (date-scheduled magenta)
@@ -366,6 +372,48 @@ standard).")
       (fg-space border)
       (bg-space-err bg-red-intense)
 
+;;;; Terminal mappings
+
+      (bg-term-black           "black")
+      (fg-term-black           "black")
+      (bg-term-black-bright    "gray35")
+      (fg-term-black-bright    "gray35")
+
+      (bg-term-red             red)
+      (fg-term-red             red)
+      (bg-term-red-bright      red-warmer)
+      (fg-term-red-bright      red-warmer)
+
+      (bg-term-green           green)
+      (fg-term-green           green)
+      (bg-term-green-bright    green-cooler)
+      (fg-term-green-bright    green-cooler)
+
+      (bg-term-yellow          yellow)
+      (fg-term-yellow          yellow)
+      (bg-term-yellow-bright   yellow-warmer)
+      (fg-term-yellow-bright   yellow-warmer)
+
+      (bg-term-blue            blue)
+      (fg-term-blue            blue)
+      (bg-term-blue-bright     blue-warmer)
+      (fg-term-blue-bright     blue-warmer)
+
+      (bg-term-magenta         magenta)
+      (fg-term-magenta         magenta)
+      (bg-term-magenta-bright  magenta-cooler)
+      (fg-term-magenta-bright  magenta-cooler)
+
+      (bg-term-cyan            cyan)
+      (fg-term-cyan            cyan)
+      (bg-term-cyan-bright     cyan-cooler)
+      (fg-term-cyan-bright     cyan-cooler)
+
+      (bg-term-white           "gray65")
+      (fg-term-white           "gray65")
+      (bg-term-white-bright    "white")
+      (fg-term-white-bright    "white")
+
 ;;;; Heading mappings
 
       (fg-heading-0 cyan-cooler)
@@ -435,7 +483,4 @@ represents."
 
   (provide-theme 'modus-vivendi-tritanopia))
 
-;;;###theme-autoload
-(put 'modus-vivendi-tritanopia 'theme-properties '(:background-mode dark :kind 
color-scheme :family modus))
-
 ;;; modus-vivendi-tritanopia-theme.el ends here
diff --git a/exec/exec.c b/exec/exec.c
index dae05755675..231b5b1c46a 100644
--- a/exec/exec.c
+++ b/exec/exec.c
@@ -309,10 +309,10 @@ write_load_command (program_header *header, bool 
use_alternate,
 #else /* HAVE_GETPAGESIZE */
   if (!pagesize)
     pagesize = sysconf (_SC_PAGESIZE);
+#endif /* HAVE_GETPAGESIZE */
 
 #define PAGE_MASK (~(pagesize - 1))
 #define PAGE_SIZE (pagesize)
-#endif /* HAVE_GETPAGESIZE */
 #endif /* PAGE_MASK */
 
   start = header->p_vaddr & PAGE_MASK;
diff --git a/exec/trace.c b/exec/trace.c
index 3b384792d0a..f9deef8eb2d 100644
--- a/exec/trace.c
+++ b/exec/trace.c
@@ -1039,16 +1039,22 @@ process_system_call (struct exec_tracee *tracee)
 #endif /* READLINK_SYSCALL */
     case READLINKAT_SYSCALL:
 
-      /* Handle this readlinkat system call.  */
-      rc = handle_readlinkat (callno, &regs, tracee,
-                             &result);
+      /* This system call is already in progress if
+        TRACEE->waiting_for_syscall is true.  */
 
-      /* rc means the same as in `handle_exec'.  */
+      if (!tracee->waiting_for_syscall)
+       {
+         /* Handle this readlinkat system call.  */
+         rc = handle_readlinkat (callno, &regs, tracee,
+                                 &result);
+
+         /* rc means the same as in `handle_exec'.  */
 
-      if (rc == 1)
-       goto report_syscall_error;
-      else if (rc == 2)
-       goto emulate_syscall;
+         if (rc == 1)
+           goto report_syscall_error;
+         else if (rc == 2)
+           goto emulate_syscall;
+       }
 
       /* Fallthrough.  */
 
diff --git a/java/AndroidManifest.xml.in b/java/AndroidManifest.xml.in
index 2cbcdbc3e5b..9ba9dabde81 100644
--- a/java/AndroidManifest.xml.in
+++ b/java/AndroidManifest.xml.in
@@ -40,6 +40,8 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>. -->
   <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
   <uses-permission android:name="android.permission.INTERNET" />
   <uses-permission android:name="android.permission.SET_WALLPAPER" />
+  <uses-permission android:name="android.permission.READ_CALENDAR" />
+  <uses-permission android:name="android.permission.WRITE_CALENDAR" />
   <!-- Despite the claim that WRITE_EXTERNAL_STORAGE also covers
        reading from external storage, specifying READ_EXTERNAL_STORAGE
        seems to still be necessary on some versions of Android.
@@ -107,73 +109,36 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>. -->
         <action android:name="android.intent.action.VIEW"/>
        <action android:name="android.intent.action.EDIT"/>
        <action android:name="android.intent.action.PICK"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+       <!-- Don't offer to start Emacs for URLs that designate
+            resources other than files.  -->
+       <data android:mimeType="*/*" android:scheme="file"/>
+       <data android:mimeType="*/*" android:scheme="content"/>
+      </intent-filter>
+
+      <!-- Facilitate opening org-protocol:// URLs as well, the same
+           way emacsclient.desktop does.  -->
+
+      <intent-filter>
+        <action android:name="android.intent.action.VIEW"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+        <category android:name="android.intent.category.BROWSABLE"/>
+        <data android:scheme="org-protocol"/>
+      </intent-filter>
+
+      <!-- And also mailto links.  -->
 
+      <intent-filter>
+        <action android:name="android.intent.action.VIEW"/>
         <category android:name="android.intent.category.DEFAULT"/>
+        <category android:name="android.intent.category.BROWSABLE"/>
+        <data android:scheme="mailto"/>
+      </intent-filter>
 
-       <data android:mimeType="image/aces"/>
-       <data android:mimeType="image/avci"/>
-       <data android:mimeType="image/avcs"/>
-       <data android:mimeType="image/avif"/>
-       <data android:mimeType="image/bmp"/>
-       <data android:mimeType="image/cgm"/>
-       <data android:mimeType="image/dicom-rle"/>
-       <data android:mimeType="image/dpx"/>
-       <data android:mimeType="image/emf"/>
-       <data android:mimeType="image/example"/>
-       <data android:mimeType="image/fits"/>
-       <data android:mimeType="image/g3fax"/>
-       <data android:mimeType="image/heic"/>
-       <data android:mimeType="image/heic-sequence"/>
-       <data android:mimeType="image/heif"/>
-       <data android:mimeType="image/heif-sequence"/>
-       <data android:mimeType="image/hej2k"/>
-       <data android:mimeType="image/hsj2"/>
-       <data android:mimeType="image/jls"/>
-       <data android:mimeType="image/jp2"/>
-       <data android:mimeType="image/jph"/>
-       <data android:mimeType="image/jphc"/>
-       <data android:mimeType="image/jpm"/>
-       <data android:mimeType="image/jpx"/>
-       <data android:mimeType="image/jxr"/>
-       <data android:mimeType="image/jxrA"/>
-       <data android:mimeType="image/jxrS"/>
-       <data android:mimeType="image/jxs"/>
-       <data android:mimeType="image/jxsc"/>
-       <data android:mimeType="image/jxsi"/>
-       <data android:mimeType="image/jxss"/>
-       <data android:mimeType="image/ktx"/>
-       <data android:mimeType="image/ktx2"/>
-       <data android:mimeType="image/naplps"/>
-       <data android:mimeType="image/png"/>
-       <data android:mimeType="image/prs.btif"/>
-       <data android:mimeType="image/prs.pti"/>
-       <data android:mimeType="image/pwg-raster"/>
-       <data android:mimeType="image/svg+xml"/>
-       <data android:mimeType="image/t38"/>
-       <data android:mimeType="image/tiff"/>
-       <data android:mimeType="image/tiff-fx"/>
-       <data android:mimeType="image/xpm"/>
-       <data android:mimeType="text/*"/>
-        <data android:mimeType="application/*xml"/>
-        <data android:mimeType="application/atom+xml"/>
-        <data android:mimeType="application/dxf"/>
-        <data android:mimeType="application/ecmascript"/>
-        <data android:mimeType="application/javascript"/>
-        <data android:mimeType="application/json"/>
-        <data android:mimeType="application/*log*"/>
-        <data android:mimeType="application/octet-stream"/>
-        <data android:mimeType="application/soap+xm"/>
-        <data android:mimeType="application/x-caramel"/>
-        <data android:mimeType="application/x-klaunch"/>
-        <data android:mimeType="application/x-latex"/>
-        <data android:mimeType="application/x-sh"/>
-        <data android:mimeType="application/x-tcl"/>
-        <data android:mimeType="application/x-tex*"/>
-        <data android:mimeType="application/x-troff*"/>
-        <data android:mimeType="application/xhtml+xml"/>
-        <data android:mimeType="application/xml*"/>
-        <data android:mimeType="application/zip"/>
-        <data android:mimeType="application/x-zip-compressed"/>
+      <intent-filter>
+        <action android:name="android.intent.action.SENDTO"/>
+        <data android:scheme="mailto"/>
+        <category android:name="android.intent.category.DEFAULT"/>
       </intent-filter>
     </activity>
 
diff --git a/java/README b/java/README
index e518e9fbb2f..a909cdd22ef 100644
--- a/java/README
+++ b/java/README
@@ -22,1027 +22,6 @@ Please keep the Java code indented with tabs and formatted 
according
 to the rules for C code in the GNU coding standards.  Always use
 C-style comments.
 
-======================================================================
-
-OVERVIEW OF JAVA
-
-Emacs developers do not know Java, and there is no reason they should
-have to.  Thus, the code in this directory is confined to what is
-strictly necessary to support Emacs, and only uses a subset of Java
-written in a way that is easily understandable to C programmers.
-
-Java is required because the entire Android runtime is based around
-Java, and there is no way to write an Android program which runs
-without Java.
-
-This text exists to prime other Emacs developers, already familar with
-C, on the basic architecture of the Android port, and to teach them
-how to read and write the Java code found in this directory.
-
-Java is an object oriented language with automatic memory management
-compiled down to bytecode, which is then subject to interpretation by
-a Java virtual machine.
-
-What that means, is that:
-
-struct emacs_window
-{
-  int some_fields;
-  int of_emacs_window;
-};
-
-static void
-do_something_with_emacs_window (struct emacs_window *a, int n)
-{
-  a->some_fields = a->of_emacs_window + n;
-}
-
-would be written:
-
-public class EmacsWindow
-{
-  public int someFields;
-  public int ofEmacsWindow;
-
-  public void
-  doSomething (int n)
-  {
-    someFields = ofEmacsWindow + n;
-  }
-}
-
-and instead of doing:
-
-do_something_with_emacs_window (my_window, 1);
-
-you say:
-
-myWindow.doSomething (1);
-
-In addition to functions associated with an object of a given class
-(such as EmacsWindow), Java also has two other kinds of functions.
-
-The first are so-called ``static'' functions (the static means
-something entirely different from what it does in C.)
-
-A static function, while still having to be defined within a class,
-can be called without any object.  Instead of the object, you write
-the name of the Java class within which it is defined. For example,
-the following C code:
-
-int
-multiply_a_with_b_and_then_add_c (int a, int b, int c)
-{
-  return a * b + c;
-}
-
-would be:
-
-public class EmacsSomething
-{
-  public static int
-  multiplyAWithBAndThenAddC (int a, int b, int c)
-  {
-    return a * b + c;
-  }
-};
-
-Then, instead of calling:
-
-int foo;
-
-foo = multiply_a_with_b_then_add_c (1, 2, 3);
-
-you say:
-
-int foo;
-
-foo = EmacsSomething.multiplyAWithBAndThenAddC (1, 2, 3);
-
-In Java, ``static'' does not mean that the function is only used
-within its compilation unit!  Instead, the ``private'' qualifier is
-used to mean more or less the same thing:
-
-static void
-this_procedure_is_only_used_within_this_file (void)
-{
-  do_something ();
-}
-
-becomes
-
-public class EmacsSomething
-{
-  private static void
-  thisProcedureIsOnlyUsedWithinThisClass ()
-  {
-
-  }
-}
-
-the other kind are called ``constructors''.  They are functions that
-must be called to allocate memory to hold a class:
-
-public class EmacsFoo
-{
-  int bar;
-
-  public
-  EmacsFoo (int tokenA, int tokenB)
-  {
-    bar = tokenA + tokenB;
-  }
-}
-
-now, the following statement:
-
-EmacsFoo foo;
-
-foo = new EmacsFoo (1, 2);
-
-becomes more or less equivalent to the following C code:
-
-struct emacs_foo
-{
-  int bar;
-};
-
-struct emacs_foo *
-make_emacs_foo (int token_a, int token_b)
-{
-  struct emacs_foo *foo;
-
-  foo = xmalloc (sizeof *foo);
-  foo->bar = token_a + token_b;
-
-  return foo;
-}
-
-/* ... */
-
-struct emacs_foo *foo;
-
-foo = make_emacs_foo (1, 2);
-
-A class may have any number of constructors, or no constructors at
-all, in which case the compiler inserts an empty constructor.
-
-
-
-Sometimes, you will see Java code that looks like this:
-
-    allFiles = filesDirectory.listFiles (new FileFilter () {
-       @Override
-       public boolean
-       accept (File file)
-       {
-         return (!file.isDirectory ()
-                 && file.getName ().endsWith (".pdmp"));
-       }
-      });
-
-This is Java's version of GCC's nested function extension.  The major
-difference is that the nested function may still be called even after
-it goes out of scope, and always retains a reference to the class and
-local variables around where it was called.
-
-Being an object-oriented language, Java also allows defining that a
-class ``extends'' another class.  The following C code:
-
-struct a
-{
-  long thirty_two;
-};
-
-struct b
-{
-  struct a a;
-  long long sixty_four;
-};
-
-extern void do_something (struct a *);
-
-void
-my_function (struct b *b)
-{
-  do_something (&b->a);
-}
-
-is roughly equivalent to the following Java code, split into two
-files:
-
-  A.java
-
-public class A
-{
-  int thirtyTwo;
-
-  public void
-  doSomething ()
-  {
-    etcEtcEtc ();
-  }
-};
-
-  B.java
-
-public class B extends A
-{
-  long sixty_four;
-
-  public static void
-  myFunction (B b)
-  {
-    b.doSomething ();
-  }
-}
-
-the Java runtime has transformed the call to ``b.doSomething'' to
-``((A) b).doSomething''.
-
-However, Java also allows overriding this behavior, by specifying the
-@Override keyword:
-
-public class B extends A
-{
-  long sixty_four;
-
-  @Override
-  public void
-  doSomething ()
-  {
-    Something.doSomethingTwo ();
-    super.doSomething ();
-  }
-}
-
-now, any call to ``doSomething'' on a ``B'' created using ``new B ()''
-will end up calling ``Something.doSomethingTwo'', before calling back
-to ``A.doSomething''.  This override also applies in reverse; that is
-to say, even if you write:
-
-  ((A) b).doSomething ();
-
-B's version of doSomething will still be called, if ``b'' was created
-using ``new B ()''.
-
-This mechanism is used extensively throughout the Java language and
-Android windowing APIs.
-
-Elsewhere, you will encounter Java code that defines arrays:
-
-public class EmacsFrobinicator
-{
-  public static void
-  emacsFrobinicate (int something)
-  {
-    int[] primesFromSomething;
-
-    primesFromSomething = new int[numberOfPrimes];
-    /* ... */
-  }
-}
-
-Java arrays are similar to C arrays in that they can not grow.  But
-they are very much unlike C arrays in that they are always references
-(as opposed to decaying into pointers in only some situations), and
-contain information about their length.
-
-If another function named ``frobinicate1'' takes an array as an
-argument, then it need not take the length of the array.
-
-Instead, it may simply iterate over the array like so:
-
-int i, k;
-
-for (i = 0; i < array.length; ++i)
-  {
-    k = array[i];
-
-    Whatever.doSomethingWithK (k);
-  }
-
-The syntax used to define arrays is also slightly different.  As
-arrays are always references, there is no way for you to tell the
-runtime to allocate an array of size N in a structure (class.)
-
-Instead, if you need an array of that size, you must declare a field
-with the type of the array, and allocate the array inside the class's
-constructor, like so:
-
-public class EmacsArrayContainer
-{
-  public int[] myArray;
-
-  public
-  EmacsArrayContainer ()
-  {
-    myArray = new array[10];
-  }
-}
-
-while in C, you could just have written:
-
-struct emacs_array_container
-{
-  int my_array[10];
-};
-
-or, possibly even better,
-
-typedef int emacs_array_container[10];
-
-Alas, Java has no equivalent of `typedef'.
-
-Like in C, Java string literals are delimited by double quotes.
-Unlike C, however, strings are not NULL-terminated arrays of
-characters, but a distinct type named ``String''.  They store their
-own length, characters in Java's 16-bit ``char'' type, and are capable
-of holding NULL bytes.
-
-Instead of writing:
-
-wchar_t character;
-extern char *s;
-size_t s;
-
-  for (/* determine n, s in a loop.  */)
-    s += mbstowc (&character, s, n);
-
-or:
-
-const char *byte;
-
-for (byte = my_string; *byte; ++byte)
-  /* do something with *byte.  */;
-
-or perhaps even:
-
-size_t length, i;
-char foo;
-
-length = strlen (my_string);
-
-for (i = 0; i < length; ++i)
-  foo = my_string[i];
-
-you write:
-
-char foo;
-int i;
-
-for (i = 0; i < myString.length (); ++i)
-  foo = myString.charAt (0);
-
-Java also has stricter rules on what can be used as a truth value in a
-conditional.  While in C, any non-zero value is true, Java requires
-that every truth value be of the boolean type ``boolean''.
-
-What this means is that instead of simply writing:
-
-  if (foo || bar)
-
-where foo can either be 1 or 0, and bar can either be NULL or a
-pointer to something, you must explicitly write:
-
-  if (foo != 0 || bar != null)
-
-in Java.
-
-JAVA NATIVE INTERFACE
-
-Java also provides an interface for C code to interface with Java.
-
-C functions exported from a shared library become static Java
-functions within a class, like so:
-
-public class EmacsNative
-{
-  /* Obtain the fingerprint of this build of Emacs.  The fingerprint
-     can be used to determine the dump file name.  */
-  public static native String getFingerprint ();
-
-  /* Set certain parameters before initializing Emacs.
-
-     assetManager must be the asset manager associated with the
-     context that is loading Emacs.  It is saved and remains for the
-     remainder the lifetime of the Emacs process.
-
-     filesDir must be the package's data storage location for the
-     current Android user.
-
-     libDir must be the package's data storage location for native
-     libraries.         It is used as PATH.
-
-     cacheDir must be the package's cache directory.  It is used as
-     the `temporary-file-directory'.
-
-     pixelDensityX and pixelDensityY are the DPI values that will be
-     used by Emacs.
-
-     classPath must be the classpath of this app_process process, or
-     NULL.
-
-     emacsService must be the EmacsService singleton, or NULL. */
-  public static native void setEmacsParams (AssetManager assetManager,
-                                           String filesDir,
-                                           String libDir,
-                                           String cacheDir,
-                                           float pixelDensityX,
-                                           float pixelDensityY,
-                                           String classPath,
-                                           EmacsService emacsService);
-}
-
-Where the corresponding C functions are located in android.c, and
-loaded by the special invocation:
-
-  static
-  {
-    System.loadLibrary ("emacs");
-  };
-
-where ``static'' defines a section of code which will be run upon the
-object (containing class) being loaded.  This is like:
-
-  __attribute__((constructor))
-
-on systems where shared object constructors are supported.
-
-See http://docs.oracle.com/en/java/javase/19/docs/specs/jni/intro.html
-for more details.
-
-
-
-OVERVIEW OF ANDROID
-
-When the Android system starts an application, it does not actually
-call the application's ``main'' function.  It may not even start the
-application's process if one is already running.
-
-Instead, Android is organized around components.  When the user opens
-the ``Emacs'' icon, the Android system looks up and starts the
-component associated with the ``Emacs'' icon.  In this case, the
-component is called an activity, and is declared in
-the AndroidManifest.xml in this directory:
-
-    <activity android:name="org.gnu.emacs.EmacsActivity"
-             android:launchMode="singleTop"
-             android:windowSoftInputMode="adjustResize"
-             android:exported="true"
-             
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden">
-      <intent-filter>
-       <action android:name="android.intent.action.MAIN" />
-       <category android:name="android.intent.category.DEFAULT" />
-       <category android:name="android.intent.category.LAUNCHER" />
-      </intent-filter>
-    </activity>
-
-This tells Android to start the activity defined in ``EmacsActivity''
-(defined in org/gnu/emacs/EmacsActivity.java), a class extending the
-Android class ``Activity''.
-
-To do so, the Android system creates an instance of ``EmacsActivity''
-and the window system window associated with it, and eventually calls:
-
-  Activity activity;
-
-  activity.onCreate (...);
-
-But which ``onCreate'' is really called?
-It is actually the ``onCreate'' defined in EmacsActivity.java, as
-it overrides the ``onCreate'' defined in Android's own Activity class:
-
-  @Override
-  public void
-  onCreate (Bundle savedInstanceState)
-  {
-    FrameLayout.LayoutParams params;
-    Intent intent;
-
-Then, this is what happens step-by-step within the ``onCreate''
-function:
-
-    /* See if Emacs should be started with -Q. */
-    intent = getIntent ();
-    EmacsService.needDashQ
-      = intent.getBooleanExtra ("org.gnu.emacs.START_DASH_Q",
-                               false);
-
-Here, Emacs obtains the intent (a request to start a component) which
-was used to start Emacs, and sets a special flag if it contains a
-request for Emacs to start with the ``-Q'' command-line argument.
-
-    /* Set the theme to one without a title bar.  */
-
-    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
-      setTheme (android.R.style.Theme_DeviceDefault_NoActionBar);
-    else
-      setTheme (android.R.style.Theme_NoTitleBar);
-
-Next, Emacs sets an appropriate theme for the activity's associated
-window decorations.
-
-    params = new FrameLayout.LayoutParams (LayoutParams.MATCH_PARENT,
-                                          LayoutParams.MATCH_PARENT);
-
-    /* Make the frame layout.  */
-    layout = new FrameLayout (this);
-    layout.setLayoutParams (params);
-
-    /* Set it as the content view.  */
-    setContentView (layout);
-
-Then, Emacs creates a ``FrameLayout'', a widget that holds a single
-other widget, and makes it the activity's ``content view''.
-
-The activity itself is a ``FrameLayout'', so the ``layout parameters''
-here apply to the FrameLayout itself, and not its children.
-
-    /* Maybe start the Emacs service if necessary.  */
-    EmacsService.startEmacsService (this);
-
-And after that, Emacs calls the static function ``startEmacsService'',
-defined in the class ``EmacsService''. This starts the Emacs service
-component if necessary.
-
-    /* Add this activity to the list of available activities.  */
-    EmacsWindowAttachmentManager.MANAGER.registerWindowConsumer (this);
-
-    super.onCreate (savedInstanceState);
-
-Finally, Emacs registers that this activity is now ready to receive
-top-level frames (windows) created from Lisp.
-
-Activities come and go, but Emacs has to stay running in the mean
-time.  Thus, Emacs also defines a ``service'', which is a long-running
-component that the Android system allows to run in the background.
-
-Let us go back and review the definition of ``startEmacsService'':
-
-  public static void
-  startEmacsService (Context context)
-  {
-    if (EmacsService.SERVICE == null)
-      {
-       if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
-         /* Start the Emacs service now.  */
-         context.startService (new Intent (context,
-                                           EmacsService.class));
-       else
-         /* Display the permanant notification and start Emacs as a
-            foreground service.  */
-         context.startForegroundService (new Intent (context,
-                                                     EmacsService.class));
-      }
-  }
-
-If ``EmacsService.SERVICE'' does not yet exist, what this does is to
-tell the ``context'' (the equivalent of an Xlib Display *) to start a
-service defined by the class ``EmacsService''. Eventually, this
-results in ``EmacsService.onCreate'' being called:
-
-  @Override
-  public void
-  onCreate ()
-  {
-    AssetManager manager;
-    Context app_context;
-    String filesDir, libDir, cacheDir, classPath;
-    double pixelDensityX;
-    double pixelDensityY;
-
-Here is what this function does, step-by-step:
-
-    SERVICE = this;
-
-First, it sets the special static variable ``SERVICE'' to ``this'',
-which is a pointer to the ``EmacsService' object that was created.
-
-    handler = new Handler (Looper.getMainLooper ());
-
-Next, it creates a ``Handler'' object for the ``main looper''.
-This is a helper structure which allows executing code on the Android
-user interface thread.
-
-    manager = getAssets ();
-    app_context = getApplicationContext ();
-    metrics = getResources ().getDisplayMetrics ();
-    pixelDensityX = metrics.xdpi;
-    pixelDensityY = metrics.ydpi;
-
-Finally, it obtains:
-
-  - the asset manager, which is used to retrieve assets packaged
-    into the Emacs application package.
-
-  - the application context, used to obtain application specific
-    information.
-
-  - the display metrics, and from them, the X and Y densities in dots
-    per inch.
-
-Then, inside a ``try'' block:
-
-    try
-      {
-       /* Configure Emacs with the asset manager and other necessary
-          parameters.  */
-       filesDir = app_context.getFilesDir ().getCanonicalPath ();
-       libDir = getLibraryDirectory ();
-       cacheDir = app_context.getCacheDir ().getCanonicalPath ();
-
-It obtains the names of the Emacs home, shared library, and temporary
-file directories.
-
-       /* Now provide this application's apk file, so a recursive
-          invocation of app_process (through android-emacs) can
-          find EmacsNoninteractive.  */
-       classPath = getApkFile ();
-
-The name of the Emacs application package.
-
-       Log.d (TAG, "Initializing Emacs, where filesDir = " + filesDir
-              + ", libDir = " + libDir + ", and classPath = " + classPath);
-
-Prints a debug message to the Android system log with this
-information.
-
-       EmacsNative.setEmacsParams (manager, filesDir, libDir,
-                                   cacheDir, (float) pixelDensityX,
-                                   (float) pixelDensityY,
-                                   classPath, this);
-
-And calls the native function ``setEmacsParams'' (defined in
-android.c) to configure Emacs with this information.
-
-       /* Start the thread that runs Emacs.  */
-       thread = new EmacsThread (this, needDashQ);
-       thread.start ();
-
-Then, it allocates an ``EmacsThread'' object, and starts that thread.
-Inside that thread is where Emacs's C code runs.
-
-      }
-    catch (IOException exception)
-      {
-       EmacsNative.emacsAbort ();
-       return;
-
-And here is the purpose of the ``try'' block.  Functions related to
-file names in Java will signal errors of various types upon failure.
-
-This ``catch'' block means that the Java virtual machine will abort
-execution of the contents of the ``try'' block as soon as an error of
-type ``IOException'' is encountered, and begin executing the contents
-of the ``catch'' block.
-
-Any failure of that type here is a crash, and
-``EmacsNative.emacsAbort'' is called to quickly abort the process to
-get a useful backtrace.
-      }
-  }
-
-Now, let us look at the definition of the class ``EmacsThread'', found
-in org/gnu/emacs/EmacsThread.java:
-
-public class EmacsThread extends Thread
-{
-  /* Whether or not Emacs should be started -Q.         */
-  private boolean startDashQ;
-
-  public
-  EmacsThread (EmacsService service, boolean startDashQ)
-  {
-    super ("Emacs main thread");
-    this.startDashQ = startDashQ;
-  }
-
-  @Override
-  public void
-  run ()
-  {
-    String args[];
-
-    if (!startDashQ)
-      args = new String[] { "libandroid-emacs.so", };
-    else
-      args = new String[] { "libandroid-emacs.so", "-Q", };
-
-    /* Run the native code now.         */
-    EmacsNative.initEmacs (args, EmacsApplication.dumpFileName);
-  }
-};
-
-The class itself defines a single field, ``startDashQ'', a constructor
-with an unused argument of the type ``EmacsService'' (which is useful
-while debugging) and a flag ``startDashQ'', and a single function
-``run'', overriding the same function in the class ``Thread''.
-
-When ``thread.start'' is called, the Java virtual machine creates a
-new thread, and then calls the function ``run'' within that thread.
-
-This function then computes a suitable argument vector, and calls
-``EmacsNative.initEmacs'' (defined in android.c), which then calls a
-modified version of the regular Emacs ``main'' function.
-
-At that point, Emacs initialization proceeds as usual:
-Vinitial_window_system is set, loadup.el calls `normal-top-level',
-which calls `command-line', and finally
-`window-system-initialization', which initializes the `android'
-terminal interface as usual.
-
-What happens here is the same as on other platforms.  Now, here is
-what happens when the initial frame is created: Fx_create_frame calls
-`android_create_frame_window' to create a top level window:
-
-static void
-android_create_frame_window (struct frame *f)
-{
-  struct android_set_window_attributes attributes;
-  enum android_window_value_mask attribute_mask;
-
-  attributes.background_pixel = FRAME_BACKGROUND_PIXEL (f);
-  attribute_mask = ANDROID_CW_BACK_PIXEL;
-
-  block_input ();
-  FRAME_ANDROID_WINDOW (f)
-    = android_create_window (FRAME_DISPLAY_INFO (f)->root_window,
-                            f->left_pos,
-                            f->top_pos,
-                            FRAME_PIXEL_WIDTH (f),
-                            FRAME_PIXEL_HEIGHT (f),
-                            attribute_mask, &attributes);
-  unblock_input ();
-}
-
-This calls the function `android_create_window' with some arguments
-whose meanings are identical to the arguments to `XCreateWindow'.
-
-Here is the definition of `android_create_window', in android.c:
-
-android_window
-android_create_window (android_window parent, int x, int y,
-                      int width, int height,
-                      enum android_window_value_mask value_mask,
-                      struct android_set_window_attributes *attrs)
-{
-  static jclass class;
-  static jmethodID constructor;
-  jobject object, parent_object, old;
-  android_window window;
-  android_handle prev_max_handle;
-  bool override_redirect;
-
-What does it do? First, some context:
-
-At any time, there can be at most 65535 Java objects referred to by
-the rest of Emacs through the Java native interface.  Each such object
-is assigned a ``handle'' (similar to an XID on X) and given a unique
-type.  The function `android_resolve_handle' returns the JNI `jobject'
-associated with a given handle.
-
-  parent_object = android_resolve_handle (parent, ANDROID_HANDLE_WINDOW);
-
-Here, it is being used to look up the `jobject' associated with the
-`parent' handle.
-
-  prev_max_handle = max_handle;
-  window = android_alloc_id ();
-
-Next, `max_handle' is saved, and a new handle is allocated for
-`window'.
-
-  if (!window)
-    error ("Out of window handles!");
-
-An error is signalled if Emacs runs out of available handles.
-
-  if (!class)
-    {
-      class = (*android_java_env)->FindClass (android_java_env,
-                                             "org/gnu/emacs/EmacsWindow");
-      assert (class != NULL);
-
-Then, if this initialization has not yet been completed, Emacs
-proceeds to find the Java class named ``EmacsWindow''.
-
-      constructor
-       = (*android_java_env)->GetMethodID (android_java_env, class, "<init>",
-                                           "(SLorg/gnu/emacs/EmacsWindow;"
-                                           "IIIIZ)V");
-      assert (constructor != NULL);
-
-And it tries to look up the constructor, which should take seven
-arguments:
-
-  S                                    - a short.  (the handle ID)
-  Lorg/gnu/Emacs/EmacsWindow;          - an instance of the EmacsWindow
-                                         class.  (the parent)
-  IIII                                 - four ints.  (the window geometry.)
-  Z                                    - a boolean.  (whether or not the
-                                         window is override-redirect; see
-                                         XChangeWindowAttributes.)
-
-      old = class;
-      class = (*android_java_env)->NewGlobalRef (android_java_env, class);
-      (*android_java_env)->ExceptionClear (android_java_env);
-      ANDROID_DELETE_LOCAL_REF (old);
-
-Next, it saves a global reference to the class and deletes the local
-reference.  Global references will never be deallocated by the Java
-virtual machine as long as they still exist.
-
-      if (!class)
-       memory_full (0);
-    }
-
-  /* N.B. that ANDROID_CW_OVERRIDE_REDIRECT can only be set at window
-     creation time.  */
-  override_redirect = ((value_mask
-                       & ANDROID_CW_OVERRIDE_REDIRECT)
-                      && attrs->override_redirect);
-
-  object = (*android_java_env)->NewObject (android_java_env, class,
-                                          constructor, (jshort) window,
-                                          parent_object, (jint) x, (jint) y,
-                                          (jint) width, (jint) height,
-                                          (jboolean) override_redirect);
-
-Then, it creates an instance of the ``EmacsWindow'' class with the
-appropriate arguments and previously determined constructor.
-
-  if (!object)
-    {
-      (*android_java_env)->ExceptionClear (android_java_env);
-
-      max_handle = prev_max_handle;
-      memory_full (0);
-
-If creating the object fails, Emacs clears the ``pending exception''
-and signals that it is out of memory.
-    }
-
-  android_handles[window].type = ANDROID_HANDLE_WINDOW;
-  android_handles[window].handle
-    = (*android_java_env)->NewGlobalRef (android_java_env,
-                                        object);
-  (*android_java_env)->ExceptionClear (android_java_env);
-  ANDROID_DELETE_LOCAL_REF (object);
-
-Otherwise, it associates a new global reference to the object with the
-handle, and deletes the local reference returned from the JNI
-NewObject function.
-
-  if (!android_handles[window].handle)
-    memory_full (0);
-
-If allocating the global reference fails, Emacs signals that it is out
-of memory.
-
-  android_change_window_attributes (window, value_mask, attrs);
-  return window;
-
-Otherwise, it applies the specified window attributes and returns the
-handle of the new window.
-}
-
-
-
-DRAWABLES, CURSORS AND HANDLES
-
-Each widget created by Emacs corresponds to a single ``window'', which
-has its own backing store.  This arrangement is quite similar to X.
-
-C code does not directly refer to the EmacsView widgets that implement
-the UI logic behind windows.  Instead, its handles refer to
-EmacsWindow structures, which contain the state necessary to interact
-with the widgets in an orderly and synchronized manner.
-
-Like X, both pixmaps and windows are drawable resources, and the same
-graphics operations can be applied to both.  Thus, a separate
-EmacsPixmap structure is used to wrap around Android Bitmap resources,
-and the Java-level graphics operation functions are capable of
-operating on them both.
-
-Finally, graphics contexts are maintained on both the C and Java
-levels; the C state recorded in `struct android_gc' is kept in sync
-with the Java state in the GContext handle's corresponding EmacsGC
-structure, and cursors are used through handles that refer to
-EmacsCursor structures that hold system PointerIcons.
-
-In all cases, the interfaces provided are identical to X.
-
-
-
-EVENT LOOP
-
-In a typical Android application, the event loop is managed by the
-operating system, and callbacks (implemented through overriding
-separate functions in widgets) are run by the event loop wherever
-necessary.  The thread which runs the event loop is also the only
-thread capable of creating and manipulating widgets and activities,
-and is referred to as the ``UI thread''.
-
-These callbacks are used by Emacs to write representations of X-like
-events to a separate event queue, which are then read from Emacs's own
-event loop running in a separate thread.  This is accomplished through
-replacing `select' by a function which waits for the event queue to be
-occupied, in addition to any file descriptors that `select' would
-normally wait for.
-
-Conversely, Emacs's event loop sometimes needs to send events to the
-UI thread.  These events are implemented as tiny fragments of code,
-which are run as they are received by the main thread.
-
-A typical example is `displayToast', which is implemented in
-EmacsService.java:
-
-  public void
-  displayToast (final String string)
-  {
-    runOnUiThread (new Runnable () {
-       @Override
-       public void
-       run ()
-       {
-         Toast toast;
-
-         toast = Toast.makeText (getApplicationContext (),
-                                 string, Toast.LENGTH_SHORT);
-         toast.show ();
-       }
-      });
-  }
-
-Here, the variable `string' is used by a nested function.  This nested
-function contains a copy of that variable, and is run on the main
-thread using the function `runOnUiThread', in order to display a short
-status message on the display.
-
-When Emacs needs to wait for the nested function to finish, it uses a
-mechanism implemented in `syncRunnable'.  This mechanism first calls a
-deadlock avoidance mechanism, then runs a nested function on the UI
-thread, which is expected to signal itself as a condition variable
-upon completion.  It is typically used to allocate resources that can
-only be allocated from the UI thread, or to obtain non-thread-safe
-information.  The following function is an example; it returns a new
-EmacsView widget corresponding to the provided window:
-
-  public EmacsView
-  getEmacsView (final EmacsWindow window, final int visibility,
-               final boolean isFocusedByDefault)
-  {
-    Runnable runnable;
-    final EmacsHolder<EmacsView> view;
-
-    view = new EmacsHolder<EmacsView> ();
-
-    runnable = new Runnable () {
-       public void
-       run ()
-       {
-         synchronized (this)
-           {
-             view.thing = new EmacsView (window);
-             view.thing.setVisibility (visibility);
-
-             /* The following function is only present on Android 26
-                or later.  */
-             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
-               view.thing.setFocusedByDefault (isFocusedByDefault);
-
-             notify ();
-           }
-       }
-      };
-
-    syncRunnable (runnable);
-    return view.thing;
-  }
-
-As no value can be directly returned from the nested function, a
-separate container object is used to hold the result after the
-function finishes execution.  Note the type name inside the angle
-brackets: this type is substituted into the class definition as it is
-used; a definition such as:
-
-public class Foo<T>
-{
-  T bar;
-};
-
-can not be used alone:
-
-  Foo holder; /* Error! */
-
-but must have a type specified:
-
-  Foo<Object> holder;
-
-in which case the effective definition is:
-
-public class Foo
-{
-  Object bar;
-};
+Refer to the file `admin/notes/java' in the toplevel directory of the
+Emacs distribution or repository for specifics regarding writing Java
+code for Emacs and the organization of the Android port.
diff --git a/java/org/gnu/emacs/EmacsContextMenu.java 
b/java/org/gnu/emacs/EmacsContextMenu.java
index c5b87aa804a..c415ba59c79 100644
--- a/java/org/gnu/emacs/EmacsContextMenu.java
+++ b/java/org/gnu/emacs/EmacsContextMenu.java
@@ -347,6 +347,13 @@ public final class EmacsContextMenu
     Runnable runnable;
     final EmacsHolder<Boolean> rc;
 
+    /* Android will permanently cease to display any popup menus at
+       all if the list of menu items is empty.  Prevent this by
+       promptly returning if there are no menu items.  */
+
+    if (menuItems.isEmpty ())
+      return false;
+
     rc = new EmacsHolder<Boolean> ();
     rc.thing = false;
 
diff --git a/java/org/gnu/emacs/EmacsDesktopNotification.java 
b/java/org/gnu/emacs/EmacsDesktopNotification.java
index 121d8f481a2..4a6bbf7a606 100644
--- a/java/org/gnu/emacs/EmacsDesktopNotification.java
+++ b/java/org/gnu/emacs/EmacsDesktopNotification.java
@@ -96,6 +96,7 @@ public final class EmacsDesktopNotification
     RemoteViews contentView;
     Intent intent;
     PendingIntent pending;
+    int priority;
 
     tem = context.getSystemService (Context.NOTIFICATION_SERVICE);
     manager = (NotificationManager) tem;
@@ -116,11 +117,37 @@ public final class EmacsDesktopNotification
                        .build ());
       }
     else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
-      notification = (new Notification.Builder (context)
-                     .setContentTitle (title)
-                     .setContentText (content)
-                     .setSmallIcon (icon)
-                     .build ());
+      {
+       /* Android 7.1 and earlier don't segregate notifications into
+          distinct categories, but permit an importance to be
+          assigned to each individual notification.  */
+
+       switch (importance)
+         {
+         case 2: /* IMPORTANCE_LOW */
+         default:
+           priority = Notification.PRIORITY_LOW;
+           break;
+
+         case 3: /* IMPORTANCE_DEFAULT */
+           priority = Notification.PRIORITY_DEFAULT;
+           break;
+
+         case 4: /* IMPORTANCE_HIGH */
+           priority = Notification.PRIORITY_HIGH;
+           break;
+         }
+
+       notification = (new Notification.Builder (context)
+                       .setContentTitle (title)
+                       .setContentText (content)
+                       .setSmallIcon (icon)
+                       .setPriority (priority)
+                       .build ());
+
+       if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN)
+         notification.priority = priority;
+      }
     else
       {
        notification = new Notification ();
diff --git a/java/org/gnu/emacs/EmacsDialog.java 
b/java/org/gnu/emacs/EmacsDialog.java
index af3bf538410..bad1ddde227 100644
--- a/java/org/gnu/emacs/EmacsDialog.java
+++ b/java/org/gnu/emacs/EmacsDialog.java
@@ -41,6 +41,7 @@ import android.widget.Button;
 import android.widget.LinearLayout;
 import android.widget.FrameLayout;
 
+import android.view.ContextThemeWrapper;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
@@ -158,6 +159,13 @@ public final class EmacsDialog implements 
DialogInterface.OnDismissListener
     TypedArray attributes;
     Window window;
 
+    /* Wrap the context within a style wrapper.  Any dialog properties
+       tied to EmacsStyle (such as those applied by the system ``dark
+       theme'') will thus affect the dialog irrespective of whether
+       CONTEXT is an activity or the service.  */
+
+    context = new ContextThemeWrapper (context, R.style.EmacsStyle);
+
     size = buttons.size ();
     styleId = -1;
 
diff --git a/java/org/gnu/emacs/EmacsDrawLine.java 
b/java/org/gnu/emacs/EmacsDrawLine.java
index d367ccff9c4..be4da54c075 100644
--- a/java/org/gnu/emacs/EmacsDrawLine.java
+++ b/java/org/gnu/emacs/EmacsDrawLine.java
@@ -29,7 +29,6 @@ public final class EmacsDrawLine
   perform (EmacsDrawable drawable, EmacsGC gc,
           int x, int y, int x2, int y2)
   {
-    Rect rect;
     Canvas canvas;
     Paint paint;
     int x0, x1, y0, y1;
@@ -48,7 +47,6 @@ public final class EmacsDrawLine
     /* And the clip rectangle.  */
 
     paint = gc.gcPaint;
-    rect = new Rect (x0, y0, x1, y1);
     canvas = drawable.lockCanvas (gc);
 
     if (canvas == null)
@@ -74,6 +72,6 @@ public final class EmacsDrawLine
 
     /* DrawLine with clip mask not implemented; it is not used by
        Emacs.  */
-    drawable.damageRect (rect);
+    drawable.damageRect (x0, y0, x1, y1);
   }
 }
diff --git a/java/org/gnu/emacs/EmacsDrawRectangle.java 
b/java/org/gnu/emacs/EmacsDrawRectangle.java
index e1261b4a2d2..ee9110daaaf 100644
--- a/java/org/gnu/emacs/EmacsDrawRectangle.java
+++ b/java/org/gnu/emacs/EmacsDrawRectangle.java
@@ -114,7 +114,6 @@ public final class EmacsDrawRectangle
        maskBitmap.recycle ();
       }
 
-    drawable.damageRect (new Rect (x, y, x + width + 1,
-                                  y + height + 1));
+    drawable.damageRect (x, y, x + width + 1, y + height + 1);
   }
 }
diff --git a/java/org/gnu/emacs/EmacsDrawable.java 
b/java/org/gnu/emacs/EmacsDrawable.java
index f2f8885e976..3ed72a836e5 100644
--- a/java/org/gnu/emacs/EmacsDrawable.java
+++ b/java/org/gnu/emacs/EmacsDrawable.java
@@ -27,6 +27,7 @@ public interface EmacsDrawable
 {
   public Canvas lockCanvas (EmacsGC gc);
   public void damageRect (Rect damageRect);
+  public void damageRect (int left, int top, int right, int bottom);
   public Bitmap getBitmap ();
   public boolean isDestroyed ();
 };
diff --git a/java/org/gnu/emacs/EmacsFontDriver.java 
b/java/org/gnu/emacs/EmacsFontDriver.java
index ff52899a897..798c3cfb032 100644
--- a/java/org/gnu/emacs/EmacsFontDriver.java
+++ b/java/org/gnu/emacs/EmacsFontDriver.java
@@ -63,6 +63,11 @@ public abstract class EmacsFontDriver
   public static final int MONO         = 100;
   public static final int CHARCELL     = 110;
 
+  /* Special glyph codes.  */
+  public static final int FONT_INVALID_CODE = 0xFFFFFFFF;
+
+
+
   public static class FontSpec
   {
     /* The fields below mean the same as they do in enum
@@ -148,15 +153,17 @@ public abstract class EmacsFontDriver
     }
   };
 
+
+
   /* These mean the same as they do in struct font_driver.  */
   public abstract FontEntity[] list (FontSpec fontSpec);
   public abstract FontEntity match (FontSpec fontSpec);
   public abstract String[] listFamilies ();
   public abstract FontObject openFont (FontEntity fontEntity, int pixelSize);
-  public abstract int hasChar (FontSpec font, char charCode);
+  public abstract int hasChar (FontSpec font, int charCode);
   public abstract void textExtents (FontObject font, int code[],
                                    FontMetrics fontMetrics);
-  public abstract int encodeChar (FontObject fontObject, char charCode);
+  public abstract int encodeChar (FontObject fontObject, int charCode);
   public abstract int draw (FontObject fontObject, EmacsGC gc,
                            EmacsDrawable drawable, int[] chars,
                            int x, int y, int backgroundWidth,
diff --git a/java/org/gnu/emacs/EmacsNative.java 
b/java/org/gnu/emacs/EmacsNative.java
index fae0ba98f86..a4b45aafbc1 100644
--- a/java/org/gnu/emacs/EmacsNative.java
+++ b/java/org/gnu/emacs/EmacsNative.java
@@ -196,6 +196,10 @@ public final class EmacsNative
      KEYCODE_VOLUME_MUTE should be forwarded to Emacs.  */
   public static native boolean shouldForwardMultimediaButtons ();
 
+  /* Return whether KEYCODE_SPACE combined with META_CTRL_MASK should
+     be prevented from reaching the system input method.  */
+  public static native boolean shouldForwardCtrlSpace ();
+
   /* Initialize the current thread, by blocking signals that do not
      interest it.  */
   public static native void setupSystemThread ();
diff --git a/java/org/gnu/emacs/EmacsOpenActivity.java 
b/java/org/gnu/emacs/EmacsOpenActivity.java
index ea503ebd120..202b3c8c5dc 100644
--- a/java/org/gnu/emacs/EmacsOpenActivity.java
+++ b/java/org/gnu/emacs/EmacsOpenActivity.java
@@ -213,12 +213,13 @@ public final class EmacsOpenActivity extends Activity
     dialog.show ();
   }
 
-  /* Check that the specified FILE is readable.  If Android 4.4 or
-     later is being used, return URI formatted into a `/content/' file
-     name.
+  /* Check that the specified FILE is non-NULL and readable.
 
      If it is not, then copy the file in FD to a location in the
-     system cache directory and return the name of that file.  */
+     system cache directory and return the name of that file.
+
+     Alternatively, return URI formatted into a `/content/' file name
+     if the system runs Android 4.4 or later.  */
 
   private String
   checkReadableOrCopy (String file, ParcelFileDescriptor fd,
@@ -232,10 +233,19 @@ public final class EmacsOpenActivity extends Activity
     int read;
     String content;
 
-    inFile = new File (file);
+    if (file != null)
+      {
+       inFile = new File (file);
+
+       if (inFile.canRead ())
+         return file;
 
-    if (inFile.canRead ())
-      return file;
+       content = inFile.getName ();
+      }
+    else
+      /* content is the name of this content file if the next branch
+        is not taken.  */
+      content = uri.getLastPathSegment ();
 
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
       {
@@ -243,8 +253,14 @@ public final class EmacsOpenActivity extends Activity
        return content;
       }
 
+    /* The file is unnamed if content is NULL.  Generate a unique
+       name with the current time as a reference.  */
+
+    if (content == null)
+      content = "content." + System.currentTimeMillis () / 1000;
+
     /* inFile is now the file being written to.  */
-    inFile = new File (getCacheDir (), inFile.getName ());
+    inFile = new File (getCacheDir (), content);
     buffer = new byte[4098];
 
     /* Initialize both streams to NULL.  */
@@ -326,8 +342,14 @@ public final class EmacsOpenActivity extends Activity
       });
   }
 
+  /* Start `emacsclient' with the provided list of ARGUMENTS, after
+     ARGUMENTS[0] is replaced with the name of the emacsclient binary.
+
+     Create a new thread to await its completion, subsequently
+     reporting any errors that arise to the user.  */
+
   public void
-  startEmacsClient (String fileName)
+  startEmacsClient (String[] arguments)
   {
     String libDir;
     ProcessBuilder builder;
@@ -336,23 +358,10 @@ public final class EmacsOpenActivity extends Activity
     File file;
     Intent intent;
 
-    /* If the Emacs service is not running, then start Emacs and make
-       it open this file.  */
-
-    if (EmacsService.SERVICE == null)
-      {
-       fileToOpen = fileName;
-       intent = new Intent (EmacsOpenActivity.this,
-                            EmacsActivity.class);
-       finish ();
-       startActivity (intent);
-       return;
-      }
-
     libDir = EmacsService.getLibraryDirectory (this);
-    builder = new ProcessBuilder (libDir + "/libemacsclient.so",
-                                 fileName, "--reuse-frame",
-                                 "--timeout=10", "--no-wait");
+    arguments[0] = libDir + "/libemacsclient.so";
+
+    builder = new ProcessBuilder (arguments);
 
     /* Redirection is unfortunately not possible in Android 7 and
        earlier.  */
@@ -397,7 +406,7 @@ public final class EmacsOpenActivity extends Activity
     ContentResolver resolver;
     ParcelFileDescriptor fd;
     byte[] names;
-    String errorBlurb;
+    String errorBlurb, scheme;
 
     super.onCreate (savedInstanceState);
 
@@ -415,7 +424,8 @@ public final class EmacsOpenActivity extends Activity
 
     if (action.equals ("android.intent.action.VIEW")
        || action.equals ("android.intent.action.EDIT")
-       || action.equals ("android.intent.action.PICK"))
+       || action.equals ("android.intent.action.PICK")
+       || action.equals ("android.intent.action.SENDTO"))
       {
        /* Obtain the URI of the action.  */
        uri = intent.getData ();
@@ -426,15 +436,41 @@ public final class EmacsOpenActivity extends Activity
            return;
          }
 
+       scheme = uri.getScheme ();
+
+       /* It is possible for scheme to be NULL, under Android 2.3 at
+          least.  */
+
+       if (scheme == null)
+         return;
+
+       /* If URL is a mailto URI, call `message-mailto' much the same
+          way emacsclient-mail.desktop does.  */
+
+       if (scheme.equals ("mailto"))
+         {
+           /* Escape the special characters $ and " before enclosing
+              the string within the `message-mailto' wrapper.  */
+           fileName = uri.toString ();
+           fileName.replace ("\"", "\\\"").replace ("$", "\\$");
+           fileName = "(message-mailto \"" + fileName + "\")";
+
+           /* Execute emacsclient in order to execute this code.  */
+           currentActivity = this;
+           startEmacsClient (new String[] { "--timeout=10", "--no-wait",
+                                            "--eval", fileName, });
+           return;
+         }
+
        /* Now, try to get the file name.  */
 
-       if (uri.getScheme ().equals ("file"))
+       if (scheme.equals ("file"))
          fileName = uri.getPath ();
        else
          {
            fileName = null;
 
-           if (uri.getScheme ().equals ("content"))
+           if (scheme.equals ("content"))
              {
                /* This is one of the annoying Android ``content''
                   URIs.  Most of the time, there is actually an
@@ -464,6 +500,14 @@ public final class EmacsOpenActivity extends Activity
                  {
                    /* Do nothing.  */
                  }
+               catch (SecurityException exception)
+                 {
+                   /* This means Emacs lacks the rights to open this
+                      file.  Display the error message and exit.  */
+                   displayFailureDialog ("Error openining file",
+                                         exception.toString ());
+                   return;
+                 }
 
                if (fd != null)
                  {
@@ -477,6 +521,10 @@ public final class EmacsOpenActivity extends Activity
                      }
                  }
              }
+           else if (scheme.equals ("org-protocol"))
+             /* URL is an org-protocol:// link, which is meant to be
+                directly relayed to emacsclient.  */
+             fileName = uri.toString ();
 
            if (fileName == null)
              {
@@ -488,11 +536,25 @@ public final class EmacsOpenActivity extends Activity
              }
          }
 
+       /* If the Emacs service is not running, then start Emacs and make
+          it open this file.  */
+
+       if (EmacsService.SERVICE == null)
+         {
+           fileToOpen = fileName;
+           intent = new Intent (EmacsOpenActivity.this,
+                                EmacsActivity.class);
+           finish ();
+           startActivity (intent);
+           return;
+         }
+
        /* And start emacsclient.  Set `currentActivity' to this now.
           Presumably, it will shortly become capable of displaying
           dialogs.  */
        currentActivity = this;
-       startEmacsClient (fileName);
+       startEmacsClient (new String[] { "--timeout=10", "--no-wait",
+                                        "--reuse-frame", fileName, });
       }
     else
       finish ();
diff --git a/java/org/gnu/emacs/EmacsPixmap.java 
b/java/org/gnu/emacs/EmacsPixmap.java
index eb011bc5e65..fa6e61c15a5 100644
--- a/java/org/gnu/emacs/EmacsPixmap.java
+++ b/java/org/gnu/emacs/EmacsPixmap.java
@@ -34,7 +34,7 @@ public final class EmacsPixmap extends EmacsHandleObject
 {
   /* The depth of the bitmap.  This is not actually used, just defined
      in order to be consistent with X.  */
-  public int depth, width, height;
+  public final int depth, width, height;
 
   /* The bitmap itself.  */
   public Bitmap bitmap;
@@ -44,41 +44,12 @@ public final class EmacsPixmap extends EmacsHandleObject
 
   /* Whether or not GC should be explicitly triggered upon
      release.  */
-  private boolean needCollect;
+  private final boolean needCollect;
 
   /* ID used to determine whether or not the GC clip rects
      changed.  */
   private long gcClipRectID;
 
-  public
-  EmacsPixmap (short handle, int colors[], int width,
-              int height, int depth)
-  {
-    super (handle);
-
-    if (depth != 1 && depth != 24)
-      throw new IllegalArgumentException ("Invalid depth specified"
-                                         + " for pixmap: " + depth);
-
-    switch (depth)
-      {
-      case 1:
-       bitmap = Bitmap.createBitmap (colors, width, height,
-                                     Bitmap.Config.ALPHA_8);
-       break;
-
-      case 24:
-       bitmap = Bitmap.createBitmap (colors, width, height,
-                                     Bitmap.Config.ARGB_8888);
-       bitmap.setHasAlpha (false);
-       break;
-      }
-
-    this.width = width;
-    this.height = height;
-    this.depth = depth;
-  }
-
   public
   EmacsPixmap (short handle, int width, int height, int depth)
   {
@@ -171,6 +142,13 @@ public final class EmacsPixmap extends EmacsHandleObject
 
   }
 
+  @Override
+  public void
+  damageRect (int left, int top, int right, int bottom)
+  {
+
+  }
+
   @Override
   public Bitmap
   getBitmap ()
diff --git a/java/org/gnu/emacs/EmacsSdk11Clipboard.java 
b/java/org/gnu/emacs/EmacsSdk11Clipboard.java
index b34753922b8..b8a43496b6d 100644
--- a/java/org/gnu/emacs/EmacsSdk11Clipboard.java
+++ b/java/org/gnu/emacs/EmacsSdk11Clipboard.java
@@ -209,7 +209,7 @@ public final class EmacsSdk11Clipboard extends 
EmacsClipboard
 
      Value is normally an array of three longs: the file descriptor,
      the start offset of the data, and its length; length may be
-     AssetFileDescriptor.UNKOWN_LENGTH, meaning that the data extends
+     AssetFileDescriptor.UNKNOWN_LENGTH, meaning that the data extends
      from that offset to the end of the file.
 
      Do not use this function to open text targets; use `getClipboard'
diff --git a/java/org/gnu/emacs/EmacsSdk23FontDriver.java 
b/java/org/gnu/emacs/EmacsSdk23FontDriver.java
index aaba8dbd166..0752d8064ac 100644
--- a/java/org/gnu/emacs/EmacsSdk23FontDriver.java
+++ b/java/org/gnu/emacs/EmacsSdk23FontDriver.java
@@ -96,7 +96,7 @@ public final class EmacsSdk23FontDriver extends 
EmacsSdk7FontDriver
 
   @Override
   public int
-  hasChar (FontSpec font, char charCode)
+  hasChar (FontSpec font, int charCode)
   {
     Sdk7FontObject fontObject;
     Paint paint;
@@ -109,6 +109,12 @@ public final class EmacsSdk23FontDriver extends 
EmacsSdk7FontDriver
     else
       paint = ((Sdk7FontEntity) font).typeface.typefacePaint;
 
-    return paint.hasGlyph (String.valueOf (charCode)) ? 1 : 0;
+    /* If the character falls within the confines of the BMP, return
+       1.  */
+    if (charCode < 65536)
+      return paint.hasGlyph (String.valueOf ((char) charCode)) ? 1 : 0;
+
+    /* Otherwise return 0.  */
+    return 0;
   }
 };
diff --git a/java/org/gnu/emacs/EmacsSdk7FontDriver.java 
b/java/org/gnu/emacs/EmacsSdk7FontDriver.java
index 97969585d16..21ae159d5bd 100644
--- a/java/org/gnu/emacs/EmacsSdk7FontDriver.java
+++ b/java/org/gnu/emacs/EmacsSdk7FontDriver.java
@@ -31,6 +31,19 @@ import android.graphics.Canvas;
 
 import android.util.Log;
 
+
+
+/* EmacsSdk7FontDriver implements a fallback font driver under
+   Android.  This font driver is enabled when the SFNT font driver (in
+   sfntfont-android.c) proves incapable of locating any fonts, which
+   has hitherto not been observed in practice.
+
+   This font driver does not supply each font installed on the system,
+   in lieu of which it provides a list of fonts for each conceivable
+   style and sub-type of the system's own Typefaces, which arises from
+   Android's absence of suitable APIs for loading individual font
+   files.  */
+
 public class EmacsSdk7FontDriver extends EmacsFontDriver
 {
   private static final String TOFU_STRING = "\uDB3F\uDFFD";
@@ -46,106 +59,26 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
     public int slant, width, weight, spacing;
 
     public
-    Sdk7Typeface (String fileName, Typeface typeface)
+    Sdk7Typeface (String familyName, Typeface typeface)
     {
       String style, testString;
       int index, measured, i;
       float[] widths;
 
+      /* Initialize the font style fields and create a paint object
+        linked with that typeface.  */
+
       slant = NORMAL;
       weight = REGULAR;
       width = UNSPECIFIED;
       spacing = PROPORTIONAL;
 
       this.typeface = typeface;
+      this.familyName = familyName;
 
       typefacePaint = new Paint ();
       typefacePaint.setAntiAlias (true);
       typefacePaint.setTypeface (typeface);
-
-      /* For the calls to measureText below.  */
-      typefacePaint.setTextSize (10.0f);
-
-      /* Parse the file name into some useful data.  First, strip off
-        the extension.  */
-      fileName = fileName.split ("\\.", 2)[0];
-
-      /* Next, split the file name by dashes.  Everything before the
-        last dash is part of the family name.  */
-      index = fileName.lastIndexOf ("-");
-
-      if (index > 0)
-       {
-         style = fileName.substring (index + 1, fileName.length ());
-         familyName = fileName.substring (0, index);
-
-         /* Look for something describing the weight.  */
-         if (style.contains ("Thin"))
-           weight = THIN;
-         else if (style.contains ("UltraLight"))
-           weight = ULTRA_LIGHT;
-         else if (style.contains ("SemiLight"))
-           weight = SEMI_LIGHT;
-         else if (style.contains ("Light"))
-           weight = LIGHT;
-         else if (style.contains ("Medium"))
-           weight = MEDIUM;
-         else if (style.contains ("SemiBold"))
-           weight = SEMI_BOLD;
-         else if (style.contains ("ExtraBold"))
-           weight = EXTRA_BOLD;
-         else if (style.contains ("Bold"))
-           weight = BOLD;
-         else if (style.contains ("Black"))
-           weight = BLACK;
-         else if (style.contains ("UltraHeavy"))
-           weight = ULTRA_HEAVY;
-
-         /* And the slant.  */
-         if (style.contains ("ReverseOblique"))
-           slant = OBLIQUE;
-         else if (style.contains ("ReverseItalic"))
-           slant = REVERSE_ITALIC;
-         else if (style.contains ("Italic"))
-           slant = ITALIC;
-         else if (style.contains ("Oblique"))
-           slant = OBLIQUE;
-
-         /* Finally, the width.  */
-         if (style.contains ("UltraCondensed"))
-           width = ULTRA_CONDENSED;
-         else if (style.contains ("ExtraCondensed"))
-           width = EXTRA_CONDENSED;
-         else if (style.contains ("SemiCondensed"))
-           width = SEMI_CONDENSED;
-         else if (style.contains ("Condensed"))
-           width = CONDENSED;
-         else if (style.contains ("SemiExpanded"))
-           width = SEMI_EXPANDED;
-         else if (style.contains ("ExtraExpanded"))
-           width = EXTRA_EXPANDED;
-         else if (style.contains ("UltraExpanded"))
-           width = ULTRA_EXPANDED;
-         else if (style.contains ("Expanded"))
-           width = EXPANDED;
-
-         /* Guess the spacing information.  */
-         testString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
-         widths = new float[testString.length ()];
-
-         measured = typefacePaint.getTextWidths (testString,
-                                                 0, testString.length (),
-                                                 widths);
-         spacing = MONO;
-         for (i = 0; i < measured; ++i)
-           {
-             if (i != 0 && widths[i - 1] != widths[i])
-               /* This isn't a monospace font.  */
-               spacing = PROPORTIONAL;
-           }
-       }
-      else
-       familyName = fileName;
     }
 
     @Override
@@ -244,43 +177,42 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
   EmacsSdk7FontDriver ()
   {
     int i;
-    File systemFontsDirectory, fontFile;
     Typeface typeface;
 
-    systemFontsDirectory = new File ("/system/fonts");
-
-    fontFamilyList = systemFontsDirectory.list ();
-
-    /* If that returned null, replace it with an empty array.  */
-    fontFamilyList = new String[0];
-
-    typefaceList = new Sdk7Typeface[fontFamilyList.length + 3];
+    typefaceList = new Sdk7Typeface[5];
 
-    /* It would be nice to avoid opening each and every font upon
-       startup.  But that doesn't seem to be possible on
-       Android.  */
+    /* Initialize the default monospace and Sans Serif typefaces.
+       Initialize the same typeface with various distinct styles.  */
+    fallbackTypeface = new Sdk7Typeface ("Sans Serif",
+                                        Typeface.DEFAULT);
+    typefaceList[1] = fallbackTypeface;
 
-    for (i = 0; i < fontFamilyList.length; ++i)
-      {
-       fontFile = new File (systemFontsDirectory,
-                            fontFamilyList[i]);
-       typeface = Typeface.createFromFile (fontFile);
-       typefaceList[i] = new Sdk7Typeface (fontFile.getName (),
-                                           typeface);
-      }
+    fallbackTypeface = new Sdk7Typeface ("Sans Serif",
+                                        Typeface.create (Typeface.DEFAULT,
+                                                         Typeface.BOLD));
+    fallbackTypeface.weight = BOLD;
+    typefaceList[2] = fallbackTypeface;
 
-    /* Initialize the default monospace and serif typefaces.  */
-    fallbackTypeface = new Sdk7Typeface ("monospace",
-                                        Typeface.MONOSPACE);
-    typefaceList[fontFamilyList.length] = fallbackTypeface;
+    fallbackTypeface = new Sdk7Typeface ("Sans Serif",
+                                        Typeface.create (Typeface.DEFAULT,
+                                                         Typeface.ITALIC));
+    fallbackTypeface.slant = ITALIC;
+    typefaceList[3] = fallbackTypeface;
+
+    fallbackTypeface
+      = new Sdk7Typeface ("Sans Serif",
+                         Typeface.create (Typeface.DEFAULT,
+                                          Typeface.BOLD_ITALIC));
+    fallbackTypeface.weight = BOLD;
+    fallbackTypeface.slant = ITALIC;
+    typefaceList[4] = fallbackTypeface;
 
     fallbackTypeface = new Sdk7Typeface ("Monospace",
                                         Typeface.MONOSPACE);
-    typefaceList[fontFamilyList.length + 1] = fallbackTypeface;
+    fallbackTypeface.spacing = MONO;
+    typefaceList[0] = fallbackTypeface;
 
-    fallbackTypeface = new Sdk7Typeface ("Sans Serif",
-                                        Typeface.DEFAULT);
-    typefaceList[fontFamilyList.length + 2] = fallbackTypeface;
+    fontFamilyList = new String[] { "Monospace", "Sans Serif", };
   }
 
   private boolean
@@ -359,13 +291,18 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
 
   @Override
   public int
-  hasChar (FontSpec font, char charCode)
+  hasChar (FontSpec font, int charCode)
   {
     float missingGlyphWidth, width;
     Rect rect1, rect2;
     Paint paint;
     Sdk7FontObject fontObject;
 
+    /* Ignore characters outside the BMP.  */
+
+    if (charCode > 65535)
+      return 0;
+
     if (font instanceof Sdk7FontObject)
       {
        fontObject = (Sdk7FontObject) font;
@@ -376,7 +313,7 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
 
     paint.setTextSize (10);
 
-    if (Character.isWhitespace (charCode))
+    if (Character.isWhitespace ((char) charCode))
       return 1;
 
     missingGlyphWidth = paint.measureText (TOFU_STRING);
@@ -393,7 +330,7 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
 
     paint.getTextBounds (TOFU_STRING, 0, TOFU_STRING.length (),
                         rect1);
-    paint.getTextBounds ("" + charCode, 0, 1, rect2);
+    paint.getTextBounds ("" + (char) charCode, 0, 1, rect2);
     return rect1.equals (rect2) ? 0 : 1;
   }
 
@@ -478,8 +415,11 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
 
   @Override
   public int
-  encodeChar (FontObject fontObject, char charCode)
+  encodeChar (FontObject fontObject, int charCode)
   {
+    if (charCode > 65535)
+      return FONT_INVALID_CODE;
+
     return charCode;
   }
 
@@ -491,16 +431,12 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
   {
     Rect backgroundRect, bounds;
     Sdk7FontObject sdk7FontObject;
-    char[] charsArray;
     int i;
     Canvas canvas;
     Paint paint;
+    char[] array;
 
     sdk7FontObject = (Sdk7FontObject) fontObject;
-    charsArray = new char[chars.length];
-
-    for (i = 0; i < chars.length; ++i)
-      charsArray[i] = (char) chars[i];
 
     backgroundRect = new Rect ();
     backgroundRect.top = y - sdk7FontObject.ascent;
@@ -526,13 +462,33 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
     paint.setTextSize (sdk7FontObject.pixelSize);
     paint.setTypeface (sdk7FontObject.typeface.typeface);
     paint.setAntiAlias (true);
-    canvas.drawText (charsArray, 0, chars.length, x, y, paint);
+
+    /* Android applies kerning to non-monospaced fonts by default,
+       which brings the dimensions of strings drawn via `drawText' out
+       of agreement with measurements previously provided to redisplay
+       by textExtents.  To avert such disaster, draw each character
+       individually, advancing the origin point by hand.  */
 
     bounds = new Rect ();
-    paint.getTextBounds (charsArray, 0, chars.length, bounds);
-    bounds.offset (x, y);
-    bounds.union (backgroundRect);
-    drawable.damageRect (bounds);
+    array = new char[1];
+
+    for (i = 0; i < chars.length; ++i)
+      {
+       /* Retrieve the text bounds for this character so as to
+          compute the damage rectangle.  */
+       array[0] = (char) chars[i];
+       paint.getTextBounds (array, 0, 1, bounds);
+       bounds.offset (x, y);
+       backgroundRect.union (bounds);
+
+       /* Draw this character.  */
+       canvas.drawText (array, 0, 1, x, y, paint);
+
+       /* Advance the origin point by that much.  */
+       x += paint.measureText ("" + array[0]);
+      }
+
+    drawable.damageRect (backgroundRect);
     paint.setAntiAlias (false);
     return 1;
   }
diff --git a/java/org/gnu/emacs/EmacsService.java 
b/java/org/gnu/emacs/EmacsService.java
index 404796cd08c..997c6923fcc 100644
--- a/java/org/gnu/emacs/EmacsService.java
+++ b/java/org/gnu/emacs/EmacsService.java
@@ -448,12 +448,13 @@ public final class EmacsService extends Service
 
   @SuppressWarnings ("deprecation")
   public void
-  ringBell ()
+  ringBell (int duration)
   {
     Vibrator vibrator;
     VibrationEffect effect;
     VibratorManager vibratorManager;
     Object tem;
+    int amplitude;
 
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
       {
@@ -467,13 +468,13 @@ public final class EmacsService extends Service
 
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
       {
+       amplitude = VibrationEffect.DEFAULT_AMPLITUDE;
        effect
-         = VibrationEffect.createOneShot (50,
-                                          VibrationEffect.DEFAULT_AMPLITUDE);
+         = VibrationEffect.createOneShot (duration, amplitude);
        vibrator.vibrate (effect);
       }
     else
-      vibrator.vibrate (50);
+      vibrator.vibrate (duration);
   }
 
   public short[]
@@ -643,13 +644,18 @@ public final class EmacsService extends Service
                                                          uri.getPath ());
              }
 
-           Log.d (TAG, ("browseUri: browsing " + url
-                        + " --> " + uri.getPath ()
-                        + " --> " + uri));
-
            intent = new Intent (Intent.ACTION_VIEW, uri);
+
+           /* Set several flags on the Intent prompting the system to
+              permit the recipient to read and edit the URI
+              indefinitely.  */
+
            intent.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK
-                            | Intent.FLAG_GRANT_READ_URI_PERMISSION);
+                            | Intent.FLAG_GRANT_READ_URI_PERMISSION
+                            | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+           if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
+             intent.addFlags (Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
          }
        else
          {
@@ -884,8 +890,6 @@ public final class EmacsService extends Service
                                        0);
     info = builder.build ();
 
-
-
     if (DEBUG_IC)
       Log.d (TAG, ("updateCursorAnchorInfo: " + x + " " + y
                   + " " + yBaseline + "-" + yBottom));
@@ -1137,8 +1141,10 @@ public final class EmacsService extends Service
     if (DEBUG_IC)
       Log.d (TAG, "updateExtractedText: @" + token + ", " + text);
 
+    icBeginSynchronous ();
     window.view.imManager.updateExtractedText (window.view,
                                               token, text);
+    icEndSynchronous ();
   }
 
 
diff --git a/java/org/gnu/emacs/EmacsView.java 
b/java/org/gnu/emacs/EmacsView.java
index 5a4bcbaa005..d09dcc7e50d 100644
--- a/java/org/gnu/emacs/EmacsView.java
+++ b/java/org/gnu/emacs/EmacsView.java
@@ -437,6 +437,16 @@ public final class EmacsView extends ViewGroup
     damageRegion.union (damageRect);
   }
 
+  /* This function enables damage to be recorded without consing a new
+     Rect object.  */
+
+  public void
+  damageRect (int left, int top, int right, int bottom)
+  {
+    EmacsService.checkEmacsThread ();
+    damageRegion.op (left, top, right, bottom, Region.Op.UNION);
+  }
+
   /* This method is called from both the UI thread and the Emacs
      thread.  */
 
@@ -470,6 +480,26 @@ public final class EmacsView extends ViewGroup
     surfaceView.setBitmap (bitmap, damageRect);
   }
 
+  @Override
+  public boolean
+  onKeyPreIme (int keyCode, KeyEvent event)
+  {
+    /* Several Android systems intercept key events representing
+       C-SPC.  Avert this by detecting C-SPC events here and relaying
+       them directly to onKeyDown.
+
+       Make this optional though, since some input methods also
+       leverage C-SPC as a shortcut for switching languages.  */
+
+    if ((keyCode == KeyEvent.KEYCODE_SPACE
+        && (window.eventModifiers (event)
+            & KeyEvent.META_CTRL_MASK) != 0)
+       && !EmacsNative.shouldForwardCtrlSpace ())
+      return onKeyDown (keyCode, event);
+
+    return super.onKeyPreIme (keyCode, event);
+  }
+
   @Override
   public boolean
   onKeyDown (int keyCode, KeyEvent event)
@@ -602,6 +632,13 @@ public final class EmacsView extends ViewGroup
     if (popupActive && !force)
       return false;
 
+    /* Android will permanently cease to display any popup menus at
+       all if the list of menu items is empty.  Prevent this by
+       promptly returning if there are no menu items.  */
+
+    if (menu.menuItems.isEmpty ())
+      return false;
+
     contextMenu = menu;
     popupActive = true;
 
diff --git a/java/org/gnu/emacs/EmacsWindow.java 
b/java/org/gnu/emacs/EmacsWindow.java
index aff5046b22e..1f28d5f4f53 100644
--- a/java/org/gnu/emacs/EmacsWindow.java
+++ b/java/org/gnu/emacs/EmacsWindow.java
@@ -514,7 +514,17 @@ public final class EmacsWindow extends EmacsHandleObject
   public void
   damageRect (Rect damageRect)
   {
-    view.damageRect (damageRect);
+    view.damageRect (damageRect.left,
+                    damageRect.top,
+                    damageRect.right,
+                    damageRect.bottom);
+  }
+
+  @Override
+  public void
+  damageRect (int left, int top, int right, int bottom)
+  {
+    view.damageRect (left, top, right, bottom);
   }
 
   public void
@@ -576,7 +586,7 @@ public final class EmacsWindow extends EmacsHandleObject
      input EVENT.  Replace bits corresponding to Left or Right keys
      with their corresponding general modifier bits.  */
 
-  private int
+  public static int
   eventModifiers (KeyEvent event)
   {
     int state;
diff --git a/leim/Makefile.in b/leim/Makefile.in
index 4c6c3179283..f7dfdf66f30 100644
--- a/leim/Makefile.in
+++ b/leim/Makefile.in
@@ -26,6 +26,7 @@ SHELL = @SHELL@
 # Here are the things that we expect ../configure to edit.
 srcdir=@srcdir@
 top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
 
 # Where the generated files go.
 leimdir = ${srcdir}/../lisp/leim
@@ -134,9 +135,15 @@ ${leimdir}/leim-list.el: ${srcdir}/leim-ext.el ${TIT_MISC}
 
 ${leimdir}/ja-dic/ja-dic.el: | $(leimdir)/ja-dic
 
+# This is used to support regeneration of ja-dic when the SMALL_JA_DIC
+# option is flipped by the configure-time option.
+small-ja-dic-option: ../config.status
+       $(AM_V_GEN)echo "Small ja-dic option: $(SMALL_JA_DIC)" > $@.$$$$ && \
+         ${top_srcdir}/build-aux/move-if-change $@.$$$$ $@
+
 .PHONY: generate-ja-dic
 generate-ja-dic: ${leimdir}/ja-dic/ja-dic.el
-${leimdir}/ja-dic/ja-dic.el: $(srcdir)/SKK-DIC/SKK-JISYO.L
+${leimdir}/ja-dic/ja-dic.el: $(srcdir)/SKK-DIC/SKK-JISYO.L small-ja-dic-option
        $(AM_V_GEN)$(RUN_EMACS) -batch -l ja-dic-cnv \
          -f batch-skkdic-convert -dir "$(leimdir)/ja-dic" 
$(JA_DIC_NO_REDUCTION_OPTION) "$<"
 
diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c
index a72fced1bf2..d15f9846163 100644
--- a/lib-src/emacsclient.c
+++ b/lib-src/emacsclient.c
@@ -618,6 +618,7 @@ decode_options (int argc, char **argv)
      display in DISPLAY (if any).  */
   if (create_frame && !tty && !display)
     {
+#ifndef HAVE_ANDROID
       /* Set these here so we use a default_display only when the user
          didn't give us an explicit display.  */
 #if defined (NS_IMPL_COCOA)
@@ -626,16 +627,22 @@ decode_options (int argc, char **argv)
       alt_display = "w32";
 #elif defined (HAVE_HAIKU)
       alt_display = "be";
-#elif defined (HAVE_ANDROID)
-      alt_display = "android";
-#endif
+#endif /* NS_IMPL_COCOA */
 
 #ifdef HAVE_PGTK
       display = egetenv ("WAYLAND_DISPLAY");
       alt_display = egetenv ("DISPLAY");
-#else
+#else /* !HAVE_PGTK */
       display = egetenv ("DISPLAY");
-#endif
+#endif /* HAVE_PGTK */
+#else /* HAVE_ANDROID */
+      /* Disregard the DISPLAY environment variable under Android.
+         Several terminal emulator programs furnish their own X
+         servers and set DISPLAY, but an Android build is incapable of
+         displaying X frames.  */
+      alt_display = NULL;
+      display = "android";
+#endif /* !HAVE_ANDROID */
     }
 
   if (!display)
diff --git a/lisp/ChangeLog.12 b/lisp/ChangeLog.12
index 6c11bdeaa49..9bc440626dc 100644
--- a/lisp/ChangeLog.12
+++ b/lisp/ChangeLog.12
@@ -32867,7 +32867,7 @@
        (reftex-index-switch-index-tag): Add `redo' to arguments of
        `reftex-display-index'.
        (reftex-index-make-phrase-regexp): Fix bug with case-sensitive
-       indexing.  Fix bug with matching is there is a quote before or
+       indexing.  Fix bug with matching if there is a quote before or
        after the word.
 
        * textmodes/reftex-cite.el (reftex-all-used-citation-keys):
diff --git a/lisp/align.el b/lisp/align.el
index 13e31e2ad60..a286addb51f 100644
--- a/lisp/align.el
+++ b/lisp/align.el
@@ -1337,12 +1337,18 @@ aligner would have dealt with are."
                 (thissep (if rulesep (cdr rulesep) separate))
                 same (eol 0)
                 search-start
-                groups ;; group-c
-                spacing spacing-c
-                tab-stop tab-stop-c
-                repeat repeat-c
-                valid valid-c
-                first
+                 (groups (ensure-list (or (cdr (assq 'group rule)) 1)))
+                 (spacing (cdr (assq 'spacing rule)))
+                 (tab-stop (let ((rule-ts (assq 'tab-stop rule)))
+                              (cond (rule-ts
+                                     (cdr rule-ts))
+                                    ((symbolp align-to-tab-stop)
+                                     (symbol-value align-to-tab-stop))
+                                    (t
+                                     align-to-tab-stop))))
+                 (repeat (cdr (assq 'repeat rule)))
+                 (valid (assq 'valid rule))
+                 (first (car groups))
                 regions index
                 last-point
                 save-match-data
@@ -1459,44 +1465,12 @@ aligner would have dealt with are."
                     (if (and (bolp) (> (point) search-start))
                         (forward-char -1))
 
-                    ;; lookup the `group' attribute the first time
-                    ;; that we need it
-                    (unless nil ;; group-c
-                      (setq groups (or (cdr (assq 'group rule)) 1))
-                      (setq groups (ensure-list groups))
-                      (setq first (car groups)))
-
-                    (unless spacing-c
-                      (setq spacing (cdr (assq 'spacing rule))
-                            spacing-c t))
-
-                    (unless tab-stop-c
-                      (setq tab-stop
-                            (let ((rule-ts (assq 'tab-stop rule)))
-                              (cond (rule-ts
-                                     (cdr rule-ts))
-                                    ((symbolp align-to-tab-stop)
-                                     (symbol-value align-to-tab-stop))
-                                    (t
-                                     align-to-tab-stop)))
-                            tab-stop-c t))
-
                     ;; test whether we have found a match on the same
                     ;; line as a previous match
                     (when (> (point) eol)
                       (setq same nil)
                       (align--set-marker eol (line-end-position)))
 
-                    ;; lookup the `repeat' attribute the first time
-                    (or repeat-c
-                        (setq repeat (cdr (assq 'repeat rule))
-                              repeat-c t))
-
-                    ;; lookup the `valid' attribute the first time
-                    (or valid-c
-                        (setq valid (assq 'valid rule)
-                              valid-c t))
-
                     ;; remember the beginning position of this rule
                     ;; match, and save the match-data, since either
                     ;; the `valid' form, or the code that searches for
@@ -1598,7 +1572,7 @@ aligner would have dealt with are."
                         (if (= (point) search-start)
                             (forward-char)))))
 
-                  ;; when they are no more matches for this rule,
+                  ;; when there are no more matches for this rule,
                   ;; align whatever was left over
                   (if regions
                       (align-regions regions align-props rule func))))))))
diff --git a/lisp/use-package/bind-key.el b/lisp/bind-key.el
similarity index 99%
rename from lisp/use-package/bind-key.el
rename to lisp/bind-key.el
index c45b9e546f7..3cff0f7f3a9 100644
--- a/lisp/use-package/bind-key.el
+++ b/lisp/bind-key.el
@@ -10,6 +10,9 @@
 ;; Keywords: keys keybinding config dotemacs extensions
 ;; URL: https://github.com/jwiegley/use-package
 
+;; This is a GNU ELPA :core package.  Avoid functionality that is not
+;; compatible with the version of Emacs recorded above.
+
 ;; This file is part of GNU Emacs.
 
 ;; GNU Emacs is free software: you can redistribute it and/or modify
diff --git a/lisp/bindings.el b/lisp/bindings.el
index 68aa0a78099..207adb3a2a4 100644
--- a/lisp/bindings.el
+++ b/lisp/bindings.el
@@ -794,7 +794,7 @@ meaningful if it refers to a lexically bound variable."
   "Describe minor mode for EVENT on minor modes area of the mode line."
   (interactive "@e")
   (let ((indicator (car (nth 4 (car (cdr event))))))
-    (describe-minor-mode-from-indicator indicator)))
+    (describe-minor-mode-from-indicator indicator event)))
 
 (defvar mode-line-defining-kbd-macro (propertize " Def" 'face 
'font-lock-warning-face)
   "String displayed in the mode line in keyboard macro recording mode.")
diff --git a/lisp/button.el b/lisp/button.el
index b01595943fc..bfe6ccc8d1f 100644
--- a/lisp/button.el
+++ b/lisp/button.el
@@ -492,7 +492,7 @@ pushing a button, use the `button-describe' command."
            (if str-button
                ;; mode-line, header-line, or display string event.
                (button-activate str t)
-              (if (eq (car pos) 'touchscreen-down)
+              (if (eq (car-safe pos) 'touchscreen-down)
                   ;; If touch-screen-track tap returns nil, then the
                   ;; tap was cancelled.
                   (when (touch-screen-track-tap pos)
diff --git a/lisp/calendar/appt.el b/lisp/calendar/appt.el
index 469b3f4023c..c761bb6a89d 100644
--- a/lisp/calendar/appt.el
+++ b/lisp/calendar/appt.el
@@ -165,6 +165,12 @@ Only relevant if reminders are being displayed in a 
window."
   :type 'function
   :group 'appt)
 
+(defface appt-notification
+  '((t :inherit mode-line-emphasis))
+  "Face for appointment notification on the modeline.
+Shown when `appt-display-mode-line' is non-nil."
+  :group 'mode-line-faces
+  :version "30.1")
 
 ;;; Internal variables below this point.
 
@@ -406,7 +412,7 @@ displayed in a window:
                          (appt-mode-line (mapcar #'number-to-string
                                                  min-list)
                                          t)
-                         'face 'mode-line-emphasis)
+                         'face 'appt-notification)
                         " ")))
         ;; Reset count to 0 in case we display another appt on the next cycle.
         (setq appt-display-count (if (equal '(0) min-list) 0
diff --git a/lisp/calendar/diary-lib.el b/lisp/calendar/diary-lib.el
index 946cf0e7236..0d894f10013 100644
--- a/lisp/calendar/diary-lib.el
+++ b/lisp/calendar/diary-lib.el
@@ -167,8 +167,8 @@ form of ((MONTH DAY YEAR) STRING), where string is the diary
 entry for the given date.  This can be used, for example, to
 produce a different buffer for display (perhaps combined with
 holidays), or hard copy output."
-  :type '(choice (const diary-fancy-display :tag "Fancy display")
-                 (const diary-simple-display :tag "Basic display")
+  :type '(choice (const :tag "Fancy display" diary-fancy-display)
+                 (const :tag "Basic display" diary-simple-display)
                  (const :tag "No display" ignore)
                  (function :tag "User-specified function"))
   :initialize 'custom-initialize-default
diff --git a/lisp/calendar/icalendar.el b/lisp/calendar/icalendar.el
index e51251d6d9f..51e6a7d1170 100644
--- a/lisp/calendar/icalendar.el
+++ b/lisp/calendar/icalendar.el
@@ -276,9 +276,9 @@ other sexp entries are enumerated in any case."
                                 :value 10)
                        (set :tag "Alarm type"
                             (list :tag "Audio"
-                                  (const audio :tag "Audio"))
+                                  (const :tag "Audio" audio))
                             (list :tag "Display"
-                                  (const display :tag "Display"))
+                                  (const :tag "Display" display))
                             (list :tag "Email"
                                   (const email)
                                   (repeat :tag "Attendees"
diff --git a/lisp/calendar/todo-mode.el b/lisp/calendar/todo-mode.el
index ffb7b7168dd..093ea0e22b6 100644
--- a/lisp/calendar/todo-mode.el
+++ b/lisp/calendar/todo-mode.el
@@ -200,7 +200,7 @@ The final element is \"*\", indicating an unspecified 
month.")
                 (month "\\(?7:[0-9]+\\|\\*\\)")
                 (day "\\(?8:[0-9]+\\|\\*\\)")
                 (year "-?\\(?9:[0-9]+\\|\\*\\)"))
-             (mapconcat #'eval calendar-date-display-form ""))
+             (mapconcat #'eval calendar-date-display-form))
            "\\)"))
   "Regular expression matching a todo item date header.")
 
@@ -1206,7 +1206,7 @@ visiting the deleted files."
                (let ((sexp (read (buffer-substring-no-properties
                                   (line-beginning-position)
                                   (line-end-position))))
-                     (buffer-read-only nil)
+                     (inhibit-read-only t)
                      (print-length nil)
                      (print-level nil))
                  (mapc (lambda (x) (aset (cdr x) 3 0)) sexp)
@@ -1304,7 +1304,7 @@ return the new category number."
       (widen)
       (goto-char (point-max))
       (save-excursion                  ; Save point for todo-category-select.
-       (let ((buffer-read-only nil))
+       (let ((inhibit-read-only t))
          (insert todo-category-beg cat "\n\n" todo-category-done "\n")))
       (todo-update-categories-sexp)
       ;; If invoked by user, display the newly added category, if
@@ -1486,7 +1486,7 @@ the archive of the file moved to, creating it if it does 
not exist."
                                      nfile-short)
                              (format "the category \"%s\";\n" cat)
                              "enter a new category name: "))
-                    (buffer-read-only nil)
+                    (inhibit-read-only t)
                     (print-length nil)
                     (print-level nil))
                (widen)
@@ -1528,7 +1528,7 @@ the archive of the file moved to, creating it if it does 
not exist."
            ;; Delete the category from the old file, and if that was the
            ;; last category, delete the file.  Also handle archive file
            ;; if necessary.
-           (let ((buffer-read-only nil))
+           (let ((inhibit-read-only t))
              (widen)
               (remove-overlays beg end)
              (delete-region beg end)
@@ -1581,7 +1581,7 @@ archive file and the source category is deleted."
         here)
     (with-current-buffer (get-buffer (find-file-noselect tfile))
       (widen)
-      (let* ((buffer-read-only nil)
+      (let* ((inhibit-read-only t)
             (cbeg (progn
                     (re-search-backward
                      (concat "^" (regexp-quote todo-category-beg)) nil t)
@@ -1609,7 +1609,7 @@ archive file and the source category is deleted."
          (unless (derived-mode-p 'todo-mode) (todo-mode))
          (widen)
          (goto-char (point-min))
-         (let ((buffer-read-only nil))
+         (let ((inhibit-read-only t))
            ;; Merge any todo items.
            (unless (zerop (length todo))
              (re-search-forward
@@ -1647,7 +1647,7 @@ archive file and the source category is deleted."
       (with-current-buffer (get-buffer (find-file-noselect tarchive))
        (widen)
        (goto-char (point-min))
-       (let* ((buffer-read-only nil)
+       (let* ((inhibit-read-only t)
               (cbeg (progn
                       (when (re-search-forward
                              (concat "^" (regexp-quote
@@ -1967,7 +1967,7 @@ their associated keys and their effects."
        (setq todo-current-todo-file file)
        (unless todo-global-current-todo-file
          (setq todo-global-current-todo-file todo-current-todo-file))
-       (let ((buffer-read-only nil)
+       (let ((inhibit-read-only t)
              done-only item-added)
          (unless copy
            (setq new-item
@@ -2197,9 +2197,9 @@ the item at point."
                                      end t)
                  (if comment-delete
                      (when (todo-y-or-n-p "Delete comment? ")
-                       (let ((buffer-read-only nil))
+                       (let ((inhibit-read-only t))
                          (delete-region (match-beginning 0) (match-end 0))))
-                   (let ((buffer-read-only nil))
+                   (let ((inhibit-read-only t))
                      (replace-match (save-match-data
                                       (prog1 (let ((buffer-read-only t))
                                                (read-string
@@ -2216,7 +2216,7 @@ the item at point."
                                     nil nil nil 1)))
                (if comment-delete
                    (user-error "There is no comment to delete")
-                 (let ((buffer-read-only nil))
+                 (let ((inhibit-read-only t))
                    (insert " [" todo-comment-string ": "
                            (prog1 (let ((buffer-read-only t))
                                     (read-string prompt))
@@ -2261,7 +2261,7 @@ the item at point."
                (todo-category-number ocat)
                (todo-category-select)
                (goto-char opoint))
-             (let ((buffer-read-only nil))
+             (let ((inhibit-read-only t))
                (todo-remove-item)
                (todo-insert-with-overlays new))
              (move-to-column item-beg)))))))))
@@ -2493,8 +2493,8 @@ made in the number or names of categories."
                            (month month)
                            (day day)
                            (dayname nil)) ;; dayname
-                        (mapconcat #'eval calendar-date-display-form "")))))
-           (let ((buffer-read-only nil))
+                        (mapconcat #'eval calendar-date-display-form)))))
+           (let ((inhibit-read-only t))
              (when ndate (replace-match ndate nil nil nil 1))
              ;; Add new time string to the header, if it was supplied.
              (when ntime
@@ -2754,7 +2754,7 @@ meaning to raise or lower the item's priority by one."
            (when match
              (user-error (concat "Cannot reprioritize items from the same "
                                  "category in this mode, only in Todo 
mode")))))
-       (let ((buffer-read-only nil))
+       (let ((inhibit-read-only t))
          ;; Interactively or with non-nil ARG, relocate the item within its
          ;; category.
          (when (or arg (called-interactively-p 'any))
@@ -2877,7 +2877,7 @@ section in the category moved to."
                 (setq here (point))
                 (while todo-items
                   (todo-forward-item)
-                  (let ((buffer-read-only nil))
+                  (let ((inhibit-read-only t))
                    (todo-insert-with-overlays (pop todo-items)))))
              ;; Move done items en bloc to top of done items section.
               (when done-items
@@ -2892,7 +2892,7 @@ section in the category moved to."
                (forward-line)
                 (unless here (setq here (point)))
                 (while done-items
-                  (let ((buffer-read-only nil))
+                  (let ((inhibit-read-only t))
                    (todo-insert-with-overlays (pop done-items)))
                   (todo-item-end)
                  (forward-line)))
@@ -2933,13 +2933,13 @@ section in the category moved to."
                        (goto-char beg)
                        (while (< (point) end)
                          (if (todo-marked-item-p)
-                             (let ((buffer-read-only nil))
+                             (let ((inhibit-read-only t))
                                (todo-remove-item))
                            (todo-forward-item)))
                        (setq todo-categories-with-marks
                              (assq-delete-all cat1 
todo-categories-with-marks)))
                    (if ov (delete-overlay ov))
-                   (let ((buffer-read-only nil))
+                   (let ((inhibit-read-only t))
                      (todo-remove-item)))))
              (when todo (todo-update-count 'todo (- todo) cat1))
              (when diary (todo-update-count 'diary (- diary) cat1))
@@ -2999,7 +2999,7 @@ visible."
             (show-done (save-excursion
                          (goto-char (point-min))
                          (re-search-forward todo-done-string-start nil t)))
-            (buffer-read-only nil)
+            (inhibit-read-only t)
             header item done-items
             (opoint (point)))
        ;; Don't add empty comment to done item.
@@ -3131,7 +3131,7 @@ comments without asking."
          (when ov (delete-overlay ov))
          (if (not undone)
              (goto-char opoint)
-           (let ((buffer-read-only nil))
+           (let ((inhibit-read-only t))
              (if marked
                  (progn
                    (setq item nil)
@@ -3299,7 +3299,7 @@ this category does not exist in the archive, it is 
created."
                  (todo-archive-mode))
                 (if headers-hidden (todo-toggle-item-header))))
            (with-current-buffer tbuf
-             (let ((buffer-read-only nil))
+             (let ((inhibit-read-only t))
                (cond
                 (all
                  (save-excursion
@@ -3602,7 +3602,7 @@ decreasing or increasing its number."
               ;; Category's name and items counts list.
               (catcons (nth (1- curnum) todo-categories))
               (todo-categories (nconc head (list catcons) tail))
-              (buffer-read-only nil)
+              (inhibit-read-only t)
               newcats)
          (when lower (setq todo-categories (nreverse todo-categories)))
          (setq todo-categories (delete-dups todo-categories))
@@ -3823,8 +3823,7 @@ which is the value of the user option
                                (cons todo-categories-diary-label 'diary)
                                (cons todo-categories-done-label 'done)
                                (cons todo-categories-archived-label
-                                     'archived)))
-                         "")
+                                     'archived))))
             " ") ; Make highlighting on last column look better.
      'face (if (and todo-skip-archived-categories
                    (zerop (todo-get-count 'todo cat))
@@ -3932,8 +3931,7 @@ which is the value of the user option
               (list (cons todo-categories-todo-label 0)
                     (cons todo-categories-diary-label 1)
                     (cons todo-categories-done-label 2)
-                    (cons todo-categories-archived-label 3)))
-            ""))
+                    (cons todo-categories-archived-label 3)))))
     ;; Put cursor on Category button initially.
     (if pt (goto-char pt))
     (setq buffer-read-only t)))
@@ -4758,7 +4756,7 @@ Helper function for `todo-convert-legacy-files'."
        (time (match-string 4))
        dayname)
     (replace-match "")
-    (insert (mapconcat #'eval calendar-date-display-form "")
+    (insert (mapconcat #'eval calendar-date-display-form)
            (when time (concat " " time)))))
 
 (defun todo-convert-legacy-files ()
@@ -5112,7 +5110,7 @@ With nil or omitted CATEGORY, default to the current 
category."
 
 (defun todo-update-categories-sexp ()
   "Update the `todo-categories' sexp at the top of the file."
-  (let ((buffer-read-only nil)
+  (let ((inhibit-read-only t)
        (print-length nil)
         (print-level nil))
     (save-excursion
@@ -6180,7 +6178,7 @@ number of the last the day of the month."
               (if (memq 'month calendar-date-display-form)
                   month
                 monthname)))
-      (mapconcat #'eval calendar-date-display-form ""))))
+      (mapconcat #'eval calendar-date-display-form))))
 
 (defun todo-read-dayname ()
   "Choose name of a day of the week with completion and return it."
diff --git a/lisp/cedet/cedet-global.el b/lisp/cedet/cedet-global.el
index a175f16d6b8..7d502cf60a2 100644
--- a/lisp/cedet/cedet-global.el
+++ b/lisp/cedet/cedet-global.el
@@ -152,7 +152,14 @@ return nil."
          nil)
       (with-current-buffer b
        (goto-char (point-min))
-       (re-search-forward "(?GNU GLOBAL)? \\([0-9.]+\\)" nil t)
+        (re-search-forward
+         (rx (or
+              ;; global (Global) 6.6.10
+              "global (Global)"
+              (seq (opt "(") "GNU GLOBAL" (opt ")")))
+             " "
+             (group (one-or-more (any "0-9."))))
+         nil t)
        (setq rev (match-string 1))
         (if (version< rev cedet-global-min-version)
            (if noerror
diff --git a/lisp/comint.el b/lisp/comint.el
index 777795bcb46..de7cc5b0e86 100644
--- a/lisp/comint.el
+++ b/lisp/comint.el
@@ -260,6 +260,7 @@ to set this in a mode hook, rather than customize the 
default value."
 
 (defcustom comint-pager nil
   "If non-nil, the program to use for pagination of program output.
+If nil, use the default pager.
 
 Some programs produce large amounts of output, and have provision for
 pagination of their output through a filter program, commonly known as
@@ -2809,7 +2810,7 @@ If N is negative, find the previous or Nth previous 
match."
 If `comint-use-prompt-regexp' is nil, then this means the beginning of
 the Nth next `input' field, otherwise, it means the Nth occurrence of
 text matching `comint-prompt-regexp'."
-  (interactive "p")
+  (interactive "^p")
   (if comint-use-prompt-regexp
       ;; Use comint-prompt-regexp
       (let ((paragraph-start comint-prompt-regexp))
@@ -2846,7 +2847,7 @@ text matching `comint-prompt-regexp'."
 If `comint-use-prompt-regexp' is nil, then this means the beginning of
 the Nth previous `input' field, otherwise, it means the Nth occurrence of
 text matching `comint-prompt-regexp'."
-  (interactive "p")
+  (interactive "^p")
   (comint-next-prompt (- n)))
 
 ;; State used by `comint-insert-previous-argument' when cycling.
@@ -2857,7 +2858,7 @@ text matching `comint-prompt-regexp'."
   "If non-nil, `comint-insert-previous-argument' counts args from the end.
 If this variable is nil, the default, `comint-insert-previous-argument'
 counts the arguments from the beginning; if non-nil, it counts from
-the end instead.  This allows to emulate the behavior of `ESC-NUM ESC-.'
+the end instead.  This emulates the behavior of `ESC-NUM ESC-.'
 in both Bash and zsh: in Bash, `number' counts from the
 beginning (variable is nil), while in zsh, it counts from the end."
   :type 'boolean
diff --git a/lisp/completion.el b/lisp/completion.el
index eed6e77da4c..b0167412b20 100644
--- a/lisp/completion.el
+++ b/lisp/completion.el
@@ -2133,7 +2133,25 @@ TYPE is the type of the wrapper to be added.  Can be 
:before or :under."
 
 ;;;###autoload
 (define-minor-mode dynamic-completion-mode
-  "Toggle dynamic word-completion on or off."
+  "Toggle dynamic word-completion on or off.
+
+When this minor mode is turned on, typing \\`M-RET' or \\`C-RET'
+invokes the command `complete', which completes the word or
+symbol at point using the record of words/symbols you used
+previously and the previously-inserted completions.  Typing
+a word or moving point across it constitutes \"using\" the
+word.
+
+By default, the database of all the dynamic completions that
+were inserted by \\[complete] is saved on the file specified
+by `save-completions-file-name' when you exit Emacs, and will
+be loaded from that file when this mode is enabled in a future
+Emacs session.
+
+The following important options control the various aspects of
+this mode: `enable-completion', `save-completions-flag', and
+`save-completions-retention-time'.  Few other less important
+options can be found in the `completion' group."
   :global t
   ;; This is always good, not specific to dynamic-completion-mode.
   (define-key function-key-map [C-return] [?\C-\r])
diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el
index 28513a2c61a..2ff620065a5 100644
--- a/lisp/dired-aux.el
+++ b/lisp/dired-aux.el
@@ -3655,6 +3655,22 @@ resume the query replace with the command 
\\[fileloop-continue]."
    delimited)
   (fileloop-continue))
 
+;;;###autoload
+(defun dired-do-replace-regexp-as-diff (from to &optional delimited)
+  "Do `replace-regexp' of FROM with TO as diff, on all marked files.
+Third arg DELIMITED (prefix arg) means replace only word-delimited matches.
+The replacements are displayed in the buffer *replace-diff* that
+you can later apply as a patch after reviewing the changes."
+  (interactive
+   (let ((common
+          (query-replace-read-args
+           "Replace regexp as diff in marked files" t t)))
+     (list (nth 0 common) (nth 1 common) (nth 2 common))))
+  (dired-post-do-command)
+  (multi-file-replace-regexp-as-diff
+   (dired-get-marked-files nil nil #'dired-nondirectory-p)
+   from to delimited))
+
 (declare-function xref-query-replace-in-results "xref")
 (declare-function project--files-in-directory "project")
 
diff --git a/lisp/dired-x.el b/lisp/dired-x.el
index 398f55f2a24..b7824fa81bd 100644
--- a/lisp/dired-x.el
+++ b/lisp/dired-x.el
@@ -491,7 +491,11 @@ status message."
               (setq count  (+ count
                               (dired-do-kill-lines
                                nil
-                               (if dired-omit-verbose "Omitted %d line%s" "")
+                               (if dired-omit-verbose
+                                   (format "Omitted %%d line%%s in %s"
+                                           (abbreviate-file-name
+                                            dired-directory))
+                                 "")
                                init-count)))
               (force-mode-line-update))))
         ;; Try to preserve modified state, so `%*' doesn't appear in
diff --git a/lisp/dired.el b/lisp/dired.el
index e96b85a173a..cc8c74839b9 100644
--- a/lisp/dired.el
+++ b/lisp/dired.el
@@ -495,6 +495,21 @@ to nil: a pipe using `zcat' or `gunzip -c' will be used."
                  (string :tag "Switches"))
   :version "29.1")
 
+(defcustom dired-movement-style nil
+  "Non-nil means point skips empty lines when moving in Dired buffers.
+This affects only `dired-next-line' and `dired-previous-line'.
+
+Possible non-nil values:
+ * `cycle':   when moving from the last/first visible line, cycle back
+              to the first/last visible line.
+ * `bounded': don't move up/down if the current line is the
+              first/last visible line."
+  :type '(choice (const :tag "Move to any line" nil)
+                 (const :tag "Cycle through non-empty lines" cycle)
+                 (const :tag "Stop on last/first non-empty line" bounded))
+  :group 'dired
+  :version "30.1")
+
 (defcustom dired-hide-details-preserved-columns nil
   "List of columns which are not hidden in `dired-hide-details-mode'."
   :type '(repeat integer)
@@ -2666,22 +2681,71 @@ Otherwise, toggle `read-only-mode'."
       (wdired-change-to-wdired-mode)
     (read-only-mode 'toggle)))
 
-(defun dired-next-line (arg)
-  "Move down lines then position at filename.
-Optional prefix ARG says how many lines to move; default is one line."
-  (interactive "^p")
+(defun dired--trivial-next-line (arg)
+  "Move down ARG lines, then position at filename."
   (let ((line-move-visual)
-       (goal-column))
+    (goal-column))
     (line-move arg t))
   ;; We never want to move point into an invisible line.
   (while (and (invisible-p (point))
-             (not (if (and arg (< arg 0)) (bobp) (eobp))))
+          (not (if (and arg (< arg 0)) (bobp) (eobp))))
     (forward-char (if (and arg (< arg 0)) -1 1)))
   (dired-move-to-filename))
 
+(defun dired-next-line (arg)
+  "Move down ARG lines, then position at filename.
+The argument ARG (interactively, prefix argument) says how many lines
+to move; the default is one line.
+
+Whether to skip empty lines and how to move from last line
+is controlled by `dired-movement-style'."
+  (interactive "^p")
+  (if dired-movement-style
+      (let ((old-position (progn
+                            ;; It's always true that we should move
+                            ;; to the filename when possible.
+                            (dired-move-to-filename)
+                            (point)))
+            ;; Up/Down indicates the direction.
+            (moving-down (if (cl-plusp arg)
+                             1    ; means Down.
+                           -1)))  ; means Up.
+        ;; Line by line in case we forget to skip empty lines.
+        (while (not (zerop arg))
+          (dired--trivial-next-line moving-down)
+          (when (= old-position (point))
+            ;; Now point is at beginning/end of movable area,
+            ;; but it still wants to move farther.
+            (if (eq dired-movement-style 'cycle)
+                ;; `cycle': go to the other end.
+                (goto-char (if (cl-plusp moving-down)
+                               (point-min)
+                             (point-max)))
+              ;; `bounded': go back to the last non-empty line.
+              (while (string-match-p "\\`[[:blank:]]*\\'"
+                                     (buffer-substring-no-properties
+                                      (line-beginning-position)
+                                      (line-end-position)))
+                (dired--trivial-next-line (- moving-down)))
+              ;; Encountered a boundary, so let's stop movement.
+              (setq arg moving-down)))
+          (when (not (string-match-p "\\`[[:blank:]]*\\'"
+                                     (buffer-substring-no-properties
+                                      (line-beginning-position)
+                                      (line-end-position))))
+            ;; Has moved to a non-empty line.  This movement does
+            ;; make sense.
+            (cl-decf arg moving-down))
+          (setq old-position (point))))
+    (dired--trivial-next-line arg)))
+
 (defun dired-previous-line (arg)
-  "Move up lines then position at filename.
-Optional prefix ARG says how many lines to move; default is one line."
+  "Move up ARG lines, then position at filename.
+The argument ARG (interactively, prefix argument) says how many lines
+to move; the default is one line.
+
+Whether to skip empty lines and how to move from first line
+is controlled by `dired-movement-style'."
   (interactive "^p")
   (dired-next-line (- (or arg 1))))
 
diff --git a/lisp/doc-view.el b/lisp/doc-view.el
index e25e63a97ee..210b7ace7d6 100644
--- a/lisp/doc-view.el
+++ b/lisp/doc-view.el
@@ -204,10 +204,10 @@ are available (see Info node `(emacs)Document View')."
     #'doc-view-pdf->png-converter-ghostscript)
   "Function to call to convert a PDF file into a PNG file."
   :type '(radio
-          (function-item doc-view-pdf->png-converter-ghostscript
-                         :doc "Use ghostscript")
-          (function-item doc-view-pdf->png-converter-mupdf
-                         :doc "Use mupdf")
+          (function-item :doc "Use Ghostscript"
+                         doc-view-pdf->png-converter-ghostscript)
+          (function-item :doc "Use MuPDF"
+                         doc-view-pdf->png-converter-mupdf)
           function)
   :version "24.4")
 
@@ -375,17 +375,15 @@ Needed for viewing OpenOffice.org (and MS Office) files."
   :type 'file)
 
 (defcustom doc-view-odf->pdf-converter-function
-  (cond
-   ((string-match "unoconv\\'" doc-view-odf->pdf-converter-program)
-    #'doc-view-odf->pdf-converter-unoconv)
-   ((string-match "soffice\\'" doc-view-odf->pdf-converter-program)
-    #'doc-view-odf->pdf-converter-soffice))
-  "Function to call to convert a ODF file into a PDF file."
+  (if (string-suffix-p "unoconv" doc-view-odf->pdf-converter-program)
+      #'doc-view-odf->pdf-converter-unoconv
+    #'doc-view-odf->pdf-converter-soffice)
+  "Function to call to convert an ODF file into a PDF file."
   :type '(radio
-          (function-item doc-view-odf->pdf-converter-unoconv
-                         :doc "Use unoconv")
-          (function-item doc-view-odf->pdf-converter-soffice
-                         :doc "Use LibreOffice")
+          (function-item :doc "Use LibreOffice"
+                         doc-view-odf->pdf-converter-soffice)
+          (function-item :doc "Use unoconv"
+                         doc-view-odf->pdf-converter-unoconv)
           function)
   :version "24.4")
 
@@ -940,8 +938,7 @@ Document types are symbols like `dvi', `ps', `pdf', `epub',
                (and doc-view-pdfdraw-program
                     (executable-find doc-view-pdfdraw-program)))))
        ((eq type 'odf)
-        (and doc-view-odf->pdf-converter-program
-             (executable-find doc-view-odf->pdf-converter-program)
+         (and (executable-find doc-view-odf->pdf-converter-program)
              (doc-view-mode-p 'pdf)))
        ((eq type 'djvu)
         (executable-find "ddjvu"))
@@ -1283,7 +1280,8 @@ The test is performed using `doc-view-pdfdraw-program'."
                                     (expand-file-name
                                      doc-view-epub-user-stylesheet)))))))
     (doc-view-start-process
-     "pdf->png" doc-view-pdfdraw-program
+     (concat "pdf->" (symbol-name doc-view--image-type))
+     doc-view-pdfdraw-program
      `(,@(doc-view-pdfdraw-program-subcommand)
        ,@options
        ,pdf
@@ -2238,8 +2236,15 @@ toggle between displaying the document or editing it as 
text.
                   ;; supposed to return nil for things like local files 
accessed
                   ;; via `su' or via file://...
                   ((let ((file-name-handler-alist nil))
-                     (not (and buffer-file-name
-                               (file-readable-p buffer-file-name))))
+                     (or (not (and buffer-file-name
+                                   (file-readable-p buffer-file-name)))
+                         ;; If the system is Android and the file name
+                         ;; begins with /content or /assets, it's not
+                         ;; readable by local processes.
+                         (and (eq system-type 'android)
+                              (string-match-p "/\\(content\\|assets\\)[/$]"
+                                              (expand-file-name
+                                               buffer-file-name)))))
                    ;; FIXME: there's a risk of name conflicts here.
                    (expand-file-name
                     (if buffer-file-name
diff --git a/lisp/edmacro.el b/lisp/edmacro.el
index 69d20d2bad3..232ef767b45 100644
--- a/lisp/edmacro.el
+++ b/lisp/edmacro.el
@@ -73,9 +73,19 @@ Default nil means to write characters above \\177 in octal 
notation."
   :type 'boolean
   :group 'kmacro)
 
+(defcustom edmacro-reverse-macro-lines nil
+  "If non-nil, `edit-kbd-macro' shows most recent line of key sequences first.
+
+This is useful when dealing with long lists of key sequences, such as
+from `kmacro-edit-lossage'."
+  :type 'boolean
+  :group 'kmacro
+  :version "30.1")
+
 (defvar-keymap edmacro-mode-map
   "C-c C-c" #'edmacro-finish-edit
-  "C-c C-q" #'edmacro-insert-key)
+  "C-c C-q" #'edmacro-insert-key
+  "C-c C-r" #'edmacro-set-macro-to-region-lines)
 
 (defface edmacro-label
   '((default :inherit bold)
@@ -88,7 +98,10 @@ Default nil means to write characters above \\177 in octal 
notation."
   :group 'kmacro)
 
 (defvar edmacro-mode-font-lock-keywords
-  `((,(rx bol (group (or "Command" "Key" "Macro") ":")) 0 'edmacro-label)
+  `((,(rx bol (group (or "Command" "Key"
+                         (seq "Macro" (zero-or-one " (most recent line 
first)")))
+                     ":"))
+     0 'edmacro-label)
     (,(rx bol
           (group ";; Keyboard Macro Editor.  Press ")
           (group (*? nonl))
@@ -166,7 +179,13 @@ With a prefix argument, format the macro in a more concise 
way."
       (let* ((oldbuf (current-buffer))
             (mmac (edmacro-fix-menu-commands mac))
             (fmt (edmacro-format-keys mmac 1))
-            (fmtv (edmacro-format-keys mmac (not prefix)))
+            (fmtv (let ((fmtv (edmacro-format-keys mmac (not prefix))))
+                     (if (not edmacro-reverse-macro-lines)
+                         fmtv
+                       (with-temp-buffer
+                         (insert fmtv)
+                         (reverse-region (point-min) (point-max))
+                         (buffer-string)))))
             (buf (get-buffer-create "*Edit Macro*")))
        (message "Formatting keyboard macro...done")
        (switch-to-buffer buf)
@@ -179,8 +198,11 @@ With a prefix argument, format the macro in a more concise 
way."
         (setq-local edmacro-finish-hook finish-hook)
         (setq-local edmacro-store-hook store-hook)
         (setq-local font-lock-defaults
-                    '(edmacro-mode-font-lock-keywords nil nil nil nil))
+                    '(edmacro-mode-font-lock-keywords nil nil ((?\" . "w"))))
         (setq font-lock-multiline nil)
+        ;; Make buffer-local so that the commands still work
+        ;; even if the default value changes.
+        (make-local-variable 'edmacro-reverse-macro-lines)
        (erase-buffer)
         (insert (substitute-command-keys
                  (concat
@@ -202,7 +224,9 @@ With a prefix argument, format the macro in a more concise 
way."
              (insert "Key: none\n")))
          (when (and mac-counter mac-format)
            (insert (format "Counter: %d\nFormat: \"%s\"\n" mac-counter 
mac-format))))
-       (insert "\nMacro:\n\n")
+       (insert (format "\nMacro%s:\n\n" (if edmacro-reverse-macro-lines
+                                             " (most recent line first)"
+                                           "")))
        (save-excursion
          (insert fmtv "\n"))
        (recenter '(4))
@@ -255,6 +279,33 @@ or nil, use a compact 80-column format."
 
 ;;; Commands for *Edit Macro* buffer.
 
+(defvar edmacro--skip-line-regexp
+  "[ \t]*\\($\\|;;\\|REM[ \t\n]\\)"
+  "A regexp identifying lines that should be ignored.")
+
+(defvar edmacro--command-line-regexp
+  "Command:[ \t]*\\([^ \t\n]*\\)[ \t]*$"
+  "A regexp identifying the line containing the command name.")
+
+(defvar edmacro--key-line-regexp
+  "Key:\\(.*\\)$"
+  "A regexp identifying the line containing the bound key sequence.")
+
+(defvar edmacro--counter-line-regexp
+  "Counter:[ \t]*\\([^ \t\n]*\\)[ \t]*$"
+  "A regexp identifying the line containing the counter value.")
+
+(defvar edmacro--format-line-regexp
+  "Format:[ \t]*\"\\([^\n]*\\)\"[ \t]*$"
+  "A regexp identifying the line containing the counter format.")
+
+(defvar edmacro--macro-lines-regexp
+  (rx "Macro"
+      (zero-or-one " (most recent line first)")
+      ":"
+      (zero-or-more (any " \t\n")))
+  "A regexp identifying the lines that precede the macro's contents.")
+
 (defun edmacro-finish-edit ()
   (interactive nil edmacro-mode)
   (unless (eq major-mode 'edmacro-mode)
@@ -266,9 +317,9 @@ or nil, use a compact 80-column format."
        (top (point-min)))
     (goto-char top)
     (let ((case-fold-search nil))
-      (while (cond ((looking-at "[ \t]*\\($\\|;;\\|REM[ \t\n]\\)")
+      (while (cond ((looking-at edmacro--skip-line-regexp)
                    t)
-                  ((looking-at "Command:[ \t]*\\([^ \t\n]*\\)[ \t]*$")
+                  ((looking-at edmacro--command-line-regexp)
                    (when edmacro-store-hook
                      (error "\"Command\" line not allowed in this context"))
                    (let ((str (match-string 1)))
@@ -283,7 +334,7 @@ or nil, use a compact 80-column format."
                                     cmd)))
                             (keyboard-quit))))
                    t)
-                  ((looking-at "Key:\\(.*\\)$")
+                  ((looking-at edmacro--key-line-regexp)
                    (when edmacro-store-hook
                      (error "\"Key\" line not allowed in this context"))
                    (let ((key (kbd (match-string 1))))
@@ -303,21 +354,21 @@ or nil, use a compact 80-column format."
                                         (edmacro-format-keys key 1))))
                                 (keyboard-quit))))))
                    t)
-                  ((looking-at "Counter:[ \t]*\\([^ \t\n]*\\)[ \t]*$")
+                  ((looking-at edmacro--counter-line-regexp)
                    (when edmacro-store-hook
                      (error "\"Counter\" line not allowed in this context"))
                    (let ((str (match-string 1)))
                      (unless (equal str "")
                        (setq mac-counter (string-to-number str))))
                    t)
-                  ((looking-at "Format:[ \t]*\"\\([^\n]*\\)\"[ \t]*$")
+                  ((looking-at edmacro--format-line-regexp)
                    (when edmacro-store-hook
                      (error "\"Format\" line not allowed in this context"))
                    (let ((str (match-string 1)))
                      (unless (equal str "")
                        (setq mac-format str)))
                    t)
-                  ((looking-at "Macro:[ \t\n]*")
+                  ((looking-at edmacro--macro-lines-regexp)
                    (goto-char (match-end 0))
                    nil)
                   ((eobp) nil)
@@ -336,7 +387,13 @@ or nil, use a compact 80-column format."
        (when (buffer-name obuf)
          (set-buffer obuf))
        (message "Compiling keyboard macro...")
-       (let ((mac (edmacro-parse-keys str)))
+       (let ((mac (edmacro-parse-keys (if edmacro-reverse-macro-lines
+                                           (with-temp-buffer
+                                             (insert str)
+                                             (reverse-region (point-min)
+                                                             (point-max))
+                                             (buffer-string))
+                                         str))))
          (message "Compiling keyboard macro...done")
          (if store-hook
              (funcall store-hook mac)
@@ -372,6 +429,36 @@ or nil, use a compact 80-column format."
       (insert (edmacro-format-keys key t) "\n")
     (insert (edmacro-format-keys key) " ")))
 
+(defun edmacro-set-macro-to-region-lines (beg end)
+  "Set the macro text to lines of text in the buffer between BEG and END.
+
+Interactively, BEG and END are the beginning and end of the
+region.  If the region does not begin at the start of a line or
+if it does not end at the end of a line, the region is extended
+to include complete lines.  If the region ends at the beginning
+of a line, that final line is excluded."
+  (interactive "*r" edmacro-mode)
+  ;; Use `save-excursion' to restore region if there are any errors.
+  ;; If there are no errors, update the macro text, then go to the
+  ;; beginning of the macro text.
+  (let ((final-position))
+    (save-excursion
+      (goto-char beg)
+      (unless (bolp) (setq beg (pos-bol)))
+      (goto-char end)
+      (unless (or (bolp) (eolp)) (setq end (pos-eol)))
+      (let ((text (buffer-substring beg end)))
+        (goto-char (point-min))
+        (if (not (let ((case-fold-search nil))
+                   (re-search-forward edmacro--macro-lines-regexp nil t)))
+            (user-error "\"Macro:\" line not found")
+          (delete-region (match-end 0)
+                         (point-max))
+          (goto-char (point-max))
+          (insert text)
+          (setq final-position (match-beginning 0)))))
+    (goto-char final-position)))
+
 (defun edmacro-mode ()
   "\\<edmacro-mode-map>Keyboard Macro Editing mode.  Press \
 \\[edmacro-finish-edit] to save and exit.
@@ -393,6 +480,10 @@ or \"none\" for no key bindings.
 You can edit these lines to change the places where the new macro
 is stored.
 
+Press \\[edmacro-set-macro-to-region-lines] to replace the text following the 
\"Macro:\" line
+with the text of the lines overlapping the region of text between
+point and mark.  If that region ends at the beginning of a line,
+that final line is excluded.
 
 Format of keyboard macros during editing:
 
diff --git a/lisp/elec-pair.el b/lisp/elec-pair.el
index e101cbc9e6f..223c7d89773 100644
--- a/lisp/elec-pair.el
+++ b/lisp/elec-pair.el
@@ -153,7 +153,7 @@ return value is considered instead."
   :type '(choice (set (const :tag "Space" ?\s)
                       (const :tag "Tab" ?\t)
                       (const :tag "Newline" ?\n))
-                 (list character)))
+                 (repeat (character :value " "))))
 
 (defvar-local electric-pair-skip-whitespace-function
   #'electric-pair--skip-whitespace
diff --git a/lisp/emacs-lisp/advice.el b/lisp/emacs-lisp/advice.el
index 3265809f592..7fbdd963e0e 100644
--- a/lisp/emacs-lisp/advice.el
+++ b/lisp/emacs-lisp/advice.el
@@ -3131,7 +3131,7 @@ usage: (defadvice FUNCTION (CLASS NAME [POSITION] 
[ARGLIST] FLAG...)
           [DOCSTRING] [INTERACTIVE-FORM]
           BODY...)"
   (declare (doc-string 3) (indent 2)
-           (obsolete "use advice-add or define-advice" "30.1")
+           (obsolete "use `advice-add' or `define-advice'" "30.1")
            (debug (&define name  ;; thing being advised.
                            (name ;; class is [&or "before" "around" "after"
                                  ;;               "activation" "deactivation"]
diff --git a/lisp/emacs-lisp/byte-run.el b/lisp/emacs-lisp/byte-run.el
index 946db634d7e..b18448fac8e 100644
--- a/lisp/emacs-lisp/byte-run.el
+++ b/lisp/emacs-lisp/byte-run.el
@@ -565,6 +565,11 @@ convention was modified."
 Return t if there isn't any."
   (gethash function advertised-signature-table t))
 
+(defun byte-run--constant-obsolete-warning (obsolete-name)
+  (if (memq obsolete-name '(nil t))
+      (error "Can't make `%s' obsolete; did you forget a quote mark?"
+             obsolete-name)))
+
 (defun make-obsolete (obsolete-name current-name when)
   "Make the byte-compiler warn that function OBSOLETE-NAME is obsolete.
 OBSOLETE-NAME should be a function name or macro name (a symbol).
@@ -574,6 +579,7 @@ If CURRENT-NAME is a string, that is the `use instead' 
message
 \(it should end with a period, and not start with a capital).
 WHEN should be a string indicating when the function
 was first made obsolete, for example a date or a release number."
+  (byte-run--constant-obsolete-warning obsolete-name)
   (put obsolete-name 'byte-obsolete-info
        ;; The second entry used to hold the `byte-compile' handler, but
        ;; is not used any more nowadays.
@@ -610,6 +616,7 @@ WHEN should be a string indicating when the variable
 was first made obsolete, for example a date or a release number.
 ACCESS-TYPE if non-nil should specify the kind of access that will trigger
   obsolescence warnings; it can be either `get' or `set'."
+  (byte-run--constant-obsolete-warning obsolete-name)
   (put obsolete-name 'byte-obsolete-variable
        (purecopy (list current-name access-type when)))
   obsolete-name)
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index 30c2480018a..2579d691bdc 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -339,7 +339,8 @@ suppress.  For example, (not free-vars) will suppress the 
`free-vars' warning.
 The t value means \"all non experimental warning types\", and
 excludes the types in `byte-compile--emacs-build-warning-types'.
 A value of `all' really means all."
-  :type `(choice (const :tag "All" t)
+  :type `(choice (const :tag "Default selection" t)
+                 (const :tag "All" all)
                 (set :menu-tag "Some"
                       ,@(mapcar (lambda (x) `(const ,x))
                                 byte-compile-warning-types))))
@@ -1618,57 +1619,6 @@ extra args."
 (dolist (elt '(format message format-message error))
   (put elt 'byte-compile-format-like t))
 
-(defun byte-compile--defcustom-type-quoted (type)
-  "Whether defcustom TYPE contains an accidentally quoted value."
-  ;; Detect mistakes such as (const 'abc).
-  ;; We don't actually follow the syntax for defcustom types, but this
-  ;; should be good enough.
-  (and (consp type)
-       (proper-list-p type)
-       (if (memq (car type) '(const other))
-           (assq 'quote type)
-         (let ((elts (cdr type)))
-           (while (and elts (not (byte-compile--defcustom-type-quoted
-                                  (car elts))))
-             (setq elts (cdr elts)))
-           elts))))
-
-;; Warn if a custom definition fails to specify :group, or :type.
-(defun byte-compile-nogroup-warn (form)
-  (let ((keyword-args (cdr (cdr (cdr (cdr form)))))
-       (name (cadr form)))
-    (when (eq (car-safe name) 'quote)
-      (when (eq (car form) 'custom-declare-variable)
-        (let ((type (plist-get keyword-args :type)))
-         (cond
-           ((not type)
-           (byte-compile-warn-x (cadr name)
-                                "defcustom for `%s' fails to specify type"
-                                 (cadr name)))
-           ((byte-compile--defcustom-type-quoted type)
-           (byte-compile-warn-x
-             (cadr name)
-            "defcustom for `%s' may have accidentally quoted value in type 
`%s'"
-             (cadr name) type)))))
-      (if (and (memq (car form) '(custom-declare-face custom-declare-variable))
-              byte-compile-current-group)
-         ;; The group will be provided implicitly.
-         nil
-       (or (and (eq (car form) 'custom-declare-group)
-                (equal name ''emacs))
-           (plist-get keyword-args :group)
-           (byte-compile-warn-x (cadr name)
-            "%s for `%s' fails to specify containing group"
-            (cdr (assq (car form)
-                       '((custom-declare-group . defgroup)
-                         (custom-declare-face . defface)
-                         (custom-declare-variable . defcustom))))
-            (cadr name)))
-       ;; Update the current group, if needed.
-       (if (and byte-compile-current-file ;Only when compiling a whole file.
-                (eq (car form) 'custom-declare-group))
-           (setq byte-compile-current-group (cadr name)))))))
-
 ;; Warn if the function or macro is being redefined with a different
 ;; number of arguments.
 (defun byte-compile-arglist-warn (name arglist macrop)
@@ -1786,8 +1736,11 @@ Warn if documentation string of FORM is too wide.
 It is too wide if it has any lines longer than the largest of
 `fill-column' and `byte-compile-docstring-max-column'."
   (when (byte-compile-warning-enabled-p 'docstrings)
-    (let ((col (max byte-compile-docstring-max-column fill-column))
-          kind name docs)
+    (let* ((kind nil) (name nil) (docs nil)
+           (prefix (lambda ()
+                     (format "%s%s"
+                             kind
+                             (if name (format-message " `%s' " name) "")))))
       (pcase (car form)
         ((or 'autoload 'custom-declare-variable 'defalias
              'defconst 'define-abbrev-table
@@ -1795,6 +1748,8 @@ It is too wide if it has any lines longer than the 
largest of
              'custom-declare-face)
          (setq kind (nth 0 form))
          (setq name (nth 1 form))
+         (when (and (consp name) (eq (car name) 'quote))
+           (setq name (cadr name)))
          (setq docs (nth 3 form)))
         ('lambda
           (setq kind "")          ; can't be "function", unfortunately
@@ -1806,11 +1761,11 @@ It is too wide if it has any lines longer than the 
largest of
         (setq name (cadr name)))
       (setq name (if name (format " `%s' " name) ""))
       (when (and kind docs (stringp docs))
-        (when (byte-compile--wide-docstring-p docs col)
-          (byte-compile-warn-x
-           name
-           "%s%sdocstring wider than %s characters"
-           kind name col))
+        (let ((col (max byte-compile-docstring-max-column fill-column)))
+          (when (byte-compile--wide-docstring-p docs col)
+            (byte-compile-warn-x
+             name
+             "%sdocstring wider than %s characters" (funcall prefix) col)))
         ;; There's a "naked" ' character before a symbol/list, so it
         ;; should probably be quoted with \=.
         (when (string-match-p (rx (| (in " \t") bol)
@@ -1820,16 +1775,19 @@ It is too wide if it has any lines longer than the 
largest of
                               docs)
           (byte-compile-warn-x
            name
-           (concat "%s%sdocstring has wrong usage of unescaped single quotes"
+           (concat "%sdocstring has wrong usage of unescaped single quotes"
                    " (use \\=%c or different quoting such as %c...%c)")
-           kind name ?' ?` ?'))
+           (funcall prefix) ?' ?` ?'))
         ;; There's a "Unicode quote" in the string -- it should probably
         ;; be an ASCII one instead.
         (when (byte-compile-warning-enabled-p 'docstrings-non-ascii-quotes)
-          (when (string-match-p "\\( \"\\|[ \t]\\|^\\)[‘’]" docs)
+          (when (string-match-p (rx (| " \"" (in " \t") bol)
+                                    (in "‘’"))
+                                docs)
             (byte-compile-warn-x
-             name "%s%sdocstring has wrong usage of \"fancy\" single quotation 
marks"
-             kind name))))))
+             name
+             "%sdocstring uses curved single quotes; use %s instead of ‘...’"
+             (funcall prefix) "`...'"))))))
   form)
 
 ;; If we have compiled any calls to functions which are not known to be
@@ -3738,10 +3696,6 @@ lambda-expression."
 (defun byte-compile-normal-call (form)
   (when (and (symbolp (car form))
              (byte-compile-warning-enabled-p 'callargs (car form)))
-    (if (memq (car form)
-              '(custom-declare-group custom-declare-variable
-                                     custom-declare-face))
-        (byte-compile-nogroup-warn form))
     (byte-compile-callargs-warn form))
   (if byte-compile-generate-call-tree
       (byte-compile-annotate-call-tree form))
@@ -5338,6 +5292,194 @@ binding slots have been popped."
   (pcase form (`(,_ ',var) (byte-compile--declare-var var)))
   (byte-compile-normal-call form))
 
+;; Warn about mistakes in `defcustom', `defface', `defgroup', `define-widget'
+
+(defvar bytecomp--cus-function)
+(defvar bytecomp--cus-name)
+
+(defun bytecomp--cus-warn (form format &rest args)
+  "Emit a warning about a `defcustom' type.
+FORM is used to provide location, `bytecomp--cus-function' and
+`bytecomp--cus-name' for context."
+  (let* ((actual-fun (or (cdr (assq bytecomp--cus-function
+                                    '((custom-declare-group    . defgroup)
+                                     (custom-declare-face     . defface)
+                                     (custom-declare-variable . defcustom))))
+                         bytecomp--cus-function))
+         (prefix (format "in %s%s: "
+                         actual-fun
+                         (if bytecomp--cus-name
+                             (format " for `%s'" bytecomp--cus-name)
+                           ""))))
+    (apply #'byte-compile-warn-x form (concat prefix format) args)))
+
+(defun bytecomp--check-cus-type (type)
+  "Warn about common mistakes in the `defcustom' type TYPE."
+  (let ((invalid-types
+         '(
+           ;; Lisp type predicates, often confused with customisation types:
+           functionp numberp integerp fixnump natnump floatp booleanp
+           characterp listp stringp consp vectorp symbolp keywordp
+           hash-table-p facep
+           ;; other mistakes occasionally seen (oh yes):
+           or and nil t
+           interger intger lits bool boolen constant filename
+           kbd any list-of auto
+           ;; from botched backquoting
+           \, \,@ \`
+           )))
+    (cond
+     ((consp type)
+      (let* ((head (car type))
+             (tail (cdr type)))
+        (while (and (keywordp (car tail)) (cdr tail))
+          (setq tail (cddr tail)))
+        (cond
+         ((plist-member (cdr type) :convert-widget) nil)
+         ((let ((tl tail))
+            (and (not (keywordp (car tail)))
+                 (progn
+                   (while (and tl (not (keywordp (car tl))))
+                     (setq tl (cdr tl)))
+                   (and tl
+                        (progn
+                          (bytecomp--cus-warn
+                           tl "misplaced %s keyword in `%s' type" (car tl) 
head)
+                          t))))))
+         ((memq head '(choice radio))
+          (unless tail
+            (bytecomp--cus-warn type "`%s' without any types inside" head))
+          (let ((clauses tail)
+                (constants nil)
+                (tags nil))
+            (while clauses
+              (let* ((ty (car clauses))
+                     (ty-head (car-safe ty)))
+                (when (and (eq ty-head 'other) (cdr clauses))
+                  (bytecomp--cus-warn ty "`other' not last in `%s'" head))
+                (when (memq ty-head '(const other))
+                  (let ((ty-tail (cdr ty))
+                        (val nil))
+                    (while (and (keywordp (car ty-tail)) (cdr ty-tail))
+                      (when (eq (car ty-tail) :value)
+                        (setq val (cadr ty-tail)))
+                      (setq ty-tail (cddr ty-tail)))
+                    (when ty-tail
+                      (setq val (car ty-tail)))
+                    (when (member val constants)
+                      (bytecomp--cus-warn
+                       ty "duplicated value in `%s': `%S'" head val))
+                    (push val constants)))
+                (let ((tag (and (consp ty) (plist-get (cdr ty) :tag))))
+                  (when (stringp tag)
+                    (when (member tag tags)
+                      (bytecomp--cus-warn
+                       ty "duplicated :tag string in `%s': %S" head tag))
+                    (push tag tags)))
+                (bytecomp--check-cus-type ty))
+              (setq clauses (cdr clauses)))))
+         ((eq head 'cons)
+          (unless (= (length tail) 2)
+            (bytecomp--cus-warn
+             type "`cons' requires 2 type specs, found %d" (length tail)))
+          (dolist (ty tail)
+            (bytecomp--check-cus-type ty)))
+         ((memq head '(list group vector set repeat))
+          (unless tail
+            (bytecomp--cus-warn type "`%s' without type specs" head))
+          (dolist (ty tail)
+            (bytecomp--check-cus-type ty)))
+         ((memq head '(alist plist))
+          (let ((key-tag (memq :key-type (cdr type)))
+                (value-tag (memq :value-type (cdr type))))
+            (when key-tag
+              (bytecomp--check-cus-type (cadr key-tag)))
+            (when value-tag
+              (bytecomp--check-cus-type (cadr value-tag)))))
+         ((memq head '(const other))
+          (let* ((value-tag (memq :value (cdr type)))
+                 (n (length tail))
+                 (val (car tail)))
+            (cond
+             ((or (> n 1) (and value-tag tail))
+              (bytecomp--cus-warn type "`%s' with too many values" head))
+             (value-tag
+              (setq val (cadr value-tag)))
+             ;; ;; This is a useful check but it results in perhaps
+             ;; ;; a bit too many complaints.
+             ;; ((null tail)
+             ;;  (bytecomp--cus-warn
+             ;;   type "`%s' without value is implicitly nil" head))
+             )
+            (when (memq (car-safe val) '(quote function))
+              (bytecomp--cus-warn type "`%s' with quoted value: %S" head 
val))))
+         ((eq head 'quote)
+          (bytecomp--cus-warn type "type should not be quoted: %s" (cadr 
type)))
+         ((memq head invalid-types)
+          (bytecomp--cus-warn type "`%s' is not a valid type" head))
+         ((or (not (symbolp head)) (keywordp head))
+          (bytecomp--cus-warn type "irregular type `%S'" head))
+         )))
+     ((or (not (symbolp type)) (keywordp type))
+      (bytecomp--cus-warn type "irregular type `%S'" type))
+     ((memq type '( list cons group vector choice radio const other
+                    function-item variable-item set repeat restricted-sexp))
+      (bytecomp--cus-warn type "`%s' without arguments" type))
+     ((memq type invalid-types)
+      (bytecomp--cus-warn type "`%s' is not a valid type" type))
+     )))
+
+;; Unified handler for multiple functions with similar arguments:
+;; (NAME SOMETHING DOC KEYWORD-ARGS...)
+(byte-defop-compiler-1 define-widget           bytecomp--custom-declare)
+(byte-defop-compiler-1 custom-declare-group    bytecomp--custom-declare)
+(byte-defop-compiler-1 custom-declare-face     bytecomp--custom-declare)
+(byte-defop-compiler-1 custom-declare-variable bytecomp--custom-declare)
+(defun bytecomp--custom-declare (form)
+  (when (>= (length form) 4)
+    (let* ((name-arg (nth 1 form))
+           (name (and (eq (car-safe name-arg) 'quote)
+                      (symbolp (nth 1 name-arg))
+                      (nth 1 name-arg)))
+           (keyword-args (nthcdr 4 form))
+           (fun (car form))
+           (bytecomp--cus-function fun)
+           (bytecomp--cus-name name))
+
+      ;; Check :type
+      (when (memq fun '(custom-declare-variable define-widget))
+        (let ((type-tag (memq :type keyword-args)))
+          (if (null type-tag)
+              ;; :type only mandatory for `defcustom'
+              (when (eq fun 'custom-declare-variable)
+                (bytecomp--cus-warn form "missing :type keyword parameter"))
+            (let ((dup-type (memq :type (cdr type-tag))))
+              (when dup-type
+                (bytecomp--cus-warn
+                 dup-type "duplicated :type keyword argument")))
+            (let ((type-arg (cadr type-tag)))
+              (when (or (null type-arg)
+                        (eq (car-safe type-arg) 'quote))
+                (bytecomp--check-cus-type (cadr type-arg)))))))
+
+      ;; Check :group
+      (when (cond
+             ((memq fun '(custom-declare-variable custom-declare-face))
+              (not byte-compile-current-group))
+             ((eq fun 'custom-declare-group)
+              (not (eq name 'emacs))))
+        (unless (plist-get keyword-args :group)
+          (bytecomp--cus-warn form "fails to specify containing group")))
+
+      ;; Update current group
+      (when (and name
+                 byte-compile-current-file  ; only when compiling a whole file
+                (eq fun 'custom-declare-group))
+       (setq byte-compile-current-group name))))
+
+  (byte-compile-normal-call form))
+
+
 (put 'function-put 'byte-hunk-handler 'byte-compile-define-symbol-prop)
 (put 'define-symbol-prop 'byte-hunk-handler 'byte-compile-define-symbol-prop)
 (defun byte-compile-define-symbol-prop (form)
diff --git a/lisp/emacs-lisp/checkdoc.el b/lisp/emacs-lisp/checkdoc.el
index 3c4b6baca53..440e133f44b 100644
--- a/lisp/emacs-lisp/checkdoc.el
+++ b/lisp/emacs-lisp/checkdoc.el
@@ -128,6 +128,14 @@
 ;; simple style rules to follow which checkdoc will auto-fix for you.
 ;; `y-or-n-p' and `yes-or-no-p' should also end in "?".
 ;;
+;; Lexical binding:
+;;
+;;   We recommend always using lexical binding in new code, and
+;; converting old code to use it.  Checkdoc warns if you don't have
+;; the recommended string "-*- lexical-binding: t -*-" at the top of
+;; the file.  You can disable this check with the user option
+;; `checkdoc-lexical-binding-flag'.
+;;
 ;; Adding your own checks:
 ;;
 ;;   You can experiment with adding your own checks by setting the
@@ -339,6 +347,12 @@ See Info node `(elisp) Documentation Tips' for background."
   :type 'boolean
   :version "28.1")
 
+(defcustom checkdoc-lexical-binding-flag t
+  "Non-nil means generate warnings if file is not using lexical binding.
+See Info node `(elisp) Converting to Lexical Binding' for more."
+  :type 'boolean
+  :version "30.1")
+
 ;; This is how you can use checkdoc to make mass fixes on the Emacs
 ;; source tree:
 ;;
@@ -1779,7 +1793,7 @@ function,command,variable,option or symbol." ms1))))))
                   (order (and (nth 3 fp) (car (nth 3 fp))))
                   (nocheck (append '("&optional" "&rest" "&key" "&aux"
                                       "&context" "&environment" "&whole"
-                                      "&body" "&allow-other-keys")
+                                      "&body" "&allow-other-keys" "nil")
                                     (nth 3 fp)))
                   (inopts nil))
               (while (and args found (> found last-pos))
@@ -2377,6 +2391,31 @@ Code:, and others referenced in the style guide."
              (point-min) (save-excursion (goto-char (point-min))
                                          (line-end-position))))
         nil))
+      (when checkdoc-lexical-binding-flag
+        (setq
+         err
+         ;; Lexical binding cookie.
+         (if (not (save-excursion
+                    (save-restriction
+                      (goto-char (point-min))
+                      (narrow-to-region (point) (pos-eol))
+                      (re-search-forward
+                       (rx "-*-" (* (* nonl) ";")
+                           (* space) "lexical-binding:" (* space) "t" (* space)
+                           (* ";" (* nonl))
+                           "-*-")
+                       nil t))))
+             (let ((pos (save-excursion (goto-char (point-min))
+                                        (goto-char (pos-eol))
+                                        (point))))
+               (if (checkdoc-y-or-n-p "There is no lexical-binding cookie!  
Add one?")
+                   (progn
+                     (goto-char pos)
+                     (insert "  -*- lexical-binding: t -*-"))
+                 (checkdoc-create-error
+                  "The first line should end with \"-*- lexical-binding: t 
-*-\""
+                  pos (1+ pos) t)))
+           nil)))
       (setq
        err
        (or
@@ -2545,11 +2584,11 @@ Argument END is the maximum bounds to search in."
                  (rx "("
                      (* (syntax whitespace))
                      (group
-                      (or (seq (* (group (or wordchar (syntax symbol))))
+                      (or (seq (* (or wordchar (syntax symbol)))
                                "error")
-                          (seq (* (group (or wordchar (syntax symbol))))
+                          (seq (* (or wordchar (syntax symbol)))
                                (or "y-or-n-p" "yes-or-no-p")
-                               (? (group "-with-timeout")))
+                               (? "-with-timeout"))
                           "checkdoc-autofix-ask-replace"))
                      (+ (any "\n\t ")))
                  end t))
diff --git a/lisp/emacs-lisp/cl-lib.el b/lisp/emacs-lisp/cl-lib.el
index 7fee780a735..f4c1ac85b13 100644
--- a/lisp/emacs-lisp/cl-lib.el
+++ b/lisp/emacs-lisp/cl-lib.el
@@ -170,6 +170,17 @@ to an element already in the list stored in PLACE.
          val
          (and (< end (length str)) (substring str end))))
 
+(gv-define-expander substring
+  (lambda (do place from &optional to)
+    (gv-letplace (getter setter) place
+      (macroexp-let2* nil ((start from) (end to))
+        (funcall do `(substring ,getter ,start ,end)
+                 (lambda (v)
+                   (macroexp-let2 nil v v
+                     `(progn
+                        ,(funcall setter `(cl--set-substring
+                                           ,getter ,start ,end ,v))
+                        ,v))))))))
 
 ;;; Blocks and exits.
 
@@ -560,6 +571,7 @@ of record objects."
     (advice-add 'type-of :around #'cl--old-struct-type-of))
    (t
     (advice-remove 'type-of #'cl--old-struct-type-of))))
+(make-obsolete 'cl-old-struct-compat-mode nil "30.1")
 
 (defun cl-constantly (value)
   "Return a function that takes any number of arguments, but returns VALUE."
diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el
index 7473de3493f..1bd9800bfe6 100644
--- a/lisp/emacs-lisp/cl-macs.el
+++ b/lisp/emacs-lisp/cl-macs.el
@@ -1465,6 +1465,7 @@ For more details, see Info node `(cl)Loop Facility'.
                          (t (setq buf (cl--pop2 cl--loop-args)))))
                  (if (and (consp var) (symbolp (car var)) (symbolp (cdr var)))
                      (setq var1 (car var) var2 (cdr var))
+                   (push (list var nil) loop-for-bindings)
                    (push (list var `(cons ,var1 ,var2)) loop-for-sets))
                  (cl--loop-set-iterator-function
                    'intervals (lambda (body)
@@ -2075,15 +2076,20 @@ info node `(cl) Function Bindings' for details.
 
 \(fn ((FUNC ARGLIST BODY...) ...) FORM...)"
   (declare (indent 1)
-           (debug ((&rest [&or (&define [&name symbolp "@cl-flet@"]
+           ;; The first (symbolp form) case doesn't use `&name' because
+           ;; it's hard to associate this name with the body of the function
+           ;; that `form' will return (bug#65344).
+           ;; We could try and use a `&name' for those cases where the
+           ;; body of the function can be found, (e.g. the form wraps
+           ;; some `prog1/progn/let' around the final `lambda'), but it's
+           ;; not clear it's worth the trouble.
+           (debug ((&rest [&or (symbolp form)
+                               (&define [&name symbolp "@cl-flet@"]
                                         [&name [] gensym] ;Make it unique!
                                         cl-lambda-list
                                         cl-declarations-or-string
                                         [&optional ("interactive" interactive)]
-                                        def-body)
-                               (&define [&name symbolp "@cl-flet@"]
-                                        [&name [] gensym] ;Make it unique!
-                                        def-form)])
+                                        def-body)])
                    cl-declarations body)))
   (let ((binds ()) (newenv macroexpand-all-environment))
     (dolist (binding bindings)
@@ -2937,7 +2943,14 @@ The function's arguments should be treated as immutable.
              ,(if (memq '&key args)
                   `(&whole cl-whole &cl-quote ,@args)
                 (cons '&cl-quote args))
-             ,(format "compiler-macro for inlining `%s'." name)
+             ;; NB.  This will produce incorrect results in some
+             ;; cases, as our coding conventions says that the first
+             ;; line must be a full sentence.  However, if we don't
+             ;; word wrap we will have byte-compiler warnings about
+             ;; overly long docstrings.  So we can't have a perfect
+             ;; result here, and choose to avoid the byte-compiler
+             ;; warnings.
+             ,(internal--format-docstring-line "compiler-macro for `%s'." name)
              (cl--defsubst-expand
               ',argns '(cl-block ,name ,@(cdr (macroexp-parse-body body)))
               nil
@@ -3186,18 +3199,30 @@ To see the documentation for a defined struct type, use
               ;; The arg "cl-x" is referenced by name in e.g. pred-form
              ;; and pred-check, so changing it is not straightforward.
              (push `(,defsym ,accessor (cl-x)
-                       ,(concat
-                         ;; NB.  This will produce incorrect results
-                         ;; in some cases, as our coding conventions
-                         ;; says that the first line must be a full
-                         ;; sentence.  However, if we don't word wrap
-                         ;; we will have byte-compiler warnings about
-                         ;; overly long docstrings.  So we can't have
-                         ;; a perfect result here, and choose to avoid
-                         ;; the byte-compiler warnings.
-                         (internal--format-docstring-line
-                          "Access slot \"%s\" of `%s' struct CL-X." slot name)
-                         (if doc (concat "\n" doc) ""))
+                       ,(let ((long-docstring
+                               (format "Access slot \"%s\" of `%s' struct 
CL-X." slot name)))
+                          (concat
+                           ;; NB.  This will produce incorrect results
+                           ;; in some cases, as our coding conventions
+                           ;; says that the first line must be a full
+                           ;; sentence.  However, if we don't word
+                           ;; wrap we will have byte-compiler warnings
+                           ;; about overly long docstrings.  So we
+                           ;; can't have a perfect result here, and
+                           ;; choose to avoid the byte-compiler
+                           ;; warnings.
+                           (if (>= (length long-docstring)
+                                   (or (bound-and-true-p
+                                        byte-compile-docstring-max-column)
+                                       80))
+                               (concat
+                                (internal--format-docstring-line
+                                 "Access slot \"%s\" of CL-X." slot)
+                                "\n"
+                                (internal--format-docstring-line
+                                 "Struct CL-X is a `%s'." name))
+                             (internal--format-docstring-line long-docstring))
+                           (if doc (concat "\n" doc) "")))
                        (declare (side-effect-free t))
                        ,access-body)
                     forms)
@@ -3273,7 +3298,16 @@ To see the documentation for a defined struct type, use
        (push `(,cldefsym ,cname
                    (&cl-defs (nil ,@descs) ,@args)
                  ,(if (stringp doc) doc
-                    (format "Constructor for objects of type `%s'." name))
+                    ;; NB.  This will produce incorrect results in
+                    ;; some cases, as our coding conventions says that
+                    ;; the first line must be a full sentence.
+                    ;; However, if we don't word wrap we will have
+                    ;; byte-compiler warnings about overly long
+                    ;; docstrings.  So we can't have a perfect result
+                    ;; here, and choose to avoid the byte-compiler
+                    ;; warnings.
+                    (internal--format-docstring-line
+                     "Constructor for objects of type `%s'." name))
                  ,@(if (cl--safe-expr-p `(progn ,@(mapcar #'cl-second descs)))
                        '((declare (side-effect-free t))))
                  (,con-fun ,@make))
@@ -3599,7 +3633,8 @@ possible.  Unlike regular macros, BODY can decide to 
\"punt\" and leave the
 original function call alone by declaring an initial `&whole foo' parameter
 and then returning foo."
   ;; Like `cl-defmacro', but with the `&whole' special case.
-  (declare (debug (&define name cl-macro-list
+  (declare (debug (&define [&name symbolp "@cl-compiler-macro"]
+                           cl-macro-list
                            cl-declarations-or-string def-body))
            (indent 2))
   (let ((p args) (res nil))
diff --git a/lisp/emacs-lisp/cl-preloaded.el b/lisp/emacs-lisp/cl-preloaded.el
index f410270d340..676326980aa 100644
--- a/lisp/emacs-lisp/cl-preloaded.el
+++ b/lisp/emacs-lisp/cl-preloaded.el
@@ -159,7 +159,9 @@ supertypes from the most specific to least specific.")
   (cl-check-type name (satisfies cl--struct-name-p))
   (unless type
     ;; Legacy defstruct, using tagged vectors.  Enable backward compatibility.
-    (cl-old-struct-compat-mode 1))
+    (with-suppressed-warnings ((obsolete cl-old-struct-compat-mode))
+      (message "cl-old-struct-compat-mode is obsolete!")
+      (cl-old-struct-compat-mode 1)))
   (if (eq type 'record)
       ;; Defstruct using record objects.
       (setq type nil))
diff --git a/lisp/emacs-lisp/cl-print.el b/lisp/emacs-lisp/cl-print.el
index 06c4f092d15..9c2a531dff5 100644
--- a/lisp/emacs-lisp/cl-print.el
+++ b/lisp/emacs-lisp/cl-print.el
@@ -287,12 +287,26 @@ into a button whose action shows the function's 
disassembly.")
 (cl-defmethod cl-print-object-contents ((object cl-structure-object) start 
stream)
   (cl-print--struct-contents object start stream)) ;FIXME: η-redex!
 
+(defvar cl-print-string-length nil
+  "Maximum length of string to print before abbreviating.
+A value of nil means no limit.
+
+When Emacs abbreviates a string, it prints the first
+`cl-print-string-length' characters of the string, followed by
+\"...\".  You can type RET, or click on this ellipsis to expand
+the string.
+
+This variable has effect only in the `cl-prin*' functions, not in
+primitives such as `prin1'.")
+
 (cl-defmethod cl-print-object ((object string) stream)
   (unless stream (setq stream standard-output))
   (let* ((has-properties (or (text-properties-at 0 object)
                              (next-property-change 0 object)))
          (len (length object))
-         (limit (if (natnump print-length) (min print-length len) len)))
+         (limit (if (natnump cl-print-string-length)
+                    (min cl-print-string-length len)
+                  len)))
     (if (and has-properties
              cl-print--depth
              (natnump print-level)
@@ -351,8 +365,9 @@ into a button whose action shows the function's 
disassembly.")
   (let* ((len (length object)))
     (if (atom start)
         ;; Print part of the string.
-        (let* ((limit (if (natnump print-length)
-                          (min (+ start print-length) len) len))
+        (let* ((limit (if (natnump cl-print-string-length)
+                          (min (+ start cl-print-string-length) len)
+                        len))
                (substr (substring-no-properties object start limit))
                (printed (prin1-to-string substr))
                (trimmed (substring printed 1 -1)))
@@ -560,14 +575,14 @@ node `(elisp)Output Variables'."
 (defun cl-print-to-string-with-limit (print-function value limit)
   "Return a string containing a printed representation of VALUE.
 Attempt to get the length of the returned string under LIMIT
-characters with appropriate settings of `print-level' and
-`print-length.'  Use PRINT-FUNCTION to print, which should take
-the arguments VALUE and STREAM and which should respect
-`print-length' and `print-level'.  LIMIT may be nil or zero in
-which case PRINT-FUNCTION will be called with `print-level' and
-`print-length' bound to nil, and it can also be t in which case
-PRINT-FUNCTION will be called with the current values of `print-level'
-and `print-length'.
+characters with appropriate settings of `print-level',
+`print-length.', and `cl-print-string-length'.  Use
+PRINT-FUNCTION to print, which should take the arguments VALUE
+and STREAM and which should respect `print-length',
+`print-level', and `cl-print-string-length'.  LIMIT may be nil or
+zero in which case PRINT-FUNCTION will be called with these
+settings bound to nil, and it can also be t in which case
+PRINT-FUNCTION will be called with their current values.
 
 Use this function with `cl-prin1' to print an object,
 abbreviating it with ellipses to fit within a size limit."
@@ -576,13 +591,18 @@ abbreviating it with ellipses to fit within a size limit."
   ;; limited, if you increase print-level here, add more depth in
   ;; call_debugger (bug#31919).
   (let* ((print-length (cond
-                        ((null limit) nil)
                         ((eq limit t) print-length)
+                        ((or (null limit) (zerop limit)) nil)
                         (t (min limit 50))))
          (print-level (cond
-                        ((null limit) nil)
                         ((eq limit t) print-level)
+                        ((or (null limit) (zerop limit)) nil)
                         (t (min 8 (truncate (log limit))))))
+         (cl-print-string-length
+          (cond
+           ((eq limit t) cl-print-string-length)
+           ((or (null limit) (zerop limit)) nil)
+           (t (max 0 (- limit 3)))))
          (delta-length (when (natnump limit)
                          (max 1 (truncate (/ print-length print-level))))))
     (with-temp-buffer
@@ -598,7 +618,10 @@ abbreviating it with ellipses to fit within a size limit."
             (let* ((ratio (/ result limit))
                    (delta-level (max 1 (min (- print-level 2) ratio))))
               (cl-decf print-level delta-level)
-              (cl-decf print-length (* delta-length delta-level)))))))))
+              (cl-decf print-length (* delta-length delta-level))
+              (when cl-print-string-length
+                (cl-decf cl-print-string-length
+                         (ceiling cl-print-string-length 4.0))))))))))
 
 (provide 'cl-print)
 ;;; cl-print.el ends here
diff --git a/lisp/emacs-lisp/comp.el b/lisp/emacs-lisp/comp.el
index b8b086b6c16..9da9f1544f6 100644
--- a/lisp/emacs-lisp/comp.el
+++ b/lisp/emacs-lisp/comp.el
@@ -1861,7 +1861,7 @@ SP-DELTA is the stack adjustment."
 (eval-when-compile
   (defun comp-op-to-fun (x)
     "Given the LAP op strip \"byte-\" to have the subr name."
-    (intern (replace-regexp-in-string "byte-" "" x)))
+    (intern (string-replace "byte-" "" x)))
 
   (defun comp-body-eff (body op-name sp-delta)
     "Given the original BODY, compute the effective one.
@@ -2934,7 +2934,7 @@ blocks."
                           finger2 (comp-block-post-num b2))))
                 b1))
             (first-processed (l)
-              (if-let ((p (cl-find-if (lambda (p) (comp-block-idom p)) l)))
+              (if-let ((p (cl-find-if #'comp-block-idom l)))
                   p
                 (signal 'native-ice '("can't find first preprocessed")))))
 
@@ -3773,13 +3773,10 @@ Prepare every function for final compilation and drive 
the C back-end."
     (comp--compile-ctxt-to-file name)))
 
 (defun comp-final1 ()
-  (let (compile-result)
-    (comp--init-ctxt)
-    (unwind-protect
-        (setf compile-result
-              (comp-compile-ctxt-to-file (comp-ctxt-output comp-ctxt)))
-      (and (comp--release-ctxt)
-           compile-result))))
+  (comp--init-ctxt)
+  (unwind-protect
+      (comp-compile-ctxt-to-file (comp-ctxt-output comp-ctxt))
+    (comp--release-ctxt)))
 
 (defvar comp-async-compilation nil
   "Non-nil while executing an asynchronous native compilation.")
@@ -4193,7 +4190,8 @@ the deferred compilation mechanism."
              (symbols-with-pos-enabled t)
              ;; Have byte compiler signal an error when compilation fails.
              (byte-compile-debug t)
-             (comp-ctxt (make-comp-ctxt :output output
+             (comp-ctxt (make-comp-ctxt :output (when output
+                                                  (expand-file-name output))
                                         :with-late-load with-late-load)))
         (comp-log "\n\n" 1)
         (unwind-protect
@@ -4521,8 +4519,10 @@ inferred from the code itself by the native compiler; if 
it is
         type-spec )
     (when-let ((res (gethash function comp-known-func-cstr-h)))
       (setf type-spec (comp-cstr-to-type-spec res)))
-    (let ((f (symbol-function function)))
-      (when (and (null type-spec)
+    (let ((f (and (symbolp function)
+                  (symbol-function function))))
+      (when (and f
+                 (null type-spec)
                  (subr-native-elisp-p f))
         (setf kind 'inferred
               type-spec (subr-type f))))
diff --git a/lisp/emacs-lisp/disass.el b/lisp/emacs-lisp/disass.el
index dd59a2e02e1..73777d7e701 100644
--- a/lisp/emacs-lisp/disass.el
+++ b/lisp/emacs-lisp/disass.el
@@ -92,17 +92,16 @@ redefine OBJECT if it is a symbol."
                  (subr-native-elisp-p obj))
             (progn
               (require 'comp)
-              (call-process "objdump" nil (current-buffer) t "-S"
-                            (native-comp-unit-file (subr-native-comp-unit 
obj)))
+              (let ((eln (native-comp-unit-file (subr-native-comp-unit obj))))
+                (if (file-exists-p eln)
+                    (call-process "objdump" nil (current-buffer) t "-S" eln)
+                  (error "Missing eln file for #<subr %s>" name)))
               (goto-char (point-min))
-              (re-search-forward (concat "^.*"
+              (re-search-forward (concat "^.*<_?"
                                          (regexp-quote
-                                          (concat "<"
-                                                  (when (eq system-type 
'darwin)
-                                                    "_")
-                                                  (comp-c-func-name
-                                                   (subr-name obj) "F" t)
-                                                  ">:"))))
+                                          (comp-c-func-name
+                                           (subr-name obj) "F" t))
+                                         ">:"))
               (beginning-of-line)
               (delete-region (point-min) (point))
               (when (re-search-forward "^.*<.*>:" nil t 2)
diff --git a/lisp/emacs-lisp/edebug.el b/lisp/emacs-lisp/edebug.el
index 9a06807bcdc..aa68978f6d6 100644
--- a/lisp/emacs-lisp/edebug.el
+++ b/lisp/emacs-lisp/edebug.el
@@ -1544,9 +1544,7 @@ contains a circular object."
 (defun edebug-list-form (cursor)
   ;; Return an instrumented form built from the list form.
   ;; The after offset will be left in the cursor after processing the form.
-  (let ((head (edebug-top-element-required cursor "Expected elements"))
-       ;; Prevent backtracking whenever instrumenting.
-       (edebug-gate t))
+  (let ((head (edebug-top-element-required cursor "Expected elements")))
     ;; Skip the first offset.
     (edebug-set-cursor cursor (edebug-cursor-expressions cursor)
                       (cdr (edebug-cursor-offsets cursor)))
@@ -2469,12 +2467,52 @@ MSG is printed after `::::} '."
   (setf (cdr (assq 'edebug edebug-behavior-alist))
         '(edebug-default-enter edebug-fast-before edebug-fast-after)))
 
-(defalias 'edebug-before nil
+;; The following versions of `edebug-before' and `edebug-after' exist
+;; to handle the error which occurs if either of them gets called
+;; without an enclosing `edebug-enter'.  This can happen, for example,
+;; when a macro mistakenly has a `form' element in its edebug spec,
+;; and it additionally, at macro-expansion time, calls `eval',
+;; `apply', or `funcall' (etc.) on the corresponding argument.  This
+;; is intended to fix bug#65620.
+
+(defun edebug-b/a-error (func)
+  "Throw an error for an invalid call of FUNC.
+FUNC is expected to be `edebug-before' or `edebug-after'."
+  (let (this-macro
+        (n 0)
+        bt-frame)
+    (while (and (setq bt-frame (backtrace-frame n))
+                (not (and (car bt-frame)
+                          (memq (cadr bt-frame)
+                                '(macroexpand macroexpand-1)))))
+      (setq n (1+ n)))
+    (when bt-frame
+      (setq this-macro (caaddr bt-frame)))
+
+    (error
+     (concat "Invalid call to `" (symbol-name func) "'"
+             (if this-macro
+                 (concat ".  Is the edebug spec for `"
+                         (symbol-name this-macro)
+                         "' correct?")
+               ""   ; Not sure this case is possible (ACM, 2023-09-02)
+               )))))
+
+(defun edebug-before (_before-index)
   "Function called by Edebug before a form is evaluated.
-See `edebug-behavior-alist' for implementations.")
-(defalias 'edebug-after nil
+See `edebug-behavior-alist' for other implementations.  This
+version of `edebug-before' gets called when edebug is not yet set
+up.  `edebug-enter' binds the function cell to a real function
+when edebug becomes active."
+  (edebug-b/a-error 'edebug-before))
+
+(defun edebug-after (_before-index _after-index _form)
   "Function called by Edebug after a form is evaluated.
-See `edebug-behavior-alist' for implementations.")
+See `edebug-behavior-alist' for other implementations.  This
+version of `edebug-after' gets called when edebug is not yet set
+up.  `edebug-enter' binds the function cell to a real function
+when edebug becomes active."
+  (edebug-b/a-error 'edebug-after))
 
 (defun edebug--update-coverage (after-index value)
   (let ((old-result (aref edebug-coverage after-index)))
diff --git a/lisp/emacs-lisp/eieio.el b/lisp/emacs-lisp/eieio.el
index ccdb52d6a1f..39a5fd5b19c 100644
--- a/lisp/emacs-lisp/eieio.el
+++ b/lisp/emacs-lisp/eieio.el
@@ -449,16 +449,13 @@ If EXTRA, include that in the string returned to 
represent the symbol."
 
 (defun eieio-class-parents (class)
   ;; FIXME: What does "(overload of variable)" mean here?
-  "Return parent classes to CLASS.  (overload of variable).
-
-The CLOS function `class-direct-superclasses' is aliased to this function."
+  "Return parent classes to CLASS.  (overload of variable)."
   (eieio--class-parents (eieio--full-class-object class)))
 
 (define-obsolete-function-alias 'class-parents #'eieio-class-parents "24.4")
 
 (defun eieio-class-children (class)
-  "Return child classes to CLASS.
-The CLOS function `class-direct-subclasses' is aliased to this function."
+  "Return child classes to CLASS."
   (cl-check-type class class)
   (eieio--class-children (cl--find-class class)))
 (define-obsolete-function-alias
diff --git a/lisp/emacs-lisp/elint.el b/lisp/emacs-lisp/elint.el
index 9812c663ea8..700f007d6b4 100644
--- a/lisp/emacs-lisp/elint.el
+++ b/lisp/emacs-lisp/elint.el
@@ -79,16 +79,16 @@ are as follows, and suppress messages about the indicated 
features:
   empty-let           - let-bindings with empty variable lists"
   :type '(choice (const :tag "Don't suppress any warnings" nil)
                 (repeat :tag "List of issues to ignore"
-                        (choice (const undefined-functions
-                                       :tag "Calls to unknown functions")
-                                (const unbound-reference
-                                       :tag "Reference to unknown variables")
-                                (const unbound-assignment
-                                       :tag "Assignment to unknown variables")
-                                (const macro-expansion
-                                       :tag "Failure to expand macros")
-                                (const empty-let
-                                       :tag "Let-binding with empty 
varlist"))))
+                         (choice (const :tag "Calls to unknown functions"
+                                        undefined-functions)
+                                 (const :tag "Reference to unknown variables"
+                                        unbound-reference)
+                                 (const :tag "Assignment to unknown variables"
+                                        unbound-assignment)
+                                 (const :tag "Failure to expand macros"
+                                        macro-expansion)
+                                 (const :tag "Let-binding with empty varlist"
+                                        empty-let))))
   :safe (lambda (value) (or (null value)
                            (and (listp value)
                                 (equal value
diff --git a/lisp/emacs-lisp/ert.el b/lisp/emacs-lisp/ert.el
index 0c17a603e8b..0ed2857f468 100644
--- a/lisp/emacs-lisp/ert.el
+++ b/lisp/emacs-lisp/ert.el
@@ -34,17 +34,18 @@
 ;; `ert-run-tests-batch-and-exit' for non-interactive use.
 ;;
 ;; The body of `ert-deftest' forms resembles a function body, but the
-;; additional operators `should', `should-not', `should-error' and
-;; `skip-unless' are available.  `should' is similar to cl's `assert',
-;; but signals a different error when its condition is violated that
-;; is caught and processed by ERT.  In addition, it analyzes its
-;; argument form and records information that helps debugging
-;; (`cl-assert' tries to do something similar when its second argument
-;; SHOW-ARGS is true, but `should' is more sophisticated).  For
-;; information on `should-not' and `should-error', see their
-;; docstrings.  `skip-unless' skips the test immediately without
-;; processing further, this is useful for checking the test
-;; environment (like availability of features, external binaries, etc).
+;; additional operators `should', `should-not', `should-error',
+;; `skip-when' and `skip-unless' are available.  `should' is similar
+;; to cl's `assert', but signals a different error when its condition
+;; is violated that is caught and processed by ERT.  In addition, it
+;; analyzes its argument form and records information that helps
+;; debugging (`cl-assert' tries to do something similar when its
+;; second argument SHOW-ARGS is true, but `should' is more
+;; sophisticated).  For information on `should-not' and
+;; `should-error', see their docstrings.  The `skip-when' and
+;; `skip-unless' forms skip the test immediately, which is useful for
+;; checking the test environment (like availability of features,
+;; external binaries, etc).
 ;;
 ;; See ERT's Info manual `(ert) Top' as well as the docstrings for
 ;; more details.  To see some examples of tests written in ERT, see
@@ -194,8 +195,8 @@ and the body."
 BODY is evaluated as a `progn' when the test is run.  It should
 signal a condition on failure or just return if the test passes.
 
-`should', `should-not', `should-error' and `skip-unless' are
-useful for assertions in BODY.
+`should', `should-not', `should-error', `skip-when', and
+`skip-unless' are useful for assertions in BODY.
 
 Use `ert' to run tests interactively.
 
@@ -227,7 +228,8 @@ in batch mode, an error is signaled.
                (tags nil tags-supplied-p))
          body)
         (ert--parse-keys-and-body docstring-keys-and-body)
-      `(cl-macrolet ((skip-unless (form) `(ert--skip-unless ,form)))
+      `(cl-macrolet ((skip-when (form) `(ert--skip-when ,form))
+                     (skip-unless (form) `(ert--skip-unless ,form)))
          (ert-set-test ',name
                        (make-ert-test
                         :name ',name
@@ -464,6 +466,15 @@ failed."
                        (list
                         :fail-reason "did not signal an error")))))))))
 
+(cl-defmacro ert--skip-when (form)
+  "Evaluate FORM.  If it returns t, skip the current test.
+Errors during evaluation are caught and handled like t."
+  (declare (debug t))
+  (ert--expand-should `(skip-when ,form) form
+                      (lambda (inner-form form-description-form _value-var)
+                        `(when (condition-case nil ,inner-form (t t))
+                           (ert-skip ,form-description-form)))))
+
 (cl-defmacro ert--skip-unless (form)
   "Evaluate FORM.  If it returns nil, skip the current test.
 Errors during evaluation are caught and handled like nil."
diff --git a/lisp/emacs-lisp/gv.el b/lisp/emacs-lisp/gv.el
index a5e29dd5e3b..5d31253fe2d 100644
--- a/lisp/emacs-lisp/gv.el
+++ b/lisp/emacs-lisp/gv.el
@@ -411,7 +411,6 @@ The return value is the last VAL in the list.
 (gv-define-setter buffer-local-value (val var buf)
   (macroexp-let2 nil v val
     `(with-current-buffer ,buf (set (make-local-variable ,var) ,v))))
-(make-obsolete-generalized-variable 'buffer-local-value nil "29.1")
 
 (gv-define-expander alist-get
   (lambda (do key alist &optional default remove testfn)
@@ -822,17 +821,5 @@ REF must have been previously obtained with `gv-ref'."
                      ((eq ,getter ,val) ,(funcall setter `(not ,val))))))))))
 (make-obsolete-generalized-variable 'eq nil "29.1")
 
-(gv-define-expander substring
-  (lambda (do place from &optional to)
-    (gv-letplace (getter setter) place
-      (macroexp-let2* nil ((start from) (end to))
-        (funcall do `(substring ,getter ,start ,end)
-                 (lambda (v)
-                   (macroexp-let2 nil v v
-                     `(progn
-                        ,(funcall setter `(cl--set-substring
-                                           ,getter ,start ,end ,v))
-                        ,v))))))))
-
 (provide 'gv)
 ;;; gv.el ends here
diff --git a/lisp/emacs-lisp/macroexp.el b/lisp/emacs-lisp/macroexp.el
index 8e35d1c205e..242c71e65ad 100644
--- a/lisp/emacs-lisp/macroexp.el
+++ b/lisp/emacs-lisp/macroexp.el
@@ -340,7 +340,34 @@ Assumes the caller has bound 
`macroexpand-all-environment'."
              (let ((defining-symbol def))
                (macroexp--all-forms form 2)))
             (`(cond . ,clauses)
-             (macroexp--cons fn (macroexp--all-clauses clauses) form))
+             ;; Check for rubbish clauses at the end before macro-expansion,
+             ;; to avoid nuisance warnings from clauses that become
+             ;; unconditional through that process.
+             ;; FIXME: this strategy is defeated by forced `macroexpand-all',
+             ;; such as in `cl-flet'.  Haven't seen that in the wild, though.
+             (let ((default-tail nil)
+                   (n 0)
+                   (rest clauses))
+               (while rest
+                 (let ((c (car-safe (car rest))))
+                   (when (cond ((consp c) (and (memq (car c) '(quote function))
+                                               (cadr c)))
+                               ((symbolp c) (or (eq c t) (keywordp c)))
+                               (t t))
+                     ;; This is unquestionably a default clause.
+                     (setq default-tail (cdr rest))
+                     (setq clauses (take (1+ n) clauses))  ; trim the tail
+                     (setq rest nil)))
+                 (setq n (1+ n))
+                 (setq rest (cdr rest)))
+               (let ((expanded-form
+                      (macroexp--cons fn (macroexp--all-clauses clauses) 
form)))
+                 (if default-tail
+                     (macroexp-warn-and-return
+                      (format-message
+                       "Useless clause following default `cond' clause")
+                      expanded-form '(suspicious cond) t default-tail)
+                   expanded-form))))
             (`(condition-case . ,(or `(,err ,body . ,handlers) 
pcase--dontcare))
              (let ((exp-body (macroexp--expand-all body)))
                (if handlers
@@ -517,12 +544,17 @@ definitions to shadow the loaded ones for use in file 
byte-compilation."
 (defun macroexp-parse-body (body)
   "Parse a function BODY into (DECLARATIONS . EXPS)."
   (let ((decls ()))
-    (while (and (cdr body)
-                (let ((e (car body)))
-                  (or (stringp e)
-                      (memq (car-safe e)
-                            '(:documentation declare interactive 
cl-declare)))))
-      (push (pop body) decls))
+    ;; If there is only a string literal with nothing following, we
+    ;; consider this to be part of the body (the return value) rather
+    ;; than a declaration at this point.
+    (unless (and (null (cdr body)) (stringp (car body)))
+      (while
+          (and body
+               (let ((e (car body)))
+                 (or (stringp e)
+                     (memq (car-safe e)
+                           '(:documentation declare interactive cl-declare)))))
+        (push (pop body) decls)))
     (cons (nreverse decls) body)))
 
 (defun macroexp-progn (exps)
@@ -804,7 +836,7 @@ test of free variables in the following ways:
           (if full-p
               (macroexpand--all-toplevel form)
             (macroexpand form)))
-      (error
+      ((debug error)
        ;; Hopefully this shouldn't happen thanks to the cycle detection,
        ;; but in case it does happen, let's catch the error and give the
        ;; code a chance to macro-expand later.
diff --git a/lisp/emacs-lisp/map.el b/lisp/emacs-lisp/map.el
index b55eb431668..b46c74343c0 100644
--- a/lisp/emacs-lisp/map.el
+++ b/lisp/emacs-lisp/map.el
@@ -8,6 +8,9 @@
 ;; Version: 3.3.1
 ;; Package-Requires: ((emacs "26"))
 
+;; This is a GNU ELPA :core package.  Avoid functionality that is not
+;; compatible with the version of Emacs recorded above.
+
 ;; This file is part of GNU Emacs.
 
 ;; GNU Emacs is free software: you can redistribute it and/or modify
diff --git a/lisp/emacs-lisp/oclosure.el b/lisp/emacs-lisp/oclosure.el
index 8ed923a1c16..851cc50c73a 100644
--- a/lisp/emacs-lisp/oclosure.el
+++ b/lisp/emacs-lisp/oclosure.el
@@ -3,6 +3,7 @@
 ;; Copyright (C) 2021-2023 Free Software Foundation, Inc.
 
 ;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
+;; Package: emacs
 
 ;; This file is part of GNU Emacs.
 
diff --git a/lisp/emacs-lisp/package-vc.el b/lisp/emacs-lisp/package-vc.el
index 747fe696204..a8393cb7e75 100644
--- a/lisp/emacs-lisp/package-vc.el
+++ b/lisp/emacs-lisp/package-vc.el
@@ -413,30 +413,36 @@ otherwise it's assumed to be an Info file."
          (default-directory (package-desc-dir pkg-desc))
          (docs-directory (file-name-directory (expand-file-name file)))
          (output (expand-file-name (format "%s.info" pkg-name)))
+         (log-buffer (get-buffer-create (format " *package-vc doc: %s*" 
pkg-name)))
          clean-up)
-    (when (string-match-p "\\.org\\'" file)
-      (require 'ox)
-      (require 'ox-texinfo)
-      (with-temp-buffer
-        (insert-file-contents file)
-        (setq file (make-temp-file "ox-texinfo-"))
-        (let ((default-directory docs-directory))
-          (org-export-to-file 'texinfo file))
-        (setq clean-up t)))
-    (with-current-buffer (get-buffer-create " *package-vc doc*")
-      (erase-buffer)
-      (cond
-       ((/= 0 (call-process "makeinfo" nil t nil
-                            "-I" docs-directory
-                            "--no-split" file
-                            "-o" output))
-        (message "Failed to build manual %s, see buffer %S"
-                 file (buffer-name)))
-       ((/= 0 (call-process "install-info" nil t nil
-                            output (expand-file-name "dir")))
-        (message "Failed to install manual %s, see buffer %S"
-                 output (buffer-name)))
-       ((kill-buffer))))
+    (with-current-buffer log-buffer
+      (erase-buffer))
+    (condition-case err
+        (progn
+          (when (string-match-p "\\.org\\'" file)
+            (require 'ox)
+            (require 'ox-texinfo)
+            (with-temp-buffer
+              (insert-file-contents file)
+              (setq file (make-temp-file "ox-texinfo-"))
+              (let ((default-directory docs-directory))
+                (org-export-to-file 'texinfo file))
+              (setq clean-up t)))
+          (cond
+           ((/= 0 (call-process "makeinfo" nil log-buffer nil
+                                "-I" docs-directory
+                                "--no-split" file
+                                "-o" output))
+            (message "Failed to build manual %s, see buffer %S"
+                     file (buffer-name)))
+           ((/= 0 (call-process "install-info" nil log-buffer nil
+                                output (expand-file-name "dir")))
+            (message "Failed to install manual %s, see buffer %S"
+                     output (buffer-name)))
+           ((kill-buffer log-buffer))))
+      (error (with-current-buffer log-buffer
+               (insert (error-message-string err)))
+             (message "Failed to export org manual for %s, see buffer %S" 
pkg-name log-buffer)))
     (when clean-up
       (delete-file file))))
 
diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el
index e1172d69bf0..e23a61c58a4 100644
--- a/lisp/emacs-lisp/package.el
+++ b/lisp/emacs-lisp/package.el
@@ -1982,8 +1982,11 @@ Used to populate `package-selected-packages'."
 
 (defun package--save-selected-packages (&optional value)
   "Set and save `package-selected-packages' to VALUE."
-  (when value
-    (setq package-selected-packages value))
+  (when (or value after-init-time)
+    ;; It is valid to set it to nil, for example when the last package
+    ;; is uninstalled.  But it shouldn't be done at init time, to
+    ;; avoid overwriting configurations that haven't yet been loaded.
+    (setq package-selected-packages (sort value #'string<)))
   (if after-init-time
       (customize-save-variable 'package-selected-packages 
package-selected-packages)
     (add-hook 'after-init-hook #'package--save-selected-packages)))
@@ -2484,7 +2487,9 @@ Clean-up the corresponding .eln files if Emacs is native
 compiled."
   (when (featurep 'native-compile)
     (cl-loop
-     for file in (directory-files-recursively dir "\\.el\\'")
+     for file in (directory-files-recursively dir
+                                              ;; Exclude lockfiles
+                                              (rx bos (or (and "." (not "#")) 
(not ".")) (* nonl) ".el" eos))
      do (comp-clean-up-stale-eln (comp-el-to-eln-filename file))))
   (if (file-symlink-p (directory-file-name dir))
       (delete-file (directory-file-name dir))
@@ -2516,8 +2521,12 @@ If NOSAVE is non-nil, the package is not removed from
                                            nil t)))
        (list (cdr (assoc package-name package-table))
              current-prefix-arg nil))))
-  (let ((dir (package-desc-dir pkg-desc))
-        (name (package-desc-name pkg-desc))
+  (let* ((dir (package-desc-dir pkg-desc))
+         (name (package-desc-name pkg-desc))
+         (new-package-alist (let ((pkgs (assq name package-alist)))
+                              (if (null (remove pkg-desc (cdr pkgs)))
+                                  (remq pkgs package-alist)
+                                package-alist)))
         pkg-used-elsewhere-by)
     ;; If the user is trying to delete this package, they definitely
     ;; don't want it marked as selected, so we remove it from
@@ -2536,7 +2545,8 @@ If NOSAVE is non-nil, the package is not removed from
                   (package-desc-full-name pkg-desc)))
           ((and (null force)
                 (setq pkg-used-elsewhere-by
-                      (package--used-elsewhere-p pkg-desc)))
+                      (let ((package-alist new-package-alist))
+                        (package--used-elsewhere-p pkg-desc)))) ;See bug#65475
            ;; Don't delete packages used as dependency elsewhere.
            (error "Package `%s' is used by `%s' as dependency, not deleting"
                   (package-desc-full-name pkg-desc)
@@ -2557,10 +2567,7 @@ If NOSAVE is non-nil, the package is not removed from
                (when (file-exists-p file)
                  (delete-file file))))
            ;; Update package-alist.
-           (let ((pkgs (assq name package-alist)))
-             (delete pkg-desc pkgs)
-             (unless (cdr pkgs)
-               (setq package-alist (delq pkgs package-alist))))
+           (setq package-alist new-package-alist)
            (package--quickstart-maybe-refresh)
            (message "Package `%s' deleted."
                     (package-desc-full-name pkg-desc))))))
@@ -2805,7 +2812,7 @@ Helper function for `describe-package'."
          (incompatible-reason (package--incompatible-p desc))
          (signed (if desc (package-desc-signed desc)))
          (maintainers (or (cdr (assoc :maintainers extras))
-                          (cdr (assoc :maintainer extras))))
+                          (list (cdr (assoc :maintainer extras)))))
          (authors (cdr (assoc :authors extras)))
          (news (and-let* (pkg-dir
                           ((not built-in))
diff --git a/lisp/emacs-lisp/pp.el b/lisp/emacs-lisp/pp.el
index 50e0e7d1da4..a93e634c685 100644
--- a/lisp/emacs-lisp/pp.el
+++ b/lisp/emacs-lisp/pp.el
@@ -262,7 +262,7 @@ Non-interactively can also be called with a single 
argument, in which
 case that argument will be inserted pretty-printed at point."
   (interactive "r")
   (if (null end) (pp--object beg #'pp-29)
-    (save-restriction beg end
+    (with-restriction beg end
       (goto-char (point-min))
       (while (not (eobp))
         (cond
diff --git a/lisp/emacs-lisp/rmc.el b/lisp/emacs-lisp/rmc.el
index bfd7434be9a..45e2bbf3831 100644
--- a/lisp/emacs-lisp/rmc.el
+++ b/lisp/emacs-lisp/rmc.el
@@ -126,7 +126,8 @@
 (defun read-multiple-choice (prompt choices &optional help-string show-help
                                     long-form)
   "Ask user to select an entry from CHOICES, prompting with PROMPT.
-This function allows to ask the user a multiple-choice question.
+This function is used to ask the user a question with multiple
+choices.
 
 CHOICES should be a list of the form (KEY NAME [DESCRIPTION]).
 KEY is a character the user should type to select the entry.
diff --git a/lisp/emacs-lisp/seq.el b/lisp/emacs-lisp/seq.el
index 6917c541f2e..346250c1d35 100644
--- a/lisp/emacs-lisp/seq.el
+++ b/lisp/emacs-lisp/seq.el
@@ -4,7 +4,7 @@
 
 ;; Author: Nicolas Petton <nicolas@petton.fr>
 ;; Keywords: sequences
-;; Version: 2.23
+;; Version: 2.24
 ;; Package: seq
 
 ;; Maintainer: emacs-devel@gnu.org
@@ -38,9 +38,6 @@
 ;; the sequence as their second argument.  All other functions take
 ;; the sequence as their first argument.
 ;;
-;; While seq.el version 1.8 is in GNU ELPA for convenience, seq.el
-;; version 2.0 requires Emacs>=25.1.
-;;
 ;; seq.el can be extended to support new type of sequences.  Here are
 ;; the generic functions that must be implemented by new seq types:
 ;; - `seq-elt'
@@ -51,11 +48,17 @@
 ;; - `seq-into-sequence'
 ;; - `seq-copy'
 ;; - `seq-into'
-;;
-;; All functions are tested in test/lisp/emacs-lisp/seq-tests.el
 
 ;;; Code:
 
+;; Note regarding the `seq' package on GNU ELPA:
+;;
+;; It was decided not to bother upgrading seq beyond 2.24 on GNU ELPA.
+;; The main purpose of the GNU ELPA package was to encourage adoption
+;; and accommodate changes more easily, but it's mature enough that
+;; changes are fairly slow.  Thus, we can now rely on "the usual"
+;; solutions to deal with compatibility issues.  (Bug#60990)
+
 (eval-when-compile (require 'cl-generic))
 
 ;; We used to use some sequence functions from cl-lib, but this
diff --git a/lisp/emacs-lisp/shorthands.el b/lisp/emacs-lisp/shorthands.el
index bb18ac33497..82200ab88e9 100644
--- a/lisp/emacs-lisp/shorthands.el
+++ b/lisp/emacs-lisp/shorthands.el
@@ -4,6 +4,7 @@
 
 ;; Author: João Távora <joaotavora@gmail.com>
 ;; Keywords: lisp
+;; Package: emacs
 
 ;; This file is part of GNU Emacs.
 
diff --git a/lisp/emacs-lisp/syntax.el b/lisp/emacs-lisp/syntax.el
index d8f6c58f6ca..ba0c91d68c4 100644
--- a/lisp/emacs-lisp/syntax.el
+++ b/lisp/emacs-lisp/syntax.el
@@ -617,150 +617,150 @@ running the hook."
   (syntax-propertize pos)
   ;;
   (with-syntax-table (or syntax-ppss-table (syntax-table))
-  (let* ((cell (syntax-ppss--data))
-         (ppss-last (car cell))
-         (ppss-cache (cdr cell))
-         (old-ppss (cdr ppss-last))
-         (old-pos (car ppss-last))
-         (ppss nil)
-         (pt-min (point-min)))
-    (if (and old-pos (> old-pos pos)) (setq old-pos nil))
-    ;; Use the OLD-POS if usable and close.  Don't update the `last' cache.
-    (condition-case nil
-       (if (and old-pos (< (- pos old-pos)
-                           ;; The time to use syntax-begin-function and
-                           ;; find PPSS is assumed to be about 2 * distance.
-                           (let ((pair (aref syntax-ppss-stats 5)))
-                             (/ (* 2 (cdr pair)) (car pair)))))
-           (progn
-             (syntax-ppss--update-stats 0 old-pos pos)
-             (parse-partial-sexp old-pos pos nil nil old-ppss))
-
-         (cond
-          ;; Use OLD-PPSS if possible and close enough.
-          ((and (not old-pos) old-ppss
-                 ;; If `pt-min' is too far from `pos', we could try to use
-                ;; other positions in (nth 9 old-ppss), but that doesn't
-                ;; seem to happen in practice and it would complicate this
-                ;; code (and the before-change-function code even more).
-                ;; But maybe it would be useful in "degenerate" cases such
-                ;; as when the whole file is wrapped in a set
-                ;; of parentheses.
-                (setq pt-min (or (syntax-ppss-toplevel-pos old-ppss)
-                                 (nth 2 old-ppss)))
-                (<= pt-min pos) (< (- pos pt-min) syntax-ppss-max-span))
-           (syntax-ppss--update-stats 1 pt-min pos)
-           (setq ppss (parse-partial-sexp pt-min pos)))
-          ;; The OLD-* data can't be used.  Consult the cache.
-          (t
-           (let ((cache-pred nil)
-                 (cache ppss-cache)
-                 (pt-min (point-min))
-                 ;; I differentiate between PT-MIN and PT-BEST because
-                 ;; I feel like it might be important to ensure that the
-                 ;; cache is only filled with 100% sure data (whereas
-                 ;; syntax-begin-function might return incorrect data).
-                 ;; Maybe that's just stupid.
-                 (pt-best (point-min))
-                 (ppss-best nil))
-             ;; look for a usable cache entry.
-             (while (and cache (< pos (caar cache)))
-               (setq cache-pred cache)
-               (setq cache (cdr cache)))
-             (if cache (setq pt-min (caar cache) ppss (cdar cache)))
-
-             ;; Setup the before-change function if necessary.
-             (unless (or ppss-cache ppss-last)
-                ;; Note: combine-change-calls-1 needs to be kept in sync
-                ;; with this!
-               (add-hook 'before-change-functions
-                         #'syntax-ppss-flush-cache
-                          ;; We should be either the very last function on
-                          ;; before-change-functions or the very first on
-                          ;; after-change-functions.
-                          99 t))
-
-             ;; Use the best of OLD-POS and CACHE.
-             (if (or (not old-pos) (< old-pos pt-min))
-                 (setq pt-best pt-min ppss-best ppss)
-               (syntax-ppss--update-stats 4 old-pos pos)
-               (setq pt-best old-pos ppss-best old-ppss))
-
-             ;; Use the `syntax-begin-function' if available.
-             ;; We could try using that function earlier, but:
-             ;; - The result might not be 100% reliable, so it's better to use
-             ;;   the cache if available.
-             ;; - The function might be slow.
-             ;; - If this function almost always finds a safe nearby spot,
-             ;;   the cache won't be populated, so consulting it is cheap.
-             (when (and syntax-begin-function
-                        (progn (goto-char pos)
-                               (funcall syntax-begin-function)
-                               ;; Make sure it's better.
-                               (> (point) pt-best))
-                        ;; Simple sanity checks.
-                         (< (point) pos) ; backward-paragraph can fail here.
-                        (not (memq (get-text-property (point) 'face)
-                                   '(font-lock-string-face font-lock-doc-face
-                                     font-lock-comment-face))))
-               (syntax-ppss--update-stats 5 (point) pos)
-               (setq pt-best (point) ppss-best nil))
-
-             (cond
-              ;; Quick case when we found a nearby pos.
-              ((< (- pos pt-best) syntax-ppss-max-span)
-               (syntax-ppss--update-stats 2 pt-best pos)
-               (setq ppss (parse-partial-sexp pt-best pos nil nil ppss-best)))
-              ;; Slow case: compute the state from some known position and
-              ;; populate the cache so we won't need to do it again soon.
-              (t
-               (syntax-ppss--update-stats 3 pt-min pos)
-                (setq syntax-ppss--updated-cache t)
-
-               ;; If `pt-min' is too far, add a few intermediate entries.
-               (while (> (- pos pt-min) (* 2 syntax-ppss-max-span))
-                 (setq ppss (parse-partial-sexp
-                             pt-min (setq pt-min (/ (+ pt-min pos) 2))
-                             nil nil ppss))
-                  (push (cons pt-min ppss)
-                        (if cache-pred (cdr cache-pred) ppss-cache)))
-
-               ;; Compute the actual return value.
-               (setq ppss (parse-partial-sexp pt-min pos nil nil ppss))
-
-               ;; Debugging check.
-               ;; (let ((real-ppss (parse-partial-sexp (point-min) pos)))
-               ;;   (setcar (last ppss 4) 0)
-               ;;   (setcar (last real-ppss 4) 0)
-               ;;   (setcar (last ppss 8) nil)
-               ;;   (setcar (last real-ppss 8) nil)
-               ;;   (unless (equal ppss real-ppss)
-               ;;     (message "!!Syntax: %s != %s" ppss real-ppss)
-               ;;     (setq ppss real-ppss)))
-
-               ;; Store it in the cache.
-               (let ((pair (cons pos ppss)))
-                 (if cache-pred
-                     (if (> (- (caar cache-pred) pos) syntax-ppss-max-span)
-                         (push pair (cdr cache-pred))
-                       (setcar cache-pred pair))
-                   (if (or (null ppss-cache)
-                           (> (- (caar ppss-cache) pos)
-                              syntax-ppss-max-span))
-                       (push pair ppss-cache)
-                     (setcar ppss-cache pair)))))))))
-
-          (setq syntax-ppss--updated-cache t)
-         (setq ppss-last (cons pos ppss))
-          (setcar cell ppss-last)
-          (setcdr cell ppss-cache)
-         ppss)
-      (args-out-of-range
-       ;; If the buffer is more narrowed than when we built the cache,
-       ;; we may end up calling parse-partial-sexp with a position before
-       ;; point-min.  In that case, just parse from point-min assuming
-       ;; a nil state.
-       (parse-partial-sexp (point-min) pos))))))
+    (let* ((cell (syntax-ppss--data))
+           (ppss-last (car cell))
+           (ppss-cache (cdr cell))
+           (old-ppss (cdr ppss-last))
+           (old-pos (car ppss-last))
+           (ppss nil)
+           (pt-min (point-min)))
+      (if (and old-pos (> old-pos pos)) (setq old-pos nil))
+      ;; Use the OLD-POS if usable and close.  Don't update the `last' cache.
+      (condition-case nil
+          (if (and old-pos (< (- pos old-pos)
+                              ;; The time to use syntax-begin-function and
+                              ;; find PPSS is assumed to be about 2 * distance.
+                              (let ((pair (aref syntax-ppss-stats 5)))
+                                (/ (* 2 (cdr pair)) (car pair)))))
+              (progn
+                (syntax-ppss--update-stats 0 old-pos pos)
+                (parse-partial-sexp old-pos pos nil nil old-ppss))
+
+            (cond
+             ;; Use OLD-PPSS if possible and close enough.
+             ((and (not old-pos) old-ppss
+                   ;; If `pt-min' is too far from `pos', we could try to use
+                   ;; other positions in (nth 9 old-ppss), but that doesn't
+                   ;; seem to happen in practice and it would complicate this
+                   ;; code (and the before-change-function code even more).
+                   ;; But maybe it would be useful in "degenerate" cases such
+                   ;; as when the whole file is wrapped in a set
+                   ;; of parentheses.
+                   (setq pt-min (or (syntax-ppss-toplevel-pos old-ppss)
+                                    (nth 2 old-ppss)))
+                   (<= pt-min pos) (< (- pos pt-min) syntax-ppss-max-span))
+              (syntax-ppss--update-stats 1 pt-min pos)
+              (setq ppss (parse-partial-sexp pt-min pos)))
+             ;; The OLD-* data can't be used.  Consult the cache.
+             (t
+              (let ((cache-pred nil)
+                    (cache ppss-cache)
+                    (pt-min (point-min))
+                    ;; I differentiate between PT-MIN and PT-BEST because
+                    ;; I feel like it might be important to ensure that the
+                    ;; cache is only filled with 100% sure data (whereas
+                    ;; syntax-begin-function might return incorrect data).
+                    ;; Maybe that's just stupid.
+                    (pt-best (point-min))
+                    (ppss-best nil))
+                ;; look for a usable cache entry.
+                (while (and cache (< pos (caar cache)))
+                  (setq cache-pred cache)
+                  (setq cache (cdr cache)))
+                (if cache (setq pt-min (caar cache) ppss (cdar cache)))
+
+                ;; Setup the before-change function if necessary.
+                (unless (or ppss-cache ppss-last)
+                  ;; Note: combine-change-calls-1 needs to be kept in sync
+                  ;; with this!
+                  (add-hook 'before-change-functions
+                            #'syntax-ppss-flush-cache
+                            ;; We should be either the very last function on
+                            ;; before-change-functions or the very first on
+                            ;; after-change-functions.
+                            99 t))
+
+                ;; Use the best of OLD-POS and CACHE.
+                (if (or (not old-pos) (< old-pos pt-min))
+                    (setq pt-best pt-min ppss-best ppss)
+                  (syntax-ppss--update-stats 4 old-pos pos)
+                  (setq pt-best old-pos ppss-best old-ppss))
+
+                ;; Use the `syntax-begin-function' if available.
+                ;; We could try using that function earlier, but:
+                ;; - The result might not be 100% reliable, so it's better to 
use
+                ;;   the cache if available.
+                ;; - The function might be slow.
+                ;; - If this function almost always finds a safe nearby spot,
+                ;;   the cache won't be populated, so consulting it is cheap.
+                (when (and syntax-begin-function
+                           (progn (goto-char pos)
+                                  (funcall syntax-begin-function)
+                                  ;; Make sure it's better.
+                                  (> (point) pt-best))
+                           ;; Simple sanity checks.
+                           (< (point) pos) ; backward-paragraph can fail here.
+                           (not (memq (get-text-property (point) 'face)
+                                      '(font-lock-string-face 
font-lock-doc-face
+                                                              
font-lock-comment-face))))
+                  (syntax-ppss--update-stats 5 (point) pos)
+                  (setq pt-best (point) ppss-best nil))
+
+                (cond
+                 ;; Quick case when we found a nearby pos.
+                 ((< (- pos pt-best) syntax-ppss-max-span)
+                  (syntax-ppss--update-stats 2 pt-best pos)
+                  (setq ppss (parse-partial-sexp pt-best pos nil nil 
ppss-best)))
+                 ;; Slow case: compute the state from some known position and
+                 ;; populate the cache so we won't need to do it again soon.
+                 (t
+                  (syntax-ppss--update-stats 3 pt-min pos)
+                  (setq syntax-ppss--updated-cache t)
+
+                  ;; If `pt-min' is too far, add a few intermediate entries.
+                  (while (> (- pos pt-min) (* 2 syntax-ppss-max-span))
+                    (setq ppss (parse-partial-sexp
+                                pt-min (setq pt-min (/ (+ pt-min pos) 2))
+                                nil nil ppss))
+                    (push (cons pt-min ppss)
+                          (if cache-pred (cdr cache-pred) ppss-cache)))
+
+                  ;; Compute the actual return value.
+                  (setq ppss (parse-partial-sexp pt-min pos nil nil ppss))
+
+                  ;; Debugging check.
+                  ;; (let ((real-ppss (parse-partial-sexp (point-min) pos)))
+                  ;;   (setcar (last ppss 4) 0)
+                  ;;   (setcar (last real-ppss 4) 0)
+                  ;;   (setcar (last ppss 8) nil)
+                  ;;   (setcar (last real-ppss 8) nil)
+                  ;;   (unless (equal ppss real-ppss)
+                  ;;     (message "!!Syntax: %s != %s" ppss real-ppss)
+                  ;;     (setq ppss real-ppss)))
+
+                  ;; Store it in the cache.
+                  (let ((pair (cons pos ppss)))
+                    (if cache-pred
+                        (if (> (- (caar cache-pred) pos) syntax-ppss-max-span)
+                            (push pair (cdr cache-pred))
+                          (setcar cache-pred pair))
+                      (if (or (null ppss-cache)
+                              (> (- (caar ppss-cache) pos)
+                                 syntax-ppss-max-span))
+                          (push pair ppss-cache)
+                        (setcar ppss-cache pair)))))))))
+
+            (setq syntax-ppss--updated-cache t)
+            (setq ppss-last (cons pos ppss))
+            (setcar cell ppss-last)
+            (setcdr cell ppss-cache)
+            ppss)
+        (args-out-of-range
+         ;; If the buffer is more narrowed than when we built the cache,
+         ;; we may end up calling parse-partial-sexp with a position before
+         ;; point-min.  In that case, just parse from point-min assuming
+         ;; a nil state.
+         (parse-partial-sexp (point-min) pos))))))
 
 ;; Debugging functions
 
diff --git a/lisp/emacs-lisp/vtable.el b/lisp/emacs-lisp/vtable.el
index 0551053df8e..61670ea69ca 100644
--- a/lisp/emacs-lisp/vtable.el
+++ b/lisp/emacs-lisp/vtable.el
@@ -240,13 +240,14 @@ See info node `(vtable)Top' for vtable documentation."
 
 (defun vtable-beginning-of-table ()
   "Go to the start of the current table."
-  (if (text-property-search-backward 'vtable (vtable-current-table))
+  (if (or (text-property-search-backward 'vtable (vtable-current-table) #'eq)
+          (get-text-property (point) 'vtable))
       (point)
     (goto-char (point-min))))
 
 (defun vtable-end-of-table ()
   "Go to the end of the current table."
-  (if (text-property-search-forward 'vtable (vtable-current-table))
+  (if (text-property-search-forward 'vtable (vtable-current-table) #'eq)
       (point)
     (goto-char (point-max))))
 
diff --git a/lisp/epa-ks.el b/lisp/epa-ks.el
index 015bf910ac6..3dbce0259b3 100644
--- a/lisp/epa-ks.el
+++ b/lisp/epa-ks.el
@@ -4,6 +4,7 @@
 
 ;; Author: Philip K. <philipk@posteo.net>
 ;; Keywords: PGP, GnuPG
+;; Package: epa
 
 ;; This file is part of GNU Emacs.
 
diff --git a/lisp/epg.el b/lisp/epg.el
index b8423d82a26..aae9b9444b4 100644
--- a/lisp/epg.el
+++ b/lisp/epg.el
@@ -266,11 +266,11 @@ car is a function and cdr is a callback data.
 The function gets three arguments: the context, the key-id in
 question, and the callback data (if any).
 
-The callback may not be called if you use GnuPG 2.x, which relies
-on the external program called `gpg-agent' for passphrase query.
-If you really want to intercept passphrase query, consider
-installing GnuPG 1.x _along with_ GnuPG 2.x, which does passphrase
-query by itself and Emacs can intercept them."
+The callback may not be called if you use GnuPG 2.0, which relies
+only on external programs for passphrase query and does not
+provide loopback pinentry.  For details see Info node `(epa)
+GnuPG version compatibility' and Info node `(epa) GnuPG
+Pinentry'."
   ;; (declare (obsolete setf "25.1"))
   (setf (epg-context-passphrase-callback context)
         (if (functionp passphrase-callback)
@@ -2018,9 +2018,7 @@ PARAMETERS is a string which tells how to create the key."
 (defun epg-start-edit-key (context key edit-callback handback)
   "Initiate an edit operation on KEY.
 
-EDIT-CALLBACK is called from process filter and takes four
-arguments: the context, a status, an argument string, and the
-handback argument.
+See `epg-edit-key' for a description of the arguments.
 
 If you use this function, you will need to wait for the completion of
 `epg-gpg-program' by using `epg-wait-for-completion' and call
@@ -2035,7 +2033,47 @@ If you are unsure, use synchronous version of this 
function
                             (car (epg-key-sub-key-list key))))))
 
 (defun epg-edit-key (context key edit-callback handback)
-  "Edit KEY in the keyring."
+  "Edit KEY in the keyring.
+
+This function and function `epg-start-edit-key' use the
+line-based protocol enabled by \"gpg\" parameter \"--status-fd\"
+to edit KEY.  For each GnuPG status line, these functions or,
+more precisely, the EPG process filter calls EDIT-CALLBACK with
+four arguments: argument CONTEXT, the GnuPG status keyword, the
+GnuPG status argument string, and argument HANDBACK.
+
+The following example uses a simple state machine to trust the
+first subkey of key KEY ultimately:
+
+  (let ((state 0))
+    (epg-edit-key
+     context key
+     (lambda (context status string _handback)
+       (pcase (vector state status string)
+         (\\=`[0  \"KEY_CONSIDERED\" ,_])
+         (\\='[1  \"GET_LINE\" \"keyedit.prompt\"]
+          (process-send-string (epg-context-process context) \"1\\n\"))
+         (\\='[2  \"GOT_IT\" \"\"])
+         (\\='[3  \"GET_LINE\" \"keyedit.prompt\"]
+          (process-send-string (epg-context-process context) \"trust\\n\"))
+         (\\='[4  \"GOT_IT\" \"\"])
+         (\\='[5  \"GET_LINE\" \"edit_ownertrust.value\"]
+          (process-send-string (epg-context-process context) \"5\\n\"))
+         (\\='[6  \"GOT_IT\" \"\"])
+         (\\='[7  \"GET_BOOL\" \"edit_ownertrust.set_ultimate.okay\"]
+          (process-send-string (epg-context-process context) \"yes\\n\"))
+         (\\='[8  \"GOT_IT\" \"\"])
+         (\\='[9  \"GET_LINE\" \"keyedit.prompt\"]
+          (process-send-string (epg-context-process context) \"quit\\n\"))
+         (\\='[10 \"GOT_IT\" \"\"])
+         (_
+          (error \"Key edit protocol error in state %d\" state)))
+       (setq state (1+ state)))
+     nil))
+
+This is a slightly simplified example: Ideally, it should have
+double-checked the fingerprint argument to the \"KEY_CONSIDERED\"
+status keyword instead of ignoring it."
   (unwind-protect
       (progn
        (epg-start-edit-key context key edit-callback handback)
diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el
index eb3ec39fedd..fb10ee31c78 100644
--- a/lisp/erc/erc-backend.el
+++ b/lisp/erc/erc-backend.el
@@ -104,7 +104,7 @@
 (defvar erc--called-as-input-p)
 (defvar erc--display-context)
 (defvar erc--target)
-(defvar erc--user-from-nick-function)
+(defvar erc--cmem-from-nick-function)
 (defvar erc-channel-list)
 (defvar erc-channel-users)
 (defvar erc-default-nicks)
@@ -1089,7 +1089,7 @@ Change value of property `erc-prompt' from t to `hidden'."
         (put-text-property erc-insert-marker (1- erc-input-marker)
                            'erc-prompt 'hidden)
         (erc--conceal-prompt))
-      (add-hook 'pre-command-hook #'erc--unhide-prompt-on-self-insert 91 t))))
+      (add-hook 'pre-command-hook #'erc--unhide-prompt-on-self-insert 80 t))))
 
 (defun erc-process-sentinel (cproc event)
   "Sentinel function for ERC process."
@@ -1944,7 +1944,7 @@ add things to `%s' instead."
             ;; at this point.
             (erc-update-channel-member (if privp nick tgt) nick nick
                                        privp nil nil nil nil nil host login 
nil nil t)
-            (let ((cdata (funcall erc--user-from-nick-function
+            (let ((cdata (funcall erc--cmem-from-nick-function
                                   (erc-downcase nick) sndr parsed)))
               (setq fnick (funcall erc-format-nick-function
                                    (car cdata) (cdr cdata))))))
diff --git a/lisp/erc/erc-button.el b/lisp/erc/erc-button.el
index 8c1188e64a2..596f896d9c5 100644
--- a/lisp/erc/erc-button.el
+++ b/lisp/erc/erc-button.el
@@ -380,32 +380,37 @@ buttonizing ought to proceed and nil otherwise.  While 
running,
 all faces defined in `erc-button' are bound temporarily and can
 be updated at will.")
 
-(defvar-local erc-button--phantom-users nil)
+(defvar-local erc-button--phantom-cmems nil)
 
-(defvar erc-button--fallback-user-function #'ignore
-  "Function to determine `erc-server-user' if not found in the usual places.
+(defvar erc-button--fallback-cmem-function #'ignore
+  "Function to determine channel member if not found in the usual places.
 Called with DOWNCASED-NICK, NICK, and NICK-BOUNDS when
 `erc-button-add-nickname-buttons' cannot find a user object for
 DOWNCASED-NICK in `erc-channel-users' or `erc-server-users'.")
 
+;; Historical or fictitious users.  As long as these two structs
+;; remain superficial "subclasses" with the same slots and defaults,
+;; they can live here instead of in erc-common.el.
+(cl-defstruct (erc--phantom-channel-user (:include erc-channel-user)))
+(cl-defstruct (erc--phantom-server-user (:include erc-server-user)))
+
 (defun erc-button--add-phantom-speaker (downcased nuh _parsed)
-  "Stash fictitious `erc-server-user' while processing \"PRIVMSG\".
-Expect DOWNCASED to be the downcased nickname, NUH to be a triple
-of (NICK LOGIN HOST), and parsed to be an `erc-response' object."
   (pcase-let* ((`(,nick ,login ,host) nuh)
-               (user (or (gethash downcased erc-button--phantom-users)
-                         (make-erc-server-user
+               (cmem (gethash downcased erc-button--phantom-cmems))
+               (user (or (car cmem)
+                         (make-erc--phantom-server-user
                           :nickname nick
                           :host (and (not (string-empty-p host)) host)
-                          :login (and (not (string-empty-p login)) login)))))
-    (list (puthash downcased user erc-button--phantom-users))))
+                          :login (and (not (string-empty-p login)) login))))
+               (cuser (or (cdr cmem)
+                          (make-erc--phantom-channel-user
+                           :last-message-time (current-time)))))
+    (puthash downcased (cons user cuser) erc-button--phantom-cmems)
+    (cons user cuser)))
 
-(defun erc-button--get-phantom-user (down _word _bounds)
-  (gethash down erc-button--phantom-users))
+(defun erc-button--get-phantom-cmem (down _word _bounds)
+  (gethash down erc-button--phantom-cmems))
 
-;; In the future, we'll most likely create temporary
-;; `erc-channel-users' tables during BATCH chathistory playback, thus
-;; obviating the need for this mode entirely.
 (define-minor-mode erc-button--phantom-users-mode
   "Minor mode to recognize unknown speakers.
 Expect to be used by module setup code for creating placeholder
@@ -415,22 +420,22 @@ appeared in a prior \"353\" message and are thus a known 
member
 of the channel.  However, don't bother creating an actual
 `erc-channel-user' object because their status prefix is unknown.
 Instead, just spoof an `erc-server-user' and stash it during
-\"PRIVMSG\" handling via `erc--user-from-nick-function' and
+\"PRIVMSG\" handling via `erc--cmem-from-nick-function' and
 retrieve it during buttonizing via
 `erc-button--fallback-user-function'."
   :interactive nil
   (if erc-button--phantom-users-mode
       (progn
-        (add-function :after-until (local 'erc--user-from-nick-function)
-                      #'erc-button--add-phantom-speaker '((depth . -50)))
-        (add-function :after-until (local 'erc-button--fallback-user-function)
-                      #'erc-button--get-phantom-user '((depth . 50)))
-        (setq erc-button--phantom-users (make-hash-table :test #'equal)))
-    (remove-function (local 'erc--user-from-nick-function)
+        (add-function :after-until (local 'erc--cmem-from-nick-function)
+                      #'erc-button--add-phantom-speaker '((depth . 30)))
+        (add-function :after-until (local 'erc-button--fallback-cmem-function)
+                      #'erc-button--get-phantom-cmem '((depth . 50)))
+        (setq erc-button--phantom-cmems (make-hash-table :test #'equal)))
+    (remove-function (local 'erc--cmem-from-nick-function)
                      #'erc-button--add-phantom-speaker)
-    (remove-function (local 'erc-button--fallback-user-function)
-                     #'erc-button--get-phantom-user)
-    (kill-local-variable 'erc-nicks--phantom-users)))
+    (remove-function (local 'erc-button--fallback-cmem-function)
+                     #'erc-button--get-phantom-cmem)
+    (kill-local-variable 'erc-button--phantom-cmems)))
 
 (defun erc-button-add-nickname-buttons (entry)
   "Search through the buffer for nicknames, and add buttons."
@@ -451,11 +456,12 @@ retrieve it during buttonizing via
          (down (erc-downcase word)))
       (let* ((erc-button-mouse-face erc-button-mouse-face)
              (erc-button-nickname-face erc-button-nickname-face)
-             (cuser (and erc-channel-users (gethash down erc-channel-users)))
+             (cuser (and erc-channel-users
+                         (or (gethash down erc-channel-users)
+                             (funcall erc-button--fallback-cmem-function
+                                      down word bounds))))
              (user (or (and cuser (car cuser))
-                       (and erc-server-users (gethash down erc-server-users))
-                       (funcall erc-button--fallback-user-function
-                                down word bounds)))
+                       (and erc-server-users (gethash down erc-server-users))))
              (data (list word)))
         (when (or (not (functionp form))
                   (and-let* ((user)
diff --git a/lisp/erc/erc-common.el b/lisp/erc/erc-common.el
index 85971797c2f..8d896e663b5 100644
--- a/lisp/erc/erc-common.el
+++ b/lisp/erc/erc-common.el
@@ -60,6 +60,7 @@
                                                      ((obsolete erc-send-this))
                                                    erc-send-this))))
   (lines nil :type (list-of string))
+  (abortp nil :type (list-of symbol))
   (cmdp nil :type boolean))
 
 (cl-defstruct (erc-server-user (:type vector) :named)
@@ -270,18 +271,20 @@ instead of a `set' state, which precludes any actual 
saving."
              " above."))))))
 
 (defun erc--fill-module-docstring (&rest strings)
+  "Concatenate STRINGS and fill as a doc string."
+  ;; Perhaps it's better to mimic `internal--format-docstring-line'
+  ;; and use basic filling instead of applying a major mode?
   (with-temp-buffer
-    (emacs-lisp-mode)
-    (insert "(defun foo ()\n"
-            (format "%S" (apply #'concat strings))
-            "\n(ignore))")
+    (delay-mode-hooks
+      (if (fboundp 'lisp-data-mode) (lisp-data-mode) (emacs-lisp-mode)))
+    (insert (format "%S" (apply #'concat strings)))
     (goto-char (point-min))
-    (forward-line 2)
-    (let ((emacs-lisp-docstring-fill-column 65)
+    (forward-line)
+    (let ((fill-column 65)
           (sentence-end-double-space t))
       (fill-paragraph))
     (goto-char (point-min))
-    (nth 3 (read (current-buffer)))))
+    (read (current-buffer))))
 
 (defmacro erc--find-feature (name alias)
   `(pcase (erc--find-group ',name ,(and alias (list 'quote alias)))
diff --git a/lisp/erc/erc-compat.el b/lisp/erc/erc-compat.el
index 109b5d245ab..4c376cfbc22 100644
--- a/lisp/erc/erc-compat.el
+++ b/lisp/erc/erc-compat.el
@@ -444,6 +444,21 @@ If START or END is negative, it counts from the end."
                  (cons '("\\`irc6?s?://" . erc-compat--29-browse-url-irc)
                        existing))))))
 
+;; We can't store (TICKS . HZ) style timestamps on 27 and 28 because
+;; `time-less-p' and friends do
+;;
+;;   message("obsolete timestamp with cdr ...", ...)
+;;   decode_lisp_time(_, WARN_OBSOLETE_TIMESTAMPS, ...)
+;;   lisp_time_struct(...)
+;;   time_cmp(...)
+;;
+;; which spams *Messages* (and stderr when running the test suite).
+(defmacro erc-compat--current-lisp-time ()
+  "Return `current-time' as a (TICKS . HZ) pair on 29+."
+  (if (>= emacs-major-version 29)
+      '(let (current-time-list) (current-time))
+    '(current-time)))
+
 
 (provide 'erc-compat)
 
diff --git a/lisp/erc/erc-fill.el b/lisp/erc/erc-fill.el
index f4835f71278..0e6b5a3efb8 100644
--- a/lisp/erc/erc-fill.el
+++ b/lisp/erc/erc-fill.el
@@ -158,9 +158,8 @@ You can put this on `erc-insert-modify-hook' and/or 
`erc-send-modify-hook'."
     (when (or erc-fill--function erc-fill-function)
       ;; skip initial empty lines
       (goto-char (point-min))
-      (save-match-data
-        (while (and (looking-at "[ \t\n]*$")
-                    (= (forward-line 1) 0))))
+      (while (and (looking-at (rx bol (* (in " \t")) eol))
+                  (zerop (forward-line 1))))
       (unless (eobp)
         (save-restriction
           (narrow-to-region (point) (point-max))
diff --git a/lisp/erc/erc-goodies.el b/lisp/erc/erc-goodies.el
index b37855cbecc..b77176d8ac7 100644
--- a/lisp/erc/erc-goodies.el
+++ b/lisp/erc/erc-goodies.el
@@ -44,42 +44,279 @@
 This should be an integer specifying the line of the buffer on which
 the input line should stay.  A value of \"-1\" would keep the input
 line positioned on the last line in the buffer.  This is passed as an
-argument to `recenter'."
+argument to `recenter', unless `erc-scrolltobottom-relaxed' is
+non-nil, in which case, ERC interprets it as additional lines to
+scroll down by per message insertion (minus one for the prompt)."
   :group 'erc-display
   :type '(choice integer (const nil)))
 
+(defcustom erc-scrolltobottom-all nil
+  "Whether to scroll all windows or just the selected one.
+A value of nil preserves pre-5.6 behavior, in which scrolling
+only affects the selected window.  Users should consider its
+non-nil behavior experimental for the time being.  Note also that
+ERC expects this option to be configured before module
+initialization."
+  :group 'erc-display
+  :package-version '(ERC . "5.6") ; FIXME sync on release
+  :type 'boolean)
+
+(defcustom erc-scrolltobottom-relaxed nil
+  "Whether to forgo forcing prompt to the bottom of the window.
+When non-nil, and point is at the prompt, ERC scrolls the window
+up when inserting messages, making the prompt appear stationary.
+Users who find this effect too \"stagnant\" can adjust the option
+`erc-input-line-position', which ERC borrows to express a scroll
+step offset when this option is non-nil.  Setting that value to
+zero lets the prompt drift toward the bottom by one line per
+message, which is generally slow enough not to distract while
+composing input.  Of course, this doesn't apply when receiving a
+large influx of messages, such as after typing \"/msg NickServ
+help\".  Note that ERC only considers this option when the
+experimental companion option `erc-scrolltobottom-all' is enabled
+and, only then, during module setup."
+  :group 'erc-display
+  :package-version '(ERC . "5.6") ; FIXME sync on release
+  :type 'boolean)
+
 ;;;###autoload(autoload 'erc-scrolltobottom-mode "erc-goodies" nil t)
 (define-erc-module scrolltobottom nil
   "This mode causes the prompt to stay at the end of the window."
-  ((add-hook 'erc-mode-hook #'erc-add-scroll-to-bottom)
-   (add-hook 'erc-insert-done-hook #'erc-possibly-scroll-to-bottom)
-   (unless erc--updating-modules-p (erc-buffer-do #'erc-add-scroll-to-bottom)))
-  ((remove-hook 'erc-mode-hook #'erc-add-scroll-to-bottom)
-   (remove-hook 'erc-insert-done-hook #'erc-possibly-scroll-to-bottom)
-   (dolist (buffer (erc-buffer-list))
-     (with-current-buffer buffer
-       (remove-hook 'post-command-hook #'erc-scroll-to-bottom t)))))
+  ((add-hook 'erc-mode-hook #'erc--scrolltobottom-setup)
+   (unless erc--updating-modules-p (erc-buffer-do #'erc--scrolltobottom-setup))
+   (if erc-scrolltobottom-all
+       (progn
+         (add-hook 'erc-insert-pre-hook #'erc--scrolltobottom-on-pre-insert 25)
+         (add-hook 'erc-pre-send-functions #'erc--scrolltobottom-on-pre-insert)
+         (add-hook 'erc-insert-done-hook #'erc--scrolltobottom-all)
+         (add-hook 'erc-send-completed-hook #'erc--scrolltobottom-all))
+     (add-hook 'erc-insert-done-hook #'erc-possibly-scroll-to-bottom)))
+  ((remove-hook 'erc-mode-hook #'erc--scrolltobottom-setup)
+   (erc-buffer-do #'erc--scrolltobottom-setup)
+   (if erc-scrolltobottom-all
+       (progn
+         (remove-hook 'erc-insert-pre-hook #'erc--scrolltobottom-on-pre-insert)
+         (remove-hook 'erc-send-completed-hook #'erc--scrolltobottom-all)
+         (remove-hook 'erc-insert-done-hook #'erc--scrolltobottom-all)
+         (remove-hook 'erc-pre-send-functions
+                      #'erc--scrolltobottom-on-pre-insert))
+     (remove-hook 'erc-insert-done-hook #'erc-possibly-scroll-to-bottom))))
 
 (defun erc-possibly-scroll-to-bottom ()
   "Like `erc-add-scroll-to-bottom', but only if window is selected."
   (when (eq (selected-window) (get-buffer-window))
     (erc-scroll-to-bottom)))
 
+(defvar-local erc--scrolltobottom-relaxed-commands '(end-of-buffer)
+  "Commands triggering a forced scroll to prompt.
+Only applies with `erc-scrolltobottom-relaxed' while away from
+prompt.")
+
+(defvar-local erc--scrolltobottom-window-info nil
+  "Alist with windows as keys and lists of window-related info as values.
+Values are lists containing the last window start position and
+the last \"window line\" of point.  The \"window line\", which
+may be nil, is the number of lines between `window-start' and
+`window-point', inclusive.")
+
+(defvar erc--scrolltobottom-post-force-commands
+  '(beginning-of-buffer
+    electric-newline-and-maybe-indent
+    newline
+    default-indent-new-line)
+  "Commands that force a scroll after execution at prompt.
+That is, ERC recalculates the window's start instead of blindly
+restoring it.")
+
+;; Unfortunately, this doesn't work when `erc-scrolltobottom-relaxed'
+;; is enabled (scaling up still moves the prompt).
+(defvar erc--scrolltobottom-post-ignore-commands '(text-scale-adjust)
+  "Commands to skip instead of force-scroll on `post-command-hook'.")
+
+(defvar erc--scrolltobottom-relaxed-skip-commands
+  '(recenter-top-bottom scroll-down-command)
+  "Commands exempt from triggering a stash and restore of `window-start'.
+Only applies with `erc-scrolltobottom-relaxed' while in the input
+area.")
+
+(defun erc--scrolltobottom-on-pre-command ()
+  (when (and (eq (selected-window) (get-buffer-window))
+             (>= (point) erc-input-marker))
+    (setq erc--scrolltobottom-window-info
+          (list (list (selected-window)
+                      (window-start)
+                      (count-screen-lines (window-start) (point-max)))))))
+
+(defun erc--scrolltobottom-on-post-command ()
+  "Restore window start or scroll to prompt and recenter.
+When `erc--scrolltobottom-window-info' is non-nil and its first
+item is associated with the selected window, restore start of
+window so long as prompt hasn't moved.  Expect buffer to be
+unnarrowed."
+  (when (eq (selected-window) (get-buffer-window))
+    (if-let (((not (input-pending-p)))
+             (erc--scrolltobottom-window-info)
+             (found (car erc--scrolltobottom-window-info))
+             ((eq (car found) (selected-window)))
+             ((not (memq this-command
+                         erc--scrolltobottom-post-force-commands)))
+             ((= (nth 2 found)
+                 (count-screen-lines (window-start) (point-max)))))
+        (set-window-start (selected-window) (nth 1 found))
+      (unless (memq this-command erc--scrolltobottom-post-ignore-commands)
+        (erc--scrolltobottom-confirm)))
+    (setq erc--scrolltobottom-window-info nil)))
+
+(defun erc--scrolltobottom-on-pre-command-relaxed ()
+  "Maybe scroll to bottom when away from prompt.
+When `erc-scrolltobottom-relaxed' is active, only scroll when
+prompt is past window's end and the command is `end-of-buffer' or
+`self-insert-command' (assuming `move-to-prompt' is active).
+When at prompt and current command does not appear in
+`erc--scrolltobottom-relaxed-skip-commands', stash
+`erc--scrolltobottom-window-info' for the selected window.
+Assume an unnarrowed buffer."
+  (when (eq (selected-window) (get-buffer-window))
+    (when (and (not (input-pending-p))
+               (< (point) erc-input-marker)
+               (memq this-command erc--scrolltobottom-relaxed-commands)
+               (< (window-end nil t) erc-input-marker))
+      (save-excursion
+        (goto-char (point-max))
+        (recenter (or erc-input-line-position -1))))
+    (when (and (>= (point) erc-input-marker)
+               (not (memq this-command
+                          erc--scrolltobottom-relaxed-skip-commands)))
+      (setq erc--scrolltobottom-window-info
+            (list (list (selected-window)
+                        (window-start)
+                        (count-screen-lines (window-start) (point-max))))))))
+
+(defun erc--scrolltobottom-on-post-command-relaxed ()
+  "Set window start or scroll when data was captured on pre-command."
+  (when-let (((eq (selected-window) (get-buffer-window)))
+             (erc--scrolltobottom-window-info)
+             (found (car erc--scrolltobottom-window-info))
+             ((eq (car found) (selected-window))))
+    (if (and (not (memq this-command erc--scrolltobottom-post-force-commands))
+             (= (nth 2 found)
+                (count-screen-lines (window-start) (point-max))))
+        (set-window-start (selected-window) (nth 1 found))
+      (recenter (nth 2 found)))
+    (setq erc--scrolltobottom-window-info nil)))
+
+;; It may be desirable to also restore the relative line position of
+;; window point after changing dimensions.  Perhaps stashing the
+;; previous ratio of window line to body height and later recentering
+;; proportionally would achieve this.
+(defun erc--scrolltobottom-at-prompt-minibuffer-active ()
+  "Scroll window to bottom when at prompt and using the minibuffer."
+  ;; This is redundant or ineffective in the selected window if at
+  ;; prompt or if only one window exists.
+  (unless (or (input-pending-p)
+              (and (minibuffer-window-active-p (minibuffer-window))
+                   (eq (old-selected-window) (minibuffer-window))))
+    (erc--scrolltobottom-confirm)))
+
+(defun erc--scrolltobottom-all (&rest _)
+  "Maybe put prompt on last line in all windows displaying current buffer.
+Expect to run when narrowing is in effect, such as on insertion
+or send-related hooks.  When recentering has not been performed,
+attempt to restore last `window-start', if known."
+  (dolist (window (get-buffer-window-list nil nil 'visible))
+    (with-selected-window window
+      (when-let
+          ((erc--scrolltobottom-window-info)
+           (found (assq window erc--scrolltobottom-window-info))
+           ((not (erc--scrolltobottom-confirm (nth 2 found)))))
+        (set-window-start window (cadr found) 'no-force))))
+  ;; Necessary unless we're sure `erc--scrolltobottom-on-pre-insert'
+  ;; always runs between calls to this function.
+  (setq erc--scrolltobottom-window-info nil))
+
 (defun erc-add-scroll-to-bottom ()
   "A hook function for `erc-mode-hook' to recenter output at bottom of window.
 
 If you find that ERC hangs when using this function, try customizing
 the value of `erc-input-line-position'.
 
-This works whenever scrolling happens, so it's added to
-`window-scroll-functions' rather than `erc-insert-post-hook'."
+Note that the prior suggestion comes from a time when this
+function used `window-scroll-functions', which was replaced by
+`post-command-hook' in ERC 5.3."
+  (declare (obsolete erc--scrolltobottom-setup "30.1"))
   (add-hook 'post-command-hook #'erc-scroll-to-bottom nil t))
 
+(cl-defgeneric erc--scrolltobottom-setup ()
+  "Arrange for scrolling to bottom on window configuration changes.
+Undo that arrangement when disabling `erc-scrolltobottom-mode'."
+  (if erc-scrolltobottom-mode
+      (add-hook 'post-command-hook #'erc-scroll-to-bottom nil t)
+    (remove-hook 'post-command-hook #'erc-scroll-to-bottom t)))
+
+(cl-defmethod erc--scrolltobottom-setup (&context
+                                         (erc-scrolltobottom-all (eql t)))
+  "Add and remove local hooks specific to `erc-scrolltobottom-all'."
+  (if erc-scrolltobottom-mode
+      (if erc-scrolltobottom-relaxed
+          (progn
+            (when (or (bound-and-true-p erc-move-to-prompt-mode)
+                      (memq 'move-to-prompt erc-modules))
+              (cl-pushnew 'self-insert-command
+                          erc--scrolltobottom-relaxed-commands))
+            (add-hook 'post-command-hook
+                      #'erc--scrolltobottom-on-post-command-relaxed 60 t)
+            (add-hook 'pre-command-hook ; preempt `move-to-prompt'
+                      #'erc--scrolltobottom-on-pre-command-relaxed 60 t))
+        (add-hook 'window-configuration-change-hook
+                  #'erc--scrolltobottom-at-prompt-minibuffer-active nil t)
+        (add-hook 'pre-command-hook
+                  #'erc--scrolltobottom-on-pre-command 60 t)
+        (add-hook 'post-command-hook
+                  #'erc--scrolltobottom-on-post-command 60 t))
+    (remove-hook 'window-configuration-change-hook
+                 #'erc--scrolltobottom-at-prompt-minibuffer-active t)
+    (remove-hook 'pre-command-hook
+                 #'erc--scrolltobottom-on-pre-command t)
+    (remove-hook 'post-command-hook
+                 #'erc--scrolltobottom-on-post-command t)
+    (remove-hook 'pre-command-hook
+                 #'erc--scrolltobottom-on-pre-command-relaxed t)
+    (remove-hook 'post-command-hook
+                 #'erc--scrolltobottom-on-post-command-relaxed t)
+    (kill-local-variable 'erc--scrolltobottom-relaxed-commands)
+    (kill-local-variable 'erc--scrolltobottom-window-info)))
+
+(defun erc--scrolltobottom-on-pre-insert (_)
+  "Remember the `window-start' before inserting a message."
+  (setq erc--scrolltobottom-window-info
+        (mapcar (lambda (w)
+                  (list w
+                        (window-start w)
+                        (and-let*
+                            ((erc-scrolltobottom-relaxed)
+                             (c (count-screen-lines (window-start w)
+                                                    (point-max) nil w)))
+                          (if (= ?\n (char-before (point-max))) (1+ c) c))))
+                (get-buffer-window-list nil nil 'visible))))
+
+(defun erc--scrolltobottom-confirm (&optional scroll-to)
+  "Like `erc-scroll-to-bottom', but use `window-point'.
+Position current line (with `recenter') SCROLL-TO lines below
+window's top.  Return nil if point is not in prompt area or if
+prompt isn't ready."
+  (when erc-insert-marker
+    (let ((resize-mini-windows nil))
+      (save-restriction
+        (widen)
+        (when (>= (window-point) erc-input-marker)
+          (save-excursion
+            (goto-char (point-max))
+            (recenter (+ (or scroll-to 0) (or erc-input-line-position -1)))
+            t))))))
+
 (defun erc-scroll-to-bottom ()
   "Recenter WINDOW so that `point' is on the last line.
 
-This is added to `window-scroll-functions' by `erc-add-scroll-to-bottom'.
-
 You can control which line is recentered to by customizing the
 variable `erc-input-line-position'."
       ;; Temporarily bind resize-mini-windows to nil so that users who have it
@@ -135,13 +372,13 @@ Put this function on `erc-insert-post-hook' and/or 
`erc-send-post-hook'."
 
 (defun erc-move-to-prompt-setup ()
   "Initialize the move-to-prompt module."
-  (add-hook 'pre-command-hook #'erc-move-to-prompt nil t))
+  (add-hook 'pre-command-hook #'erc-move-to-prompt 70 t))
 
 ;;; Keep place in unvisited channels
 ;;;###autoload(autoload 'erc-keep-place-mode "erc-goodies" nil t)
 (define-erc-module keep-place nil
   "Leave point above un-viewed text in other channels."
-  ((add-hook 'erc-insert-pre-hook  #'erc-keep-place))
+  ((add-hook 'erc-insert-pre-hook  #'erc-keep-place 65))
   ((remove-hook 'erc-insert-pre-hook  #'erc-keep-place)))
 
 (defcustom erc-keep-place-indicator-style t
@@ -213,12 +450,15 @@ the active frame."
   (add-hook 'window-configuration-change-hook
             #'erc--keep-place-indicator-on-window-configuration-change nil t)
   (when-let* (((memq erc-keep-place-indicator-style '(t arrow)))
+              (ov-property (if (zerop (fringe-columns 'left))
+                               'after-string
+                             'before-string))
               (display (if (zerop (fringe-columns 'left))
                            `((margin left-margin) ,overlay-arrow-string)
                          '(left-fringe right-triangle
                                        erc-keep-place-indicator-arrow)))
               (bef (propertize " " 'display display)))
-    (overlay-put erc--keep-place-indicator-overlay 'before-string bef))
+    (overlay-put erc--keep-place-indicator-overlay ov-property bef))
   (when (memq erc-keep-place-indicator-style '(t face))
     (overlay-put erc--keep-place-indicator-overlay 'face
                  'erc-keep-place-indicator-line)))
@@ -233,7 +473,7 @@ and `keep-place-indicator' in different buffers."
          ((memq 'keep-place erc-modules)
           (erc-keep-place-mode +1))
          ;; Enable a local version of `keep-place-mode'.
-         (t (add-hook 'erc-insert-pre-hook  #'erc-keep-place 90 t)))
+         (t (add-hook 'erc-insert-pre-hook  #'erc-keep-place 65 t)))
    (if (pcase erc-keep-place-indicator-buffer-type
          ('target erc--target)
          ('server (not erc--target))
@@ -256,7 +496,7 @@ That is, ensure the local module can survive a user 
toggling the
 global one."
   (if erc-keep-place-mode
       (remove-hook 'erc-insert-pre-hook  #'erc-keep-place t)
-    (add-hook 'erc-insert-pre-hook  #'erc-keep-place 90 t)))
+    (add-hook 'erc-insert-pre-hook  #'erc-keep-place 65 t)))
 
 (defun erc-keep-place-move (pos)
   "Move keep-place indicator to current line or POS.
diff --git a/lisp/erc/erc-ibuffer.el b/lisp/erc/erc-ibuffer.el
index 612814ac6da..790efae97ac 100644
--- a/lisp/erc/erc-ibuffer.el
+++ b/lisp/erc/erc-ibuffer.el
@@ -27,6 +27,9 @@
 ;; needs work.  Usage:  Type / C-e C-h when in Ibuffer-mode to see new
 ;; limiting commands
 
+;; This library does not contain a module, but you can `require' it
+;; after loading `erc' to make use of its functionality.
+
 ;;; Code:
 
 (require 'ibuffer)
@@ -118,11 +121,11 @@
 
 (define-ibuffer-column
  erc-members (:name "Users")
-  (if (and (eq major-mode 'erc-mode)
-          (boundp 'erc-channel-users)
-          (hash-table-p erc-channel-users)
-          (> (hash-table-size erc-channel-users) 0))
-     (number-to-string (hash-table-size erc-channel-users))
+  (if-let ((table (or erc-channel-users erc-server-users))
+           ((hash-table-p table))
+           (count (hash-table-count table))
+           ((> count 0)))
+      (number-to-string count)
     ""))
 
 (define-ibuffer-column erc-away (:name "A")
@@ -177,8 +180,7 @@
 (defvar erc-ibuffer-limit-map nil
   "Prefix keymap to use for ERC related limiting.")
 (define-prefix-command 'erc-ibuffer-limit-map)
-;; FIXME: Where is `ibuffer-limit-by-erc-server' defined?
-(define-key 'erc-ibuffer-limit-map (kbd "s") 'ibuffer-limit-by-erc-server)
+(define-key 'erc-ibuffer-limit-map (kbd "s") #'ibuffer-filter-by-erc-server)
 (define-key ibuffer-mode-map (kbd "/ \C-e") 'erc-ibuffer-limit-map)
 
 (provide 'erc-ibuffer)
diff --git a/lisp/erc/erc-stamp.el b/lisp/erc/erc-stamp.el
index a021cd26607..0f3163bf68d 100644
--- a/lisp/erc/erc-stamp.el
+++ b/lisp/erc/erc-stamp.el
@@ -136,14 +136,27 @@ hidden, they will still be present in the logs."
   "If non-nil, print timestamp in the minibuffer when point is moved.
 Using this variable, you can turn off normal timestamping,
 and simply move point to an irc message to see its timestamp
-printed in the minibuffer."
+printed in the minibuffer.  When attempting to enable this option
+after `erc-stamp-mode' is already active, you may need to run the
+command `erc-show-timestamps' (or `erc-hide-timestamps') in the
+appropriate ERC buffer before the change will take effect."
   :type 'boolean)
 
 (defcustom erc-echo-timestamp-format "Timestamped %A, %H:%M:%S"
   "Format string to be used when `erc-echo-timestamps' is non-nil.
 This string specifies the format of the timestamp being echoed in
 the minibuffer."
-  :type 'string)
+  :type '(choice (const "Timestamped %A, %H:%M:%S")
+                 (const  "%Y-%m-%d %H:%M:%S %Z")
+                 string))
+
+(defcustom erc-echo-timestamp-zone nil
+  "Default timezone for the option `erc-echo-timestamps'.
+Also affects the command `erc-echo-timestamp' (singular).  See
+the ZONE parameter of `format-time-string' for a description of
+acceptable value types."
+  :type '(choice boolean number (const wall) (list number string))
+  :package-version '(ERC . "5.6")) ; FIXME sync on release
 
 (defcustom erc-timestamp-intangible nil
   "Whether the timestamps should be intangible, i.e. prevent the point
@@ -167,14 +180,15 @@ from entering them and instead jump over them."
    (add-hook 'erc-send-modify-hook #'erc-add-timestamp 60)
    (add-hook 'erc-mode-hook #'erc-stamp--recover-on-reconnect)
    (add-hook 'erc--pre-clear-functions #'erc-stamp--reset-on-clear)
-   (unless erc--updating-modules-p
-     (erc-buffer-do #'erc-munge-invisibility-spec)))
+   (unless erc--updating-modules-p (erc-buffer-do #'erc-stamp--setup)))
   ((remove-hook 'erc-mode-hook #'erc-munge-invisibility-spec)
    (remove-hook 'erc-insert-modify-hook #'erc-add-timestamp)
    (remove-hook 'erc-send-modify-hook #'erc-add-timestamp)
    (remove-hook 'erc-mode-hook #'erc-stamp--recover-on-reconnect)
    (remove-hook 'erc--pre-clear-functions #'erc-stamp--reset-on-clear)
    (erc-with-all-buffers-of-server nil nil
+     (erc-stamp--setup)
+     (kill-local-variable 'erc-stamp--last-stamp)
      (kill-local-variable 'erc-timestamp-last-inserted)
      (kill-local-variable 'erc-timestamp-last-inserted-left)
      (kill-local-variable 'erc-timestamp-last-inserted-right))))
@@ -200,9 +214,8 @@ the stamp passed to `erc-insert-timestamp-function'.")
 
 (cl-defgeneric erc-stamp--current-time ()
   "Return a lisp time object to associate with an IRC message.
-This becomes the message's `erc-timestamp' text property, which
-may not be unique, `equal'-wise."
-  (erc-current-time))
+This becomes the message's `erc-timestamp' text property."
+  (erc-compat--current-lisp-time))
 
 (cl-defmethod erc-stamp--current-time :around ()
   (or erc-stamp--current-time (cl-call-next-method)))
@@ -218,15 +231,17 @@ or `erc-send-modify-hook'."
            (erc-stamp--invisible-property
             ;; FIXME on major version bump, make this `erc-' prefixed.
             (if invisible `(timestamp ,@(ensure-list invisible)) 'timestamp))
+           (skipp (and erc-stamp--skip-when-invisible invisible))
            (erc-stamp--current-time ct))
-      (unless (setq invisible (and erc-stamp--skip-when-invisible invisible))
+      (unless skipp
         (funcall erc-insert-timestamp-function
                  (erc-format-timestamp ct erc-timestamp-format)))
-      ;; FIXME this will error when advice has been applied.
-      (when (and (not invisible) (fboundp erc-insert-away-timestamp-function)
-                erc-away-timestamp-format
-                (erc-away-time)
-                (not erc-timestamp-format))
+      ;; Check `erc-insert-away-timestamp-function' for historical
+      ;; reasons even though its Custom :type only allows functions.
+      (when (and (not (or skipp erc-timestamp-format))
+                 erc-away-timestamp-format
+                 (functionp erc-insert-away-timestamp-function)
+                 (erc-away-time))
        (funcall erc-insert-away-timestamp-function
                 (erc-format-timestamp ct erc-away-timestamp-format)))
       (add-text-properties (point-min) (1- (point-max))
@@ -640,14 +655,31 @@ Return the empty string if FORMAT is nil."
 ;; please modify this function and move it to a more appropriate
 ;; location.
 (defun erc-munge-invisibility-spec ()
-  (and erc-timestamp-intangible (not (bound-and-true-p cursor-intangible-mode))
-       (cursor-intangible-mode 1))
-  (and erc-echo-timestamps (not (bound-and-true-p cursor-sensor-mode))
-       (cursor-sensor-mode 1))
+  (if erc-timestamp-intangible
+      (cursor-intangible-mode +1) ; idempotent
+    (when (bound-and-true-p cursor-intangible-mode)
+      (cursor-intangible-mode -1)))
+  (if erc-echo-timestamps
+      (progn
+        (cursor-sensor-mode +1) ; idempotent
+        (when (>= emacs-major-version 29)
+          (add-function :before-until (local 'clear-message-function)
+                        #'erc-stamp--on-clear-message)))
+    (when (bound-and-true-p cursor-sensor-mode)
+      (cursor-sensor-mode -1))
+    (remove-function (local 'clear-message-function)
+                     #'erc-stamp--on-clear-message))
   (if erc-hide-timestamps
       (add-to-invisibility-spec 'timestamp)
     (remove-from-invisibility-spec 'timestamp)))
 
+(defun erc-stamp--setup ()
+  "Enable or disable buffer-local `erc-stamp-mode' modifications."
+  (if erc-stamp-mode
+      (erc-munge-invisibility-spec)
+    (let (erc-echo-timestamps erc-hide-timestamps erc-timestamp-intangible)
+      (erc-munge-invisibility-spec))))
+
 (defun erc-hide-timestamps ()
   "Hide timestamp information from display."
   (interactive)
@@ -677,14 +709,33 @@ enabled when the message was inserted."
            (erc-munge-invisibility-spec)))
        (erc-buffer-list)))
 
-(defun erc-echo-timestamp (dir stamp)
-  "Print timestamp text-property of an IRC message."
-  ;; Could also pass an &optional `zone' arg to `format-time-string'.
-  (interactive (list 'entered (get-text-property (point) 'erc-timestamp)))
-  (when (eq 'entered dir)
-    (when stamp
-      (message "%s" (format-time-string erc-echo-timestamp-format
-                                       stamp)))))
+(defvar-local erc-stamp--last-stamp nil)
+
+(defun erc-stamp--on-clear-message (&rest _)
+  "Return `dont-clear-message' when operating inside the same stamp."
+  (and erc-stamp--last-stamp erc-echo-timestamps
+       (eq (get-text-property (point) 'erc-timestamp) erc-stamp--last-stamp)
+       'dont-clear-message))
+
+(defun erc-echo-timestamp (dir stamp &optional zone)
+  "Display timestamp of message at point in echo area.
+Interactively, interpret a numeric prefix as a ZONE offset in
+hours (or seconds, if its abs value is larger than 14), and
+interpret a \"raw\" prefix as UTC.  To specify a zone for use
+with the option `erc-echo-timestamps', see the companion option
+`erc-echo-timestamp-zone'."
+  (interactive (list nil (get-text-property (point) 'erc-timestamp)
+                     (pcase current-prefix-arg
+                       ((and (pred numberp) v)
+                        (if (<= (abs v) 14) (* v 3600) v))
+                       (`(,_) t))))
+  (if (and stamp (or (null dir) (and erc-echo-timestamps (eq 'entered dir))))
+      (progn
+        (setq erc-stamp--last-stamp stamp)
+        (message (format-time-string erc-echo-timestamp-format
+                                     stamp (or zone erc-echo-timestamp-zone))))
+    (when (and erc-echo-timestamps (eq 'left dir))
+      (setq erc-stamp--last-stamp nil))))
 
 (defun erc--echo-ts-csf (_window _before dir)
   (erc-echo-timestamp dir (get-text-property (point) 'erc-timestamp)))
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 7375b5308ea..fb236f1f189 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -17,6 +17,9 @@
 ;; Keywords: IRC, chat, client, Internet
 ;; URL: https://www.gnu.org/software/emacs/erc.html
 
+;; This is a GNU ELPA :core package.  Avoid functionality that is not
+;; compatible with the version of Emacs recorded above.
+
 ;; This file is part of GNU Emacs.
 
 ;; GNU Emacs is free software: you can redistribute it and/or modify
@@ -249,7 +252,14 @@ node `(auth) Top' and Info node `(erc) auth-source'.")
   :type 'boolean)
 
 (defcustom erc-warn-about-blank-lines t
-  "Warn the user if they attempt to send a blank line."
+  "Warn the user if they attempt to send a blank line.
+When non-nil, ERC signals a `user-error' upon encountering prompt
+input containing empty or whitespace-only lines.  When nil, ERC
+still inhibits sending but does so silently.  With the companion
+option `erc-send-whitespace-lines' enabled, ERC sends pending
+input and prints a message in the echo area indicating the amount
+of padding and/or stripping applied, if any.  Setting this option
+to nil suppresses such reporting."
   :group 'erc
   :type 'boolean)
 
@@ -261,8 +271,8 @@ node `(auth) Top' and Info node `(erc) auth-source'.")
 (defcustom erc-inhibit-multiline-input nil
   "When non-nil, conditionally disallow input consisting of multiple lines.
 Issue an error when the number of input lines submitted for
-sending exceeds this value.  The value t means disallow more
-than 1 line of input."
+sending meets or exceeds this value.  The value t is synonymous
+with a value of 2 and means disallow more than 1 line of input."
   :package-version '(ERC . "5.5")
   :group 'erc
   :type '(choice integer boolean))
@@ -1092,9 +1102,10 @@ subprotocols should probably be handled manually."
 
 (define-obsolete-variable-alias 'erc--pre-send-split-functions
   'erc--input-review-functions "30.1")
-(defvar erc--input-review-functions '(erc--discard-trailing-multiline-nulls
-                                      erc--split-lines
-                                      erc--run-input-validation-checks)
+(defvar erc--input-review-functions '(erc--split-lines
+                                      erc--run-input-validation-checks
+                                      erc--discard-trailing-multiline-nulls
+                                      erc--inhibit-slash-cmd-insertion)
   "Special hook for reviewing and modifying prompt input.
 ERC runs this before clearing the prompt and before running any
 send-related hooks, such as `erc-pre-send-functions'.  Thus, it's
@@ -1538,6 +1549,7 @@ Defaults to the server buffer."
   (setq-local paragraph-start
               (concat "\\(" (regexp-quote (erc-prompt)) "\\)"))
   (setq-local completion-ignore-case t)
+  (add-hook 'post-command-hook #'erc-check-text-conversion nil t)
   (add-hook 'kill-buffer-hook #'erc-kill-buffer-function nil t)
   (add-hook 'completion-at-point-functions #'erc-complete-word-at-point nil t))
 
@@ -5261,14 +5273,15 @@ Assume buffer is narrowed to the confines of an 
inserted message."
                  (next-single-property-change (point-min) 'erc-speaker))))
      (cons beg (next-single-property-change beg 'erc-speaker)))))
 
-(defvar erc--user-from-nick-function #'erc--examine-nick
-  "Function to possibly consider unknown user.
-Must return either nil or a cons of an `erc-server-user' and a
-possibly nil `erc-channel-user' for formatting a server user's
-nick.  Called in the appropriate buffer with the downcased nick,
-the parsed NUH, and the original `erc-response' object.")
+(defvar erc--cmem-from-nick-function #'erc--cmem-get-existing
+  "Function maybe returning a \"channel member\" cons from a nick.
+Must return either nil or a cons of an `erc-server-user' and an
+`erc-channel-user' (see `erc-channel-users') for use in
+formatting a user's nick prior to insertion.  Called in the
+appropriate target buffer with the downcased nick, the parsed
+NUH, and the current `erc-response' object.")
 
-(defun erc--examine-nick (downcased _nuh _parsed)
+(defun erc--cmem-get-existing (downcased _nuh _parsed)
   (and erc-channel-users (gethash downcased erc-channel-users)))
 
 (defun erc-format-privmessage (nick msg privp msgp)
@@ -6316,12 +6329,22 @@ EmacsSpeak support."
 
 (defalias 'erc-list 'ensure-list)
 
+(defconst erc--parse-user-regexp-pedantic
+  (rx bot (group (* (not (any "!\r\n"))))
+      "!" (group (* nonl))
+      "@" (group (* nonl)) eot))
+
+(defconst erc--parse-user-regexp-legacy
+  "^\\([^!\n]*\\)!\\([^@\n]*\\)@\\(.*\\)$")
+
+(defvar erc--parse-user-regexp erc--parse-user-regexp-legacy)
+
 (defun erc-parse-user (string)
   "Parse STRING as a user specification (nick!login@host).
 
 Return a list of the three separate tokens."
   (cond
-   ((string-match "^\\([^!\n]*\\)!\\([^@\n]*\\)@\\(.*\\)$" string)
+   ((string-match erc--parse-user-regexp string)
     (list (match-string 1 string)
           (match-string 2 string)
           (match-string 3 string)))
@@ -6409,20 +6432,6 @@ holds off on submitting it, for obvious reasons."
 (defvar erc-command-regexp "^/\\([A-Za-z']+\\)\\(\\s-+.*\\|\\s-*\\)$"
   "Regular expression used for matching commands in ERC.")
 
-(defun erc--blank-in-multiline-input-p (lines)
-  "Detect whether LINES contains a blank line.
-When `erc-send-whitespace-lines' is in effect, return nil if
-LINES is multiline or the first line is non-empty.  When
-`erc-send-whitespace-lines' is nil, return non-nil when any line
-is empty or consists of one or more spaces, tabs, or form-feeds."
-  (catch 'return
-    (let ((multilinep (cdr lines)))
-      (dolist (line lines)
-        (when (if erc-send-whitespace-lines
-                  (and (string-empty-p line) (not multilinep))
-                (string-match (rx bot (* (in " \t\f")) eot) line))
-          (throw 'return t))))))
-
 (defun erc--check-prompt-input-for-excess-lines (_ lines)
   "Return non-nil when trying to send too many LINES."
   (when erc-inhibit-multiline-input
@@ -6442,13 +6451,78 @@ is empty or consists of one or more spaces, tabs, or 
form-feeds."
                      (y-or-n-p (concat "Send input " msg "?")))
           (concat "Too many lines " msg))))))
 
-(defun erc--check-prompt-input-for-multiline-blanks (_ lines)
-  "Return non-nil when multiline prompt input has blank LINES."
-  (when (erc--blank-in-multiline-input-p lines)
+(defun erc--check-prompt-input-for-something (string _)
+  (when (string-empty-p string)
     (if erc-warn-about-blank-lines
         "Blank line - ignoring..."
       'invalid)))
 
+(defun erc--count-blank-lines (lines)
+  "Report on the number of whitespace-only and empty LINES.
+Return a list of (BLANKS TO-PAD TO-STRIP).  Expect caller to know
+that BLANKS includes non-empty whitespace-only lines and that no
+padding or stripping has yet occurred."
+  (let ((real 0) (total 0) (pad 0) (strip 0))
+    (dolist (line lines)
+      (if (string-match (rx bot (* (in " \t\f")) eot) line)
+          (progn
+            (cl-incf total)
+            (if (zerop (match-end 0))
+                (cl-incf strip)
+              (cl-incf pad strip)
+              (setq strip 0)))
+        (cl-incf real)
+        (unless (zerop strip)
+          (cl-incf pad strip)
+          (setq strip 0))))
+    (when (and (zerop real) (not (zerop total)) (= total (+ pad strip)))
+      (cl-incf strip (1- pad))
+      (setq pad 1))
+    (list total pad strip)))
+
+(defvar erc--check-prompt-explanation nil
+  "List of strings to print if no validator returns non-nil.")
+
+(defun erc--check-prompt-input-for-multiline-blanks (_ lines)
+  "Return non-nil when multiline prompt input has blank LINES.
+Consider newlines to be intervening delimiters, meaning the empty
+\"logical\" line between a trailing newline and `eob' constitutes
+a separate message."
+  (pcase-let ((`(,total ,pad ,strip)(erc--count-blank-lines lines)))
+    (cond ((zerop total) nil)
+          ((and erc-warn-about-blank-lines erc-send-whitespace-lines)
+           (let (msg args)
+             (unless (zerop strip)
+               (push "stripping (%d)" msg)
+               (push strip args))
+             (unless (zerop pad)
+               (when msg
+                 (push "and" msg))
+               (push "padding (%d)" msg)
+               (push pad args))
+             (when msg
+               (push "blank" msg)
+               (push (if (> (apply #'+ args) 1) "lines" "line") msg))
+             (when msg
+               (setf msg (nreverse msg)
+                     (car msg) (capitalize (car msg))))
+             (when msg
+               (push (apply #'format (string-join msg " ") (nreverse args))
+                     erc--check-prompt-explanation)
+               nil)))
+          (erc-warn-about-blank-lines
+           (concat (if (= total 1)
+                       (if (zerop strip) "Blank" "Trailing")
+                     (if (= total strip)
+                         (format "%d trailing" strip)
+                       (format "%d blank" total)))
+                   (and (> total 1) (/= total strip) (not (zerop strip))
+                        (format " (%d trailing)" strip))
+                   (if (= total 1) " line" " lines")
+                   " detected (see `erc-send-whitespace-lines')"))
+          (erc-send-whitespace-lines nil)
+          (t 'invalid))))
+
 (defun erc--check-prompt-input-for-point-in-bounds (_ _)
   "Return non-nil when point is before prompt."
   (when (< (point) (erc-beg-of-input-line))
@@ -6469,25 +6543,39 @@ is empty or consists of one or more spaces, tabs, or 
form-feeds."
 
 (defvar erc--check-prompt-input-functions
   '(erc--check-prompt-input-for-point-in-bounds
+    erc--check-prompt-input-for-something
+    erc--check-prompt-input-for-multiline-command
     erc--check-prompt-input-for-multiline-blanks
     erc--check-prompt-input-for-running-process
-    erc--check-prompt-input-for-excess-lines
-    erc--check-prompt-input-for-multiline-command)
+    erc--check-prompt-input-for-excess-lines)
   "Validators for user input typed at prompt.
-Called with latest input string submitted by user and the list of
-lines produced by splitting it.  If any member function returns
-non-nil, processing is abandoned and input is left untouched.
-When the returned value is a string, ERC passes it to `erc-error'.")
+Called with two arguments: the current input submitted by the
+user, as a string, along with the same input as a list of
+strings.  If any member function returns non-nil, ERC abandons
+processing and leaves pending input untouched in the prompt area.
+When the returned value is a string, ERC passes it to
+`user-error'.  Any other non-nil value tells ERC to abort
+silently.  If all members return nil, and the variable
+`erc--check-prompt-explanation' is a nonempty list of strings,
+ERC prints them as a single message joined by newlines.")
 
 (defun erc--run-input-validation-checks (state)
   "Run input checkers from STATE, an `erc--input-split' object."
-  (when-let ((msg (run-hook-with-args-until-success
-                   'erc--check-prompt-input-functions
-                   (erc--input-split-string state)
-                   (erc--input-split-lines state))))
-    (unless (stringp msg)
-      (setq msg (format "Input error: %S" msg)))
-    (user-error msg)))
+  (let* ((erc--check-prompt-explanation nil)
+         (msg (run-hook-with-args-until-success
+               'erc--check-prompt-input-functions
+               (erc--input-split-string state)
+               (erc--input-split-lines state))))
+    (cond ((stringp msg) (user-error msg))
+          (msg (push msg (erc--input-split-abortp state)))
+          (erc--check-prompt-explanation
+           (message "%s" (string-join (nreverse erc--check-prompt-explanation)
+                                      "\n"))))))
+
+(defun erc--inhibit-slash-cmd-insertion (state)
+  "Don't insert STATE object's message if it's a \"slash\" command."
+  (when (erc--input-split-cmdp state)
+    (setf (erc--input-split-insertp state) nil)))
 
 (defun erc-send-current-line ()
   "Parse current line and send it to IRC."
@@ -6511,8 +6599,9 @@ When the returned value is a string, ERC passes it to 
`erc-error'.")
                                  str erc--input-line-delim-regexp)
                          :cmdp (string-match erc-command-regexp str))))
             (run-hook-with-args 'erc--input-review-functions state)
-            (let ((inhibit-read-only t)
-                  (old-buf (current-buffer)))
+            (when-let (((not (erc--input-split-abortp state)))
+                       (inhibit-read-only t)
+                       (old-buf (current-buffer)))
               (progn ; unprogn this during next major surgery
                 (erc-set-active-buffer (current-buffer))
                 ;; Kill the input and the prompt
@@ -6541,12 +6630,11 @@ When the returned value is a string, ERC passes it to 
`erc-error'.")
    (erc-end-of-input-line)))
 
 (defun erc--discard-trailing-multiline-nulls (state)
-  "Ensure last line of STATE's string is non-null.
-But only when `erc-send-whitespace-lines' is non-nil.  STATE is
-an `erc--input-split' object."
-  (when (and erc-send-whitespace-lines (erc--input-split-lines state))
+  "Remove trailing empty lines from STATE, an `erc--input-split' object.
+When all lines are empty, remove all but the first."
+  (when (erc--input-split-lines state)
     (let ((reversed (nreverse (erc--input-split-lines state))))
-      (while (and reversed (string-empty-p (car reversed)))
+      (while (and (cdr reversed) (string-empty-p (car reversed)))
         (setq reversed (cdr reversed)))
       (setf (erc--input-split-lines state) (nreverse reversed)))))
 
@@ -6566,7 +6654,7 @@ multiline input.  Optionally readjust lines to protocol 
length
 limits and pad empty ones, knowing full well that additional
 processing may still corrupt messages before they reach the send
 queue.  Expect LINES-OBJ to be an `erc--input-split' object."
-  (when (or erc-send-pre-hook erc-pre-send-functions)
+  (progn ; FIXME remove `progn' after code review.
     (with-suppressed-warnings ((lexical str) (obsolete erc-send-this))
       (defvar str) ; see note in string `erc-send-input'.
       (let* ((str (string-join (erc--input-split-lines lines-obj) "\n"))
@@ -6597,9 +6685,8 @@ queue.  Expect LINES-OBJ to be an `erc--input-split' 
object."
   "Send lines in `erc--input-split-lines' object LINES-OBJ."
   (when (erc--input-split-sendp lines-obj)
     (dolist (line (erc--input-split-lines lines-obj))
-      (unless (erc--input-split-cmdp lines-obj)
-        (when (erc--input-split-insertp lines-obj)
-          (erc-display-msg line)))
+      (when (erc--input-split-insertp lines-obj)
+        (erc-display-msg line))
       (erc-process-input-line (concat line "\n")
                               (null erc-flood-protect)
                               (not (erc--input-split-cmdp lines-obj))))))
@@ -8020,6 +8107,22 @@ or `erc-kill-buffer-hook' if any other buffer."
      (t
       (run-hooks 'erc-kill-buffer-hook)))))
 
+(declare-function set-text-conversion-style "textconv.c")
+
+(defun erc-check-text-conversion ()
+  "Check if point is within the ERC prompt and toggle text conversion.
+If `text-conversion-style' is not `action' if point is within the
+prompt or `nil' otherwise, set it to such a value, so as to
+guarantee that the input method functions properly for the
+purpose of typing within the ERC prompt."
+  (when (and (eq major-mode 'erc-mode)
+             (fboundp 'set-text-conversion-style))
+    (if (>= (point) (erc-beg-of-input-line))
+        (unless (eq text-conversion-style 'action)
+          (set-text-conversion-style 'action))
+      (unless (not text-conversion-style)
+        (set-text-conversion-style nil)))))
+
 (defun erc-kill-server ()
   "Sends a QUIT command to the server when the server buffer is killed.
 This function should be on `erc-kill-server-hook'."
diff --git a/lisp/eshell/em-basic.el b/lisp/eshell/em-basic.el
index 016afe811b2..95d7d8f4ebe 100644
--- a/lisp/eshell/em-basic.el
+++ b/lisp/eshell/em-basic.el
@@ -188,6 +188,38 @@ or `eshell-printn' for display."
 
 (put 'eshell/umask 'eshell-no-numeric-conversions t)
 
+(defun eshell/eshell-debug (&rest args)
+  "A command for toggling certain debug variables."
+  (eshell-eval-using-options
+   "eshell-debug" args
+   '((?h "help" nil nil "display this usage message")
+     :usage "[KIND]...
+This command is used to aid in debugging problems related to Eshell
+itself.  It is not useful for anything else.  The recognized `kinds'
+are:
+
+   error       stops Eshell from trapping errors
+   form        shows command form manipulation in `*eshell last cmd*'
+   process     shows process events in `*eshell last cmd*'")
+   (if args
+       (dolist (kind args)
+         (if (equal kind "error")
+             (setq eshell-handle-errors (not eshell-handle-errors))
+           (let ((kind-sym (intern kind)))
+             (if (memq kind-sym eshell-debug-command)
+                 (setq eshell-debug-command
+                       (delq kind-sym eshell-debug-command))
+               (push kind-sym eshell-debug-command)))))
+     ;; Output the currently-enabled debug kinds.
+     (unless eshell-handle-errors
+       (eshell-print "errors\n"))
+     (dolist (kind eshell-debug-command)
+       (eshell-printn (symbol-name kind))))))
+
+(defun pcomplete/eshell-mode/eshell-debug ()
+  "Completion for the `debug' command."
+  (while (pcomplete-here '("error" "form" "process"))))
+
 (provide 'em-basic)
 
 ;; Local Variables:
diff --git a/lisp/eshell/em-cmpl.el b/lisp/eshell/em-cmpl.el
index 25dccbd695c..61f1237b907 100644
--- a/lisp/eshell/em-cmpl.el
+++ b/lisp/eshell/em-cmpl.el
@@ -343,7 +343,7 @@ to writing a completion function."
 (defun eshell-complete-parse-arguments ()
   "Parse the command line arguments for `pcomplete-argument'."
   (when (and eshell-no-completion-during-jobs
-            (eshell-interactive-process-p))
+             eshell-foreground-command)
     (eshell--pcomplete-insert-tab))
   (let ((end (point-marker))
        (begin (save-excursion (beginning-of-line) (point)))
diff --git a/lisp/eshell/em-dirs.el b/lisp/eshell/em-dirs.el
index 640d3676750..d62f36e56c2 100644
--- a/lisp/eshell/em-dirs.el
+++ b/lisp/eshell/em-dirs.el
@@ -253,26 +253,19 @@ Thus, this does not include the current directory.")
     (throw 'eshell-replace-command
           (eshell-parse-command "cd" (flatten-tree args)))))
 
-(defun eshell-expand-user-reference-1 (file)
+(defun eshell-expand-user-reference (file)
   "Expand a user reference in FILE to its real directory name."
   (replace-regexp-in-string
    (rx bos (group "~" (*? anychar)) (or "/" eos))
    #'expand-file-name file))
 
-(defun eshell-expand-user-reference (file)
-  "Expand a user reference in FILE to its real directory name.
-FILE can be either a string or a list of strings to expand."
-  ;; If the argument was a glob pattern, then FILE is a list, so
-  ;; expand each element of the glob's resulting list.
-  (if (listp file)
-      (mapcar #'eshell-expand-user-reference-1 file)
-    (eshell-expand-user-reference-1 file)))
-
 (defun eshell-parse-user-reference ()
   "An argument beginning with ~ is a filename to be expanded."
   (when (and (not eshell-current-argument)
              (eq (char-after) ?~))
-    (add-to-list 'eshell-current-modifiers #'eshell-expand-user-reference)
+    ;; Apply this modifier fairly early so it happens before things
+    ;; like glob expansion.
+    (add-hook 'eshell-current-modifiers #'eshell-expand-user-reference -50)
     (forward-char)
     (char-to-string (char-before))))
 
diff --git a/lisp/eshell/em-glob.el b/lisp/eshell/em-glob.el
index 1141b673e97..0d0ff6188b6 100644
--- a/lisp/eshell/em-glob.el
+++ b/lisp/eshell/em-glob.el
@@ -156,8 +156,8 @@ This mimics the behavior of zsh if non-nil, but bash if 
nil."
 (defun eshell-add-glob-modifier ()
   "Add `eshell-extended-glob' to the argument modifier list."
   (when eshell-glob-splice-results
-    (add-to-list 'eshell-current-modifiers 'eshell-splice-args t))
-  (add-to-list 'eshell-current-modifiers 'eshell-extended-glob))
+    (add-hook 'eshell-current-modifiers #'eshell-splice-args 99))
+  (add-hook 'eshell-current-modifiers #'eshell-extended-glob))
 
 (defun eshell-parse-glob-chars ()
   "Parse a globbing delimiter.
diff --git a/lisp/eshell/em-pred.el b/lisp/eshell/em-pred.el
index 1d67f1af990..ae7d0c43bc4 100644
--- a/lisp/eshell/em-pred.el
+++ b/lisp/eshell/em-pred.el
@@ -302,24 +302,14 @@ This function is specially for adding onto 
`eshell-parse-argument-hook'."
                   (preds (car modifiers))
                   (mods (cdr modifiers)))
               (when (or preds mods)
-                ;; Has to go at the end, which is only natural since
+                ;; Has to go near the end (but before
+                ;; `eshell-splice-args'), which is only natural since
                 ;; syntactically it can only occur at the end.
-                (setq eshell-current-modifiers
-                      (append
-                       eshell-current-modifiers
-                       (list
-                        (lambda (lst)
-                          (eshell-apply-modifiers
-                           lst preds mods modifier-string)))))
-                (when (memq 'eshell-splice-args eshell-current-modifiers)
-                  ;; If splicing results, ensure that
-                  ;; `eshell-splice-args' is the last modifier.
-                  ;; Eshell's command parsing can't handle it anywhere
-                  ;; else.
-                  (setq eshell-current-modifiers
-                        (append (delq 'eshell-splice-args
-                                      eshell-current-modifiers)
-                                (list 'eshell-splice-args)))))))
+                (add-hook 'eshell-current-modifiers
+                          (lambda (lst)
+                            (eshell-apply-modifiers
+                             lst preds mods modifier-string))
+                          90))))
          (goto-char (1+ end))
          (eshell-finish-arg))))))
 
diff --git a/lisp/eshell/em-prompt.el b/lisp/eshell/em-prompt.el
index 42f8f273b52..15b849a4d37 100644
--- a/lisp/eshell/em-prompt.el
+++ b/lisp/eshell/em-prompt.el
@@ -50,22 +50,18 @@ as is common with most shells."
 (defcustom eshell-prompt-function
   (lambda ()
     (concat (abbreviate-file-name (eshell/pwd))
+            (unless (eshell-exit-success-p)
+              (format " [%d]" eshell-last-command-status))
             (if (= (file-user-uid) 0) " # " " $ ")))
-  "A function that returns the Eshell prompt string.
-Make sure to update `eshell-prompt-regexp' so that it will match your
-prompt."
+  "A function that returns the Eshell prompt string."
   :type 'function
   :group 'eshell-prompt)
 
 (defcustom eshell-prompt-regexp "^[^#$\n]* [#$] "
-  "A regexp which fully matches your Eshell prompt.
-This is useful for navigating by paragraph using \
-\\[forward-paragraph] and \\[backward-paragraph].
-
-If this variable is changed, all Eshell buffers must be exited
-and re-entered for it to take effect."
+  "A regexp which fully matches your Eshell prompt."
   :type 'regexp
   :group 'eshell-prompt)
+(make-obsolete-variable 'eshell-prompt-regexp nil "30.1")
 
 (defcustom eshell-highlight-prompt t
   "If non-nil, Eshell should highlight the prompt."
@@ -98,8 +94,10 @@ arriving, or after."
   :group 'eshell-prompt)
 
 (defvar-keymap eshell-prompt-mode-map
-  "C-c C-n" #'eshell-next-prompt
-  "C-c C-p" #'eshell-previous-prompt)
+  "C-c C-n"                      #'eshell-next-prompt
+  "C-c C-p"                      #'eshell-previous-prompt
+  "<remap> <forward-paragraph>"  #'eshell-forward-paragraph
+  "<remap> <backward-paragraph>" #'eshell-backward-paragraph)
 
 (defvar-keymap eshell-prompt-repeat-map
   :doc "Keymap to repeat eshell-prompt key sequences.  Used in `repeat-mode'."
@@ -119,11 +117,6 @@ arriving, or after."
   "Initialize the prompting code."
   (unless eshell-non-interactive-p
     (add-hook 'eshell-post-command-hook 'eshell-emit-prompt nil t)
-
-    (make-local-variable 'eshell-prompt-regexp)
-    (if eshell-prompt-regexp
-        (setq-local paragraph-start eshell-prompt-regexp))
-
     (eshell-prompt-mode)))
 
 (defun eshell-emit-prompt ()
@@ -172,16 +165,41 @@ negative, find the Nth next match."
   (interactive (eshell-regexp-arg "Backward input matching (regexp): "))
   (eshell-forward-matching-input regexp (- arg)))
 
-(defun eshell-next-prompt (n)
+(defun eshell-forward-paragraph (&optional n)
+  "Move to the beginning of the Nth next prompt in the buffer.
+Like `forward-paragraph', but navigates using fields."
+  (interactive "p")
+  (eshell-next-prompt n)
+  (goto-char (field-beginning (point) t)))
+
+(defun eshell-backward-paragraph (&optional n)
+  "Move to the beginning of the Nth previous prompt in the buffer.
+Like `backward-paragraph', but navigates using fields."
+  (interactive "p")
+  (eshell-previous-prompt n)
+  (goto-char (field-beginning (point) t)))
+
+(defun eshell-next-prompt (&optional n)
   "Move to end of Nth next prompt in the buffer."
   (interactive "p")
+  (unless n (setq n 1))
+  ;; First, move point to our starting position: the end of the
+  ;; current prompt (aka the beginning of the input), if any.  (The
+  ;; welcome message and output from commands don't count as having a
+  ;; current prompt.)
+  (pcase (get-text-property (point) 'field)
+    ('command-output)
+    ('prompt (goto-char (field-end)))
+    (_ (when-let ((match (text-property-search-backward 'field 'prompt t)))
+         (goto-char (prop-match-end match)))))
+  ;; Now, move forward/backward to our destination prompt.
   (if (natnump n)
       (while (and (> n 0)
                   (text-property-search-forward 'field 'prompt t))
         (setq n (1- n)))
     (let (match this-match)
-      ;; Don't count the current prompt.
-      (text-property-search-backward 'field 'prompt t)
+      ;; Go to the beginning of the current prompt.
+      (goto-char (field-beginning (point) t))
       (while (and (< n 0)
                   (setq this-match (text-property-search-backward
                                     'field 'prompt t)))
@@ -190,10 +208,10 @@ negative, find the Nth next match."
       (when match
         (goto-char (prop-match-end match))))))
 
-(defun eshell-previous-prompt (n)
+(defun eshell-previous-prompt (&optional n)
   "Move to end of Nth previous prompt in the buffer."
   (interactive "p")
-  (eshell-next-prompt (- n)))
+  (eshell-next-prompt (- (or n 1))))
 
 (defun eshell-skip-prompt ()
   "Skip past the text matching regexp `eshell-prompt-regexp'.
diff --git a/lisp/eshell/em-script.el b/lisp/eshell/em-script.el
index 55a05076342..3a4c315ad15 100644
--- a/lisp/eshell/em-script.el
+++ b/lisp/eshell/em-script.el
@@ -89,26 +89,18 @@ This includes when running `eshell-command'."
 (defun eshell-source-file (file &optional args subcommand-p)
   "Execute a series of Eshell commands in FILE, passing ARGS.
 Comments begin with `#'."
-  (let ((orig (point))
-       (here (point-max)))
-    (goto-char (point-max))
-    (with-silent-modifications
-      ;; FIXME: Why not use a temporary buffer and avoid this
-      ;; "insert&delete" business?  --Stef
-      (insert-file-contents file)
-      (goto-char (point-max))
-      (throw 'eshell-replace-command
-             (prog1
-                 (list 'let
-                       (list (list 'eshell-command-name (list 'quote file))
-                             (list 'eshell-command-arguments
-                                   (list 'quote args)))
-                       (let ((cmd (eshell-parse-command (cons here (point)))))
-                         (if subcommand-p
-                             (setq cmd (list 'eshell-as-subcommand cmd)))
-                         cmd))
-               (delete-region here (point))
-               (goto-char orig))))))
+  (let ((cmd (eshell-parse-command `(:file . ,file))))
+    (when subcommand-p
+      (setq cmd `(eshell-as-subcommand ,cmd)))
+    (throw 'eshell-replace-command
+           `(let ((eshell-command-name ',file)
+                  (eshell-command-arguments ',args)
+                  ;; Don't print subjob messages by default.
+                  ;; Otherwise, if this function was called as a
+                  ;; subjob, then *all* commands in the script would
+                  ;; print start/stop messages.
+                  (eshell-subjob-messages nil))
+              ,cmd))))
 
 (defun eshell/source (&rest args)
   "Source a file in a subshell environment."
diff --git a/lisp/eshell/em-smart.el b/lisp/eshell/em-smart.el
index d5002a59d14..4c39a991ec6 100644
--- a/lisp/eshell/em-smart.el
+++ b/lisp/eshell/em-smart.el
@@ -294,7 +294,7 @@ and the end of the buffer are still visible."
        ((eq this-command 'self-insert-command)
        (if (eq last-command-event ? )
            (if (and eshell-smart-space-goes-to-end
-                    eshell-current-command)
+                    eshell-foreground-command)
                (if (not (pos-visible-in-window-p (point-max)))
                    (setq this-command 'scroll-up)
                  (setq this-command 'eshell-smart-goto-end))
diff --git a/lisp/eshell/esh-arg.el b/lisp/eshell/esh-arg.el
index 26be1127880..e7b5eef11db 100644
--- a/lisp/eshell/esh-arg.el
+++ b/lisp/eshell/esh-arg.el
@@ -293,10 +293,13 @@ then the result will be:
               (append (list 'eshell-concat eshell-current-quoted)
                       eshell-current-argument)))
       (setq eshell-arg-listified nil))
-    (while eshell-current-modifiers
+    (when eshell-current-modifiers
+      (eshell-debug-command 'form
+        "applying modifiers %S\n\n%s" eshell-current-modifiers
+        (eshell-stringify eshell-current-argument)))
+    (dolist (modifier eshell-current-modifiers)
       (setq eshell-current-argument
-           (list (car eshell-current-modifiers) eshell-current-argument)
-           eshell-current-modifiers (cdr eshell-current-modifiers))))
+            (list modifier eshell-current-argument))))
   (setq eshell-current-modifiers nil))
 
 (defun eshell-finish-arg (&rest arguments)
diff --git a/lisp/eshell/esh-cmd.el b/lisp/eshell/esh-cmd.el
index 80066263396..990d2ca1122 100644
--- a/lisp/eshell/esh-cmd.el
+++ b/lisp/eshell/esh-cmd.el
@@ -237,17 +237,6 @@ return non-nil if the command is complex."
   :version "24.1"                     ; removed eshell-cmd-initialize
   :type 'hook)
 
-(defcustom eshell-debug-command nil
-  "If non-nil, enable Eshell debugging code.
-This is slow, and only useful for debugging problems with Eshell.
-If you change this without using customize after Eshell has loaded,
-you must re-load `esh-cmd.el'."
-  :initialize 'custom-initialize-default
-  :set (lambda (symbol value)
-        (set symbol value)
-        (load "esh-cmd"))
-  :type 'boolean)
-
 (defcustom eshell-deferrable-commands
   '(eshell-named-command
     eshell-lisp-command
@@ -274,7 +263,24 @@ command line.")
 
 ;;; Internal Variables:
 
-(defvar eshell-current-command nil)
+;; These variables have been merged into `eshell-foreground-command'.
+;; Outside of this file, the most-common use for them is to check
+;; whether they're nil.
+(define-obsolete-variable-alias 'eshell-last-async-procs
+  'eshell-foreground-command "30.1")
+(define-obsolete-variable-alias 'eshell-current-command
+  'eshell-foreground-command "30.1")
+
+(defvar eshell-foreground-command nil
+  "The currently-running foreground command, if any.
+This is a list of the form (FORM PROCESSES).  FORM is the Eshell
+command form.  PROCESSES is a list of processes that deferred the
+command.")
+(defvar eshell-background-commands nil
+  "A list of currently-running deferred commands.
+Each element is of the form (FORM PROCESSES), as with
+`eshell-foreground-command' (which see).")
+
 (defvar eshell-command-name nil)
 (defvar eshell-command-arguments nil)
 (defvar eshell-in-pipeline-p nil
@@ -284,14 +290,6 @@ otherwise t.")
 (defvar eshell-in-subcommand-p nil)
 (defvar eshell-last-arguments nil)
 (defvar eshell-last-command-name nil)
-(defvar eshell-last-async-procs nil
-  "The currently-running foreground process(es).
-When executing a pipeline, this is a cons cell whose CAR is the
-first process (usually reading from stdin) and whose CDR is the
-last process (usually writing to stdout).  Otherwise, the CAR and
-CDR are the same process.
-
-When the process in the CDR completes, resume command evaluation.")
 
 (defvar eshell-allow-commands t
   "If non-nil, allow evaluating command forms (including Lisp forms).
@@ -308,42 +306,32 @@ also `eshell-complete-parse-arguments'.")
 
 (defsubst eshell-interactive-process-p ()
   "Return non-nil if there is a currently running command process."
-  eshell-last-async-procs)
+  (declare (obsolete 'eshell-foreground-command "30.1"))
+  eshell-foreground-command)
 
 (defsubst eshell-head-process ()
   "Return the currently running process at the head of any pipeline.
 This only returns external (non-Lisp) processes."
-  (car-safe eshell-last-async-procs))
+  (caadr eshell-foreground-command))
 
 (defsubst eshell-tail-process ()
   "Return the currently running process at the tail of any pipeline.
 This only returns external (non-Lisp) processes."
-  (cdr-safe eshell-last-async-procs))
+  (car (last (cadr eshell-foreground-command))))
 
 (define-obsolete-function-alias 'eshell-interactive-process
   'eshell-tail-process "29.1")
 
 (defun eshell-cmd-initialize ()     ;Called from `eshell-mode' via intern-soft!
   "Initialize the Eshell command processing module."
-  (setq-local eshell-current-command nil)
+  (setq-local eshell-foreground-command nil)
+  (setq-local eshell-background-commands nil)
   (setq-local eshell-command-name nil)
   (setq-local eshell-command-arguments nil)
   (setq-local eshell-last-arguments nil)
   (setq-local eshell-last-command-name nil)
-  (setq-local eshell-last-async-procs nil)
 
   (add-hook 'eshell-kill-hook #'eshell-resume-command nil t)
-
-  ;; make sure that if a command is over, and no process is being
-  ;; waited for, that `eshell-current-command' is set to nil.  This
-  ;; situation can occur, for example, if a Lisp function results in
-  ;; `debug' being called, and the user then types \\[top-level]
-  (add-hook 'eshell-post-command-hook
-            (lambda ()
-              (setq eshell-current-command nil
-                    eshell-last-async-procs nil))
-            nil t)
-
   (add-hook 'eshell-parse-argument-hook
            #'eshell-parse-subcommand-argument nil t)
   (add-hook 'eshell-parse-argument-hook
@@ -362,50 +350,105 @@ This only returns external (non-Lisp) processes."
       (throw 'pcomplete-completions
             (all-completions pcomplete-stub obarray 'boundp)))))
 
+;; Current command management
+
+(defun eshell-add-command (form &optional background)
+  "Add a command FORM to our list of known commands and return the new entry.
+If non-nil, BACKGROUND indicates that this is a command running
+in the background.  The result is a command entry in the
+form (BACKGROUND FORM PROCESSES), where PROCESSES is initially
+nil."
+  (cons (when background 'background)
+        (if background
+            (car (push (list form nil) eshell-background-commands))
+          (cl-assert (null eshell-foreground-command))
+          (setq eshell-foreground-command (list form nil)))))
+
+(defun eshell-remove-command (command)
+  "Remove COMMAND from our list of known commands.
+COMMAND should be a list of the form (BACKGROUND FORM PROCESSES),
+as returned by `eshell-add-command' (which see)."
+  (let ((background (car command))
+        (entry (cdr command)))
+    (if background
+        (setq eshell-background-commands
+              (delq entry eshell-background-commands))
+      (cl-assert (eq eshell-foreground-command entry))
+      (setq eshell-foreground-command nil))))
+
+(defun eshell-commands-for-process (process)
+  "Return all commands associated with a PROCESS.
+Each element will have the form (BACKGROUND FORM PROCESSES), as
+returned by `eshell-add-command' (which see).
+
+Usually, there should only be one element in this list, but it's
+theoretically possible to have more than one associated command
+for a given process."
+  (nconc (when (memq process (cadr eshell-foreground-command))
+           (list (cons nil eshell-foreground-command)))
+         (seq-keep (lambda (cmd)
+                     (when (memq process (cadr cmd))
+                       (cons 'background cmd)))
+                   eshell-background-commands)))
+
 ;; Command parsing
 
-(defmacro eshell-with-temp-command (region &rest body)
-  "Narrow the buffer to REGION and execute the forms in BODY.
+(defsubst eshell--region-p (object)
+  "Return non-nil if OBJECT is a pair of numbers or markers."
+  (and (consp object)
+       (number-or-marker-p (car object))
+       (number-or-marker-p (cdr object))))
 
-REGION is a cons cell (START . END) that specifies the region to
-which to narrow the buffer.  REGION can also be a string, in
-which case the macro temporarily inserts it into the buffer at
-point, and narrows the buffer to the inserted string.  Before
-executing BODY, point is set to the beginning of the narrowed
-REGION.
+(defmacro eshell-with-temp-command (command &rest body)
+  "Temporarily insert COMMAND into the buffer and execute the forms in BODY.
+
+COMMAND can be a string to insert, a cons cell (START . END)
+specifying a region in the current buffer, or (:file . FILENAME)
+to temporarily insert the contents of FILENAME.
+
+Before executing BODY, narrow the buffer to the text for COMMAND
+and and set point to the beginning of the narrowed region.
 
 The value returned is the last form in BODY."
   (declare (indent 1))
-  `(let ((reg ,region))
-     (if (stringp reg)
+  (let ((command-sym (make-symbol "command"))
+        (begin-sym (make-symbol "begin"))
+        (end-sym (make-symbol "end")))
+    `(let ((,command-sym ,command))
+       (if (eshell--region-p ,command-sym)
+           (save-restriction
+             (narrow-to-region (car ,command-sym) (cdr ,command-sym))
+             (goto-char (car ,command-sym))
+             ,@body)
          ;; Since parsing relies partly on buffer-local state
          ;; (e.g. that of `eshell-parse-argument-hook'), we need to
          ;; perform the parsing in the Eshell buffer.
-         (let ((begin (point)) end)
+         (let ((,begin-sym (point)) ,end-sym)
            (with-silent-modifications
-             (insert reg)
-             (setq end (point))
+             (if (stringp ,command-sym)
+                 (insert ,command-sym)
+               (forward-char (cadr (insert-file-contents (cdr ,command-sym)))))
+             (setq ,end-sym (point))
              (unwind-protect
                  (save-restriction
-                   (narrow-to-region begin end)
-                   (goto-char begin)
+                   (narrow-to-region ,begin-sym ,end-sym)
+                   (goto-char ,begin-sym)
                    ,@body)
-               (delete-region begin end))))
-       (save-restriction
-         (narrow-to-region (car reg) (cdr reg))
-         (goto-char (car reg))
-         ,@body))))
+               (delete-region ,begin-sym ,end-sym))))))))
 
 (defun eshell-parse-command (command &optional args toplevel)
   "Parse the COMMAND, adding ARGS if given.
-COMMAND can either be a string, or a cons cell demarcating a buffer
-region.  TOPLEVEL, if non-nil, means that the outermost command (the
-user's input command) is being parsed, and that pre and post command
-hooks should be run before and after the command."
+COMMAND can be a string, a cons cell (START . END) demarcating a
+buffer region, or (:file . FILENAME) to parse the contents of
+FILENAME.
+
+TOPLEVEL, if non-nil, means that the outermost command (the
+user's input command) is being parsed, and that pre and post
+command hooks should be run before and after the command."
   (pcase-let*
     ((terms
       (append
-       (if (consp command)
+       (if (eshell--region-p command)
            (eshell-parse-arguments (car command) (cdr command))
          (eshell-with-temp-command command
            (goto-char (point-max))
@@ -418,8 +461,6 @@ hooks should be run before and after the command."
        (lambda (cmd)
          (let ((sep (pop sep-terms)))
            (setq cmd (eshell-parse-pipeline cmd))
-           (when (equal sep "&")
-             (setq cmd `(eshell-do-subjob (cons :eshell-background ,cmd))))
            (unless eshell-in-pipeline-p
              (setq cmd `(eshell-trap-errors ,cmd)))
            ;; Copy I/O handles so each full statement can manipulate
@@ -427,31 +468,22 @@ hooks should be run before and after the command."
            ;; command in the list; we won't use the originals again
            ;; anyway.
            (setq cmd `(eshell-with-copied-handles ,cmd ,(not sep)))
+           (when (equal sep "&")
+             (setq cmd `(eshell-do-subjob ,cmd)))
            cmd))
        sub-chains)))
     (if toplevel
        `(eshell-commands (progn
                             (run-hooks 'eshell-pre-command-hook)
-                            (catch 'top-level (progn ,@commands))
-                            (run-hooks 'eshell-post-command-hook)))
+                            (unwind-protect
+                                (progn ,@commands)
+                              (run-hooks 'eshell-post-command-hook))))
       (macroexp-progn commands))))
 
-(defun eshell-debug-command (tag subform)
-  "Output a debugging message to `*eshell last cmd*'."
-  (let ((buf (get-buffer-create "*eshell last cmd*"))
-       (text (eshell-stringify eshell-current-command)))
-    (with-current-buffer buf
-      (if (not tag)
-         (erase-buffer)
-       (insert "\n\C-l\n" tag "\n\n" text
-               (if subform
-                   (concat "\n\n" (eshell-stringify subform)) ""))))))
-
 (defun eshell-debug-show-parsed-args (terms)
   "Display parsed arguments in the debug buffer."
-  (ignore
-   (if eshell-debug-command
-       (eshell-debug-command "parsed arguments" terms))))
+  (ignore (eshell-debug-command 'form
+            "parsed arguments\n\n%s" (eshell-stringify terms))))
 
 (defun eshell-no-command-conversion (terms)
   "Don't convert the command argument."
@@ -762,10 +794,13 @@ if none)."
 
 (defmacro eshell-do-subjob (object)
   "Evaluate a command OBJECT as a subjob.
-We indicate that the process was run in the background by returning it
-ensconced in a list."
-  `(let ((eshell-current-subjob-p t))
-     ,object))
+We indicate that the process was run in the background by
+returning it as (:eshell-background . PROCESSES)."
+  `(let ((eshell-current-subjob-p t)
+         ;; Print subjob messages.  This could have been cleared
+         ;; (e.g. by `eshell-source-file', which see).
+         (eshell-subjob-messages t))
+     (eshell-resume-eval (eshell-add-command ',object 'background))))
 
 (defmacro eshell-commands (object &optional silent)
   "Place a valid set of handles, and context, around command OBJECT."
@@ -784,15 +819,14 @@ to this hook using `nconc', and *not* `add-hook'.
 
 Someday, when Scheme will become the dominant Emacs language, all of
 this grossness will be made to disappear by using `call/cc'..."
-  `(let ((eshell-this-command-hook '(ignore)))
-     (eshell-condition-case err
-        (prog1
-            ,object
-          (mapc #'funcall eshell-this-command-hook))
-       (error
-       (mapc #'funcall eshell-this-command-hook)
-       (eshell-errorn (error-message-string err))
-       (eshell-close-handles 1)))))
+  `(eshell-condition-case err
+       (let ((eshell-this-command-hook '(ignore)))
+         (unwind-protect
+             ,object
+           (mapc #'funcall eshell-this-command-hook)))
+     (error
+      (eshell-errorn (error-message-string err))
+      (eshell-close-handles 1))))
 
 (defvar eshell-output-handle)           ;Defined in esh-io.el.
 (defvar eshell-error-handle)            ;Defined in esh-io.el.
@@ -814,89 +848,83 @@ current ones (see `eshell-duplicate-handles')."
      (eshell-protect-handles eshell-current-handles)
      ,object))
 
+(defun eshell--unmark-deferrable (command)
+  "If COMMAND is (or ends with) a deferrable command, unmark it as such.
+This changes COMMAND in-place by converting function calls listed
+in `eshell-deferrable-commands' to their non-deferrable forms so
+that Eshell doesn't erroneously allow deferring it.  For example,
+`eshell-named-command' becomes `eshell-named-command*', "
+  (let ((cmd command))
+    (when (memq (car cmd) '(let progn))
+      (setq cmd (car (last cmd))))
+    (when (memq (car cmd) eshell-deferrable-commands)
+      (setcar cmd (intern-soft
+                  (concat (symbol-name (car cmd)) "*"))))
+    command))
+
 (defmacro eshell-do-pipelines (pipeline &optional notfirst)
   "Execute the commands in PIPELINE, connecting each to one another.
+Returns a list of the processes in the pipeline.
+
 This macro calls itself recursively, with NOTFIRST non-nil."
   (when (setq pipeline (cadr pipeline))
+    (eshell--unmark-deferrable (car pipeline))
     `(eshell-with-copied-handles
-      (progn
-       ,(when (cdr pipeline)
-          `(let ((nextproc
-                  (eshell-do-pipelines (quote ,(cdr pipeline)) t)))
-              (eshell-set-output-handle ,eshell-output-handle
-                                        'append nextproc)))
-       ,(let ((head (car pipeline)))
-          (if (memq (car head) '(let progn))
-              (setq head (car (last head))))
-          (when (memq (car head) eshell-deferrable-commands)
-            (ignore
-             (setcar head
-                     (intern-soft
-                      (concat (symbol-name (car head)) "*"))))))
-       ;; First and last elements in a pipeline may need special treatment.
-       ;; (Currently only eshell-ls-files uses 'last.)
-       ;; Affects process-connection-type in eshell-gather-process-output.
-       (let ((eshell-in-pipeline-p
-              ,(cond ((not notfirst) (quote 'first))
-                     ((cdr pipeline) t)
-                     (t (quote 'last)))))
-          (let ((proc ,(car pipeline)))
-            (set headproc (or proc (symbol-value headproc)))
-            (set tailproc (or (symbol-value tailproc) proc))
-            proc)))
+      (let ((next-procs
+             ,(when (cdr pipeline)
+                `(eshell-do-pipelines (quote ,(cdr pipeline)) t)))
+            ;; First and last elements in a pipeline may need special
+            ;; treatment (currently only `eshell-ls-files' uses
+            ;; `last').  Affects `process-connection-type' in
+            ;; `eshell-gather-process-output'.
+            (eshell-in-pipeline-p
+             ,(cond ((not notfirst) (quote 'first))
+                    ((cdr pipeline) t)
+                    (t (quote 'last)))))
+        ,(when (cdr pipeline)
+           `(eshell-set-output-handle ,eshell-output-handle
+                                      'append (car next-procs)))
+        (let ((proc ,(car pipeline)))
+          (cons proc next-procs)))
       ;; Steal handles if this is the last item in the pipeline.
       ,(null (cdr pipeline)))))
 
 (defmacro eshell-do-pipelines-synchronously (pipeline)
   "Execute the commands in PIPELINE in sequence synchronously.
-Output of each command is passed as input to the next one in the pipeline.
-This is used on systems where async subprocesses are not supported."
+This collects the output of each command in turn, passing it as
+input to the next one in the pipeline.  Returns the result of the
+first command invocation in the pipeline (usually t or nil).
+
+This is used on systems where async subprocesses are not
+supported."
   (when (setq pipeline (cadr pipeline))
-    `(progn
+    ;; FIXME: is deferrable significant here?
+    (eshell--unmark-deferrable (car pipeline))
+    `(prog1
+         (eshell-with-copied-handles
+          (progn
+            ,(when (cdr pipeline)
+               `(let ((output-marker ,(point-marker)))
+                  (eshell-set-output-handle ,eshell-output-handle
+                                            'append output-marker)))
+            (let (;; XXX: `eshell-in-pipeline-p' has a different
+                  ;; meaning for synchronous processes: it's non-nil
+                  ;; only when piping *to* a process.
+                  (eshell-in-pipeline-p ,(and (cdr pipeline) t)))
+              ,(car pipeline)))
+          ;; Steal handles if this is the last item in the pipeline.
+          ,(null (cdr pipeline)))
        ,(when (cdr pipeline)
-          `(let ((output-marker ,(point-marker)))
-             (eshell-set-output-handle ,eshell-output-handle
-                                       'append output-marker)))
-       ,(let ((head (car pipeline)))
-          (if (memq (car head) '(let progn))
-              (setq head (car (last head))))
-          ;; FIXME: is deferrable significant here?
-          (when (memq (car head) eshell-deferrable-commands)
-            (ignore
-             (setcar head
-                     (intern-soft
-                      (concat (symbol-name (car head)) "*"))))))
-       ;; The last process in the pipe should get its handles
-       ;; redirected as we found them before running the pipe.
-       ,(if (null (cdr pipeline))
-            '(progn
-               (setq eshell-current-handles tail-handles)
-               (setq eshell-in-pipeline-p nil)))
-       (let ((result ,(car pipeline)))
-         ;; tailproc gets the result of the last successful process in
-         ;; the pipeline.
-         (set tailproc (or result (symbol-value tailproc)))
-         ,(if (cdr pipeline)
-              `(eshell-do-pipelines-synchronously (quote ,(cdr pipeline))))
-         result))))
+          `(eshell-do-pipelines-synchronously (quote ,(cdr pipeline)))))))
 
 (defalias 'eshell-process-identity 'identity)
 
 (defmacro eshell-execute-pipeline (pipeline)
   "Execute the commands in PIPELINE, connecting each to one another."
-  `(let ((eshell-in-pipeline-p t)
-         (headproc (make-symbol "headproc"))
-         (tailproc (make-symbol "tailproc")))
-     (set headproc nil)
-     (set tailproc nil)
-     (progn
-       ,(if eshell-supports-asynchronous-processes
-           `(eshell-do-pipelines ,pipeline)
-          `(let ((tail-handles (eshell-duplicate-handles
-                                eshell-current-handles)))
-            (eshell-do-pipelines-synchronously ,pipeline)))
-       (eshell-process-identity (cons (symbol-value headproc)
-                                      (symbol-value tailproc))))))
+  `(eshell-process-identity
+    ,(if eshell-supports-asynchronous-processes
+         `(remove nil (eshell-do-pipelines ,pipeline))
+       `(eshell-do-pipelines-synchronously ,pipeline))))
 
 (defmacro eshell-as-subcommand (command)
   "Execute COMMAND as a subcommand.
@@ -942,38 +970,6 @@ This avoids the need to use `let*'."
 ;; finishes, it will resume the evaluation using the remainder of the
 ;; command tree.
 
-(defun eshell/eshell-debug (&rest args)
-  "A command for toggling certain debug variables."
-  (ignore
-   (cond
-    ((not args)
-     (if eshell-handle-errors
-        (eshell-print "errors\n"))
-     (if eshell-debug-command
-        (eshell-print "commands\n")))
-    ((member (car args) '("-h" "--help"))
-     (eshell-print "usage: eshell-debug [kinds]
-
-This command is used to aid in debugging problems related to Eshell
-itself.  It is not useful for anything else.  The recognized `kinds'
-at the moment are:
-
-  errors       stops Eshell from trapping errors
-  commands     shows command execution progress in `*eshell last cmd*'
-"))
-    (t
-     (while args
-       (cond
-       ((string= (car args) "errors")
-        (setq eshell-handle-errors (not eshell-handle-errors)))
-       ((string= (car args) "commands")
-        (setq eshell-debug-command (not eshell-debug-command))))
-       (setq args (cdr args)))))))
-
-(defun pcomplete/eshell-mode/eshell-debug ()
-  "Completion for the `debug' command."
-  (while (pcomplete-here '("errors" "commands"))))
-
 (iter-defun eshell--find-subcommands (haystack)
   "Recursively search for subcommand forms in HAYSTACK.
 This yields the SUBCOMMANDs when found in forms like
@@ -1038,67 +1034,89 @@ Return the process (or head and tail processes) created 
by
 COMMAND, if any.  If COMMAND is a background command, return the
 process(es) in a cons cell like:
 
-  (:eshell-background . PROCESS)"
-  (if eshell-current-command
-      ;; We can just stick the new command at the end of the current
-      ;; one, and everything will happen as it should.
-      (setcdr (last (cdr eshell-current-command))
-              (list `(let ((here (and (eobp) (point))))
-                       ,(and input
-                             `(insert-and-inherit ,(concat input "\n")))
-                       (if here
-                           (eshell-update-markers here))
-                       (eshell-do-eval ',command))))
-    (and eshell-debug-command
-         (with-current-buffer (get-buffer-create "*eshell last cmd*")
-           (erase-buffer)
-           (insert "command: \"" input "\"\n")))
-    (setq eshell-current-command command)
+  (:eshell-background . PROCESSES)"
+  (if eshell-foreground-command
+      (progn
+        ;; We can just stick the new command at the end of the current
+        ;; one, and everything will happen as it should.
+        (setcdr (last (cdar eshell-foreground-command))
+                (list `(let ((here (and (eobp) (point))))
+                         ,(and input
+                               `(insert-and-inherit ,(concat input "\n")))
+                         (if here
+                             (eshell-update-markers here))
+                         (eshell-do-eval ',command))))
+        (eshell-debug-command 'form
+          "enqueued command form for %S\n\n%s"
+          (or input "<no string>")
+          (eshell-stringify (car eshell-foreground-command))))
+    (eshell-debug-command-start input)
     (let* (result
            (delim (catch 'eshell-incomplete
-                    (ignore (setq result (eshell-resume-eval))))))
+                    (ignore (setq result (eshell-resume-eval
+                                          (eshell-add-command command)))))))
       (when delim
         (error "Unmatched delimiter: %S" delim))
       result)))
 
 (defun eshell-resume-command (proc status)
-  "Resume the current command when a process ends."
+  "Resume the current command when a pipeline ends.
+PROC is the process that invoked this from its sentinel, and
+STATUS is its status."
   (when proc
-    (unless (or (not (stringp status))
-               (string= "stopped" status)
-               (string-match eshell-reset-signals status))
-      (if (eq proc (eshell-tail-process))
-         (eshell-resume-eval)))))
-
-(defun eshell-resume-eval ()
-  "Destructively evaluate a form which may need to be deferred."
+    (dolist (command (eshell-commands-for-process proc))
+      (unless (seq-some #'eshell-process-active-p (nth 2 command))
+        (setf (nth 2 command) nil) ; Clear processes from command.
+        (if (and ;; Check STATUS to determine whether we want to resume or
+                 ;; abort the command.
+                 (stringp status)
+                 (not (string= "stopped" status))
+                 (not (string-match eshell-reset-signals status)))
+            (eshell-resume-eval command)
+          (eshell-remove-command command)
+          (declare-function eshell-reset "esh-mode" (&optional no-hooks))
+          (eshell-reset))))))
+
+(defun eshell-resume-eval (command)
+  "Destructively evaluate a COMMAND which may need to be deferred.
+COMMAND is a command entry of the form (BACKGROUND FORM
+PROCESSES) (see `eshell-add-command').
+
+Return the result of COMMAND's FORM if it wasn't deferred.  If
+BACKGROUND is non-nil and Eshell defers COMMAND, return a list of
+the form (:eshell-background . PROCESSES)."
   (eshell-condition-case err
-      (progn
-       (setq eshell-last-async-procs nil)
-       (when eshell-current-command
-         (let* (retval
-                (procs (catch 'eshell-defer
-                        (ignore
-                         (setq retval
-                               (eshell-do-eval
-                                eshell-current-command))))))
-           (if (eshell-process-pair-p procs)
-               (ignore (setq eshell-last-async-procs procs))
-             (cadr retval)))))
+      (let (retval procs)
+        (unwind-protect
+            (progn
+              (setq procs
+                    (catch 'eshell-defer
+                      (ignore (setq retval (eshell-do-eval (cadr command))))))
+              (cond
+               (retval (cadr retval))
+               ((car command) (cons :eshell-background procs))))
+          (if procs
+              (setf (nth 2 command) procs)
+            ;; If we didn't defer this command, clear it out.  This
+            ;; applies both when the command has finished normally,
+            ;; and when a signal or thrown value causes us to unwind.
+            (eshell-remove-command command))))
     (error
      (error (error-message-string err)))))
 
-(defmacro eshell-manipulate (tag &rest commands)
-  "Manipulate a COMMAND form, with TAG as a debug identifier."
-  (declare (indent 1))
-  ;; Check `bound'ness since at compile time the code until here has not
-  ;; executed yet.
-  (if (not (and (boundp 'eshell-debug-command) eshell-debug-command))
-      `(progn ,@commands)
-    `(progn
-       (eshell-debug-command ,(eval tag) form)
-       ,@commands
-       (eshell-debug-command ,(concat "done " (eval tag)) form))))
+(defmacro eshell-manipulate (form tag &rest body)
+  "Manipulate a command FORM with BODY, using TAG as a debug identifier."
+  (declare (indent 2))
+  (let ((tag-symbol (make-symbol "tag")))
+    `(if (not (memq 'form eshell-debug-command))
+         (progn ,@body)
+       (let ((,tag-symbol ,tag))
+         (eshell-always-debug-command 'form
+           "%s\n\n%s" ,tag-symbol (eshell-stringify ,form))
+         (unwind-protect
+             (progn ,@body)
+           (eshell-always-debug-command 'form
+             "done %s\n\n%s" ,tag-symbol (eshell-stringify ,form)))))))
 
 (defun eshell-do-eval (form &optional synchronous-p)
   "Evaluate FORM, simplifying it as we go.
@@ -1125,8 +1143,8 @@ have been replaced by constants."
     ;; we can modify any `let' forms to evaluate only once.
     (if (macrop (car form))
         (let ((exp (copy-tree (macroexpand form))))
-         (eshell-manipulate (format-message "expanding macro `%s'"
-                                            (symbol-name (car form)))
+          (eshell-manipulate form
+              (format-message "expanding macro `%s'" (symbol-name (car form)))
            (setcar form (car exp))
            (setcdr form (cdr exp)))))
     (let ((args (cdr form)))
@@ -1138,7 +1156,7 @@ have been replaced by constants."
         (let ((new-form (copy-tree `(let ((eshell--command-body nil)
                                           (eshell--test-body nil))
                                       (eshell--wrapped-while ,@args)))))
-          (eshell-manipulate "modifying while form"
+          (eshell-manipulate form "modifying while form"
             (setcar form (car new-form))
             (setcdr form (cdr new-form)))
           (eshell-do-eval form synchronous-p)))
@@ -1161,17 +1179,22 @@ have been replaced by constants."
           (setq eshell--command-body nil
                 eshell--test-body (copy-tree (car args)))))
        ((eq (car form) 'if)
-        (eshell-manipulate "evaluating if condition"
-          (setcar args (eshell-do-eval (car args) synchronous-p)))
-        (eshell-do-eval
-         (cond
-          ((eval (car args))            ; COND is non-nil
-           (cadr args))
-          ((cdddr args)                 ; Multiple ELSE forms
-           `(progn ,@(cddr args)))
-          (t                            ; Zero or one ELSE forms
-           (caddr args)))
-         synchronous-p))
+        (eshell-manipulate form "evaluating if condition"
+          ;; Evaluate the condition and replace our `if' form with
+          ;; THEN or ELSE as appropriate.
+          (let ((new-form
+                 (cond
+                  ((cadr (eshell-do-eval (car args) synchronous-p))
+                   (cadr args))            ; COND is non-nil
+                  ((cdddr args)
+                   `(progn ,@(cddr args))) ; Multiple ELSE forms
+                  (t
+                   (caddr args)))))        ; Zero or one ELSE forms
+            (unless (consp new-form)
+              (setq new-form (cons 'progn new-form)))
+            (setcar form (car new-form))
+            (setcdr form (cdr new-form))))
+        (eshell-do-eval form synchronous-p))
        ((eq (car form) 'setcar)
        (setcar (cdr args) (eshell-do-eval (cadr args) synchronous-p))
        (eval form))
@@ -1180,7 +1203,7 @@ have been replaced by constants."
        (eval form))
        ((eq (car form) 'let)
         (unless (eq (car-safe (cadr args)) 'eshell-do-eval)
-          (eshell-manipulate "evaluating let args"
+          (eshell-manipulate form "evaluating let args"
             (dolist (letarg (car args))
               (when (and (listp letarg)
                          (not (eq (cadr letarg) 'quote)))
@@ -1207,7 +1230,7 @@ have been replaced by constants."
             ;; the let-bindings' values so that those values are
             ;; correct when we resume evaluation of this form.
             (when deferred
-              (eshell-manipulate "rebinding let args after `eshell-defer'"
+              (eshell-manipulate form "rebinding let args after `eshell-defer'"
                 (let ((bindings (car args)))
                   (while bindings
                     (let ((binding (if (consp (car bindings))
@@ -1221,30 +1244,50 @@ have been replaced by constants."
             ;; If we get here, there was no `eshell-defer' thrown, so
             ;; just return the `let' body's result.
             result)))
-       ((memq (car form) '(catch condition-case unwind-protect))
-       ;; `condition-case' and `unwind-protect' have to be
-       ;; handled specially, because we only want to call
-       ;; `eshell-do-eval' on their first form.
+       ((memq (car form) '(catch condition-case))
+        ;; `catch' and `condition-case' have to be handled specially,
+        ;; because we only want to call `eshell-do-eval' on their
+        ;; second forms.
        ;;
        ;; NOTE: This requires obedience by all forms which this
        ;; function might encounter, that they do not contain
        ;; other special forms.
-       (unless (eq (car form) 'unwind-protect)
-         (setq args (cdr args)))
+        (setq args (cdr args))
        (unless (eq (caar args) 'eshell-do-eval)
-         (eshell-manipulate "handling special form"
+          (eshell-manipulate form "handling special form"
            (setcar args `(eshell-do-eval ',(car args) ,synchronous-p))))
        (eval form))
+       ((eq (car form) 'unwind-protect)
+        ;; `unwind-protect' has to be handled specially, because we
+        ;; only want to call `eshell-do-eval' on its first form, and
+        ;; we need to ensure we let `eshell-defer' through without
+        ;; evaluating the unwind forms.
+        (let (deferred)
+          (unwind-protect
+              (eshell-manipulate form "handling `unwind-protect' body form"
+                (setq deferred
+                      (catch 'eshell-defer
+                        (ignore
+                         (setcar args (eshell-do-eval
+                                       (car args) synchronous-p)))))
+                (car args))
+            (if deferred
+                (throw 'eshell-defer deferred)
+              (eshell-manipulate form "handling `unwind-protect' unwind forms"
+                (pop args)
+                (while args
+                  (setcar args (eshell-do-eval (car args) synchronous-p))
+                  (pop args)))))))
        ((eq (car form) 'setq)
        (if (cddr args) (error "Unsupported form (setq X1 E1 X2 E2..)"))
-        (eshell-manipulate "evaluating arguments to setq"
+        (eshell-manipulate form "evaluating arguments to setq"
           (setcar (cdr args) (eshell-do-eval (cadr args) synchronous-p)))
        (list 'quote (eval form)))
        (t
        (if (and args (not (memq (car form) '(run-hooks))))
-           (eshell-manipulate
+            (eshell-manipulate form
                (format-message "evaluating arguments to `%s'"
-                               (symbol-name (car form)))
+                               (car form))
              (while args
                (setcar args (eshell-do-eval (car args) synchronous-p))
                (setq args (cdr args)))))
@@ -1283,16 +1326,15 @@ have been replaced by constants."
                      (setq result (eval form))))))
            (if new-form
                (progn
-                 (eshell-manipulate "substituting replacement form"
+                  (eshell-manipulate form "substituting replacement form"
                    (setcar form (car new-form))
                    (setcdr form (cdr new-form)))
                  (eshell-do-eval form synchronous-p))
               (if-let (((memq (car form) eshell-deferrable-commands))
-                       ((not eshell-current-subjob-p))
-                       (procs (eshell-make-process-pair result)))
+                       (procs (eshell-make-process-list result)))
                   (if synchronous-p
-                     (eshell/wait (cdr procs))
-                   (eshell-manipulate "inserting ignore form"
+                     (apply #'eshell/wait procs)
+                   (eshell-manipulate form "inserting ignore form"
                      (setcar form 'ignore)
                      (setcdr form nil))
                    (throw 'eshell-defer procs))
@@ -1341,16 +1383,24 @@ have been replaced by constants."
 COMMAND may result in an alias being executed, or a plain command."
   (unless eshell-allow-commands
     (signal 'eshell-commands-forbidden '(named)))
+  ;; Strip off any leading nil values.  This can only happen if a
+  ;; variable evaluates to nil, such as "$var x", where `var' is nil.
+  ;; In that case, the command name becomes `x', for compatibility
+  ;; with most regular shells (the difference is that they do an
+  ;; interpolation pass before the argument parsing pass, but Eshell
+  ;; does both at the same time).
+  (while (and (not command) args)
+    (setq command (pop args)))
   (setq eshell-last-arguments args
-       eshell-last-command-name (eshell-stringify command))
+        eshell-last-command-name (eshell-stringify command))
   (run-hook-with-args 'eshell-prepare-command-hook)
   (cl-assert (stringp eshell-last-command-name))
-  (if eshell-last-command-name
-      (or (run-hook-with-args-until-success
-          'eshell-named-command-hook eshell-last-command-name
-          eshell-last-arguments)
-         (eshell-plain-command eshell-last-command-name
-                               eshell-last-arguments))))
+  (when eshell-last-command-name
+    (or (run-hook-with-args-until-success
+         'eshell-named-command-hook eshell-last-command-name
+         eshell-last-arguments)
+        (eshell-plain-command eshell-last-command-name
+                              eshell-last-arguments))))
 
 (defalias 'eshell-named-command* 'eshell-named-command)
 
diff --git a/lisp/eshell/esh-io.el b/lisp/eshell/esh-io.el
index c07f871dd37..d0f1e04e925 100644
--- a/lisp/eshell/esh-io.el
+++ b/lisp/eshell/esh-io.el
@@ -170,7 +170,7 @@ describing the mode, e.g. for using with 
`eshell-get-target'.")
 
 (defvar eshell-current-handles nil)
 
-(defvar eshell-last-command-status 0
+(defvar-local eshell-last-command-status 0
   "The exit code from the last command.  0 if successful.")
 
 (defvar eshell-last-command-result nil
@@ -648,8 +648,11 @@ Returns what was actually sent, or nil if nothing was 
sent.")
       (process-send-string target object)
     (error
      ;; If `process-send-string' raises an error and the process has
-     ;; finished, treat it as a broken pipe.  Otherwise, just
-     ;; re-throw the signal.
+     ;; finished, treat it as a broken pipe.  Otherwise, just re-raise
+     ;; the signal.  NOTE: When running Emacs in batch mode
+     ;; (e.g. during regression tests), Emacs can abort due to SIGPIPE
+     ;; here.  Maybe `process-send-string' should handle SIGPIPE even
+     ;; in batch mode (bug#66186).
      (if (process-live-p target)
          (signal (car err) (cdr err))
        (signal 'eshell-pipe-broken (list target)))))
diff --git a/lisp/eshell/esh-mode.el b/lisp/eshell/esh-mode.el
index 0c381dbb86a..2b560afb92c 100644
--- a/lisp/eshell/esh-mode.el
+++ b/lisp/eshell/esh-mode.el
@@ -453,7 +453,7 @@ and the hook `eshell-exit-hook'."
                     last-command-event))))
 
 (defun eshell-intercept-commands ()
-  (when (and (eshell-interactive-process-p)
+  (when (and eshell-foreground-command
             (not (and (integerp last-input-event)
                       (memq last-input-event '(?\C-x ?\C-c)))))
     (let ((possible-events (where-is-internal this-command))
@@ -967,7 +967,7 @@ buffer's process if STRING contains a password prompt 
defined by
 `eshell-password-prompt-regexp'.
 
 This function could be in the list `eshell-output-filter-functions'."
-  (when (eshell-interactive-process-p)
+  (when eshell-foreground-command
     (save-excursion
       (let ((case-fold-search t))
        (goto-char eshell-last-output-block-begin)
diff --git a/lisp/eshell/esh-proc.el b/lisp/eshell/esh-proc.el
index 9c4036004ff..126c7d0f26e 100644
--- a/lisp/eshell/esh-proc.el
+++ b/lisp/eshell/esh-proc.el
@@ -100,6 +100,8 @@ information, for example."
 (defvar eshell-supports-asynchronous-processes (fboundp 'make-process)
   "Non-nil if Eshell can create asynchronous processes.")
 
+(defvar eshell-subjob-messages t
+  "Non-nil if we should print process start/end messages for subjobs.")
 (defvar eshell-current-subjob-p nil)
 
 (defvar eshell-process-list nil
@@ -112,6 +114,7 @@ To add or remove elements of this list, see
 `eshell-record-process-object' and `eshell-remove-process-entry'.")
 
 (declare-function eshell-send-eof-to-process "esh-mode")
+(declare-function eshell-interactive-filter "esh-mode" (buffer string))
 (declare-function eshell-tail-process "esh-cmd")
 
 (defvar-keymap eshell-proc-mode-map
@@ -128,6 +131,7 @@ To add or remove elements of this list, see
   "Function run when killing a process.
 Runs `eshell-reset-after-proc' and `eshell-kill-hook', passing arguments
 PROC and STATUS to functions on the latter."
+  (declare (obsolete nil "30.1"))
   ;; Was there till 24.1, but it is not optional.
   (remove-hook 'eshell-kill-hook #'eshell-reset-after-proc)
   ;; Only reset the prompt if this process is running interactively.
@@ -150,17 +154,28 @@ PROC and STATUS to functions on the latter."
   "Reset the command input location after a process terminates.
 The signals which will cause this to happen are matched by
 `eshell-reset-signals'."
+  (declare (obsolete nil "30.1"))
   (when (and (stringp status)
             (string-match eshell-reset-signals status))
     (require 'esh-mode)
     (declare-function eshell-reset "esh-mode" (&optional no-hooks))
     (eshell-reset)))
 
+(defun eshell-process-active-p (process)
+  "Return non-nil if PROCESS is active.
+This is like `process-live-p', but additionally checks whether
+`eshell-sentinel' has finished all of its work yet."
+  (or (process-live-p process)
+      ;; If we have handles, this is an Eshell-managed
+      ;; process.  Wait until we're 100% done and have
+      ;; cleared out the handles (see `eshell-sentinel').
+      (process-get process :eshell-handles)))
+
 (defun eshell-wait-for-process (&rest procs)
   "Wait until PROCS have successfully completed."
   (dolist (proc procs)
     (when (eshell-processp proc)
-      (while (process-live-p proc)
+      (while (eshell-process-active-p proc)
         (when (input-pending-p)
           (discard-input))
         (sit-for eshell-process-wait-seconds
@@ -230,8 +245,9 @@ The prompt will be set to PROMPT."
 
 (defsubst eshell-record-process-object (object)
   "Record OBJECT as now running."
-  (when (and (eshell-processp object)
-            eshell-current-subjob-p)
+  (when (and eshell-subjob-messages
+             eshell-current-subjob-p
+             (eshell-processp object))
     (require 'esh-mode)
     (declare-function eshell-interactive-print "esh-mode" (string))
     (eshell-interactive-print
@@ -240,11 +256,12 @@ The prompt will be set to PROMPT."
 
 (defun eshell-remove-process-entry (entry)
   "Record the process ENTRY as fully completed."
-  (if (and (eshell-processp (car entry))
-          (cdr entry)
-          eshell-done-messages-in-minibuffer)
-      (message "[%s]+ Done %s" (process-name (car entry))
-              (process-command (car entry))))
+  (when (and eshell-subjob-messages
+             eshell-done-messages-in-minibuffer
+             (eshell-processp (car entry))
+             (cdr entry))
+    (message "[%s]+ Done %s" (process-name (car entry))
+             (process-command (car entry))))
   (setq eshell-process-list
        (delq entry eshell-process-list)))
 
@@ -266,6 +283,8 @@ nil, write to `eshell-output-handle'."
   "A marker that tracks the beginning of output of the last subprocess.
 Used only on systems which do not support async subprocesses.")
 
+(defvar tramp-remote-path)
+
 (defun eshell-gather-process-output (command args)
   "Gather the output from COMMAND + ARGS."
   (require 'esh-var)
@@ -273,7 +292,9 @@ Used only on systems which do not support async 
subprocesses.")
   (unless (and (file-executable-p command)
               (file-regular-p (file-truename command)))
     (error "%s: not an executable file" command))
-  (let* ((delete-exited-processes
+  (let* ((real-path (getenv "PATH"))
+         (tramp-remote-path (bound-and-true-p tramp-remote-path))
+         (delete-exited-processes
          (if eshell-current-subjob-p
              eshell-delete-exited-processes
            delete-exited-processes))
@@ -281,6 +302,16 @@ Used only on systems which do not support async 
subprocesses.")
          (coding-system-for-read coding-system-for-read)
          (coding-system-for-write coding-system-for-write)
         proc stderr-proc decoding encoding changed)
+    ;; HACK: We want to supply our subprocess with the all the
+    ;; environment variables we've set in Eshell.  However, supplying
+    ;; a remote PATH this way can break Tramp, which needs the *local*
+    ;; PATH for calling "ssh", etc.  Instead, set the local path in
+    ;; our `process-environment' and pass the remote PATH via
+    ;; `tramp-remote-path'.  (If we handle this some better way in the
+    ;; future, remember to remove `tramp-remote-path' above, too.)
+    (when (file-remote-p default-directory)
+      (push (concat "PATH=" real-path) process-environment)
+      (setq tramp-remote-path (eshell-get-path)))
     ;; MS-Windows needs special setting of encoding/decoding, because
     ;; (a) non-ASCII text in command-line arguments needs to be
     ;; encoded in the system's codepage; and (b) because many Windows
@@ -332,8 +363,19 @@ Used only on systems which do not support async 
subprocesses.")
                :connection-type conn-type
                :stderr stderr-proc
                :file-handler t)))
+      (eshell-debug-command 'process
+        "started external process `%s'\n\n%s" proc
+        (mapconcat (lambda (i) (shell-quote-argument i 'posix))
+                   (process-command proc) " "))
       (eshell-record-process-object proc)
       (eshell-record-process-properties proc)
+      (when stderr-proc
+        ;; Provide a shared flag between the primary and stderr
+        ;; processes.  This lets the primary process wait to clean up
+        ;; until stderr is totally finished (see `eshell-sentinel').
+        (let ((stderr-live (list t)))
+          (process-put proc :eshell-stderr-live stderr-live)
+          (process-put stderr-proc :eshell-stderr-live stderr-live)))
       (run-hook-with-args 'eshell-exec-hook proc)
       (when (fboundp 'process-coding-system)
        (let ((coding-systems (process-coding-system proc)))
@@ -398,7 +440,7 @@ Used only on systems which do not support async 
subprocesses.")
        (eshell-close-handles
          (if (numberp exit-status) exit-status -1)
          (list 'quote (and (numberp exit-status) (= exit-status 0))))
-       (eshell-kill-process-function command exit-status)
+       (run-hook-with-args 'eshell-kill-hook command exit-status)
        (or (bound-and-true-p eshell-in-pipeline-p)
            (setq eshell-last-sync-output-start nil))
        (if (not (numberp exit-status))
@@ -410,9 +452,9 @@ Used only on systems which do not support async 
subprocesses.")
   "Send the output from PROCESS (STRING) to the interactive display.
 This is done after all necessary filtering has been done."
   (when string
+    (eshell-debug-command 'process
+      "received output from process `%s'\n\n%s" process string)
     (eshell--mark-as-output 0 (length string) string)
-    (require 'esh-mode)
-    (declare-function eshell-interactive-filter "esh-mode" (buffer string))
     (eshell-interactive-filter (if process (process-buffer process)
                                  (current-buffer))
                                string)))
@@ -424,19 +466,23 @@ This is done after all necessary filtering has been done."
   "Insert a string into the eshell buffer, or a process/file/buffer.
 PROC is the process for which we're inserting output.  STRING is the
 output."
+  (eshell-debug-command 'process
+    "received output from process `%s'\n\n%s" proc string)
   (when (buffer-live-p (process-buffer proc))
     (with-current-buffer (process-buffer proc)
       (process-put proc :eshell-pending
                    (concat (process-get proc :eshell-pending)
                            string))
-      (unless (process-get proc :eshell-busy) ; Already being handled?
-        (while (process-get proc :eshell-pending)
-          (let ((handles (process-get proc :eshell-handles))
-                (index (process-get proc :eshell-handle-index))
-                (data (process-get proc :eshell-pending)))
-            (process-put proc :eshell-pending nil)
-            (process-put proc :eshell-busy t)
-            (unwind-protect
+      (if (process-get proc :eshell-busy)
+          (eshell-debug-command 'process "i/o busy for process `%s'" proc)
+        (unwind-protect
+            (let ((handles (process-get proc :eshell-handles))
+                  (index (process-get proc :eshell-handle-index))
+                  data)
+              (while (setq data (process-get proc :eshell-pending))
+                (process-put proc :eshell-pending nil)
+                (eshell-debug-command 'process
+                  "forwarding output from process `%s'\n\n%s" proc data)
                 (condition-case nil
                     (eshell-output-object data index handles)
                   ;; FIXME: We want to send SIGPIPE to the process
@@ -454,46 +500,65 @@ output."
                    (if (or (process-get proc 'remote-pid)
                            (eq system-type 'windows-nt))
                        (delete-process proc)
-                     (signal-process proc 'SIGPIPE))))
-              (process-put proc :eshell-busy nil))))))))
+                     (signal-process proc 'SIGPIPE))))))
+                (process-put proc :eshell-busy nil))))))
 
 (defun eshell-sentinel (proc string)
   "Generic sentinel for command processes.  Reports only signals.
 PROC is the process that's exiting.  STRING is the exit message."
-  (when (buffer-live-p (process-buffer proc))
+  (eshell-debug-command 'process
+    "sentinel for external process `%s': %S" proc string)
+  (when (and (buffer-live-p (process-buffer proc))
+             (not (string= string "run")))
     (with-current-buffer (process-buffer proc)
       (unwind-protect
-          (unless (string= string "run")
-            ;; Write the exit message if the status is abnormal and
-            ;; the process is already writing to the terminal.
+          (let* ((handles (process-get proc :eshell-handles))
+                 (index (process-get proc :eshell-handle-index))
+                 (primary (= index eshell-output-handle))
+                 (data (process-get proc :eshell-pending))
+                 ;; Only get the status for the primary subprocess,
+                 ;; not the pipe process (if any).
+                 (status (when primary (process-exit-status proc)))
+                 (stderr-live (process-get proc :eshell-stderr-live)))
+            ;; Write the exit message for the last process in the
+            ;; foreground pipeline if its status is abnormal and
+            ;; stderr is already writing to the terminal.
             (when (and (eq proc (eshell-tail-process))
+                       (eshell-interactive-output-p eshell-error-handle 
handles)
                        (not (string-match "^\\(finished\\|exited\\)"
                                           string)))
-              (funcall (process-filter proc) proc string))
-            (let* ((handles (process-get proc :eshell-handles))
-                   (index (process-get proc :eshell-handle-index))
-                   (data (process-get proc :eshell-pending))
-                   ;; Only get the status for the primary subprocess,
-                   ;; not the pipe process (if any).
-                   (status (when (= index eshell-output-handle)
-                            (process-exit-status proc))))
-              (process-put proc :eshell-pending nil)
-              ;; If we're in the middle of handling output from this
-              ;; process then schedule the EOF for later.
-              (letrec ((finish-io
-                        (lambda ()
-                          (if (process-get proc :eshell-busy)
-                              (run-at-time 0 nil finish-io)
-                            (when data
-                              (ignore-error eshell-pipe-broken
-                                (eshell-output-object
-                                 data index handles)))
-                            (eshell-close-handles
-                             status
-                             (when status (list 'quote (= status 0)))
-                             handles)
-                            (eshell-kill-process-function proc string)))))
-                (funcall finish-io))))
+              (eshell--mark-as-output 0 (length string) string)
+              (eshell-interactive-filter (process-buffer proc) string))
+            (process-put proc :eshell-pending nil)
+            ;; If we're in the middle of handling output from this
+            ;; process then schedule the EOF for later.
+            (letrec ((wait-for-stderr (and primary
+                                           (not (process-live-p proc))))
+                     (finish-io
+                      (lambda ()
+                        (if (or (process-get proc :eshell-busy)
+                                (and wait-for-stderr (car stderr-live)))
+                            (progn
+                              (eshell-debug-command 'process
+                                "i/o busy for process `%s'" proc)
+                              (run-at-time 0 nil finish-io))
+                          (when data
+                            (ignore-error eshell-pipe-broken
+                              (eshell-output-object
+                               data index handles)))
+                          (eshell-close-handles
+                           status
+                           (when status (list 'quote (= status 0)))
+                           handles)
+                          ;; Clear the handles to mark that we're 100%
+                          ;; finished with the I/O for this process.
+                          (process-put proc :eshell-handles nil)
+                          (eshell-debug-command 'process
+                            "finished external process `%s'" proc)
+                          (if primary
+                              (run-hook-with-args 'eshell-kill-hook proc 
string)
+                            (setcar stderr-live nil))))))
+              (funcall finish-io)))
         (when-let ((entry (assq proc eshell-process-list)))
           (eshell-remove-process-entry entry))))))
 
@@ -588,25 +653,25 @@ See the variable `eshell-kill-processes-on-exit'."
   "Interrupt a process."
   (interactive)
   (unless (eshell-process-interact 'interrupt-process)
-    (eshell-kill-process-function nil "interrupt")))
+    (run-hook-with-args 'eshell-kill-hook nil "interrupt")))
 
 (defun eshell-kill-process ()
   "Kill a process."
   (interactive)
   (unless (eshell-process-interact 'kill-process)
-    (eshell-kill-process-function nil "killed")))
+    (run-hook-with-args 'eshell-kill-hook nil "killed")))
 
 (defun eshell-quit-process ()
   "Send quit signal to process."
   (interactive)
   (unless (eshell-process-interact 'quit-process)
-    (eshell-kill-process-function nil "quit")))
+    (run-hook-with-args 'eshell-kill-hook nil "quit")))
 
 ;(defun eshell-stop-process ()
 ;  "Send STOP signal to process."
 ;  (interactive)
 ;  (unless (eshell-process-interact 'stop-process)
-;    (eshell-kill-process-function nil "stopped")))
+;    (run-hook-with-args 'eshell-kill-hook nil "stopped")))
 
 ;(defun eshell-continue-process ()
 ;  "Send CONTINUE signal to process."
@@ -615,7 +680,7 @@ See the variable `eshell-kill-processes-on-exit'."
 ;    ;; jww (1999-09-17): this signal is not dealt with yet.  For
 ;    ;; example, `eshell-reset' will be called, and so will
 ;    ;; `eshell-resume-eval'.
-;    (eshell-kill-process-function nil "continue")))
+;    (run-hook-with-args 'eshell-kill-hook nil "continue")))
 
 (provide 'esh-proc)
 ;;; esh-proc.el ends here
diff --git a/lisp/eshell/esh-util.el b/lisp/eshell/esh-util.el
index 87cd1f5dcb2..4c251a29269 100644
--- a/lisp/eshell/esh-util.el
+++ b/lisp/eshell/esh-util.el
@@ -102,6 +102,16 @@ argument matches `eshell-number-regexp'."
                                     (string :tag "Username")
                                     (repeat :tag "UIDs" string))))))
 
+(defcustom eshell-debug-command nil
+  "A list of debug features to enable when running Eshell commands.
+Possible entries are `form', to log the manipulation of Eshell
+command forms, and `process', to log external process operations.
+
+If nil, don't debug commands at all."
+  :version "30.1"
+  :type '(set (const :tag "Form manipulation" form)
+              (const :tag "Process operations" process)))
+
 ;;; Internal Variables:
 
 (defvar eshell-number-regexp
@@ -145,6 +155,9 @@ function `string-to-number'.")
                             ,#'eshell--mark-yanked-as-output))
   "A list of text properties to apply to command output.")
 
+(defvar eshell-debug-command-buffer "*eshell last cmd*"
+  "The name of the buffer to log debug messages about command invocation.")
+
 ;;; Obsolete variables:
 
 (define-obsolete-variable-alias 'eshell-host-names
@@ -164,11 +177,41 @@ function `string-to-number'.")
   "If `eshell-handle-errors' is non-nil, this is `condition-case'.
 Otherwise, evaluates FORM with no error handling."
   (declare (indent 2) (debug (sexp form &rest form)))
-  (if eshell-handle-errors
-      `(condition-case-unless-debug ,tag
-          ,form
-        ,@handlers)
-    form))
+  `(if eshell-handle-errors
+       (condition-case-unless-debug ,tag
+           ,form
+         ,@handlers)
+     ,form))
+
+(defun eshell-debug-command-start (command)
+  "Start debugging output for the command string COMMAND.
+If debugging is enabled (see `eshell-debug-command'), this will
+start logging to `*eshell last cmd*'."
+  (when eshell-debug-command
+    (with-current-buffer (get-buffer-create eshell-debug-command-buffer)
+      (erase-buffer)
+      (insert "command: \"" command "\"\n"))))
+
+(defun eshell-always-debug-command (kind string &rest objects)
+  "Output a debugging message to `*eshell last cmd*'.
+KIND is the kind of message to log.  STRING and OBJECTS are as
+`format-message' (which see)."
+  (declare (indent 1))
+  (with-current-buffer (get-buffer-create eshell-debug-command-buffer)
+    (insert "\n\C-l\n[" (symbol-name kind) "] "
+            (apply #'format-message string objects))))
+
+(defmacro eshell-debug-command (kind string &rest objects)
+  "Output a debugging message to `*eshell last cmd*' if debugging is enabled.
+KIND is the kind of message to log (either `form' or `process').  If
+present in `eshell-debug-command', output this message; otherwise, ignore it.
+
+STRING and OBJECTS are as `format-message' (which see)."
+  (declare (indent 1))
+  (let ((kind-sym (make-symbol "kind")))
+    `(let ((,kind-sym ,kind))
+       (when (memq ,kind-sym eshell-debug-command)
+         (eshell-always-debug-command ,kind-sym ,string ,@objects)))))
 
 (defun eshell--mark-as-output (start end &optional object)
   "Mark the text from START to END as Eshell output.
@@ -326,7 +369,7 @@ as the $PATH was actually specified."
                  (eshell-under-windows-p))
         (push "." path))
       (if (and remote (not literal-p))
-          (mapcar (lambda (x) (file-name-concat remote x)) path)
+          (mapcar (lambda (x) (concat remote x)) path)
         path))))
 
 (defun eshell-set-path (path)
@@ -695,19 +738,18 @@ gid format.  Valid values are `string' and `integer', 
defaulting to
   "If the `processp' function does not exist, PROC is not a process."
   (and (fboundp 'processp) (processp proc)))
 
-(defun eshell-process-pair-p (procs)
-  "Return non-nil if PROCS is a pair of process objects."
-  (and (consp procs)
-       (eshell-processp (car procs))
-       (eshell-processp (cdr procs))))
+(defun eshell-process-list-p (procs)
+  "Return non-nil if PROCS is a list of process objects."
+  (and (listp procs)
+       (seq-every-p #'eshell-processp procs)))
 
-(defun eshell-make-process-pair (procs)
-  "Make a pair of process objects from PROCS if possible.
-This represents the head and tail of a pipeline of processes,
-where the head and tail may be the same process."
+(defun eshell-make-process-list (procs)
+  "Make a list of process objects from PROCS if possible.
+PROCS can be a single process or a list thereof.  If PROCS is
+anything else, return nil instead."
   (pcase procs
-    ((pred eshell-processp) (cons procs procs))
-    ((pred eshell-process-pair-p) procs)))
+    ((pred eshell-processp) (list procs))
+    ((pred eshell-process-list-p) procs)))
 
 ;; (defun eshell-copy-file
 ;;   (file newname &optional ok-if-already-exists keep-date)
diff --git a/lisp/eshell/esh-var.el b/lisp/eshell/esh-var.el
index 711c35f8527..d484aa406e1 100644
--- a/lisp/eshell/esh-var.el
+++ b/lisp/eshell/esh-var.el
@@ -296,43 +296,30 @@ copied (a.k.a. \"exported\") to the environment of 
created subprocesses."
 
 (defun eshell-handle-local-variables ()
   "Allow for the syntax `VAR=val <command> <args>'."
-  ;; strip off any null commands, which can only happen if a variable
-  ;; evaluates to nil, such as "$var x", where `var' is nil.  The
-  ;; command name in that case becomes `x', for compatibility with
-  ;; most regular shells (the difference is that they do an
-  ;; interpolation pass before the argument parsing pass, but Eshell
-  ;; does both at the same time).
-  (while (and (not eshell-last-command-name)
-             eshell-last-arguments)
-    (setq eshell-last-command-name (car eshell-last-arguments)
-         eshell-last-arguments (cdr eshell-last-arguments)))
+  ;; Eshell handles local variable settings (e.g. 'CFLAGS=-O2 make')
+  ;; by making the whole command into a subcommand, and calling
+  ;; `eshell-set-variable' immediately before the command is invoked.
+  ;; This means that 'FOO=x cd bar' won't work exactly as expected,
+  ;; but that is by no means a typical use of local environment
+  ;; variables.
   (let ((setvar "\\`\\([A-Za-z_][A-Za-z0-9_]*\\)=\\(.*\\)\\'")
-       (command (eshell-stringify eshell-last-command-name))
-       (args eshell-last-arguments))
-    ;; local variable settings (such as 'CFLAGS=-O2 make') are handled
-    ;; by making the whole command into a subcommand, and calling
-    ;; setenv immediately before the command is invoked.  This means
-    ;; that 'BLAH=x cd blah' won't work exactly as expected, but that
-    ;; is by no means a typical use of local environment variables.
-    (if (and command (string-match setvar command))
-       (throw
-        'eshell-replace-command
-        (list
-         'eshell-as-subcommand
-         (append
-          (list 'progn)
-          (let ((l (list t)))
-            (while (string-match setvar command)
-              (nconc
-               l (list
-                   (list 'eshell-set-variable
-                         (match-string 1 command)
-                         (match-string 2 command))))
-              (setq command (eshell-stringify (car args))
-                    args (cdr args)))
-            (cdr l))
-          (list (list 'eshell-named-command
-                      command (list 'quote args)))))))))
+        (command eshell-last-command-name)
+        (args eshell-last-arguments))
+    (when (and (stringp command) (string-match setvar command))
+      (throw 'eshell-replace-command
+             `(eshell-as-subcommand
+               (progn
+                 ,@(let (locals)
+                     (while (and (stringp command)
+                                 (string-match setvar command))
+                       (push `(eshell-set-variable
+                               ,(match-string 1 command)
+                               ,(match-string 2 command))
+                             locals)
+                       (setq command (pop args)))
+                     (nreverse locals))
+                 (eshell-named-command ,command ,(list 'quote args)))
+              )))))
 
 (defun eshell-interpolate-variable ()
   "Parse a variable interpolation.
diff --git a/lisp/eshell/eshell.el b/lisp/eshell/eshell.el
index 15fc2ae6310..8765ba499a1 100644
--- a/lisp/eshell/eshell.el
+++ b/lisp/eshell/eshell.el
@@ -301,7 +301,8 @@ argument), then insert output into the current buffer at 
point."
                     `(let ((eshell-current-handles
                             (eshell-create-handles ,stdout 'insert))
                            (eshell-current-subjob-p))
-                      ,(eshell-parse-command command))))
+                      ,(eshell-parse-command command))
+                    command))
             intr
             (bufname (if (eq (car-safe proc) :eshell-background)
                          "*Eshell Async Command Output*"
@@ -314,9 +315,8 @@ argument), then insert output into the current buffer at 
point."
        ;; make the output as attractive as possible, with no
        ;; extraneous newlines
        (when intr
-         (if (eshell-interactive-process-p)
-             (eshell-wait-for-process (eshell-tail-process)))
-         (cl-assert (not (eshell-interactive-process-p)))
+         (apply #'eshell-wait-for-process (cadr eshell-foreground-command))
+         (cl-assert (not eshell-foreground-command))
          (goto-char (point-max))
          (while (and (bolp) (not (bobp)))
            (delete-char -1)))
@@ -356,6 +356,7 @@ corresponding to a successful execution."
     (with-temp-buffer
       (let ((eshell-non-interactive-p t))
        (eshell-mode)
+        (eshell-debug-command-start command)
        (let ((result (eshell-do-eval
                       (list 'eshell-commands
                             (list 'eshell-command-to-value
diff --git a/lisp/external-completion.el b/lisp/external-completion.el
index 3f80be1c0a4..dd2710602fe 100644
--- a/lisp/external-completion.el
+++ b/lisp/external-completion.el
@@ -7,6 +7,9 @@
 ;; Maintainer: João Távora <joaotavora@gmail.com>
 ;; Keywords:
 
+;; This is a GNU ELPA :core package.  Avoid functionality that is not
+;; compatible with the version of Emacs recorded above.
+
 ;; This file is part of GNU Emacs.
 
 ;; GNU Emacs is free software: you can redistribute it and/or modify
diff --git a/lisp/faces.el b/lisp/faces.el
index 8f93f9b2c0c..7eacc40443a 100644
--- a/lisp/faces.el
+++ b/lisp/faces.el
@@ -1145,16 +1145,16 @@ returned.  Otherwise, DEFAULT is returned verbatim."
                       (format-prompt prompt default)
                     (format "%s: " prompt)))
           (completion-extra-properties
-           '(:affixation-function
-             (lambda (faces)
-               (mapcar
-                (lambda (face)
-                  (list face
-                        (concat (propertize read-face-name-sample-text
-                                            'face face)
-                                "\t")
-                        ""))
-                faces))))
+           `(:affixation-function
+             ,(lambda (faces)
+                (mapcar
+                 (lambda (face)
+                   (list face
+                         (concat (propertize read-face-name-sample-text
+                                             'face face)
+                                 "\t")
+                         ""))
+                 faces))))
           aliasfaces nonaliasfaces faces)
       ;; Build up the completion tables.
       (mapatoms (lambda (s)
diff --git a/lisp/ffap.el b/lisp/ffap.el
index 907f56763ff..6f477dd790b 100644
--- a/lisp/ffap.el
+++ b/lisp/ffap.el
@@ -174,7 +174,8 @@ Note this name may be omitted if it equals the default
   "\\`/\\(afs\\|net\\)/."
   ;; afs only: (and (file-exists-p "/afs") "\\`/afs/.")
   "Matching file names are treated as remote.  Use nil to disable."
-  :type 'regexp
+  :type '(choice (const :tag "Disable" nil)
+                 regexp)
   :group 'ffap)
 
 (defvar ffap-url-regexp
diff --git a/lisp/files.el b/lisp/files.el
index 4188615e490..ddae097f1d1 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -1322,10 +1322,10 @@ consecutive checks.  For example:
   :group 'tramp
   :version "24.1"
   :type '(choice
-         (const   :tag "Do not inhibit file name cache" nil)
-         (const   :tag "Do not use file name cache" t)
-         (integer :tag "Do not use file name cache"
-                  :format "Do not use file name cache older then %v seconds"
+          (const   :tag "Do not cache remote file attributes" t)
+          (const   :tag "Cache remote file attributes" nil)
+          (integer :tag "Cache remote file attributes with expiration"
+                   :format "Cache expiry in seconds: %v"
                   :value 10)))
 
 (defcustom remote-file-name-access-timeout nil
@@ -6068,14 +6068,18 @@ See `save-some-buffers' for PRED values."
 
 (defvar save-some-buffers-functions nil
   "Functions to be run by `save-some-buffers' after saving the buffers.
-The functions can be called in two \"modes\", depending on the
-first argument.  If the first argument is `query', then the
+These functions should accept one mandatory and one optional
+argument, and they can be called in two \"modes\", depending on
+the first argument.  If the first argument is `query', then the
 function should return non-nil if there is something to be
 saved (but it should not actually save anything).
 
 If the first argument is something else, then the function should
 save according to the value of the second argument, which is the
-ARG argument from `save-some-buffers'.")
+ARG argument with which `save-some-buffers' was called.
+
+The main purpose of these functions is to save stuff that is kept
+in variables (rather than in buffers).")
 
 (defun save-some-buffers (&optional arg pred)
   "Save some modified file-visiting buffers.  Asks user about each one.
@@ -7723,10 +7727,28 @@ need to be passed verbatim to shell commands."
       pattern))))
 
 
-(defvar insert-directory-program (purecopy "ls")
+(defcustom insert-directory-program
+  (if (and (memq system-type '(berkeley-unix darwin))
+           (executable-find "gls"))
+      (purecopy "gls")
+    (purecopy "ls"))
   "Absolute or relative name of the `ls'-like program.
 This is used by `insert-directory' and `dired-insert-directory'
-\(thus, also by `dired').")
+(thus, also by `dired').  For Dired, this should ideally point to
+GNU ls, or another version of ls that supports the \"--dired\"
+flag.  See `dired-use-ls-dired'.
+
+On GNU/Linux and other capable systems, the default is \"ls\".
+
+On *BSD and macOS systems, the default \"ls\" does not support
+the \"--dired\" flag.  Therefore, the default is to use the
+\"gls\" executable on such machines, if it exists.  This means
+that there should normally be no need to customize this when
+installing GNU coreutils using something like ports or Homebrew."
+  :group 'dired
+  :type 'string
+  :initialize #'custom-initialize-delay
+  :version "30.1")
 
 (defcustom directory-free-space-program (purecopy "df")
   "Program to get the amount of free space on a file system.
diff --git a/lisp/filesets.el b/lisp/filesets.el
index 81a194a45e6..639b108ac03 100644
--- a/lisp/filesets.el
+++ b/lisp/filesets.el
@@ -413,15 +413,14 @@ directory's name.
 
 Note: You have to manually rebuild the menu if you change this value."
   :set #'filesets-set-default
-  :type '(choice :tag "Function:"
+  :type '(choice :tag "Function"
                 (const :tag "dired"
                        :value dired)
                 (list :tag "Command"
                       :value ("" "%s")
                       (string :tag "Name")
                       (string :tag "Arguments"))
-                (function :tag "Function"
-                          :value nil)))
+                 (function :tag "Function")))
 
 (defcustom filesets-open-file-function #'filesets-find-or-display-file
   "The function used for opening files.
@@ -437,23 +436,21 @@ readable, will not be opened.
 
 Caveat: Changes will take effect only after rebuilding the menu."
   :set #'filesets-set-default
-  :type '(choice :tag "Function:"
+  :type '(choice :tag "Function"
                 (const :tag "filesets-find-or-display-file"
                        :value filesets-find-or-display-file)
                 (const :tag "filesets-find-file"
                        :value filesets-find-file)
-                (function :tag "Function"
-                          :value nil)))
+                 (function :tag "Function")))
 
 (defcustom filesets-save-buffer-function #'save-buffer
   "The function used to save a buffer.
 Caveat: Changes will take effect after rebuilding the menu."
   :set #'filesets-set-default
-  :type '(choice :tag "Function:"
+  :type '(choice :tag "Function"
                 (const :tag "save-buffer"
                        :value save-buffer)
-                (function :tag "Function"
-                          :value nil)))
+                 (function :tag "Function")))
 
 (defcustom filesets-find-file-delay
   (if (and (featurep 'xemacs) gutter-buffers-tab-visible-p)
@@ -535,7 +532,7 @@ the filename."
   :type '(repeat :tag "Commands"
                 (list :tag "Definition" :value ("")
                       (string "Name")
-                      (choice :tag "Command"
+                       (choice :tag "Command" :value ""
                               (string :tag "String")
                               (function :tag "Function"))
                       (repeat :tag "Argument List"
@@ -546,8 +543,7 @@ the filename."
                                               :value "<file-name>")
                                       (string :tag "Quoted File Name"
                                               :value "<<file-name>>")
-                                      (function :tag "Function"
-                                                 :value nil))))))
+                                       (function :tag "Function"))))))
 
 (defcustom filesets-external-viewers
   (let
@@ -647,12 +643,12 @@ In order to view pdf or rtf files in an Emacs buffer, you 
could use these:
                       (repeat :tag "Properties"
                               (choice
                                (list :tag ":constraintp"
-                                     :value (:constraintp)
+                                      :value (:constraintp ignore)
                                      (const :format ""
                                             :value :constraintp)
                                      (function :tag "Function"))
                                (list :tag ":constraint-flag (obsolete)"
-                                     :value (:constraint-flag)
+                                      :value (:constraint-flag nil)
                                      (const :format ""
                                             :value :constraint-flag)
                                      (sexp :tag "Symbol"))
@@ -667,7 +663,7 @@ In order to view pdf or rtf files in an Emacs buffer, you 
could use these:
                                              :value :ignore-on-read-text)
                                      (boolean :tag "Boolean"))
                                (list :tag ":args"
-                                     :value (:args)
+                                      :value (:args nil)
                                      (const :format ""
                                             :value :args)
                                      (repeat :tag "List"
@@ -676,10 +672,9 @@ In order to view pdf or rtf files in an Emacs buffer, you 
could use these:
                                                              :value "")
                                                      (symbol :tag "Symbol"
                                                              :value nil)
-                                                     (function :tag "Function"
-                                                               :value nil))))
+                                                      (function :tag 
"Function"))))
                                (list :tag ":open-hook"
-                                     :value (:open-hook)
+                                      :value (:open-hook nil)
                                      (const :format ""
                                             :value :open-hook)
                                      (hook :tag "Hook"))
diff --git a/lisp/finder.el b/lisp/finder.el
index f5bf2641537..4e3a3566ce9 100644
--- a/lisp/finder.el
+++ b/lisp/finder.el
@@ -147,7 +147,11 @@ would otherwise be.")
     ("gnus" . gnus)
     ("international" . emacs)
     ("language" . emacs)
+    ("leim" . emacs)
+    ("ja-dic" . emacs)
+    ("quail" . emacs)
     ("mh-e" . mh-e)
+    ("obsolete" . emacs)
     ("semantic" . semantic)
     ("analyze" . semantic)
     ("bovine" . semantic)
@@ -162,6 +166,7 @@ would otherwise be.")
     ("org"  . org)
     ("srecode" . srecode)
     ("term" . emacs)
+    ("use-package" . use-package)
     ("url"  . url))
   "Alist of built-in package directories.
 Each element should have the form (DIR . PACKAGE), where DIR is a
diff --git a/lisp/font-lock.el b/lisp/font-lock.el
index f8815c1698a..3df63f82fa1 100644
--- a/lisp/font-lock.el
+++ b/lisp/font-lock.el
@@ -299,8 +299,8 @@ that match at least one applicable CONDITION are disabled."
   "If non-nil, means show status messages for buffer fontification.
 If a number, only buffers greater than this size have fontification messages."
   :type '(choice (const :tag "never" nil)
-                (other :tag "always" t)
-                (integer :tag "size"))
+                (integer :tag "size")
+                 (other :tag "always" t))
   :group 'font-lock
   :version "24.1")
 
diff --git a/lisp/frame.el b/lisp/frame.el
index ba5d1caafa2..2452f7f8d4a 100644
--- a/lisp/frame.el
+++ b/lisp/frame.el
@@ -2906,7 +2906,7 @@ Values smaller than 0.2 sec are treated as 0.2 sec."
   "How many times to blink before using a solid cursor on NS, X, and 
MS-Windows.
 Use 0 or negative value to blink forever."
   :version "24.4"
-  :type 'natnum
+  :type 'integer
   :group 'cursor)
 
 (defvar blink-cursor-blinks-done 1
diff --git a/lisp/gnus/gnus-cloud.el b/lisp/gnus/gnus-cloud.el
index ae4c4cc0d71..a21d57d86ff 100644
--- a/lisp/gnus/gnus-cloud.el
+++ b/lisp/gnus/gnus-cloud.el
@@ -148,6 +148,7 @@ easy interactive way to set this from the Server buffer."
 
 (defun gnus-cloud-decode-data ()
   (cond
+   ;; FIXME: Duplicated value in ‘cond’: base64-gzip.
    ((memq gnus-cloud-storage-method '(base64 base64-gzip))
     (base64-decode-region (point-min) (point-max)))
 
diff --git a/lisp/gnus/gnus-logic.el b/lisp/gnus/gnus-logic.el
index 628d2bd0958..426249e9a40 100644
--- a/lisp/gnus/gnus-logic.el
+++ b/lisp/gnus/gnus-logic.el
@@ -71,11 +71,11 @@
                    (+ (cdr score) new-score))
          (push (cons (mail-header-number gnus-advanced-headers)
                      new-score)
-               gnus-newsgroup-scored)
-         (when trace
-           (push (cons "A file" rule)
-                 ;; Must be synced with `gnus-score-edit-file-at-point'.
-                 gnus-score-trace)))))))
+               gnus-newsgroup-scored))
+       (when trace
+         (push (cons "A file" rule)
+               ;; Must be synced with `gnus-score-edit-file-at-point'.
+               gnus-score-trace))))))
 
 (defun gnus-advanced-score-rule (rule)
   "Apply RULE to `gnus-advanced-headers'."
diff --git a/lisp/gnus/gnus-msg.el b/lisp/gnus/gnus-msg.el
index 0439bf0d59b..b065ae34851 100644
--- a/lisp/gnus/gnus-msg.el
+++ b/lisp/gnus/gnus-msg.el
@@ -1104,12 +1104,12 @@ If VERY-WIDE, make a very wide reply."
                (setq headers (concat headers (buffer-string)))))))
        (set-buffer (gnus-copy-article-buffer))
        (gnus-msg-treat-broken-reply-to gnus-msg-force-broken-reply-to)
-       (save-restriction
-         (message-narrow-to-head)
-         (when very-wide
-           (erase-buffer)
-           (insert headers))
-         (goto-char (point-max)))
+       (when very-wide
+          (save-restriction
+           (message-narrow-to-head)
+           (delete-region (point-min) (point-max))
+           (insert headers)
+           (goto-char (point-max))))
        (mml-quote-region (point) (point-max))
        (message-reply nil wide)
        (when yank
diff --git a/lisp/gnus/gnus-util.el b/lisp/gnus/gnus-util.el
index 4b5bc69894b..1d25d7ac919 100644
--- a/lisp/gnus/gnus-util.el
+++ b/lisp/gnus/gnus-util.el
@@ -343,10 +343,10 @@ Symbols are also allowed; their print names are used 
instead."
       (yes-or-no-p prompt)
     (message "")))
 
-;; By Frank Schmitt <ich@Frank-Schmitt.net>. Allows to have
-;; age-depending date representations. (e.g. just the time if it's
-;; from today, the day of the week if it's within the last 7 days and
-;; the full date if it's older)
+;; By Frank Schmitt <ich@Frank-Schmitt.net>.  Enables age-dependent
+;; date representations.  (e.g. just the time if it's from today, the
+;; day of the week if it's within the last 7 days and the full date if
+;; it's older)
 
 (defun gnus-seconds-today ()
   "Return the integer number of seconds passed today."
diff --git a/lisp/gnus/message.el b/lisp/gnus/message.el
index 7a31f86a2c4..969589bb942 100644
--- a/lisp/gnus/message.el
+++ b/lisp/gnus/message.el
@@ -747,16 +747,14 @@ default is system dependent and determined by the function
 `message-send-mail-function'.
 
 See also `send-mail-function'."
-  :type '(radio (function-item message--default-send-mail-function
-                              :tag "Use send-mail-function")
+  :type '(radio (function-item message--default-send-mail-function)
                (function-item message-send-mail-with-sendmail)
                (function-item message-send-mail-with-mh)
                (function-item message-send-mail-with-qmail)
                (function-item message-smtpmail-send-it)
-               (function-item smtpmail-send-it)
+                (function-item :doc "Use SMTPmail package." smtpmail-send-it)
                (function-item feedmail-send-it)
-               (function-item message-send-mail-with-mailclient
-                              :tag "Use Mailclient package")
+                (function-item message-send-mail-with-mailclient)
                (function :tag "Other"))
   :group 'message-sending
   :version "27.1"
@@ -911,8 +909,10 @@ installations, which are rare these days."
 (defcustom message-sendmail-envelope-from
   'obey-mail-envelope-from
   "Envelope-from when sending mail with sendmail.
-If this is nil, use `user-mail-address'.  If it is the symbol
-`header', use the From: header of the message."
+If this is `obey-mail-envelope-from', then use
+`mail-envelope-from' to decide what to do.  If it is nil, use
+`user-mail-address'.  If it is the symbol `header', use the
+\"From:\" header of the message."
   :version "27.1"
   :type '(choice (string :tag "From name")
                 (const :tag "Use From: header from message" header)
@@ -2838,11 +2838,11 @@ will not be inserted."
                        (const :tag "No ID" nil))
                (choice (string :tag "Key")
                        (const :tag "No Key" nil))
-               (choice (other :tag "None" nil)
-                       (const :tag "Unprotected" "unprotected")
+                (choice (const :tag "Unprotected" "unprotected")
                        (const :tag "Sign" "sign")
                        (const :tag "Encrypt" "encrypt")
-                       (const :tag "Sign and Encrypt" "signencrypt"))))
+                        (const :tag "Sign and Encrypt" "signencrypt")
+                        (other :tag "None" nil))))
   :version "28.1")
 
 (defun message-add-openpgp-header ()
@@ -6593,8 +6593,8 @@ they are."
     (widen)
     (forward-line 1)
     (unless (looking-at "$")
-      (forward-line 2)))
-   (sit-for 0)))
+      (forward-line 2))))
+  (sit-for 0))
 
 (defcustom message-beginning-of-line t
   "Whether \\<message-mode-map>\\[message-beginning-of-line]\
@@ -8208,7 +8208,6 @@ which specify the range to operate on."
 It can be either a list or a symbol referring to a list.  See
 `gmm-tool-bar-from-list' for the format of the list.  The
 default key map is `message-mode-map'."
-  :type '(repeat gmm-tool-bar-list-item)
   :type '(choice (repeat :tag "User defined list" gmm-tool-bar-item)
                 (symbol))
   :version "29.1"
diff --git a/lisp/gnus/mm-decode.el b/lisp/gnus/mm-decode.el
index b9beedf6c5c..3c7d1e7e073 100644
--- a/lisp/gnus/mm-decode.el
+++ b/lisp/gnus/mm-decode.el
@@ -119,7 +119,7 @@
        ((executable-find "links") 'links)
         ((executable-find "lynx") 'lynx)
         (t 'shr))
-  "Render of HTML contents.
+  "Renderer of HTML contents.
 It is one of defined renderer types, or a rendering function.
 The defined renderer types are:
 `shr': use the built-in Gnus HTML renderer;
@@ -131,8 +131,8 @@ The defined renderer types are:
   :version "29.1"
   :type '(choice (const shr)
                  (const gnus-w3m)
-                 (const w3m :tag "emacs-w3m")
-                (const w3m-standalone :tag "standalone w3m" )
+                 (const :tag "emacs-w3m" w3m)
+                 (const :tag "standalone w3m" w3m-standalone)
                 (const links)
                 (const lynx)
                 (function))
diff --git a/lisp/help-fns.el b/lisp/help-fns.el
index 609bed18f2f..e93c535bbef 100644
--- a/lisp/help-fns.el
+++ b/lisp/help-fns.el
@@ -369,7 +369,8 @@ if the variable `help-downcase-arguments' is non-nil."
       (setq doc (replace-regexp-in-string
                  ;; This is heuristic, but covers all common cases
                  ;; except ARG1-ARG2
-                 (concat "\\<"                   ; beginning of word
+                 (concat "([^ ]+ .*"             ; skip function name
+                         "\\<"                   ; beginning of word
                          "\\(?:[a-z-]*-\\)?"     ; for xxx-ARG
                          "\\("
                          (regexp-quote arg)
@@ -766,7 +767,7 @@ the C sources, too."
              " is obsolete")
       (when (nth 2 obsolete)
         (insert (format " since %s" (nth 2 obsolete))))
-      (insert (cond ((stringp use) (concat "; " use))
+      (insert (cond ((stringp use) (concat "; " (substitute-quotes use)))
                     (use (format-message "; use `%s' instead." use))
                     (t "."))
               "\n")
diff --git a/lisp/help.el b/lisp/help.el
index 614d90b84ba..e278dd17b7f 100644
--- a/lisp/help.el
+++ b/lisp/help.el
@@ -1230,15 +1230,60 @@ appeared on the mode-line."
                      i))))
                minor-mode-alist)))
 
-(defun describe-minor-mode-from-indicator (indicator)
+(defun describe-minor-mode-from-indicator (indicator &optional event)
   "Display documentation of a minor mode specified by INDICATOR.
 If you call this function interactively, you can give indicator which
-is currently activated with completion."
+is currently activated with completion.
+
+If non-nil, EVENT is a mouse event used to establish which minor
+mode lighter was clicked."
   (interactive (list
                (completing-read
                 "Minor mode indicator: "
                 (describe-minor-mode-completion-table-for-indicator))))
-  (let ((minor-mode (lookup-minor-mode-from-indicator indicator)))
+  (when (and event mode-line-compact)
+    (let* ((event-start (event-start event))
+           (window (posn-window event-start)))
+      ;; If INDICATOR is a string object, WINDOW is set, and
+      ;; `mode-line-compact' might be enabled, find a string in
+      ;; `minor-mode-alist' that is present within the INDICATOR and
+      ;; whose extents within INDICATOR contain the position of the
+      ;; object within the string.
+      (when (windowp window)
+        (setq indicator (posn-object event-start))
+        (catch 'found
+          (with-selected-window window
+            (let ((alist minor-mode-alist) string position)
+              (when (consp indicator)
+                (with-temp-buffer
+                  (insert (car indicator))
+                  (dolist (menu alist)
+                    ;; If this is a valid minor mode menu entry,
+                    (when (and (consp menu)
+                               (setq string (format-mode-line (cadr menu)
+                                                              nil window))
+                               (> (length string) 0))
+                      ;; Start searching for an appearance of (cdr
+                      ;; menu).
+                      (goto-char (point-min))
+                      (while (search-forward string nil 0)
+                        ;; If the position of the string object is
+                        ;; contained within, set indicator to the
+                        ;; minor mode in question.
+                        (setq position (1+ (cdr indicator)))
+                        (and (>= position (match-beginning 0))
+                             (<= position (match-end 0))
+                             (setq indicator (car menu))
+                             (throw 'found nil)))))))))))))
+  ;; If INDICATOR is still a cons, use its car.
+  (when (consp indicator)
+    (setq indicator (car indicator)))
+  (let ((minor-mode (if (symbolp indicator)
+                        ;; indicator being set to a symbol means that
+                        ;; the loop above has already found a
+                        ;; matching minor mode.
+                        indicator
+                      (lookup-minor-mode-from-indicator indicator))))
     (if minor-mode
        (describe-minor-mode-from-symbol minor-mode)
       (error "Cannot find minor mode for `%s'" indicator))))
@@ -1423,7 +1468,7 @@ Otherwise, return a new string."
                   ;; in case it is a local variable.
                   (with-current-buffer orig-buf
                     ;; This is for computing the SHADOWS arg for
-                    ;; describe-map-tree.
+                    ;; help--describe-map-tree.
                     (setq active-maps (current-active-maps))
                     (when (boundp name)
                       (setq this-keymap (and (keymapp (symbol-value name))
@@ -1444,9 +1489,10 @@ Otherwise, return a new string."
                     ;; If this one's not active, get nil.
                     (let ((earlier-maps
                            (cdr (memq this-keymap (reverse active-maps)))))
-                      (describe-map-tree this-keymap t (nreverse earlier-maps)
-                                         nil nil (not include-menus)
-                                         nil nil t))))))))
+                      (help--describe-map-tree this-keymap t
+                                               (nreverse earlier-maps)
+                                               nil nil (not include-menus)
+                                               nil nil t))))))))
              ;; 2. Handle quotes.
              ((and (eq (text-quoting-style) 'curve)
                    (or (and (= (following-char) ?\`)
@@ -1463,10 +1509,11 @@ Otherwise, return a new string."
         (buffer-string)))))
 
 (defun substitute-quotes (string)
-  "Substitute quote characters for display.
+  "Substitute quote characters in STRING for display.
 Each grave accent \\=` is replaced by left quote, and each
-apostrophe \\=' is replaced by right quote.  Left and right quote
-characters are specified by `text-quoting-style'."
+apostrophe \\=' is replaced by right quote.  Which left and right
+quote characters to use is determined by the variable
+`text-quoting-style'."
   (cond ((eq (text-quoting-style) 'curve)
          (string-replace "`" "‘"
                          (string-replace "'" "’" string)))
@@ -1475,9 +1522,9 @@ characters are specified by `text-quoting-style'."
         (t string)))
 
 (defvar help--keymaps-seen nil)
-(defun describe-map-tree (startmap &optional partial shadow prefix title
-                                   no-menu transl always-title mention-shadow
-                                   buffer)
+(defun help--describe-map-tree (startmap &optional partial shadow prefix title
+                                         no-menu transl always-title 
mention-shadow
+                                         buffer)
   "Insert a description of the key bindings in STARTMAP.
 This is followed by the key bindings of all maps reachable
 through STARTMAP.
@@ -1631,7 +1678,7 @@ Assume that this keymap itself is reached by the sequence 
of
 prefix keys PREFIX (a string or vector).
 
 TRANSL, PARTIAL, SHADOW, NOMENU, MENTION-SHADOW and BUFFER are as
-in `describe-map-tree'."
+in `help--describe-map-tree'."
   ;; Converted from describe_map in keymap.c.
   (let* ((map (keymap-canonicalize map))
          (tail map)
@@ -2412,6 +2459,7 @@ the suggested string to use instead.  See
         #'help-command-error-confusable-suggestions))
 
 (define-obsolete-function-alias 'help-for-help-internal #'help-for-help "28.1")
+(define-obsolete-function-alias 'describe-map-tree #'help--describe-map-tree 
"30.1")
 
 
 (provide 'help)
diff --git a/lisp/ibuf-macs.el b/lisp/ibuf-macs.el
index c38dfefe0c5..36616389f99 100644
--- a/lisp/ibuf-macs.el
+++ b/lisp/ibuf-macs.el
@@ -230,6 +230,9 @@ buffer object.
                                (_
                                 'ibuffer-marked-buffer-names)))))
         (when (null marked-names)
+           (cl-assert (get-text-property (line-beginning-position)
+                                         'ibuffer-properties)
+                      nil "No buffer on this line")
           (setq marked-names (list (buffer-name (ibuffer-current-buffer))))
           (ibuffer-set-mark ,(pcase mark
                                (:deletion
@@ -243,7 +246,9 @@ buffer object.
                            ())
                           (and after `(,after)) ; post-operation form.
                          `((ibuffer-redisplay t)
-                           (message ,(concat "Operation finished; " opstring " 
%s buffers") count))))
+                           (message ,(concat "Operation finished; " opstring
+                                              " %s %s")
+                                     count (ngettext "buffer" "buffers" 
count)))))
                 (inner-body (if complex
                                 `(progn ,@body)
                               `(progn
diff --git a/lisp/ibuffer.el b/lisp/ibuffer.el
index a26bb1811ec..1368a62d87b 100644
--- a/lisp/ibuffer.el
+++ b/lisp/ibuffer.el
@@ -1872,7 +1872,8 @@ the buffer object itself and the current mark symbol."
            (let ((result
                   (if (buffer-live-p (ibuffer-current-buffer))
                       (when (or (null group)
-                                 (when-let ((it (get-text-property (point) 
'ibuffer-filter-group)))
+                                 (when-let ((it (get-text-property
+                                                 (point) 
'ibuffer-filter-group)))
                                    (equal group it)))
                         (save-excursion
                           (funcall function
@@ -1897,7 +1898,19 @@ the buffer object itself and the current mark symbol."
                    (t
                     (cl-incf ibuffer-map-lines-count)
                     (forward-line 1)))))
-         ibuffer-map-lines-count)
+         ;; With `ibuffer-auto-mode' enabled, `ibuffer-expert' nil
+         ;; and more than one marked buffer lines, the preceding loop
+         ;; counts the automatically popped up (and hence not
+         ;; user-marked) buffer "*Ibuffer confirmation*".  Since
+         ;; Ibuffer reports how many marked buffers lines were acted
+         ;; upon, and in this case the reported count would be too
+         ;; high by one, we decrement the count to avoid the
+         ;; confusing message (see bug#64230).
+          (if (and (featurep 'ibuf-ext) ibuffer-auto-mode
+                   (> ibuffer-map-lines-count 1)
+                   (not ibuffer-expert))
+              (1- ibuffer-map-lines-count)
+            ibuffer-map-lines-count))
       (progn
        (setq buffer-read-only t)
        (unless nomodify
diff --git a/lisp/ido.el b/lisp/ido.el
index 00a2e57f7ba..041ed33aa99 100644
--- a/lisp/ido.el
+++ b/lisp/ido.el
@@ -864,7 +864,8 @@ also modify the dynamic variables described for the variable
 (defcustom ido-completion-buffer "*Ido Completions*"
   "Name of completion buffer used by Ido.
 Set to nil to disable completion buffers popping up."
-  :type 'string)
+  :type '(choice (const :tag "Disable popping up completion buffer" nil)
+                 string))
 
 (defcustom ido-completion-buffer-all-completions nil
   "Non-nil means to show all completions in completion buffer.
diff --git a/lisp/ielm.el b/lisp/ielm.el
index f7d025b8c01..e51c46054b4 100644
--- a/lisp/ielm.el
+++ b/lisp/ielm.el
@@ -491,8 +491,7 @@ addition to `comint-indirect-setup-hook', run this hook 
with the
 indirect buffer as the current buffer after its setup is done.
 This can be used to further customize fontification and other
 behavior of the indirect buffer."
-  :type 'boolean
-  :safe 'booleanp
+  :type 'hook
   :version "29.1")
 
 (defun ielm-indirect-setup-hook ()
diff --git a/lisp/image-mode.el b/lisp/image-mode.el
index 713125d4e58..ecc7d73dd9e 100644
--- a/lisp/image-mode.el
+++ b/lisp/image-mode.el
@@ -69,8 +69,8 @@ Its value should be one of the following:
 Resizing will always preserve the aspect ratio of the image."
   :type '(choice (const :tag "No resizing" nil)
                  (const :tag "Fit to window" fit-window)
-                 (other :tag "Scale down to fit window" t)
-                 (number :tag "Scale factor" 1))
+                 (number :tag "Scale factor" 1)
+                 (other :tag "Scale down to fit window" t))
   :version "29.1"
   :group 'image)
 
@@ -89,7 +89,7 @@ This will always keep the image fit to the window.
 When non-nil, the value should be a number of seconds to wait before
 resizing according to the value specified in `image-auto-resize'."
   :type '(choice (const :tag "No auto-resize on window size change" nil)
-                 (integer :tag "Wait for number of seconds before resize" 1))
+                 (number :tag "Wait for number of seconds before resize" 1))
   :version "27.1"
   :group 'image)
 
diff --git a/lisp/image.el b/lisp/image.el
index 08190cf86bc..e20fbcf4c98 100644
--- a/lisp/image.el
+++ b/lisp/image.el
@@ -38,7 +38,7 @@
 (defconst image-type-header-regexps
   `(("\\`/[\t\n\r ]*\\*.*XPM.\\*/" . xpm)
     ("\\`P[1-6]\\(?:\
-\\(?:\\(?:#[^\r\n]*[\r\n]\\)*[[:space:]]\\)+\
+\\(?:\\(?:#[^\r\n]*[\r\n]\\)*[ \t\r\n]\\)+\
 \\(?:\\(?:#[^\r\n]*[\r\n]\\)*[0-9]\\)+\
 \\)\\{2\\}" . pbm)
     ("\\`GIF8[79]a" . gif)
diff --git a/lisp/image/image-dired-dired.el b/lisp/image/image-dired-dired.el
index 6b932601df0..d522c06d10b 100644
--- a/lisp/image/image-dired-dired.el
+++ b/lisp/image/image-dired-dired.el
@@ -5,6 +5,7 @@
 ;; Author: Mathias Dahl <mathias.rem0veth1s.dahl@gmail.com>
 ;; Maintainer: Stefan Kangas <stefankangas@gmail.com>
 ;; Keywords: multimedia
+;; Package: image-dired
 
 ;; This file is part of GNU Emacs.
 
diff --git a/lisp/image/image-dired-external.el 
b/lisp/image/image-dired-external.el
index 77352c25a3b..1da41ad178f 100644
--- a/lisp/image/image-dired-external.el
+++ b/lisp/image/image-dired-external.el
@@ -5,6 +5,7 @@
 ;; Author: Mathias Dahl <mathias.rem0veth1s.dahl@gmail.com>
 ;; Maintainer: Stefan Kangas <stefankangas@gmail.com>
 ;; Keywords: multimedia
+;; Package: image-dired
 
 ;; This file is part of GNU Emacs.
 
diff --git a/lisp/image/image-dired-tags.el b/lisp/image/image-dired-tags.el
index b9c1a811850..79ac6fb58f2 100644
--- a/lisp/image/image-dired-tags.el
+++ b/lisp/image/image-dired-tags.el
@@ -5,6 +5,7 @@
 ;; Author: Mathias Dahl <mathias.rem0veth1s.dahl@gmail.com>
 ;; Maintainer: Stefan Kangas <stefankangas@gmail.com>
 ;; Keywords: multimedia
+;; Package: image-dired
 
 ;; This file is part of GNU Emacs.
 
diff --git a/lisp/image/image-dired-util.el b/lisp/image/image-dired-util.el
index 53a5e274175..e17cc6c919f 100644
--- a/lisp/image/image-dired-util.el
+++ b/lisp/image/image-dired-util.el
@@ -4,6 +4,7 @@
 
 ;; Author: Mathias Dahl <mathias.rem0veth1s.dahl@gmail.com>
 ;; Maintainer: Stefan Kangas <stefankangas@gmail.com>
+;; Package: image-dired
 
 ;; This file is part of GNU Emacs.
 
diff --git a/lisp/international/characters.el b/lisp/international/characters.el
index 1aa570ca59a..a48c0f77008 100644
--- a/lisp/international/characters.el
+++ b/lisp/international/characters.el
@@ -141,10 +141,10 @@ with L, LRE, or LRO Unicode bidi character type.")
 ;; Chinese characters (Unicode)
 (modify-category-entry '(#x2E80 . #x312F) ?|)
 (modify-category-entry '(#x3190 . #x33FF) ?|)
-(modify-category-entry '(#x3400 . #x4DB5) ?C)
-(modify-category-entry '(#x4E00 . #x9FD5) ?C)
-(modify-category-entry '(#x3400 . #x9FD5) ?c)
-(modify-category-entry '(#x3400 . #x9FD5) ?|)
+(modify-category-entry '(#x3400 . #x4DBF) ?C)
+(modify-category-entry '(#x4E00 . #x9FFF) ?C)
+(modify-category-entry '(#x3400 . #x9FFF) ?c)
+(modify-category-entry '(#x3400 . #x9FFF) ?|)
 (modify-category-entry '(#xF900 . #xFAFF) ?C)
 (modify-category-entry '(#xF900 . #xFAFF) ?c)
 (modify-category-entry '(#xF900 . #xFAFF) ?|)
@@ -1303,12 +1303,13 @@ with L, LRE, or LRO Unicode bidi character type.")
           (#x2E80 . #x2E99)
            (#x2E9B . #x2EF3)
            (#x2F00 . #x2FD5)
-           (#x2FF0 . #x2FFB)
+           (#x2FF0 . #x2FFF)
            (#x3000 . #x303E)
           (#x3041 . #x3096)
            (#x3099 . #x30FF)
            (#x3105 . #x312F)
            (#x3131 . #x31E3)
+           (#x31EF . #x31EF)
            (#x31F0 . #x3247)
           (#x3250 . #x4DBF)
           (#x4E00 . #xA48C)
diff --git a/lisp/international/emoji.el b/lisp/international/emoji.el
index 856c405b545..8bb31e15b61 100644
--- a/lisp/international/emoji.el
+++ b/lisp/international/emoji.el
@@ -97,7 +97,7 @@ representing names.  For instance:
                        (multisession-value emoji--recent)))
 
 ;;;###autoload (autoload 'emoji-search "emoji" nil t)
-(transient-define-prefix emoji-search ()
+(transient-define-prefix emoji-search (glyph derived)
   "Choose and insert an emoji glyph by typing its Unicode name.
 This command prompts for an emoji name, with completion, and
 inserts it.  It recognizes the Unicode Standard names of emoji,
@@ -106,15 +106,17 @@ and also consults the `emoji-alternate-names' alist."
   [:class transient-columns
    :setup-children emoji--setup-suffixes
    :description emoji--group-description]
-  (interactive "*")
-  (emoji--init)
-  (pcase-let ((`(,glyph . ,derived) (emoji--read-emoji)))
-    (if derived
-        (emoji--setup-prefix 'emoji-search "Choose Emoji"
-                             (list glyph)
-                             (cons glyph derived))
-      (emoji--add-recent glyph)
-      (insert glyph))))
+  (interactive
+   (progn (barf-if-buffer-read-only)
+          (emoji--init)
+          (let ((cons (emoji--read-emoji)))
+            (list (car cons) (cdr cons)))))
+  (if derived
+      (emoji--setup-prefix 'emoji-search "Choose Emoji"
+                           (list glyph)
+                           (cons glyph derived))
+    (emoji--add-recent glyph)
+    (insert glyph)))
 
 (defclass emoji--narrow (transient-suffix)
   ((title :initarg :title)
@@ -142,12 +144,15 @@ and also consults the `emoji-alternate-names' alist."
 (defun emoji--group-description ()
   (car (oref transient--prefix scope)))
 
-(transient-define-suffix emoji-insert-glyph ()
+(transient-define-suffix emoji-insert-glyph (glyph)
   "Insert the emoji you selected."
-  (interactive nil not-a-mode)
-  (let ((glyph (oref (transient-suffix-object) description)))
-    (emoji--add-recent glyph)
-    (insert glyph)))
+  (interactive
+   (list (if (string-prefix-p "emoji-" (symbol-name transient-current-command))
+             (oref (transient-suffix-object) description)
+           (car (multisession-value emoji--recent))))
+   not-a-mode)
+  (emoji--add-recent glyph)
+  (insert glyph))
 
 ;;;###autoload
 (defun emoji-list ()
@@ -710,10 +715,14 @@ FACTOR is the multiplication factor for the size."
             (add-text-properties
              (point) (1+ (point))
              (list 'face
-                   (if (eq (car old) :height)
-                       (plist-put (copy-sequence old) :height newheight)
+                   (cond
+                    ((eq (car old) :height)
+                     (plist-put (copy-sequence old) :height newheight))
+                    ((plistp (car old))
                      (cons (plist-put (car old) :height newheight)
                            (cdr old)))
+                    (t
+                     (append (list (list :height newheight)) old)))
                    'rear-nonsticky t))
           (add-face-text-property (point) (1+ (point))
                                   (list :height newheight))
diff --git a/lisp/international/ja-dic-cnv.el b/lisp/international/ja-dic-cnv.el
index 5477473ae8c..81d5a1acdf4 100644
--- a/lisp/international/ja-dic-cnv.el
+++ b/lisp/international/ja-dic-cnv.el
@@ -346,6 +346,8 @@ If NO-REDUCTION is non-nil, do not reduce the dictionary 
vocabulary."
       (erase-buffer)
       (buffer-disable-undo)
       (generate-lisp-file-heading ja-dic-filename 'skkdic-convert :code nil)
+      (insert (format ";; Generated with small ja-dic option: %s\n\n"
+                      (if no-reduction "no" "yes")))
       (insert ";; Original SKK dictionary file: "
              (file-relative-name (expand-file-name filename) dirname)
              "\n\n"
diff --git a/lisp/international/mule-cmds.el b/lisp/international/mule-cmds.el
index c26898f7649..fe3610b0fbc 100644
--- a/lisp/international/mule-cmds.el
+++ b/lisp/international/mule-cmds.el
@@ -3094,6 +3094,10 @@ on encoding."
 (defun ucs-names ()
   "Return table of CHAR-NAME keys and CHAR-CODE values cached in `ucs-names'."
   (or ucs-names
+      ;; Sometimes these ranges will need adjusting as codepoints are
+      ;; added to unicode.  The test case
+      ;; 'mule-cmds-tests--ucs-names-missing-names' will tell you
+      ;; which are missing (Bug#65997).
       (let ((ranges
             '((#x0000 . #x33FF)
               ;; (#x3400 . #x4DBF) CJK Ideographs Extension A
@@ -3106,14 +3110,16 @@ on encoding."
                (#x14400 . #x14646)
               ;; (#x14647 . #x167FF) unused
               (#x16800 . #x16F9F)
-               (#x16FE0 . #x16FE3)
+               (#x16FE0 . #x16FF1)
                ;; (#x17000 . #x187FF) Tangut Ideographs
                ;; (#x18800 . #x18AFF) Tangut Components
                ;; (#x18B00 . #x18CFF) Khitan Small Script
                ;; (#x18D00 . #x18D0F) Tangut Ideograph Supplement
               ;; (#x18D10 . #x1AFEF) unused
-              (#x1AFF0 . #x1B12F)
-               ;; (#x1B130 . #x1B14F) unused
+              (#x1AFF0 . #x1B122)
+               ;; (#x1B123 . #x1B131) unused
+               (#x1B132 . #x1B132)
+               ;; (#x1B133 . #x1B14F) unused
                (#x1B150 . #x1B16F)
                (#x1B170 . #x1B2FF)
               ;; (#x1B300 . #x1BBFF) unused
@@ -3130,12 +3136,16 @@ on encoding."
            (while (<= c end)
              (let ((new-name (get-char-code-property c 'name))
                    (old-name (get-char-code-property c 'old-name)))
-               ;; In theory this code could end up pushing an "old-name" that
-               ;; shadows a "new-name" but in practice every time an
-               ;; `old-name' conflicts with a `new-name', the newer one has a
-               ;; higher code, so it gets pushed later!
+                ;; This code used to push both old-name and new-name
+                ;; on the assumption that the new-name codepoint would
+                ;; always be higher, which was true for a long time.
+                ;; As of at latest 2023-09-15, this is no longer true,
+                ;; so we now skip the old-name if it conflicts with an
+                ;; existing new-name (Bug#65997).
                (if new-name (puthash new-name c names))
-               (if old-name (puthash old-name c names))
+                (when (and old-name
+                           (not (gethash old-name names)))
+                  (puthash old-name c names))
                 ;; Unicode uses the spelling "lamda" in character
                 ;; names, instead of "lambda", due to "preferences
                 ;; expressed by the Greek National Body" (Bug#30513).
diff --git a/lisp/international/ucs-normalize.el 
b/lisp/international/ucs-normalize.el
index aed7a6a1392..67e8ab428e9 100644
--- a/lisp/international/ucs-normalize.el
+++ b/lisp/international/ucs-normalize.el
@@ -131,11 +131,19 @@
       #x1D1BF #x1D1C0)
    "Composition Exclusion List.
   This list is taken from
-    https://www.unicode.org/Public/UNIDATA/5.2/CompositionExclusions.txt";)
+    https://www.unicode.org/Public/UNIDATA/15.0/CompositionExclusions.txt";)
+
+  ;; Unicode ranges where decompositions & combining characters are
+  ;; defined.  Find them by running the following Awk program on
+  ;; UnicodeData.txt:
+  ;;
+  ;;                  gawk -F";" "$6 != \"\" {print $0}"
 
-  ;; Unicode ranges that decompositions & combining characters are defined.
   (defvar check-range nil)
-    (setq check-range '((#x00a0 . #x3400) (#xA600 . #xAC00) (#xF900 . #x110ff) 
(#x1d000 . #x1dfff) (#x1f100 . #x1f2ff) (#x2f800 . #x2faff)))
+    (setq check-range
+          '((#x00A0 . #x3400) (#xA600 . #xAC00) (#xF900 . #x11100)
+            (#x11100 . #x11A00) (#x1D000 . #x1E100) (#x1EE00 . #x1F300)
+            (#x1FBF0 . #x1FC00) (#x2F800 . #x2FB00)))
 
   ;; Basic normalization functions
   (defun nfd (char)
diff --git a/lisp/keymap.el b/lisp/keymap.el
index 017b2d6ead0..7e659c42002 100644
--- a/lisp/keymap.el
+++ b/lisp/keymap.el
@@ -2,6 +2,10 @@
 
 ;; Copyright (C) 2021-2023 Free Software Foundation, Inc.
 
+;; Maintainer: emacs-devel@gnu.org
+;; Keywords: internal
+;; Package: emacs
+
 ;; This file is part of GNU Emacs.
 
 ;; GNU Emacs is free software: you can redistribute it and/or modify
@@ -378,15 +382,17 @@ which is
 This function creates a `keyboard-translate-table' if necessary
 and then modifies one entry in it.
 
-Both KEY and TO should be specified by strings that satisfy `key-valid-p'."
+Both FROM and TO should be specified by strings that satisfy `key-valid-p'."
   (declare (compiler-macro
             (lambda (form) (keymap--compile-check from to) form)))
   (keymap--check from)
   (keymap--check to)
   (or (char-table-p keyboard-translate-table)
       (setq keyboard-translate-table
-           (make-char-table 'keyboard-translate-table nil)))
-  (aset keyboard-translate-table (key-parse from) (key-parse to)))
+            (make-char-table 'keyboard-translate-table nil)))
+  (aset keyboard-translate-table
+        (aref (key-parse from) 0)
+        (aref (key-parse to) 0)))
 
 (defun keymap-lookup (keymap key &optional accept-default no-remap position)
   "Return the binding for command KEY in KEYMAP.
diff --git a/lisp/leim/quail/indian.el b/lisp/leim/quail/indian.el
index a30028329c4..70ea9290662 100644
--- a/lisp/leim/quail/indian.el
+++ b/lisp/leim/quail/indian.el
@@ -154,8 +154,8 @@ strings that describe how to insert CONSONANT."
   (setq consonants
      (sort consonants
          (lambda (x y)
-           (or (seq-position (car x) quail-tamil-itrans--consonant-order) 1000)
-           (or (seq-position (car y) quail-tamil-itrans--consonant-order) 
1000))))
+           (< (or (seq-position quail-tamil-itrans--consonant-order (car x)) 
1000)
+              (or (seq-position quail-tamil-itrans--consonant-order (car y)) 
1000)))))
   (let ((virama #x0BCD)
        clm)
     (with-temp-buffer
diff --git a/lisp/leim/quail/latin-pre.el b/lisp/leim/quail/latin-pre.el
index f469c1ec941..fd38084e645 100644
--- a/lisp/leim/quail/latin-pre.el
+++ b/lisp/leim/quail/latin-pre.el
@@ -789,9 +789,9 @@ and Silesian (both Steuer and Ślabikŏrzowy szrajbōnek) 
scripts."
  ("'Z" ?Ź)
  (".z" ?ż)
  (".Z" ?Ż)
- ;; Explicit input of prefix characters. Normally, to input a prefix
- ;; character itself, one needs to press <Tab>. Definitions below
- ;; allow to input those characters by entering them twice.
+ ;; Explicit input of prefix characters.  Normally, to input a prefix
+ ;; character itself, one needs to press <Tab>.  Definitions below
+ ;; allow inputting those characters by entering them twice.
  ("//" ?/)
  ("\\\\" ?\\)
  ("~~" ?~)
diff --git a/lisp/loadup.el b/lisp/loadup.el
index 38fb0fc1fa9..35c59dba453 100644
--- a/lisp/loadup.el
+++ b/lisp/loadup.el
@@ -248,7 +248,7 @@
 (load "simple")
 (load "emacs-lisp/seq")
 (load "emacs-lisp/nadvice")
-(load "minibuffer") ;Needs cl-generic (and define-minor-mode).
+(load "minibuffer") ; Needs cl-generic, seq (and define-minor-mode).
 (load "frame")
 (load "startup")
 (load "term/tty-colors")
diff --git a/lisp/ls-lisp.el b/lisp/ls-lisp.el
index b0f86839740..efc06ffbbf8 100644
--- a/lisp/ls-lisp.el
+++ b/lisp/ls-lisp.el
@@ -161,8 +161,8 @@ systems, set your locale instead."
        ((eq ls-lisp-emulation 'MS-Windows)
         (if (and (fboundp 'w32-using-nt) (w32-using-nt))
             '(links)))                 ; distinguish NT/2K from 9x
-       ((eq ls-lisp-emulation 'UNIX) '(links uid)) ; UNIX ls
-       (t '(links uid gid)))           ; GNU ls
+       ((eq ls-lisp-emulation 'UNIX) '(links uid modes)) ; UNIX ls
+       (t '(links uid gid modes)))             ; GNU ls
   "A list of optional file attributes that ls-lisp should display.
 It should contain none or more of the symbols: links, uid, gid.
 A value of nil (or an empty list) means display none of them.
@@ -808,7 +808,9 @@ SWITCHES and TIME-INDEX give the full switch list and time 
data."
                             (* 1024.0 (fceiling (/ file-size 1024.0)))))
                  (format ls-lisp-filesize-b-fmt
                          (fceiling (/ file-size 1024.0)))))
-           drwxrwxrwx                  ; attribute string
+            (if (memq 'modes ls-lisp-verbosity)
+               drwxrwxrwx      ; modes string
+              (substring drwxrwxrwx 0 1)) ; "d" or "-" for directory vs file
            (if (memq 'links ls-lisp-verbosity)
                (format "%3d" (file-attribute-link-number file-attr)))
            ;; Numeric uid/gid are more confusing than helpful;
diff --git a/lisp/mail/ietf-drums-date.el b/lisp/mail/ietf-drums-date.el
index b2cceb5cef2..68ba88e89ec 100644
--- a/lisp/mail/ietf-drums-date.el
+++ b/lisp/mail/ietf-drums-date.el
@@ -3,6 +3,7 @@
 ;; Copyright (C) 2022-2023 Free Software Foundation, Inc.
 
 ;; Author: Bob Rogers <rogers@rgrjr.com>
+;; Package: ietf-drums
 ;; Keywords: mail, util
 
 ;; This file is part of GNU Emacs.
diff --git a/lisp/mail/rmailsum.el b/lisp/mail/rmailsum.el
index e3a6c16933b..661bfee4ab3 100644
--- a/lisp/mail/rmailsum.el
+++ b/lisp/mail/rmailsum.el
@@ -52,7 +52,7 @@ Setting this option to nil might speed up the generation of 
summaries."
 
 (defcustom rmail-summary-progressively-narrow nil
   "Non-nil means progressively narrow the set of messages produced by summary.
-This allows to apply the summary criteria on top one another,
+This enables you to apply the summary criteria on top one another,
 thus progressively narrowing the selection of the messages produced
 by each summary criteria.
 For example, applying `rmail-summary-by-senders' on top
diff --git a/lisp/mail/sendmail.el b/lisp/mail/sendmail.el
index 8d7e90ccacf..8306bd3b30c 100644
--- a/lisp/mail/sendmail.el
+++ b/lisp/mail/sendmail.el
@@ -151,11 +151,11 @@ not a valid RFC 822 (or later) header or continuation 
line,
 that matches the variable `mail-header-separator'.
 This is used by the default mail-sending commands.  See also
 `message-send-mail-function' for use with the Message package."
-  :type '(radio (function-item sendmail-send-it :tag "Use Sendmail package")
-               (function-item sendmail-query-once :tag "Query the user")
-               (function-item smtpmail-send-it :tag "Use SMTPmail package")
-               (function-item feedmail-send-it :tag "Use Feedmail package")
-               (function-item mailclient-send-it :tag "Use Mailclient package")
+  :type '(radio (function-item sendmail-send-it)
+                (function-item sendmail-query-once)
+                (function-item :doc "Use SMTPmail package." smtpmail-send-it)
+                (function-item feedmail-send-it)
+                (function-item mailclient-send-it)
                function)
   :version "24.1")
 
diff --git a/lisp/menu-bar.el b/lisp/menu-bar.el
index 5e837485db3..3a348ebcdc6 100644
--- a/lisp/menu-bar.el
+++ b/lisp/menu-bar.el
@@ -2227,7 +2227,7 @@ otherwise it could decide to silently do nothing."
    ((not (menu-bar-menu-frame-live-and-visible-p)))
    ((menu-bar-non-minibuffer-window-p)
     (kill-buffer (current-buffer))
-    ;; Also close the current window if `menu-bar-close-windows' is
+    ;; Also close the current window if `menu-bar-close-window' is
     ;; set.
     (when menu-bar-close-window
       (ignore-errors (delete-window))))
@@ -2314,14 +2314,16 @@ The menu shows all the killed text sequences stored in 
`kill-ring'."
 
 ;;; Buffers Menu
 
-(defcustom buffers-menu-max-size 10
+(defcustom buffers-menu-max-size (if (display-graphic-p) 15 10)
   "Maximum number of entries which may appear on the Buffers menu.
-If this is 10, then only the ten most-recently-selected buffers are shown.
-If this is nil, then all buffers are shown.
-A large number or nil slows down menu responsiveness."
-  :type '(choice integer
-                (const :tag "All" nil))
-  :group 'menu)
+If this is a number, only that many most-recently-selected
+buffers are shown.
+If this is nil, all buffers are shown."
+  :initialize #'custom-initialize-delay
+  :type '(choice natnum
+                 (const :tag "All" nil))
+  :group 'menu
+  :version "30.1")
 
 (defcustom buffers-menu-buffer-name-length 30
   "Maximum length of the buffer name on the Buffers menu.
@@ -2465,9 +2467,12 @@ It must accept a buffer as its only required argument.")
         ;; Make the menu of buffers proper.
         (setq buffers-menu
                (let ((i 0)
-                    (limit (and (integerp buffers-menu-max-size)
-                                (> buffers-menu-max-size 1)
-                                buffers-menu-max-size))
+                     (limit (if (boundp 'buffers-menu-max-size)
+                                (and (integerp buffers-menu-max-size)
+                                     (> buffers-menu-max-size 1)
+                                     buffers-menu-max-size)
+                              ;; Used when bootstrapping.
+                              10))
                      alist)
                 ;; Put into each element of buffer-list
                 ;; the name for actual display,
diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index 35b359a75e2..2120e31775e 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -153,19 +153,6 @@ The metadata of a completion table should be constant 
between two boundaries."
 (defun completion-metadata-get (metadata prop)
   (cdr (assq prop metadata)))
 
-(defun completion--some (fun xs)
-  "Apply FUN to each element of XS in turn.
-Return the first non-nil returned value.
-Like CL's `some'."
-  (let ((firsterror nil)
-        res)
-    (while (and (not res) xs)
-      (condition-case-unless-debug err
-          (setq res (funcall fun (pop xs)))
-        (error (unless firsterror (setq firsterror err)) nil)))
-    (or res
-        (if firsterror (signal (car firsterror) (cdr firsterror))))))
-
 (defun complete-with-action (action collection string predicate)
   "Perform completion according to ACTION.
 STRING, COLLECTION and PREDICATE are used as in `try-completion'.
@@ -426,9 +413,9 @@ obeys predicates."
   ;; is returned by TABLE2 (because TABLE1 returned an empty list).
   ;; Same potential problem if any of the tables use quoting.
   (lambda (string pred action)
-    (completion--some (lambda (table)
-                        (complete-with-action action table string pred))
-                      tables)))
+    (seq-some (lambda (table)
+                (complete-with-action action table string pred))
+              tables)))
 
 (defun completion-table-merge (&rest tables)
   "Create a completion table that collects completions from all TABLES."
@@ -451,9 +438,9 @@ obeys predicates."
                                 (all-completions string table pred))
                               tables)))
      (t
-      (completion--some (lambda (table)
-                          (complete-with-action action table string pred))
-                        tables)))))
+      (seq-some (lambda (table)
+                  (complete-with-action action table string pred))
+                tables)))))
 
 (defun completion-table-with-quoting (table unquote requote)
   ;; A difficult part of completion-with-quoting is to map positions in the
@@ -1216,7 +1203,7 @@ overrides the default specified in 
`completion-category-defaults'."
               (cl-assert (<= point (length string)))
               (pop new))))
          (result-and-style
-          (completion--some
+          (seq-some
            (lambda (style)
              (let ((probe (funcall
                            (or (nth n (assq style completion-styles-alist))
@@ -4038,7 +4025,9 @@ the same set of elements."
                      (unique (or (and (eq prefix t) (setq prefix fixed))
                                  (and (stringp prefix)
                                       (eq t (try-completion prefix comps))))))
-                (unless (or (eq elem 'prefix)
+                ;; if the common prefix is unique, it also is a common
+                ;; suffix, so we should add it for `prefix' elements
+                (unless (or (and (eq elem 'prefix) (not unique))
                             (equal prefix ""))
                   (push prefix res))
                 ;; If there's only one completion, `elem' is not useful
@@ -4674,6 +4663,179 @@ The latter is implemented in `touch-screen.el'."
 (add-hook 'minibuffer-setup-hook #'minibuffer-setup-on-screen-keyboard)
 (add-hook 'minibuffer-exit-hook #'minibuffer-exit-on-screen-keyboard)
 
+(defvar minibuffer-regexp-mode)
+
+(defun minibuffer--regexp-propertize ()
+  "In current minibuffer propertize parens and slashes in regexps.
+Put punctuation `syntax-table' property on selected paren and
+backslash characters in current buffer to make `show-paren-mode'
+and `blink-matching-paren' more user-friendly."
+  (let (in-char-alt-p)
+    (save-excursion
+      (with-silent-modifications
+        (remove-text-properties (point-min) (point-max) '(syntax-table nil))
+        (goto-char (point-min))
+        (while (re-search-forward
+                (rx (| (group "\\\\")
+                       (: "\\" (| (group (in "(){}"))
+                                  (group "[")
+                                  (group "]")))
+                       (group "[:" (+ (in "A-Za-z")) ":]")
+                       (group "[")
+                       (group "]")
+                       (group (in "(){}"))))
+               (point-max) 'noerror)
+         (cond
+           ((match-beginning 1))                ; \\, skip
+           ((match-beginning 2)                        ; \( \) \{ \}
+            (if in-char-alt-p
+               ;; Within character alternative, set symbol syntax for
+               ;; paren only.
+                (put-text-property (1- (point)) (point) 'syntax-table '(3))
+             ;; Not within character alternative, set symbol syntax for
+             ;; backslash only.
+              (put-text-property (- (point) 2) (1- (point)) 'syntax-table 
'(3))))
+          ((match-beginning 3)                 ; \[
+            (if in-char-alt-p
+                (progn
+                 ;; Set symbol syntax for backslash.
+                  (put-text-property (- (point) 2) (1- (point)) 'syntax-table 
'(3))
+                  ;; Re-read bracket we might be before a character class.
+                  (backward-char))
+             ;; Set symbol syntax for bracket.
+             (put-text-property (1- (point)) (point) 'syntax-table '(3))))
+          ((match-beginning 4)                 ; \]
+            (if in-char-alt-p
+                (progn
+                  ;; Within character alternative, set symbol syntax for
+                 ;; backslash, exit alternative.
+                  (put-text-property (- (point) 2) (1- (point)) 'syntax-table 
'(3))
+                 (setq in-char-alt-p nil))
+             ;; Not within character alternative, set symbol syntax for
+             ;; bracket.
+             (put-text-property (1- (point)) (point) 'syntax-table '(3))))
+          ((match-beginning 5))         ; POSIX character class, skip
+          ((match-beginning 6)          ; [
+           (if in-char-alt-p
+               ;; Within character alternative, set symbol syntax.
+               (put-text-property (1- (point)) (point) 'syntax-table '(3))
+             ;; Start new character alternative.
+             (setq in-char-alt-p t)
+              ;; Looking for immediately following non-closing ].
+             (when (looking-at "\\^?\\]")
+               ;; Non-special right bracket, set symbol syntax.
+               (goto-char (match-end 0))
+               (put-text-property (1- (point)) (point) 'syntax-table '(3)))))
+          ((match-beginning 7)                 ; ]
+            (if in-char-alt-p
+                (setq in-char-alt-p nil)
+              ;; The only warning we can emit before RET.
+             (message "Not in character alternative")))
+          ((match-beginning 8)                 ; (){}
+           ;; Plain parenthesis or brace, set symbol syntax.
+           (put-text-property (1- (point)) (point) 'syntax-table '(3)))))))))
+
+;; The following variable is set by 'minibuffer--regexp-before-change'.
+;; If non-nil, either 'minibuffer--regexp-post-self-insert' or
+;; 'minibuffer--regexp-after-change', whichever comes next, will
+;; propertize the minibuffer via 'minibuffer--regexp-propertize' and
+;; reset this variable to nil, avoiding to propertize the buffer twice.
+(defvar-local minibuffer--regexp-primed nil
+  "Non-nil when minibuffer contents change.")
+
+(defun minibuffer--regexp-before-change (_a _b)
+  "`minibuffer-regexp-mode' function on `before-change-functions'."
+  (setq minibuffer--regexp-primed t))
+
+(defun minibuffer--regexp-after-change (_a _b _c)
+  "`minibuffer-regexp-mode' function on `after-change-functions'."
+  (when minibuffer--regexp-primed
+    (setq minibuffer--regexp-primed nil)
+    (minibuffer--regexp-propertize)))
+
+(defun minibuffer--regexp-post-self-insert ()
+  "`minibuffer-regexp-mode' function on `post-self-insert-hook'."
+  (when minibuffer--regexp-primed
+    (setq minibuffer--regexp-primed nil)
+    (minibuffer--regexp-propertize)))
+
+(defvar minibuffer--regexp-prompt-regexp
+  "\\(?:Posix search\\|RE search\\|Search for regexp\\|Query replace regexp\\)"
+  "Regular expression compiled from `minibuffer-regexp-prompts'.")
+
+(defcustom minibuffer-regexp-prompts
+  '("Posix search" "RE search" "Search for regexp" "Query replace regexp")
+  "List of regular expressions that trigger `minibuffer-regexp-mode' features.
+The features of `minibuffer-regexp-mode' will be activated in a minibuffer
+interaction if and only if a prompt matching some regexp in this list
+appears at the beginning of the minibuffer.
+
+Setting this variable directly with `setq' has no effect; instead,
+either use \\[customize-option] interactively or use `setopt'."
+  :type '(repeat (string :tag "Prompt"))
+  :set (lambda (sym val)
+        (set-default sym val)
+         (when val
+           (setq minibuffer--regexp-prompt-regexp
+                 (concat "\\(?:" (mapconcat 'regexp-quote val "\\|") "\\)"))))
+  :version "30.1")
+
+(defun minibuffer--regexp-setup ()
+  "Function to activate`minibuffer-regexp-mode' in current buffer.
+Run by `minibuffer-setup-hook'."
+  (if (and minibuffer-regexp-mode
+           (save-excursion
+             (goto-char (point-min))
+             (looking-at minibuffer--regexp-prompt-regexp)))
+      (progn
+        (setq-local parse-sexp-lookup-properties t)
+        (add-hook 'before-change-functions #'minibuffer--regexp-before-change 
nil t)
+        (add-hook 'after-change-functions #'minibuffer--regexp-after-change 
nil t)
+        (add-hook 'post-self-insert-hook #'minibuffer--regexp-post-self-insert 
nil t))
+    ;; Make sure.
+    (minibuffer--regexp-exit)))
+
+(defun minibuffer--regexp-exit ()
+  "Function to deactivate `minibuffer-regexp-mode' in current buffer.
+Run by `minibuffer-exit-hook'."
+  (with-silent-modifications
+    (remove-text-properties (point-min) (point-max) '(syntax-table nil)))
+  (setq-local parse-sexp-lookup-properties nil)
+  (remove-hook 'before-change-functions #'minibuffer--regexp-before-change t)
+  (remove-hook 'after-change-functions #'minibuffer--regexp-after-change t)
+  (remove-hook 'post-self-insert-hook #'minibuffer--regexp-post-self-insert t))
+
+(define-minor-mode minibuffer-regexp-mode
+  "Minor mode for editing regular expressions in the minibuffer.
+Highlight parens via `show-paren-mode' and `blink-matching-paren'
+in a user-friendly way, avoid reporting alleged paren mismatches
+and make sexp navigation more intuitive.
+
+The list of prompts activating this mode in specific minibuffer
+interactions is customizable via `minibuffer-regexp-prompts'."
+  :global t
+  :initialize 'custom-initialize-delay
+  :init-value t
+  (if minibuffer-regexp-mode
+      (progn
+        (add-hook 'minibuffer-setup-hook #'minibuffer--regexp-setup)
+        (add-hook 'minibuffer-exit-hook #'minibuffer--regexp-exit))
+    ;; Clean up - why is Vminibuffer_list not available in Lisp?
+    (dolist (buffer (buffer-list))
+      (when (and (minibufferp)
+                 parse-sexp-lookup-properties
+                 (with-current-buffer buffer
+                   (save-excursion
+                     (goto-char (point-min))
+                     (looking-at minibuffer--regexp-prompt-regexp))))
+        (with-current-buffer buffer
+          (with-silent-modifications
+            (remove-text-properties
+             (point-min) (point-max) '(syntax-table nil)))
+          (setq-local parse-sexp-lookup-properties t))))
+    (remove-hook 'minibuffer-setup-hook #'minibuffer--regexp-setup)
+    (remove-hook 'minibuffer-exit-hook #'minibuffer--regexp-exit)))
+
 (provide 'minibuffer)
 
 ;;; minibuffer.el ends here
diff --git a/lisp/misearch.el b/lisp/misearch.el
index 9ac28c26c48..2873a2542f9 100644
--- a/lisp/misearch.el
+++ b/lisp/misearch.el
@@ -387,6 +387,157 @@ whose file names match the specified wildcard."
     (goto-char (if isearch-forward (point-min) (point-max)))
     (isearch-forward-regexp nil t)))
 
+
+;;; Global multi-file replacements as diff
+
+(defcustom multi-file-diff-unsaved 'save-buffers
+  "What to do with unsaved edits when showing multi-file replacements as diffs.
+If the value is `save-buffers', save unsaved buffers before creating diff.
+If the value is `use-file', use text from the file even when the
+file-visiting buffer is modified.
+If the value is `use-modified-buffer', use text from the file-visiting
+modified buffer to be able to use unsaved changes."
+  :type '(choice
+          (const :tag "Save buffers" save-buffers)
+          (const :tag "Use file" use-file)
+          (const :tag "Use modified buffer" use-modified-buffer))
+  :version "30.1")
+
+(declare-function diff-setup-whitespace "diff-mode" ())
+(declare-function diff-setup-buffer-type "diff-mode" ())
+
+(defun multi-file-replace-as-diff (files from-string replacements regexp-flag 
delimited-flag)
+  "Show as diffs replacements of FROM-STRING with REPLACEMENTS.
+FILES is a list of file names.  REGEXP-FLAG and DELIMITED-FLAG have
+the same meaning as in `perform-replace'."
+  (require 'diff)
+  (let ((inhibit-message t)
+        (diff-buffer (get-buffer-create "*replace-diff*")))
+    (when (eq multi-file-diff-unsaved 'save-buffers)
+      (save-some-buffers t (lambda ()
+                             (seq-some (lambda (f-or-b)
+                                         (equal f-or-b buffer-file-name))
+                                       files))))
+    (with-current-buffer diff-buffer
+      (buffer-disable-undo (current-buffer))
+      (let ((inhibit-read-only t))
+        (erase-buffer))
+      ;; Make the *vc-diff* buffer read only, the diff-mode key
+      ;; bindings are nicer for read only buffers.
+      (setq buffer-read-only t)
+      (diff-mode))
+    (dolist (file-name files)
+      (let* ((file-exists (file-exists-p file-name))
+             (file-buffer
+              (when (or (not file-exists)
+                        (eq multi-file-diff-unsaved 'use-modified-buffer))
+                (find-buffer-visiting file-name))))
+        (when file-name
+          (with-temp-buffer
+            (if (and file-buffer
+                     (or (not file-exists)
+                         (buffer-modified-p file-buffer)))
+                (insert-buffer-substring file-buffer)
+              (insert-file-contents file-name))
+            (goto-char (point-min))
+            (perform-replace from-string replacements nil regexp-flag 
delimited-flag)
+            (multi-file-diff-no-select
+             (if file-exists file-name file-buffer)
+             (current-buffer) nil diff-buffer
+             (concat file-name "~") file-name)))))
+    (with-current-buffer diff-buffer
+      (diff-setup-whitespace)
+      (diff-setup-buffer-type)
+      (buffer-enable-undo (current-buffer))
+      (setq-local revert-buffer-function
+                  (lambda (_ignore-auto _noconfirm)
+                    (multi-file-replace-as-diff
+                     files from-string replacements regexp-flag 
delimited-flag)))
+      (goto-char (point-min)))
+    (pop-to-buffer diff-buffer)))
+
+;;;###autoload
+(defun multi-file-replace-regexp-as-diff (files regexp to-string &optional 
delimited)
+  "Show as diffs replacements of REGEXP with TO-STRING in FILES.
+DELIMITED has the same meaning as in `replace-regexp'.
+The replacements are displayed in the buffer *replace-diff* that
+you can later apply as a patch after reviewing the changes."
+  (interactive
+   (let ((files (multi-isearch-read-files))
+         (common
+          (query-replace-read-args
+           (concat "Replace"
+                   (if current-prefix-arg " word" "")
+                   " regexp as diff in files")
+           t t)))
+     (list files (nth 0 common) (nth 1 common) (nth 2 common))))
+  (multi-file-replace-as-diff files regexp to-string t delimited))
+
+;;;###autoload
+(defun replace-regexp-as-diff (regexp to-string &optional delimited)
+  "Show as diffs replacements of REGEXP with TO-STRING in the current buffer.
+DELIMITED has the same meaning as in `replace-regexp'.
+The replacements are displayed in the buffer *replace-diff* that
+you can later apply as a patch after reviewing the changes."
+  (interactive
+   (let ((common
+          (query-replace-read-args
+           (concat "Replace"
+                   (if current-prefix-arg " word" "")
+                   " regexp as diff")
+           t t)))
+     (list (nth 0 common) (nth 1 common) (nth 2 common))))
+  (multi-file-replace-as-diff
+   (list buffer-file-name) regexp to-string t delimited))
+
+(defvar diff-use-labels)
+(declare-function diff-check-labels "diff" (&optional force))
+(declare-function diff-file-local-copy "diff" (file-or-buf))
+
+(defun multi-file-diff-no-select (old new &optional switches buf label-old 
label-new)
+  ;; Based on `diff-no-select' tailored to multi-file diffs.
+  "Compare the OLD and NEW file/buffer.
+If the optional SWITCHES is nil, the switches specified in the
+variable `diff-switches' are passed to the diff command,
+otherwise SWITCHES is used.  SWITCHES can be a string or a list
+of strings.  BUF should be non-nil.  LABEL-OLD and LABEL-NEW
+specify labels to use for file names."
+  (unless (bufferp new) (setq new (expand-file-name new)))
+  (unless (bufferp old) (setq old (expand-file-name old)))
+  (or switches (setq switches diff-switches)) ; If not specified, use default.
+  (setq switches (ensure-list switches))
+  (diff-check-labels)
+  (let* ((old-alt (diff-file-local-copy old))
+         (new-alt (diff-file-local-copy new))
+         (command
+          (mapconcat #'identity
+                     `(,diff-command
+                       ;; Use explicitly specified switches
+                       ,@switches
+                       ,@(mapcar #'shell-quote-argument
+                                 (nconc
+                                  (and (or old-alt new-alt)
+                                       (eq diff-use-labels t)
+                                       (list "--label"
+                                             (cond ((stringp label-old) 
label-old)
+                                                   ((stringp old) old)
+                                                   ((prin1-to-string old)))
+                                             "--label"
+                                             (cond ((stringp label-new) 
label-new)
+                                                   ((stringp new) new)
+                                                   ((prin1-to-string new)))))
+                                  (list (or old-alt old)
+                                        (or new-alt new)))))
+                     " ")))
+    (with-current-buffer buf
+      (let ((inhibit-read-only t))
+        (insert command "\n")
+        (call-process shell-file-name nil buf nil
+                      shell-command-switch command))
+      (if old-alt (delete-file old-alt))
+      (if new-alt (delete-file new-alt)))))
+
+
 (defvar unload-function-defs-list)
 
 (defun multi-isearch-unload-function ()
diff --git a/lisp/mouse.el b/lisp/mouse.el
index 11a2db52eec..6d916e8223a 100644
--- a/lisp/mouse.el
+++ b/lisp/mouse.el
@@ -35,7 +35,7 @@
 (put 'track-mouse 'lisp-indent-function 0)
 
 (defgroup mouse nil
-  "Input from the mouse."  ;; "Mouse support."
+  "Input from the mouse."
   :group 'environment
   :group 'editing)
 
@@ -1829,10 +1829,11 @@ The region will be defined with mark and point."
              map)
            t (lambda ()
                (funcall cleanup)
-               ;; Don't deactivate the mark when the context menu was invoked
-               ;; by down-mouse-3 immediately after down-mouse-1 and without
-               ;; releasing the mouse button with mouse-1. This allows to use
-               ;; region-related context menu to operate on the selected 
region.
+               ;; Don't deactivate the mark when the context menu was
+               ;; invoked by down-mouse-3 immediately after
+               ;; down-mouse-1 and without releasing the mouse button
+               ;; with mouse-1.  This enables region-related context
+               ;; menu to operate on the selected region.
                (unless (and context-menu-mode
                             (eq (car-safe (aref (this-command-keys-vector) 0))
                                 'down-mouse-3))
diff --git a/lisp/net/browse-url.el b/lisp/net/browse-url.el
index daa46baf8f2..11bfeb1b339 100644
--- a/lisp/net/browse-url.el
+++ b/lisp/net/browse-url.el
@@ -236,7 +236,7 @@ be used instead."
 
 (defcustom browse-url-button-regexp
   (concat
-   "\\b\\(\\(www\\.\\|\\(s?https?\\|ftp\\|file\\|gopher\\|gemini\\|"
+   "\\b\\(\\(www\\.\\|\\(s?https?\\|ftps?\\|file\\|gophers?\\|gemini\\|"
    "nntp\\|news\\|telnet\\|wais\\|mailto\\|info\\):\\)"
    "\\(//[-a-z0-9_.]+:[0-9]*\\)?"
    (let ((chars "-a-z0-9_=#$@~%&*+\\/[:word:]")
@@ -914,6 +914,11 @@ If ARGS are omitted, the default is to pass
                 ;; (setenv "WAYLAND_DISPLAY" dpy)
                 )
             (setenv "DISPLAY" dpy)))
+         ((featurep 'android)
+          ;; Avoid modifying the DISPLAY environment variable here,
+          ;; which interferes with any X server the user may have
+          ;; expressly set.
+          nil)
          (t
           (setenv "DISPLAY" dpy)))))
     (if (functionp function)
@@ -1457,8 +1462,7 @@ used instead of `browse-url-new-window-flag'."
 
 ;;;###autoload
 (defun browse-url-w3-gnudoit (url &optional _new-window)
-  ;; new-window ignored
-  "Ask another Emacs running gnuserv to load the URL using the W3 browser.
+  "Ask another Emacs running emacsclient to load the URL using the W3 browser.
 The `browse-url-gnudoit-program' program is used with options given by
 `browse-url-gnudoit-args'.  Default to the URL around or before point."
   (declare (obsolete nil "25.1"))
diff --git a/lisp/net/eudc-capf.el b/lisp/net/eudc-capf.el
index d454851ae67..6d51d572485 100644
--- a/lisp/net/eudc-capf.el
+++ b/lisp/net/eudc-capf.el
@@ -1,21 +1,22 @@
 ;;; eudc-capf.el --- EUDC - completion-at-point bindings  -*- 
lexical-binding:t -*-
 
 ;; Copyright (C) 2022-2023 Free Software Foundation, Inc.
-;;
+
 ;; Author: Alexander Adolf
-;;
+;; Package: eudc
+
 ;; This file is part of GNU Emacs.
-;;
+
 ;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
 ;; the Free Software Foundation, either version 3 of the License, or
 ;; (at your option) any later version.
-;;
+
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 ;; GNU General Public License for more details.
-;;
+
 ;; You should have received a copy of the GNU General Public License
 ;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
 
diff --git a/lisp/net/eudcb-ecomplete.el b/lisp/net/eudcb-ecomplete.el
index 20bdd9059f5..a4b7a183d25 100644
--- a/lisp/net/eudcb-ecomplete.el
+++ b/lisp/net/eudcb-ecomplete.el
@@ -1,29 +1,32 @@
 ;;; eudcb-ecomplete.el --- EUDC - ecomplete backend -*- lexical-binding: t -*-
 
 ;; Copyright (C) 2022-2023 Free Software Foundation, Inc.
-;;
+
 ;; Author: Alexander Adolf
-;;
+;; Package: eudc
+
 ;; This file is part of GNU Emacs.
-;;
+
 ;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
 ;; the Free Software Foundation, either version 3 of the License, or
 ;; (at your option) any later version.
-;;
+
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 ;; GNU General Public License for more details.
-;;
+
 ;; You should have received a copy of the GNU General Public License
 ;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
 
 ;;; Commentary:
+
 ;;    This library provides an interface to the ecomplete package as
 ;;    an EUDC data source.
 
 ;;; Usage:
+
 ;;    No setup is required, since there is an entry for this backend
 ;;    in `eudc-server-hotlist' by default.
 ;;
diff --git a/lisp/net/eudcb-macos-contacts.el b/lisp/net/eudcb-macos-contacts.el
index bb73237e6ec..e9ce22f4134 100644
--- a/lisp/net/eudcb-macos-contacts.el
+++ b/lisp/net/eudcb-macos-contacts.el
@@ -3,6 +3,7 @@
 ;; Copyright (C) 2020-2023 Free Software Foundation, Inc.
 
 ;; Author: Alexander Adolf
+;; Package: eudc
 
 ;; This file is part of GNU Emacs.
 
@@ -20,11 +21,13 @@
 ;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
 
 ;;; Commentary:
+
 ;;    This library provides an interface to the macOS Contacts app as
 ;;    an EUDC data source.  It uses AppleScript to interface with the
 ;;    Contacts app on localhost, so no 3rd party tools are needed.
 
 ;;; Usage:
+
 ;;    To load the library, first `require' it:
 ;;
 ;;      (require 'eudcb-macos-contacts)
diff --git a/lisp/net/eudcb-mailabbrev.el b/lisp/net/eudcb-mailabbrev.el
index e47f8687093..196e8ff2525 100644
--- a/lisp/net/eudcb-mailabbrev.el
+++ b/lisp/net/eudcb-mailabbrev.el
@@ -1,29 +1,32 @@
 ;;; eudcb-mailabbrev.el --- EUDC - mailabbrev backend -*- lexical-binding: t 
-*-
 
 ;; Copyright (C) 2022-2023 Free Software Foundation, Inc.
-;;
+
 ;; Author: Alexander Adolf
-;;
+;; Package: eudc
+
 ;; This file is part of GNU Emacs.
-;;
+
 ;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
 ;; the Free Software Foundation, either version 3 of the License, or
 ;; (at your option) any later version.
-;;
+
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 ;; GNU General Public License for more details.
-;;
+
 ;; You should have received a copy of the GNU General Public License
 ;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
 
 ;;; Commentary:
+
 ;;    This library provides an interface to the mailabbrev package as
 ;;    an EUDC data source.
 
 ;;; Usage:
+
 ;;    No setup is required, since there is an entry for this backend
 ;;    in `eudc-server-hotlist' by default.
 ;;
diff --git a/lisp/net/eww.el b/lisp/net/eww.el
index 089e481ead2..e43ef2bfe8b 100644
--- a/lisp/net/eww.el
+++ b/lisp/net/eww.el
@@ -620,46 +620,49 @@ The renaming scheme is performed in accordance with
     (let ((redirect (plist-get status :redirect)))
       (when redirect
         (setq url redirect)))
-    (with-current-buffer buffer
-      ;; Save the https peer status.
-      (plist-put eww-data :peer (plist-get status :peer))
-      ;; Make buffer listings more informative.
-      (setq list-buffers-directory url)
-      ;; Let the URL library have a handle to the current URL for
-      ;; referer purposes.
-      (setq url-current-lastloc (url-generic-parse-url url)))
-    (unwind-protect
-       (progn
-         (cond
-           ((and eww-use-external-browser-for-content-type
-                 (string-match-p eww-use-external-browser-for-content-type
-                                 (car content-type)))
-            (erase-buffer)
-            (insert "<title>Unsupported content type</title>")
-            (insert (format "<h1>Content-type %s is unsupported</h1>"
-                            (car content-type)))
-            (insert (format "<a href=%S>Direct link to the document</a>"
-                            url))
-            (goto-char (point-min))
-           (eww-display-html charset url nil point buffer encode))
-          ((eww-html-p (car content-type))
-           (eww-display-html charset url nil point buffer encode))
-          ((equal (car content-type) "application/pdf")
-           (eww-display-pdf))
-          ((string-match-p "\\`image/" (car content-type))
-           (eww-display-image buffer))
-          (t
-           (eww-display-raw buffer (or encode charset 'utf-8))))
-         (with-current-buffer buffer
-           (plist-put eww-data :url url)
-           (eww--after-page-change)
-           (setq eww-history-position 0)
-           (and last-coding-system-used
-                (set-buffer-file-coding-system last-coding-system-used))
-           (run-hooks 'eww-after-render-hook)
-            ;; Enable undo again so that undo works in text input
-            ;; boxes.
-            (setq buffer-undo-list nil)))
+    (when (buffer-live-p buffer)
+      (with-current-buffer buffer
+        ;; Save the https peer status.
+        (plist-put eww-data :peer (plist-get status :peer))
+        ;; Make buffer listings more informative.
+        (setq list-buffers-directory url)
+        ;; Let the URL library have a handle to the current URL for
+        ;; referer purposes.
+        (setq url-current-lastloc (url-generic-parse-url url)))
+      (unwind-protect
+         (progn
+           (cond
+             ((and eww-use-external-browser-for-content-type
+                   (string-match-p eww-use-external-browser-for-content-type
+                                   (car content-type)))
+              (erase-buffer)
+              (insert "<title>Unsupported content type</title>")
+              (insert (format "<h1>Content-type %s is unsupported</h1>"
+                              (car content-type)))
+              (insert (format "<a href=%S>Direct link to the document</a>"
+                              url))
+              (goto-char (point-min))
+             (eww-display-html charset url nil point buffer encode))
+            ((eww-html-p (car content-type))
+             (eww-display-html charset url nil point buffer encode))
+            ((equal (car content-type) "application/pdf")
+             (eww-display-pdf))
+            ((string-match-p "\\`image/" (car content-type))
+             (eww-display-image buffer))
+            (t
+             (eww-display-raw buffer (or encode charset 'utf-8))))
+           (with-current-buffer buffer
+             (plist-put eww-data :url url)
+             (eww--after-page-change)
+             (setq eww-history-position 0)
+             (and last-coding-system-used
+                  (set-buffer-file-coding-system last-coding-system-used))
+             (run-hooks 'eww-after-render-hook)
+              ;; Enable undo again so that undo works in text input
+              ;; boxes.
+              (setq buffer-undo-list nil)))
+        (kill-buffer data-buffer)))
+    (unless (buffer-live-p buffer)
       (kill-buffer data-buffer))))
 
 (defun eww-parse-headers ()
@@ -2059,7 +2062,8 @@ If CHARSET is nil then use UTF-8."
   (let ((completion-extra-properties
          '(:annotation-function (lambda (buf)
                                   (with-current-buffer buf
-                                    (format " %s" (eww-current-url)))))))
+                                    (format " %s" (eww-current-url))))))
+        (curbuf (current-buffer)))
     (pop-to-buffer-same-window
      (read-buffer "Switch to EWW buffer: "
                   (cl-loop for buf in (nreverse (buffer-list))
@@ -2067,9 +2071,10 @@ If CHARSET is nil then use UTF-8."
                            return buf)
                   t
                   (lambda (bufn)
-                    (with-current-buffer
-                        (if (consp bufn) (cdr bufn) (get-buffer bufn))
-                      (derived-mode-p 'eww-mode)))))))
+                    (setq bufn (if (consp bufn) (cdr bufn) (get-buffer bufn)))
+                    (and (with-current-buffer bufn
+                           (derived-mode-p 'eww-mode))
+                         (not (eq bufn curbuf))))))))
 
 (defun eww-toggle-fonts ()
   "Toggle whether to use monospaced or font-enabled layouts."
diff --git a/lisp/net/gnutls.el b/lisp/net/gnutls.el
index 36b1654222a..955172d8bb6 100644
--- a/lisp/net/gnutls.el
+++ b/lisp/net/gnutls.el
@@ -96,7 +96,7 @@ Security'."
           (repeat :tag "List of hostname regexps with flags for each"
            (list
             (choice :tag "Hostname"
-                    (const ".*" :tag "Any hostname")
+                    (const :tag "Any hostname" ".*")
                     regexp)
             (set (const :trustfiles)
                  (const :hostname))))))
diff --git a/lisp/net/mailcap.el b/lisp/net/mailcap.el
index 4d01737e3e6..81cc51f1bf0 100644
--- a/lisp/net/mailcap.el
+++ b/lisp/net/mailcap.el
@@ -689,9 +689,9 @@ to supply to the test."
         status cache result)
     (cond ((not (or (stringp viewer) (fboundp viewer)))
           nil)                         ; Non-existent Lisp function
+         ((null test-info) t)          ; No test clause
          ((setq cache (assoc test mailcap-viewer-test-cache))
           (cadr cache))
-         ((not test-info) t)           ; No test clause
          (t
           (setq
            result
diff --git a/lisp/net/newst-backend.el b/lisp/net/newst-backend.el
index 31dc8d8e177..055a38a76e3 100644
--- a/lisp/net/newst-backend.el
+++ b/lisp/net/newst-backend.el
@@ -1676,15 +1676,6 @@ Sat, 07 Sep 2002 00:00:01 GMT
              nil))))
     nil))
 
-;; FIXME: Can this be replaced by seq-intersection?
-(defun newsticker--lists-intersect-p (list1 list2)
-  "Return t if LIST1 and LIST2 share elements."
-  (let ((result nil))
-    (dolist (elt list1)
-      (if (memq elt list2)
-          (setq result t)))
-    result))
-
 (defun newsticker--update-process-ids ()
   "Update list of ids of active newsticker processes.
 Checks list of active processes against list of newsticker processes."
diff --git a/lisp/net/newst-plainview.el b/lisp/net/newst-plainview.el
index 55fa19cbf2a..5c734aaeb2c 100644
--- a/lisp/net/newst-plainview.el
+++ b/lisp/net/newst-plainview.el
@@ -573,14 +573,10 @@ calls `w3m-toggle-inline-image'.  It works only if
                 (when pos
                   (goto-char pos)
                   (when (get-text-property pos 'w3m-image)
-                    (let ((invis (newsticker--lists-intersect-p
-                                  (get-text-property (1- (point))
-                                                     'invisible)
-                                  buffer-invisibility-spec)))
-                      (unless  (car (get-text-property (1- (point))
-                                                       'display))
-                        (unless invis
-                          (w3m-toggle-inline-image t)))))))))))))
+                    (unless (car (get-text-property (1- (point))
+                                                    'display))
+                      (unless (invisible-p (1- (point)))
+                        (w3m-toggle-inline-image t))))))))))))
 
 ;; ======================================================================
 ;;; Keymap stuff
@@ -606,9 +602,7 @@ is non-nil."
          (goto-char (point-min))
          (newsticker-next-new-item t))
        (setq go-ahead nil))
-      (unless (newsticker--lists-intersect-p
-               (get-text-property (point) 'invisible)
-               buffer-invisibility-spec)
+      (unless (invisible-p (point))
        ;; this item is invisible -- continue search
         (setq go-ahead nil))))
   (run-hooks 'newsticker-select-item-hook)
@@ -627,9 +621,7 @@ is non-nil."
        (unless do-not-wrap-at-bob
          (goto-char (point-max))
          (newsticker--buffer-goto '(item) 'new t)))
-      (unless (newsticker--lists-intersect-p
-               (get-text-property (point) 'invisible)
-                   buffer-invisibility-spec)
+      (unless (invisible-p (point))
        (setq go-ahead nil))))
   (run-hooks 'newsticker-select-item-hook)
   (point))
@@ -652,9 +644,7 @@ non-nil."
        (unless do-not-wrap-at-eob
          (goto-char (point-min)))
        (setq go-ahead nil))
-      (unless (newsticker--lists-intersect-p
-               (get-text-property (point) 'invisible)
-                   buffer-invisibility-spec)
+      (unless (invisible-p (point))
        (setq go-ahead nil))))
   (run-hooks 'newsticker-select-item-hook)
   (force-mode-line-update)
@@ -673,9 +663,7 @@ auto-narrow-to-item is enabled, nil is returned."
       (while go-ahead
         (unless (newsticker--buffer-goto '(item))
           (setq go-ahead nil))
-        (unless (newsticker--lists-intersect-p
-                 (get-text-property (point) 'invisible)
-                 buffer-invisibility-spec)
+        (unless (invisible-p (point))
           (setq go-ahead nil)))
       (if (and (> (point) current-pos)
                (< (point) end-of-feed))
@@ -700,9 +688,7 @@ is non-nil."
        (goto-char (point-max))))
     (while go-ahead
       (if (newsticker--buffer-goto search-list nil t)
-          (unless (newsticker--lists-intersect-p
-                   (get-text-property (point) 'invisible)
-                   buffer-invisibility-spec)
+          (unless (invisible-p (point))
             (setq go-ahead nil))
         (goto-char (point-min))
         (setq go-ahead nil))))
@@ -1079,9 +1065,7 @@ If VALUE is nil, auto-narrowing is turned off, otherwise 
it is turned on."
       (while (< (point) (point-max))
         (unless (newsticker--buffer-goto '(item))
           (throw 'result nil))
-        (unless (newsticker--lists-intersect-p
-                 (get-text-property (point) 'invisible)
-                 buffer-invisibility-spec)
+        (unless (invisible-p (point))
           (throw 'result t))))))
 
 (defun newsticker-previous-item-available-p ()
@@ -1091,9 +1075,7 @@ If VALUE is nil, auto-narrowing is turned off, otherwise 
it is turned on."
       (while (> (point) (point-min))
         (unless (newsticker--buffer-goto '(item) nil t)
           (throw 'result nil))
-        (unless (newsticker--lists-intersect-p
-                 (get-text-property (point) 'invisible)
-                 buffer-invisibility-spec)
+        (unless (invisible-p (point))
           (throw 'result t))))))
 
 (defun newsticker-item-not-old-p ()
diff --git a/lisp/net/ntlm.el b/lisp/net/ntlm.el
index c92c90bf694..3d994ed9e76 100644
--- a/lisp/net/ntlm.el
+++ b/lisp/net/ntlm.el
@@ -1,6 +1,6 @@
 ;;; ntlm.el --- NTLM (NT LanManager) authentication support  -*- 
lexical-binding:t -*-
 
-;; Copyright (C) 2001, 2007-2023 Free Software Foundation, Inc.
+;; Copyright (C) 2001-2023 Free Software Foundation, Inc.
 
 ;; Author: Taro Kawagishi <tarok@transpulse.org>
 ;; Maintainer: Thomas Fitzsimmons <fitzsim@fitzsim.org>
@@ -8,6 +8,9 @@
 ;; Version: 2.1.0
 ;; Created: February 2001
 
+;; This is a GNU ELPA :core package.  Avoid functionality that is not
+;; compatible with the version of Emacs recorded above.
+
 ;; This file is part of GNU Emacs.
 
 ;; GNU Emacs is free software: you can redistribute it and/or modify
diff --git a/lisp/net/rcirc.el b/lisp/net/rcirc.el
index a6dad4b640d..3f6242d9347 100644
--- a/lisp/net/rcirc.el
+++ b/lisp/net/rcirc.el
@@ -859,6 +859,7 @@ If QUIET is non-nil, no not emit a message."
       (if (rcirc--connection-open-p process)
           (throw 'exit (or quiet (message "Server process is alive")))
         (delete-process process))
+      (setq rcirc-user-authenticated nil)
       (let ((conn-info rcirc-connection-info))
         (setf (nth 5 conn-info)
               (cl-remove-if-not #'rcirc-channel-p
diff --git a/lisp/net/shr.el b/lisp/net/shr.el
index b5bb7b42650..645e1cc51e5 100644
--- a/lisp/net/shr.el
+++ b/lisp/net/shr.el
@@ -2617,11 +2617,12 @@ flags that control whether to collect or render 
objects."
     columns))
 
 (defun shr-count (dom elem)
+  ;; This is faster than `seq-count', and shr can use it.
   (let ((i 0))
     (dolist (sub (dom-children dom))
       (when (and (not (stringp sub))
-                (eq (dom-tag sub) elem))
-       (setq i (1+ i))))
+                 (eq (dom-tag sub) elem))
+        (setq i (1+ i))))
     i))
 
 (defun shr-max-columns (dom)
diff --git a/lisp/net/sieve-mode.el b/lisp/net/sieve-mode.el
index a54c3c6f103..a1ad0bffddc 100644
--- a/lisp/net/sieve-mode.el
+++ b/lisp/net/sieve-mode.el
@@ -55,39 +55,19 @@
 ;; Font-lock
 
 (defface sieve-control-commands
-  '((((type tty) (class color)) (:foreground "blue" :weight light))
-    (((class grayscale) (background light)) (:foreground "LightGray" :bold t))
-    (((class grayscale) (background dark)) (:foreground "DimGray" :bold t))
-    (((class color) (background light)) (:foreground "Orchid"))
-    (((class color) (background dark)) (:foreground "LightSteelBlue"))
-    (t (:bold t)))
+  '((t :inherit font-lock-builtin-face))
   "Face used for Sieve Control Commands.")
 
 (defface sieve-action-commands
-  '((((type tty) (class color)) (:foreground "blue" :weight bold))
-    (((class color) (background light)) (:foreground "Blue"))
-    (((class color) (background dark)) (:foreground "LightSkyBlue"))
-    (t (:inverse-video t :bold t)))
+  '((t :inherit font-lock-function-name-face))
   "Face used for Sieve Action Commands.")
 
 (defface sieve-test-commands
-  '((((type tty) (class color)) (:foreground "magenta"))
-    (((class grayscale) (background light))
-     (:foreground "LightGray" :bold t :underline t))
-    (((class grayscale) (background dark))
-     (:foreground "Gray50" :bold t :underline t))
-    (((class color) (background light)) (:foreground "CadetBlue"))
-    (((class color) (background dark)) (:foreground "Aquamarine"))
-    (t (:bold t :underline t)))
+  '((t :inherit font-lock-constant-face))
   "Face used for Sieve Test Commands.")
 
 (defface sieve-tagged-arguments
-  '((((type tty) (class color)) (:foreground "cyan" :weight bold))
-    (((class grayscale) (background light)) (:foreground "LightGray" :bold t))
-    (((class grayscale) (background dark)) (:foreground "DimGray" :bold t))
-    (((class color) (background light)) (:foreground "Purple"))
-    (((class color) (background dark)) (:foreground "Cyan"))
-    (t (:bold t)))
+  '((t :inherit font-lock-keyword face))
   "Face used for Sieve Tagged Arguments.")
 
 
diff --git a/lisp/net/soap-client.el b/lisp/net/soap-client.el
index 010e86354b6..b2bb59a25a0 100644
--- a/lisp/net/soap-client.el
+++ b/lisp/net/soap-client.el
@@ -11,6 +11,9 @@
 ;; URL: https://github.com/alex-hhh/emacs-soap-client
 ;; Package-Requires: ((emacs "24.1") (cl-lib "0.6.1"))
 
+;; This is a GNU ELPA :core package.  Avoid functionality that is not
+;; compatible with the version of Emacs recorded above.
+
 ;; This file is part of GNU Emacs.
 
 ;; GNU Emacs is free software: you can redistribute it and/or modify
diff --git a/lisp/net/tramp-cmds.el b/lisp/net/tramp-cmds.el
index 8d95adb597c..22fb7eee8f3 100644
--- a/lisp/net/tramp-cmds.el
+++ b/lisp/net/tramp-cmds.el
@@ -31,6 +31,8 @@
 (require 'tramp)
 
 ;; Pacify byte-compiler.
+(declare-function dired-advertise "dired")
+(declare-function dired-unadvertise "dired")
 (declare-function mml-mode "mml")
 (declare-function mml-insert-empty-tag "mml")
 (declare-function reporter-dump-variable "reporter")
@@ -75,6 +77,8 @@ SYNTAX can be one of the symbols `default' (default),
       (when (tramp-tramp-file-p (tramp-get-default-directory x)) x))
     (buffer-list))))
 
+;;; Cleanup
+
 ;;;###tramp-autoload
 (defvar tramp-cleanup-connection-hook nil
   "List of functions to be called after Tramp connection is cleaned up.
@@ -221,13 +225,13 @@ functions are called with `current-buffer' set."
 (add-hook 'tramp-cleanup-some-buffers-hook
          #'buffer-file-name)
 
-(defun tramp-cleanup-dired-buffer-p ()
+(defun tramp-dired-buffer-p ()
   "Return t if current buffer runs `dired-mode'."
   (declare (tramp-suppress-trace t))
   (derived-mode-p 'dired-mode))
 
 (add-hook 'tramp-cleanup-some-buffers-hook
-         #'tramp-cleanup-dired-buffer-p)
+         #'tramp-dired-buffer-p)
 
 (defvar tramp-tainted-remote-process-buffers nil
   "List of process buffers to be cleaned up.")
@@ -256,12 +260,12 @@ functions are called with `current-buffer' set."
            (remove-hook 'kill-buffer-hook
                         
#'tramp-delete-tainted-remote-process-buffer-function)))
 
-(defun tramp-cleanup-remote-process-p ()
+(defun tramp-remote-process-p ()
   "Return t if current buffer belongs to a remote process."
   (memq (current-buffer) tramp-tainted-remote-process-buffers))
 
 (add-hook 'tramp-cleanup-some-buffers-hook
-         #'tramp-cleanup-remote-process-p)
+         #'tramp-remote-process-p)
 
 ;;;###tramp-autoload
 (defun tramp-cleanup-some-buffers ()
@@ -288,6 +292,8 @@ non-nil."
   (let ((tramp-cleanup-some-buffers-hook '(tramp-compat-always)))
     (tramp-cleanup-some-buffers)))
 
+;;; Rename
+
 (defcustom tramp-default-rename-alist nil
   "Default target for renaming remote buffer file names.
 This is an alist of cons cells (SOURCE . TARGET).  The first
@@ -552,6 +558,73 @@ For details, see `tramp-rename-files'."
 (function-put
  #'tramp-rename-these-files 'completion-predicate #'tramp-command-completion-p)
 
+;;; Run as sudo
+
+(defcustom tramp-file-name-with-method "sudo"
+  "Which method to be used in `tramp-file-name-with-sudo'."
+  :group 'tramp
+  :version "30.1"
+  :type '(choice (const "su")
+                (const "sudo")
+                (const "doas")
+                (const "ksu")))
+
+(defun tramp-file-name-with-sudo (filename)
+  "Convert FILENAME into a multi-hop file name with \"sudo\".
+An alternative method could be chosen with `tramp-file-name-with-method'."
+  (setq filename (expand-file-name filename))
+  (if (tramp-tramp-file-p filename)
+      (with-parsed-tramp-file-name filename nil
+       (cond
+        ;; Remote file with proper method.
+        ((string-equal method tramp-file-name-with-method)
+         filename)
+        ;; Remote file on the local host.
+        ((and
+          (stringp tramp-local-host-regexp) (stringp host)
+          (string-match-p tramp-local-host-regexp host))
+         (tramp-make-tramp-file-name
+          (make-tramp-file-name
+           :method tramp-file-name-with-method :localname localname)))
+        ;; Remote file with multi-hop capable method..
+        ((tramp-multi-hop-p v)
+         (tramp-make-tramp-file-name
+          (make-tramp-file-name
+           :method (tramp-find-method tramp-file-name-with-method nil host)
+           :user (tramp-find-user tramp-file-name-with-method nil host)
+           :host (tramp-find-host tramp-file-name-with-method nil host)
+           :localname localname :hop (tramp-make-tramp-hop-name v))))
+        ;; Other remote file.
+        (t (tramp-user-error v "Multi-hop with `%s' not applicable" method))))
+    ;; Local file.
+    (tramp-make-tramp-file-name
+     (make-tramp-file-name
+      :method tramp-file-name-with-method :localname filename))))
+
+;;;###tramp-autoload
+(defun tramp-revert-buffer-with-sudo ()
+  "Revert current buffer to visit with \"sudo\" permissions.
+An alternative method could be chosen with `tramp-file-name-with-method'.
+If the buffer visits a file, the file is replaced.
+If the buffer runs `dired', the buffer is reverted."
+  (interactive)
+  (cond
+   ((buffer-file-name)
+    (find-alternate-file (tramp-file-name-with-sudo (buffer-name))))
+   ((tramp-dired-buffer-p)
+    (dired-unadvertise (expand-file-name default-directory))
+    (setq default-directory (tramp-file-name-with-sudo default-directory)
+         list-buffers-directory
+         (tramp-file-name-with-sudo list-buffers-directory))
+    (if (consp dired-directory)
+       (setcar
+        dired-directory (tramp-file-name-with-sudo (car dired-directory)))
+      (setq dired-directory (tramp-file-name-with-sudo dired-directory)))
+    (dired-advertise)
+    (revert-buffer))))
+
+;;; Recompile on ELPA
+
 ;; This function takes action since Emacs 28.1, when
 ;; `read-extended-command-predicate' is set to
 ;; `command-completion-default-include-p'.
diff --git a/lisp/net/tramp-container.el b/lisp/net/tramp-container.el
index 687092e7e95..7383ea583cb 100644
--- a/lisp/net/tramp-container.el
+++ b/lisp/net/tramp-container.el
@@ -157,6 +157,28 @@ If it is nil, the default context will be used."
 (defconst tramp-flatpak-method "flatpak"
   "Tramp method name to use to connect to Flatpak sandboxes.")
 
+;;;###tramp-autoload
+(defmacro tramp-skeleton-completion-function (method &rest body)
+  "Skeleton for `tramp-*-completion-function' with multi-hop support.
+BODY is the backend specific code."
+  (declare (indent 1) (debug t))
+  `(let* ((default-directory
+          (or (and (member ,method tramp-completion-multi-hop-methods)
+                   tramp--last-hop-directory)
+              tramp-compat-temporary-file-directory))
+         (program (let ((tramp-verbose 0))
+                    (tramp-get-method-parameter
+                     (make-tramp-file-name :method ,method)
+                     'tramp-login-program)))
+         (vec (when (tramp-tramp-file-p default-directory)
+                (tramp-dissect-file-name default-directory)))
+         non-essential)
+     ;; We don't use connection properties, because this information
+     ;; shouldn't be kept persistently.
+     (with-tramp-file-property
+        vec (concat "/" ,method ":") "user-host-completions"
+       ,@body)))
+
 ;;;###tramp-autoload
 (defun tramp-container--completion-function (method)
   "List running containers available for connection.
@@ -165,61 +187,51 @@ METHOD is the Tramp method to be used for \"ps\", either
 
 This function is used by `tramp-set-completion-function', please
 see its function help for a description of the format."
-  (let ((default-directory
-        (or (and tramp-completion-remote-containers tramp--last-hop-directory)
-            tramp-compat-temporary-file-directory))
-       (program (tramp-get-method-parameter
-                 (make-tramp-file-name :method method) 'tramp-login-program))
-       non-essential)
-    ;; We don't use connection properties, because this information
-    ;; shouldn't be kept persistently.
-    (with-tramp-file-property
-       (when (tramp-tramp-file-p default-directory)
-         (tramp-dissect-file-name default-directory))
-       (concat "/" method ":") "user-host-completions"
-      (when-let ((raw-list
-                 (shell-command-to-string
-                  (concat program " ps --format '{{.ID}}\t{{.Names}}'")))
-                (lines (split-string raw-list "\n" 'omit))
-                (names
-                 (mapcar
-                  (lambda (line)
-                    (when (string-match
-                           (rx bol (group (1+ nonl))
-                               "\t" (? (group (1+ nonl))) eol)
-                           line)
-                      (or (match-string 2 line) (match-string 1 line))))
-                  lines)))
-       (mapcar (lambda (name) (list nil name)) (delq nil names))))))
+  (tramp-skeleton-completion-function method
+    (when-let ((raw-list
+               (shell-command-to-string
+                (concat program " ps --format '{{.ID}}\t{{.Names}}'")))
+              (lines (split-string raw-list "\n" 'omit))
+              (names
+               (mapcar
+                (lambda (line)
+                  (when (string-match
+                         (rx bol (group (1+ nonl))
+                             "\t" (? (group (1+ nonl))) eol)
+                         line)
+                    (or (match-string 2 line) (match-string 1 line))))
+                lines)))
+      (mapcar (lambda (name) (list nil name)) (delq nil names)))))
 
 ;;;###tramp-autoload
-(defun tramp-kubernetes--completion-function (&rest _args)
+(defun tramp-kubernetes--completion-function (method)
   "List Kubernetes pods available for connection.
 
 This function is used by `tramp-set-completion-function', please
 see its function help for a description of the format."
-  (when-let ((default-directory tramp-compat-temporary-file-directory)
-            (raw-list (shell-command-to-string
-                       (concat
-                        tramp-kubernetes-program " "
-                        (tramp-kubernetes--context-namespace nil)
-                         " get pods --no-headers"
-                        ;; We separate pods by "|".  Inside a pod,
-                        ;; its name is separated from the containers
-                        ;; by ":".  Containers are separated by ",".
-                        " -o jsonpath='{range 
.items[*]}{\"|\"}{.metadata.name}"
-                        "{\":\"}{range .spec.containers[*]}{.name}{\",\"}"
-                        "{end}{end}'")))
-             (lines (split-string raw-list "|" 'omit)))
-    (let (names)
-      (dolist (line lines)
-       (setq line (split-string line ":" 'omit))
-       ;; Pod name.
-       (push (car line) names)
-       ;; Container names.
-       (dolist (elt (split-string (cadr line) "," 'omit))
-         (push (concat elt "." (car line)) names)))
-      (mapcar (lambda (name) (list nil name)) (delq nil names)))))
+  (tramp-skeleton-completion-function method
+    (when-let ((raw-list
+               (shell-command-to-string
+                (concat
+                 program " "
+                 (tramp-kubernetes--context-namespace vec)
+                  " get pods --no-headers"
+                 ;; We separate pods by "|".  Inside a pod, its name
+                 ;; is separated from the containers by ":".
+                 ;; Containers are separated by ",".
+                 " -o jsonpath='{range .items[*]}{\"|\"}{.metadata.name}"
+                 "{\":\"}{range .spec.containers[*]}{.name}{\",\"}"
+                 "{end}{end}'")))
+               (lines (split-string raw-list "|" 'omit)))
+      (let (names)
+       (dolist (line lines)
+         (setq line (split-string line ":" 'omit))
+         ;; Pod name.
+         (push (car line) names)
+         ;; Container names.
+         (dolist (elt (split-string (cadr line) "," 'omit))
+           (push (concat elt "." (car line)) names)))
+       (mapcar (lambda (name) (list nil name)) (delq nil names))))))
 
 (defconst tramp-kubernetes--host-name-regexp
   (rx (? (group (regexp tramp-host-regexp)) ".")
@@ -242,30 +254,53 @@ see its function help for a description of the format."
             (match-string 2 host)))
       ""))
 
+;; We must change `vec' and `default-directory' to the previous hop,
+;; in order to run `process-file' in a proper environment.
+(defmacro tramp-skeleton-kubernetes-vector (vec &rest body)
+  "Skeleton for `tramp-kubernetes--current-context*' with multi-hop support.
+BODY is the backend specific code."
+  (declare (indent 1) (debug t))
+  `(let* ((vec
+          (cond
+           ((null ,vec) tramp-null-hop)
+           ((equal (tramp-file-name-method ,vec) tramp-kubernetes-method)
+            (if (tramp-file-name-hop ,vec)
+                (tramp-dissect-hop-name (tramp-file-name-hop ,vec))
+              tramp-null-hop))
+           (t ,vec)))
+         (default-directory
+          (if (equal vec tramp-null-hop)
+              tramp-compat-temporary-file-directory
+            (tramp-make-tramp-file-name vec "/"))))
+     ,@body))
+
 (defun tramp-kubernetes--current-context (vec)
   "Return Kubernetes current context.
 Obey `tramp-kubernetes-context'"
   (or tramp-kubernetes-context
-      (with-tramp-connection-property nil "current-context"
-       (with-temp-buffer
-         (when (zerop
-                (tramp-call-process
-                 vec tramp-kubernetes-program nil t nil
-                 "config" "current-context"))
-           (goto-char (point-min))
-           (buffer-substring (point) (line-end-position)))))))
+      (tramp-skeleton-kubernetes-vector vec
+       (with-tramp-file-property
+           vec (concat "/" tramp-kubernetes-method ":") "current-context"
+         (with-temp-buffer
+           (when (zerop
+                  (process-file
+                   tramp-kubernetes-program nil t nil
+                   "config" "current-context"))
+             (goto-char (point-min))
+             (buffer-substring (point) (line-end-position))))))))
 
 (defun tramp-kubernetes--current-context-data (vec)
   "Return Kubernetes current context data as JSON string."
   (when-let ((current-context (tramp-kubernetes--current-context vec)))
-    (with-temp-buffer
-      (when (zerop
-            (tramp-call-process
-             vec tramp-kubernetes-program nil t nil
-             "config" "view" "-o"
-             (format
-              "jsonpath='{.contexts[?(@.name == \"%s\")]}'" current-context)))
-       (buffer-string)))))
+    (tramp-skeleton-kubernetes-vector vec
+      (with-temp-buffer
+       (when (zerop
+              (process-file
+               tramp-kubernetes-program nil t nil
+               "config" "view" "-o"
+               (format
+                "jsonpath='{.contexts[?(@.name == \"%s\")]}'" 
current-context)))
+         (buffer-string))))))
 
 ;;;###tramp-autoload
 (defun tramp-kubernetes--context-namespace (vec)
@@ -279,49 +314,48 @@ Obey `tramp-kubernetes-context'"
    " "))
 
 ;;;###tramp-autoload
-(defun tramp-toolbox--completion-function (&rest _args)
+(defun tramp-toolbox--completion-function (method)
   "List Toolbox containers available for connection.
 
 This function is used by `tramp-set-completion-function', please
 see its function help for a description of the format."
-  (when-let ((default-directory tramp-compat-temporary-file-directory)
-            (raw-list (shell-command-to-string
-                       (concat tramp-toolbox-program " list -c")))
-            ;; Ignore header line.
-             (lines (cdr (split-string raw-list "\n" 'omit)))
-             (names (mapcar
-                    (lambda (line)
-                       (when (string-match
-                             (rx bol (1+ (not space))
-                                 (1+ space) (group (1+ (not space))) space)
-                             line)
-                        (match-string 1 line)))
-                     lines)))
-    (mapcar (lambda (name) (list nil name)) (delq nil names))))
+  (tramp-skeleton-completion-function method
+    (when-let ((raw-list (shell-command-to-string (concat program " list -c")))
+              ;; Ignore header line.
+               (lines (cdr (split-string raw-list "\n" 'omit)))
+               (names (mapcar
+                      (lambda (line)
+                        (when (string-match
+                               (rx bol (1+ (not space))
+                                   (1+ space) (group (1+ (not space))) space)
+                               line)
+                          (match-string 1 line)))
+                       lines)))
+      (mapcar (lambda (name) (list nil name)) (delq nil names)))))
 
 ;;;###tramp-autoload
-(defun tramp-flatpak--completion-function (&rest _args)
+(defun tramp-flatpak--completion-function (method)
   "List Flatpak sandboxes available for connection.
 It returns application IDs or, in case there is no application
 ID, instance IDs.
 
 This function is used by `tramp-set-completion-function', please
 see its function help for a description of the format."
-  (when-let ((default-directory tramp-compat-temporary-file-directory)
-            (raw-list
-             (shell-command-to-string
-              (concat tramp-flatpak-program
-                      " ps --columns=instance,application")))
-             (lines (split-string raw-list "\n" 'omit))
-             (names (mapcar
-                    (lambda (line)
-                       (when (string-match
-                             (rx bol (* space) (group (+ (not space)))
-                                 (? (+ space) (group (+ (not space)))) eol)
-                             line)
-                        (or (match-string 2 line) (match-string 1 line))))
-                     lines)))
-    (mapcar (lambda (name) (list nil name)) (delq nil names))))
+  (tramp-skeleton-completion-function method
+    (when-let ((raw-list
+               (shell-command-to-string
+                ;; Ignore header line.
+                (concat program " ps --columns=instance,application | cat -")))
+               (lines (split-string raw-list "\n" 'omit))
+               (names (mapcar
+                      (lambda (line)
+                        (when (string-match
+                               (rx bol (* space) (group (+ (not space)))
+                                   (? (+ space) (group (+ (not space)))) eol)
+                               line)
+                          (or (match-string 2 line) (match-string 1 line))))
+                       lines)))
+      (mapcar (lambda (name) (list nil name)) (delq nil names)))))
 
 ;;;###tramp-autoload
 (defvar tramp-default-remote-shell) ;; Silence byte compiler.
@@ -403,15 +437,21 @@ see its function help for a description of the format."
 
  (tramp-set-completion-function
   tramp-kubernetes-method
-  '((tramp-kubernetes--completion-function "")))
+  `((tramp-kubernetes--completion-function ,tramp-kubernetes-method)))
 
  (tramp-set-completion-function
   tramp-toolbox-method
-  '((tramp-toolbox--completion-function "")))
+  `((tramp-toolbox--completion-function ,tramp-toolbox-method)))
 
  (tramp-set-completion-function
   tramp-flatpak-method
-  '((tramp-flatpak--completion-function "")))
+  `((tramp-flatpak--completion-function ,tramp-flatpak-method)))
+
+ (add-to-list 'tramp-completion-multi-hop-methods tramp-docker-method)
+ (add-to-list 'tramp-completion-multi-hop-methods tramp-podman-method)
+ (add-to-list 'tramp-completion-multi-hop-methods tramp-kubernetes-method)
+ (add-to-list 'tramp-completion-multi-hop-methods tramp-toolbox-method)
+ (add-to-list 'tramp-completion-multi-hop-methods tramp-flatpak-method)
 
  ;; Default connection-local variables for Tramp.
 
@@ -421,7 +461,8 @@ see its function help for a description of the format."
      (tramp-extra-expand-args
       . (?a (tramp-kubernetes--container (car tramp-current-connection))
         ?h (tramp-kubernetes--pod (car tramp-current-connection))
-        ?x (tramp-kubernetes--context-namespace (car 
tramp-current-connection)))))
+        ?x (tramp-kubernetes--context-namespace
+            (car tramp-current-connection)))))
    "Default connection-local variables for remote kubernetes connections.")
 
  (connection-local-set-profile-variables
diff --git a/lisp/net/tramp-gvfs.el b/lisp/net/tramp-gvfs.el
index 71ef8215ab0..577760f806c 100644
--- a/lisp/net/tramp-gvfs.el
+++ b/lisp/net/tramp-gvfs.el
@@ -980,8 +980,8 @@ or `dbus-call-method-asynchronously'."
   (vec bus service path interface)
   "Return all properties of INTERFACE.
 The call will be traced by Tramp with trace level 6."
-     ;; Check, that interface exists at object path.  Retrieve properties.
   (declare (indent 1) (debug t))
+  ;; Check, that interface exists at object path.  Retrieve properties.
   `(when (member
          ,interface
          (tramp-dbus-function
diff --git a/lisp/net/tramp-message.el b/lisp/net/tramp-message.el
index 5b909b69ae3..8afc8d5fd87 100644
--- a/lisp/net/tramp-message.el
+++ b/lisp/net/tramp-message.el
@@ -236,9 +236,12 @@ ARGUMENTS to actually emit the message (if applicable)."
                  tramp-trace-functions))
              (unless (get elt 'tramp-suppress-trace)
                (trace-function-background elt (tramp-trace-buffer-name vec)))))
-         ;; Delete debug file.
+         ;; Delete debug file.  Announce its further existence.
          (when (and tramp-debug-to-file (tramp-get-debug-file-name vec))
-           (ignore-errors (delete-file (tramp-get-debug-file-name vec)))))
+           (ignore-errors (delete-file (tramp-get-debug-file-name vec)))
+           (let ((message-log-max t))
+             (message
+              "Tramp debug file is %s" (tramp-get-debug-file-name vec)))))
        (unless (bolp)
          (insert "\n"))
        ;; Timestamp.
@@ -301,18 +304,6 @@ applicable)."
           (if tramp-debug-command-messages
               (max tramp-verbose 6) tramp-verbose)))
       (when (<= level tramp-verbose)
-       ;; Display only when there is a minimum level, and the
-       ;; progress reporter doesn't suppress further messages.
-       (when (and (<= level 3) (null tramp-inhibit-progress-reporter))
-         (apply #'message
-                (concat
-                 (cond
-                  ((= level 0) "")
-                  ((= level 1) "")
-                  ((= level 2) "Warning: ")
-                  (t           "Tramp: "))
-                 fmt-string)
-                arguments))
        ;; Log only when there is a minimum level.
        (when (>= tramp-verbose 4)
          (let ((tramp-verbose 0))
@@ -338,7 +329,19 @@ applicable)."
            (apply #'tramp-debug-message
                   vec-or-proc
                   (concat (format "(%d) # " level) fmt-string)
-                  arguments)))))))
+                  arguments)))
+       ;; Display only when there is a minimum level, and the
+       ;; progress reporter doesn't suppress further messages.
+       (when (and (<= level 3) (null tramp-inhibit-progress-reporter))
+         (apply #'message
+                (concat
+                 (cond
+                  ((= level 0) "")
+                  ((= level 1) "")
+                  ((= level 2) "Warning: ")
+                  (t           "Tramp: "))
+                 fmt-string)
+                arguments))))))
 
 ;; We cannot use the `declare' form for `tramp-suppress-trace' in
 ;; autoloaded functions, because the tramp-loaddefs.el generation
diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el
index 0599f89655c..95c27626166 100644
--- a/lisp/net/tramp-sh.el
+++ b/lisp/net/tramp-sh.el
@@ -540,6 +540,7 @@ shell from reading its init file."
 (defconst tramp-actions-before-shell
   '((tramp-login-prompt-regexp tramp-action-login)
     (tramp-password-prompt-regexp tramp-action-password)
+    (tramp-otp-password-prompt-regexp tramp-action-otp-password)
     (tramp-wrong-passwd-regexp tramp-action-permission-denied)
     (shell-prompt-pattern tramp-action-succeed)
     (tramp-shell-prompt-pattern tramp-action-succeed)
@@ -563,6 +564,7 @@ corresponding PATTERN matches, the ACTION function is 
called.")
 
 (defconst tramp-actions-copy-out-of-band
   '((tramp-password-prompt-regexp tramp-action-password)
+    (tramp-otp-password-prompt-regexp tramp-action-otp-password)
     (tramp-wrong-passwd-regexp tramp-action-permission-denied)
     (tramp-copy-failed-regexp tramp-action-permission-denied)
     (tramp-security-key-confirm-regexp tramp-action-show-and-confirm-message)
@@ -634,7 +636,10 @@ characters need to be doubled.")
 
 (defconst tramp-perl-file-name-all-completions
   "%p -e '
-($dir = $ARGV[0]) =~ s#/+$##;
+$dir = $ARGV[0];
+if ($dir ne \"/\") {
+  $dir =~ s#/+$##;
+}
 opendir(d, $dir) || die(\"$dir: $!\\nfail\\n\");
 @files = readdir(d); closedir(d);
 print \"(\\n\";
@@ -740,6 +745,13 @@ on the remote file system, including SELinux context.
 Format specifiers are replaced by `tramp-expand-script', percent
 characters need to be doubled.")
 
+(defconst tramp-ls-file-attributes
+  "%s -ild %s \"$1\" || return\n%s -lnd%s %s \"$1\""
+  "Shell function to produce output suitable for use with `file-attributes'
+on the remote file system.
+Format specifiers are replaced by `tramp-expand-script', percent
+characters need to be doubled.")
+
 (defconst tramp-perl-directory-files-and-attributes
   "%p -e '
 chdir($ARGV[0]) or printf(\"\\\"Cannot change to $ARGV[0]: $''!''\\\"\\n\"), 
exit();
@@ -1279,6 +1291,9 @@ Operations not mentioned here will be handled by the 
normal Emacs functions.")
 (defconst tramp-sunos-unames (rx (| "SunOS 5.10" "SunOS 5.11"))
   "Regexp to determine remote SunOS.")
 
+(defconst tramp-bsd-unames (rx (| "BSD" "DragonFly" "Darwin"))
+  "Regexp to determine remote *BSD and macOS.")
+
 (defun tramp-sh--quoting-style-options (vec)
   "Quoting style options to be used for VEC."
   (or
@@ -1292,43 +1307,25 @@ Operations not mentioned here will be handled by the 
normal Emacs functions.")
 
 (defun tramp-do-file-attributes-with-ls (vec localname)
   "Implement `file-attributes' for Tramp files using the ls(1) command."
-  (let (symlinkp dirp
+  (tramp-message vec 5 "file attributes with ls: %s" localname)
+  (let ((tramp-ls-file-attributes
+        (format tramp-ls-file-attributes
+                (tramp-get-ls-command vec)
+                ;; On systems which have no quoting style, file
+                ;; names with special characters could fail.
+                (tramp-sh--quoting-style-options vec)
+                (tramp-get-ls-command vec)
+                (if (tramp-remote-selinux-p vec) "Z" "")
+                (tramp-sh--quoting-style-options vec)))
+       symlinkp dirp
        res-inode res-filemodes res-numlinks
        res-uid-string res-gid-string res-uid-integer res-gid-integer
        res-size res-symlink-target res-context)
-    (tramp-message vec 5 "file attributes with ls: %s" localname)
-    ;; We cannot send both commands combined, it could exceed NAME_MAX
-    ;; or PATH_MAX.  Happened on macOS, for example.
+    (tramp-maybe-send-script
+     vec tramp-ls-file-attributes "tramp_ls_file_attributes")
     (when (tramp-send-command-and-check
-           vec
-           (format "cd %s && (%s %s || %s -h %s)"
-                  (tramp-shell-quote-argument
-                   (tramp-run-real-handler
-                    #'file-name-directory (list localname)))
-                  (tramp-get-file-exists-command vec)
-                  (if (string-empty-p (file-name-nondirectory localname))
-                      "."
-                     (tramp-shell-quote-argument
-                     (file-name-nondirectory localname)))
-                   (tramp-get-test-command vec)
-                  (if (string-empty-p (file-name-nondirectory localname))
-                      "."
-                     (tramp-shell-quote-argument
-                     (file-name-nondirectory localname)))))
-      (tramp-send-command
-       vec
-       (format "%s -ild %s %s; %s -lnd%s %s %s"
-               (tramp-get-ls-command vec)
-               ;; On systems which have no quoting style, file names
-               ;; with special characters could fail.
-               (tramp-sh--quoting-style-options vec)
-               (tramp-shell-quote-argument localname)
-               (tramp-get-ls-command vec)
-              (if (tramp-remote-selinux-p vec) "Z" "")
-               ;; On systems which have no quoting style, file names
-               ;; with special characters could fail.
-               (tramp-sh--quoting-style-options vec)
-               (tramp-shell-quote-argument localname)))
+          vec (format "tramp_ls_file_attributes %s"
+                      (tramp-shell-quote-argument localname)))
       ;; Parse `ls -l' output ...
       (with-current-buffer (tramp-get-buffer vec)
         (when (> (buffer-size) 0)
@@ -1795,7 +1792,7 @@ ID-FORMAT valid values are `string' and `integer'."
                 ;; On BSD-derived systems files always inherit the
                  ;; parent directory's group, so skip the group-gid
                  ;; test.
-                 (tramp-check-remote-uname v (rx (| "BSD" "DragonFly" 
"Darwin")))
+                 (tramp-check-remote-uname v tramp-bsd-unames)
                 (= (file-attribute-group-id attributes)
                    (tramp-get-remote-gid v 'integer)))))))))
 
@@ -2640,21 +2637,23 @@ The method used must be an out-of-band method."
 (defun tramp-sh-handle-insert-directory
     (filename switches &optional wildcard full-directory-p)
   "Like `insert-directory' for Tramp files."
-  (unless switches (setq switches ""))
-  ;; Check, whether directory is accessible.
-  (unless wildcard
-    (access-file filename "Reading directory"))
-  (with-parsed-tramp-file-name (expand-file-name filename) nil
-    (if (and (featurep 'ls-lisp)
-            (not ls-lisp-use-insert-directory-program))
-       (tramp-handle-insert-directory
-        filename switches wildcard full-directory-p)
-      (when (stringp switches)
-        (setq switches (split-string switches)))
-      (setq switches
-           (append switches (split-string (tramp-sh--quoting-style-options 
v))))
-      (unless (tramp-get-ls-command-with v "--dired")
-       (setq switches (delete "-N" (delete "--dired" switches))))
+  (if (and (featurep 'ls-lisp)
+          (not ls-lisp-use-insert-directory-program))
+      (tramp-handle-insert-directory
+       filename switches wildcard full-directory-p)
+    (unless switches (setq switches ""))
+    ;; Check, whether directory is accessible.
+    (unless wildcard
+      (access-file filename "Reading directory"))
+    (with-parsed-tramp-file-name (expand-file-name filename) nil
+      (let ((dired (tramp-get-ls-command-with v "--dired")))
+       (when (stringp switches)
+          (setq switches (split-string switches)))
+       (setq switches
+             (append switches (split-string (tramp-sh--quoting-style-options 
v))
+                     (when dired `(,dired))))
+       (unless dired
+         (setq switches (delete "-N" (delete "--dired" switches)))))
       (when wildcard
         (setq wildcard (tramp-run-real-handler
                        #'file-name-nondirectory (list localname)))
@@ -2662,7 +2661,8 @@ The method used must be an out-of-band method."
                         #'file-name-directory (list localname))))
       (unless (or full-directory-p (member "-d" switches))
         (setq switches (append switches '("-d"))))
-      (setq switches (mapconcat #'tramp-shell-quote-argument switches " "))
+      (setq switches (delete-dups switches)
+           switches (mapconcat #'tramp-shell-quote-argument switches " "))
       (when wildcard
        (setq switches (concat switches " " wildcard)))
       (tramp-message
@@ -2831,7 +2831,8 @@ the result will be a local, non-Tramp, file name."
     (with-parsed-tramp-file-name name nil
       ;; If connection is not established yet, run the real handler.
       (if (not (tramp-connectable-p v))
-         (tramp-run-real-handler #'expand-file-name (list name))
+         (tramp-drop-volume-letter
+          (tramp-run-real-handler #'expand-file-name (list name)))
        (unless (tramp-run-real-handler #'file-name-absolute-p (list localname))
          (setq localname (concat "~/" localname)))
        ;; Tilde expansion if necessary.  This needs a shell which
@@ -4171,6 +4172,18 @@ This function expects to be in the right *tramp* buffer."
 ;; On hydra.nixos.org, the $PATH environment variable is too long to
 ;; send it.  This is likely not due to PATH_MAX, but PIPE_BUF.  We
 ;; check it, and use a temporary file in case of.  See Bug#33781.
+
+;; The PIPE_BUF in POSIX [1] can be as low as 512 [2]. Here are the values
+;; on various platforms:
+;;   - 512 on macOS, FreeBSD, NetBSD, OpenBSD, MirBSD, native Windows.
+;;   - 4 KiB on Linux, OSF/1, Cygwin, Haiku.
+;;   - 5 KiB on Solaris.
+;;   - 8 KiB on HP-UX, Plan9.
+;;   - 10 KiB on IRIX.
+;;   - 32 KiB on AIX, Minix.
+;; [1] https://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html
+;; [2] https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html
+;; See Bug#65324.
 (defun tramp-set-remote-path (vec)
   "Set the remote environment PATH to existing directories.
 I.e., for each directory in `tramp-remote-path', it is tested
@@ -4424,7 +4437,7 @@ seconds.  If not, it produces an error message with the 
given ERROR-ARGS."
   "A function to be called with one argument, VEC.
 It should return a string which is used to check, whether the
 configuration of the remote host has been changed (which would
-require to flush the cache data).  This string is kept as
+require flushing the cache data).  This string is kept as
 connection property \"config-check-data\".
 This variable is intended as connection-local variable.")
 
@@ -4579,7 +4592,7 @@ process to set up.  VEC specifies the connection."
       (tramp-send-command vec "set +H" t))
 
     ;; Disable tab expansion.
-    (if (string-match-p (rx (| "BSD" "DragonFly" "Darwin")) uname)
+    (if (string-match-p tramp-bsd-unames uname)
        (tramp-send-command vec "stty tabs" t)
       (tramp-send-command vec "stty tab0" t))
 
@@ -5687,7 +5700,10 @@ Nonexistent directories are removed from spec."
     (tramp-message vec 5 "Finding a suitable `ls' command")
     (or
      (catch 'ls-found
-       (dolist (cmd '("ls" "gnuls" "gls"))
+       (dolist (cmd
+               ;; Prefer GNU ls on *BSD and macOS.
+                (if (tramp-check-remote-uname vec tramp-bsd-unames)
+                   '( "gls" "ls" "gnuls") '("ls" "gnuls" "gls")))
         (let ((dl (tramp-get-remote-path vec))
               result)
           (while (and dl (setq result (tramp-find-executable vec cmd dl t t)))
diff --git a/lisp/net/tramp-sshfs.el b/lisp/net/tramp-sshfs.el
index 131f632a0fe..102ba637f55 100644
--- a/lisp/net/tramp-sshfs.el
+++ b/lisp/net/tramp-sshfs.el
@@ -60,7 +60,7 @@
                ;; These are for remote processes.
                 (tramp-login-program        "ssh")
                 (tramp-login-args           (("-q") ("-l" "%u") ("-p" "%p")
-                                            ("-e" "none") ("-t" "-t")
+                                            ("-e" "none") ("%a" "%a")
                                             ("%h") ("%l")))
                 (tramp-direct-async         t)
                 (tramp-remote-shell         ,tramp-default-remote-shell)
@@ -326,7 +326,7 @@ arguments to pass to the OPERATION."
            ?h (or (tramp-file-name-host v) "")
            ?u (or (tramp-file-name-user v) "")
            ?p (or (tramp-file-name-port v) "")
-           ?l command))
+            ?a "-t" ?l command))
 
        ;; Synchronize stderr.
        (when tmpstderr
diff --git a/lisp/net/tramp-sudoedit.el b/lisp/net/tramp-sudoedit.el
index 3d6e1d92d0b..c22bfd7ff5c 100644
--- a/lisp/net/tramp-sudoedit.el
+++ b/lisp/net/tramp-sudoedit.el
@@ -23,7 +23,7 @@
 
 ;;; Commentary:
 
-;; The "sudoedit" Tramp method allows to edit a file as a different
+;; The "sudoedit" Tramp method enables editing a file as a different
 ;; user on the local host.  Contrary to the "sudo" method, all magic
 ;; file name functions are implemented by single "sudo ..."  commands.
 ;; The purpose is to make editing such a file as secure as possible;
diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el
index b34d3ff6695..bff9a010c3b 100644
--- a/lisp/net/tramp.el
+++ b/lisp/net/tramp.el
@@ -8,6 +8,9 @@
 ;; Keywords: comm, processes
 ;; Package: tramp
 
+;; This is a GNU ELPA :core package.  Avoid functionality that is not
+;; compatible with the version of Emacs recorded in trampver.el.
+
 ;; This file is part of GNU Emacs.
 
 ;; GNU Emacs is free software: you can redistribute it and/or modify
@@ -64,7 +67,11 @@
 (declare-function file-notify-rm-watch "filenotify")
 (declare-function netrc-parse "netrc")
 (defvar auto-save-file-name-transforms)
+(defvar ls-lisp-dirs-first)
+(defvar ls-lisp-emulation)
+(defvar ls-lisp-ignore-case)
 (defvar ls-lisp-use-insert-directory-program)
+(defvar ls-lisp-verbosity)
 (defvar tramp-prefix-format)
 (defvar tramp-prefix-regexp)
 (defvar tramp-method-regexp)
@@ -240,9 +247,9 @@ pair of the form (KEY VALUE).  The following KEYs are 
defined:
     \"%\" followed by a letter are expanded in the arguments as
     follows:
 
-    - \"%h\" is replaced by the host name
-    - \"%u\" is replaced by the user name
-    - \"%p\" is replaced by the port number
+    - \"%h\" is replaced by the host name.
+    - \"%u\" is replaced by the user name.
+    - \"%p\" is replaced by the port number.
     - \"%%\" can be used to obtain a literal percent character.
 
     If a sub-list containing \"%h\", \"%u\" or \"%p\" is
@@ -271,6 +278,8 @@ pair of the form (KEY VALUE).  The following KEYs are 
defined:
     - \"%z\" is replaced by the `tramp-scp-direct-remote-copying'
       argument if it is supported.
     - \"%d\" is replaced by the device detected by `tramp-adb-get-device'.
+    - \"%a\" adds the pseudo-terminal allocation argument \"-t\" in
+       asynchronous processes, if the connection type is not `pipe'.
 
     The existence of `tramp-login-args', combined with the
     absence of `tramp-copy-args', is an indication that the
@@ -659,6 +668,17 @@ The `sudo' program appears to insert a `^@' character into 
the prompt."
   :version "29.1"
   :type 'regexp)
 
+(defcustom tramp-otp-password-prompt-regexp
+  (rx-to-string
+   `(: bol (* nonl)
+       ;; JumpCloud.
+       (group (| "Verification code"))
+       (* nonl) (any . ,tramp-compat-password-colon-equivalents) (* blank)))
+  "Regexp matching one-time password prompts.
+The regexp should match at end of buffer."
+  :version "29.2"
+  :type 'regexp)
+
 (defcustom tramp-wrong-passwd-regexp
   (rx bol (* nonl)
       (| "Permission denied"
@@ -1331,6 +1351,7 @@ let-bind this variable."
 ;; GNU/Linux (Debian, Suse, RHEL, Cygwin, MINGW64): /bin:/usr/bin
 ;; FreeBSD, DragonFly: /usr/bin:/bin:/usr/sbin:/sbin: - beware trailing ":"!
 ;; FreeBSD 12.1, Darwin: /usr/bin:/bin:/usr/sbin:/sbin
+;; NetBSD 9.3: 
/usr/bin:/bin:/usr/sbin:/sbin:/usr/pkg/bin:/usr/pkg/sbin:/usr/local/bin:/usr/local/sbin
 ;; IRIX64: /usr/bin
 ;; QNAP QTS: ---
 ;; Hydra: /run/current-system/sw/bin:/bin:/usr/bin
@@ -2923,7 +2944,7 @@ remote host and localname (filename on remote host)."
 
 ;; This function returns all possible method completions, adding the
 ;; trailing method delimiter.
-(defun tramp-get-completion-methods (partial-method multi-hop)
+(defun tramp-get-completion-methods (partial-method &optional multi-hop)
   "Return all method completions for PARTIAL-METHOD.
 If MULTI-HOP is non-nil, return only multi-hop capable methods."
   (mapcar
@@ -3009,10 +3030,11 @@ This function is added always in 
`tramp-get-completion-function'
 for all methods.  Resulting data are derived from default settings."
   `((,(tramp-find-user method nil nil) ,(tramp-find-host method nil nil))))
 
-(defcustom tramp-completion-remote-containers nil
-  "Whether container hosts in multi-hop paths should be queried for 
completions."
+;;;###tramp-autoload
+(defcustom tramp-completion-multi-hop-methods nil
+  "Methods for which to provide completions over multi-hop connections."
   :version "30.1"
-  :type 'boolean)
+  :type '(repeat (string :tag "Method name")))
 
 (defcustom tramp-completion-use-auth-sources auth-source-do-cache
   "Whether to use `auth-source-search' for completion of user and host names.
@@ -3509,7 +3531,7 @@ BODY is the backend specific code."
                        (let ((inhibit-file-name-handlers
                               `(tramp-file-name-handler
                                 tramp-crypt-file-name-handler
-                                . inhibit-file-name-handlers))
+                                . ,inhibit-file-name-handlers))
                              (inhibit-file-name-operation 'write-region))
                          (find-file-name-handler ,visit 'write-region))))
          ;; We use this to save the value of
@@ -4141,7 +4163,7 @@ Let-bind it when necessary.")
          (tramp-error v 'file-error "Unsafe backup file name"))))))
 
 (defun tramp-handle-insert-directory
-  (filename switches &optional wildcard full-directory-p)
+    (filename switches &optional wildcard full-directory-p)
   "Like `insert-directory' for Tramp files."
   (require 'ls-lisp)
   (unless switches (setq switches ""))
@@ -4154,8 +4176,14 @@ Let-bind it when necessary.")
     (access-file filename "Reading directory"))
   (with-parsed-tramp-file-name (expand-file-name filename) nil
     (with-tramp-progress-reporter v 0 (format "Opening directory %s" filename)
-      (let (ls-lisp-use-insert-directory-program start)
-       ;; Silence byte compiler.
+      ;; We bind `ls-lisp-emulation' to nil (which is GNU).
+      ;; `ls-lisp-set-options' modifies `ls-lisp-ignore-case',
+      ;; `ls-lisp-dirs-first' and `ls-lisp-verbosity', so we bind them
+      ;; as well.  We don't want to use `insert-directory-program'.
+      (let (ls-lisp-emulation ls-lisp-ignore-case ls-lisp-dirs-first
+           ls-lisp-verbosity ls-lisp-use-insert-directory-program start)
+       ;; Set proper options based on `ls-lisp-emulation'.
+       (tramp-compat-funcall 'ls-lisp-set-options)
        (tramp-run-real-handler
         #'insert-directory
         (list filename switches wildcard full-directory-p))
@@ -4857,6 +4885,7 @@ a connection-local variable."
                  (when adb-file-name-handler-p
                    (tramp-compat-funcall
                     'tramp-adb-get-device v)))
+                 (pta (unless (eq connection-type 'pipe) "-t"))
                 login-args p)
 
            ;; Replace `login-args' place holders.  Split
@@ -4872,7 +4901,7 @@ a connection-local variable."
                 v 'tramp-login-args
                 ?h (or host "") ?u (or user "") ?p (or port "")
                 ?c (format-spec (or options "") (format-spec-make ?t tmpfile))
-                ?d (or device "") ?l ""))))
+                ?d (or device "") ?a (or pta "") ?l ""))))
             p (make-process
                :name name :buffer buffer
                :command (append `(,login-program) login-args command)
@@ -5365,6 +5394,25 @@ of."
       (narrow-to-region (point-max) (point-max))))
   t)
 
+(defun tramp-action-otp-password (proc vec)
+  "Query the user for a one-time password."
+  (with-current-buffer (process-buffer proc)
+    (let ((case-fold-search t)
+         prompt)
+      (goto-char (point-min))
+      (tramp-check-for-regexp proc tramp-process-action-regexp)
+      (setq prompt (concat (match-string 1) " "))
+      (tramp-message vec 3 "Sending %s" (match-string 1))
+      ;; We don't call `tramp-send-string' in order to hide the
+      ;; password from the debug buffer and the traces.
+      (process-send-string
+       proc
+       (concat
+       (tramp-read-passwd-without-cache proc prompt) tramp-local-end-of-line))
+      ;; Hide password prompt.
+      (narrow-to-region (point-max) (point-max))))
+  t)
+
 (defun tramp-action-succeed (_proc _vec)
   "Signal success in finding shell prompt."
   (throw 'tramp-action 'ok))
@@ -6530,8 +6578,8 @@ Consults the auth-source package."
 
 (defun tramp-read-passwd-without-cache (proc &optional prompt)
   "Read a password from user (compat function)."
-  ;; We suspend the timers while reading the password.
   (declare (tramp-suppress-trace t))
+  ;; We suspend the timers while reading the password.
   (let (tramp-dont-suspend-timers)
     (with-tramp-suspended-timers
       (password-read
diff --git a/lisp/obsolete/landmark.el b/lisp/obsolete/landmark.el
index 57ca28e6d1d..968b817e9e8 100644
--- a/lisp/obsolete/landmark.el
+++ b/lisp/obsolete/landmark.el
@@ -549,10 +549,10 @@ along the DX, DY direction, considering that DVAL has 
been added on SQUARE."
 ;;; GAME CONTROL.
 ;;;
 
-;; Several variables are used to monitor a game, including a GAME-HISTORY (the
-;; list of all (SQUARE . PREVSCORE) played) that allows to take moves back
-;; (anti-updating the score table) and to compute the table from scratch in
-;; case of an interruption.
+;; Several variables are used to monitor a game, including a
+;; GAME-HISTORY (the list of all (SQUARE . PREVSCORE) played) that
+;; enables rescinding moves (anti-updating the score table) and to
+;; compute the table from scratch in case of an interruption.
 
 (defvar landmark-game-in-progress nil
   "Non-nil if a game is in progress.")
diff --git a/lisp/obsolete/url-ns.el b/lisp/obsolete/url-ns.el
index 20318867634..53db684887c 100644
--- a/lisp/obsolete/url-ns.el
+++ b/lisp/obsolete/url-ns.el
@@ -39,13 +39,14 @@
 
 ;;;###autoload
 (defun dnsResolve (host)
-  (url-gateway-nslookup-host host))
+  (with-suppressed-warnings ((obsolete url-gateway-nslookup-host))
+    (url-gateway-nslookup-host host)))
 
 ;;;###autoload
 (defun isResolvable (host)
   (if (string-match "^[0-9.]+$" host)
       t
-    (not (string= host (url-gateway-nslookup-host host)))))
+    (not (string= host (dnsResolve host)))))
 
 ;;;###autoload
 (defun isInNet (ip net mask)
diff --git a/lisp/org/ChangeLog.1 b/lisp/org/ChangeLog.1
index a4eae350d98..82b1c832c40 100644
--- a/lisp/org/ChangeLog.1
+++ b/lisp/org/ChangeLog.1
@@ -25932,7 +25932,7 @@
 
        * org-latex.el (org-export-as-latex): Do nit require the buffer to
        be visiting a file when only exporting to a buffer or string.
-       (org-export-latex-fix-inputenc): Only save the buffer is there is
+       (org-export-latex-fix-inputenc): Only save the buffer if there is
        a file name attached to it.
 
 2010-04-10  Dan Davison  <davison@stats.ox.ac.uk>
diff --git a/lisp/org/ob-eshell.el b/lisp/org/ob-eshell.el
index 95f5777ae7d..b3fbe3ad52d 100644
--- a/lisp/org/ob-eshell.el
+++ b/lisp/org/ob-eshell.el
@@ -47,11 +47,12 @@
   "Execute a block of Eshell code BODY with PARAMS.
 This function is called by `org-babel-execute-src-block'.
 
-The BODY can be any code which allowed executed in Eshell.
-Eshell allow to execute normal shell command and Elisp code.
-More details please reference Eshell Info.
+The BODY argument is code which can be executed in Eshell.
+Eshell allows executing normal shell command and Elisp code.
+For more details, see Info node `(eshell) Top'.
 
-The PARAMS are variables assignments."
+The PARAMS argument is passed to
+`org-babel-expand-body:generic' (which see)."
   (let* ((session (org-babel-eshell-initiate-session
                   (cdr (assq :session params))))
         (full-body (org-babel-expand-body:generic
diff --git a/lisp/org/ob-python.el b/lisp/org/ob-python.el
index c19af0ab331..6c05d1c8b2a 100644
--- a/lisp/org/ob-python.el
+++ b/lisp/org/ob-python.el
@@ -235,7 +235,7 @@ then create.  Return the initialized session."
           ;; multiple prompts during initialization.
           (with-current-buffer py-buffer
             (while (not org-babel-python--initialized)
-              (org-babel-comint-wait-for-output py-buffer)))
+              (sleep-for 0 10)))
         (org-babel-comint-wait-for-output py-buffer))
       (setq org-babel-python-buffers
            (cons (cons session py-buffer)
diff --git a/lisp/org/oc-basic.el b/lisp/org/oc-basic.el
index 1c8c37aa941..5c9aad8f6a5 100644
--- a/lisp/org/oc-basic.el
+++ b/lisp/org/oc-basic.el
@@ -162,7 +162,7 @@ Return a hash table with citation references as keys and 
fields alist as values.
         (puthash (cdr (assq 'id item))
                  (mapcar (pcase-lambda (`(,field . ,value))
                            (pcase field
-                             ((or 'author 'editors)
+                             ((or 'author 'editor)
                               ;; Author and editors are arrays of
                               ;; objects, each of them designing a
                               ;; person.  These objects may contain
diff --git a/lisp/org/ol.el b/lisp/org/ol.el
index 9ad191c8f78..cbc02a985c1 100644
--- a/lisp/org/ol.el
+++ b/lisp/org/ol.el
@@ -1803,7 +1803,7 @@ generate a description as described in 
`org-link-parameters'
 docstring.  Otherwise, if `org-link-make-description-function' is
 non-nil, this function will be called with the link target, and
 the result will be the default link description.  When called
-non-interactively, don't allow to edit the default description."
+non-interactively, don't allow editing the default description."
   (interactive "P")
   (let* ((wcf (current-window-configuration))
         (origbuf (current-buffer))
@@ -2042,7 +2042,7 @@ Also refresh fontification if needed."
   (interactive)
   (let ((old-regexp org-target-link-regexp)
        ;; Some languages, e.g., Chinese, do not use spaces to
-       ;; separate words.  Also allow to surround radio targets with
+        ;; separate words.  Also allow surrounding radio targets with
        ;; line-breakable characters.
        (before-re "\\(?:^\\|[^[:alnum:]]\\|\\c|\\)\\(")
        (after-re "\\)\\(?:$\\|[^[:alnum:]]\\|\\c|\\)")
diff --git a/lisp/org/org-element.el b/lisp/org/org-element.el
index 296468eed1a..0dd149762c4 100644
--- a/lisp/org/org-element.el
+++ b/lisp/org/org-element.el
@@ -2968,7 +2968,7 @@ CONTENTS is verse block contents."
 ;; object types they can contain will be specified in
 ;; `org-element-object-restrictions'.
 ;;
-;; Creating a new type of object requires to alter
+;; Creating a new type of object requires altering
 ;; `org-element--object-regexp' and `org-element--object-lex', add the
 ;; new type in `org-element-all-objects', and possibly add
 ;; restrictions in `org-element-object-restrictions'.
@@ -3523,7 +3523,7 @@ Assume point is at the beginning of the link."
        ;;
        ;; Also treat any newline character and associated
        ;; indentation as a single space character.  This is not
-       ;; compatible with RFC 3986, which requires to ignore
+        ;; compatible with RFC 3986, which requires ignoring
        ;; them altogether.  However, doing so would require
        ;; users to encode spaces on the fly when writing links
        ;; (e.g., insert [[shell:ls%20*.org]] instead of
@@ -6705,20 +6705,8 @@ The function returns the new value of 
`org-element--cache-change-warning'."
        (setq org-element--cache-change-tic (buffer-chars-modified-tick))
        (setq org-element--cache-last-buffer-size (buffer-size))
        (goto-char beg)
-       (beginning-of-line)
-       (let ((bottom (save-excursion
-                       (goto-char end)
-                       (if (and (bolp)
-                                ;; When beg == end, still extent to eol.
-                                (> (point) beg))
-                           ;; FIXME: Potential pitfall.
-                           ;; We are appending to an element end.
-                           ;; Unless the last inserted char is not
-                           ;; newline, the next element is not broken
-                           ;; and does not need to be purged from the
-                           ;; cache.
-                           end
-                         (line-end-position)))))
+       (forward-line 0)
+       (let ((bottom (save-excursion (goto-char end) (line-end-position))))
          (prog1
              ;; Use the worst change warning to not miss important edits.
              ;; This function is called before edit and after edit by
@@ -7859,7 +7847,7 @@ element ending there."
     (setq cached-only nil))
   (let (element)
     (when (org-element--cache-active-p)
-      (if (not org-element--cache) (org-element-cache-reset)
+      (if (not (org-with-base-buffer nil org-element--cache)) 
(org-element-cache-reset)
         (unless cached-only (org-element--cache-sync (current-buffer) pom))))
     (setq element (if cached-only
                       (when (and (org-element--cache-active-p)
diff --git a/lisp/org/org-table.el b/lisp/org/org-table.el
index 221497f53b7..aa819aa7d2f 100644
--- a/lisp/org/org-table.el
+++ b/lisp/org/org-table.el
@@ -417,7 +417,7 @@ It is probably good to never set this variable to nil, for 
the sake of
 portability of tables."
   :group 'org-table-calculation
   :type '(choice
-         (const :tag "Allow to cross" t)
+          (const :tag "Allow crossing hline" t)
          (const :tag "Stick to hline" nil)
          (const :tag "Error on attempt to cross" error)))
 
@@ -3900,7 +3900,7 @@ When non-nil, return the overlay narrowing the field."
     ;; Aligning table from the first row will not shrink again the
     ;; second row, which was not visible initially.
     ;;
-    ;; However, fixing it requires to check every row, which may be
+    ;; However, fixing it requires checking every row, which may be
     ;; slow on large tables.  Moreover, the hindrance of this
     ;; pathological case is very limited.
     (beginning-of-line)
diff --git a/lisp/org/org-version.el b/lisp/org/org-version.el
index 57e406b24fc..a859fe6d412 100644
--- a/lisp/org/org-version.el
+++ b/lisp/org/org-version.el
@@ -5,13 +5,13 @@
 (defun org-release ()
   "The release version of Org.
 Inserted by installing Org mode or when a release is made."
-   (let ((org-release "9.6.7"))
+   (let ((org-release "9.6.9"))
      org-release))
 ;;;###autoload
 (defun org-git-version ()
   "The Git version of Org mode.
 Inserted by installing Org or when a release is made."
-   (let ((org-git-version "release_9.6.7-13-g99cc96"))
+   (let ((org-git-version "release_9.6.9"))
      org-git-version))
 
 (provide 'org-version)
diff --git a/lisp/org/org.el b/lisp/org/org.el
index f56aa4f6f69..9ca7f155614 100644
--- a/lisp/org/org.el
+++ b/lisp/org/org.el
@@ -9,7 +9,7 @@
 ;; URL: https://orgmode.org
 ;; Package-Requires: ((emacs "26.1"))
 
-;; Version: 9.6.7
+;; Version: 9.6.9
 
 ;; This file is part of GNU Emacs.
 ;;
@@ -1021,7 +1021,7 @@ time."
 This is useful since some lines containing links can be very long and
 uninteresting.  Also tables look terrible when wrapped.
 
-The variable `org-startup-truncated' allows to configure
+The variable `org-startup-truncated' enables you to configure
 truncation for Org mode different to the other modes that use the
 variable `truncate-lines' and as a shortcut instead of putting
 the variable `truncate-lines' into the `org-mode-hook'.  If one
@@ -1321,14 +1321,15 @@ Possible values for the file identifier are:
                      to open [[file:document.pdf::5]] with evince at page 5.
 
                  Likely, you will need more entries: without page
-                 number; with search pattern; with cross-reference
-                 anchor; some combination of options.  Consider simple
-                 pattern here and a Lisp function to determine command
-                 line arguments instead.  Passing argument list to
-                 `call-process' or `make-process' directly allows to
-                 avoid treating some character in peculiar file names
-                 as shell specialls causing executing part of file
-                 name as a subcommand.
+                 number; with search pattern; with
+                 cross-reference anchor; some combination of
+                 options.  Consider simple pattern here and a
+                 Lisp function to determine command line
+                 arguments instead.  Passing an argument list to
+                 `call-process' or `make-process' directly avoids
+                 treating some character in peculiar file names
+                 as shell specials that prompt parts of said file
+                 names to be executed as subcommands.
 
  `directory'   Matches a directory
  `remote'      Matches a remote file, accessible through tramp.
@@ -6322,7 +6323,10 @@ unconditionally."
        (if (not level) (outline-next-heading) ;before first headline
         (org-back-to-heading invisible-ok)
         (when (equal arg '(16)) (org-up-heading-safe))
-        (org-end-of-subtree)))
+        (org-end-of-subtree invisible-ok 'to-heading)))
+      ;; At `point-max', if the file does not have ending newline,
+      ;; create one, so that we are not appending stars at non-empty
+      ;; line.
       (unless (bolp) (insert "\n"))
       (when (and blank? (save-excursion
                           (backward-char)
@@ -6334,7 +6338,9 @@ unconditionally."
         (backward-char))
       (unless (and blank? (org-previous-line-empty-p))
        (org-N-empty-lines-before-current (if blank? 1 0)))
-      (insert stars " ")
+      (insert stars " " "\n")
+      ;; Move point after stars.
+      (backward-char)
       ;; When INVISIBLE-OK is non-nil, ensure newly created headline
       ;; is visible.
       (unless invisible-ok
@@ -13222,7 +13228,7 @@ Optional argument DEFAULT provides a default value for 
PROPERTY."
      nil nil nil nil default-prop)))
 
 (defun org-set-property-and-value (use-last)
-  "Allow to set [PROPERTY]: [value] direction from prompt.
+  "Allow setting [PROPERTY]: [value] direction from prompt.
 When use-default, don't even ask, just use the last
 \"[PROPERTY]: [value]\" string from the history."
   (interactive "P")
@@ -14029,6 +14035,7 @@ user."
       (unless deltadef
        (let ((now (decode-time)))
          (setq day (nth 3 now) month (nth 4 now) year (nth 5 now))))
+      ;; FIXME: Duplicated value in ‘cond’: ""
       (cond ((member deltaw '("h" ""))
              (when (boundp 'org-time-was-given)
                (setq org-time-was-given t))
@@ -14753,12 +14760,12 @@ is considered `day' (i.e. only `bracket', `day', and 
`after' return
 values are possible).
 
 When matching, the match groups are the following:
-  group 1: year, if any
-  group 2: month, if any
-  group 3: day number, if any
-  group 4: day name, if any
-  group 5: hours, if any
-  group 6: minutes, if any"
+  group 2: year, if any
+  group 3: month, if any
+  group 4: day number, if any
+  group 5: day name, if any
+  group 7: hours, if any
+  group 8: minutes, if any"
   (let* ((regexp
           (if extended
               (if (eq extended 'agenda)
@@ -17649,8 +17656,8 @@ region."
 (defun org-open-line (n)
   "Insert a new row in tables, call `open-line' elsewhere.
 If `org-special-ctrl-o' is nil, just call `open-line' everywhere.
-As a special case, when a document starts with a table, allow to
-call `open-line' on the very first character."
+As a special case, when a document starts with a table, allow
+calling `open-line' on the very first character."
   (interactive "*p")
   (if (and org-special-ctrl-o (/= (point) 1) (org-at-table-p))
       (org-table-insert-row)
@@ -17662,6 +17669,8 @@ If INDENT is non-nil, call `newline-and-indent' with 
ARG to
 indent unconditionally; otherwise, call `newline' with ARG and
 INTERACTIVE, which can trigger indentation if
 `electric-indent-mode' is enabled."
+  (when interactive
+    (org-fold-check-before-invisible-edit 'insert))
   (if indent
       (org-newline-and-indent arg)
     (newline arg interactive)))
@@ -18864,9 +18873,7 @@ ELEMENT."
             (goto-char start)
             (current-indentation)))
          ;; In any other case, indent like the current line.
-         (t (current-indentation)))))
-      ;; Finally, no indentation is needed, fall back to 0.
-      (t (current-indentation))))))
+         (t (current-indentation)))))))))
 
 (defun org--align-node-property ()
   "Align node property at point.
diff --git a/lisp/org/ox-html.el b/lisp/org/ox-html.el
index b27254b8ac5..cfad311207a 100644
--- a/lisp/org/ox-html.el
+++ b/lisp/org/ox-html.el
@@ -3094,6 +3094,7 @@ CONTENTS is nil.  INFO is a plist holding contextual 
information."
   (let ((latex-frag (org-element-property :value latex-fragment))
        (processing-type (plist-get info :with-latex)))
     (cond
+     ;; FIXME: Duplicated value in ‘cond’: t
      ((memq processing-type '(t mathjax))
       (org-html-format-latex latex-frag 'mathjax info))
      ((memq processing-type '(t html))
diff --git a/lisp/outline.el b/lisp/outline.el
index 97a51c9b92a..d8dd491212e 100644
--- a/lisp/outline.el
+++ b/lisp/outline.el
@@ -314,12 +314,15 @@ These buttons can be used to hide and show the body under 
the heading.
 When the value is `insert', additional placeholders for buttons are
 inserted to the buffer, so buttons are not only clickable,
 but also typing `RET' on them can hide and show the body.
+Using the value `insert' is not recommended in editable
+buffers because it modifies them.
 When the value is `in-margins', then clickable buttons are
 displayed in the margins before the headings.
 When the value is `t', clickable buttons are displayed
 in the buffer before the headings.  The values `t' and
 `in-margins' can be used in editing buffers because they
 don't modify the buffer."
+  ;; The value `insert' is not intended to be customizable.
   :type '(choice (const :tag "Do not use outline buttons" nil)
                  (const :tag "Show outline buttons in margins" in-margins)
                  (const :tag "Show outline buttons in buffer" t))
diff --git a/lisp/pcomplete.el b/lisp/pcomplete.el
index 151611f94b7..0457f1b00c0 100644
--- a/lisp/pcomplete.el
+++ b/lisp/pcomplete.el
@@ -470,6 +470,8 @@ Same as `pcomplete' but using the standard completion UI."
            ;; rely less on c-t-subvert.
            (beg (max (- (point) (length pcomplete-stub))
                      argbeg))
+           (end (point))
+           tmp
            buftext)
       ;; Try and improve our guess of `beg' in case the difference
       ;; between pcomplete-stub and the buffer's text is simply due to
@@ -477,11 +479,19 @@ Same as `pcomplete' but using the standard completion UI."
       ;; indispensable but reduces the reliance on c-t-subvert and
       ;; improves corner case behaviors.
       (while (progn (setq buftext (pcomplete-unquote-argument
-                                   (buffer-substring beg (point))))
+                                   (buffer-substring beg end)))
                     (and (> beg argbeg)
                          (> (length pcomplete-stub) (length buftext))))
         (setq beg (max argbeg (- beg (- (length pcomplete-stub)
                                         (length buftext))))))
+      ;; Try and improve our guess of `end' in case it's not point.
+      (while (and (< (length buftext) (length pcomplete-stub))
+                  (< end (point-max))
+                  (string-prefix-p (setq tmp (pcomplete-unquote-argument
+                                              (buffer-substring beg (1+ end))))
+                                   pcomplete-stub))
+        (setq end (1+ end))
+        (setq buftext tmp))
       (when completions
         (let ((table
                (completion-table-with-quoting
@@ -515,7 +525,7 @@ Same as `pcomplete' but using the standard completion UI."
                            seen)))))))
           (when completion-ignore-case
             (setq table (completion-table-case-fold table)))
-          (list beg (point) table
+          (list beg end table
                 :annotation-function
                 (lambda (cand)
                   (when (stringp cand)
diff --git a/lisp/pixel-scroll.el b/lisp/pixel-scroll.el
index 488f6781254..224a2b5f1e0 100644
--- a/lisp/pixel-scroll.el
+++ b/lisp/pixel-scroll.el
@@ -148,67 +148,62 @@ is always with pixel resolution.")
   "<next>"                             #'pixel-scroll-interpolate-down
   "<prior>"                            #'pixel-scroll-interpolate-up)
 
+(defgroup pixel-scroll-precision nil
+  "Precise pixel scrolling."
+  :group 'mouse
+  :version "30.1")
+
 (defcustom pixel-scroll-precision-use-momentum nil
   "If non-nil, continue to scroll the display after wheel movement stops.
 This is only effective if supported by your mouse or touchpad."
-  :group 'mouse
   :type 'boolean
   :version "29.1")
 
 (defcustom pixel-scroll-precision-momentum-tick 0.01
   "Number of seconds between each momentum scroll."
-  :group 'mouse
   :type 'float
   :version "29.1")
 
 (defcustom pixel-scroll-precision-momentum-seconds 1.75
   "The maximum duration in seconds of momentum scrolling."
-  :group 'mouse
   :type 'float
   :version "29.1")
 
 (defcustom pixel-scroll-precision-momentum-min-velocity 10.0
   "The minimum scrolled pixels per second before momentum scrolling starts."
-  :group 'mouse
   :type 'float
   :version "29.1")
 
 (defcustom pixel-scroll-precision-initial-velocity-factor (/ 0.0335 4)
   "Factor applied to the initial velocity before momentum scrolling begins."
-  :group 'mouse
   :type 'float
   :version "29.1")
 
 (defcustom pixel-scroll-precision-large-scroll-height nil
   "Pixels that must be scrolled before an animation is performed.
 Nil means to not interpolate such scrolls."
-  :group 'mouse
   :type '(choice (const :tag "Do not interpolate large scrolls" nil)
                  number)
   :version "29.1")
 
 (defcustom pixel-scroll-precision-interpolation-total-time 0.1
   "The total time in seconds to spend interpolating a large scroll."
-  :group 'mouse
   :type 'float
   :version "29.1")
 
 (defcustom pixel-scroll-precision-interpolation-factor 2.0
   "A factor to apply to the distance of an interpolated scroll."
-  :group 'mouse
   :type 'float
   :version "29.1")
 
 (defcustom pixel-scroll-precision-interpolation-between-scroll 0.001
   "The number of seconds between each step of an interpolated scroll."
-  :group 'mouse
   :type 'float
   :version "29.1")
 
 (defcustom pixel-scroll-precision-interpolate-page nil
   "Whether or not to interpolate scrolling via the Page Down and Page Up keys.
 This is only effective when `pixel-scroll-precision-mode' is enabled."
-  :group 'scrolling
   :type 'boolean
   :version "29.1")
 
@@ -216,7 +211,6 @@ This is only effective when `pixel-scroll-precision-mode' 
is enabled."
   "Whether or not to interpolate scrolling from a mouse.
 If non-nil, scrolling from the mouse wheel of an actual mouse (as
 opposed to a touchpad) will cause Emacs to interpolate the scroll."
-  :group 'scrolling
   :type 'boolean
   :version "29.1")
 
@@ -851,7 +845,7 @@ It is a vector of the form [ VELOCITY TIME SIGN ]."
 ;;;###autoload
 (define-minor-mode pixel-scroll-precision-mode
   "Toggle pixel scrolling.
-When enabled, this minor mode allows to scroll the display
+When enabled, this minor mode allows you to scroll the display
 precisely, according to the turning of the mouse wheel."
   :global t
   :group 'mouse
diff --git a/lisp/play/zone.el b/lisp/play/zone.el
index 235c6f4e7b5..b8a83baa44b 100644
--- a/lisp/play/zone.el
+++ b/lisp/play/zone.el
@@ -3,7 +3,7 @@
 ;; Copyright (C) 2000-2023 Free Software Foundation, Inc.
 
 ;; Author: Victor Zandy <zandy@cs.wisc.edu>
-;; Maintainer: Thien-Thi Nguyen <ttn@gnu.org>
+;; Maintainer: emacs-devel@gnu.org
 ;; Keywords: games
 ;; Created: June 6, 1998
 
diff --git a/lisp/plstore.el b/lisp/plstore.el
index d18d461d7d1..758f9fc7292 100644
--- a/lisp/plstore.el
+++ b/lisp/plstore.el
@@ -24,14 +24,31 @@
 
 ;; Plist based data store providing search and partial encryption.
 ;;
-;; By default, this package uses symmetric encryption, which means
+;; By default, this library uses symmetric encryption, which means
 ;; that you have to enter the password protecting your store more
 ;; often than you probably expect to.  To use public key encryption
-;; with this package, create a GnuPG key and customize user option
+;; with this library, create a GnuPG key and customize user option
 ;; `plstore-encrypt-to' to use it.  You can then configure the GnuPG
 ;; agent to adjust caching and expiration of the passphrase for your
 ;; store.
 ;;
+;; You can read more on these topics in the EasyPG Assistant user's
+;; manual (Info node `(epa)'), of which much information also applies
+;; to this library.  Most notably:
+;;
+;; - Info node `(epa)GnuPG version compatibility'
+;; - Info node `(epa)GnuPG Pinentry'
+;; - Info node `(epa)Caching Passphrases'
+;;
+;; Use only keyword symbols (starting with a colon) as property names
+;; in any plist stored with this library.  While this library does not
+;; actively enforce the use of keyword symbols, it silently assumes
+;; that the first character of all property names can be discarded
+;; without sacrificing uniqueness of names (FIXME).  Likewise, this
+;; library does not enforce that the plists provided as input are
+;; actually valid and can behave in undefined ways if they are not
+;; (FIXME).
+;;
 ;; Creating:
 ;;
 ;; ;; Open a new store associated with ~/.emacs.d/auth.plist.
@@ -52,8 +69,8 @@
 ;; (plstore-close store)
 ;;
 ;; Avoid marking one property both as public *and* secret, as the
-;; behavior of this package with respect to such duplicate properties
-;; is not (yet) defined.
+;; behavior of this library with respect to such duplicate properties
+;; is not defined (FIXME).
 ;;
 ;; Searching:
 ;;
@@ -71,7 +88,7 @@
 ;; ;; While the entry "baz" associated with "baz.example.org" has also
 ;; ;; a secret property `:password', it is encrypted together with
 ;; ;; `:user' of "bar", so no need to decrypt the secret.
-;; (plstore-find store '(:host ("bar.example.org")))
+;; (plstore-find store '(:host ("baz.example.org")))
 ;;
 ;; (plstore-close store)
 ;;
@@ -87,8 +104,8 @@
 ;; `:secret-' prefix) is marked as secret.  Thus, when you save the
 ;; buffer, the `:secret-user' property is encrypted as `:user'.  Do
 ;; not use a property consisting solely of the prefix, as the behavior
-;; of this package with respect to such properties is not (yet)
-;; defined.
+;; of this library with respect to such properties is not defined
+;; (FIXME).
 ;;
 ;; You can toggle the view between encrypted form and the decrypted
 ;; form with C-c C-c.
@@ -101,7 +118,7 @@
 ;; Internals:
 ;;
 ;; This is information on the internal data structure and functions of
-;; this package.  None of it should be necessary to actually use it.
+;; this library.  None of it should be necessary to actually use it.
 ;; For easier reading, we usually do not distinguish in this internal
 ;; documentation between a Lisp object and its printed representation.
 ;;
@@ -553,18 +570,23 @@ SECRET-KEYS is a plist containing secret data."
 
 (defvar pp-escape-newlines)
 (defun plstore--insert-buffer (plstore)
-  "Insert the file representation of PLSTORE at point.
-Assumes that PLSTORE has been decrypted."
+  "Insert the file representation of PLSTORE at point."
   (insert ";;; public entries -*- mode: plstore -*- \n"
          (pp-to-string (plstore--get-alist plstore)))
-  (if (plstore--get-secret-alist plstore)
+  (let ((pp-escape-newlines nil)
+        (cipher nil))
+    (cond
+     ;; Reuse the encrypted data as cipher text if this store has not
+     ;; been decrypted yet.
+     ((plstore--get-encrypted-data plstore)
+      (setq cipher (plstore--get-encrypted-data plstore)))
+     ;; Encrypt the secret alist to generate the cipher text.
+     ((plstore--get-secret-alist plstore)
       (let ((context (epg-make-context 'OpenPGP))
-           (pp-escape-newlines nil)
            (recipients
             (cond
              ((listp plstore-encrypt-to) plstore-encrypt-to)
-             ((stringp plstore-encrypt-to) (list plstore-encrypt-to))))
-           cipher)
+             ((stringp plstore-encrypt-to) (list plstore-encrypt-to)))))
        (setf (epg-context-armor context) t)
        (epg-context-set-passphrase-callback
         context
@@ -584,12 +606,17 @@ Assumes that PLSTORE has been decrypted."
 If no one is selected, symmetric encryption will be performed.  "
                           recipients)
                        (if plstore-encrypt-to
-                           (epg-list-keys context recipients)))))
-       (goto-char (point-max))
-       (insert ";;; secret entries\n" (pp-to-string cipher)))))
+                           (epg-list-keys context recipients))))))))
+    (when cipher
+      (goto-char (point-max))
+      (insert ";;; secret entries\n" (pp-to-string cipher)))))
 
 (defun plstore-save (plstore)
-  "Save PLSTORE to its associated file."
+  "Save PLSTORE to its associated file.
+Save with symmetric encryption or public key encryption depending
+on `plstore-encrypt-to'.  If `plstore-encrypt-to' is non-nil but
+none of the recipients from `plstore-encrypt-to' matches any
+GnuPG key, silently save with symmetric encryption." ; (FIXME)
   (with-current-buffer (plstore--get-buffer plstore)
     (erase-buffer)
     (plstore--insert-buffer plstore)
diff --git a/lisp/progmodes/c-ts-common.el b/lisp/progmodes/c-ts-common.el
index e0a7c46508e..3b0814970ad 100644
--- a/lisp/progmodes/c-ts-common.el
+++ b/lisp/progmodes/c-ts-common.el
@@ -3,6 +3,7 @@
 ;; Copyright (C) 2023 Free Software Foundation, Inc.
 
 ;; Maintainer : 付禹安 (Yuan Fu) <casouri@gmail.com>
+;; Package    : emacs
 ;; Keywords   : c c++ java javascript rust languages tree-sitter
 
 ;; This file is part of GNU Emacs.
diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el
index b3c48eb2c65..70717a90caa 100644
--- a/lisp/progmodes/c-ts-mode.el
+++ b/lisp/progmodes/c-ts-mode.el
@@ -71,6 +71,8 @@
 (eval-when-compile (require 'rx))
 
 (declare-function treesit-parser-create "treesit.c")
+(declare-function treesit-parser-root-node "treesit.c")
+(declare-function treesit-parser-set-included-ranges "treesit.c")
 (declare-function treesit-node-parent "treesit.c")
 (declare-function treesit-node-start "treesit.c")
 (declare-function treesit-node-end "treesit.c")
@@ -80,7 +82,6 @@
 (declare-function treesit-node-prev-sibling "treesit.c")
 (declare-function treesit-node-first-child-for-pos "treesit.c")
 (declare-function treesit-node-next-sibling "treesit.c")
-(declare-function treesit-parser-set-included-ranges "treesit.c")
 (declare-function treesit-query-compile "treesit.c")
 
 ;;; Custom variables
@@ -478,6 +479,7 @@ MODE is either `c' or `cpp'."
        ((parent-is "labeled_statement") parent-bol c-ts-mode-indent-offset)
        ((parent-is "compound_statement") parent-bol c-ts-mode-indent-offset)
        ((parent-is "if_statement") parent-bol 0)
+       ((parent-is "else_clause") parent-bol 0)
        ((parent-is "for_statement") parent-bol 0)
        ((parent-is "while_statement") parent-bol 0)
        ((parent-is "switch_statement") parent-bol 0)
@@ -495,6 +497,13 @@ NODE should be a labeled_statement.  PARENT is its parent."
 
 ;;; Font-lock
 
+(defvar c-ts-mode--feature-list
+  '(( comment definition)
+    ( keyword preprocessor string type)
+    ( assignment constant escape-sequence label literal)
+    ( bracket delimiter error function operator property variable))
+  "`treesit-font-lock-feature-list' for `c-ts-mode'.")
+
 (defvar c-ts-mode--preproc-keywords
   '("#define" "#if" "#ifdef" "#ifndef"
     "#else" "#elif" "#endif" "#include")
@@ -1013,17 +1022,46 @@ if `c-ts-mode-emacs-sources-support' is non-nil."
   (or (treesit-add-log-current-defun)
       (c-ts-mode--defun-name (c-ts-mode--emacs-defun-at-point))))
 
+;;; Things
+
+(defvar c-ts-mode--thing-settings
+  `(;; It's more useful to include semicolons as sexp so
+    ;; that users can move to the end of a statement.
+    (sexp (not ,(rx (or "{" "}" "[" "]" "(" ")" ","))))
+    ;; compound_statement makes us jump over too big units
+    ;; of code, so skip that one, and include the other
+    ;; statements.
+    (sentence
+     ,(regexp-opt '("preproc"
+                    "declaration"
+                    "specifier"
+                    "attributed_statement"
+                    "labeled_statement"
+                    "expression_statement"
+                    "if_statement"
+                    "switch_statement"
+                    "do_statement"
+                    "while_statement"
+                    "for_statement"
+                    "return_statement"
+                    "break_statement"
+                    "continue_statement"
+                    "goto_statement"
+                    "case_statement")))
+    (text ,(regexp-opt '("comment"
+                         "raw_string_literal"))))
+  "`treesit-thing-settings' for both C and C++.")
+
 ;;; Support for FOR_EACH_* macros
 ;;
 ;; FOR_EACH_TAIL, FOR_EACH_TAIL_SAFE, FOR_EACH_FRAME etc., followed by
 ;; an unbracketed body will mess up the parser, which parses the thing
 ;; as a function declaration.  We "fix" it by adding a shadow parser
-;; for a language 'emacs-c' (which is just 'c' but under a different
-;; name).  We use 'emacs-c' to find each FOR_EACH_* macro with a
-;; unbracketed body, and set the ranges of the C parser so that it
-;; skips those FOR_EACH_*'s.  Note that we only ignore FOR_EACH_*'s
-;; with a unbracketed body.  Those with a bracketed body parse more
-;; or less fine.
+;; with the tag `for-each'.  We use this parser to find each
+;; FOR_EACH_* macro with a unbracketed body, and set the ranges of the
+;; default C parser so that it skips those FOR_EACH_*'s.  Note that we
+;; only ignore FOR_EACH_*'s with a unbracketed body.  Those with a
+;; bracketed body parse more or less fine.
 ;;
 ;; In the meantime, we have a special fontification rule for
 ;; FOR_EACH_* macros with a bracketed body that removes any applied
@@ -1044,12 +1082,12 @@ For BOL see `treesit-simple-indent-rules'."
 (defvar c-ts-mode--emacs-c-range-query
   (when (treesit-available-p)
     (treesit-query-compile
-     'emacs-c `(((declaration
-                  type: (macro_type_specifier
-                         name: (identifier) @_name)
-                  @for-each-tail)
-                 (:match ,c-ts-mode--for-each-tail-regexp
-                         @_name)))))
+     'c `(((declaration
+            type: (macro_type_specifier
+                   name: (identifier) @_name)
+            @for-each-tail)
+           (:match ,c-ts-mode--for-each-tail-regexp
+                   @_name)))))
   "Query that finds a FOR_EACH_* macro with an unbracketed body.")
 
 (defvar-local c-ts-mode--for-each-tail-ranges nil
@@ -1079,9 +1117,11 @@ parser parse the whole buffer."
   "Set ranges for the C parser to skip some FOR_EACH_* macros.
 BEG and END are described in `treesit-range-rules'."
   (let* ((c-parser (treesit-parser-create 'c))
+         (for-each-parser (treesit-parser-create 'c nil nil 'for-each))
          (old-ranges c-ts-mode--for-each-tail-ranges)
          (new-ranges (treesit-query-range
-                      'emacs-c c-ts-mode--emacs-c-range-query beg end))
+                      (treesit-parser-root-node for-each-parser)
+                      c-ts-mode--emacs-c-range-query beg end))
          (set-ranges (treesit--clip-ranges
                       (treesit--merge-ranges
                        old-ranges new-ranges beg end)
@@ -1127,34 +1167,12 @@ BEG and END are described in `treesit-range-rules'."
   (setq-local treesit-defun-skipper #'c-ts-mode--defun-skipper)
   (setq-local treesit-defun-name-function #'c-ts-mode--defun-name)
 
-  (setq-local treesit-sentence-type-regexp
-              ;; compound_statement makes us jump over too big units
-              ;; of code, so skip that one, and include the other
-              ;; statements.
-              (regexp-opt '("preproc"
-                            "declaration"
-                            "specifier"
-                            "attributed_statement"
-                            "labeled_statement"
-                            "expression_statement"
-                            "if_statement"
-                            "switch_statement"
-                            "do_statement"
-                            "while_statement"
-                            "for_statement"
-                            "return_statement"
-                            "break_statement"
-                            "continue_statement"
-                            "goto_statement"
-                            "case_statement")))
-
   ;; IMO it makes more sense to define what's NOT sexp, since sexp by
   ;; spirit, especially when used for movement, is like "expression"
   ;; or "syntax unit". --yuan
-  (setq-local treesit-sexp-type-regexp
-              ;; It more useful to include semicolons as sexp so that
-              ;; users can move to the end of a statement.
-              (rx (not (or "{" "}" "[" "]" "(" ")" ","))))
+  (setq-local treesit-thing-settings
+              `((c ,@c-ts-mode--thing-settings)
+                (cpp ,@c-ts-mode--thing-settings)))
 
   ;; Nodes like struct/enum/union_specifier can appear in
   ;; function_definitions, so we need to find the top-level node.
@@ -1204,10 +1222,7 @@ BEG and END are described in `treesit-range-rules'."
                    c-ts-mode--defun-for-class-in-imenu-p nil))))
 
   (setq-local treesit-font-lock-feature-list
-              '(( comment definition)
-                ( keyword preprocessor string type)
-                ( assignment constant escape-sequence label literal)
-                ( bracket delimiter error function operator property 
variable))))
+              c-ts-mode--feature-list))
 
 (defvar treesit-load-name-override-list)
 
@@ -1231,16 +1246,10 @@ in your configuration."
   :after-hook (c-ts-mode-set-modeline)
 
   (when (treesit-ready-p 'c)
-    ;; Add a fake "emacs-c" language which is just C.  Used for
-    ;; skipping FOR_EACH_* macros, see `c-ts-mode--emacs-set-ranges'.
-    (setf (alist-get 'emacs-c treesit-load-name-override-list)
-          '("libtree-sitter-c" "tree_sitter_c"))
-    ;; If Emacs source support is enabled, make sure emacs-c parser is
-    ;; after c parser in the parser list. This way various tree-sitter
-    ;; functions will automatically use the c parser rather than the
-    ;; emacs-c parser.
+    ;; Create an "for-each" parser, see `c-ts-mode--emacs-set-ranges'
+    ;; for more.
     (when c-ts-mode-emacs-sources-support
-      (treesit-parser-create 'emacs-c))
+      (treesit-parser-create 'c nil nil 'for-each))
 
     (treesit-parser-create 'c)
     ;; Comments.
@@ -1291,9 +1300,6 @@ recommended to enable `electric-pair-mode' with this 
mode."
   :after-hook (c-ts-mode-set-modeline)
 
   (when (treesit-ready-p 'cpp)
-    (setq-local treesit-text-type-regexp
-                (regexp-opt '("comment"
-                              "raw_string_literal")))
 
     (treesit-parser-create 'cpp)
 
@@ -1412,5 +1418,6 @@ the code is C or C++ and based on that chooses whether to 
enable
     (add-to-list 'auto-mode-alist '("\\.h\\'" . c-or-c++-ts-mode)))
 
 (provide 'c-ts-mode)
+(provide 'c++-ts-mode)
 
 ;;; c-ts-mode.el ends here
diff --git a/lisp/progmodes/cc-awk.el b/lisp/progmodes/cc-awk.el
index c367341345d..22f63bb5be7 100644
--- a/lisp/progmodes/cc-awk.el
+++ b/lisp/progmodes/cc-awk.el
@@ -754,14 +754,14 @@
   (if (eq (char-after beg) ?_) (setq beg (1+ beg)))
 
   ;; First put the properties on the delimiters.
-  (cond ((eq end (point-max))           ; string/regexp terminated by EOB
-         (c-put-char-property beg 'syntax-table '(15))) ; (15) = "string fence"
-        ((/= (char-after beg) (char-after end)) ; missing end delimiter
-         (c-put-char-property beg 'syntax-table '(15))
-         (c-put-char-property end 'syntax-table '(15)))
-        ((eq (char-after beg) ?/)       ; Properly bracketed regexp
-         (c-put-char-property beg 'syntax-table '(7)) ; (7) = "string"
-         (c-put-char-property end 'syntax-table '(7)))
+  (cond ((eq end (point-max))          ; string/regexp terminated by EOB
+        (c-put-string-fence beg))
+       ((/= (char-after beg) (char-after end)) ; missing end delimiter
+        (c-put-string-fence beg)
+        (c-put-string-fence end))
+       ((eq (char-after beg) ?/)       ; Properly bracketed regexp
+        (c-put-char-property beg 'syntax-table '(7)) ; (7) = "string"
+        (c-put-char-property end 'syntax-table '(7)))
         (t))                       ; Properly bracketed string: Nothing to do.
   ;; Now change the properties of any escaped "s in the string to punctuation.
   (save-excursion
diff --git a/lisp/progmodes/cc-defs.el b/lisp/progmodes/cc-defs.el
index 2cbe9ca7e92..8662e0cade6 100644
--- a/lisp/progmodes/cc-defs.el
+++ b/lisp/progmodes/cc-defs.el
@@ -733,9 +733,10 @@ various buffer change hooks."
 
 (defmacro c-forward-syntactic-ws (&optional limit)
   "Forward skip over syntactic whitespace.
-Syntactic whitespace is defined as whitespace characters, comments,
-and preprocessor directives.  However if point starts inside a comment
-or preprocessor directive, the content of it is not treated as
+Syntactic whitespace is defined as whitespace characters with
+whitespace (or comment-end) syntax, comments, and preprocessor
+directives.  However if point starts inside a comment or
+preprocessor directive, the content of it is not treated as
 whitespace.
 
 LIMIT sets an upper limit of the forward movement, if specified.  If
@@ -755,9 +756,10 @@ comment at the start of cc-engine.el for more info."
 
 (defmacro c-backward-syntactic-ws (&optional limit)
   "Backward skip over syntactic whitespace.
-Syntactic whitespace is defined as whitespace characters, comments,
-and preprocessor directives.  However if point starts inside a comment
-or preprocessor directive, the content of it is not treated as
+Syntactic whitespace is defined as whitespace characters with
+whitespace (or comment-end) syntax, comments, and preprocessor
+directives.  However if point starts inside a comment or
+preprocessor directive, the content of it is not treated as
 whitespace.
 
 LIMIT sets a lower limit of the backward movement, if specified.  If
@@ -1102,6 +1104,38 @@ continuations."
                   (eq (char-before) ?\\)))
        (backward-char))))
 
+(defmacro c-skip-ws-chars-forward (string &optional lim)
+  ;; Move point forward, stopping before a char which isn't in STRING, or a
+  ;; char whose syntax isn't whitespace or comment-end, or at pos LIM.
+  ;; Note that \n usually has comment-end syntax.
+  ;;
+  ;; Returns the distance traveled, either zero or positive.
+  (declare (debug t))
+  `(let ((-lim- ,lim)
+        (here (point))
+        count)
+     (setq count (skip-chars-forward ,string -lim-))
+     (when (> count 0)
+       (goto-char here)
+       (setq count (skip-syntax-forward " >" (+ here count))))
+     count))
+
+(defmacro c-skip-ws-chars-backward (string &optional lim)
+  ;; Move point backward, stopping after a char which isn't in STRING, or a
+  ;; char whose syntax isn't whitespace or comment-end, or at pos LIM.  Note
+  ;; that \n usually has comment-end syntax.
+  ;;
+  ;; Returns the distance traveled, either zero or negative.
+  (declare (debug t))
+  `(let ((-lim- ,lim)
+        (here (point))
+        count)
+     (setq count (skip-chars-backward ,string -lim-))
+     (when (< count 0)
+       (goto-char here)
+       (setq count (skip-syntax-backward " >" (+ here count))))
+     count))
+
 (eval-and-compile
   (defvar c-langs-are-parametric nil))
 
@@ -1208,6 +1242,17 @@ MODE is either a mode symbol or a list of mode symbols."
           `((setq c-syntax-table-hwm (min c-syntax-table-hwm -pos-))))
        (put-text-property -pos- (1+ -pos-) ',property ,value))))
 
+(defmacro c-put-string-fence (pos)
+  ;; Put the string-fence syntax-table text property at POS.
+  ;; Since the character there cannot then count as syntactic whitespace,
+  ;; clear the properties `c-is-sws' and `c-in-sws' (see functions
+  ;; `c-forward-sws' and `c-backward-sws' in cc-engine.el for details).
+  (declare (debug t))
+  `(let ((-pos- ,pos))
+     (c-put-char-property -pos- 'syntax-table '(15))
+     (c-clear-char-property -pos- 'c-is-sws)
+     (c-clear-char-property -pos- 'c-in-sws)))
+
 (eval-and-compile
   ;; Constant to decide at compilation time whether to use category
   ;; properties.  Currently (2010-03) they're available only on GNU
@@ -1298,27 +1343,19 @@ MODE is either a mode symbol or a list of mode symbols."
       (most-positive-fixnum))))
 
 (defmacro c-put-char-properties (from to property value)
-  ;; FIXME!!!  Doc comment here!
+  ;; Put the given PROPERTY with the given VALUE on the characters between
+  ;; FROM and TO.  PROPERTY is assumed to be constant.  The return value is
+  ;; undefined.
+  ;;
+  ;; This macro does hidden buffer changes.
   (declare (debug t))
   (setq property (eval property))
-  `(let ((-to- ,to) (-from- ,from))
-     ,(if c-use-extents
-         ;; XEmacs
-         `(progn
-            (map-extents (lambda (ext ignored)
-                           (delete-extent ext))
-                         nil -from- -to- nil nil ',property)
-            (set-extent-properties (make-extent -from- -to-)
-                                   (cons property
-                                         (cons ,value
-                                               '(start-open t
-                                                            end-open t)))))
-       ;; Emacs
-       `(progn
+  `(let ((-from- ,from))
+        (progn
           ,@(when (and (fboundp 'syntax-ppss)
                        (eq `,property 'syntax-table))
               `((setq c-syntax-table-hwm (min c-syntax-table-hwm -from-))))
-          (put-text-property -from- -to- ',property ,value)))))
+          (put-text-property -from- ,to ',property ,value))))
 
 (defmacro c-clear-char-properties (from to property)
   ;; Remove all the occurrences of the given property in the given
diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el
index abcc20fcb82..e687f44d657 100644
--- a/lisp/progmodes/cc-engine.el
+++ b/lisp/progmodes/cc-engine.el
@@ -976,10 +976,10 @@ comment at the start of cc-engine.el for more info."
                  (point-min)))
       (widen)
 
-      (if (save-excursion
-           (and (c-beginning-of-macro)
-                (/= (point) start)))
-         (setq macro-start (point)))
+      (save-excursion
+       (if (and (c-beginning-of-macro)
+                (/= (point) start))
+           (setq macro-start (point))))
 
       ;; Try to skip back over unary operator characters, to register
       ;; that we've moved.
@@ -2130,7 +2130,7 @@ comment at the start of cc-engine.el for more info."
     ;; Skip simple ws and do a quick check on the following character to see
     ;; if it's anything that can't start syntactic ws, so we can bail out
     ;; early in the majority of cases when there just are a few ws chars.
-    (skip-chars-forward " \t\n\r\f\v")
+    (c-skip-ws-chars-forward " \t\n\r\f\v")
     (when (or (looking-at c-syntactic-ws-start)
              (and c-opt-cpp-prefix
                   (looking-at c-noise-macro-name-re))
@@ -2180,7 +2180,7 @@ comment at the start of cc-engine.el for more info."
                   rung-pos (point) (point-max))
 
                  (setq rung-pos (point))
-                 (and (> (skip-chars-forward " \t\n\r\f\v") 0)
+                 (and (> (c-skip-ws-chars-forward " \t\n\r\f\v") 0)
                       (not (eobp))))
 
              ;; We'll loop here if there is simple ws after the last rung.
@@ -2246,7 +2246,7 @@ comment at the start of cc-engine.el for more info."
                (and c-opt-cpp-prefix
                     (looking-at c-opt-cpp-start)
                     (setq macro-start (point))
-                    (progn (skip-chars-backward " \t")
+                    (progn (c-skip-ws-chars-backward " \t")
                            (bolp))
                     (or (bobp)
                         (progn (backward-char)
@@ -2286,7 +2286,7 @@ comment at the start of cc-engine.el for more info."
        ;; We've searched over a piece of non-white syntactic ws.  See if this
        ;; can be cached.
        (setq next-rung-pos (point))
-       (skip-chars-forward " \t\n\r\f\v")
+       (c-skip-ws-chars-forward " \t\n\r\f\v")
        (setq rung-end-pos (min (1+ (point)) (point-max)))
 
        (if (or
@@ -2383,7 +2383,7 @@ comment at the start of cc-engine.el for more info."
     ;; bail out early in the majority of cases when there just are a few ws
     ;; chars.  Newlines are complicated in the backward direction, so we can't
     ;; skip over them.
-    (skip-chars-backward " \t\f")
+    (c-skip-ws-chars-backward " \t\f")
     (when (and (not (bobp))
               (save-excursion
                 (or (and
@@ -2411,7 +2411,7 @@ comment at the start of cc-engine.el for more info."
       (setq simple-ws-beg (or attr-end       ; After attribute.
                              (match-end 1) ; Noise macro, etc.
                              (match-end 0))) ; c-syntactic-ws-end
-      (skip-chars-backward " \t\n\r\f\v")
+      (c-skip-ws-chars-backward " \t\n\r\f\v")
       (if (setq rung-is-marked (text-property-any
                                (point) (min (1+ rung-pos) (point-max))
                                'c-is-sws t))
@@ -2448,10 +2448,10 @@ comment at the start of cc-engine.el for more info."
                   (point) rung-pos (point-min))
 
                  (setq rung-pos (point))
-                 (if (and (< (min (skip-chars-backward " \t\f\v")
+                 (if (and (< (min (c-skip-ws-chars-backward " \t\f\v")
                                   (progn
                                     (setq simple-ws-beg (point))
-                                    (skip-chars-backward " \t\n\r\f\v")))
+                                    (c-skip-ws-chars-backward " \t\n\r\f\v")))
                              0)
                           (setq rung-is-marked
                                 (text-property-any (point) rung-pos
@@ -2531,7 +2531,7 @@ comment at the start of cc-engine.el for more info."
                  ;; the macro, and then `simple-ws-beg' must be kept on the
                  ;; same side of those comments.
                  (goto-char simple-ws-beg)
-                 (skip-chars-backward " \t\n\r\f\v")
+                 (c-skip-ws-chars-backward " \t\n\r\f\v")
                  (if (eq (char-before) ?\\)
                      (forward-char))
                  (forward-line 1)
@@ -2544,7 +2544,7 @@ comment at the start of cc-engine.el for more info."
                  t)))
 
             ((/= (save-excursion
-                   (skip-chars-forward " \t\n\r\f\v" simple-ws-beg)
+                   (c-skip-ws-chars-forward " \t\n\r\f\v" simple-ws-beg)
                    (setq next-rung-pos (point)))
                  simple-ws-beg)
              ;; Skipped over comments.  Must put point at the end of
@@ -2581,7 +2581,7 @@ comment at the start of cc-engine.el for more info."
        ;; We've searched over a piece of non-white syntactic ws.  See if this
        ;; can be cached.
        (setq next-rung-pos (point))
-       (skip-chars-backward " \t\f\v")
+       (c-skip-ws-chars-backward " \t\f\v")
 
        (if (or
             ;; Cache if we started either from a marked rung or from a
@@ -2591,7 +2591,7 @@ comment at the start of cc-engine.el for more info."
 
             ;; Cache if there's a marked rung in the encountered simple ws.
             (save-excursion
-              (skip-chars-backward " \t\n\r\f\v")
+              (c-skip-ws-chars-backward " \t\n\r\f\v")
               (text-property-any (point) (min (1+ next-rung-pos) (point-max))
                                  'c-is-sws t)))
 
@@ -7202,10 +7202,8 @@ comment at the start of cc-engine.el for more info."
                        (progn
                          (c-clear-char-property (1- beg-literal-end)
                                                 'syntax-table)
-                         (c-put-char-property (1- end-literal-end)
-                                              'syntax-table '(15)))
-                     (c-put-char-property (1- beg-literal-end)
-                                          'syntax-table '(15))
+                         (c-put-string-fence (1- end-literal-end)))
+                     (c-put-string-fence (1- beg-literal-end))
                      (c-clear-char-property (1- end-literal-end)
                                             'syntax-table)))
 
@@ -7284,10 +7282,8 @@ comment at the start of cc-engine.el for more info."
                  (progn
                    (c-clear-char-property (1- beg-literal-end)
                                           'syntax-table)
-                   (c-put-char-property (1- end-literal-end)
-                                        'syntax-table '(15)))
-               (c-put-char-property (1- beg-literal-end)
-                                    'syntax-table '(15))
+                   (c-put-string-fence (1- end-literal-end)))
+               (c-put-string-fence (1- beg-literal-end))
                (c-clear-char-property (1- end-literal-end)
                                       'syntax-table)))))
          ;; Extend the fontification region, if needed.
@@ -7946,7 +7942,7 @@ multi-line strings (but not C++, for example)."
                (insert (nth 3 (car state))))
               ((eq (nth 3 (car state)) t)
                (insert ?\")
-               (c-put-char-property end 'syntax-table '(15))))
+               (c-put-string-fence end)))
              (c-truncate-lit-pos-cache end)
              ;; ....ensure c-new-END extends right to the end of the about
              ;; to be un-stringed raw string....
@@ -8191,7 +8187,7 @@ multi-line strings (but not C++, for example)."
        (goto-char (cadr end-delim))
        t)
     (c-put-char-property (cddr delim) 'syntax-table '(1))
-    (c-put-char-property (1- (cadr delim)) 'syntax-table '(15))
+    (c-put-string-fence (1- (cadr delim)))
     (c-truncate-lit-pos-cache (1- (cddr delim)))
     (when bound
       ;; In a CPP construct, we try to apply a generic-string
@@ -8221,10 +8217,10 @@ multi-line strings (but not C++, for example)."
             (cadr delim) t))
          (if (match-beginning 10)
              (progn
-               (c-put-char-property (match-beginning 10) 'syntax-table '(15))
+               (c-put-string-fence (match-beginning 10))
                (c-truncate-lit-pos-cache (match-beginning 10)))
            (c-put-char-property (match-beginning 5) 'syntax-table '(1))
-           (c-put-char-property (1+ (match-beginning 5)) 'syntax-table '(15))
+           (c-put-string-fence (1+ (match-beginning 5)))
            (c-truncate-lit-pos-cache (match-beginning 5))))
       (goto-char bound))
     nil))
diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el
index 1dbe91b5633..8dea599ed98 100644
--- a/lisp/progmodes/cc-mode.el
+++ b/lisp/progmodes/cc-mode.el
@@ -1279,7 +1279,9 @@ Note that the style variables are always made local to 
the buffer."
   ;; VALUE (which should not be nil).
   ;; `(let ((-pos- ,pos)
   ;;    (-value- ,value))
-  (c-put-char-property pos 'syntax-table value)
+  (if (equal value '(15))
+      (c-put-string-fence pos)
+    (c-put-char-property pos 'syntax-table value))
   (c-put-char-property pos 'c-fl-syn-tab value)
   (cond
    ((null c-min-syn-tab-mkr)
diff --git a/lisp/progmodes/cperl-mode.el b/lisp/progmodes/cperl-mode.el
index 98943ebda3f..1736b45c72d 100644
--- a/lisp/progmodes/cperl-mode.el
+++ b/lisp/progmodes/cperl-mode.el
@@ -79,6 +79,9 @@
 (eval-when-compile (require 'cl-lib))
 (require 'facemenu)
 
+(defvar Man-switches)
+(defvar manual-program)
+(defvar imenu-max-items)
 (defvar msb-menu-cond)
 (defvar gud-perldb-history)
 (defvar vc-rcs-header)
@@ -333,17 +336,7 @@ Affects: `cperl-font-lock', `cperl-electric-lbrace-space',
 (defcustom cperl-vc-rcs-header '("($rcs) = (' $Id\ $ ' =~ 
/(\\d+(\\.\\d+)+)/);")
   "Special version of `vc-rcs-header' that is used in CPerl mode buffers."
   :type '(repeat string)
-     :group 'cperl)
-
-;; (defcustom cperl-clobber-mode-lists
-;;   (not
-;;    (and
-;;     (boundp 'interpreter-mode-alist)
-;;     (assoc "miniperl" interpreter-mode-alist)
-;;     (assoc "\\.\\([pP][Llm]\\|al\\)$" auto-mode-alist)))
-;;   "Whether to install us into `interpreter-' and `extension' mode lists."
-;;   :type 'boolean
-;;   :group 'cperl)
+  :group 'cperl)
 
 (defcustom cperl-info-on-command-no-prompt nil
   "Not-nil (and non-null) means not to prompt on \\[cperl-info-on-command].
@@ -746,7 +739,8 @@ voice);
        s) Allows indentation of //x-style regular expressions;
        t) Highlights different symbols in regular expressions according
           to their function; much less problems with backslashitis;
-       u) Allows to find regular expressions which contain interpolated parts.
+       u) Allows you to locate regular expressions which contain
+          interpolated parts.
 
 5) The indentation engine was very smart, but most of tricks may be
 not needed anymore with the support for `syntax-table' property.  Has
@@ -911,17 +905,6 @@ Unless KEEP, removes the old indentation."
       (delete-horizontal-space))
   (indent-to column minimum))
 
-;; Probably it is too late to set these guys already, but it can help later:
-
-;;(and cperl-clobber-mode-lists
-;;(setq auto-mode-alist
-;;      (append '(("\\.\\([pP][Llm]\\|al\\)$" . perl-mode))  auto-mode-alist ))
-;;(and (boundp 'interpreter-mode-alist)
-;;     (setq interpreter-mode-alist (append interpreter-mode-alist
-;;                                       '(("miniperl" . perl-mode))))))
-(eval-when-compile
-  (mapc #'require '(imenu easymenu etags timer man info)))
-
 (define-abbrev-table 'cperl-mode-electric-keywords-abbrev-table
   (mapcar (lambda (x)
             (let ((name (car x))
diff --git a/lisp/progmodes/csharp-mode.el b/lisp/progmodes/csharp-mode.el
index 37bb84ab5ba..c550c9670c8 100644
--- a/lisp/progmodes/csharp-mode.el
+++ b/lisp/progmodes/csharp-mode.el
@@ -493,7 +493,7 @@ compilation and evaluation time conflicts."
        ;; Next non-whitespace character should be '{'
        (goto-char (c-point 'boi))
        (unless (eq (char-after) ?{)
-         (backward-up-list 1 t t))
+         (ignore-errors (backward-up-list 1 t t)))
        (save-excursion
          ;; 'new' should be part of the line
          (goto-char (c-point 'iopl))
@@ -959,10 +959,12 @@ Key bindings:
   ;; Comments.
   (c-ts-common-comment-setup)
 
-  (setq-local treesit-text-type-regexp
-              (regexp-opt '("comment"
-                            "verbatim_string-literal"
-                            "interpolated_verbatim_string-text")))
+  (setq-local treesit-thing-settings
+              `((c-sharp
+                 (text
+                  ,(regexp-opt '("comment"
+                                 "verbatim_string-literal"
+                                 "interpolated_verbatim_string-text"))))))
 
   ;; Indent.
   (setq-local treesit-simple-indent-rules csharp-ts-mode--indent-rules)
diff --git a/lisp/progmodes/dockerfile-ts-mode.el 
b/lisp/progmodes/dockerfile-ts-mode.el
index 333158e20f6..0305bea5182 100644
--- a/lisp/progmodes/dockerfile-ts-mode.el
+++ b/lisp/progmodes/dockerfile-ts-mode.el
@@ -175,8 +175,9 @@ the subtrees."
                 dockerfile-ts-mode--indent-rules)
 
     ;; Navigation
-    (setq-local treesit-sentence-type-regexp
-                "instruction")
+    (setq-local treesit-thing-settings
+                `((dockerfile
+                   (sentence "instruction"))))
 
     ;; Font-lock.
     (setq-local treesit-font-lock-settings
diff --git a/lisp/progmodes/ebnf2ps.el b/lisp/progmodes/ebnf2ps.el
index 3e83d288408..819f39cb4e5 100644
--- a/lisp/progmodes/ebnf2ps.el
+++ b/lisp/progmodes/ebnf2ps.el
@@ -5247,7 +5247,7 @@ killed after process termination."
        (or ebnf-fonts-required
           (setq ebnf-fonts-required
                 (mapconcat #'identity
-                           (ps-remove-duplicates
+                           (delete-dups
                             (mapcar #'ebnf-font-name-select
                                     (list ebnf-production-font
                                           ebnf-terminal-font
diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index 65daa0941d5..e511df01850 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -108,6 +108,8 @@
 (require 'filenotify)
 (require 'ert)
 (require 'text-property-search nil t)
+(require 'diff-mode)
+(require 'diff)
 
 ;; These dependencies are also GNU ELPA core packages.  Because of
 ;; bug#62576, since there is a risk that M-x package-install, despite
@@ -130,6 +132,35 @@
 (defvar tramp-use-ssh-controlmaster-options)
 
 
+;;; Obsolete aliases
+;;;
+(make-obsolete-variable 'eglot--managed-mode-hook
+                        'eglot-managed-mode-hook "1.6")
+(define-obsolete-variable-alias 'eglot-confirm-server-initiated-edits
+  'eglot-confirm-server-edits "1.16")
+(define-obsolete-function-alias 'eglot--uri-to-path 'eglot-uri-to-path "1.16")
+(define-obsolete-function-alias 'eglot--path-to-uri 'eglot-path-to-uri "1.16")
+(define-obsolete-function-alias 'eglot--range-region 'eglot-range-region 
"1.16")
+(define-obsolete-function-alias 'eglot--server-capable 'eglot-server-capable 
"1.16")
+(define-obsolete-function-alias 'eglot--server-capable-or-lose 
'eglot-server-capable-or-lose "1.16")
+(define-obsolete-function-alias
+  'eglot-lsp-abiding-column 'eglot-utf-16-linepos "1.12")
+(define-obsolete-function-alias
+  'eglot-current-column 'eglot-utf-32-linepos "1.12")
+(define-obsolete-variable-alias
+  'eglot-current-column-function 'eglot-current-linepos-function "1.12")
+(define-obsolete-function-alias
+  'eglot-move-to-current-column 'eglot-move-to-utf-32-linepos "1.12")
+(define-obsolete-function-alias
+  'eglot-move-to-lsp-abiding-column 'eglot-move-to-utf-16-linepos "1.12")
+(define-obsolete-variable-alias
+'eglot-move-to-column-function 'eglot-move-to-linepos-function "1.12")
+(define-obsolete-variable-alias 'eglot-ignored-server-capabilites
+  'eglot-ignored-server-capabilities "1.8")
+;;;###autoload
+(define-obsolete-function-alias 'eglot-update 'eglot-upgrade-eglot "29.1")
+
+
 ;;; User tweakable stuff
 (defgroup eglot nil
   "Interaction with Language Server Protocol servers."
@@ -197,7 +228,7 @@ chosen (interactively or automatically)."
                                  . ,(eglot-alternatives
                                      '(("phpactor" "language-server")
                                        ("php" 
"vendor/felixfbecker/language-server/bin/php-language-server.php"))))
-                                ((c-mode c-ts-mode c++-mode c++-ts-mode)
+                                ((c-mode c-ts-mode c++-mode c++-ts-mode 
objc-mode)
                                  . ,(eglot-alternatives
                                      '("clangd" "ccls")))
                                 (((caml-mode :language-id "ocaml")
@@ -237,8 +268,8 @@ chosen (interactively or automatically)."
                                 (gdscript-mode . ("localhost" 6008))
                                 ((fortran-mode f90-mode) . ("fortls"))
                                 (futhark-mode . ("futhark" "lsp"))
-                                (lua-mode . ,(eglot-alternatives
-                                              '("lua-language-server" 
"lua-lsp")))
+                                ((lua-mode lua-ts-mode) . ,(eglot-alternatives
+                                                            
'("lua-language-server" "lua-lsp")))
                                 (zig-mode . ("zls"))
                                 ((css-mode css-ts-mode)
                                  . ,(eglot-alternatives 
'(("vscode-css-language-server" "--stdio")
@@ -257,7 +288,8 @@ chosen (interactively or automatically)."
                                  . ,(eglot-alternatives
                                      '(("marksman" "server")
                                        ("vscode-markdown-language-server" 
"--stdio"))))
-                                (graphviz-dot-mode . ("dot-language-server" 
"--stdio")))
+                                (graphviz-dot-mode . ("dot-language-server" 
"--stdio"))
+                                (terraform-mode . ("terraform-ls" "serve")))
   "How the command `eglot' guesses the server to start.
 An association list of (MAJOR-MODE . CONTACT) pairs.  MAJOR-MODE
 identifies the buffers that are to be managed by a specific
@@ -388,10 +420,36 @@ done by `eglot-reconnect'."
   :type '(choice (const :tag "No limit" nil)
                  (integer :tag "Number of characters")))
 
-(defcustom eglot-confirm-server-initiated-edits 'confirm
-  "Non-nil if server-initiated edits should be confirmed with user."
-  :type '(choice (const :tag "Don't show confirmation prompt" nil)
-                 (const :tag "Show confirmation prompt" confirm)))
+(defcustom eglot-confirm-server-edits '((eglot-rename . nil)
+                                        (t . maybe-summary))
+  "Control if changes proposed by LSP should be confirmed with user.
+
+If this variable's value is the symbol `diff', a diff buffer is
+pops up, allowing the user to apply each change individually.  If
+the symbol `summary' or any other non-nil value, the user is
+prompted in the minibuffer with aa short summary of changes.  The
+symbols `maybe-diff' and `maybe-summary' mean that the
+confirmation is offered to the user only if the changes target
+files visited in buffers.  Finally, a nil value means all changes
+are applied directly without any confirmation.
+
+If this variable's value can also be an alist ((COMMAND . ACTION)
+...) where COMMAND is a symbol designating a command, such as
+`eglot-rename', `eglot-code-actions',
+`eglot-code-action-quickfix', etc.  ACTION is one of the symbols
+described above.  The value `t' for COMMAND is accepted and its
+ACTION is the default value for commands not in the alist."
+  :type (let ((basic-choices
+               '((const :tag "Use diff" diff)
+                 (const :tag "Summarize and prompt" summary)
+                 (const :tag "Maybe use diff" maybe-diff)
+                 (const :tag "Maybe summarize and prompt" maybe-summary)
+                 (const :tag "Don't confirm" nil))))
+          `(choice ,@basic-choices
+                   (alist :tag "Per-command alist"
+                          :key-type (choice (function :tag "Command")
+                                            (const :tag "Default" t))
+                          :value-type (choice . ,basic-choices)))))
 
 (defcustom eglot-extend-to-xref nil
   "If non-nil, activate Eglot in cross-referenced non-project files."
@@ -412,6 +470,36 @@ mode line indicator."
   :type 'boolean
   :version "1.10")
 
+(defcustom eglot-ignored-server-capabilities (list)
+  "LSP server capabilities that Eglot could use, but won't.
+You could add, for instance, the symbol
+`:documentHighlightProvider' to prevent automatic highlighting
+under cursor."
+  :type '(set
+          :tag "Tick the ones you're not interested in"
+          (const :tag "Documentation on hover" :hoverProvider)
+          (const :tag "Code completion" :completionProvider)
+          (const :tag "Function signature help" :signatureHelpProvider)
+          (const :tag "Go to definition" :definitionProvider)
+          (const :tag "Go to type definition" :typeDefinitionProvider)
+          (const :tag "Go to implementation" :implementationProvider)
+          (const :tag "Go to declaration" :declarationProvider)
+          (const :tag "Find references" :referencesProvider)
+          (const :tag "Highlight symbols automatically" 
:documentHighlightProvider)
+          (const :tag "List symbols in buffer" :documentSymbolProvider)
+          (const :tag "List symbols in workspace" :workspaceSymbolProvider)
+          (const :tag "Execute code actions" :codeActionProvider)
+          (const :tag "Code lens" :codeLensProvider)
+          (const :tag "Format buffer" :documentFormattingProvider)
+          (const :tag "Format portion of buffer" 
:documentRangeFormattingProvider)
+          (const :tag "On-type formatting" :documentOnTypeFormattingProvider)
+          (const :tag "Rename symbol" :renameProvider)
+          (const :tag "Highlight links in document" :documentLinkProvider)
+          (const :tag "Decorate color references" :colorProvider)
+          (const :tag "Fold regions of buffer" :foldingRangeProvider)
+          (const :tag "Execute custom commands" :executeCommandProvider)
+          (const :tag "Inlay hints" :inlayHintProvider)))
+
 (defvar eglot-withhold-process-id nil
   "If non-nil, Eglot will not send the Emacs process id to the language server.
 This can be useful when using docker to run a language server.")
@@ -456,6 +544,7 @@ It is nil if Eglot is not byte-complied.")
     (2 . eglot-diagnostic-tag-deprecated-face)))
 
 (defvaralias 'eglot-{} 'eglot--{})
+
 (defconst eglot--{} (make-hash-table :size 1) "The empty JSON object.")
 
 (defun eglot--executable-find (command &optional remote)
@@ -467,6 +556,12 @@ It is nil if Eglot is not byte-complied.")
   (if (and (not eglot-prefer-plaintext) (fboundp 'gfm-view-mode))
       ["markdown" "plaintext"] ["plaintext"]))
 
+(defconst eglot--uri-path-allowed-chars
+  (let ((vec (copy-sequence url-path-allowed-chars)))
+    (aset vec ?: nil) ;; see github#639
+    vec)
+  "Like `url-path-allows-chars' but more restrictive.")
+
 
 ;;; Message verification helpers
 ;;;
@@ -660,7 +755,6 @@ Honor `eglot-strict-mode'."
                 (cl-destructuring-bind (&key ,@vars &allow-other-keys) 
,object-once
                   (funcall ,fn-once ,@vars))))))))
 
-
 (cl-defmacro eglot--lambda (cl-lambda-list &body body)
   "Function of args CL-LAMBDA-LIST for processing INTERFACE objects.
 Honor `eglot-strict-mode'."
@@ -709,9 +803,6 @@ treated as in `eglot--dbind'."
                        ,obj-once
                        ',(mapcar #'car clauses)))))))
 
-
-;;; API (WORK-IN-PROGRESS!)
-;;;
 (cl-defmacro eglot--when-live-buffer (buf &rest body)
   "Check BUF live, then do BODY in it." (declare (indent 1) (debug t))
   (let ((b (cl-gensym)))
@@ -729,6 +820,9 @@ treated as in `eglot--dbind'."
   "Save excursion and restriction.  Widen.  Then run BODY." (declare (debug t))
   `(save-excursion (save-restriction (widen) ,@body)))
 
+
+;;; Public Elisp API
+;;;
 (cl-defgeneric eglot-handle-request (server method &rest params)
   "Handle SERVER's METHOD request with PARAMS.")
 
@@ -751,9 +845,9 @@ ACTION is an LSP object of either `CodeAction' or `Command' 
type."
      (((Command)) (eglot--request server :workspace/executeCommand action))
      (((CodeAction) edit command data)
       (if (and (null edit) (null command) data
-               (eglot--server-capable :codeActionProvider :resolveProvider))
+               (eglot-server-capable :codeActionProvider :resolveProvider))
           (eglot-execute server (eglot--request server :codeAction/resolve 
action))
-        (when edit (eglot--apply-workspace-edit edit))
+        (when edit (eglot--apply-workspace-edit edit this-command))
         (when command (eglot--request server :workspace/executeCommand 
command)))))))
 
 (cl-defgeneric eglot-initialization-options (server)
@@ -871,7 +965,7 @@ ACTION is an LSP object of either `CodeAction' or `Command' 
type."
   (let ((project (eglot--project server)))
     (vconcat
      (mapcar (lambda (dir)
-               (list :uri (eglot--path-to-uri dir)
+               (list :uri (eglot-path-to-uri dir)
                      :name (abbreviate-file-name dir)))
              `(,(project-root project) ,@(project-external-roots project))))))
 
@@ -917,6 +1011,81 @@ ACTION is an LSP object of either `CodeAction' or 
`Command' type."
   :documentation
   "Represents a server. Wraps a process for LSP communication.")
 
+(declare-function w32-long-file-name "w32proc.c" (fn))
+(defun eglot-uri-to-path (uri)
+  "Convert URI to file path, helped by `eglot--current-server'."
+  (when (keywordp uri) (setq uri (substring (symbol-name uri) 1)))
+  (let* ((server (eglot-current-server))
+         (remote-prefix (and server (eglot--trampish-p server)))
+         (url (url-generic-parse-url uri)))
+    ;; Only parse file:// URIs, leave other URI untouched as
+    ;; `file-name-handler-alist' should know how to handle them
+    ;; (bug#58790).
+    (if (string= "file" (url-type url))
+        (let* ((retval (url-unhex-string (url-filename url)))
+               ;; Remove the leading "/" for local MS Windows-style paths.
+               (normalized (if (and (not remote-prefix)
+                                    (eq system-type 'windows-nt)
+                                    (cl-plusp (length retval)))
+                               (w32-long-file-name (substring retval 1))
+                             retval)))
+          (concat remote-prefix normalized))
+      uri)))
+
+(defun eglot-path-to-uri (path)
+  "Convert PATH, a file name, to LSP URI string and return it."
+  (let ((truepath (file-truename path)))
+    (if (and (url-type (url-generic-parse-url path))
+             ;; It might be MS Windows path which includes a drive
+             ;; letter that looks like a URL scheme (bug#59338)
+             (not (and (eq system-type 'windows-nt)
+                       (file-name-absolute-p truepath))))
+        ;; Path is already a URI, so forward it to the LSP server
+        ;; untouched.  The server should be able to handle it, since
+        ;; it provided this URI to clients in the first place.
+        path
+      (concat "file://"
+              ;; Add a leading "/" for local MS Windows-style paths.
+              (if (and (eq system-type 'windows-nt)
+                       (not (file-remote-p truepath)))
+                  "/")
+              (url-hexify-string
+               ;; Again watch out for trampy paths.
+               (directory-file-name (file-local-name truepath))
+               eglot--uri-path-allowed-chars)))))
+
+(defun eglot-range-region (range &optional markers)
+  "Return a cons (BEG . END) of positions representing LSP RANGE.
+If optional MARKERS, make markers instead."
+  (let* ((st (plist-get range :start))
+         (beg (eglot--lsp-position-to-point st markers))
+         (end (eglot--lsp-position-to-point (plist-get range :end) markers)))
+    (cons beg end)))
+
+(defun eglot-server-capable (&rest feats)
+  "Determine if current server is capable of FEATS."
+  (unless (cl-some (lambda (feat)
+                     (memq feat eglot-ignored-server-capabilities))
+                   feats)
+    (cl-loop for caps = (eglot--capabilities (eglot--current-server-or-lose))
+             then (cadr probe)
+             for (feat . more) on feats
+             for probe = (plist-member caps feat)
+             if (not probe) do (cl-return nil)
+             if (eq (cadr probe) :json-false) do (cl-return nil)
+             if (not (listp (cadr probe))) do (cl-return (if more nil (cadr 
probe)))
+             finally (cl-return (or (cadr probe) t)))))
+
+(defun eglot-server-capable-or-lose (&rest feats)
+  "Like `eglot-server-capable', but maybe error out."
+  (let ((retval (apply #'eglot-server-capable feats)))
+    (unless retval
+      (eglot--error "Unsupported or ignored LSP capability `%s'"
+                    (mapconcat #'symbol-name feats " ")))
+    retval))
+
+
+;;; Process/server management
 (defun eglot--major-modes (s) "Major modes server S is responsible for."
   (mapcar #'car (eglot--languages s)))
 
@@ -926,8 +1095,6 @@ ACTION is an LSP object of either `CodeAction' or 
`Command' type."
 (cl-defmethod initialize-instance :before ((_server eglot-lsp-server) 
&optional args)
   (cl-remf args :initializationOptions))
 
-
-;;; Process management
 (defvar eglot--servers-by-project (make-hash-table :test #'equal)
   "Keys are projects.  Values are lists of processes.")
 
@@ -1369,7 +1536,7 @@ This docstring appeases checkdoc, that's all."
                             ;; into `/path/to/baz.py', so LSP groks it.
                             :rootPath (file-local-name
                                        (expand-file-name default-directory))
-                            :rootUri (eglot--path-to-uri default-directory)
+                            :rootUri (eglot-path-to-uri default-directory)
                             :initializationOptions 
(eglot-initialization-options
                                                     server)
                             :capabilities (eglot-client-capabilities server)
@@ -1524,13 +1691,6 @@ Unless IMMEDIATE, send pending changes before making 
request."
 
 ;;; Encoding fever
 ;;;
-(define-obsolete-function-alias
-  'eglot-lsp-abiding-column 'eglot-utf-16-linepos "1.12")
-(define-obsolete-function-alias
-  'eglot-current-column 'eglot-utf-32-linepos "1.12")
-(define-obsolete-variable-alias
-  'eglot-current-column-function 'eglot-current-linepos-function "1.12")
-
 (defvar eglot-current-linepos-function #'eglot-utf-16-linepos
   "Function calculating position relative to line beginning.
 
@@ -1569,13 +1729,6 @@ LBP defaults to `eglot--bol'."
          :character (progn (when pos (goto-char pos))
                            (funcall eglot-current-linepos-function)))))
 
-(define-obsolete-function-alias
-  'eglot-move-to-current-column 'eglot-move-to-utf-32-linepos "1.12")
-(define-obsolete-function-alias
-  'eglot-move-to-lsp-abiding-column 'eglot-move-to-utf-16-linepos "1.12")
-(define-obsolete-variable-alias
-'eglot-move-to-column-function 'eglot-move-to-linepos-function "1.12")
-
 (defvar eglot-move-to-linepos-function #'eglot-move-to-utf-16-linepos
   "Function to move to a position within a line reported by the LSP server.
 
@@ -1642,55 +1795,6 @@ If optional MARKER, return a marker instead"
 
 
 ;;; More helpers
-(defconst eglot--uri-path-allowed-chars
-  (let ((vec (copy-sequence url-path-allowed-chars)))
-    (aset vec ?: nil) ;; see github#639
-    vec)
-  "Like `url-path-allows-chars' but more restrictive.")
-
-(defun eglot--path-to-uri (path)
-  "URIfy PATH."
-  (let ((truepath (file-truename path)))
-    (if (and (url-type (url-generic-parse-url path))
-             ;; It might be MS Windows path which includes a drive
-             ;; letter that looks like a URL scheme (bug#59338)
-             (not (and (eq system-type 'windows-nt)
-                       (file-name-absolute-p truepath))))
-        ;; Path is already a URI, so forward it to the LSP server
-        ;; untouched.  The server should be able to handle it, since
-        ;; it provided this URI to clients in the first place.
-        path
-      (concat "file://"
-              ;; Add a leading "/" for local MS Windows-style paths.
-              (if (and (eq system-type 'windows-nt)
-                       (not (file-remote-p truepath)))
-                  "/")
-              (url-hexify-string
-               ;; Again watch out for trampy paths.
-               (directory-file-name (file-local-name truepath))
-               eglot--uri-path-allowed-chars)))))
-
-(declare-function w32-long-file-name "w32proc.c" (fn))
-(defun eglot--uri-to-path (uri)
-  "Convert URI to file path, helped by `eglot--current-server'."
-  (when (keywordp uri) (setq uri (substring (symbol-name uri) 1)))
-  (let* ((server (eglot-current-server))
-         (remote-prefix (and server (eglot--trampish-p server)))
-         (url (url-generic-parse-url uri)))
-    ;; Only parse file:// URIs, leave other URI untouched as
-    ;; `file-name-handler-alist' should know how to handle them
-    ;; (bug#58790).
-    (if (string= "file" (url-type url))
-        (let* ((retval (url-unhex-string (url-filename url)))
-               ;; Remove the leading "/" for local MS Windows-style paths.
-               (normalized (if (and (not remote-prefix)
-                                    (eq system-type 'windows-nt)
-                                    (cl-plusp (length retval)))
-                               (w32-long-file-name (substring retval 1))
-                             retval)))
-          (concat remote-prefix normalized))
-      uri)))
-
 (defun eglot--snippet-expansion-fn ()
   "Compute a function to expand snippets.
 Doubles as an indicator of snippet support."
@@ -1725,69 +1829,6 @@ Doubles as an indicator of snippet support."
                              (prop-match-end match)))))
         (string-trim (buffer-string))))))
 
-(define-obsolete-variable-alias 'eglot-ignored-server-capabilites
-  'eglot-ignored-server-capabilities "1.8")
-
-(defcustom eglot-ignored-server-capabilities (list)
-  "LSP server capabilities that Eglot could use, but won't.
-You could add, for instance, the symbol
-`:documentHighlightProvider' to prevent automatic highlighting
-under cursor."
-  :type '(set
-          :tag "Tick the ones you're not interested in"
-          (const :tag "Documentation on hover" :hoverProvider)
-          (const :tag "Code completion" :completionProvider)
-          (const :tag "Function signature help" :signatureHelpProvider)
-          (const :tag "Go to definition" :definitionProvider)
-          (const :tag "Go to type definition" :typeDefinitionProvider)
-          (const :tag "Go to implementation" :implementationProvider)
-          (const :tag "Go to declaration" :declarationProvider)
-          (const :tag "Find references" :referencesProvider)
-          (const :tag "Highlight symbols automatically" 
:documentHighlightProvider)
-          (const :tag "List symbols in buffer" :documentSymbolProvider)
-          (const :tag "List symbols in workspace" :workspaceSymbolProvider)
-          (const :tag "Execute code actions" :codeActionProvider)
-          (const :tag "Code lens" :codeLensProvider)
-          (const :tag "Format buffer" :documentFormattingProvider)
-          (const :tag "Format portion of buffer" 
:documentRangeFormattingProvider)
-          (const :tag "On-type formatting" :documentOnTypeFormattingProvider)
-          (const :tag "Rename symbol" :renameProvider)
-          (const :tag "Highlight links in document" :documentLinkProvider)
-          (const :tag "Decorate color references" :colorProvider)
-          (const :tag "Fold regions of buffer" :foldingRangeProvider)
-          (const :tag "Execute custom commands" :executeCommandProvider)
-          (const :tag "Inlay hints" :inlayHintProvider)))
-
-(defun eglot--server-capable (&rest feats)
-  "Determine if current server is capable of FEATS."
-  (unless (cl-some (lambda (feat)
-                     (memq feat eglot-ignored-server-capabilities))
-                   feats)
-    (cl-loop for caps = (eglot--capabilities (eglot--current-server-or-lose))
-             then (cadr probe)
-             for (feat . more) on feats
-             for probe = (plist-member caps feat)
-             if (not probe) do (cl-return nil)
-             if (eq (cadr probe) :json-false) do (cl-return nil)
-             if (not (listp (cadr probe))) do (cl-return (if more nil (cadr 
probe)))
-             finally (cl-return (or (cadr probe) t)))))
-
-(defun eglot--server-capable-or-lose (&rest feats)
-  "Like `eglot--server-capable', but maybe error out."
-  (let ((retval (apply #'eglot--server-capable feats)))
-    (unless retval
-      (eglot--error "Unsupported or ignored LSP capability `%s'"
-                    (mapconcat #'symbol-name feats " ")))
-    retval))
-
-(defun eglot--range-region (range &optional markers)
-  "Return region (BEG . END) that represents LSP RANGE.
-If optional MARKERS, make markers."
-  (let* ((st (plist-get range :start))
-         (beg (eglot--lsp-position-to-point st markers))
-         (end (eglot--lsp-position-to-point (plist-get range :end) markers)))
-    (cons beg end)))
-
 (defun eglot--read-server (prompt &optional dont-if-just-the-one)
   "Read a running Eglot server from minibuffer using PROMPT.
 If DONT-IF-JUST-THE-ONE and there's only one server, don't prompt
@@ -2046,9 +2087,6 @@ If it is activated, also signal textDocument/didOpen."
       (package-delete existing t))
     (package-install (cadr (assoc 'eglot package-archive-contents)))))
 
-;;;###autoload
-(define-obsolete-function-alias 'eglot-update 'eglot-upgrade-eglot "29.1")
-
 (easy-menu-define eglot-menu nil "Eglot"
   `("Eglot"
     ;; Commands for getting information and customization.
@@ -2057,47 +2095,47 @@ If it is activated, also signal textDocument/didOpen."
     ;; xref like commands.
     ["Find definitions" xref-find-definitions
      :help "Find definitions of identifier at point"
-     :active (eglot--server-capable :definitionProvider)]
+     :active (eglot-server-capable :definitionProvider)]
     ["Find references" xref-find-references
      :help "Find references to identifier at point"
-     :active (eglot--server-capable :referencesProvider)]
+     :active (eglot-server-capable :referencesProvider)]
     ["Find symbols in workspace (apropos)" xref-find-apropos
      :help "Find symbols matching a query"
-     :active (eglot--server-capable :workspaceSymbolProvider)]
+     :active (eglot-server-capable :workspaceSymbolProvider)]
     ["Find declaration" eglot-find-declaration
      :help "Find declaration for identifier at point"
-     :active (eglot--server-capable :declarationProvider)]
+     :active (eglot-server-capable :declarationProvider)]
     ["Find implementation" eglot-find-implementation
      :help "Find implementation for identifier at point"
-     :active (eglot--server-capable :implementationProvider)]
+     :active (eglot-server-capable :implementationProvider)]
     ["Find type definition" eglot-find-typeDefinition
      :help "Find type definition for identifier at point"
-     :active (eglot--server-capable :typeDefinitionProvider)]
+     :active (eglot-server-capable :typeDefinitionProvider)]
     "--"
     ;; LSP-related commands (mostly Eglot's own commands).
     ["Rename symbol" eglot-rename
-     :active (eglot--server-capable :renameProvider)]
+     :active (eglot-server-capable :renameProvider)]
     ["Format buffer" eglot-format-buffer
-     :active (eglot--server-capable :documentFormattingProvider)]
+     :active (eglot-server-capable :documentFormattingProvider)]
     ["Format active region" eglot-format
      :active (and (region-active-p)
-                  (eglot--server-capable :documentRangeFormattingProvider))]
+                  (eglot-server-capable :documentRangeFormattingProvider))]
     ["Show Flymake diagnostics for buffer" flymake-show-buffer-diagnostics]
     ["Show Flymake diagnostics for project" flymake-show-project-diagnostics]
     ["Show Eldoc documentation at point" eldoc-doc-buffer]
     "--"
     ["All possible code actions" eglot-code-actions
-     :active (eglot--server-capable :codeActionProvider)]
+     :active (eglot-server-capable :codeActionProvider)]
     ["Organize imports" eglot-code-action-organize-imports
-     :visible (eglot--server-capable :codeActionProvider)]
+     :visible (eglot-server-capable :codeActionProvider)]
     ["Extract" eglot-code-action-extract
-     :visible (eglot--server-capable :codeActionProvider)]
+     :visible (eglot-server-capable :codeActionProvider)]
     ["Inline" eglot-code-action-inline
-     :visible (eglot--server-capable :codeActionProvider)]
+     :visible (eglot-server-capable :codeActionProvider)]
     ["Rewrite" eglot-code-action-rewrite
-     :visible (eglot--server-capable :codeActionProvider)]
+     :visible (eglot-server-capable :codeActionProvider)]
     ["Quickfix" eglot-code-action-quickfix
-     :visible (eglot--server-capable :codeActionProvider)]))
+     :visible (eglot-server-capable :codeActionProvider)]))
 
 (easy-menu-define eglot-server-menu nil "Monitor server communication"
   '("Debugging the server communication"
@@ -2291,7 +2329,7 @@ still unanswered LSP requests to the server\n")))
                     (t          'eglot-note)))
             (mess (source code message)
               (concat source (and code (format " [%s]" code)) ": " message)))
-    (if-let* ((path (expand-file-name (eglot--uri-to-path uri)))
+    (if-let* ((path (expand-file-name (eglot-uri-to-path uri)))
               (buffer (find-buffer-visiting path)))
         (with-current-buffer buffer
           (cl-loop
@@ -2303,7 +2341,7 @@ still unanswered LSP requests to the server\n")))
                        diag-spec
                      (setq message (mess source code message))
                      (pcase-let
-                         ((`(,beg . ,end) (eglot--range-region range)))
+                         ((`(,beg . ,end) (eglot-range-region range)))
                        ;; Fallback to `flymake-diag-region' if server
                        ;; botched the range
                        (when (= beg end)
@@ -2379,7 +2417,7 @@ THINGS are either registrations or unregisterations 
(sic)."
 (cl-defmethod eglot-handle-request
   (_server (_method (eql workspace/applyEdit)) &key _label edit)
   "Handle server request workspace/applyEdit."
-  (eglot--apply-workspace-edit edit eglot-confirm-server-initiated-edits)
+  (eglot--apply-workspace-edit edit last-command)
   `(:applied t))
 
 (cl-defmethod eglot-handle-request
@@ -2395,7 +2433,7 @@ THINGS are either registrations or unregisterations 
(sic)."
         (filename))
     (cond
      ((eq external t) (browse-url uri))
-     ((file-readable-p (setq filename (eglot--uri-to-path uri)))
+     ((file-readable-p (setq filename (eglot-uri-to-path uri)))
       ;; Use run-with-timer to avoid nested client requests like the
       ;; "synchronous imenu" floated in bug#62116 presumably caused by
       ;; which-func-mode.
@@ -2408,7 +2446,7 @@ THINGS are either registrations or unregisterations 
(sic)."
                   (select-frame-set-input-focus (selected-frame)))
                  ((display-buffer (current-buffer))))
            (when selection
-             (pcase-let ((`(,beg . ,end) (eglot--range-region selection)))
+             (pcase-let ((`(,beg . ,end) (eglot-range-region selection)))
                ;; FIXME: it is very naughty to use someone else's `--'
                ;; function, but `xref--goto-char' happens to have
                ;; exactly the semantics we want vis-a-vis widening.
@@ -2419,7 +2457,7 @@ THINGS are either registrations or unregisterations 
(sic)."
 
 (defun eglot--TextDocumentIdentifier ()
   "Compute TextDocumentIdentifier object for current buffer."
-  `(:uri ,(eglot--path-to-uri (or buffer-file-name
+  `(:uri ,(eglot-path-to-uri (or buffer-file-name
                                   (ignore-errors
                                     (buffer-file-name
                                      (buffer-base-buffer)))))))
@@ -2460,7 +2498,7 @@ buffer."
 (defun eglot--post-self-insert-hook ()
   "Set `eglot--last-inserted-char', maybe call on-type-formatting."
   (setq eglot--last-inserted-char last-command-event)
-  (let ((ot-provider (eglot--server-capable 
:documentOnTypeFormattingProvider)))
+  (let ((ot-provider (eglot-server-capable :documentOnTypeFormattingProvider)))
     (when (and ot-provider
                (ignore-errors ; github#906, some LS's send empty strings
                  (or (eq eglot--last-inserted-char
@@ -2484,7 +2522,7 @@ buffer."
    `(:context
      ,(if-let (trigger (and (characterp eglot--last-inserted-char)
                             (cl-find eglot--last-inserted-char
-                                     (eglot--server-capable :completionProvider
+                                     (eglot-server-capable :completionProvider
                                                             :triggerCharacters)
                                      :key (lambda (str) (aref str 0))
                                      :test #'char-equal)))
@@ -2608,8 +2646,10 @@ local value of the `eglot-workspace-configuration' 
variable, else
 use the root of SERVER's `eglot--project'."
   (let ((val (with-temp-buffer
                (setq default-directory
-                     (if path
-                         (file-name-directory path)
+                     ;; See github#1281
+                     (if path (if (file-directory-p path)
+                                  (file-name-as-directory path)
+                                (file-name-directory path))
                        (project-root (eglot--project server))))
                ;; Set the major mode to be the first of the managed
                ;; modes.  This is the one the user started eglot in.
@@ -2643,7 +2683,7 @@ When called interactively, use the currently active 
server"
          (mapcar
           (eglot--lambda ((ConfigurationItem) scopeUri section)
             (cl-loop
-             with scope-uri-path = (and scopeUri (eglot--uri-to-path scopeUri))
+             with scope-uri-path = (and scopeUri (eglot-uri-to-path scopeUri))
              for (wsection o)
              on (eglot--workspace-configuration-plist server scope-uri-path)
              by #'cddr
@@ -2659,7 +2699,7 @@ When called interactively, use the currently active 
server"
   "Send textDocument/didChange to server."
   (when eglot--recent-changes
     (let* ((server (eglot--current-server-or-lose))
-           (sync-capability (eglot--server-capable :textDocumentSync))
+           (sync-capability (eglot-server-capable :textDocumentSync))
            (sync-kind (if (numberp sync-capability) sync-capability
                         (plist-get sync-capability :change)))
            (full-sync-p (or (eq sync-kind 1)
@@ -2704,9 +2744,9 @@ When called interactively, use the currently active 
server"
   "Maybe send textDocument/willSave to server."
   (let ((server (eglot--current-server-or-lose))
         (params `(:reason 1 :textDocument ,(eglot--TextDocumentIdentifier))))
-    (when (eglot--server-capable :textDocumentSync :willSave)
+    (when (eglot-server-capable :textDocumentSync :willSave)
       (jsonrpc-notify server :textDocument/willSave params))
-    (when (eglot--server-capable :textDocumentSync :willSaveWaitUntil)
+    (when (eglot-server-capable :textDocumentSync :willSaveWaitUntil)
       (ignore-errors
         (eglot--apply-text-edits
          (eglot--request server :textDocument/willSaveWaitUntil params
@@ -2715,7 +2755,7 @@ When called interactively, use the currently active 
server"
 (defun eglot--signal-textDocument/didSave ()
   "Maybe send textDocument/didSave to server."
   (eglot--signal-textDocument/didChange)
-  (when (eglot--server-capable :textDocumentSync :save)
+  (when (eglot-server-capable :textDocumentSync :save)
     (jsonrpc-notify
      (eglot--current-server-or-lose)
      :textDocument/didSave
@@ -2774,12 +2814,12 @@ may be called multiple times (respecting the protocol of
   "Like `xref-make-match' but with LSP's NAME, URI and RANGE.
 Try to visit the target file for a richer summary line."
   (pcase-let*
-      ((file (eglot--uri-to-path uri))
+      ((file (eglot-uri-to-path uri))
        (visiting (or (find-buffer-visiting file)
                      (gethash uri eglot--temp-location-buffers)))
        (collect (lambda ()
                   (eglot--widening
-                   (pcase-let* ((`(,beg . ,end) (eglot--range-region range))
+                   (pcase-let* ((`(,beg . ,end) (eglot-range-region range))
                                 (bol (progn (goto-char beg) (eglot--bol)))
                                 (substring (buffer-substring bol 
(line-end-position)))
                                 (hi-beg (- beg bol))
@@ -2810,7 +2850,7 @@ Try to visit the target file for a richer summary line."
   "Ask for :workspace/symbol on PAT, return list of formatted strings.
 If BUFFER, switch to it before."
   (with-current-buffer (or buffer (current-buffer))
-    (eglot--server-capable-or-lose :workspaceSymbolProvider)
+    (eglot-server-capable-or-lose :workspaceSymbolProvider)
     (mapcar
      (lambda (wss)
        (eglot--dbind ((WorkspaceSymbol) name containerName kind) wss
@@ -2872,7 +2912,7 @@ If BUFFER, switch to it before."
 
 (cl-defun eglot--lsp-xrefs-for-method (method &key extra-params capability)
   "Make `xref''s for METHOD, EXTRA-PARAMS, check CAPABILITY."
-  (eglot--server-capable-or-lose
+  (eglot-server-capable-or-lose
    (or capability
        (intern
         (format ":%sProvider"
@@ -2936,7 +2976,7 @@ If BUFFER, switch to it before."
     :textDocument/references :extra-params `(:context (:includeDeclaration 
t)))))
 
 (cl-defmethod xref-backend-apropos ((_backend (eql eglot)) pattern)
-  (when (eglot--server-capable :workspaceSymbolProvider)
+  (when (eglot-server-capable :workspaceSymbolProvider)
     (eglot--collecting-xrefs (collect)
       (mapc
        (eglot--lambda ((SymbolInformation) name location)
@@ -2974,7 +3014,7 @@ for which LSP on-type-formatting should be requested."
                                   :end (eglot--pos-to-lsp-position end)))))
                 (t
                  '(:textDocument/formatting :documentFormattingProvider 
nil)))))
-    (eglot--server-capable-or-lose cap)
+    (eglot-server-capable-or-lose cap)
     (eglot--apply-text-edits
      (eglot--request
       (eglot--current-server-or-lose)
@@ -2999,7 +3039,7 @@ for which LSP on-type-formatting should be requested."
 (defun eglot-completion-at-point ()
   "Eglot's `completion-at-point' function."
   ;; Commit logs for this function help understand what's going on.
-  (when-let (completion-capability (eglot--server-capable :completionProvider))
+  (when-let (completion-capability (eglot-server-capable :completionProvider))
     (let* ((server (eglot--current-server-or-lose))
            (sort-completions
             (lambda (completions)
@@ -3062,7 +3102,7 @@ for which LSP on-type-formatting should be requested."
             (lambda (lsp-comp)
               (or (gethash lsp-comp resolved)
                   (setf (gethash lsp-comp resolved)
-                        (if (and (eglot--server-capable :completionProvider
+                        (if (and (eglot-server-capable :completionProvider
                                                         :resolveProvider)
                                  (plist-get lsp-comp :data))
                             (eglot--request server :completionItem/resolve
@@ -3190,7 +3230,7 @@ for which LSP on-type-formatting should be requested."
                         (delete-region orig-pos (point))
                         (eglot--dbind ((TextEdit) range newText) textEdit
                           (pcase-let ((`(,beg . ,end)
-                                       (eglot--range-region range)))
+                                       (eglot-range-region range)))
                             (delete-region beg end)
                             (goto-char beg)
                             (funcall (or snippet-fn #'insert) newText))))
@@ -3273,7 +3313,7 @@ for which LSP on-type-formatting should be requested."
 
 (defun eglot-signature-eldoc-function (cb)
   "A member of `eldoc-documentation-functions', for signatures."
-  (when (eglot--server-capable :signatureHelpProvider)
+  (when (eglot-server-capable :signatureHelpProvider)
     (let ((buf (current-buffer)))
       (jsonrpc-async-request
        (eglot--current-server-or-lose)
@@ -3297,7 +3337,7 @@ for which LSP on-type-formatting should be requested."
 
 (defun eglot-hover-eldoc-function (cb)
   "A member of `eldoc-documentation-functions', for hover."
-  (when (eglot--server-capable :hoverProvider)
+  (when (eglot-server-capable :hoverProvider)
     (let ((buf (current-buffer)))
       (jsonrpc-async-request
        (eglot--current-server-or-lose)
@@ -3319,7 +3359,7 @@ for which LSP on-type-formatting should be requested."
   ;; FIXME: Obviously, this is just piggy backing on eldoc's calls for
   ;; convenience, as shown by the fact that we just ignore cb.
   (let ((buf (current-buffer)))
-    (when (eglot--server-capable :documentHighlightProvider)
+    (when (eglot-server-capable :documentHighlightProvider)
       (jsonrpc-async-request
        (eglot--current-server-or-lose)
        :textDocument/documentHighlight (eglot--TextDocumentPositionParams)
@@ -3331,7 +3371,7 @@ for which LSP on-type-formatting should be requested."
                  (mapcar
                   (eglot--lambda ((DocumentHighlight) range)
                     (pcase-let ((`(,beg . ,end)
-                                 (eglot--range-region range)))
+                                 (eglot-range-region range)))
                       (let ((ov (make-overlay beg end)))
                         (overlay-put ov 'face 'eglot-highlight-symbol-face)
                         (overlay-put ov 'modification-hooks
@@ -3351,7 +3391,7 @@ for which LSP on-type-formatting should be requested."
        (pcase-lambda (`(,container . ,objs))
          (let ((elems (mapcar
                        (eglot--lambda ((SymbolInformation) kind name location)
-                         (let ((reg (eglot--range-region
+                         (let ((reg (eglot-range-region
                                      (plist-get location :range)))
                                (kind (alist-get kind 
eglot--symbol-kind-names)))
                            (cons (propertize name
@@ -3367,7 +3407,7 @@ for which LSP on-type-formatting should be requested."
 (defun eglot--imenu-DocumentSymbol (res)
   "Compute `imenu--index-alist' for RES vector of DocumentSymbol."
   (cl-labels ((dfs (&key name children range kind &allow-other-keys)
-                (let* ((reg (eglot--range-region range))
+                (let* ((reg (eglot-range-region range))
                        (kind (alist-get kind eglot--symbol-kind-names))
                        (name (propertize name
                                          'breadcrumb-region reg
@@ -3381,7 +3421,7 @@ for which LSP on-type-formatting should be requested."
 (cl-defun eglot-imenu ()
   "Eglot's `imenu-create-index-function'.
 Returns a list as described in docstring of `imenu--index-alist'."
-  (unless (eglot--server-capable :documentSymbolProvider)
+  (unless (eglot-server-capable :documentSymbolProvider)
     (cl-return-from eglot-imenu))
   (let* ((res (eglot--request (eglot--current-server-or-lose)
                               :textDocument/documentSymbol
@@ -3423,39 +3463,106 @@ If SILENT, don't echo progress in mode-line."
                       (when reporter
                         (eglot--reporter-update reporter (cl-incf done))))))))
             (mapcar (eglot--lambda ((TextEdit) range newText)
-                      (cons newText (eglot--range-region range 'markers)))
+                      (cons newText (eglot-range-region range 'markers)))
                     (reverse edits)))
       (undo-amalgamate-change-group change-group)
       (when reporter
         (progress-reporter-done reporter)))))
 
-(defun eglot--apply-workspace-edit (wedit &optional confirm)
-  "Apply the workspace edit WEDIT.  If CONFIRM, ask user first."
+(defun eglot--confirm-server-edits (origin _prepared)
+  "Helper for `eglot--apply-workspace-edit.
+ORIGIN is a symbol designating a command.  Reads the
+`eglot-confirm-server-edits' user option and returns a symbol
+like `diff', `summary' or nil."
+  (let (v)
+    (cond ((symbolp eglot-confirm-server-edits) eglot-confirm-server-edits)
+          ((setq v (assoc origin eglot-confirm-server-edits)) (cdr v))
+          ((setq v (assoc t eglot-confirm-server-edits)) (cdr v)))))
+
+(defun eglot--propose-changes-as-diff (prepared)
+  "Helper for `eglot--apply-workspace-edit'.
+Goal is to popup a `diff-mode' buffer containing all the changes
+of PREPARED, ready to apply with C-c C-a.  PREPARED is a
+list ((FILENAME EDITS VERSION)...)."
+  (with-current-buffer (get-buffer-create "*EGLOT proposed server changes*")
+    (buffer-disable-undo (current-buffer))
+    (let ((inhibit-read-only t)
+          (target (current-buffer)))
+      (diff-mode)
+      (erase-buffer)
+      (pcase-dolist (`(,path ,edits ,_) prepared)
+        (with-temp-buffer
+          (let* ((diff (current-buffer))
+                 (existing-buf (find-buffer-visiting path))
+                 (existing-buf-label (prin1-to-string existing-buf)))
+            (with-temp-buffer
+              (if existing-buf
+                  (insert-buffer-substring existing-buf)
+                (insert-file-contents path))
+              (eglot--apply-text-edits edits nil t)
+              (diff-no-select (or existing-buf path) (current-buffer) nil t 
diff)
+              (when existing-buf
+                ;; Here we have to pretend the label of the unsaved
+                ;; buffer is the actual file, just so that we can
+                ;; diff-apply without troubles.  If there's a better
+                ;; way, it probably involves changes to `diff.el'.
+                (with-current-buffer diff
+                  (goto-char (point-min))
+                  (while (search-forward existing-buf-label nil t)
+                    (replace-match (buffer-file-name existing-buf))))))
+            (with-current-buffer target
+              (insert-buffer-substring diff))))))
+    (setq-local buffer-read-only t)
+    (buffer-enable-undo (current-buffer))
+    (goto-char (point-min))
+    (pop-to-buffer (current-buffer))
+    (font-lock-ensure)))
+
+(defun eglot--apply-workspace-edit (wedit origin)
+  "Apply (or offer to apply) the workspace edit WEDIT.
+ORIGIN is a symbol designating the command that originated this
+edit proposed by the server."
   (eglot--dbind ((WorkspaceEdit) changes documentChanges) wedit
     (let ((prepared
            (mapcar (eglot--lambda ((TextDocumentEdit) textDocument edits)
                      (eglot--dbind ((VersionedTextDocumentIdentifier) uri 
version)
                          textDocument
-                       (list (eglot--uri-to-path uri) edits version)))
+                       (list (eglot-uri-to-path uri) edits version)))
                    documentChanges)))
       (unless (and changes documentChanges)
         ;; We don't want double edits, and some servers send both
         ;; changes and documentChanges.  This unless ensures that we
         ;; prefer documentChanges over changes.
         (cl-loop for (uri edits) on changes by #'cddr
-                 do (push (list (eglot--uri-to-path uri) edits) prepared)))
-      (if (or confirm
-              (cl-notevery #'find-buffer-visiting
-                           (mapcar #'car prepared)))
-          (unless (y-or-n-p
-                   (format "[eglot] Server wants to edit:\n  %s\n Proceed? "
-                           (mapconcat #'identity (mapcar #'car prepared) "\n  
")))
-            (jsonrpc-error "User canceled server edit")))
-      (cl-loop for edit in prepared
-               for (path edits version) = edit
-               do (with-current-buffer (find-file-noselect path)
-                    (eglot--apply-text-edits edits version))
-               finally (eldoc) (eglot--message "Edit successful!")))))
+                 do (push (list (eglot-uri-to-path uri) edits) prepared)))
+      (cl-flet ((notevery-visited-p ()
+                  (cl-notevery #'find-buffer-visiting
+                               (mapcar #'car prepared)))
+                (accept-p ()
+                  (y-or-n-p
+                   (format "[eglot] Server wants to edit:\n%sProceed? "
+                           (cl-loop
+                            for (f eds _) in prepared
+                            concat (format
+                                    "  %s (%d change%s)\n"
+                                    f (length eds)
+                                    (if (> (length eds) 1) "s" ""))))))
+                (apply ()
+                  (cl-loop for edit in prepared
+                   for (path edits version) = edit
+                   do (with-current-buffer (find-file-noselect path)
+                        (eglot--apply-text-edits edits version))
+                   finally (eldoc) (eglot--message "Edit successful!"))))
+        (let ((decision (eglot--confirm-server-edits origin prepared)))
+          (cond
+           ((or (eq decision 'diff)
+                (and (eq decision 'maybe-diff) (notevery-visited-p)))
+            (eglot--propose-changes-as-diff prepared))
+           ((or (memq decision '(t summary))
+                (and (eq decision 'maybe-summary) (notevery-visited-p)))
+            (when (accept-p) (apply)))
+           (t
+            (apply))))))))
 
 (defun eglot-rename (newname)
   "Rename the current symbol to NEWNAME."
@@ -3465,18 +3572,25 @@ If SILENT, don't echo progress in mode-line."
                                          "unknown symbol"))
           nil nil nil nil
           (symbol-name (symbol-at-point)))))
-  (eglot--server-capable-or-lose :renameProvider)
+  (eglot-server-capable-or-lose :renameProvider)
   (eglot--apply-workspace-edit
    (eglot--request (eglot--current-server-or-lose)
                    :textDocument/rename `(,@(eglot--TextDocumentPositionParams)
                                           :newName ,newname))
-   current-prefix-arg))
-
-(defun eglot--region-bounds ()
-  "Region bounds if active, else bounds of things at point."
-  (if (use-region-p) `(,(region-beginning) ,(region-end))
-    (let ((boftap (bounds-of-thing-at-point 'sexp)))
-      (list (car boftap) (cdr boftap)))))
+   this-command))
+
+(defun eglot--code-action-bounds ()
+  "Calculate appropriate bounds depending on region and point."
+  (let (diags)
+    (cond ((use-region-p) `(,(region-beginning) ,(region-end)))
+          ((setq diags (flymake-diagnostics (point)))
+           (cl-loop for d in diags
+                    minimizing (flymake-diagnostic-beg d) into beg
+                    maximizing (flymake-diagnostic-end d) into end
+                    finally (cl-return (list beg end))))
+          (t
+           (let ((boftap (bounds-of-thing-at-point 'sexp)))
+             (list (car boftap) (cdr boftap)))))))
 
 (defun eglot-code-actions (beg &optional end action-kind interactive)
   "Find LSP code actions of type ACTION-KIND between BEG and END.
@@ -3486,13 +3600,13 @@ Interactively, default BEG and END to region's bounds 
else BEG is
 point and END is nil, which results in a request for code actions
 at point.  With prefix argument, prompt for ACTION-KIND."
   (interactive
-   `(,@(eglot--region-bounds)
+   `(,@(eglot--code-action-bounds)
      ,(and current-prefix-arg
            (completing-read "[eglot] Action kind: "
                             '("quickfix" "refactor.extract" "refactor.inline"
                               "refactor.rewrite" "source.organizeImports")))
      t))
-  (eglot--server-capable-or-lose :codeActionProvider)
+  (eglot-server-capable-or-lose :codeActionProvider)
   (let* ((server (eglot--current-server-or-lose))
          (actions
           (eglot--request
@@ -3549,7 +3663,7 @@ at point.  With prefix argument, prompt for ACTION-KIND."
   "Define NAME to execute KIND code action."
   `(defun ,name (beg &optional end)
      ,(format "Execute `%s' code actions between BEG and END." kind)
-     (interactive (eglot--region-bounds))
+     (interactive (eglot--code-action-bounds))
      (eglot-code-actions beg end ,kind t)))
 
 (eglot--code-action eglot-code-action-organize-imports 
"source.organizeImports")
@@ -3592,7 +3706,7 @@ at point.  With prefix argument, prompt for ACTION-KIND."
                                           (funcall glob file))))
                (jsonrpc-notify
                 server :workspace/didChangeWatchedFiles
-                `(:changes ,(vector `(:uri ,(eglot--path-to-uri file)
+                `(:changes ,(vector `(:uri ,(eglot-path-to-uri file)
                                            :type ,action-type))))
                (when (and (eq action 'created)
                           (file-directory-p file))
@@ -3859,7 +3973,7 @@ If NOERROR, return predicate, else erroring function."
   "Minor mode for annotating buffers with LSP server's inlay hints."
   :global nil
   (cond (eglot-inlay-hints-mode
-         (if (eglot--server-capable :inlayHintProvider)
+         (if (eglot-server-capable :inlayHintProvider)
              (jit-lock-register #'eglot--update-hints 'contextual)
            (eglot-inlay-hints-mode -1)))
         (t
@@ -3886,11 +4000,7 @@ If NOERROR, return predicate, else erroring function."
                 "https://github.com/joaotavora/eglot/issues/%s";
               "https://debbugs.gnu.org/%s";)
             (match-string 3))))
-;;; Obsolete
-;;;
 
-(make-obsolete-variable 'eglot--managed-mode-hook
-                        'eglot-managed-mode-hook "1.6")
 (provide 'eglot)
 
 
diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el
index d0f15f049a9..997df648289 100644
--- a/lisp/progmodes/elisp-mode.el
+++ b/lisp/progmodes/elisp-mode.el
@@ -461,14 +461,14 @@ use of `macroexpand-all' as a way to find the 
\"underlying raw code\".")
               (lambda (expander form &rest args)
                 (condition-case err
                     (apply expander form args)
-                  (error (message "Ignoring macroexpansion error: %S" err)
-                         form))))
+                  ((debug error)
+                   (message "Ignoring macroexpansion error: %S" err) form))))
              (sexp
               (unwind-protect
                   (let ((warning-minimum-log-level :emergency))
                     (advice-add 'macroexpand-1 :around macroexpand-advice)
                     (macroexpand-all sexp elisp--local-macroenv))
-                (advice-remove 'macroexpand macroexpand-advice)))
+                (advice-remove 'macroexpand-1 macroexpand-advice)))
              (vars (elisp--local-variables-1 nil sexp)))
         (delq nil
               (mapcar (lambda (var)
diff --git a/lisp/progmodes/elixir-ts-mode.el b/lisp/progmodes/elixir-ts-mode.el
index 7175fe4bff8..2ddce3de105 100644
--- a/lisp/progmodes/elixir-ts-mode.el
+++ b/lisp/progmodes/elixir-ts-mode.el
@@ -53,6 +53,7 @@
 (declare-function treesit-parser-language "treesit.c")
 (declare-function treesit-parser-included-ranges "treesit.c")
 (declare-function treesit-parser-list "treesit.c")
+(declare-function treesit-node-p "treesit.c")
 (declare-function treesit-node-parent "treesit.c")
 (declare-function treesit-node-start "treesit.c")
 (declare-function treesit-node-end "treesit.c")
@@ -312,7 +313,16 @@
        ((parent-is "^catch_block$") parent ,offset)
        ((parent-is "^keywords$") parent-bol 0)
        ((node-is "^call$") parent-bol ,offset)
-       ((node-is "^comment$") parent-bol ,offset)))))
+       ((node-is "^comment$") parent-bol ,offset)
+       ((node-is "\"\"\"") parent-bol 0)
+       ;; Handle quoted_content indentation on the last
+       ;; line before the closing \"\"\", where it might
+       ;; see it as no-node outside a HEEx tag.
+       (no-node (lambda (_n _p _bol)
+                  (treesit-node-start
+                   (treesit-node-parent
+                    (treesit-node-at (point) 'elixir))))
+                  0)))))
 
 (defvar elixir-ts--font-lock-settings
   (treesit-font-lock-rules
@@ -510,21 +520,15 @@ With ARG, do it many times.  Negative ARG means move 
backward."
 
 (defun elixir-ts--treesit-language-at-point (point)
   "Return the language at POINT."
-  (let* ((range nil)
-         (language-in-range
-          (cl-loop
-           for parser in (treesit-parser-list)
-           do (setq range
-                    (cl-loop
-                     for range in (treesit-parser-included-ranges parser)
-                     if (and (>= point (car range)) (<= point (cdr range)))
-                     return parser))
-           if range
-           return (treesit-parser-language parser))))
-    (if (null language-in-range)
-        (when-let ((parser (car (treesit-parser-list))))
-          (treesit-parser-language parser))
-      language-in-range)))
+  (let ((node (treesit-node-at point 'elixir)))
+    (if (and (equal (treesit-node-type node) "quoted_content")
+             (let ((prev-sibling (treesit-node-prev-sibling node t)))
+               (and (treesit-node-p prev-sibling)
+                    (string-match-p
+                     (rx bos (or "H" "F") eos)
+                     (treesit-node-text prev-sibling)))))
+        'heex
+      'elixir)))
 
 (defun elixir-ts--defun-p (node)
   "Return non-nil when NODE is a defun."
diff --git a/lisp/progmodes/flymake-proc.el b/lisp/progmodes/flymake-proc.el
index f640e7f32bf..2e9c28a92d5 100644
--- a/lisp/progmodes/flymake-proc.el
+++ b/lisp/progmodes/flymake-proc.el
@@ -607,6 +607,9 @@ Create parent directories as needed."
             (process-put proc 'flymake-proc--unprocessed-mark
                          (point-marker))))))))
 
+(defvar-local flymake-proc--temp-source-file-name nil)
+(defvar-local flymake-proc--temp-master-file-name nil)
+
 (defun flymake-proc--process-sentinel (proc _event)
   "Sentinel for syntax check buffers."
   (let (debug
@@ -910,9 +913,7 @@ can also be executed interactively independently of
        (file-truename (expand-file-name suffix temp-dir)))
       (setq suffix (file-name-directory suffix)))))
 
-(defvar-local flymake-proc--temp-source-file-name nil)
 (defvar-local flymake-proc--master-file-name nil)
-(defvar-local flymake-proc--temp-master-file-name nil)
 (defvar-local flymake-proc--base-dir nil)
 
 (defun flymake-proc-init-create-temp-buffer-copy (create-temp-f)
diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el
index b4c0e4db6ac..b27e6527f81 100644
--- a/lisp/progmodes/flymake.el
+++ b/lisp/progmodes/flymake.el
@@ -4,7 +4,7 @@
 
 ;; Author: Pavel Kobyakov <pk_at_work@yahoo.com>
 ;; Maintainer: João Távora <joaotavora@gmail.com>
-;; Version: 1.3.4
+;; Version: 1.3.6
 ;; Keywords: c languages tools
 ;; Package-Requires: ((emacs "26.1") (eldoc "1.14.0") (project "0.7.1"))
 
@@ -114,7 +114,7 @@
 (require 'thingatpt) ; end-of-thing
 (require 'warnings) ; warning-numeric-level, display-warning
 (require 'compile) ; for some faces
-;; We need the next require to avoid compiler warnings and run-time
+;; We need the next `require' to avoid compiler warnings and run-time
 ;; errors about mouse-wheel-up/down-event in builds --without-x, where
 ;; mwheel is not preloaded.
 (require 'mwheel)
@@ -354,8 +354,13 @@ the diagnostic's type symbol."
 If neither BEG or END is supplied, use whole accessible buffer,
 otherwise if BEG is non-nil and END is nil, consider only
 diagnostics at BEG."
-  (mapcar (lambda (ov) (overlay-get ov 'flymake-diagnostic))
-          (flymake--overlays :beg beg :end end)))
+  (save-restriction
+    (widen)
+    (cl-loop for o in
+             (cond (end (overlays-in beg end))
+                   (beg (overlays-at beg))
+                   (t (overlays-in (point-min) (point-max))))
+             when (overlay-get o 'flymake-diagnostic) collect it)))
 
 (defmacro flymake--diag-accessor (public internal thing)
   "Make PUBLIC an alias for INTERNAL, add doc using THING."
@@ -385,7 +390,7 @@ type."
                   (flymake--lookup-type-property
                    (flymake-diagnostic-type diag) 'echo-face 
'flymake-error)))))
 
-(cl-defun flymake--overlays (&key beg end filter compare key)
+(cl-defun flymake--really-all-overlays ()
   "Get flymake-related overlays.
 If BEG is non-nil and END is nil, consider only `overlays-at'
 BEG.  Otherwise consider `overlays-in' the region comprised by BEG
@@ -393,19 +398,8 @@ and END, defaulting to the whole buffer.  Remove all that 
do not
 verify FILTER, a function, and sort them by COMPARE (using KEY)."
   (save-restriction
     (widen)
-    (let ((ovs (cl-remove-if-not
-                (lambda (ov)
-                  (and (overlay-get ov 'flymake-diagnostic)
-                       (or (not filter)
-                           (funcall filter ov))))
-                (if (and beg (null end))
-                    (overlays-at beg t)
-                  (overlays-in (or beg (point-min))
-                               (or end (point-max)))))))
-      (if compare
-          (cl-sort ovs compare :key (or key
-                                        #'identity))
-        ovs))))
+    (cl-remove-if-not (lambda (o) (overlay-get o 'flymake-overlay))
+                      (overlays-in (point-min) (point-max)))))
 
 (defface flymake-error
   '((((supports :underline (:style wave)))
@@ -442,7 +436,7 @@ verify FILTER, a function, and sort them by COMPARE (using 
KEY)."
   :package-version '(Flymake . "1.3.4"))
 
 (defface flymake-note-echo
-  '((t :inherit flymake-note))
+  '((t :inherit compilation-info))
   "Face used for showing summarized descriptions of notes."
   :package-version '(Flymake . "1.3.4"))
 
@@ -463,14 +457,26 @@ See variable `flymake-show-diagnostics-at-end-of-line'."
   :package-version '(Flymake . "1.3.5"))
 
 (defface flymake-note-echo-at-eol
-  '((t :inherit (flymake-end-of-line-diagnostics-face flymake-note)))
+  '((t :inherit (flymake-end-of-line-diagnostics-face compilation-info)))
   "Face like `flymake-note-echo', but for end-of-line overlays."
   :package-version '(Flymake . "1.3.5"))
 
+(defface flymake-eol-information-face
+  '((t :inherit (flymake-end-of-line-diagnostics-face)
+       :box nil
+       :slant italic))
+  "Face used for information about end-of-line diagnostics."
+  :package-version '(Flymake . "1.3.6"))
+
 (defcustom flymake-show-diagnostics-at-end-of-line nil
-  "If non-nil, add diagnostic summary messages at end-of-line."
-  :type 'boolean
-  :package-version '(Flymake . "1.3.4"))
+  "If non-nil, add diagnostic summary messages at end-of-line.
+The value `short' means that only the most severe diagnostic
+shall be shown.  Any other non-nil value means show all
+diagnostic summaries at end-of-line."
+  :type '(choice (const :tag "Display most severe diagnostic" short)
+                 (const :tag "Display all diagnostics" t)
+                 (const :tag "Don't display diagnostics at end-of-line" nil))
+  :package-version '(Flymake . "1.3.6"))
 
 (define-obsolete-face-alias 'flymake-warnline 'flymake-warning "26.1")
 (define-obsolete-face-alias 'flymake-errline 'flymake-error "26.1")
@@ -703,9 +709,49 @@ associated `flymake-category' return DEFAULT."
 (defun flymake--delete-overlay (ov)
   "Like `delete-overlay', delete OV, but do some more stuff."
   (let ((eolov (overlay-get ov 'eol-ov)))
-    (when eolov (delete-overlay eolov))
+    (when eolov
+      (let ((src-ovs (delq ov (overlay-get eolov 
'flymake-eol-source-overlays))))
+        (overlay-put eolov 'flymake-eol-source-overlays src-ovs)))
     (delete-overlay ov)))
 
+(defun flymake--eol-overlay-summary (src-ovs)
+  "Helper function for `flymake--eol-overlay-update'."
+  (cl-flet ((summarize (d)
+              (propertize (flymake-diagnostic-oneliner d t) 'face
+                          (flymake--lookup-type-property (flymake--diag-type d)
+                                                         'eol-face))))
+    (let* ((diags
+            (cl-sort
+             (mapcar (lambda (o) (overlay-get o 'flymake-diagnostic)) src-ovs)
+             #'>
+             :key (lambda (d) (flymake--severity (flymake-diagnostic-type 
d)))))
+           (summary
+            (concat
+             "  "
+             (cond ((eq flymake-show-diagnostics-at-end-of-line 'short)
+                    (concat
+                     (summarize (car diags))
+                     (and (cdr diags)
+                          (concat
+                           " "
+                           (propertize (format "and %s more"
+                                               (1- (length diags)))
+                                       'face 'flymake-eol-information-face)))))
+                   (t
+                    (mapconcat #'summarize diags " "))))))
+      (put-text-property 0 1 'cursor t summary)
+      summary)))
+
+(defun flymake--update-eol-overlays ()
+  "Update the `before-string' property of end-of-line overlays."
+  (save-excursion
+    (widen)
+    (dolist (o (overlays-in (point-min) (point-max)))
+      (when (overlay-get o 'flymake--eol-overlay)
+        (if-let ((src-ovs (overlay-get o 'flymake-eol-source-overlays)))
+            (overlay-put o 'before-string (flymake--eol-overlay-summary 
src-ovs))
+          (delete-overlay o))))))
+
 (cl-defun flymake--highlight-line (diagnostic &optional foreign)
   "Attempt to overlay DIAGNOSTIC in current buffer.
 
@@ -741,9 +787,9 @@ Return nil or the overlay created."
              (setq beg a end b))))
     (setf (flymake--diag-beg diagnostic) beg
           (flymake--diag-end diagnostic) end)
-    ;; Try to fix the remedy the situation if there is the same
-    ;; diagnostic is already registered in the same place, which only
-    ;; happens for clashes between domestic and foreign diagnostics
+    ;; Try to remedy the situation if the same diagnostic is already
+    ;; registered in the same place.  This happens for clashes between
+    ;; domestic and foreign diagnostics
     (cl-loop for e in (flymake-diagnostics beg end)
              for eov = (flymake--diag-overlay e)
              when (flymake--equal-diagnostic-p e diagnostic)
@@ -762,7 +808,12 @@ Return nil or the overlay created."
                         (flymake--diag-end e)
                         (flymake--diag-orig-end e))
                   (flymake--delete-overlay eov)))
-    (setq ov (make-overlay end beg))
+    (setq ov (make-overlay beg end))
+    (when (= (overlay-start ov) (overlay-end ov))
+      ;; Some backends report diagnostics with invalid bounds.  Don't
+      ;; bother.
+      (delete-overlay ov)
+      (cl-return-from flymake--highlight-line nil))
     (setf (flymake--diag-beg diagnostic) (overlay-start ov)
           (flymake--diag-end diagnostic) (overlay-end ov))
     ;; First set `category' in the overlay
@@ -779,39 +830,6 @@ Return nil or the overlay created."
               (flymake--lookup-type-property type 'flymake-overlay-control))
              (alist-get type flymake-diagnostic-types-alist))
      do (overlay-put ov ov-prop value))
-    ;; Handle `flymake-show-diagnostics-at-end-of-line'
-    ;;
-    (when-let ((eol-face (and flymake-show-diagnostics-at-end-of-line
-                              (flymake--lookup-type-property type 'eol-face))))
-      (save-excursion
-        (goto-char (overlay-start ov))
-        (let* ((start (line-end-position))
-               (end (min (1+ start) (point-max)))
-               (eolov (car
-                       (cl-remove-if-not
-                        (lambda (o) (overlay-get o 'flymake-eol-source-region))
-                        (overlays-at start))))
-               (bs (flymake-diagnostic-oneliner diagnostic t)))
-          (setq bs (propertize bs 'face eol-face))
-          ;; FIXME: 1. no checking if there are unexpectedly more than
-          ;; one eolov at point.  2. The first regular source ov to
-          ;; die also kills the eolov (very rare this matters, but
-          ;; could be improved).
-          (cond (eolov
-                 (overlay-put eolov 'before-string
-                              (concat (overlay-get eolov 'before-string) " " 
bs))
-                 (let ((e (overlay-get eolov 'flymake-eol-source-region)))
-                   (setcar e (min (car e) (overlay-start ov)))
-                   (setcdr e (max (cdr e) (overlay-end ov)))))
-                (t
-                 (setq eolov (make-overlay start end nil t nil))
-                 (setq bs (concat "   " bs))
-                 (put-text-property 0 1 'cursor t bs)
-                 (overlay-put eolov 'before-string bs)
-                 (overlay-put eolov 'evaporate (not (= start end)))
-                 (overlay-put eolov 'flymake-eol-source-region
-                              (cons (overlay-start ov) (overlay-end ov)))
-                 (overlay-put ov 'eol-ov eolov))))))
     ;; Now ensure some essential defaults are set
     ;;
     (cl-flet ((default-maybe
@@ -843,8 +861,30 @@ Return nil or the overlay created."
     ;; Some properties can't be overridden.
     ;;
     (overlay-put ov 'evaporate t)
+    (overlay-put ov 'flymake-overlay t)
     (overlay-put ov 'flymake-diagnostic diagnostic)
     (setf (flymake--diag-overlay diagnostic) ov)
+    ;; Handle `flymake-show-diagnostics-at-end-of-line'
+    ;;
+    (when flymake-show-diagnostics-at-end-of-line
+      (save-excursion
+        (goto-char (overlay-start ov))
+        (let* ((start (line-end-position))
+               (end (min (1+ start) (point-max)))
+               (eolov (car
+                       (cl-remove-if-not
+                        (lambda (o) (overlay-get o 
'flymake-eol-source-overlays))
+                        (overlays-in start end)))))
+          ;; FIXME: 1. no checking if there are unexpectedly more than
+          ;; one eolov at point.
+          (if eolov
+              (push ov (overlay-get eolov 'flymake-eol-source-overlays))
+            (setq eolov (make-overlay start end nil t nil))
+            (overlay-put eolov 'flymake-overlay t)
+            (overlay-put eolov 'flymake--eol-overlay t)
+            (overlay-put eolov 'flymake-eol-source-overlays (list ov))
+            (overlay-put eolov 'evaporate (not (= start end)))) ; FIXME: fishy
+          (overlay-put ov 'eol-ov eolov))))
     ov))
 
 ;; Nothing in Flymake uses this at all any more, so this is just for
@@ -953,6 +993,14 @@ report applies to that region."
                      (float-time
                       (time-since flymake-check-start-time))))))
     (setf (flymake--state-reported-p state) t)
+    ;; All of the above might have touched the eol overlays, so issue
+    ;; a call to update them.  But check running and reporting
+    ;; backends first to flickering when multiple backends touch the
+    ;; same eol overlays.
+    (when (and flymake-show-diagnostics-at-end-of-line
+               (not (cl-set-difference (flymake-running-backends)
+                                       (flymake-reporting-backends))))
+      (flymake--update-eol-overlays))
     (flymake--update-diagnostics-listings (current-buffer))))
 
 (defun flymake--clear-foreign-diags (state)
@@ -965,6 +1013,9 @@ report applies to that region."
 
 (defvar-local flymake-mode nil)
 
+(defvar-local flymake--mode-line-counter-cache nil
+  "A cache used in `flymake-mode-line-counters'.")
+
 (cl-defun flymake--publish-diagnostics (diags &key backend state region)
   "Helper for `flymake--handle-report'.
 Publish DIAGS, which contain diagnostics for the current buffer
@@ -1025,7 +1076,9 @@ and other buffers."
                    (setf (flymake--diag-locus d) (buffer-file-name))))
                (cl-assert (stringp (flymake--diag-locus d)))
                (push d (gethash (flymake--diag-locus d)
-                                (flymake--state-foreign-diags state))))))))
+                                (flymake--state-foreign-diags state))))))
+    ;; Finally, flush some caches
+    (setq flymake--mode-line-counter-cache nil)))
 
 (defun flymake-make-report-fn (backend &optional token)
   "Make a suitable anonymous report function for BACKEND.
@@ -1094,15 +1147,7 @@ with a report function."
       (setf (flymake--state-running state) run-token
             (flymake--state-disabled state) nil
             (flymake--state-reported-p state) nil))
-    ;; FIXME: Should use `condition-case-unless-debug' here, but don't
-    ;; for two reasons: (1) that won't let me catch errors from inside
-    ;; `ert-deftest' where `debug-on-error' appears to be always
-    ;; t. (2) In cases where the user is debugging elisp somewhere
-    ;; else, and using flymake, the presence of a frequently
-    ;; misbehaving backend in the global hook (most likely the legacy
-    ;; backend) will trigger an annoying backtrace.
-    ;;
-    (condition-case err
+    (condition-case-unless-debug err
         (apply backend (flymake-make-report-fn backend run-token)
                args)
       (error
@@ -1169,6 +1214,11 @@ Interactively, with a prefix arg, FORCE is t."
                            (cl-reduce
                             #'max (mapcar #'cadr flymake--recent-changes))))))
                (setq flymake--recent-changes nil)
+               (run-hook-wrapped
+                'flymake-diagnostic-functions
+                (lambda (backend)
+                  (flymake--with-backend-state backend state
+                    (setf (flymake--state-reported-p state) nil))))
                (run-hook-wrapped
                 'flymake-diagnostic-functions
                 (lambda (backend)
@@ -1239,7 +1289,7 @@ special *Flymake log* buffer."  :group 'flymake :lighter
     ;; existing diagnostic overlays, lest we forget them by blindly
     ;; reinitializing `flymake--state' in the next line.
     ;; See https://github.com/joaotavora/eglot/issues/223.
-    (mapc #'flymake--delete-overlay (flymake--overlays))
+    (mapc #'flymake--delete-overlay (flymake--really-all-overlays))
     (setq flymake--state (make-hash-table))
     (setq flymake--recent-changes nil)
 
@@ -1286,13 +1336,13 @@ special *Flymake log* buffer."  :group 'flymake :lighter
     (when flymake-timer
       (cancel-timer flymake-timer)
       (setq flymake-timer nil))
-    (mapc #'flymake--delete-overlay (flymake--overlays))
+    (mapc #'flymake--delete-overlay (flymake--really-all-overlays))
     (when flymake--state
       (maphash (lambda (_backend state)
                  (flymake--clear-foreign-diags state))
-               flymake--state)))
+               flymake--state))))
    ;; turning Flymake on or off has consequences for listings
-   (flymake--update-diagnostics-listings (current-buffer))))
+   (flymake--update-diagnostics-listings (current-buffer)))
 
 (defun flymake--schedule-timer-maybe ()
   "(Re)schedule an idle timer for checking the buffer.
@@ -1346,8 +1396,10 @@ START and STOP and LEN are as in 
`after-change-functions'."
       (when-let* ((probe (search-forward "\n" stop t))
                   (eolovs (cl-remove-if-not
                            (lambda (o)
-                             (let ((reg (overlay-get o 
'flymake-eol-source-region)))
-                               (and reg (< (car reg) (1- probe)))))
+                             (let ((lbound
+                                    (cl-loop for s in (overlay-get o 
'flymake-eol-source-overlays)
+                                             minimizing (overlay-start s))))
+                               (and lbound (< lbound (1- probe)))))
                            (overlays-at (line-end-position)))))
         (goto-char start)
         (let ((newend (line-end-position)))
@@ -1396,20 +1448,17 @@ default) no filter is applied."
                          '(:error :warning))
                      t))
   (let* ((n (or n 1))
-         (ovs (flymake--overlays :filter
-                                 (lambda (ov)
-                                   (let ((diag (overlay-get
-                                                ov
-                                                'flymake-diagnostic)))
-                                     (and diag
-                                          (or
-                                           (not filter)
-                                           (cl-find
-                                            (flymake--severity
-                                             (flymake-diagnostic-type diag))
-                                            filter :key 
#'flymake--severity)))))
-                                 :compare (if (cl-plusp n) #'< #'>)
-                                 :key #'overlay-start))
+         (ovs (cl-loop
+               for o in (overlays-in (point-min) (point-max))
+               for diag = (overlay-get o 'flymake-diagnostic)
+               when (and diag (or (not filter) (cl-find
+                                                (flymake--severity
+                                                 (flymake-diagnostic-type 
diag))
+                                                filter :key 
#'flymake--severity)))
+               collect o into retval
+               finally (cl-return
+                        (cl-sort retval (if (cl-plusp n) #'< #'>)
+                                 :key #'overlay-start))))
          (tail (cl-member-if (lambda (ov)
                                (if (cl-plusp n)
                                    (> (overlay-start ov)
@@ -1501,7 +1550,7 @@ The counters are only placed if some Flymake backend 
initialized
 correctly.")
 
 (defvar flymake-mode-line-error-counter
-  `(:eval (flymake--mode-line-counter :error t)))
+  `(:eval (flymake--mode-line-counter :error)))
 (defvar flymake-mode-line-warning-counter
   `(:eval (flymake--mode-line-counter :warning)))
 (defvar flymake-mode-line-note-counter
@@ -1598,9 +1647,8 @@ correctly.")
                 #'flymake--mode-line-counter-scroll-next)
     map))
 
-(defun flymake--mode-line-counter (type &optional no-space)
-  "Compute number of diagnostics in buffer with TYPE's severity.
-TYPE is usually keyword `:error', `:warning' or `:note'."
+(defun flymake--mode-line-counter-1 (type)
+  "Helper for `flymake--mode-line-counter'."
   (let ((count 0)
         (face (flymake--lookup-type-property type
                                              'mode-line-face
@@ -1617,7 +1665,7 @@ TYPE is usually keyword `:error', `:warning' or `:note'."
                          (warning-numeric-level
                           flymake-suppress-zero-counters)))
                     (t t)))
-      `(,(if no-space "" '(:propertize " "))
+      `(,(if (eq type :error) "" '(:propertize " "))
         (:propertize
          ,(format "%d" count)
          face ,face
@@ -1631,6 +1679,15 @@ TYPE is usually keyword `:error', `:warning' or `:note'."
          flymake--diagnostic-type ,type
          keymap ,flymake--mode-line-counter-map)))))
 
+(defun flymake--mode-line-counter (type)
+  "Compute number of diagnostics in buffer with TYPE's severity.
+TYPE is usually keyword `:error', `:warning' or `:note'."
+  (let ((probe (alist-get type flymake--mode-line-counter-cache 'none)))
+    (if (eq probe 'none)
+        (setf (alist-get type flymake--mode-line-counter-cache)
+            (flymake--mode-line-counter-1 type))
+      probe)))
+
 ;;; Per-buffer diagnostic listing
 
 (defvar-local flymake--diagnostics-buffer-source nil)
@@ -1920,6 +1977,4 @@ some of this variable's contents the diagnostic 
listings.")
 
 (provide 'flymake)
 
-(require 'flymake-proc)
-
 ;;; flymake.el ends here
diff --git a/lisp/progmodes/gdb-mi.el b/lisp/progmodes/gdb-mi.el
index a1091de43e9..bc0070d2630 100644
--- a/lisp/progmodes/gdb-mi.el
+++ b/lisp/progmodes/gdb-mi.el
@@ -222,7 +222,6 @@ address for root variables.")
 Only used for files that Emacs can't find.")
 (defvar gdb-active-process nil
   "GUD tooltips display variable values when t, and macro definitions 
otherwise.")
-(defvar gdb-error "Non-nil when GDB is reporting an error.")
 (defvar gdb-macro-info nil
   "Non-nil if GDB knows that the inferior includes preprocessor macro info.")
 (defvar gdb-register-names nil "List of register names.")
@@ -717,6 +716,13 @@ that GDB starts to reuse existing source windows."
   :group 'gdb
   :version "28.1")
 
+(defcustom gdb-display-io-buffer t
+  "When non-nil, display the separate `gdb-inferior-io' buffer.
+Otherwise, send program output to the GDB buffer."
+  :type 'boolean
+  :group 'gdb-buffers
+  :version "30.1")
+
 (defvar gdbmi-debug-mode nil
   "When non-nil, print the messages sent/received from GDB/MI in *Messages*.")
 
@@ -928,7 +934,7 @@ detailed description of this mode.
           (setq-local comint-input-ring-file-name hfile))
       (comint-read-input-ring t)))
   (gud-def gud-tbreak "tbreak %f:%l" "\C-t"
-          "Set temporary breakpoint at current line.")
+          "Set temporary breakpoint at current line." t)
   (gud-def gud-jump
           (progn (gud-call "tbreak %f:%l" arg) (gud-call "jump %f:%l"))
           "\C-j" "Set execution address to current line.")
@@ -959,7 +965,7 @@ detailed description of this mode.
           "Finish executing current function.")
   (gud-def gud-run    "-exec-run"
            nil
-           "Run the program.")
+           "Run the program." t)
 
   (gud-def gud-break (if (not (string-match "Disassembly" mode-name))
                         (gud-call "break %f:%l" arg)
@@ -967,7 +973,7 @@ detailed description of this mode.
                         (beginning-of-line)
                         (forward-char 2)
                         (gud-call "break *%a" arg)))
-          "\C-b" "Set breakpoint at current line or address.")
+          "\C-b" "Set breakpoint at current line or address." t)
 
   (gud-def gud-remove (if (not (string-match "Disassembly" mode-name))
                          (gud-call "clear %f:%l" arg)
@@ -975,7 +981,7 @@ detailed description of this mode.
                          (beginning-of-line)
                          (forward-char 2)
                          (gud-call "clear *%a" arg)))
-          "\C-d" "Remove breakpoint at current line or address.")
+          "\C-d" "Remove breakpoint at current line or address." t)
 
   ;; -exec-until doesn't support --all yet
   (gud-def gud-until  (if (not (string-match "Disassembly" mode-name))
@@ -1044,6 +1050,7 @@ detailed description of this mode.
 
   (setq gdb-first-prompt t)
   (setq gud-running nil)
+  (setq gud-async-running nil)
 
   (gdb-update)
 
@@ -1098,9 +1105,10 @@ detailed description of this mode.
                      (if gdb-debuginfod-enable "on" "off"))
              'gdb-debuginfod-message)
 
-  (gdb-get-buffer-create 'gdb-inferior-io)
-  (gdb-clear-inferior-io)
-  (gdb-inferior-io--init-proc (get-process "gdb-inferior"))
+  (when gdb-display-io-buffer
+    (gdb-get-buffer-create 'gdb-inferior-io)
+    (gdb-clear-inferior-io)
+    (gdb-inferior-io--init-proc (get-process "gdb-inferior")))
 
   (when (eq system-type 'windows-nt)
     ;; Don't create a separate console window for the debuggee.
@@ -2671,9 +2679,11 @@ Sets `gdb-thread-number' to new id."
   ;; Set `gdb-non-stop' when `gdb-last-command' is a CLI background
   ;; running command e.g. "run &", attach &" or a MI command
   ;; e.g. "-exec-run" or "-exec-attach".
-  (when (or (string-match "&\s*$" gdb-last-command)
-            (string-match "^-" gdb-last-command))
-    (gdb-try-check-target-async-support))
+  (if (or (string-match "&\s*$" gdb-last-command)
+          (string-match "^-" gdb-last-command))
+      (progn (gdb-try-check-target-async-support)
+             (setq gud-async-running t))
+    (setq gud-async-running nil))
 
   (gdb-force-mode-line-update
    (propertize gdb-inferior-status 'face font-lock-type-face))
diff --git a/lisp/progmodes/gud.el b/lisp/progmodes/gud.el
index 09860a4cbde..3cc63aab84f 100644
--- a/lisp/progmodes/gud.el
+++ b/lisp/progmodes/gud.el
@@ -128,6 +128,10 @@ If SOFT is non-nil, returns nil if the symbol doesn't 
already exist."
   "Non-nil if debugged program is running.
 Used to gray out relevant toolbar icons.")
 
+(defvar gud-async-running nil
+  "Non-nil if debugged program is running in async mode.
+Check it when `gud-running' is t")
+
 (defvar gud-target-name "--unknown--"
   "The apparent name of the program being debugged in a gud buffer.")
 
@@ -261,13 +265,13 @@ Used to gray out relevant toolbar icons.")
      :visible (memq gud-minor-mode
                    '(gdbmi gdb guiler dbx xdb jdb pdb))]
     ["Set Breakpoint" gud-break
-     :enable (not gud-running)
+     :enable (or (not gud-running) gud-async-running)
      :visible (gud-tool-bar-item-visible-no-fringe)]
     ["Temporary Breakpoint" gud-tbreak
-     :enable (not gud-running)
+     :enable (or (not gud-running) gud-async-running)
      :visible (memq gud-minor-mode '(gdbmi gdb sdb xdb))]
     ["Remove Breakpoint" gud-remove
-     :enable (not gud-running)
+     :enable (or (not gud-running) gud-async-running)
      :visible (gud-tool-bar-item-visible-no-fringe)]
     ["Continue to selection" gud-until
      :enable (not gud-running)
@@ -283,7 +287,7 @@ Used to gray out relevant toolbar icons.")
      :visible (and (eq gud-minor-mode 'gdbmi)
                    (gdb-show-run-p))]
     ["Run" gud-run
-     :enable (not gud-running)
+     :enable (or (not gud-running) gud-async-running)
      :visible (or (memq gud-minor-mode '(gdb dbx jdb))
                  (and (eq gud-minor-mode 'gdbmi)
                       (or (not (gdb-show-run-p))
@@ -403,13 +407,15 @@ Uses `gud-<MINOR-MODE>-directories' to find the source 
files."
 ;; Of course you may use `gud-def' with any other debugger command, including
 ;; user defined ones.
 
-;; A macro call like (gud-def FUNC CMD KEY DOC) expands to a form
+;; A macro call like (gud-def FUNC CMD KEY DOC ASYNC-OK) expands to a form
 ;; which defines FUNC to send the command CMD to the debugger, gives
 ;; it the docstring DOC, and binds that function to KEY in the GUD
-;; major mode.  The function is also bound in the global keymap with the
+;; major mode. The FUNC still sends CMD when both ASYNC-OK and
+;; `gud-async-running' are t even `gud-running' is t.
+;; The function is also bound in the global keymap with the
 ;; GUD prefix.
 
-(defmacro gud-def (func cmd key &optional doc)
+(defmacro gud-def (func cmd key &optional doc async-ok)
   "Define FUNC to be a command sending CMD and bound to KEY, with
 optional doc string DOC.  Certain %-escapes in the string arguments
 are interpreted specially if present.  These are:
@@ -434,7 +440,7 @@ we're in the GUD buffer)."
      (defalias ',func (lambda (arg)
        ,@(if doc (list doc))
        (interactive "p")
-       (if (not gud-running)
+       (if (or (not gud-running) (and ,async-ok gud-async-running))
         ,(if (stringp cmd)
              `(gud-call ,cmd arg)
            ;; Unused lexical warning if cmd does not use "arg".
diff --git a/lisp/progmodes/heex-ts-mode.el b/lisp/progmodes/heex-ts-mode.el
index 68a537b9229..5237c767330 100644
--- a/lisp/progmodes/heex-ts-mode.el
+++ b/lisp/progmodes/heex-ts-mode.el
@@ -149,8 +149,9 @@ With ARG, do it many times.  Negative ARG means move 
backward."
     (treesit-parser-create 'heex)
 
     ;; Comments
-    (setq-local treesit-text-type-regexp
-                (regexp-opt '("comment" "text")))
+    (setq-local treesit-thing-settings
+                `((heex
+                   (text ,(regexp-opt '("comment" "text"))))))
 
     (setq-local forward-sexp-function #'heex-ts--forward-sexp)
 
diff --git a/lisp/progmodes/hideshow.el b/lisp/progmodes/hideshow.el
index b878986d7a4..78e39fad740 100644
--- a/lisp/progmodes/hideshow.el
+++ b/lisp/progmodes/hideshow.el
@@ -264,6 +264,7 @@ This has effect only if `search-invisible' is set to 
`open'."
     (java-ts-mode "{" "}" "/[*/]" nil nil)
     (js-mode "{" "}" "/[*/]" nil)
     (js-ts-mode "{" "}" "/[*/]" nil)
+    (lua-ts-mode "{\\|\\[\\[" "}\\|\\]\\]" "--" nil)
     (mhtml-mode "{\\|<[^/>]*?" "}\\|</[^/>]*[^/]>" "<!--" mhtml-forward nil)
     ;; Add more support here.
     ))
diff --git a/lisp/progmodes/idlwave.el b/lisp/progmodes/idlwave.el
index 3c00046a26a..d9eccacc48b 100644
--- a/lisp/progmodes/idlwave.el
+++ b/lisp/progmodes/idlwave.el
@@ -309,7 +309,7 @@ beginning with a \";\".  Expressions for comments at the 
beginning of
 the line should begin with \"^\"."
   :group 'idlwave-code-formatting
   :type '(choice (const :tag "Any line beginning with `;'" nil)
-                'regexp))
+                 regexp))
 
 (defcustom idlwave-code-comment ";;[^;]"
   "A comment that starts with this regular expression on a line by
@@ -8750,11 +8750,12 @@ This expects NAME TYPE IDLWAVE-TWIN-CLASS to be bound 
to the right values."
 
 (defun idlwave-count-eq (elt list)
   "How often is ELT in LIST?"
-  (length (delq nil (mapcar (lambda (x) (eq x elt)) list))))
+  (declare (obsolete nil "30.1"))
+  (seq-count (lambda (x) (eq x elt)) list))
 
 (defun idlwave-count-memq (elt alist)
   "How often is ELT a key in ALIST?"
-  (length (delq nil (mapcar (lambda (x) (eq (car x) elt)) alist))))
+  (seq-count (lambda (x) (eq (car x) elt)) alist))
 
 (defun idlwave-syslib-p (file)
   "Non-nil if FILE is in the system library."
diff --git a/lisp/progmodes/java-ts-mode.el b/lisp/progmodes/java-ts-mode.el
index 121c8550be0..6ca7a473411 100644
--- a/lisp/progmodes/java-ts-mode.el
+++ b/lisp/progmodes/java-ts-mode.el
@@ -99,6 +99,8 @@
      ((parent-is "field_declaration") parent-bol java-ts-mode-indent-offset)
      ((parent-is "return_statement") parent-bol java-ts-mode-indent-offset)
      ((parent-is "variable_declarator") parent-bol java-ts-mode-indent-offset)
+     ((match ">" "type_arguments") parent-bol 0)
+     ((parent-is "type_arguments") parent-bol java-ts-mode-indent-offset)
      ((parent-is "method_invocation") parent-bol java-ts-mode-indent-offset)
      ((parent-is "switch_rule") parent-bol java-ts-mode-indent-offset)
      ((parent-is "switch_label") parent-bol java-ts-mode-indent-offset)
@@ -303,6 +305,13 @@ Return nil if there is no name or if NODE is not a defun 
node."
       (treesit-node-child-by-field-name node "name")
       t))))
 
+
+(defvar java-ts-mode--feature-list
+  '(( comment definition )
+    ( constant keyword string type)
+    ( annotation expression literal)
+    ( bracket delimiter operator)))
+
 ;;;###autoload
 (define-derived-mode java-ts-mode prog-mode "Java"
   "Major mode for editing Java, powered by tree-sitter."
@@ -317,11 +326,6 @@ Return nil if there is no name or if NODE is not a defun 
node."
   ;; Comments.
   (c-ts-common-comment-setup)
 
-  (setq-local treesit-text-type-regexp
-              (regexp-opt '("line_comment"
-                            "block_comment"
-                            "text_block")))
-
   ;; Indent.
   (setq-local c-ts-common-indent-type-regexp-alist
               `((block . ,(rx (or "class_body"
@@ -360,36 +364,34 @@ Return nil if there is no name or if NODE is not a defun 
node."
                             "constructor_declaration")))
   (setq-local treesit-defun-name-function #'java-ts-mode--defun-name)
 
-  (setq-local treesit-sentence-type-regexp
-              (regexp-opt '("statement"
-                            "local_variable_declaration"
-                            "field_declaration"
-                            "module_declaration"
-                            "package_declaration"
-                            "import_declaration")))
-
-  (setq-local treesit-sexp-type-regexp
-              (regexp-opt '("annotation"
-                            "parenthesized_expression"
-                            "argument_list"
-                            "identifier"
-                            "modifiers"
-                            "block"
-                            "body"
-                            "literal"
-                            "access"
-                            "reference"
-                            "_type"
-                            "true"
-                            "false")))
+  (setq-local treesit-thing-settings
+              `((java
+                (sexp ,(rx (or "annotation"
+                               "parenthesized_expression"
+                               "argument_list"
+                               "identifier"
+                               "modifiers"
+                               "block"
+                               "body"
+                               "literal"
+                               "access"
+                               "reference"
+                               "_type"
+                               "true"
+                               "false")))
+                (sentence ,(rx (or "statement"
+                                   "local_variable_declaration"
+                                   "field_declaration"
+                                   "module_declaration"
+                                   "package_declaration"
+                                   "import_declaration")))
+                (text ,(regexp-opt '("line_comment"
+                                     "block_comment"
+                                     "text_block"))))))
 
   ;; Font-lock.
   (setq-local treesit-font-lock-settings java-ts-mode--font-lock-settings)
-  (setq-local treesit-font-lock-feature-list
-              '(( comment definition )
-                ( constant keyword string type)
-                ( annotation expression literal)
-                ( bracket delimiter operator)))
+  (setq-local treesit-font-lock-feature-list java-ts-mode--feature-list)
 
   ;; Imenu.
   (setq-local treesit-simple-imenu-settings
diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el
index 1d89b35aa2d..9ca6bee8454 100644
--- a/lisp/progmodes/js.el
+++ b/lisp/progmodes/js.el
@@ -3831,7 +3831,7 @@ Currently there are `js-mode' and `js-ts-mode'."
     "jsx_element"
     "jsx_self_closing_element")
   "Nodes that designate sentences in JavaScript.
-See `treesit-sentence-type-regexp' for more information.")
+See `treesit-thing-settings' for more information.")
 
 (defvar js--treesit-sexp-nodes
   '("expression"
@@ -3873,15 +3873,12 @@ See `treesit-sexp-type-regexp' for more information.")
     (c-ts-common-comment-setup)
     (setq-local comment-multi-line t)
 
-    (setq-local treesit-text-type-regexp
-                (regexp-opt '("comment"
-                              "template_string")))
-
     ;; Electric-indent.
     (setq-local electric-indent-chars
                 (append "{}():;,<>/" electric-indent-chars)) ;FIXME: js2-mode 
adds "[]*".
     (setq-local electric-layout-rules
                '((?\; . after) (?\{ . after) (?\} . before)))
+    (setq-local syntax-propertize-function #'js-ts--syntax-propertize)
 
     ;; Tree-sitter setup.
     (treesit-parser-create 'javascript)
@@ -3896,11 +3893,12 @@ See `treesit-sexp-type-regexp' for more information.")
                         "lexical_declaration")))
     (setq-local treesit-defun-name-function #'js--treesit-defun-name)
 
-    (setq-local treesit-sentence-type-regexp
-                (regexp-opt js--treesit-sentence-nodes))
-
-    (setq-local treesit-sexp-type-regexp
-                (regexp-opt js--treesit-sexp-nodes))
+    (setq-local treesit-thing-settings
+                `((javascript
+                   (sexp ,(regexp-opt js--treesit-sexp-nodes))
+                   (sentence ,(regexp-opt js--treesit-sentence-nodes))
+                   (text ,(regexp-opt '("comment"
+                                        "template_string"))))))
 
     ;; Fontification.
     (setq-local treesit-font-lock-settings js--treesit-font-lock-settings)
@@ -3924,6 +3922,31 @@ See `treesit-sexp-type-regexp' for more information.")
     (add-to-list 'auto-mode-alist
                  '("\\(\\.js[mx]\\|\\.har\\)\\'" . js-ts-mode))))
 
+(defvar js-ts--s-p-query
+  (when (treesit-available-p)
+    (treesit-query-compile 'javascript
+                           '(((regex pattern: (regex_pattern) @regexp))
+                             ((variable_declarator value: (jsx_element) @jsx))
+                             ((assignment_expression right: (jsx_element) 
@jsx))
+                             ((arguments (jsx_element) @jsx))
+                             ((parenthesized_expression (jsx_element) @jsx))
+                             ((return_statement (jsx_element) @jsx))))))
+
+(defun js-ts--syntax-propertize (beg end)
+  (let ((captures (treesit-query-capture 'javascript js-ts--s-p-query beg 
end)))
+    (pcase-dolist (`(,name . ,node) captures)
+      (let* ((ns (treesit-node-start node))
+             (ne (treesit-node-end node))
+             (syntax (pcase-exhaustive name
+                       ('regexp
+                        (cl-decf ns)
+                        (cl-incf ne)
+                        (string-to-syntax "\"/"))
+                       ('jsx
+                        (string-to-syntax "|")))))
+        (put-text-property ns (1+ ns) 'syntax-table syntax)
+        (put-text-property (1- ne) ne 'syntax-table syntax)))))
+
 ;;;###autoload
 (define-derived-mode js-json-mode js-mode "JSON"
   (setq-local js-enabled-frameworks nil)
diff --git a/lisp/progmodes/json-ts-mode.el b/lisp/progmodes/json-ts-mode.el
index f56d118c0fe..78117356821 100644
--- a/lisp/progmodes/json-ts-mode.el
+++ b/lisp/progmodes/json-ts-mode.el
@@ -147,7 +147,9 @@ Return nil if there is no name or if NODE is not a defun 
node."
               (rx (or "pair" "object")))
   (setq-local treesit-defun-name-function #'json-ts-mode--defun-name)
 
-  (setq-local treesit-sentence-type-regexp "pair")
+  (setq-local treesit-thing-settings
+              `((json
+                 (sentence "pair"))))
 
   ;; Font-lock.
   (setq-local treesit-font-lock-settings json-ts-mode--font-lock-settings)
diff --git a/lisp/progmodes/lua-ts-mode.el b/lisp/progmodes/lua-ts-mode.el
new file mode 100644
index 00000000000..030a3585158
--- /dev/null
+++ b/lisp/progmodes/lua-ts-mode.el
@@ -0,0 +1,455 @@
+;;; lua-ts-mode.el --- Major mode for editing Lua files -*- lexical-binding: t 
-*-
+
+;; Copyright (C) 2023 Free Software Foundation, Inc.
+
+;; Author: John Muhl <jm@pub.pink>
+;; Created: June 27, 2023
+;; Keywords: lua languages tree-sitter
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+;;
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This package provides `lua-ts-mode' which is a major mode for Lua
+;; files that uses Tree Sitter to parse the language.
+;;
+;; This package is compatible with and tested against the grammar
+;; for Lua found at https://github.com/MunifTanjim/tree-sitter-lua
+
+;;; Code:
+
+(require 'comint)
+(require 'treesit)
+
+(eval-when-compile
+  (require 'cl-lib)
+  (require 'rx))
+
+(declare-function treesit-node-child-by-field-name "treesit.c")
+(declare-function treesit-node-type "treesit.c")
+(declare-function treesit-parser-create "treesit.c")
+(declare-function treesit-search-subtree "treesit.c")
+
+(defgroup lua-ts nil
+  "Major mode for editing Lua files."
+  :prefix "lua-ts-"
+  :group 'languages)
+
+(defcustom lua-ts-indent-offset 4
+  "Number of spaces for each indentation step in `lua-ts-mode'."
+  :type 'natnum
+  :safe 'natnump
+  :group 'lua-ts
+  :version "30.1")
+
+(defcustom lua-ts-luacheck-program "luacheck"
+  "Location of the Luacheck program."
+  :type '(choice (const nil) string)
+  :group 'lua-ts
+  :version "30.1")
+
+(defcustom lua-ts-inferior-buffer "*Lua*"
+  "Name of the inferior Lua buffer."
+  :type 'string
+  :safe 'stringp
+  :group 'lua-ts
+  :version "30.1")
+
+(defcustom lua-ts-inferior-program "lua"
+  "Program to run in the inferior Lua process."
+  :type '(choice (const nil) string)
+  :group 'lua-ts
+  :version "30.1")
+
+(defcustom lua-ts-inferior-options '("-i")
+  "Command line options for the inferior Lua process."
+  :type '(repeat string)
+  :group 'lua-ts
+  :version "30.1")
+
+(defcustom lua-ts-inferior-startfile nil
+  "File to load into the inferior Lua process at startup."
+  :type '(choice (const nil) (file :must-match t))
+  :group 'lua-ts
+  :version "30.1")
+
+(defcustom lua-ts-inferior-prompt-regexp "^>>?[[:blank:]]"
+  "Regular expression matching the prompt of the inferior Lua process."
+  :type 'regexp
+  :group 'lua-ts
+  :version "30.1")
+
+(defvar lua-ts--builtins
+  '("assert" "bit32" "collectgarbage" "coroutine" "debug" "dofile"
+    "error" "getmetatable" "io" "ipairs" "load" "loadfile"
+    "math" "next" "os" "package" "pairs" "pcall" "print"
+    "rawequal" "rawget" "rawlen" "rawset" "require" "select"
+    "setmetatable" "string" "table" "tonumber" "tostring"
+    "type" "utf8" "warn" "xpcall" "_G" "_VERSION"
+    ;; methods for file handlers
+    "close" "flush" "lines" "read" "seek" "setvbuf" "write")
+  "Lua built-in functions for tree-sitter font-locking.")
+
+(defvar lua-ts--font-lock-settings
+  (treesit-font-lock-rules
+   :language 'lua
+   :feature 'bracket
+   '(["(" ")" "[" "]" "{" "}"] @font-lock-bracket-face)
+
+   :language 'lua
+   :feature 'delimiter
+   '(["," ";"] @font-lock-delimiter-face)
+
+   :language 'lua
+   :feature 'escape
+   '((escape_sequence) @font-lock-escape-face)
+
+   :language 'lua
+   :feature 'constant
+   '((variable_list
+      attribute: (attribute (["<" ">"] (identifier))))
+     @font-lock-constant-face
+     (goto_statement (identifier) @font-lock-constant-face)
+     (label_statement) @font-lock-constant-face)
+
+   :language 'lua
+   :feature 'operator
+   '(["and" "not" "or" "+" "-" "*" "/" "%" "^"
+      "#" "==" "~=" "<=" ">=" "<" ">" "=" "&"
+      "~" "|" "<<" ">>" "//" ".."]
+     @font-lock-operator-face
+     (vararg_expression) @font-lock-operator-face)
+
+   :language 'lua
+   :feature 'builtin
+   `(((identifier) @font-lock-builtin-face
+      (:match ,(regexp-opt lua-ts--builtins 'symbols)
+              @font-lock-builtin-face)))
+
+   :language 'lua
+   :feature 'function
+   '((function_call name: (identifier) @font-lock-function-call-face)
+     (function_call
+      name: (method_index_expression
+             method: (identifier) @font-lock-function-call-face))
+     (function_call
+      name: (dot_index_expression (identifier) @font-lock-function-call-face)))
+
+   :language 'lua
+   :feature 'punctuation
+   '(["." ":"] @font-lock-punctuation-face)
+
+   :language 'lua
+   :feature 'variable
+   '((function_call
+      arguments: (arguments (identifier))
+      @font-lock-variable-use-face)
+     (function_call
+      name: (method_index_expression
+             table: (identifier) @font-lock-variable-use-face)))
+
+   :language 'lua
+   :feature 'number
+   '((number) @font-lock-number-face)
+
+   :language 'lua
+   :feature 'keyword
+   '((break_statement) @font-lock-keyword-face
+     (true) @font-lock-constant-face
+     (false) @font-lock-constant-face
+     (nil) @font-lock-constant-face
+     ["and" "do" "else" "elseif" "end" "for" "function"
+      "goto" "if" "in" "local" "not" "or" "repeat"
+      "return" "then" "until" "while"]
+     @font-lock-keyword-face)
+
+   :language 'lua
+   :feature 'string
+   '((string) @font-lock-string-face)
+
+   :language 'lua
+   :feature 'comment
+   '((comment) @font-lock-comment-face
+     (hash_bang_line) @font-lock-comment-face)
+
+   :language 'lua
+   :feature 'definition
+   '((function_declaration
+      name: (identifier) @font-lock-function-name-face)
+     (assignment_statement
+      (variable_list name: [(identifier)]) @font-lock-function-name-face
+      (expression_list value: (function_definition)))
+     (table_constructor
+      (field
+        name: (identifier) @font-lock-function-name-face
+        value: (function_definition)))
+     (function_declaration
+      name: (dot_index_expression (identifier) @font-lock-function-name-face))
+     (function_declaration
+      name: (method_index_expression (identifier) 
@font-lock-function-name-face))
+     (function_declaration
+      (method_index_expression
+       (dot_index_expression
+        table: (identifier) @font-lock-function-name-face
+        field: (identifier) @font-lock-property-name-face
+        )))
+     (parameters
+      name: (identifier) @font-lock-variable-name-face)
+     (for_numeric_clause name: (identifier) @font-lock-variable-name-face))
+
+   :language 'lua
+   :feature 'property
+   '((field name: (identifier) @font-lock-property-name-face)
+     (dot_index_expression
+      field: (identifier) @font-lock-property-use-face))
+
+   :language 'lua
+   :feature 'assignment
+   '((variable_list
+      [(identifier)
+       (bracket_index_expression)]
+      @font-lock-variable-name-face)
+     (variable_list
+      (dot_index_expression
+       table: (identifier))
+      @font-lock-variable-name-face))
+
+   :language 'lua
+   :feature 'error
+   :override t
+   '((ERROR) @font-lock-warning-face))
+  "Tree-sitter font-lock settings for `lua-ts-mode'.")
+
+(defvar lua-ts--simple-indent-rules
+  `((lua
+     ((parent-is "chunk") column-0 0)
+     ((node-is "comment_end") column-0 0)
+     ((parent-is "block") parent-bol 0)
+     ((node-is "}") parent-bol 0)
+     ((node-is ")") parent-bol 0)
+     ((node-is "else_statement") parent-bol 0)
+     ((node-is "elseif_statement") parent-bol 0)
+     ((node-is "end") parent-bol 0)
+     ((node-is "until") parent-bol 0)
+     ((parent-is "for_statement") parent-bol lua-ts-indent-offset)
+     ((parent-is "function_declaration") parent-bol lua-ts-indent-offset)
+     ((parent-is "function_definition") parent-bol lua-ts-indent-offset)
+     ((parent-is "if_statement") parent-bol lua-ts-indent-offset)
+     ((parent-is "else_statement") parent-bol lua-ts-indent-offset)
+     ((parent-is "repeat_statement") parent-bol lua-ts-indent-offset)
+     ((parent-is "while_statement") parent-bol lua-ts-indent-offset)
+     ((parent-is "table_constructor") parent-bol lua-ts-indent-offset)
+     ((parent-is "arguments") parent-bol lua-ts-indent-offset)
+     ((parent-is "parameters") parent-bol lua-ts-indent-offset)
+     ((parent-is "ERROR") no-indent 0))))
+
+(defvar lua-ts--syntax-table
+  (let ((table (make-syntax-table)))
+    (modify-syntax-entry ?+  "."    table)
+    (modify-syntax-entry ?-  ". 12" table)
+    (modify-syntax-entry ?=  "."    table)
+    (modify-syntax-entry ?%  "."    table)
+    (modify-syntax-entry ?^  "."    table)
+    (modify-syntax-entry ?~  "."    table)
+    (modify-syntax-entry ?<  "."    table)
+    (modify-syntax-entry ?>  "."    table)
+    (modify-syntax-entry ?/  "."    table)
+    (modify-syntax-entry ?*  "."    table)
+    (modify-syntax-entry ?\n ">"    table)
+    (modify-syntax-entry ?\' "\""   table)
+    (modify-syntax-entry ?\" "\""   table)
+    table)
+  "Syntax table for `lua-ts-mode'.")
+
+(defun lua-ts--defun-name-function (node)
+  "Return the defun name of NODE.
+Return nil if there is no name or if NODE is not a defun node."
+  (let ((child (treesit-node-child-by-field-name node "name")))
+    (pcase (treesit-node-type node)
+      ((or "function_declaration" "function_definition")
+       (treesit-node-text child t))
+      ("variable_declaration"
+       (if child
+           (treesit-node-text child t)
+         (treesit-node-text
+          (treesit-node-child-by-field-name
+           (treesit-search-subtree node "assignment_statement" nil nil 1)
+           "name"))))
+      ("field"
+       (and (treesit-search-subtree node "function_definition" nil nil 1)
+            (treesit-node-text child t))))))
+
+(defvar-local lua-ts--flymake-process nil)
+
+(defun lua-ts-flymake-luacheck (report-fn &rest _args)
+  "Luacheck backend for Flymake.
+Calls REPORT-FN directly."
+  (when (process-live-p lua-ts--flymake-process)
+    (kill-process lua-ts--flymake-process))
+  (let ((source (current-buffer)))
+    (save-restriction
+      (widen)
+      (setq lua-ts--flymake-process
+            (make-process
+             :name "lua-ts-flymake-luacheck"
+             :noquery t
+             :connection-type 'pipe
+             :buffer (generate-new-buffer " *lua-ts-flymake-luacheck*")
+             :command `(,lua-ts-luacheck-program
+                        "--codes" "--ranges" "--formatter" "plain" "-")
+             :sentinel
+             (lambda (proc _event)
+               (when (eq 'exit (process-status proc))
+                 (unwind-protect
+                     (if (with-current-buffer source
+                           (eq proc lua-ts--flymake-process))
+                         (with-current-buffer (process-buffer proc)
+                           (goto-char (point-min))
+                           (cl-loop
+                            while (search-forward-regexp
+                                   (rx (seq bol
+                                            (0+ alnum) ":"
+                                            (group (1+ digit)) ":"
+                                            (group (1+ digit)) "-"
+                                            (group (1+ digit)) ": "
+                                            (group (0+ nonl))
+                                            eol))
+                                   nil t)
+                            for line = (string-to-number (match-string 1))
+                            for beg = (string-to-number (match-string 2))
+                            for end = (string-to-number (match-string 3))
+                            for msg = (match-string 4)
+                            for type = (if (string-match "^(W" msg)
+                                           :warning
+                                         :error)
+                            when (and beg end)
+                            collect (flymake-make-diagnostic source
+                                                             (cons line beg)
+                                                             (cons line (1+ 
end))
+                                                             type
+                                                             msg)
+                            into diags
+                            finally (funcall report-fn diags)))
+                       (flymake-log :warning "Canceling obsolete check %s" 
proc))
+                   (kill-buffer (process-buffer proc)))))))
+      (process-send-region lua-ts--flymake-process (point-min) (point-max))
+      (process-send-eof lua-ts--flymake-process))))
+
+;;;###autoload
+(defun lua-ts-inferior-lua ()
+  "Run a Lua interpreter in an inferior process."
+  (interactive)
+  (let* ((buffer lua-ts-inferior-buffer)
+         (name (string-replace "*" "" buffer))
+         (program lua-ts-inferior-program)
+         (prompt-regexp lua-ts-inferior-prompt-regexp)
+         (switches lua-ts-inferior-options)
+         (startfile lua-ts-inferior-startfile))
+    (unless (comint-check-proc buffer)
+      (set-buffer (apply (function make-comint) name program startfile 
switches))
+      (setq-local comint-input-ignoredups t
+                  comint-prompt-read-only t
+                  comint-prompt-regexp prompt-regexp
+                  comint-use-prompt-regexp t))
+    (select-window (display-buffer buffer '((display-buffer-reuse-window
+                                             display-buffer-pop-up-frame)
+                                            (reusable-frames . t))))))
+
+;;;###autoload
+(define-derived-mode lua-ts-mode prog-mode "Lua"
+  "Major mode for editing Lua files, powered by tree-sitter."
+  :syntax-table lua-ts--syntax-table
+
+  (when (treesit-ready-p 'lua)
+    (treesit-parser-create 'lua)
+
+    ;; Comments.
+    (setq-local comment-start "--")
+    (setq-local comment-start-skip "--\\s-*")
+    (setq-local comment-end "")
+
+    ;; Font-lock.
+    (setq-local treesit-font-lock-settings lua-ts--font-lock-settings)
+    (setq-local treesit-font-lock-feature-list
+                '((comment definition)
+                  (keyword property string)
+                  (assignment builtin constant number)
+                  (bracket
+                   delimiter
+                   escape
+                   function
+                   operator
+                   punctuation
+                   variable)))
+
+    ;; Indent.
+    (setq-local treesit-simple-indent-rules lua-ts--simple-indent-rules)
+
+    ;; Navigation.
+    (setq-local treesit-defun-name-function #'lua-ts--defun-name-function)
+    (setq-local treesit-defun-type-regexp
+                (rx (or "function_declaration" "function_definition")))
+    (setq-local treesit-thing-settings
+                `((lua
+                   (sentence ,(rx (or "do_statement"
+                                      "field"
+                                      "for_statement"
+                                      "function_call"
+                                      "if_statement"
+                                      "repeat_statement"
+                                      "return_statement"
+                                      "variable_declaration"
+                                      "while_statement")))
+                   (sexp ,(rx (or "arguments"
+                                  "block"
+                                  "parameters"
+                                  "string"
+                                  "table_constructor")))
+                   (text "comment"))))
+
+    ;; Imenu.
+    (setq-local treesit-simple-imenu-settings
+                `(("Variable" ,(rx bos "variable_declaration" eos) nil nil)
+                  ("Function" ,(rx bos
+                                   (or "function_declaration"
+                                       "function_definition"
+                                       "field")
+                                   eos)
+                   nil nil)))
+
+    ;; Which-function.
+    (setq-local which-func-functions (treesit-defun-at-point))
+
+    ;; Outline.
+    (setq-local outline-regexp
+                (rx (seq (0+ space)
+                         (or (seq "--[[" (0+ space) eol)
+                             (seq symbol-start
+                                  (or "do" "for" "if" "repeat" "while"
+                                      (seq (? (seq "local" (1+ space)))
+                                           "function"))
+                                  symbol-end)))))
+
+    (treesit-major-mode-setup))
+
+  (add-hook 'flymake-diagnostic-functions #'lua-ts-flymake-luacheck nil 
'local))
+
+(when (treesit-ready-p 'lua)
+  (add-to-list 'auto-mode-alist '("\\.lua\\'" . lua-ts-mode)))
+
+(provide 'lua-ts-mode)
+
+;;; lua-ts-mode.el ends here
diff --git a/lisp/progmodes/perl-mode.el b/lisp/progmodes/perl-mode.el
index ab6333e4b23..b8d811baf0d 100644
--- a/lisp/progmodes/perl-mode.el
+++ b/lisp/progmodes/perl-mode.el
@@ -47,10 +47,6 @@
 ;; comment; move to end of line; create an empty comment; tell you that
 ;; the line ends in a quoted string, or has a # which should be a \#.
 
-;; If your machine is slow, you may want to remove some of the bindings
-;; to perl-electric-terminator.  I changed the indenting defaults to be
-;; what Larry Wall uses in perl/lib, but left in all the options.
-
 ;; I also tuned a few things:  comments and labels starting in column
 ;; zero are left there by perl-indent-exp; perl-beginning-of-function
 ;; goes back to the first open brace/paren in column zero, the open brace
@@ -227,7 +223,10 @@
             "\\|=>"
             "\\|[?:.,;|&*=!~({[]"
             "\\|[^-+][-+]"    ;Bug#42168: `+' is intro but `++' isn't!
-            "\\|\\(^\\)\\)[ \t\n]*")))
+            "\\|\\(^\\)\\)[ \t\n]*"))
+
+  (defconst perl--format-regexp "^[ \t]*format.*=[ \t]*\\(\n\\)"
+  "Regexp to match the start of a format declaration."))
 
 (defun perl-syntax-propertize-function (start end)
   (let ((case-fold-search nil))
@@ -256,7 +255,7 @@
       ;; Handle funny names like $DB'stop.
       ("\\$ ?{?\\^?[_[:alpha:]][_[:alnum:]]*\\('\\)[_[:alpha:]]" (1 "_"))
       ;; format statements
-      ("^[ \t]*format.*=[ \t]*\\(\n\\)"
+      (perl--format-regexp
        (1 (prog1 "\"" (perl-syntax-propertize-special-constructs end))))
       ;; Propertize perl prototype chars `$%&*;+@\[]' as punctuation
       ;; in `sub' arg-specs like `sub myfun ($)' and `sub ($)'.  But
@@ -950,6 +949,17 @@ changed by, or (parse-state) if line starts in a quoted 
string."
        (goto-char (- (point-max) pos)))
     shift-amt))
 
+(defun perl--end-of-format-p ()
+  "Non-nil if point is at the end of a format declaration, skipping 
whitespace."
+  (save-excursion
+    (skip-chars-backward " \t\n")
+    (beginning-of-line)
+    (when-let ((comm (and (looking-at "^\\.$")
+                          (nth 8 (syntax-ppss)))))
+      (goto-char comm)
+      (beginning-of-line)
+      (looking-at perl--format-regexp))))
+
 (defun perl-continuation-line-p ()
   "Move to end of previous line and return non-nil if continued."
   ;; Statement level.  Is it a continuation or a new statement?
@@ -963,12 +973,13 @@ changed by, or (parse-state) if line starts in a quoted 
string."
     (beginning-of-line)
     (perl-backward-to-noncomment))
   ;; Now we get the answer.
-  (unless (memq (preceding-char) '(?\; ?\} ?\{))
+  (unless (or (memq (preceding-char) '(?\; ?\} ?\{))
+              (perl--end-of-format-p))
     (preceding-char)))
 
 (defun perl-hanging-paren-p ()
   "Non-nil if we are right after a hanging parenthesis-like char."
-  (and (looking-at "[ \t]*$")
+  (and (looking-at "[ \t]*\\(?:#.*\\)?$")
        (save-excursion
         (skip-syntax-backward " (") (not (bolp)))))
 
@@ -1003,7 +1014,9 @@ Returns (parse-state) if line starts inside a string."
           (state (syntax-ppss))
           (containing-sexp (nth 1 state))
           ;; Don't auto-indent in a quoted string or a here-document.
-          (unindentable (or (nth 3 state) (eq 2 (nth 7 state)))))
+           (unindentable (or (nth 3 state) (eq 2 (nth 7 state))))
+           (format (and (nth 3 state)
+                        (char-equal (nth 3 state) ?\n))))
       (when (and (eq t (nth 3 state))
                  (save-excursion
                    (goto-char (nth 8 state))
@@ -1013,7 +1026,7 @@ Returns (parse-state) if line starts inside a string."
         (setq unindentable nil)
         (setq containing-sexp (nth 8 state)))
       (cond
-       (unindentable 'noindent)
+       (unindentable (if format 0 'noindent))
        ((null containing-sexp)          ; Line is at top level.
         (skip-chars-forward " \t\f")
         (if (memq (following-char)
@@ -1022,7 +1035,8 @@ Returns (parse-state) if line starts inside a string."
           ;; indent a little if this is a continuation line
           (perl-backward-to-noncomment)
           (if (or (bobp)
-                  (memq (preceding-char) '(?\; ?\})))
+                  (memq (preceding-char) '(?\; ?\}))
+                  (perl--end-of-format-p))
               0 perl-continued-statement-offset)))
        ((/= (char-after containing-sexp) ?{)
         ;; line is expression, not statement:
diff --git a/lisp/progmodes/prog-mode.el b/lisp/progmodes/prog-mode.el
index 16497097061..37c54a90f42 100644
--- a/lisp/progmodes/prog-mode.el
+++ b/lisp/progmodes/prog-mode.el
@@ -37,6 +37,7 @@
 (declare-function treesit-parser-list "treesit.c")
 (declare-function treesit-node-type "treesit.c")
 (declare-function treesit-node-at "treesit.c")
+(declare-function treesit-node-match-p "treesit.c")
 
 (defgroup prog-mode nil
   "Generic programming mode, from which others derive."
@@ -160,9 +161,8 @@ or follows point."
     (let ((treesit-text-node
            (and (treesit-available-p)
                 (treesit-parser-list)
-                (string-match-p
-                 treesit-text-type-regexp
-                 (treesit-node-type (treesit-node-at (point)))))))
+                (treesit-node-match-p
+                 (treesit-node-at (point)) 'text t))))
       (if (or treesit-text-node
               (nth 8 (syntax-ppss))
               (re-search-forward "\\s-*\\s<" (line-end-position) t))
@@ -248,7 +248,10 @@ If set to the symbol `right-edge', also unprettify if point
 is immediately after the symbol.  The prettification will be
 reapplied as soon as point moves away from the symbol.  If
 set to nil, the prettification persists even when point is
-on the symbol."
+on the symbol.
+
+This will only have an effect if it is set to a non-nil value
+before `prettify-symbols-mode' is activated."
   :version "25.1"
   :type '(choice (const :tag "Never unprettify" nil)
                  (const :tag "Unprettify when point is inside" t)
diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el
index 7aaf7a9f9fb..2e6ae89a443 100644
--- a/lisp/progmodes/project.el
+++ b/lisp/progmodes/project.el
@@ -1,7 +1,7 @@
 ;;; project.el --- Operations on the current project  -*- lexical-binding: t; 
-*-
 
 ;; Copyright (C) 2015-2023 Free Software Foundation, Inc.
-;; Version: 0.9.8
+;; Version: 0.10.0
 ;; Package-Requires: ((emacs "26.1") (xref "1.4.0"))
 
 ;; This is a GNU ELPA :core package.  Avoid using functionality that
@@ -410,7 +410,8 @@ the buffer's value of `default-directory'."
 (defcustom project-vc-ignores nil
   "List of patterns to add to `project-ignores'."
   :type '(repeat string))
-;;;###autoload(put 'project-vc-ignores 'safe-local-variable #'listp)
+;; Change to `list-of-strings-p' when support for Emacs 28 is dropped.
+;;;###autoload(put 'project-vc-ignores 'safe-local-variable (lambda (val) (and 
(listp val) (not (memq nil (mapcar #'stringp val))))))
 
 (defcustom project-vc-merge-submodules t
   "Non-nil to consider submodules part of the parent project.
@@ -465,6 +466,7 @@ variables, such as `project-vc-ignores' or 
`project-vc-name'."
   :type '(repeat string)
   :version "29.1"
   :package-version '(project . "0.9.0"))
+;; Change to `list-of-strings-p' when support for Emacs 28 is dropped.
 ;;;###autoload(put 'project-vc-extra-root-markers 'safe-local-variable (lambda 
(val) (and (listp val) (not (memq nil (mapcar #'stringp val))))))
 
 ;; FIXME: Using the current approach, major modes are supposed to set
@@ -565,6 +567,12 @@ See `project-vc-extra-root-markers' for the marker value 
format.")
           (let* ((parent (file-name-directory (directory-file-name root))))
             (setq root (vc-call-backend 'Git 'root parent))))
         (when root
+          (when (not backend)
+            (let* ((project-vc-extra-root-markers nil)
+                   ;; Avoid submodules scan.
+                   (enable-dir-local-variables nil)
+                   (parent (project-try-vc root)))
+              (and parent (setq backend (nth 1 parent)))))
           (setq project (list 'vc backend root))
           ;; FIXME: Cache for a shorter time.
           (vc-file-setprop dir 'project-vc project)
@@ -731,11 +739,10 @@ See `project-vc-extra-root-markers' for the marker value 
format.")
 
 (cl-defmethod project-ignores ((project (head vc)) dir)
   (let* ((root (nth 2 project))
-         backend)
+         (backend (cadr project)))
     (append
      (when (and backend
                 (file-equal-p dir root))
-       (setq backend (cadr project))
        (delq
         nil
         (mapcar
diff --git a/lisp/progmodes/ps-mode.el b/lisp/progmodes/ps-mode.el
index 1147db816bb..01a075d6512 100644
--- a/lisp/progmodes/ps-mode.el
+++ b/lisp/progmodes/ps-mode.el
@@ -97,11 +97,9 @@ When the figure is finished these values should be replaced."
          (const :tag "archC"       (1296 1728))
          (const :tag "archB"        (864 1296))
          (const :tag "archA"        (648  864))
-         (const :tag "flsa"         (612  936))
-         (const :tag "flse"         (612  936))
+          (const :tag "flsa, flse"   (612  936))
          (const :tag "halfletter"   (396  612))
-         (const :tag "11x17"        (792 1224))
-         (const :tag "tabloid"      (792 1224))
+          (const :tag "11x17, tabloid" (792 1224))
          (const :tag "ledger"      (1224  792))
          (const :tag "csheet"      (1224 1584))
          (const :tag "dsheet"      (1584 2448))
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index 4b940b3f13b..d3cb5a77e22 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -10,6 +10,9 @@
 ;; Created: Jul 2010
 ;; Keywords: languages
 
+;; This is a GNU ELPA :core package.  Avoid functionality that is not
+;; compatible with the version of Emacs recorded above.
+
 ;; This file is part of GNU Emacs.
 
 ;; GNU Emacs is free software: you can redistribute it and/or modify
diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el
index adbf18786cb..fabe5859779 100644
--- a/lisp/progmodes/ruby-ts-mode.el
+++ b/lisp/progmodes/ruby-ts-mode.el
@@ -1122,44 +1122,45 @@ leading double colon is not added."
   ;; Navigation.
   (setq-local treesit-defun-type-regexp ruby-ts--method-regex)
 
-  (setq-local treesit-sexp-type-regexp
-              (cons (rx
-                     bol
-                     (or
-                      "class"
-                      "module"
-                      "method"
-                      "array"
-                      "hash"
-                      "parenthesized_statements"
-                      "method_parameters"
-                      "array_pattern"
-                      "hash_pattern"
-                      "if"
-                      "unless"
-                      "case"
-                      "case_match"
-                      "when"
-                      "block"
-                      "do_block"
-                      "begin"
-                      "integer"
-                      "identifier"
-                      "constant"
-                      "simple_symbol"
-                      "hash_key_symbol"
-                      "symbol_array"
-                      "string"
-                      "string_array"
-                      "heredoc_body"
-                      "regex"
-                      "argument_list"
-                      "interpolation"
-                      "instance_variable"
-                      "global_variable"
-                      )
-                     eol)
-                    #'ruby-ts--sexp-p))
+  (setq-local treesit-thing-settings
+              `((ruby
+                 (sexp ,(cons (rx
+                               bol
+                               (or
+                                "class"
+                                "module"
+                                "method"
+                                "array"
+                                "hash"
+                                "parenthesized_statements"
+                                "method_parameters"
+                                "array_pattern"
+                                "hash_pattern"
+                                "if"
+                                "unless"
+                                "case"
+                                "case_match"
+                                "when"
+                                "block"
+                                "do_block"
+                                "begin"
+                                "integer"
+                                "identifier"
+                                "constant"
+                                "simple_symbol"
+                                "hash_key_symbol"
+                                "symbol_array"
+                                "string"
+                                "string_array"
+                                "heredoc_body"
+                                "regex"
+                                "argument_list"
+                                "interpolation"
+                                "instance_variable"
+                                "global_variable"
+                                )
+                               eol)
+                              #'ruby-ts--sexp-p)))))
 
   ;; AFAIK, Ruby can not nest methods
   (setq-local treesit-defun-prefer-top-level nil)
diff --git a/lisp/progmodes/rust-ts-mode.el b/lisp/progmodes/rust-ts-mode.el
index 999c1d7ae96..88344934e49 100644
--- a/lisp/progmodes/rust-ts-mode.el
+++ b/lisp/progmodes/rust-ts-mode.el
@@ -48,6 +48,12 @@
   :safe 'integerp
   :group 'rust)
 
+(defvar rust-ts-mode-prettify-symbols-alist
+  '(("&&" . ?∧) ("||" . ?∨)
+    ("<=" . ?≤)  (">=" . ?≥) ("!=" . ?≠)
+    ("INFINITY" . ?∞) ("->" . ?→) ("=>" . ?⇒))
+  "Value for `prettify-symbols-alist' in `rust-ts-mode'.")
+
 (defvar rust-ts-mode--syntax-table
   (let ((table (make-syntax-table)))
     (modify-syntax-entry ?+   "."      table)
@@ -386,6 +392,19 @@ delimiters < and >'s."
                             (?< '(4 . ?>))
                             (?> '(5 . ?<))))))))
 
+(defun rust-ts-mode--prettify-symbols-compose-p (start end match)
+  "Return true iff the symbol MATCH should be composed.
+See `prettify-symbols-compose-predicate'."
+  (and (fboundp 'prettify-symbols-default-compose-p)
+       (prettify-symbols-default-compose-p start end match)
+       ;; Make sure || is not a closure with 0 arguments and && is not
+       ;; a double reference.
+       (pcase match
+         ((or "||" "&&")
+          (string= (treesit-node-field-name (treesit-node-at (point)))
+                   "operator"))
+         (_ t))))
+
 ;;;###autoload
 (define-derived-mode rust-ts-mode prog-mode "Rust"
   "Major mode for editing Rust, powered by tree-sitter."
@@ -411,6 +430,11 @@ delimiters < and >'s."
                     number type)
                   ( bracket delimiter error function operator property 
variable)))
 
+    ;; Prettify configuration
+    (setq prettify-symbols-alist rust-ts-mode-prettify-symbols-alist)
+    (setq prettify-symbols-compose-predicate
+          #'rust-ts-mode--prettify-symbols-compose-p)
+
     ;; Imenu.
     (setq-local treesit-simple-imenu-settings
                 `(("Module" "\\`mod_item\\'" nil nil)
diff --git a/lisp/progmodes/sh-script.el b/lisp/progmodes/sh-script.el
index ed4ea8e3618..cc521cb0591 100644
--- a/lisp/progmodes/sh-script.el
+++ b/lisp/progmodes/sh-script.el
@@ -1629,10 +1629,11 @@ not written in Bash or sh."
                   ( bracket delimiter misc-punctuation operator)))
     (setq-local treesit-font-lock-settings
                 sh-mode--treesit-settings)
-    (setq-local treesit-text-type-regexp
-                (regexp-opt '("comment"
-                              "heredoc_start"
-                              "heredoc_body")))
+    (setq-local treesit-thing-settings
+                `((bash
+                   (sentence ,(regexp-opt '("comment"
+                                            "heredoc_start"
+                                            "heredoc_body"))))))
     (setq-local treesit-defun-type-regexp "function_definition")
     (treesit-major-mode-setup)))
 
diff --git a/lisp/progmodes/typescript-ts-mode.el 
b/lisp/progmodes/typescript-ts-mode.el
index 3f8e232b71f..01a021c64fc 100644
--- a/lisp/progmodes/typescript-ts-mode.el
+++ b/lisp/progmodes/typescript-ts-mode.el
@@ -32,8 +32,11 @@
 (eval-when-compile (require 'rx))
 (require 'c-ts-common) ; For comment indent and filling.
 
+(declare-function treesit-node-start "treesit.c")
+(declare-function treesit-node-end "treesit.c")
 (declare-function treesit-parser-create "treesit.c")
 (declare-function treesit-query-capture "treesit.c")
+(declare-function treesit-query-compile "treesit.c")
 
 (defcustom typescript-ts-mode-indent-offset 2
   "Number of spaces for each indentation step in `typescript-ts-mode'."
@@ -84,7 +87,7 @@ Check if a node type is available, then return the right 
indent rules."
       (progn (treesit-query-capture 'tsx '((jsx_fragment) @capture))
              `(((match "<" "jsx_fragment") parent 0)
                ((parent-is "jsx_fragment") parent 
typescript-ts-mode-indent-offset)))
-    (error
+    (treesit-query-error
      `(((match "<" "jsx_text") parent 0)
        ((parent-is "jsx_text") parent typescript-ts-mode-indent-offset)))))
 
@@ -163,7 +166,7 @@ Argument LANGUAGE is either `typescript' or `tsx'."
   ;; but then raises an error if the wrong node type is used. So it is
   ;; important to check with the new node type (member_expression)
   (condition-case nil
-      (progn (treesit-query-capture language '((member_expression) @capture))
+      (progn (treesit-query-capture language '(jsx_opening_element 
(member_expression) @capture))
             '((jsx_opening_element
                [(member_expression (identifier)) (identifier)]
                @typescript-ts-jsx-tag-face)
@@ -175,7 +178,8 @@ Argument LANGUAGE is either `typescript' or `tsx'."
               (jsx_self_closing_element
                [(member_expression (identifier)) (identifier)]
                @typescript-ts-jsx-tag-face)))
-    (error '((jsx_opening_element
+    (treesit-query-error
+           '((jsx_opening_element
              [(nested_identifier (identifier)) (identifier)]
              @typescript-ts-jsx-tag-face)
 
@@ -387,7 +391,7 @@ Argument LANGUAGE is either `typescript' or `tsx'."
     "lexical_declaration"
     "property_signature")
   "Nodes that designate sentences in TypeScript.
-See `treesit-sentence-type-regexp' for more information.")
+See `treesit-thing-settings' for more information.")
 
 (defvar typescript-ts-mode--sexp-nodes
   '("expression"
@@ -409,11 +413,13 @@ See `treesit-sentence-type-regexp' for more information.")
     "arguments"
     "pair")
   "Nodes that designate sexps in TypeScript.
-See `treesit-sexp-type-regexp' for more information.")
+See `treesit-thing-settings' for more information.")
 
 ;;;###autoload
 (define-derived-mode typescript-ts-base-mode prog-mode "TypeScript"
-  "Major mode for editing TypeScript."
+  "Generic major mode for editing TypeScript.
+
+This mode is intended to be inherited by concrete major modes."
   :group 'typescript
   :syntax-table typescript-ts-mode--syntax-table
 
@@ -421,10 +427,6 @@ See `treesit-sexp-type-regexp' for more information.")
   (c-ts-common-comment-setup)
   (setq-local treesit-defun-prefer-top-level t)
 
-  (setq-local treesit-text-type-regexp
-              (regexp-opt '("comment"
-                            "template_string")))
-
   ;; Electric
   (setq-local electric-indent-chars
               (append "{}():;,<>/" electric-indent-chars))
@@ -438,11 +440,13 @@ See `treesit-sexp-type-regexp' for more information.")
                             "lexical_declaration")))
   (setq-local treesit-defun-name-function #'js--treesit-defun-name)
 
-  (setq-local treesit-sentence-type-regexp
-              (regexp-opt typescript-ts-mode--sentence-nodes))
-
-  (setq-local treesit-sexp-type-regexp
-              (regexp-opt typescript-ts-mode--sexp-nodes))
+  (setq-local treesit-thing-settings
+              `((typescript
+                 (sexp ,(regexp-opt typescript-ts-mode--sexp-nodes))
+                 (sentence ,(regexp-opt
+                             typescript-ts-mode--sentence-nodes))
+                 (text ,(regexp-opt '("comment"
+                                      "template_string"))))))
 
   ;; Imenu (same as in `js-ts-mode').
   (setq-local treesit-simple-imenu-settings
@@ -475,6 +479,7 @@ See `treesit-sexp-type-regexp' for more information.")
                   (keyword string escape-sequence)
                   (constant expression identifier number pattern property)
                   (function bracket delimiter)))
+    (setq-local syntax-propertize-function #'typescript-ts--syntax-propertize)
 
     (treesit-major-mode-setup)))
 
@@ -513,17 +518,15 @@ at least 3 (which is the default value)."
     (setq-local treesit-simple-indent-rules
                 (typescript-ts-mode--indent-rules 'tsx))
 
-    ;; Navigation
-    (setq-local treesit-sentence-type-regexp
-                (regexp-opt (append
-                             typescript-ts-mode--sentence-nodes
-                             '("jsx_element"
-                               "jsx_self_closing_element"))))
-
-  (setq-local treesit-sexp-type-regexp
-              (regexp-opt (append
-                           typescript-ts-mode--sexp-nodes
-                           '("jsx"))))
+    (setq-local treesit-thing-settings
+                `((tsx
+                   (sexp ,(regexp-opt
+                           (append typescript-ts-mode--sexp-nodes
+                                   '("jsx"))))
+                   (sentence ,(regexp-opt
+                               (append typescript-ts-mode--sentence-nodes
+                                       '("jsx_element"
+                                         "jsx_self_closing_element")))))))
 
     ;; Font-lock.
     (setq-local treesit-font-lock-settings
@@ -533,9 +536,47 @@ at least 3 (which is the default value)."
                   (keyword string escape-sequence)
                   (constant expression identifier jsx number pattern property)
                   (function bracket delimiter)))
+    (setq-local syntax-propertize-function #'tsx-ts--syntax-propertize)
 
     (treesit-major-mode-setup)))
 
+(defvar typescript-ts--s-p-query
+  (when (treesit-available-p)
+    (treesit-query-compile 'typescript
+                           '(((regex pattern: (regex_pattern) @regexp))))))
+
+(defvar tsx-ts--s-p-query
+  (when (treesit-available-p)
+    (treesit-query-compile 'tsx
+                           '(((regex pattern: (regex_pattern) @regexp))
+                             ((variable_declarator value: (jsx_element) @jsx))
+                             ((assignment_expression right: (jsx_element) 
@jsx))
+                             ((arguments (jsx_element) @jsx))
+                             ((parenthesized_expression (jsx_element) @jsx))
+                             ((return_statement (jsx_element) @jsx))))))
+
+(defun typescript-ts--syntax-propertize (beg end)
+  (let ((captures (treesit-query-capture 'typescript typescript-ts--s-p-query 
beg end)))
+    (tsx-ts--syntax-propertize-captures captures)))
+
+(defun tsx-ts--syntax-propertize (beg end)
+  (let ((captures (treesit-query-capture 'tsx tsx-ts--s-p-query beg end)))
+    (tsx-ts--syntax-propertize-captures captures)))
+
+(defun tsx-ts--syntax-propertize-captures (captures)
+  (pcase-dolist (`(,name . ,node) captures)
+    (let* ((ns (treesit-node-start node))
+           (ne (treesit-node-end node))
+           (syntax (pcase-exhaustive name
+                     ('regexp
+                      (cl-decf ns)
+                      (cl-incf ne)
+                      (string-to-syntax "\"/"))
+                     ('jsx
+                      (string-to-syntax "|")))))
+      (put-text-property ns (1+ ns) 'syntax-table syntax)
+      (put-text-property (1- ne) ne 'syntax-table syntax))))
+
 (if (treesit-ready-p 'tsx)
     (add-to-list 'auto-mode-alist '("\\.tsx\\'" . tsx-ts-mode)))
 
diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el
index 3f75f8d7132..b7bfb192d87 100644
--- a/lisp/progmodes/xref.el
+++ b/lisp/progmodes/xref.el
@@ -1468,7 +1468,6 @@ The meanings of both arguments are the same as documented 
in
   (xref--show-xrefs fetcher display-action))
 
 (defun xref--show-xrefs (fetcher display-action &optional _always-show-list)
-  (xref--push-markers)
   (unless (functionp fetcher)
     ;; Old convention.
     (let ((xrefs fetcher))
@@ -1479,21 +1478,32 @@ The meanings of both arguments are the same as 
documented in
                 (prog1
                     xrefs
                   (setq xrefs 'called-already)))))))
-  (funcall xref-show-xrefs-function fetcher
-           `((window . ,(selected-window))
-             (display-action . ,display-action)
-             (auto-jump . ,xref-auto-jump-to-first-xref))))
+  (let ((cb (current-buffer))
+        (pt (point)))
+    (prog1
+        (funcall xref-show-xrefs-function fetcher
+                 `((window . ,(selected-window))
+                   (display-action . ,display-action)
+                   (auto-jump . ,xref-auto-jump-to-first-xref)))
+      (xref--push-markers cb pt))))
 
 (defun xref--show-defs (xrefs display-action)
-  (xref--push-markers)
-  (funcall xref-show-definitions-function xrefs
-           `((window . ,(selected-window))
-             (display-action . ,display-action)
-             (auto-jump . ,xref-auto-jump-to-first-definition))))
-
-(defun xref--push-markers ()
-  (unless (region-active-p) (push-mark nil t))
-  (xref-push-marker-stack))
+  (let ((cb (current-buffer))
+        (pt (point)))
+    (prog1
+        (funcall xref-show-definitions-function xrefs
+                 `((window . ,(selected-window))
+                   (display-action . ,display-action)
+                   (auto-jump . ,xref-auto-jump-to-first-definition)))
+      (xref--push-markers cb pt))))
+
+(defun xref--push-markers (buf pt)
+  (when (buffer-live-p buf)
+    (save-excursion
+      (with-no-warnings (set-buffer buf))
+      (goto-char pt)
+      (unless (region-active-p) (push-mark nil t))
+      (xref-push-marker-stack))))
 
 (defun xref--prompt-p (command)
   (or (eq xref-prompt-for-identifier t)
@@ -1638,7 +1648,9 @@ This command is intended to be bound to a mouse event."
            (mouse-set-point event)
            (xref-backend-identifier-at-point (xref-find-backend)))))
     (if identifier
-        (xref-find-definitions identifier)
+        (progn
+          (mouse-set-point event)
+          (xref-find-definitions identifier))
       (user-error "No identifier here"))))
 
 ;;;###autoload
@@ -1652,6 +1664,7 @@ This command is intended to be bound to a mouse event."
            (xref-backend-identifier-at-point (xref-find-backend)))))
     (if identifier
         (let ((xref-prompt-for-identifier nil))
+          (mouse-set-point event)
           (xref-find-references identifier))
       (user-error "No identifier here"))))
 
diff --git a/lisp/ps-print.el b/lisp/ps-print.el
index b54f09b2bdd..aa3037f5273 100644
--- a/lisp/ps-print.el
+++ b/lisp/ps-print.el
@@ -4850,17 +4850,6 @@ page-height == ((floor print-height ((th + ls) * zh)) * 
((th + ls) * zh)) - th
     (and has-local-background (ps-output "}def\n"))))
 
 
-;; Return a list of the distinct elements of LIST.
-;; Elements are compared with `equal'.
-(defun ps-remove-duplicates (list)
-  (let (new (tail list))
-    (while tail
-      (or (member (car tail) new)
-         (setq new (cons (car tail) new)))
-      (setq tail (cdr tail)))
-    (nreverse new)))
-
-
 ;; Find the first occurrence of ITEM in LIST.
 ;; Return the index of the matching item, or nil if not found.
 ;; Elements are compared with `eq'.
@@ -5342,7 +5331,7 @@ XSTART YSTART are the relative position for the first 
page in a sheet.")
      (if ps-landscape-mode "Landscape" "Portrait")
      "\n%%DocumentNeededResources: font Times-Roman Times-Italic\n%%+ font "
      (mapconcat 'identity
-               (ps-remove-duplicates
+                (seq-uniq
                 (append (ps-fonts 'ps-font-for-text)
                         (list (ps-font 'ps-font-for-header 'normal)
                               (ps-font 'ps-font-for-header 'bold)
@@ -5491,7 +5480,7 @@ XSTART YSTART are the relative position for the first 
page in a sheet.")
      "\n%%IncludeResource: font Times-Italic"
      "\n%%IncludeResource: font "
      (mapconcat 'identity
-               (ps-remove-duplicates
+                (seq-uniq
                 (append (ps-fonts 'ps-font-for-text)
                         (list (ps-font 'ps-font-for-header 'normal)
                               (ps-font 'ps-font-for-header 'bold)
@@ -6548,6 +6537,7 @@ Please send all bug fixes and enhancements to
 (make-obsolete-variable 'ps-print-version 'emacs-version "29.1")
 
 (define-obsolete-function-alias 'ps-print-ensure-fontified #'font-lock-ensure 
"29.1")
+(define-obsolete-function-alias 'ps-remove-duplicates #'seq-uniq "30.1")
 
 (provide 'ps-print)
 
diff --git a/lisp/replace.el b/lisp/replace.el
index eeac734f3bd..6b06e48c384 100644
--- a/lisp/replace.el
+++ b/lisp/replace.el
@@ -1667,13 +1667,15 @@ A positive number means to include that many lines both 
before and after."
 (defcustom list-matching-lines-face 'match
   "Face used by \\[list-matching-lines] to show the text that matches.
 If the value is nil, don't highlight the matching portions specially."
-  :type 'face
+  :type '(choice (const :tag "Don't highlight matching portions" nil)
+                 face)
   :group 'matching)
 
 (defcustom list-matching-lines-buffer-name-face 'underline
   "Face used by \\[list-matching-lines] to show the names of buffers.
 If the value is nil, don't highlight the buffer names specially."
-  :type 'face
+  :type '(choice (const :tag "Don't highlight buffer names" nil)
+                 face)
   :group 'matching)
 
 (defcustom list-matching-lines-current-line-face 'lazy-highlight
diff --git a/lisp/saveplace.el b/lisp/saveplace.el
index a28de6f63a8..590c55d2609 100644
--- a/lisp/saveplace.el
+++ b/lisp/saveplace.el
@@ -196,12 +196,13 @@ removable and network volumes."
 
 (defcustom save-place-ignore-files-regexp
   
"\\(?:COMMIT_EDITMSG\\|hg-editor-[[:alnum:]]+\\.txt\\|svn-commit\\.tmp\\|bzr_log\\.[[:alnum:]]+\\)$"
-  "Regexp matching files for which no position should be recorded.
-Useful for temporary file such as commit message files that are
-automatically created by the VCS.  If set to nil, this feature is
-disabled, i.e., the position is recorded for all files."
+  "Regexp matching files whose positions should not be recorded.
+Useful to exclude temporary files, such as commit message files that are
+automatically created by VCSes.  If set to nil, this feature is
+disabled, i.e., no files are excluded."
   :version "24.1"
-  :type 'regexp)
+  :type '(choice (const :tag "Don't exclude any files" nil)
+                 regexp))
 
 (declare-function dired-current-directory "dired" (&optional localp))
 
diff --git a/lisp/server.el b/lisp/server.el
index 10f15598221..ce68e9aebc9 100644
--- a/lisp/server.el
+++ b/lisp/server.el
@@ -1287,9 +1287,12 @@ The following commands are accepted by the client:
                  ;; choice there.)  In daemon mode on Windows, we can't
                  ;; make tty frames, so force the frame type to GUI
                  ;; there too.
-                 (when (and (eq system-type 'windows-nt)
-                            (or (daemonp)
-                                (eq window-system 'w32)))
+                 (when (or (and (eq system-type 'windows-nt)
+                                (or (daemonp)
+                                    (eq window-system 'w32)))
+                           ;; Client runs on Windows, but the server
+                           ;; runs on a Posix host.
+                           (equal tty-name "CONOUT$"))
                    (push "-window-system" args-left)))
 
                 ;; -position +LINE[:COLUMN]:  Set point to the given
diff --git a/lisp/shell.el b/lisp/shell.el
index b554ee5add9..48978fecbdd 100644
--- a/lisp/shell.el
+++ b/lisp/shell.el
@@ -327,9 +327,8 @@ and syntax highlighting is set up with `sh-mode'.  In 
addition to
 buffer as the current buffer after its setup is done.  This can
 be used to further customize fontification and other behavior of
 the indirect buffer."
-  :type 'boolean
+  :type 'hook
   :group 'shell
-  :safe 'booleanp
   :version "29.1")
 
 (defcustom shell-highlight-undef-enable nil
@@ -596,7 +595,8 @@ Shell buffers.  It implements `shell-completion-execonly' 
for
   ;; shell-dynamic-complete-functions instead.
   (setq-local pcomplete-default-completion-function #'ignore)
   ;; Do not expand remote file names.
-  (setq-local pcomplete-remote-file-ignore t)
+  (setq-local pcomplete-remote-file-ignore
+              (not (file-remote-p default-directory)))
   (setq-local comint-input-autoexpand shell-input-autoexpand)
   ;; Not needed in shell-mode because it's inherited from comint-mode, but
   ;; placed here for read-shell-command.
@@ -1637,15 +1637,15 @@ Returns t if successful."
   "Whether to inhibit cache for fontifying shell commands in remote buffers.
 When fontification of non-existent commands is enabled in a
 remote shell buffer, use a cache to speed up searching for
-executable files on the remote machine.  This options is used to
-control expiry of this cache.  See `remote-file-name-inhibit-cache'
-for description."
+executable files on the remote machine.  This option controls
+expiry of the cache.  See `remote-file-name-inhibit-cache' for
+a description of the possible options."
   :group 'faces
   :type '(choice
-          (const :tag "Do not inhibit file name cache" nil)
-          (const :tag "Do not use file name cache" t)
-          (integer :tag "Do not use file name cache"
-                   :format "Do not use file name cache older than %v seconds"
+          (const :tag "Do not cache remote executables" t)
+          (const :tag "Cache remote executables" nil)
+          (integer :tag "Cache remote executables with expiration"
+                   :format "Cache expiry in seconds: %v"
                    :value 10))
   :version "29.1")
 
@@ -1658,7 +1658,7 @@ EXECUTABLES is a hash table with keys being the 
base-names of
 executable files.
 
 Cache expiry is controlled by the user option
-`remote-file-name-inhibit-cache'.")
+`shell-highlight-undef-remote-file-name-inhibit-cache'.")
 
 (defvar shell--highlight-undef-face 'shell-highlight-undef-defined-face)
 
diff --git a/lisp/simple.el b/lisp/simple.el
index 50d6abffb93..0a8d721b155 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -2504,7 +2504,7 @@ Equivalent key-bindings are also shown in the completion 
list of
   :group 'keyboard
   :type '(choice (const :tag "off" nil)
                  (natnum :tag "time" 2)
-                 (other :tag "on")))
+                 (other :tag "on" t)))
 
 (defcustom extended-command-suggest-shorter t
   "If non-nil, show a shorter \\[execute-extended-command] invocation \
@@ -4903,7 +4903,7 @@ appears at the end of the output.
 Optional fourth arg OUTPUT-BUFFER specifies where to put the
 command's output.  If the value is a buffer or buffer name,
 erase that buffer and insert the output there; a non-nil value of
-`shell-command-dont-erase-buffer' prevent to erase the buffer.
+`shell-command-dont-erase-buffer' prevents erasing the buffer.
 If the value is nil, use the buffer specified by `shell-command-buffer-name'.
 Any other non-nil value means to insert the output in the
 current buffer after START.
@@ -5098,7 +5098,16 @@ characters."
     exit-status))
 
 (defun shell-command-to-string (command)
-  "Execute shell command COMMAND and return its output as a string."
+  "Execute shell command COMMAND and return its output as a string.
+Use `shell-quote-argument' to quote dangerous characters in
+COMMAND before passing it as an argument to this function.
+
+Use this function only when a shell interpreter is needed.  In
+other cases, consider alternatives such as `call-process' or
+`process-lines', which do not invoke the shell.  Consider using
+built-in functions like `rename-file' instead of the external
+command \"mv\".  For more information, see Info node
+`(elisp)Security Considerations'."
   (with-output-to-string
     (with-current-buffer standard-output
       (shell-command command t))))
@@ -5150,7 +5159,7 @@ never with `setq'.")
 (defcustom process-file-return-signal-string nil
   "Whether to return a string describing the signal interrupting a process.
 When a process returns an exit code greater than 128, it is
-interpreted as a signal.  `process-file' requires to return a
+interpreted as a signal.  `process-file' requires returning a
 string describing this signal.
 Since there are processes violating this rule, returning exit
 codes greater than 128 which are not bound to a signal,
@@ -6464,9 +6473,9 @@ If non-nil, the kill ring is rotated after selecting 
previously killed text."
 
 (defun yank-from-kill-ring (string &optional arg)
   "Select a stretch of previously killed text and insert (\"paste\") it.
-This command allows to choose one of the stretches of text killed
-or yanked by previous commands, which are recorded in `kill-ring',
-and reinsert the chosen kill at point.
+This command allows you to select one of the stretches of text
+killed or yanked by previous commands, which are recorded in
+`kill-ring', and reinsert the chosen kill at point.
 
 This command prompts for a previously-killed text in the minibuffer.
 Use the minibuffer history and search commands, or the minibuffer
@@ -8267,7 +8276,11 @@ rests."
       (let ((newpos
             (save-excursion
               (let ((goal-column 0)
-                    (line-move-visual nil))
+                    (line-move-visual nil)
+                     ;; Always move to eol when invoking `C-e' from
+                     ;; within the minibuffer's prompt string (see
+                     ;; bug#65980).
+                     (inhibit-field-text-motion (minibufferp)))
                 (and (line-move arg t)
                      ;; With bidi reordering, we may not be at bol,
                      ;; so make sure we are.
@@ -8444,7 +8457,7 @@ even beep.)"
         (and (= (cdr (nth 6 (posn-at-point))) orig-vlnum)
              ;; Make sure we delete the character where the line wraps
              ;; under visual-line-mode, be it whitespace or a
-             ;; character whose category set allows to wrap at it.
+             ;; character whose category set permits wrapping at it.
              (or (looking-at-p "[ \t]")
                  (and word-wrap-by-category
                       (aref (char-category-set (following-char)) ?\|)))
@@ -8570,12 +8583,12 @@ variables `truncate-lines' and 
`truncate-partial-width-windows'."
   "Interchange characters around point, moving forward one character.
 With prefix arg ARG, effect is to take character before point
 and drag it forward past ARG other characters (backward if ARG negative).
-If no argument and at end of line, the previous two chars are exchanged."
-  (interactive "*P")
-  (when (and (null arg) (eolp) (not (bobp))
+If at end of line, the previous two chars are exchanged."
+  (interactive "*p")
+  (when (and (eolp) (not (bobp))
             (not (get-text-property (1- (point)) 'read-only)))
     (forward-char -1))
-  (transpose-subr 'forward-char (prefix-numeric-value arg)))
+  (transpose-subr #'forward-char arg))
 
 (defun transpose-words (arg)
   "Interchange words around point, leaving point at end of them.
diff --git a/lisp/sort.el b/lisp/sort.el
index a9686f92d48..6b008b6644e 100644
--- a/lisp/sort.el
+++ b/lisp/sort.el
@@ -257,18 +257,15 @@ the sort order."
                  (lambda () (skip-chars-forward "\n"))
                 'forward-page))))
 
-(defvar sort-fields-syntax-table nil)
-(if sort-fields-syntax-table nil
-  (let ((table (make-syntax-table))
-       (i 0))
-    (while (< i 256)
-      (modify-syntax-entry i "w" table)
-      (setq i (1+ i)))
+(defvar sort-fields-syntax-table
+  (let ((table (make-syntax-table)))
+    (dotimes (i 256)
+      (modify-syntax-entry i "w" table))
     (modify-syntax-entry ?\s " " table)
     (modify-syntax-entry ?\t " " table)
     (modify-syntax-entry ?\n " " table)
     (modify-syntax-entry ?\. "_" table)        ; for floating pt. numbers. -wsr
-    (setq sort-fields-syntax-table table)))
+    table))
 
 (defcustom sort-numeric-base 10
   "The default base used by `sort-numeric-fields'."
diff --git a/lisp/sqlite-mode.el b/lisp/sqlite-mode.el
index 8cb94485369..38e9f84b842 100644
--- a/lisp/sqlite-mode.el
+++ b/lisp/sqlite-mode.el
@@ -63,6 +63,7 @@
   (setq-local sqlite--db (sqlite-open file))
   (unless (sqlitep sqlite--db)
     (error "`sqlite-open' failed to open SQLite file"))
+  (add-hook 'kill-buffer-hook (lambda () (sqlite-close sqlite--db)) nil t)
   (sqlite-mode-list-tables))
 
 (defun sqlite-mode-list-tables ()
@@ -135,22 +136,7 @@
 
 (defun sqlite-mode--column-names (table)
   "Return a list of the column names for TABLE."
-  (let ((sql
-         (caar
-          (sqlite-select
-           sqlite--db
-           "select sql from sqlite_master where tbl_name = ? AND type = 
'table'"
-           (list table)))))
-    (with-temp-buffer
-      (insert sql)
-      (mapcar #'string-trim
-              (split-string
-               ;; Extract the args to CREATE TABLE.  Point is
-               ;; currently at its end.
-               (buffer-substring
-                (1- (point))                          ; right before )
-                (1+ (progn (backward-sexp) (point)))) ; right after (
-               ",")))))
+  (mapcar (lambda (row) (nth 1 row)) (sqlite-select sqlite--db (format "pragma 
table_info(%s)" table))))
 
 (defun sqlite-mode-list-data ()
   "List the data from the table under point."
diff --git a/lisp/startup.el b/lisp/startup.el
index 4d0e59ba4f3..6329e3ea8d0 100644
--- a/lisp/startup.el
+++ b/lisp/startup.el
@@ -1682,7 +1682,7 @@ Changed settings will be marked as \"CHANGED outside of 
Customize\"."
 
 (defcustom initial-scratch-message (purecopy "\
 ;; This buffer is for text that is not saved, and for Lisp evaluation.
-;; To create a file, visit it with \\[find-file] and enter text in its buffer.
+;; To create a file, visit it with `\\[find-file]' and enter text in its 
buffer.
 
 ")
   "Initial documentation displayed in *scratch* buffer at startup.
diff --git a/lisp/strokes.el b/lisp/strokes.el
index 694677ada0b..5bfcab5f9e2 100644
--- a/lisp/strokes.el
+++ b/lisp/strokes.el
@@ -266,6 +266,14 @@ able to see the strokes.  This be helpful for people who 
don't like
 the delay in switching to the strokes buffer."
   :type 'boolean)
 
+(defvar strokes-no-match-function 'strokes-no-match-default
+  "Function run by `strokes-execute-stroke' when no stroke matches.
+The function is called with two arguments, the stroke and the
+closest match returned by `strokes-match-stroke'.  It can be used
+to show detailed information about the unmatched stroke or
+perform some fallback action.  The default function
+`strokes-no-match-default' simply signals an error.")
+
 ;;; internal variables...
 
 (defvar strokes-window-configuration nil
@@ -838,14 +846,16 @@ Optional EVENT is acceptable as the starting event of the 
stroke."
            (goto-char (point-min))
            (bury-buffer)))))))
 
+(defun strokes-no-match-default (&rest _)
+  "Signal an error when no stroke matches."
+  (error
+   "No stroke matches; see variable `strokes-minimum-match-score'"))
+
 (defun strokes-execute-stroke (stroke)
   "Given STROKE, execute the command which corresponds to it.
 The command will be executed provided one exists for that stroke,
-based on the variable `strokes-minimum-match-score'.
-If no stroke matches, nothing is done and return value is nil."
-  ;; FIXME: Undocument return value.  It is not documented for all cases,
-  ;; and doesn't allow differentiating between no stroke matches and
-  ;; command-execute returning nil, anyway.
+based on the variable `strokes-minimum-match-score'.  If no
+stroke matches, `strokes-no-match-function' is called."
   (let* ((match (strokes-match-stroke stroke strokes-global-map))
         (command (car match))
         (score (cdr match)))
@@ -859,10 +869,7 @@ If no stroke matches, nothing is done and return value is 
nil."
                                     strokes-file))
                    (strokes-load-user-strokes))
             (error "No strokes defined; use `strokes-global-set-stroke'")))
-         (t
-          (error
-           "No stroke matches; see variable `strokes-minimum-match-score'")
-          nil))))
+         (t (funcall strokes-no-match-function stroke match)))))
 
 ;;;###autoload
 (defun strokes-do-stroke (event)
diff --git a/lisp/subr.el b/lisp/subr.el
index 0bdfe6a8e0d..2aaaa521bb5 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -281,6 +281,21 @@ change the list."
          (macroexp-let2 macroexp-copyable-p x getter
            `(prog1 ,x ,(funcall setter `(cdr ,x))))))))
 
+;; Note: `static-if' can be copied into a package to enable it to be
+;; used in Emacsen older than Emacs 30.1.  If the package is used in
+;; very old Emacsen or XEmacs (in which `eval' takes exactly one
+;; argument) the copy will need amending.
+(defmacro static-if (condition then-form &rest else-forms)
+  "A conditional compilation macro.
+Evaluate CONDITION at macro-expansion time.  If it is non-nil,
+expand the macro to THEN-FORM.  Otherwise expand it to ELSE-FORMS
+enclosed in a `progn' form.  ELSE-FORMS may be empty."
+  (declare (indent 2)
+           (debug (sexp sexp &rest sexp)))
+  (if (eval condition lexical-binding)
+      then-form
+    (cons 'progn else-forms)))
+
 (defmacro when (cond &rest body)
   "If COND yields non-nil, do BODY, else return nil.
 When COND yields non-nil, eval BODY forms sequentially and return
@@ -543,8 +558,10 @@ If COUNT is negative, shifting is actually to the right.
 In this case, if VALUE is a negative fixnum treat it as unsigned,
 i.e., subtract 2 * `most-negative-fixnum' from VALUE before shifting it.
 
-This function is provided for compatibility.  In new code, use `ash'
-instead."
+Most uses of this function turn out to be mistakes.  We recommend
+to use `ash' instead, unless COUNT could ever be negative, and
+if, when COUNT is negative, your program really needs the special
+treatment of negative COUNT provided by this function."
   (declare (compiler-macro
             (lambda (form)
               (macroexp-warn-and-return
@@ -982,11 +999,11 @@ SEQ must be a list, vector, or string.  The comparison is 
done with `equal'.
 Contrary to `delete', this does not use side-effects, and the argument
 SEQ is not modified."
   (declare (side-effect-free t))
-  (if (nlistp seq)
-      ;; If SEQ isn't a list, there's no need to copy SEQ because
-      ;; `delete' will return a new object.
-      (delete elt seq)
-    (delete elt (copy-sequence seq))))
+  (delete elt (if (nlistp seq)
+                  ;; If SEQ isn't a list, there's no need to copy SEQ because
+                  ;; `delete' will return a new object.
+                  seq
+                (copy-sequence seq))))
 
 (defun remq (elt list)
   "Return LIST with all occurrences of ELT removed.
@@ -2025,6 +2042,7 @@ instead; it will indirectly limit the specpdl stack size 
as well.")
 (defalias 'store-match-data #'set-match-data)
 (defalias 'chmod #'set-file-modes)
 (defalias 'mkdir #'make-directory)
+(defalias 'wholenump #'natnump)
 
 ;; These were the XEmacs names, now obsolete:
 (defalias 'point-at-eol #'line-end-position)
@@ -3487,7 +3505,7 @@ If there is a natural number at point, use it as default."
   (let ((map (make-sparse-keymap)))
     (set-keymap-parent map minibuffer-local-map)
 
-    (define-key map [remap self-insert-command] 
#'read-char-from-minibuffer-insert-char)
+    ;; (define-key map [remap self-insert-command] 
#'read-char-from-minibuffer-insert-char)
     (define-key map [remap exit-minibuffer] 
#'read-char-from-minibuffer-insert-other)
 
     (define-key map [remap recenter-top-bottom] 
#'minibuffer-recenter-top-bottom)
@@ -3518,7 +3536,7 @@ allowed to type into the minibuffer.  When the user types 
any
 such key, this command discard all minibuffer input and displays
 an error message."
   (interactive)
-  (when (minibufferp)
+  (when (minibufferp) ;;FIXME: Why?
     (delete-minibuffer-contents)
     (ding)
     (discard-input)
@@ -3566,6 +3584,10 @@ There is no need to explicitly add `help-char' to CHARS;
                                         (interactive)
                                         (let ((help-form msg)) ; lexically 
bound msg
                                           (help-form-show)))))
+                        ;; FIXME: We use 
`read-char-from-minibuffer-insert-char'
+                        ;; here only as a kind of alias of 
`self-insert-command'
+                        ;; to prevent those keys from being remapped to
+                        ;; `read-char-from-minibuffer-insert-other'.
                         (dolist (char chars)
                           (define-key map (vector char)
                                       #'read-char-from-minibuffer-insert-char))
@@ -3577,7 +3599,15 @@ There is no need to explicitly add `help-char' to CHARS;
                 read-char-from-minibuffer-map))
          ;; Protect this-command when called from pre-command-hook (bug#45029)
          (this-command this-command)
-         (result (progn
+         (result (minibuffer-with-setup-hook
+                    (lambda ()
+                      (add-hook 'post-command-hook
+                                (lambda ()
+                                  ;; FIXME: Should we use `<='?
+                                  (if (= (1+ (minibuffer-prompt-end))
+                                         (point-max))
+                                       (exit-minibuffer)))
+                                nil 'local))
                    ;; Disable text conversion if it is enabled.
                    ;; (bug#65370)
                    (when (fboundp 'set-text-conversion-style)
@@ -5673,9 +5703,11 @@ See also `string-equal'."
   (eq t (compare-strings string1 0 nil string2 0 nil t)))
 
 (defun string-prefix-p (prefix string &optional ignore-case)
-  "Return non-nil if PREFIX is a prefix of STRING.
+  "Return non-nil if STRING begins with PREFIX.
+PREFIX should be a string; the function returns non-nil if the
+characters at the beginning of STRING compare equal with PREFIX.
 If IGNORE-CASE is non-nil, the comparison is done without paying attention
-to case differences."
+to letter-case differences."
   (declare (side-effect-free t))
   (let ((prefix-length (length prefix)))
     (if (> prefix-length (length string)) nil
@@ -5683,9 +5715,11 @@ to case differences."
                             0 prefix-length ignore-case)))))
 
 (defun string-suffix-p (suffix string  &optional ignore-case)
-  "Return non-nil if SUFFIX is a suffix of STRING.
+  "Return non-nil if STRING ends with SUFFIX.
+SUFFIX should be a string; the function returns non-nil if the
+characters at end of STRING compare equal with SUFFIX.
 If IGNORE-CASE is non-nil, the comparison is done without paying
-attention to case differences."
+attention to letter-case differences."
   (declare (side-effect-free t))
   (let ((start-pos (- (length string) (length suffix))))
     (and (>= start-pos 0)
@@ -5742,8 +5776,8 @@ Return nil if there isn't one."
         (load-elt (and loads (car loads))))
     (save-match-data
       (while (and loads
-                 (or (null (car load-elt))
-                     (not (string-match file-regexp (car load-elt)))))
+                 (not (and (car load-elt)
+                            (string-match file-regexp (car load-elt)))))
        (setq loads (cdr loads)
              load-elt (and loads (car loads)))))
     load-elt))
@@ -6027,7 +6061,7 @@ by `find-word-boundary-function-table'.  It is also not 
interactive."
 With argument ARG, do this that many times.
 If ARG is omitted or nil, move point backward one word.
 
-This function is like `forward-word', but it is not affected
+This function is like `backward-word', but it is not affected
 by `find-word-boundary-function-table'.  It is also not interactive."
   (let ((find-word-boundary-function-table
          (if (char-table-p word-move-empty-char-table)
@@ -6316,7 +6350,7 @@ command is called from a keyboard macro?"
              ;; Skip special forms (from non-compiled code).
              (and frame (null (car frame)))
              ;; Skip also `interactive-p' (because we don't want to know if
-             ;; interactive-p was called interactively but if it's caller was).
+             ;; interactive-p was called interactively but if its caller was).
              (eq (nth 1 frame) 'interactive-p)
              ;; Skip package-specific stack-frames.
              (let ((skip (run-hook-with-args-until-success
@@ -6561,7 +6595,6 @@ effectively rounded up."
   (unless min-time
     (setq min-time 0.2))
   (let ((reporter
-        ;; Force a call to `message' now
         (cons (or min-value 0)
               (vector (if (>= min-time 0.02)
                           (float-time) nil)
@@ -6572,6 +6605,7 @@ effectively rounded up."
                        min-time
                        ;; SUFFIX
                        nil))))
+    ;; Force a call to `message' now.
     (progress-reporter-update reporter (or current-value min-value))
     reporter))
 
diff --git a/lisp/svg.el b/lisp/svg.el
index 15004357811..dc8b4feb51f 100644
--- a/lisp/svg.el
+++ b/lisp/svg.el
@@ -8,6 +8,9 @@
 ;; Version: 1.1
 ;; Package-Requires: ((emacs "25"))
 
+;; This is a GNU ELPA :core package.  Avoid functionality that is not
+;; compatible with the version of Emacs recorded above.
+
 ;; This file is part of GNU Emacs.
 
 ;; GNU Emacs is free software: you can redistribute it and/or modify
diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el
index 6b286598a14..d2815c03ebf 100644
--- a/lisp/tab-bar.el
+++ b/lisp/tab-bar.el
@@ -31,13 +31,8 @@
 
 ;;; Code:
 
-(eval-when-compile
-  (require 'cl-lib)
-  (require 'seq)
-  (require 'icons))
-
-(autoload 'cl--set-substring "cl-lib")
-
+(eval-when-compile (require 'icons))
+(eval-when-compile (require 'cl-lib))
 
 (defgroup tab-bar nil
   "Frame-local tabs."
@@ -166,6 +161,7 @@ For easier selection of tabs by their numbers, consider 
customizing
     (define-icon tab-bar-new nil
       `((image "symbols/plus_16.svg" "tabs/new.xpm"
                :face shadow
+               :height (1 . em)
                :margin ,tab-bar-button-margin
                :ascent center)
         ;; (emoji "➕")
@@ -180,7 +176,7 @@ For easier selection of tabs by their numbers, consider 
customizing
     (define-icon tab-bar-close nil
       `((image "symbols/cross_16.svg" "tabs/close.xpm"
                :face shadow
-               :height (1.0 . em)
+               :height (1 . em)
                :margin ,tab-bar-button-margin
                :ascent center)
         ;; (emoji " ❌")
@@ -195,6 +191,7 @@ For easier selection of tabs by their numbers, consider 
customizing
   (unless (iconp 'tab-bar-menu-bar)
     (define-icon tab-bar-menu-bar nil
       `((image "symbols/menu_16.svg"
+               :height (1 . em)
                :margin ,tab-bar-button-margin
                :ascent center)
         ;; (emoji "🍔")
@@ -1237,8 +1234,7 @@ tab bar might wrap to the second line when it shouldn't.")
                                            space
                                            (substring name ins-pos)))
                         (setq curr-width (string-pixel-width name))
-                        (if (and (< curr-width width)
-                                 (> curr-width prev-width))
+                        (if (< curr-width width)
                             (setq prev-width curr-width
                                   prev-name name)
                           ;; Set back a shorter name
@@ -1252,8 +1248,7 @@ tab bar might wrap to the second line when it shouldn't.")
                                            (and del-pos2
                                                 (substring name del-pos2))))
                         (setq curr-width (string-pixel-width name))
-                        (if (and (> curr-width width)
-                                 (< curr-width prev-width))
+                        (if (> curr-width width)
                             (setq prev-width curr-width)
                           (setq continue nil)))
                       (let* ((len (length name))
@@ -2285,6 +2280,7 @@ and can restore them."
         (unless (iconp 'tab-bar-back)
           (define-icon tab-bar-back nil
             `((image "symbols/chevron_left_16.svg" "tabs/left-arrow.xpm"
+                     :height (1 . em)
                      :margin ,tab-bar-button-margin
                      :ascent center)
               (text " < "))
@@ -2295,6 +2291,7 @@ and can restore them."
         (unless (iconp 'tab-bar-forward)
           (define-icon tab-bar-forward nil
             `((image "symbols/chevron_right_16.svg" "tabs/right-arrow.xpm"
+                     :height (1 . em)
                      :margin ,tab-bar-button-margin
                      :ascent center)
               (text " > "))
diff --git a/lisp/tab-line.el b/lisp/tab-line.el
index e277d1fb9ed..4637dafcd90 100644
--- a/lisp/tab-line.el
+++ b/lisp/tab-line.el
@@ -28,7 +28,7 @@
 ;;; Code:
 
 (require 'cl-lib)
-(require 'seq) ; tab-line.el is not pre-loaded so it's safe to use it here
+(require 'seq)
 (require 'icons)
 
 
@@ -194,6 +194,7 @@ If the value is a function, call it with no arguments."
 (define-icon tab-line-new nil
   `((image "symbols/plus_16.svg" "tabs/new.xpm"
            :face shadow
+           :height (1 . em)
            :margin (2 . 0)
            :ascent center)
     (text " + "))
@@ -229,7 +230,7 @@ If nil, don't show it at all."
 (define-icon tab-line-close nil
   `((image "symbols/cross_16.svg" "tabs/close.xpm"
            :face shadow
-           :height (1.0 . em)
+           :height (1 . em)
            :margin (2 . 0)
            :ascent center)
     (text " x"))
@@ -248,6 +249,7 @@ If nil, don't show it at all."
 (define-icon tab-line-left nil
   `((image "symbols/chevron_left_16.svg" "tabs/left-arrow.xpm"
            :face shadow
+           :height (1 . em)
            :margin (2 . 0)
            :ascent center)
     (text " <"))
@@ -265,6 +267,7 @@ If nil, don't show it at all."
 (define-icon tab-line-right nil
   `((image "symbols/chevron_right_16.svg" "tabs/right-arrow.xpm"
            :face shadow
+           :height (1 . em)
            :margin (2 . 0)
            :ascent center)
     (text "> "))
@@ -359,48 +362,6 @@ or by `tab-line-tabs-buffer-groups'."
   "Function to return a global list of buffers.
 Used only for `tab-line-tabs-mode-buffers' and `tab-line-tabs-buffer-groups'.")
 
-
-
-;;; Touch screen support.
-
-(defun tab-line-track-tap (event &optional function)
-  "Track a tap starting from EVENT.
-If EVENT is not a `touchscreen-begin' event, return t.
-Otherwise, return t if the tap completes successfully, and nil if
-the tap should be ignored.
-
-If FUNCTION is specified and the tap does not complete within
-`touch-screen-delay' seconds, display the appropriate context
-menu by calling FUNCTION with EVENT, and return nil."
-  (if (not (eq (car-safe event) 'touchscreen-begin))
-      t
-    (let ((result (catch 'context-menu
-                    (let (timer)
-                      (unwind-protect
-                          (progn
-                            (when function
-                              (setq timer
-                                    (run-at-time touch-screen-delay t
-                                                 #'throw 'context-menu
-                                                 'context-menu)))
-                            (touch-screen-track-tap event))
-                        (when timer
-                          (cancel-timer timer)))))))
-      (cond ((eq result 'context-menu)
-             (prog1 nil
-               (funcall function event)))
-            (result t)))))
-
-(defun tab-line-event-start (event)
-  "Like `event-start'.
-However, return the correct mouse position list if EVENT is a
-`touchscreen-begin' event."
-  (or (and (eq (car-safe event) 'touchscreen-begin)
-           (cdadr event))
-      (event-start event)))
-
-
-
 (defun tab-line-tabs-buffer-list ()
   (seq-filter (lambda (b) (and (buffer-live-p b)
                                (/= (aref (buffer-name b) 0) ?\s)))
@@ -995,6 +956,45 @@ sight of the tab line."
     (popup-menu menu event)))
 
 
+;;; Touch screen support.
+
+(defun tab-line-track-tap (event &optional function)
+  "Track a tap starting from EVENT.
+If EVENT is not a `touchscreen-begin' event, return t.
+Otherwise, return t if the tap completes successfully, and nil if
+the tap should be ignored.
+
+If FUNCTION is specified and the tap does not complete within
+`touch-screen-delay' seconds, display the appropriate context
+menu by calling FUNCTION with EVENT, and return nil."
+  (if (not (eq (car-safe event) 'touchscreen-begin))
+      t
+    (let ((result (catch 'context-menu
+                    (let (timer)
+                      (unwind-protect
+                          (progn
+                            (when function
+                              (setq timer
+                                    (run-at-time touch-screen-delay t
+                                                 #'throw 'context-menu
+                                                 'context-menu)))
+                            (touch-screen-track-tap event))
+                        (when timer
+                          (cancel-timer timer)))))))
+      (cond ((eq result 'context-menu)
+             (prog1 nil
+               (funcall function event)))
+            (result t)))))
+
+(defun tab-line-event-start (event)
+  "Like `event-start'.
+However, return the correct mouse position list if EVENT is a
+`touchscreen-begin' event."
+  (or (and (eq (car-safe event) 'touchscreen-begin)
+           (cdadr event))
+      (event-start event)))
+
+
 ;;;###autoload
 (define-minor-mode tab-line-mode
   "Toggle display of tab line in the windows displaying the current buffer."
diff --git a/lisp/tempo.el b/lisp/tempo.el
index b59342b2c9d..45dc24dcc97 100644
--- a/lisp/tempo.el
+++ b/lisp/tempo.el
@@ -116,8 +116,7 @@
   "Prompt user for strings in templates.
 If this variable is non-nil, `tempo-insert' prompts the
 user for text to insert in the templates."
-  :type 'boolean
-  :group 'tempo)
+  :type 'boolean)
 
 (defcustom tempo-insert-region nil
   "Automatically insert current region when there is a `r' in the template
@@ -126,20 +125,17 @@ elements, unless the template function is given a prefix 
(or a non-nil
 argument).  If this variable is non-nil, the behavior is reversed.
 
 In Transient Mark mode, this option is unused."
-  :type 'boolean
-  :group 'tempo)
+  :type 'boolean)
 
 (defcustom tempo-show-completion-buffer t
   "If non-nil, show a buffer with possible completions, when only
 a partial completion can be found."
-  :type 'boolean
-  :group 'tempo)
+  :type 'boolean)
 
 (defcustom tempo-leave-completion-buffer nil
   "If nil, a completion buffer generated by \\[tempo-complete-tag]
 disappears at the next keypress; otherwise, it remains forever."
-  :type 'boolean
-  :group 'tempo)
+  :type 'boolean)
 
 ;;; Internal variables
 
@@ -189,11 +185,12 @@ returns a pair of the form (STRING . POS), where STRING 
is the string
 used for matching and POS is the buffer position after which text
 should be replaced with a template.")
 
-(defvar tempo-user-elements nil
+(define-obsolete-variable-alias 'tempo-user-elements 
'tempo-user-element-functions "30.1")
+(defvar tempo-user-element-functions nil
   "Element handlers for user-defined elements.
-A list of symbols which are bound to functions that take one argument.
-This function should return something to be sent to `tempo-insert' if
-it recognizes the argument, and nil otherwise.")
+This is an abnormal hook where the functions are called with one argument
+\(an element in a template) and they should return something to be sent to
+`tempo-insert' if they recognize the argument, and nil otherwise.")
 
 (defvar-local tempo-named-insertions nil
   "Temporary storage for named insertions.")
@@ -262,7 +259,7 @@ The elements in ELEMENTS can be of several types:
  - `n>': Inserts a newline and indents line.
  - `o': Like `%' but leaves the point before the newline.
  - nil: It is ignored.
- - Anything else: Each function in `tempo-user-elements' is called
+ - Anything else: Each function in `tempo-user-element-functions' is called
    with it as argument until one of them returns non-nil, and the
    result is inserted.  If all of them return nil, it is evaluated and
    the result is treated as an element to be inserted.  One additional
@@ -274,14 +271,13 @@ The elements in ELEMENTS can be of several types:
                                       name)))
         (command-name template-name))
     (set template-name elements)
-    (fset command-name (list 'lambda (list '&optional 'arg)
-                            (or documentation
-                                (concat "Insert a " name "."))
-                            (list 'interactive "*P")
-                            (list 'tempo-insert-template (list 'quote
-                                                               template-name)
-                                  (list 'if 'tempo-insert-region
-                                        (list 'not 'arg) 'arg))))
+    (fset command-name (lambda (&optional arg)
+                        (:documentation
+                         (or documentation (concat "Insert a " name ".")))
+                        (interactive "*P")
+                        (tempo-insert-template template-name
+                                               (if tempo-insert-region
+                                                   (not arg) arg))))
     (and tag
         (tempo-add-tag tag template-name taglist))
     command-name))
@@ -325,72 +321,57 @@ elements are replaced with the current region.
 
 See documentation for `tempo-define-template' for the kind of elements
 possible."
-  (cond ((stringp element) (tempo-process-and-insert-string element))
-       ((and (consp element)
-             (eq (car element) 'p)) (tempo-insert-prompt-compat
-                                     (cdr element)))
-       ((and (consp element)
-             (eq (car element) 'P)) (let ((tempo-interactive t))
-                                      (tempo-insert-prompt-compat
-                                       (cdr element))))
-;;;    ((and (consp element)
-;;;          (eq (car element) 'v)) (tempo-save-named
-;;;                                  (nth 1 element)
-;;;                                  nil
-;;;                                  (nth 2 element)))
-       ((and (consp element)
-             (eq (car element) 'r)) (if on-region
-                                        (goto-char tempo-region-stop)
-                                      (tempo-insert-prompt-compat
-                                       (cdr element))))
-        ((and (consp element)
-              (eq (car element) 'r>)) (if on-region
-                                          (progn
-                                            (goto-char tempo-region-stop)
-                                            (indent-region (mark) (point) nil))
-                                        (tempo-insert-prompt-compat
-                                         (cdr element))))
-       ((and (consp element)
-             (eq (car element) 's)) (tempo-insert-named (car (cdr element))))
-       ((and (consp element)
-              (eq (car element) 'l)) (mapcar (lambda (elt)
-                                               (tempo-insert elt on-region))
-                                            (cdr element)))
-       ((eq element 'p) (tempo-insert-mark (point-marker)))
-       ((eq element 'r) (if on-region
-                            (goto-char tempo-region-stop)
-                          (tempo-insert-mark (point-marker))))
-       ((eq element 'r>) (if on-region
-                             (progn
-                               (goto-char tempo-region-stop)
-                               (indent-region (mark) (point) nil))
-                           (tempo-insert-mark (point-marker))))
-       ((eq element '>) (indent-according-to-mode))
-       ((eq element '&) (if (not (or (= (current-column) 0)
-                                     (save-excursion
-                                       (re-search-backward
-                                        "^\\s-*\\=" nil t))))
-                            (insert "\n")))
-       ((eq element '%) (if (not (or (eolp)
-                                     (save-excursion
-                                       (re-search-forward
-                                        "\\=\\s-*$" nil t))))
-                            (insert "\n")))
-       ((eq element 'n) (insert "\n"))
-       ((eq element 'n>) (insert "\n") (indent-according-to-mode))
-       ;; Bug: If the 'o is the first element in a template, strange
-       ;; things can happen when the template is inserted at the
-       ;; beginning of a line.
-       ((eq element 'o) (if (not (or on-region
-                                     (eolp)
-                                     (save-excursion
-                                       (re-search-forward
-                                        "\\=\\s-*$" nil t))))
-                            (open-line 1)))
-       ((null element))
-       (t (tempo-insert (or (tempo-is-user-element element)
-                            (eval element))
-                        on-region))))
+  (pcase element
+    ((pred stringp) (tempo-process-and-insert-string element))
+    (`(p . ,rest) (tempo-insert-prompt-compat rest))
+    (`(P . ,rest) (let ((tempo-interactive t))
+                    (tempo-insert-prompt-compat rest)))
+    ;; (`(v ,name ,data) (tempo-save-named name nil data))
+    (`(r . ,rest) (if on-region
+                     (goto-char tempo-region-stop)
+                   (tempo-insert-prompt-compat rest)))
+    (`(r> . ,rest) (if on-region
+                       (progn
+                         (goto-char tempo-region-stop)
+                         (indent-region (mark) (point) nil))
+                       (tempo-insert-prompt-compat rest)))
+    (`(s ,name) (tempo-insert-named name))
+    (`(l . ,rest) (dolist (elt rest) (tempo-insert elt on-region)))
+    ('p (tempo-insert-mark (point-marker)))
+    ('r (if on-region
+           (goto-char tempo-region-stop)
+         (tempo-insert-mark (point-marker))))
+    ('r> (if on-region
+            (progn
+              (goto-char tempo-region-stop)
+              (indent-region (mark) (point) nil))
+          (tempo-insert-mark (point-marker))))
+    ('> (indent-according-to-mode))
+    ('& (if (not (or (= (current-column) 0)
+                    (save-excursion
+                      (re-search-backward
+                       "^\\s-*\\=" nil t))))
+           (insert "\n")))
+    ('% (if (not (or (eolp)
+                    (save-excursion
+                      (re-search-forward
+                       "\\=\\s-*$" nil t))))
+           (insert "\n")))
+    ('n (insert "\n"))
+    ('n> (insert "\n") (indent-according-to-mode))
+    ;; Bug: If the 'o is the first element in a template, strange
+    ;; things can happen when the template is inserted at the
+    ;; beginning of a line.
+    ('o (if (not (or on-region
+                    (eolp)
+                    (save-excursion
+                      (re-search-forward
+                       "\\=\\s-*$" nil t))))
+           (open-line 1)))
+    ('nil nil)
+    (_ (tempo-insert (or (tempo-is-user-element element)
+                        (eval element t))
+                    on-region))))
 
 ;;;
 ;;; tempo-insert-prompt
@@ -400,7 +381,7 @@ possible."
 PROMPT can be either a prompt string, or a list of arguments to
 `tempo-insert-prompt', or nil."
   (if (consp prompt)                   ; not nil either
-      (apply 'tempo-insert-prompt prompt)
+      (apply #'tempo-insert-prompt prompt)
     (tempo-insert-prompt prompt)))
 
 (defun tempo-insert-prompt (prompt &optional save-name no-insert)
@@ -445,14 +426,8 @@ never prompted."
 ;;; tempo-is-user-element
 
 (defun tempo-is-user-element (element)
-  "Try all the user-defined element handlers in `tempo-user-elements'."
-  ;; Sigh... I need (some list)
-  (catch 'found
-    (mapc (lambda (handler)
-            (let ((result (funcall handler element)))
-              (if result (throw 'found result))))
-         tempo-user-elements)
-    (throw 'found nil)))
+  "Try all the user-defined element handlers in 
`tempo-user-element-functions'."
+  (run-hook-with-args-until-success 'tempo-user-element-functions element))
 
 ;;;
 ;;; tempo-forget-insertions
@@ -636,12 +611,12 @@ If `tempo-dirty-collection' is nil, the old collection is 
reused."
       (or (and (not tempo-dirty-collection)
               tempo-collection)
          (setq tempo-collection
-               (apply (function append)
+               (apply #'append
                        (mapcar (lambda (tag-list)
                                        ; If the format for
                                        ; tempo-local-tags changes,
                                        ; change this
-                                 (eval (car tag-list)))
+                                 (eval (car tag-list) t))
                               tempo-local-tags))))
     (setq tempo-dirty-collection nil)))
 
@@ -653,16 +628,10 @@ If `tempo-dirty-collection' is nil, the old collection is 
reused."
 FINDER is a function or a string.  Returns (STRING . POS), or nil
 if no reasonable string is found."
   (cond ((stringp finder)
-        (let (successful)
-          (save-excursion
-            (or (setq successful (re-search-backward finder nil t))
-                0))
-          (if successful
-              (cons (buffer-substring (match-beginning 1)
-                                      (match-end 1)) ; This seems to be a
-                                       ; bug in emacs
-                    (match-beginning 1))
-            nil)))
+        (if (save-excursion (re-search-backward finder nil t))
+            (cons (match-string 1)   ; This seems to be a bug in Emacs (?)
+                  (match-beginning 1))
+          nil))
        (t
         (funcall finder))))
 
diff --git a/lisp/term/android-win.el b/lisp/term/android-win.el
index d425ea401a9..db873c176c8 100644
--- a/lisp/term/android-win.el
+++ b/lisp/term/android-win.el
@@ -1,4 +1,4 @@
-;;; x-win.el --- parse relevant switches and set up for Android  -*- 
lexical-binding:t -*-
+;;; android-win.el --- terminal set up for Android  -*- lexical-binding:t -*-
 
 ;; Copyright (C) 2023 Free Software Foundation, Inc.
 
diff --git a/lisp/textmodes/dns-mode.el b/lisp/textmodes/dns-mode.el
index 0167c757473..1b5f0c1d94b 100644
--- a/lisp/textmodes/dns-mode.el
+++ b/lisp/textmodes/dns-mode.el
@@ -131,6 +131,7 @@ manually with \\[dns-mode-soa-increment-serial]."
   (let ((table (make-syntax-table)))
     (modify-syntax-entry ?\; "<   " table)
     (modify-syntax-entry ?\n ">   " table)
+    (modify-syntax-entry ?\" "\""   table)
     table)
   "Syntax table in use in DNS master file buffers.")
 
diff --git a/lisp/textmodes/enriched.el b/lisp/textmodes/enriched.el
index 92be5a52bf2..004b844a3e7 100644
--- a/lisp/textmodes/enriched.el
+++ b/lisp/textmodes/enriched.el
@@ -146,7 +146,7 @@ them and their old values to `enriched-old-bindings'."
   :type 'hook)
 
 (defcustom enriched-allow-eval-in-display-props nil
-  "If non-nil allow to evaluate arbitrary forms in display properties.
+  "If non-nil, allow evaluating arbitrary forms in display properties.
 
 Enriched mode recognizes display properties of text stored using
 an extension command to the text/enriched format, \"x-display\".
diff --git a/lisp/textmodes/flyspell.el b/lisp/textmodes/flyspell.el
index 84c207b8a48..1ca508e14ef 100644
--- a/lisp/textmodes/flyspell.el
+++ b/lisp/textmodes/flyspell.el
@@ -1,6 +1,6 @@
 ;;; flyspell.el --- On-the-fly spell checker  -*- lexical-binding:t -*-
 
-;; Copyright (C) 1998, 2000-2023 Free Software Foundation, Inc.
+;; Copyright (C) 1998-2023 Free Software Foundation, Inc.
 
 ;; Author: Manuel Serrano <Manuel.Serrano@sophia.inria.fr>
 ;; Maintainer: emacs-devel@gnu.org
@@ -22,7 +22,7 @@
 ;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
 
 ;;; Commentary:
-;;
+
 ;; Flyspell is a minor Emacs mode performing on-the-fly spelling
 ;; checking.
 ;;
@@ -33,8 +33,7 @@
 ;; M-x flyspell-prog-mode.
 ;; In that mode only text inside comments and strings is checked.
 ;;
-;; Some user variables control the behavior of flyspell.  They are
-;; those defined under the `User configuration' comment.
+;; Use `M-x customize-group RET flyspell RET' to customize flyspell.
 
 ;;; Code:
 
@@ -410,18 +409,6 @@ like <img alt=\"Some thing.\">."
   (flyspell-mode 1)
   (run-hooks 'flyspell-prog-mode-hook))
 
-;;*---------------------------------------------------------------------*/
-;;*    Overlay compatibility                                            */
-;;*---------------------------------------------------------------------*/
-(autoload 'make-overlay            "overlay" "Overlay compatibility kit." t)
-(autoload 'overlayp                "overlay" "Overlay compatibility kit." t)
-(autoload 'overlays-in             "overlay" "Overlay compatibility kit." t)
-(autoload 'delete-overlay          "overlay" "Overlay compatibility kit." t)
-(autoload 'overlays-at             "overlay" "Overlay compatibility kit." t)
-(autoload 'overlay-put             "overlay" "Overlay compatibility kit." t)
-(autoload 'overlay-get             "overlay" "Overlay compatibility kit." t)
-(autoload 'previous-overlay-change "overlay" "Overlay compatibility kit." t)
-
 ;;*---------------------------------------------------------------------*/
 ;;*    The minor mode declaration.                                      */
 ;;*---------------------------------------------------------------------*/
@@ -529,10 +516,10 @@ in your init file.
   :group 'flyspell
   (if flyspell-mode
       (condition-case err
-         (flyspell-mode-on (called-interactively-p 'interactive))
+          (flyspell--mode-on (called-interactively-p 'interactive))
        (error (message "Error enabling Flyspell mode:\n%s" (cdr err))
               (flyspell-mode -1)))
-    (flyspell-mode-off)))
+    (flyspell--mode-off)))
 
 ;;;###autoload
 (defun turn-on-flyspell ()
@@ -597,14 +584,14 @@ in your init file.
       (kill-local-variable 'flyspell-word-cache-word))))
 
 ;; Make sure we flush our caches when needed.  Do it here rather than in
-;; flyspell-mode-on, since flyspell-region may be used without ever turning
+;; flyspell--mode-on, since flyspell-region may be used without ever turning
 ;; on flyspell-mode.
 (add-hook 'ispell-kill-ispell-hook 'flyspell-kill-ispell-hook)
 
 ;;*---------------------------------------------------------------------*/
-;;*    flyspell-mode-on ...                                             */
+;;*    flyspell--mode-on ...                                            */
 ;;*---------------------------------------------------------------------*/
-(defun flyspell-mode-on (&optional show-msg)
+(defun flyspell--mode-on (&optional show-msg)
   "Turn Flyspell mode on.  Do not use this; use `flyspell-mode' instead.
 
 If optional argument SHOW-MSG is non-nil, show a welcome message
@@ -612,7 +599,6 @@ if `flyspell-issue-message-flag' and 
`flyspell-issue-welcome-flag'
 are both non-nil."
   (ispell-set-spellchecker-params) ; Initialize variables and dicts alists
   (setq ispell-highlight-face 'flyspell-incorrect)
-  ;; local dictionaries setup
   (or ispell-local-dictionary ispell-dictionary
       (if flyspell-default-dictionary
          (ispell-change-dictionary flyspell-default-dictionary)))
@@ -622,24 +608,16 @@ are both non-nil."
   ;; Pass the `force' argument for the case where flyspell was active already
   ;; but the buffer's local-defs have been edited.
   (flyspell-accept-buffer-local-defs 'force)
-  ;; we put the `flyspell-delayed' property on some commands
   (flyspell-delay-commands)
-  ;; we put the `flyspell-deplacement' property on some commands
   (flyspell-deplacement-commands)
-  ;; we bound flyspell action to post-command hook
   (add-hook 'post-command-hook (function flyspell-post-command-hook) t t)
-  ;; we bound flyspell action to pre-command hook
   (add-hook 'pre-command-hook (function flyspell-pre-command-hook) t t)
-  ;; we bound flyspell action to after-change hook
   (add-hook 'after-change-functions 'flyspell-after-change-function nil t)
-  ;; we bound flyspell action to hack-local-variables-hook
   (add-hook 'hack-local-variables-hook
            (function flyspell-hack-local-variables-hook) t t)
-  ;; set flyspell-generic-check-word-predicate based on the major mode
   (let ((mode-predicate (get major-mode 'flyspell-mode-predicate)))
     (if mode-predicate
        (setq flyspell-generic-check-word-predicate mode-predicate)))
-  ;; the welcome message
   (if (and flyspell-issue-message-flag
            flyspell-issue-welcome-flag
            show-msg)
@@ -726,23 +704,19 @@ has been used, the current word is not checked."
   (setq flyspell-pre-column (current-column)))
 
 ;;*---------------------------------------------------------------------*/
-;;*    flyspell-mode-off ...                                            */
+;;*    flyspell--mode-off ...                                           */
 ;;*---------------------------------------------------------------------*/
 ;;;###autoload
-(defun flyspell-mode-off ()
+(defun flyspell--mode-off ()
   "Turn Flyspell mode off."
-  ;; We remove the hooks.
   (remove-hook 'post-command-hook (function flyspell-post-command-hook) t)
   (remove-hook 'pre-command-hook (function flyspell-pre-command-hook) t)
   (remove-hook 'after-change-functions 'flyspell-after-change-function t)
   (remove-hook 'hack-local-variables-hook
               (function flyspell-hack-local-variables-hook) t)
-  ;; We remove all the flyspell highlightings.
   (flyspell-delete-all-overlays)
-  ;; We have to erase pre cache variables.
   (setq flyspell-pre-buffer nil)
   (setq flyspell-pre-point  nil)
-  ;; We mark the mode as killed.
   (setq flyspell-mode nil))
 
 ;;*---------------------------------------------------------------------*/
@@ -2381,6 +2355,9 @@ This function is meant to be added to 
`flyspell-incorrect-hook'."
 (defun flyspell-change-abbrev (table old new)
   (set (abbrev-symbol old table) new))
 
+(define-obsolete-function-alias 'flyspell-mode-on 'flyspell--mode-on "30.1")
+(define-obsolete-function-alias 'flyspell-mode-off 'flyspell--mode-off "30.1")
+
 (provide 'flyspell)
 
 ;;; flyspell.el ends here
diff --git a/lisp/textmodes/glyphless-mode.el b/lisp/textmodes/glyphless-mode.el
index 99bbb2769e4..eb4447f3048 100644
--- a/lisp/textmodes/glyphless-mode.el
+++ b/lisp/textmodes/glyphless-mode.el
@@ -30,7 +30,6 @@ The value can be any of the groups supported by
 `all', for all glyphless characters."
   :version "29.1"
   :type '(repeat (choice (const :tag "All" all)
-                         (const :tag "No font" no-font)
                          (const :tag "C0 Control" c0-control)
                          (const :tag "C1 Control" c1-control)
                          (const :tag "Format Control" format-control)
diff --git a/lisp/textmodes/html-ts-mode.el b/lisp/textmodes/html-ts-mode.el
index 4c1f410a7ef..d08d899c815 100644
--- a/lisp/textmodes/html-ts-mode.el
+++ b/lisp/textmodes/html-ts-mode.el
@@ -94,10 +94,6 @@ Return nil if there is no name or if NODE is not a defun 
node."
 
   (treesit-parser-create 'html)
 
-  ;; Comments.
-  (setq-local treesit-text-type-regexp
-              (regexp-opt '("comment" "text")))
-
   ;; Indent.
   (setq-local treesit-simple-indent-rules html-ts-mode--indent-rules)
 
@@ -106,13 +102,14 @@ Return nil if there is no name or if NODE is not a defun 
node."
 
   (setq-local treesit-defun-name-function #'html-ts-mode--defun-name)
 
-  (setq-local treesit-sentence-type-regexp "tag")
-
-  (setq-local treesit-sexp-type-regexp
-              (regexp-opt '("element"
-                            "text"
-                            "attribute"
-                            "value")))
+  (setq-local treesit-thing-settings
+              `((html
+                 (sexp ,(regexp-opt '("element"
+                                      "text"
+                                      "attribute"
+                                      "value")))
+                 (sentence "tag")
+                 (text ,(regexp-opt '("comment" "text"))))))
 
   ;; Font-lock.
   (setq-local treesit-font-lock-settings html-ts-mode--font-lock-settings)
diff --git a/lisp/textmodes/ispell.el b/lisp/textmodes/ispell.el
index c73f92aa0b3..9bd1135c5be 100644
--- a/lisp/textmodes/ispell.el
+++ b/lisp/textmodes/ispell.el
@@ -2524,9 +2524,9 @@ this function signals an error."
 
   (if lookup-dict
       (unless (file-readable-p lookup-dict)
-       (error "lookup-words error: Unreadable or missing plain word-list %s."
+        (error "ispell-lookup-words: Unreadable or missing plain word-list %s"
               lookup-dict))
-    (error (concat "lookup-words error: No plain word-list found at system"
+    (error (concat "ispell-lookup-words: No plain word-list found at system"
                    "default locations.  "
                    "Customize `ispell-alternate-dictionary' to set yours.")))
 
diff --git a/lisp/textmodes/reftex-vars.el b/lisp/textmodes/reftex-vars.el
index ebe49ae9fef..a153bfb3ec3 100644
--- a/lisp/textmodes/reftex-vars.el
+++ b/lisp/textmodes/reftex-vars.el
@@ -1156,7 +1156,7 @@ immediately offer the correct label menu - otherwise it 
will prompt you for
 a label type.  If you set this variable to nil, RefTeX will always prompt."
   :group 'reftex-referencing-labels
   :type 'boolean)
-;;;###autoload(put 'reftex-guess-label-type 'safe-local-variable (lambda (x) 
(memq x '(nil t))))
+;;;###autoload(put 'reftex-guess-label-type 'safe-local-variable #'booleanp)
 
 (defcustom reftex-format-ref-function nil
   "Function which produces the string to insert as a reference.
diff --git a/lisp/textmodes/reftex.el b/lisp/textmodes/reftex.el
index ae3ae1a198d..0a1fa8580d0 100644
--- a/lisp/textmodes/reftex.el
+++ b/lisp/textmodes/reftex.el
@@ -439,7 +439,7 @@ the label information is recompiled on next use."
         ;; When it is a symbol, remove all other symbols
         (and (symbolp entry)
              (not (memq entry list))
-             (setq list (reftex-remove-symbols-from-list list)))
+             (setq list (seq-remove #'symbolp list)))
         ;; Add to list unless already member
         (unless (member entry list)
           (setq reftex-tables-dirty t
@@ -1820,15 +1820,6 @@ When DIE is non-nil, throw an error if file not found."
       (push (pop list) rtn))
     (nreverse rtn)))
 
-(defun reftex-remove-symbols-from-list (list)
-  ;; Remove all symbols from list
-  (let (rtn)
-    (while list
-      (unless (symbolp (car list))
-        (push (car list) rtn))
-      (setq list (cdr list)))
-    (nreverse rtn)))
-
 (defun reftex-uniquify (list &optional sort)
   ;; Return a list of all strings in LIST, but each only once, keeping order
   ;; unless SORT is set (faster!).
@@ -2336,6 +2327,10 @@ Your bug report will be posted to the AUCTeX bug 
reporting list.
   (declare (obsolete "use variable `reftex-use-fonts' instead." "30.1"))
   reftex-use-fonts)
 
+(defun reftex-remove-symbols-from-list (list)
+  (declare (obsolete seq-remove "30.1"))
+  (seq-remove #'symbolp list))
+
 (provide 'reftex)
 
 ;;; reftex.el ends here
diff --git a/lisp/textmodes/sgml-mode.el b/lisp/textmodes/sgml-mode.el
index 27f3b2acd1c..5c53716c3ac 100644
--- a/lisp/textmodes/sgml-mode.el
+++ b/lisp/textmodes/sgml-mode.el
@@ -923,7 +923,7 @@ With prefix argument, only self insert."
   "Skip to beginning of tag or matching opening tag if present.
 With prefix argument ARG, repeat this ARG times.
 Return non-nil if we skipped over matched tags."
-  (interactive "p")
+  (interactive "^p")
   ;; FIXME: use sgml-get-context or something similar.
   (let ((return t))
     (while (>= arg 1)
@@ -1036,7 +1036,7 @@ an opening markup tag automatically updates the closing 
tag."
   "Skip to end of tag or matching closing tag if present.
 With prefix argument ARG, repeat this ARG times.
 Return t if after a closing tag."
-  (interactive "p")
+  (interactive "^p")
   ;; FIXME: Use sgml-get-context or something similar.
   ;; It currently might jump to an unrelated </P> if the <P>
   ;; we're skipping has no matching </P>.
diff --git a/lisp/textmodes/tex-mode.el b/lisp/textmodes/tex-mode.el
index 41c4a6a1373..a26e7b9c83a 100644
--- a/lisp/textmodes/tex-mode.el
+++ b/lisp/textmodes/tex-mode.el
@@ -2673,17 +2673,17 @@ This function is more useful than \\[tex-buffer] when 
you need the
 The last line of the buffer is displayed on
 line LINE of the window, or centered if LINE is nil."
   (interactive "P")
-  (let ((tex-shell (get-buffer "*tex-shell*"))
-       (window))
+  (let ((tex-shell (get-buffer "*tex-shell*")))
     (if (null tex-shell)
        (message "No TeX output buffer")
-      (setq window (display-buffer tex-shell display-tex-shell-buffer-action))
-      (with-selected-window window
-       (bury-buffer tex-shell)
-       (goto-char (point-max))
-       (recenter (if linenum
-                     (prefix-numeric-value linenum)
-                   (/ (window-height) 2)))))))
+      (when-let ((window
+                  (display-buffer tex-shell display-tex-shell-buffer-action)))
+        (with-selected-window window
+         (bury-buffer tex-shell)
+         (goto-char (point-max))
+         (recenter (if linenum
+                       (prefix-numeric-value linenum)
+                     (/ (window-height) 2))))))))
 
 (defcustom tex-print-file-extension ".dvi"
   "The TeX-compiled file extension for viewing and printing.
diff --git a/lisp/tmm.el b/lisp/tmm.el
index a4058594622..b587b416a35 100644
--- a/lisp/tmm.el
+++ b/lisp/tmm.el
@@ -79,7 +79,8 @@ See the documentation for `tmm-prompt'."
   "String to insert between shortcut and menu item.
 If nil, there will be no shortcuts.  It should not consist only of spaces,
 or else the correct item might not be found in the `*Completions*' buffer."
-  :type 'string)
+  :type '(choice (const :tag "No shortcuts" nil)
+                 string))
 
 (defvar tmm-mb-map nil
   "A place to store minibuffer map.")
diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el
index 577c993efcf..2621aebf037 100644
--- a/lisp/touch-screen.el
+++ b/lisp/touch-screen.el
@@ -1034,6 +1034,10 @@ If the fourth element of `touch-screen-current-tool' is
 original position of the tool to display its bound keymap as a
 menu.
 
+If the fourth element of `touch-screen-current-tool' is `drag' or
+`held', the region is active, and the tool's initial window's
+selected buffer isn't read-only, display the on screen keyboard.
+
 If the command being executed is listed in
 `touch-screen-set-point-commands' also display the on-screen
 keyboard if the current buffer and the character at the new point
@@ -1064,8 +1068,8 @@ is not read-only."
                                  posn))
                     ;; Look for the command bound to this event.
                     (command (key-binding (if prefix
-                                                 (vector prefix
-                                                         (car event))
+                                              (vector prefix
+                                                      (car event))
                                             (vector (car event)))
                                           t nil posn)))
                (deactivate-mark)
@@ -1136,9 +1140,7 @@ is not read-only."
                               ;; ... generate a mouse-1 event...
                               (list 'mouse-1 posn)
                             ;; ... otherwise, generate a drag-mouse-1 event.
-                            (list 'drag-mouse-1 (cons old-window
-                                                      old-posn)
-                                  (cons new-window posn))))
+                            (list 'drag-mouse-1 old-posn posn)))
                       (if (and (eq new-window old-window)
                                (eq new-point old-point)
                                (windowp new-window)
@@ -1146,15 +1148,34 @@ is not read-only."
                           ;; ... generate a mouse-1 event...
                           (list 'mouse-1 posn)
                         ;; ... otherwise, generate a drag-mouse-1 event.
-                        (list 'drag-mouse-1 (cons old-window
-                                                  old-posn)
-                              (cons new-window posn)))))))
+                        (list 'drag-mouse-1 old-posn posn))))))
           ((eq what 'mouse-1-menu)
            ;; Generate a `down-mouse-1' event at the position the tap
            ;; took place.
            (throw 'input-event
                   (list 'down-mouse-1
-                        (nth 4 touch-screen-current-tool)))))))
+                        (nth 4 touch-screen-current-tool))))
+          ((or (eq what 'drag)
+               ;; Merely initiating a drag is sufficient to select a
+               ;; word if word selection is enabled.
+               (eq what 'held))
+           ;; Display the on screen keyboard if the region is now
+           ;; active.  Check this within the window where the tool was
+           ;; first place.
+           (setq window (nth 1 touch-screen-current-tool))
+           (when window
+             (with-selected-window window
+               (when (and (region-active-p)
+                          (not buffer-read-only))
+                 ;; Once the on-screen keyboard has been opened, add
+                 ;; `touch-screen-window-selection-changed' as a window
+                 ;; selection change function This then prevents it from
+                 ;; being hidden after exiting the minibuffer.
+                 (progn
+                   (add-hook 'window-selection-change-functions
+                             #'touch-screen-window-selection-changed)
+                   (frame-toggle-on-screen-keyboard (selected-frame)
+                                                    nil)))))))))
 
 (defun touch-screen-handle-touch (event prefix &optional interactive)
   "Handle a single touch EVENT, and perform associated actions.
diff --git a/lisp/treesit.el b/lisp/treesit.el
index a9761dbb38d..402417c6ca9 100644
--- a/lisp/treesit.el
+++ b/lisp/treesit.el
@@ -56,6 +56,7 @@
 (declare-function treesit-parser-list "treesit.c")
 (declare-function treesit-parser-buffer "treesit.c")
 (declare-function treesit-parser-language "treesit.c")
+(declare-function treesit-parser-tag "treesit.c")
 
 (declare-function treesit-parser-root-node "treesit.c")
 
@@ -92,6 +93,8 @@
 
 (declare-function treesit-available-p "treesit.c")
 
+(defvar treesit-thing-settings)
+
 ;;; Custom options
 
 ;; Tree-sitter always appear as treesit in symbols.
@@ -133,14 +136,26 @@ Return the root node of the syntax tree."
 This is used by `treesit-language-at', which is used by various
 functions to determine which parser to use at point.
 
-The function is called with one argument, the position of point.")
+The function is called with one argument, the position of point.
+
+In general, this function should call `treesit-node-at' with an
+explicit language (usually the host language), and determine the
+language at point using the type of the returned node.
+
+DO NOT derive the language at point from parser ranges.  It's
+cumbersome and can't deal with some edge cases.")
 
 (defun treesit-language-at (position)
   "Return the language at POSITION.
+
 This function assumes that parser ranges are up-to-date.  It
 returns the return value of `treesit-language-at-point-function'
 if it's non-nil, otherwise it returns the language of the first
-parser in `treesit-parser-list', or nil if there is no parser."
+parser in `treesit-parser-list', or nil if there is no parser.
+
+In a multi-language buffer, make sure
+`treesit-language-at-point-function' is implemented!  Otherwise
+`treesit-language-at' wouldn't return the correct result."
   (if treesit-language-at-point-function
       (funcall treesit-language-at-point-function position)
     (when-let ((parser (car (treesit-parser-list))))
@@ -184,11 +199,20 @@ only look for named nodes.
 If PARSER-OR-LANG is a parser, use that parser; if PARSER-OR-LANG
 is a language, find the first parser for that language in the
 current buffer, or create one if none exists; If PARSER-OR-LANG
-is nil, try to guess the language at POS using `treesit-language-at'."
+is nil, try to guess the language at POS using `treesit-language-at'.
+
+If there's a local parser at POS, the local parser takes priority
+unless PARSER-OR-LANG is a parser, or PARSER-OR-LANG is a
+language and doesn't match the language of the local parser."
   (let* ((root (if (treesit-parser-p parser-or-lang)
                    (treesit-parser-root-node parser-or-lang)
-                 (treesit-buffer-root-node
-                  (or parser-or-lang (treesit-language-at pos)))))
+                 (or (when-let ((parser
+                                 (car (treesit-local-parsers-at
+                                       pos parser-or-lang))))
+                       (treesit-parser-root-node parser))
+                     (treesit-buffer-root-node
+                      (or parser-or-lang
+                          (treesit-language-at pos))))))
          (node root)
          (node-before root)
          (pos-1 (max (1- pos) (point-min)))
@@ -233,11 +257,20 @@ named node.
 If PARSER-OR-LANG is a parser, use that parser; if PARSER-OR-LANG
 is a language, find the first parser for that language in the
 current buffer, or create one if none exists; If PARSER-OR-LANG
-is nil, try to guess the language at BEG using `treesit-language-at'."
-  (let ((root (if (treesit-parser-p parser-or-lang)
-                  (treesit-parser-root-node parser-or-lang)
-                (treesit-buffer-root-node
-                 (or parser-or-lang (treesit-language-at beg))))))
+is nil, try to guess the language at BEG using `treesit-language-at'.
+
+If there's a local parser between BEG and END, try to use that
+parser first."
+  (let* ((lang-at-point (treesit-language-at beg))
+         (root (if (treesit-parser-p parser-or-lang)
+                   (treesit-parser-root-node parser-or-lang)
+                 (or (when-let ((parser
+                                 (car (treesit-local-parsers-on
+                                       beg end (or parser-or-lang
+                                                   lang-at-point)))))
+                       (treesit-parser-root-node parser))
+                     (treesit-buffer-root-node
+                      (or parser-or-lang lang-at-point))))))
     (treesit-node-descendant-for-range root beg (or end beg) named)))
 
 (defun treesit-node-top-level (node &optional pred include-node)
@@ -257,19 +290,21 @@ If INCLUDE-NODE is non-nil, return NODE if it satisfies 
PRED."
                             (treesit-node-parent node))
              then (treesit-node-parent cursor)
              while cursor
-             if (treesit-node-match-p cursor pred)
+             if (treesit-node-match-p cursor pred t)
              do (setq result cursor))
     result))
 
-(defun treesit-buffer-root-node (&optional language)
+(defun treesit-buffer-root-node (&optional language tag)
   "Return the root node of the current buffer.
 
 Use the first parser in the parser list if LANGUAGE is omitted.
-If LANGUAGE is non-nil, use the first parser for LANGUAGE in the
-parser list, or create one if none exists."
+
+If LANGUAGE is non-nil, use the first parser for LANGUAGE with
+TAG in the parser list, or create one if none exists.  TAG
+defaults to nil."
   (if-let ((parser
             (if language
-                (treesit-parser-create language)
+                (treesit-parser-create language nil nil tag)
               (or (car (treesit-parser-list))
                   (signal 'treesit-no-parser (list (current-buffer)))))))
       (treesit-parser-root-node parser)))
@@ -417,32 +452,40 @@ See `treesit-query-capture' for QUERY."
        (treesit-parser-root-node parser)
        query))))
 
-(defun treesit-query-range (node query &optional beg end)
+(defun treesit-query-range (node query &optional beg end offset)
   "Query the current buffer and return ranges of captured nodes.
 
 QUERY, NODE, BEG, END are the same as in `treesit-query-capture'.
 This function returns a list of (START . END), where START and
-END specifics the range of each captured node.  Capture names
-generally don't matter, but names that starts with an underscore
-are ignored."
-  (cl-loop for capture
-           in (treesit-query-capture node query beg end)
-           for name = (car capture)
-           for node = (cdr capture)
-           if (not (string-prefix-p "_" (symbol-name name)))
-           collect (cons (treesit-node-start node)
-                         (treesit-node-end node))))
+END specifics the range of each captured node.  OFFSET is an
+optional pair of numbers (START-OFFSET . END-OFFSET).  The
+respective offset values are added to each (START . END) range
+being returned.  Capture names generally don't matter, but names
+that starts with an underscore are ignored."
+  (let ((offset-left (or (car offset) 0))
+        (offset-right (or (cdr offset) 0)))
+    (cl-loop for capture
+             in (treesit-query-capture node query beg end)
+             for name = (car capture)
+             for node = (cdr capture)
+             if (not (string-prefix-p "_" (symbol-name name)))
+             collect (cons (+ (treesit-node-start node) offset-left)
+                           (+ (treesit-node-end node) offset-right)))))
 
 ;;; Range API supplement
 
 (defvar-local treesit-range-settings nil
   "A list of range settings.
 
-Each element of the list is of the form (QUERY LANGUAGE).
-When updating the range of each parser in the buffer,
+Each element of the list is of the form (QUERY LANGUAGE LOCAL-P
+OFFSET).  When updating the range of each parser in the buffer,
 `treesit-update-ranges' queries each QUERY, and sets LANGUAGE's
 range to the range spanned by captured nodes.  QUERY must be a
-compiled query.
+compiled query.  If LOCAL-P is t, give each range a separate
+local parser rather than using a single parser for all the
+ranges.  If OFFSET is non-nil, it should be a cons of
+numbers (START-OFFSET . END-OFFSET), where the start and end
+offset are added to each queried range to get the result ranges.
 
 Capture names generally don't matter, but names that starts with
 an underscore are ignored.
@@ -475,6 +518,7 @@ it.  For example,
     (treesit-range-rules
      :embed \\='javascript
      :host \\='html
+     :offset \\='(1 . -1)
      \\='((script_element (raw_text) @cap)))
 
 The `:embed' keyword specifies the embedded language, and the
@@ -483,15 +527,28 @@ this way: Emacs queries QUERY in the host language's 
parser,
 computes the ranges spanned by the captured nodes, and applies
 these ranges to parsers for the embedded language.
 
+If there's a `:local' keyword with value t, the range computed by
+this QUERY is given a dedicated local parser.  Otherwise, the
+range shares the same parser with other ranges.
+
+If there's an `:offset' keyword with a pair of numbers, each
+captured range is offset by those numbers.  For example, an
+offset of (1 . -1) will update a captured range of (2 . 8) to
+be (3 . 7).  This can be used to exclude things like surrounding
+delimiters from being included in the range covered by an
+embedded parser.
+
 QUERY can also be a function that takes two arguments, START and
 END.  If QUERY is a function, it doesn't need the :KEYWORD VALUE
 pair preceding it.  This function should set the ranges for
 parsers in the current buffer in the region between START and
 END.  It is OK for this function to set ranges in a larger region
 that encompasses the region between START and END."
-  (let (host embed result)
+  (let (host embed offset result local)
     (while query-specs
       (pcase (pop query-specs)
+        (:local (when (eq t (pop query-specs))
+                  (setq local t)))
         (:host (let ((host-lang (pop query-specs)))
                  (unless (symbolp host-lang)
                    (signal 'treesit-error (list "Value of :host option should 
be a symbol" host-lang)))
@@ -500,6 +557,12 @@ that encompasses the region between START and END."
                   (unless (symbolp embed-lang)
                     (signal 'treesit-error (list "Value of :embed option 
should be a symbol" embed-lang)))
                   (setq embed embed-lang)))
+        (:offset (let ((range-offset (pop query-specs)))
+                   (unless (and (consp range-offset)
+                                (numberp (car range-offset))
+                                (numberp (cdr range-offset)))
+                    (signal 'treesit-error (list "Value of :offset option 
should be a pair of numbers" range-offset)))
+                  (setq offset range-offset)))
         (query (if (functionp query)
                    (push (list query nil nil) result)
                  (when (null embed)
@@ -507,9 +570,9 @@ that encompasses the region between START and END."
                  (when (null host)
                    (signal 'treesit-error (list "Value of :host option cannot 
be omitted")))
                  (push (list (treesit-query-compile host query)
-                             embed host)
+                             embed local offset)
                        result))
-               (setq host nil embed nil))))
+               (setq host nil embed nil offset nil))))
     (nreverse result)))
 
 (defun treesit--merge-ranges (old-ranges new-ranges start end)
@@ -558,6 +621,73 @@ those inside are kept."
            if (<= start (car range) (cdr range) end)
            collect range))
 
+(defun treesit-local-parsers-at (&optional pos language)
+  "Return all the local parsers at POS.
+
+POS defaults to point.
+Local parsers are those which only parse a limited region marked
+by an overlay with non-nil `treesit-parser' property.
+If LANGUAGE is non-nil, only return parsers for LANGUAGE."
+  (let ((res nil))
+    (dolist (ov (overlays-at (or pos (point))))
+      (when-let ((parser (overlay-get ov 'treesit-parser)))
+        (when (or (null language)
+                  (eq (treesit-parser-language parser)
+                      language))
+          (push parser res))))
+    (nreverse res)))
+
+(defun treesit-local-parsers-on (&optional beg end language)
+  "Return all the local parsers between BEG END.
+
+BEG and END default to the beginning and end of the buffer's
+accessible portion.
+Local parsers are those which have an `embedded' tag, and only parse
+a limited region marked by an overlay with a non-nil `treesit-parser'
+property.  If LANGUAGE is non-nil, only return parsers for LANGUAGE."
+  (let ((res nil))
+    (dolist (ov (overlays-in (or beg (point-min)) (or end (point-max))))
+      (when-let ((parser (overlay-get ov 'treesit-parser)))
+        (when (or (null language)
+                  (eq (treesit-parser-language parser)
+                      language))
+          (push parser res))))
+    (nreverse res)))
+
+(defun treesit--update-ranges-local
+    (query embedded-lang &optional beg end)
+  "Update range for local parsers betwwen BEG and END.
+Use QUERY to get the ranges, and make sure each range has a local
+parser for EMBEDDED-LANG."
+  ;; Clean up.
+  (dolist (ov (overlays-in (or beg (point-min)) (or end (point-max))))
+    (when-let ((parser (overlay-get ov 'treesit-parser)))
+      (when (eq (overlay-start ov) (overlay-end ov))
+        (delete-overlay ov)
+        (treesit-parser-delete parser))))
+  ;; Update range.
+  (let* ((host-lang (treesit-query-language query))
+         (ranges (treesit-query-range host-lang query beg end)))
+    (pcase-dolist (`(,beg . ,end) ranges)
+      (let ((has-parser nil))
+        (dolist (ov (overlays-in beg end))
+          ;; Update range of local parser.
+          (let ((embedded-parser (overlay-get ov 'treesit-parser)))
+            (when (and (treesit-parser-p embedded-parser)
+                       (eq (treesit-parser-language embedded-parser)
+                           embedded-lang))
+              (treesit-parser-set-included-ranges
+               embedded-parser `((,beg . ,end)))
+              (setq has-parser t))))
+        ;; Create overlay and local parser.
+        (when (not has-parser)
+          (let ((embedded-parser (treesit-parser-create
+                                  embedded-lang nil t 'embedded))
+                (ov (make-overlay beg end nil nil t)))
+            (overlay-put ov 'treesit-parser embedded-parser)
+            (treesit-parser-set-included-ranges
+             embedded-parser `((,beg . ,end)))))))))
+
 (defun treesit-update-ranges (&optional beg end)
   "Update the ranges for each language in the current buffer.
 If BEG and END are non-nil, only update parser ranges in that
@@ -570,23 +700,32 @@ region."
   (dolist (setting treesit-range-settings)
     (let ((query (nth 0 setting))
           (language (nth 1 setting))
+          (local (nth 2 setting))
+          (offset (nth 3 setting))
           (beg (or beg (point-min)))
           (end (or end (point-max))))
-      (if (functionp query) (funcall query beg end)
+      (cond
+       ((functionp query) (funcall query beg end))
+       (local
+        (treesit--update-ranges-local query language beg end))
+       (t
         (let* ((host-lang (treesit-query-language query))
                (parser (treesit-parser-create language))
                (old-ranges (treesit-parser-included-ranges parser))
                (new-ranges (treesit-query-range
-                            host-lang query beg end))
+                            host-lang query beg end offset))
                (set-ranges (treesit--clip-ranges
                             (treesit--merge-ranges
                              old-ranges new-ranges beg end)
                             (point-min) (point-max))))
-          (dolist (parser (treesit-parser-list))
-            (when (eq (treesit-parser-language parser)
-                      language)
+          (dolist (parser (treesit-parser-list nil language))
               (treesit-parser-set-included-ranges
-               parser set-ranges))))))))
+               parser (or set-ranges
+                          ;; When there's no range for the embedded
+                          ;; language, set it's range to a dummy (1
+                          ;; . 1), otherwise it would be set to the
+                          ;; whole buffer, which is not what we want.
+                          `((,(point-min) . ,(point-min))))))))))))
 
 (defun treesit-parser-range-on (parser beg &optional end)
   "Check if PARSER's range covers the portion between BEG and END.
@@ -844,7 +983,8 @@ name, it is ignored."
 (defvar treesit--font-lock-verbose nil
   "If non-nil, print debug messages when fontifying.")
 
-(defun treesit-font-lock-recompute-features (&optional add-list remove-list)
+(defun treesit-font-lock-recompute-features
+    (&optional add-list remove-list language)
   "Enable/disable font-lock features.
 
 Enable each feature in ADD-LIST, disable each feature in
@@ -859,7 +999,10 @@ the features are disabled.
 
 ADD-LIST and REMOVE-LIST are lists of feature symbols.  The
 same feature symbol cannot appear in both lists; the function
-signals the `treesit-font-lock-error' error if that happens."
+signals the `treesit-font-lock-error' error if that happens.
+
+If LANGUAGE is non-nil, only compute features for that language,
+and leave settings for other languages unchanged."
   (when-let ((intersection (cl-intersection add-list remove-list)))
     (signal 'treesit-font-lock-error
             (list "ADD-LIST and REMOVE-LIST contain the same feature"
@@ -879,9 +1022,13 @@ signals the `treesit-font-lock-error' error if that 
happens."
          (additive (or add-list remove-list)))
     (cl-loop for idx = 0 then (1+ idx)
              for setting in treesit-font-lock-settings
+             for lang = (treesit-query-language (nth 0 setting))
              for feature = (nth 2 setting)
              for current-value = (nth 1 setting)
-             ;; Set the ENABLE flag for the setting.
+             ;; Set the ENABLE flag for the setting if its language is
+             ;; relevant.
+             if (or (null language)
+                    (eq language lang))
              do (setf (nth 1 (nth idx treesit-font-lock-settings))
                       (cond
                        ((not additive)
@@ -1038,72 +1185,92 @@ If LOUDLY is non-nil, display some debugging 
information."
     (message "Fontifying region: %s-%s" start end))
   (treesit-update-ranges start end)
   (font-lock-unfontify-region start end)
-  (dolist (setting treesit-font-lock-settings)
-    (let* ((query (nth 0 setting))
-           (enable (nth 1 setting))
-           (override (nth 3 setting))
-           (language (treesit-query-language query)))
-
-      ;; Use deterministic way to decide whether to turn on "fast
-      ;; mode". (See bug#60691, bug#60223.)
-      (when (eq treesit--font-lock-fast-mode 'unspecified)
-        (pcase-let ((`(,max-depth ,max-width)
-                     (treesit-subtree-stat
-                      (treesit-buffer-root-node language))))
-          (if (or (> max-depth 100) (> max-width 4000))
-              (setq treesit--font-lock-fast-mode t)
-            (setq treesit--font-lock-fast-mode nil))))
-
-      (when-let* ((root (treesit-buffer-root-node language))
-                  (nodes (if (eq t treesit--font-lock-fast-mode)
-                             (treesit--children-covering-range-recurse
-                              root start end (* 4 jit-lock-chunk-size))
-                           (list (treesit-buffer-root-node language))))
-                  ;; Only activate if ENABLE flag is t.
-                  (activate (eq t enable)))
-        (ignore activate)
-
-        ;; Query each node.
-        (dolist (sub-node nodes)
-          (let* ((delta-start (car treesit--font-lock-query-expand-range))
-                 (delta-end (cdr treesit--font-lock-query-expand-range))
-                 (captures (treesit-query-capture
-                            sub-node query
-                            (max (- start delta-start) (point-min))
-                            (min (+ end delta-end) (point-max)))))
-
-            ;; For each captured node, fontify that node.
-            (with-silent-modifications
-              (dolist (capture captures)
-                (let* ((face (car capture))
-                       (node (cdr capture))
-                       (node-start (treesit-node-start node))
-                       (node-end (treesit-node-end node)))
-
-                  ;; If node is not in the region, take them out.  See
-                  ;; comment #3 above for more detail.
-                  (if (and (facep face)
-                           (or (>= start node-end) (>= node-start end)))
-                      (when (or loudly treesit--font-lock-verbose)
-                        (message "Captured node %s(%s-%s) but it is outside of 
fontifing region" node node-start node-end))
-
-                    (cond
-                     ((facep face)
-                      (treesit-fontify-with-override
-                       (max node-start start) (min node-end end)
-                       face override))
-                     ((functionp face)
-                      (funcall face node override start end)))
-
-                    ;; Don't raise an error if FACE is neither a face nor
-                    ;; a function.  This is to allow intermediate capture
-                    ;; names used for #match and #eq.
-                    (when (or loudly treesit--font-lock-verbose)
-                      (message "Fontifying text from %d to %d, Face: %s, Node: 
%s"
-                               (max node-start start) (min node-end end)
-                               face (treesit-node-type node))))))))))))
+  (let* ((local-parsers (treesit-local-parsers-on start end))
+         (global-parsers (treesit-parser-list))
+         (root-nodes
+          (mapcar #'treesit-parser-root-node
+                  (append local-parsers global-parsers))))
+    (dolist (setting treesit-font-lock-settings)
+      (let* ((query (nth 0 setting))
+             (enable (nth 1 setting))
+             (override (nth 3 setting))
+             (language (treesit-query-language query))
+             (root-nodes (cl-remove-if-not
+                          (lambda (node)
+                            (eq (treesit-node-language node) language))
+                          root-nodes)))
+
+        ;; Use deterministic way to decide whether to turn on "fast
+        ;; mode". (See bug#60691, bug#60223.)
+        (when (eq treesit--font-lock-fast-mode 'unspecified)
+          (pcase-let ((`(,max-depth ,max-width)
+                       (treesit-subtree-stat
+                        (treesit-buffer-root-node language))))
+            (if (or (> max-depth 100) (> max-width 4000))
+                (setq treesit--font-lock-fast-mode t)
+              (setq treesit--font-lock-fast-mode nil))))
+
+        ;; Only activate if ENABLE flag is t.
+        (when-let
+            ((activate (eq t enable))
+             (nodes (if (eq t treesit--font-lock-fast-mode)
+                        (mapcan
+                         (lambda (node)
+                           (treesit--children-covering-range-recurse
+                            node start end (* 4 jit-lock-chunk-size)))
+                         root-nodes)
+                      root-nodes)))
+          (ignore activate)
+
+          ;; Query each node.
+          (dolist (sub-node nodes)
+            (treesit--font-lock-fontify-region-1
+             sub-node query start end override loudly))))))
   `(jit-lock-bounds ,start . ,end))
 
+(defun treesit--font-lock-fontify-region-1 (node query start end override 
loudly)
+  "Fontify the region between START and END by querying NODE with QUERY.
+
+If OVERRIDE is non-nil, override existing faces, if LOUDLY is
+non-nil, print debugging information."
+  (let* ((delta-start (car treesit--font-lock-query-expand-range))
+         (delta-end (cdr treesit--font-lock-query-expand-range))
+         (captures (treesit-query-capture
+                    node query
+                    (max (- start delta-start) (point-min))
+                    (min (+ end delta-end) (point-max)))))
+
+    ;; For each captured node, fontify that node.
+    (with-silent-modifications
+      (dolist (capture captures)
+        (let* ((face (car capture))
+               (node (cdr capture))
+               (node-start (treesit-node-start node))
+               (node-end (treesit-node-end node)))
+
+          ;; If node is not in the region, take them out.  See
+          ;; comment #3 above for more detail.
+          (if (and (facep face)
+                   (or (>= start node-end) (>= node-start end)))
+              (when (or loudly treesit--font-lock-verbose)
+                (message "Captured node %s(%s-%s) but it is outside of 
fontifing region" node node-start node-end))
+
+            (cond
+             ((facep face)
+              (treesit-fontify-with-override
+               (max node-start start) (min node-end end)
+               face override))
+             ((functionp face)
+              (funcall face node override start end)))
+
+            ;; Don't raise an error if FACE is neither a face nor
+            ;; a function.  This is to allow intermediate capture
+            ;; names used for #match and #eq.
+            (when (or loudly treesit--font-lock-verbose)
+              (message "Fontifying text from %d to %d, Face: %s, Node: %s"
+                       (max node-start start) (min node-end end)
+                       face (treesit-node-type node)))))))))
+
 (defun treesit--font-lock-notifier (ranges parser)
   "Ensures updated parts of the parse-tree are refontified.
 RANGES is a list of (BEG . END) ranges, PARSER is the tree-sitter
@@ -1518,12 +1685,15 @@ Return (ANCHOR . OFFSET).  This function is used by
                 (forward-line 0)
                 (skip-chars-forward " \t")
                 (point)))
+         (local-parsers (treesit-local-parsers-at bol))
          (smallest-node
           (cond ((null (treesit-parser-list)) nil)
-                ((eq 1 (length (treesit-parser-list)))
+                (local-parsers (treesit-node-at
+                                bol (car local-parsers)))
+                ((eq 1 (length (treesit-parser-list nil nil t)))
                  (treesit-node-at bol))
-                ((treesit-language-at (point))
-                 (treesit-node-at bol (treesit-language-at (point))))
+                ((treesit-language-at bol)
+                 (treesit-node-at bol (treesit-language-at bol)))
                 (t (treesit-node-at bol))))
          (root (treesit-parser-root-node
                 (treesit-node-parser smallest-node)))
@@ -1701,12 +1871,28 @@ OFFSET."
                         (message "No matched rule"))
                       (cons nil nil))))))
 
-(defun treesit-check-indent (mode)
-  "Check current buffer's indentation against a major mode MODE.
+(defun treesit--read-major-mode ()
+  "Read a major mode using completion.
+Helper function to use in the `interactive' spec of `treesit-check-indent'."
+  (let* ((default (and (symbolp major-mode) (symbol-name major-mode)))
+        (mode
+         (completing-read
+          (format-prompt "Target major mode" default)
+          obarray
+          (lambda (sym)
+            (and (string-suffix-p "-mode" (symbol-name sym))
+                 (not (or (memq sym minor-mode-list)
+                           (string-suffix-p "-minor-mode"
+                                            (symbol-name sym))))))
+          nil nil nil default nil)))
+    (cond
+     ((equal mode "nil") nil)
+     ((and (stringp mode) (fboundp (intern mode))) (intern mode))
+     (t mode))))
 
-Pop up a diff buffer showing the difference.  Correct
-indentation (target) is in green, current indentation is in red."
-  (interactive "CTarget major mode: ")
+(defun treesit-check-indent (mode)
+  "Compare the current buffer with how major mode MODE would indent it."
+  (interactive (list (treesit--read-major-mode)))
   (let ((source-buf (current-buffer)))
     (with-temp-buffer
       (insert-buffer-substring source-buf)
@@ -1799,6 +1985,8 @@ BACKWARD and ALL are the same as in 
`treesit-search-forward'."
       (goto-char current-pos)))
     node))
 
+(make-obsolete 'treesit-sexp-type-regexp "`treesit-sexp-type-regexp' will be 
removed in a few months, use `treesit-thing-settings' instead." "30.0.5")
+
 (defvar-local treesit-sexp-type-regexp nil
   "A regexp that matches the node type of sexp nodes.
 
@@ -1816,7 +2004,7 @@ like `forward-sexp' does.  If point is already at 
top-level,
 return nil without moving point."
   (interactive "^p")
   (let ((arg (or arg 1))
-        (pred treesit-sexp-type-regexp))
+        (pred (or treesit-sexp-type-regexp 'sexp)))
     (or (if (> arg 0)
             (treesit-end-of-thing pred (abs arg) 'restricted)
           (treesit-beginning-of-thing pred (abs arg) 'restricted))
@@ -1842,7 +2030,12 @@ its sibling node ARG nodes away.
 Return a pair of positions as described by
 `transpose-sexps-function' for use in `transpose-subr' and
 friends."
-  (let* ((parent (treesit-node-parent (treesit-node-at (point))))
+  ;; First arrive at the right level at where the node at point is
+  ;; considered a sexp. If sexp isn't defined, or we can't find any
+  ;; node that's a sexp, use the node at point.
+  (let* ((node (or (treesit-thing-at-point 'sexp 'nested)
+                   (treesit-node-at (point))))
+         (parent (treesit-node-parent node))
          (child (treesit-node-child parent 0 t)))
     (named-let loop ((prev child)
                      (next (treesit-node-next-sibling child t)))
@@ -1900,9 +2093,6 @@ for invalid node.
 
 This is used by `treesit-beginning-of-defun' and friends.")
 
-(defvar-local treesit-block-type-regexp nil
-  "Like `treesit-defun-type-regexp', but for blocks.")
-
 (defvar-local treesit-defun-tactic 'nested
   "Determines how does Emacs treat nested defuns.
 If the value is `top-level', Emacs only moves across top-level
@@ -1928,11 +2118,28 @@ nil.")
   "The delimiter used to connect several defun names.
 This is used in `treesit-add-log-current-defun'.")
 
-(defun treesit-beginning-of-thing (pred &optional arg tactic)
+(defun treesit-thing-definition (thing language)
+  "Return the predicate for THING if it's defined for LANGUAGE.
+A thing is considered defined if it has an entry in
+`treesit-thing-settings'.
+
+If LANGUAGE is nil, return the first definition for THING in
+`treesit-thing-settings'."
+  (if language
+      (car (alist-get thing (alist-get language
+                                       treesit-thing-settings)))
+    (car (alist-get thing (mapcan (lambda (entry)
+                                    (copy-tree (cdr entry)))
+                                  treesit-thing-settings)))))
+
+(defalias 'treesit-thing-defined-p 'treesit-thing-definition
+  "Return non-nil if THING is defined.")
+
+(defun treesit-beginning-of-thing (thing &optional arg tactic)
   "Like `beginning-of-defun', but generalized into things.
 
-PRED is like `treesit-defun-type-regexp', ARG
-is the same as in `beginning-of-defun'.
+THING can be a thing defined in `treesit-thing-settings', which see,
+or a predicate.  ARG is the same as in `beginning-of-defun'.
 
 TACTIC determines how does this function move between things.  It
 can be `nested', `top-level', `restricted', or nil.  `nested'
@@ -1947,15 +2154,15 @@ should there be one.  If omitted, TACTIC is considered 
to be
 Return non-nil if successfully moved, nil otherwise."
   (pcase-let* ((arg (or arg 1))
                (dest (treesit--navigate-thing
-                      (point) (- arg) 'beg pred tactic)))
+                      (point) (- arg) 'beg thing tactic)))
     (when dest
       (goto-char dest))))
 
-(defun treesit-end-of-thing (pred &optional arg tactic)
+(defun treesit-end-of-thing (thing &optional arg tactic)
   "Like `end-of-defun', but generalized into things.
 
-PRED is like `treesit-defun-type-regexp', ARG is the same as
-in `end-of-defun'.
+THING can be a thing defined in `treesit-thing-settings', which
+see, or a predicate.  ARG is the same as in `end-of-defun'.
 
 TACTIC determines how does this function move between things.  It
 can be `nested', `top-level', `restricted', or nil.  `nested'
@@ -1970,7 +2177,7 @@ should there be one.  If omitted, TACTIC is considered to 
be
 Return non-nil if successfully moved, nil otherwise."
   (pcase-let* ((arg (or arg 1))
                (dest (treesit--navigate-thing
-                      (point) arg 'end pred tactic)))
+                      (point) arg 'end thing tactic)))
     (when dest
       (goto-char dest))))
 
@@ -1984,19 +2191,21 @@ If search is successful, return t, otherwise return nil.
 
 This is a tree-sitter equivalent of `beginning-of-defun'.
 Behavior of this function depends on `treesit-defun-type-regexp'
-and `treesit-defun-skipper'."
+and `treesit-defun-skipper'.  If `treesit-defun-type-regexp' is
+not set, Emacs also looks for definition of defun in
+`treesit-thing-settings'."
   (interactive "^p")
   (or (not (eq this-command 'treesit-beginning-of-defun))
       (eq last-command 'treesit-beginning-of-defun)
       (and transient-mark-mode mark-active)
       (push-mark))
   (let ((orig-point (point))
-        (success nil))
+        (success nil)
+        (pred (or treesit-defun-type-regexp 'defun)))
     (catch 'done
       (dotimes (_ 2)
 
-        (when (treesit-beginning-of-thing
-               treesit-defun-type-regexp arg treesit-defun-tactic)
+        (when (treesit-beginning-of-thing pred arg treesit-defun-tactic)
           (when treesit-defun-skipper
             (funcall treesit-defun-skipper)
             (setq success t)))
@@ -2017,9 +2226,12 @@ Negative argument -N means move back to Nth preceding 
end of defun.
 
 This is a tree-sitter equivalent of `end-of-defun'.  Behavior of
 this function depends on `treesit-defun-type-regexp' and
-`treesit-defun-skipper'."
+`treesit-defun-skipper'.  If `treesit-defun-type-regexp' is not
+set, Emacs also looks for definition of defun in
+`treesit-thing-settings'."
   (interactive "^p\nd")
-  (let ((orig-point (point)))
+  (let ((orig-point (point))
+        (pred (or treesit-defun-type-regexp 'defun)))
     (if (or (null arg) (= arg 0)) (setq arg 1))
     (or (not (eq this-command 'treesit-end-of-defun))
         (eq last-command 'treesit-end-of-defun)
@@ -2028,8 +2240,7 @@ this function depends on `treesit-defun-type-regexp' and
     (catch 'done
       (dotimes (_ 2) ; Not making progress is better than infloop.
 
-        (when (treesit-end-of-thing
-               treesit-defun-type-regexp arg treesit-defun-tactic)
+        (when (treesit-end-of-thing pred arg treesit-defun-tactic)
           (when treesit-defun-skipper
             (funcall treesit-defun-skipper)))
 
@@ -2041,6 +2252,8 @@ this function depends on `treesit-defun-type-regexp' and
             (throw 'done nil)
           (setq arg (if (> arg 0) (1+ arg) (1- arg))))))))
 
+(make-obsolete 'treesit-text-type-regexp "`treesit-text-type-regexp' will be 
removed in a few months, use `treesit-thing-settings' instead." "30.0.5")
+
 (defvar-local treesit-text-type-regexp "\\`comment\\'"
   "A regexp that matches the node type of textual nodes.
 
@@ -2050,6 +2263,8 @@ comments and multiline string literals.  For example,
 \"text_block\" in the case of a string.  This is used by
 `prog-fill-reindent-defun' and friends.")
 
+(make-obsolete 'treesit-sentence-type-regexp "`treesit-sentence-type-regexp' 
will be removed in a few months, use `treesit-thing-settings' instead." 
"30.0.5")
+
 (defvar-local treesit-sentence-type-regexp nil
   "A regexp that matches the node type of sentence nodes.
 
@@ -2059,21 +2274,21 @@ smaller in scope than defuns.  This is used by
 `treesit-forward-sentence' and friends.")
 
 (defun treesit-forward-sentence (&optional arg)
-  "Tree-sitter `forward-sentence-function' function.
+  "Tree-sitter `forward-sentence-function' implementation.
 
 ARG is the same as in `forward-sentence'.
 
-If inside comment or other nodes described in
-`treesit-sentence-type-regexp', use
-`forward-sentence-default-function', else move across nodes as
-described by `treesit-sentence-type-regexp'."
-  (if (string-match-p
-       treesit-text-type-regexp
-       (treesit-node-type (treesit-node-at (point))))
+If point is inside a text environment, go forward a prose
+sentence using `forward-sentence-default-function'.  If point is
+inside code, go forward a source code sentence.
+
+What constitutes as text and source code sentence is determined
+by `text' and `sentence' in `treesit-thing-settings'."
+  (if (treesit-node-match-p (treesit-node-at (point)) 'text t)
       (funcall #'forward-sentence-default-function arg)
     (funcall
      (if (> arg 0) #'treesit-end-of-thing #'treesit-beginning-of-thing)
-     treesit-sentence-type-regexp (abs arg))))
+     'sentence (abs arg))))
 
 (defun treesit-default-defun-skipper ()
   "Skips spaces after navigating a defun.
@@ -2103,7 +2318,7 @@ the current line if the beginning of the defun is 
indented."
 ;; parent:
 ;; 1. node covers pos
 ;; 2. smallest such node
-(defun treesit--things-around (pos pred)
+(defun treesit--things-around (pos thing)
   "Return the previous, next, and parent thing around POS.
 
 Return a list of (PREV NEXT PARENT), where PREV and NEXT are
@@ -2111,8 +2326,8 @@ previous and next sibling things around POS, and PARENT 
is the
 parent thing surrounding POS.  All of three could be nil if no
 sound things exists.
 
-PRED can be a regexp, a predicate function, and more.  See
-`treesit-thing-settings' for details."
+THING should be a thing defined in `treesit-thing-settings',
+which see; it can also be a predicate."
   (let* ((node (treesit-node-at pos))
          (result (list nil nil nil)))
     ;; 1. Find previous and next sibling defuns.
@@ -2135,7 +2350,7 @@ PRED can be a regexp, a predicate function, and more.  See
      when node
      do (let ((cursor node)
               (iter-pred (lambda (node)
-                           (and (treesit-node-match-p node pred)
+                           (and (treesit-node-match-p node thing t)
                                 (funcall pos-pred node)))))
           ;; Find the node just before/after POS to start searching.
           (save-excursion
@@ -2149,11 +2364,11 @@ PRED can be a regexp, a predicate function, and more.  
See
             (setf (nth idx result)
                   (treesit-node-top-level cursor iter-pred t))
             (setq cursor (treesit-search-forward
-                          cursor pred backward backward)))))
+                          cursor thing backward backward)))))
     ;; 2. Find the parent defun.
     (let ((cursor (or (nth 0 result) (nth 1 result) node))
           (iter-pred (lambda (node)
-                       (and (treesit-node-match-p node pred)
+                       (and (treesit-node-match-p node thing t)
                             (not (treesit-node-eq node (nth 0 result)))
                             (not (treesit-node-eq node (nth 1 result)))
                             (< (treesit-node-start node)
@@ -2190,7 +2405,7 @@ PRED can be a regexp, a predicate function, and more.  See
 ;;    -> Obviously we don't want to go to parent's end, instead, we
 ;;       want to go to parent's prev-sibling's end.  Again, we recurse
 ;;       in the function to do that.
-(defun treesit--navigate-thing (pos arg side pred &optional tactic recursing)
+(defun treesit--navigate-thing (pos arg side thing &optional tactic recursing)
   "Navigate thing ARG steps from POS.
 
 If ARG is positive, move forward that many steps, if negative,
@@ -2201,7 +2416,7 @@ This function doesn't actually move point, it just 
returns the
 position it would move to.  If there aren't enough things to move
 across, return nil.
 
-PRED can be a regexp, a predicate function, and more.  See
+THING can be a regexp, a predicate function, and more.  See
 `treesit-thing-settings' for details.
 
 TACTIC determines how does this function move between things.  It
@@ -2231,13 +2446,13 @@ function is called recursively."
       (while (> counter 0)
         (pcase-let
             ((`(,prev ,next ,parent)
-              (treesit--things-around pos pred)))
+              (treesit--things-around pos thing)))
           ;; When PARENT is nil, nested and top-level are the same, if
           ;; there is a PARENT, make PARENT to be the top-level parent
           ;; and pretend there is no nested PREV and NEXT.
           (when (and (eq tactic 'top-level)
                      parent)
-            (setq parent (treesit-node-top-level parent pred t)
+            (setq parent (treesit-node-top-level parent thing t)
                   prev nil
                   next nil))
           ;; If TACTIC is `restricted', the implementation is very simple.
@@ -2269,7 +2484,7 @@ function is called recursively."
                     ;; the end of next before recurring.)
                     (setq pos (or (treesit--navigate-thing
                                    (treesit-node-end (or next parent))
-                                   1 'beg pred tactic t)
+                                   1 'beg thing tactic t)
                                   (throw 'term nil)))
                   ;; Normal case.
                   (setq pos (funcall advance (or next parent))))
@@ -2281,7 +2496,7 @@ function is called recursively."
                   ;; Special case: go to prev end-of-defun.
                   (setq pos (or (treesit--navigate-thing
                                  (treesit-node-start (or prev parent))
-                                 -1 'end pred tactic t)
+                                 -1 'end thing tactic t)
                                 (throw 'term nil)))
                 ;; Normal case.
                 (setq pos (funcall advance (or prev parent))))))
@@ -2291,17 +2506,17 @@ function is called recursively."
     (if (eq counter 0) pos nil)))
 
 ;; TODO: In corporate into thing-at-point.
-(defun treesit-thing-at-point (pred tactic)
+(defun treesit-thing-at-point (thing tactic)
   "Return the thing node at point or nil if none is found.
 
-\"Thing\" is defined by PRED, which can be a regexp, a
+\"Thing\" is defined by THING, which can be a regexp, a
 predication function, and more, see `treesit-thing-settings'
 for details.
 
 Return the top-level defun if TACTIC is `top-level', return the
 immediate parent thing if TACTIC is `nested'."
   (pcase-let* ((`(,_ ,next ,parent)
-                (treesit--things-around (point) pred))
+                (treesit--things-around (point) thing))
                ;; If point is at the beginning of a thing, we
                ;; prioritize that thing over the parent in nested
                ;; mode.
@@ -2309,7 +2524,7 @@ immediate parent thing if TACTIC is `nested'."
                               next)
                          parent)))
     (if (eq tactic 'top-level)
-        (treesit-node-top-level node pred t)
+        (treesit-node-top-level node thing t)
       node)))
 
 (defun treesit-defun-at-point ()
@@ -2319,10 +2534,11 @@ Respects `treesit-defun-tactic': return the top-level 
defun if it
 is `top-level', return the immediate parent defun if it is
 `nested'.
 
-Return nil if `treesit-defun-type-regexp' is not set."
-  (when treesit-defun-type-regexp
+Return nil if `treesit-defun-type-regexp' isn't set and `defun'
+isn't defined in `treesit-thing-settings'."
+  (when (or treesit-defun-type-regexp (treesit-thing-defined-p 'defun))
     (treesit-thing-at-point
-     treesit-defun-type-regexp treesit-defun-tactic)))
+     (or treesit-defun-type-regexp 'defun) treesit-defun-tactic)))
 
 (defun treesit-defun-name (node)
   "Return the defun name of NODE.
@@ -2495,14 +2711,18 @@ and enable `font-lock-mode'.
 
 If `treesit-simple-indent-rules' is non-nil, set up indentation.
 
-If `treesit-defun-type-regexp' is non-nil, set up
-`beginning-of-defun-function' and `end-of-defun-function'.
+If `treesit-defun-type-regexp' is non-nil or `defun' is defined
+in `treesit-thing-settings', set up `beginning-of-defun-function'
+and `end-of-defun-function'.
 
 If `treesit-defun-name-function' is non-nil, set up
 `add-log-current-defun'.
 
 If `treesit-simple-imenu-settings' is non-nil, set up Imenu.
 
+If `sexp', `sentence' are defined in `treesit-thing-settings',
+enable tree-sitter navigation commands for them.
+
 Make sure necessary parsers are created for the current buffer
 before calling this function."
   ;; Font-lock.
@@ -2513,7 +2733,6 @@ before calling this function."
                 '( nil nil nil nil
                    (font-lock-fontify-syntactically-function
                     . treesit-font-lock-fontify-region)))
-    (font-lock-mode 1)
     (treesit-font-lock-recompute-features)
     (dolist (parser (treesit-parser-list))
       (treesit-parser-add-notifier
@@ -2526,7 +2745,8 @@ before calling this function."
     (setq-local indent-line-function #'treesit-indent)
     (setq-local indent-region-function #'treesit-indent-region))
   ;; Navigation.
-  (when treesit-defun-type-regexp
+  (when (or treesit-defun-type-regexp
+            (treesit-thing-defined-p 'defun nil))
     (keymap-set (current-local-map) "<remap> <beginning-of-defun>"
                 #'treesit-beginning-of-defun)
     (keymap-set (current-local-map) "<remap> <end-of-defun>"
@@ -2545,16 +2765,23 @@ before calling this function."
     (setq-local add-log-current-defun-function
                 #'treesit-add-log-current-defun))
 
-  (when treesit-sexp-type-regexp
-    (setq-local forward-sexp-function #'treesit-forward-sexp))
-  (setq-local transpose-sexps-function #'treesit-transpose-sexps)
-  (when treesit-sentence-type-regexp
+  (when (treesit-thing-defined-p 'sexp nil)
+    (setq-local forward-sexp-function #'treesit-forward-sexp)
+    (setq-local transpose-sexps-function #'treesit-transpose-sexps))
+
+  (when (treesit-thing-defined-p 'sentence nil)
     (setq-local forward-sentence-function #'treesit-forward-sentence))
 
   ;; Imenu.
   (when treesit-simple-imenu-settings
     (setq-local imenu-create-index-function
-                #'treesit-simple-imenu)))
+                #'treesit-simple-imenu))
+
+  ;; Remove existing local parsers.
+  (dolist (ov (overlays-in (point-min) (point-max)))
+    (when-let ((parser (overlay-get ov 'treesit-parser)))
+      (treesit-parser-delete parser)
+      (delete-overlay ov))))
 
 ;;; Debugging
 
@@ -2605,7 +2832,8 @@ in `treesit-parser-list'."
                         'bold nil))
         name
         (if (treesit-node-check node 'named) ")" "\""))))
-    (setq treesit--inspect-name name)
+    ;; Escape the percent character for mode-line. (Bug#65540)
+    (setq treesit--inspect-name (string-replace "%" "%%" name))
     (force-mode-line-update)
     (when arg
       (if node-list
@@ -2947,10 +3175,12 @@ the text in the active region is highlighted in the 
explorer
 window."
   :lighter " TSexplore"
   (if treesit-explore-mode
-      (let ((language (intern (completing-read
-                               "Language: "
-                               (mapcar #'treesit-parser-language
-                                       (treesit-parser-list))))))
+      (let ((language
+             (intern (completing-read
+                      "Language: "
+                      (cl-remove-duplicates
+                       (mapcar #'treesit-parser-language
+                               (treesit-parser-list nil nil t)))))))
         (if (not (treesit-language-available-p language))
             (user-error "Cannot find tree-sitter grammar for %s: %s"
                         language (cdr (treesit-language-available-p
diff --git a/lisp/uniquify.el b/lisp/uniquify.el
index 2ad2fb0eeac..7119ae7eac3 100644
--- a/lisp/uniquify.el
+++ b/lisp/uniquify.el
@@ -187,9 +187,9 @@ name will then be used to uniquify the buffer's name.
 
 To include components from the `project-name' of the buffer, set
 this variable to `project-uniquify-dirname-transform'."
-  :type '(choice (function-item :tag "Use directory name as-is" identity)
+  :type `(choice (function-item :tag "Use directory name as-is" identity)
                  (function-item :tag "Include project name in directory name"
-                                #'project-uniquify-dirname-transform)
+                                ,#'project-uniquify-dirname-transform)
                  function)
   :version "30.1"
   :group 'uniquify)
diff --git a/lisp/url/url-gw.el b/lisp/url/url-gw.el
index 4d7297f6f2e..568ce8679f5 100644
--- a/lisp/url/url-gw.el
+++ b/lisp/url/url-gw.el
@@ -1,6 +1,6 @@
 ;;; url-gw.el --- Gateway munging for URL loading  -*- lexical-binding: t; -*-
 
-;; Copyright (C) 1997-1998, 2004-2023 Free Software Foundation, Inc.
+;; Copyright (C) 1997-2023 Free Software Foundation, Inc.
 
 ;; Author: Bill Perry <wmperry@gnu.org>
 ;; Maintainer: emacs-devel@gnu.org
@@ -97,21 +97,27 @@ This list will be executed as a command after logging in 
via telnet."
 
 (defcustom url-gateway-broken-resolution nil
   "Whether to use nslookup to resolve hostnames.
-This should be used when your version of Emacs cannot correctly use DNS,
-but your machine can.  This usually happens if you are running a statically
-linked Emacs under SunOS 4.x."
+This should be used when your version of Emacs cannot correctly
+use DNS, but your machine can.
+
+This used to happen on SunOS 4.x and Ultrix when Emacs was linked
+statically, and also was not linked with the resolver libraries.
+Those systems are no longer supported by Emacs."
   :type 'boolean
   :group 'url-gateway)
+(make-obsolete-variable 'url-gateway-broken-resolution nil "30.1")
 
 (defcustom url-gateway-nslookup-program "nslookup"
   "If non-nil then a string naming nslookup program."
   :type '(choice (const :tag "None" :value nil) string)
   :group 'url-gateway)
+(make-obsolete-variable 'url-gateway-nslookup-program nil "30.1")
 
 ;; Stolen from ange-ftp
 ;;;###autoload
 (defun url-gateway-nslookup-host (host)
   "Attempt to resolve the given HOST using nslookup if possible."
+  (declare (obsolete nil "30.1"))
   (interactive "sHost:  ")
   (if url-gateway-nslookup-program
       (let ((proc (start-process " *nslookup*" " *nslookup*"
@@ -237,7 +243,8 @@ overriding the value of `url-gateway-method'."
 
       ;; If the user told us to do DNS for them, do it.
       (if url-gateway-broken-resolution
-         (setq host (url-gateway-nslookup-host host)))
+          (with-suppressed-warnings ((obsolete url-gateway-nslookup-host))
+            (setq host (url-gateway-nslookup-host host))))
 
       ;; This is a clean way to ensure the new process inherits the
       ;; right coding systems in both Emacs and XEmacs.
diff --git a/lisp/url/url-vars.el b/lisp/url/url-vars.el
index 7e2290217d0..ef4b8b2841b 100644
--- a/lisp/url/url-vars.el
+++ b/lisp/url/url-vars.el
@@ -331,7 +331,7 @@ undefined."
 (defcustom url-max-redirections 30
   "The maximum number of redirection requests to honor in a HTTP connection.
 A negative number means to honor an unlimited number of redirection requests."
-  :type 'natnum
+  :type 'integer
   :group 'url)
 
 (defcustom url-confirmation-func 'y-or-n-p
diff --git a/lisp/use-package/use-package.el b/lisp/use-package/use-package.el
index 1b63a6d651a..f5e6a551bb3 100644
--- a/lisp/use-package/use-package.el
+++ b/lisp/use-package/use-package.el
@@ -10,6 +10,9 @@
 ;; Keywords: dotemacs startup speed config package extensions
 ;; URL: https://github.com/jwiegley/use-package
 
+;; This is a GNU ELPA :core package.  Avoid functionality that is not
+;; compatible with the version of Emacs recorded above.
+
 ;; This file is part of GNU Emacs.
 
 ;; GNU Emacs is free software: you can redistribute it and/or modify
diff --git a/lisp/vc/diff-mode.el b/lisp/vc/diff-mode.el
index d776375d681..24a925eda73 100644
--- a/lisp/vc/diff-mode.el
+++ b/lisp/vc/diff-mode.el
@@ -216,6 +216,7 @@ The default \"-b\" means to ignore whitespace-only changes,
   "C-x 4 A" #'diff-add-change-log-entries-other-window
   ;; Misc operations.
   "C-c C-a" #'diff-apply-hunk
+  "C-c C-m a" #'diff-apply-buffer
   "C-c C-e" #'diff-ediff-patch
   "C-c C-n" #'diff-restrict-view
   "C-c C-s" #'diff-split-hunk
@@ -2054,6 +2055,40 @@ With a prefix argument, try to REVERSE the hunk."
           (diff-hunk-kill)
         (diff-hunk-next)))))
 
+(defun diff-apply-buffer ()
+  "Apply the diff in the entire diff buffer.
+When applying all hunks was successful, then save the changed buffers."
+  (interactive)
+  (let ((buffer-edits nil)
+        (failures 0)
+        (diff-refine nil))
+    (save-excursion
+      (goto-char (point-min))
+      (diff-beginning-of-hunk t)
+      (while (pcase-let ((`(,buf ,line-offset ,pos ,_src ,dst ,switched)
+                          (diff-find-source-location nil nil)))
+               (cond ((and line-offset (not switched))
+                      (push (cons pos dst)
+                            (alist-get buf buffer-edits)))
+                     (t (setq failures (1+ failures))))
+               (and (not (eq (prog1 (point) (ignore-errors (diff-hunk-next)))
+                             (point)))
+                    (looking-at-p diff-hunk-header-re)))))
+    (cond ((zerop failures)
+           (dolist (buf-edits (reverse buffer-edits))
+             (with-current-buffer (car buf-edits)
+               (dolist (edit (cdr buf-edits))
+                 (let ((pos (car edit))
+                       (dst (cdr edit))
+                       (inhibit-read-only t))
+                   (goto-char (car pos))
+                   (delete-region (car pos) (cdr pos))
+                   (insert (car dst))))
+               (save-buffer)))
+           (message "Saved %d buffers" (length buffer-edits)))
+          (t
+           (message "%d hunks failed; no buffers changed" failures)))))
+
 (defalias 'diff-mouse-goto-source #'diff-goto-source)
 
 (defun diff-goto-source (&optional other-file event)
diff --git a/lisp/vc/ediff-util.el b/lisp/vc/ediff-util.el
index c4ebe20d7e4..be698370b97 100644
--- a/lisp/vc/ediff-util.el
+++ b/lisp/vc/ediff-util.el
@@ -1269,36 +1269,28 @@ which see."
     (or (display-graphic-p)
        (user-error "Emacs is not running as a window application"))
 
-  (cond ((eq ediff-window-setup-function #'ediff-setup-windows-multiframe)
-        (setq ediff-multiframe nil)
-        (setq window-setup-func #'ediff-setup-windows-plain)
-         (message "ediff is now in `plain' mode"))
-       ((eq ediff-window-setup-function #'ediff-setup-windows-plain)
-        (if (and (ediff-buffer-live-p ediff-control-buffer)
-                 (window-live-p ediff-control-window))
-            (set-window-dedicated-p ediff-control-window nil))
-        (setq ediff-multiframe t)
-        (setq window-setup-func #'ediff-setup-windows-multiframe)
-         (message "ediff is now in `multiframe' mode"))
-       (t
-        (if (and (ediff-buffer-live-p ediff-control-buffer)
-                 (window-live-p ediff-control-window))
-            (set-window-dedicated-p ediff-control-window nil))
-        (setq ediff-multiframe t)
-        (setq window-setup-func #'ediff-setup-windows-multiframe))
-         (message "ediff is now in `multiframe' mode"))
-
-  ;; change default
-  (setq-default ediff-window-setup-function window-setup-func)
-  ;; change in all active ediff sessions
-  (mapc (lambda(buf) (ediff-with-current-buffer buf
-                      (setq ediff-window-setup-function window-setup-func
-                            ediff-window-B nil)))
-       ediff-session-registry)
-  (if (ediff-in-control-buffer-p)
-      (progn
-       (set-window-dedicated-p (selected-window) nil)
-       (ediff-recenter 'no-rehighlight)))))
+    (cond ((eq ediff-window-setup-function #'ediff-setup-windows-multiframe)
+           (setq ediff-multiframe nil)
+           (setq window-setup-func #'ediff-setup-windows-plain)
+           (message "ediff is now in `plain' mode"))
+          (t ; (eq ediff-window-setup-function #'ediff-setup-windows-plain)
+           (if (and (ediff-buffer-live-p ediff-control-buffer)
+                    (window-live-p ediff-control-window))
+               (set-window-dedicated-p ediff-control-window nil))
+           (setq ediff-multiframe t)
+           (setq window-setup-func #'ediff-setup-windows-multiframe)
+           (message "ediff is now in `multiframe' mode")))
+
+    ;; change default
+    (setq-default ediff-window-setup-function window-setup-func)
+    ;; change in all active ediff sessions
+    (mapc (lambda (buf) (ediff-with-current-buffer buf
+                          (setq ediff-window-setup-function window-setup-func
+                                ediff-window-B nil)))
+          ediff-session-registry)
+    (when (ediff-in-control-buffer-p)
+      (set-window-dedicated-p (selected-window) nil)
+      (ediff-recenter 'no-rehighlight))))
 
 
 ;;;###autoload
@@ -3138,16 +3130,15 @@ Hit \\[ediff-recenter] to reset the windows afterward."
                   ;; e.g., if file name ends with .Z or .gz
                    ;; This is needed so that patches produced by ediff will
                   ;; have more meaningful names
-                  (ediff-make-empty-tmp-file short-f))
+                   (make-temp-file short-f))
                  (prefix
                   ;; Prefix is most often the same as the file name for the
-                  ;; variant.  Here we are trying to use the original file
-                  ;; name but in the temp directory.
-                  (ediff-make-empty-tmp-file f 'keep-name))
+                   ;; variant.
+                   (make-temp-file f))
                  (t
                   ;; If don't care about name, add some random stuff
                   ;; to proposed file name.
-                  (ediff-make-empty-tmp-file short-f))))
+                   (make-temp-file short-f))))
 
     ;; create the file
     (ediff-with-current-buffer buff
@@ -3159,28 +3150,6 @@ Hit \\[ediff-recenter] to reset the windows afterward."
       (set-file-modes f ediff-temp-file-mode)
       (expand-file-name f))))
 
-;; Create a temporary file.
-;; The returned file name (created by appending some random characters at the
-;; end of PROPOSED-NAME is guaranteed to point to a newly created empty file.
-;; This is a replacement for make-temp-name, which eliminates a security hole.
-;; If KEEP-PROPOSED-NAME isn't nil, try to keep PROPOSED-NAME, unless such file
-;; already exists.
-;; It is a modified version of make-temp-file in emacs 20.5
-(defun ediff-make-empty-tmp-file (proposed-name &optional keep-proposed-name)
-  (let ((file proposed-name))
-    (while (condition-case ()
-               (progn
-                (if (or (file-exists-p file) (not keep-proposed-name))
-                    (setq file (make-temp-name proposed-name)))
-                 (write-region "" nil file nil 'silent nil 'excl)
-                 nil)
-            (file-already-exists t))
-      ;; the file was somehow created by someone else between
-      ;; `make-temp-name' and `write-region', let's try again.
-      nil)
-    file))
-
-
 ;; Make sure the current buffer (for a file) has the same contents as the
 ;; file on disk, and attempt to remedy the situation if not.
 ;; Signal an error if we can't make them the same, or the user doesn't want
@@ -4144,6 +4113,10 @@ Mail anyway? (y or n) ")
 (define-obsolete-function-alias 'ediff-intersection #'seq-intersection "28.1")
 (define-obsolete-function-alias 'ediff-set-difference #'seq-difference "28.1")
 
+(defun ediff-make-empty-tmp-file (prefix &optional _ignored)
+  (declare (obsolete make-temp-file "30.1"))
+  (make-temp-file prefix))
+
 (run-hooks 'ediff-load-hook)
 
 ;;; ediff-util.el ends here
diff --git a/lisp/vc/emerge.el b/lisp/vc/emerge.el
index e95742b304a..5328ebc73ad 100644
--- a/lisp/vc/emerge.el
+++ b/lisp/vc/emerge.el
@@ -877,8 +877,8 @@ This is *not* a user option, since Emerge uses it for its 
own processing.")
 (defun emerge-buffers (buffer-A buffer-B &optional startup-hooks quit-hooks)
   "Run Emerge on two buffers BUFFER-A and BUFFER-B."
   (interactive "bBuffer A to merge: \nbBuffer B to merge: ")
-  (let ((emerge-file-A (emerge-make-temp-file "A"))
-       (emerge-file-B (emerge-make-temp-file "B")))
+  (let ((emerge-file-A (make-temp-file "emerge-A"))
+        (emerge-file-B (make-temp-file "emerge-B")))
     (with-current-buffer
      buffer-A
      (write-region (point-min) (point-max) emerge-file-A nil 'no-message))
@@ -901,9 +901,9 @@ This is *not* a user option, since Emerge uses it for its 
own processing.")
   "Run Emerge on two buffers, giving another buffer as the ancestor."
   (interactive
    "bBuffer A to merge: \nbBuffer B to merge: \nbAncestor buffer: ")
-  (let ((emerge-file-A (emerge-make-temp-file "A"))
-       (emerge-file-B (emerge-make-temp-file "B"))
-       (emerge-file-ancestor (emerge-make-temp-file "anc")))
+  (let ((emerge-file-A (make-temp-file "emerge-A"))
+        (emerge-file-B (make-temp-file "emerge-B"))
+        (emerge-file-ancestor (make-temp-file "emerge-ancestor")))
     (with-current-buffer
      buffer-A
      (write-region (point-min) (point-max) emerge-file-A nil 'no-message))
@@ -1039,8 +1039,8 @@ This is *not* a user option, since Emerge uses it for its 
own processing.")
                                   startup-hooks quit-hooks _output-file)
   (let ((buffer-A (get-buffer-create (format "%s,%s" file revision-A)))
        (buffer-B (get-buffer-create (format "%s,%s" file revision-B)))
-       (emerge-file-A (emerge-make-temp-file "A"))
-       (emerge-file-B (emerge-make-temp-file "B")))
+        (emerge-file-A (make-temp-file "emerge-A"))
+        (emerge-file-B (make-temp-file "emerge-B")))
     ;; Get the revisions into buffers
     (with-current-buffer
      buffer-A
@@ -1076,9 +1076,9 @@ This is *not* a user option, since Emerge uses it for its 
own processing.")
   (let ((buffer-A (get-buffer-create (format "%s,%s" file revision-A)))
        (buffer-B (get-buffer-create (format "%s,%s" file revision-B)))
        (buffer-ancestor (get-buffer-create (format "%s,%s" file ancestor)))
-       (emerge-file-A (emerge-make-temp-file "A"))
-       (emerge-file-B (emerge-make-temp-file "B"))
-       (emerge-ancestor (emerge-make-temp-file "ancestor")))
+        (emerge-file-A (make-temp-file "emerge-A"))
+        (emerge-file-B (make-temp-file "emerge-B"))
+        (emerge-ancestor (make-temp-file "emerge-ancestor")))
     ;; Get the revisions into buffers
     (with-current-buffer
      buffer-A
@@ -2851,14 +2851,6 @@ Otherwise, signal an error."
     (setq vars (cdr vars))
     (setq values (cdr values))))
 
-;; When the pointless option emerge-temp-file-prefix goes,
-;; make this function obsolete too, and just use make-temp-file.
-(defun emerge-make-temp-file (prefix)
-  "Make a private temporary file based on PREFIX.
-This is named by concatenating `emerge-temp-file-prefix' with
-PREFIX."
-  (make-temp-file (concat emerge-temp-file-prefix prefix)))
-
 ;;; Functions that query the user before he can write out the current buffer.
 
 (defun emerge-query-write-file ()
@@ -3062,6 +3054,8 @@ See also `auto-save-file-name-p'."
   :type '(choice (const nil) regexp))
 (make-obsolete-variable 'emerge-metachars nil "26.1")
 
+(define-obsolete-function-alias 'emerge-make-temp-file #'make-temp-file "30.1")
+
 (provide 'emerge)
 
 ;;; emerge.el ends here
diff --git a/lisp/vc/vc-annotate.el b/lisp/vc/vc-annotate.el
index d83660f9d79..de6c3adbbdb 100644
--- a/lisp/vc/vc-annotate.el
+++ b/lisp/vc/vc-annotate.el
@@ -718,23 +718,24 @@ The annotations are relative to the current time, unless 
overridden by OFFSET."
         (let* ((color (or (vc-annotate-compcar difference 
vc-annotate-color-map)
                           (cons nil vc-annotate-very-old-color)))
                ;; substring from index 1 to remove any leading `#' in the name
-               (face-name (concat "vc-annotate-face-"
-                                  (if (string-equal
-                                       (substring (cdr color) 0 1) "#")
-                                      (substring (cdr color) 1)
-                                    (cdr color))))
+               (face (intern (concat "vc-annotate-face-"
+                                     (if (string-equal
+                                          (substring (cdr color) 0 1) "#")
+                                         (substring (cdr color) 1)
+                                       (cdr color)))))
                ;; Make the face if not done.
-               (face (or (intern-soft face-name)
-                         (let ((tmp-face (make-face (intern face-name))))
-                           (set-face-extend tmp-face t)
-                           (cond
-                            (vc-annotate-background-mode
-                             (set-face-background tmp-face (cdr color)))
-                            (t
-                             (set-face-foreground tmp-face (cdr color))
-                             (when vc-annotate-background
-                              (set-face-background tmp-face 
vc-annotate-background))))
-                           tmp-face))))        ; Return the face
+               (face (if (facep face)
+                         face
+                       (make-face face)
+                       (set-face-extend face t)
+                       (cond
+                        (vc-annotate-background-mode
+                         (set-face-background face (cdr color)))
+                        (t
+                         (set-face-foreground face (cdr color))
+                         (when vc-annotate-background
+                          (set-face-background face vc-annotate-background))))
+                       face)))
           (put-text-property start end 'face face)))))
   ;; Pretend to font-lock there were no matches.
   nil)
diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el
index c689eec444b..5c21a5b884e 100644
--- a/lisp/vc/vc-git.el
+++ b/lisp/vc/vc-git.el
@@ -122,7 +122,10 @@ If nil, use the value of `vc-diff-switches'.  If t, use no 
switches."
 
 (defcustom vc-git-annotate-switches nil
   "String or list of strings specifying switches for Git blame under VC.
-If nil, use the value of `vc-annotate-switches'.  If t, use no switches."
+If nil, use the value of `vc-annotate-switches'.  If t, use no switches.
+
+Tip: Set this to \"-w\" to make Git blame ignore whitespace when
+comparing changes.  See Man page `git-blame' for more."
   :type '(choice (const :tag "Unspecified" nil)
                 (const :tag "None" t)
                 (string :tag "Argument String")
@@ -1058,7 +1061,8 @@ It is based on `log-edit-mode', and has Git-specific 
extensions."
           ;; might not support the non-ASCII characters in the log
           ;; message.  Handle also remote files.
           (if (eq system-type 'windows-nt)
-              (let ((default-directory (file-name-directory file1)))
+              (let ((default-directory (or (file-name-directory file1)
+                                           default-directory)))
                 (make-nearby-temp-file "git-msg"))))
          to-stash)
     (when vc-git-patch-string
@@ -1120,7 +1124,15 @@ It is based on `log-edit-mode', and has Git-specific 
extensions."
                     (t (push file-name to-stash)))
               (setq pos (point))))))
       (unless (string-empty-p vc-git-patch-string)
-        (let ((patch-file (make-nearby-temp-file "git-patch")))
+        (let ((patch-file (make-nearby-temp-file "git-patch"))
+              ;; Temporarily countermand the let-binding at the
+              ;; beginning of this function.
+              (coding-system-for-write
+               (coding-system-change-eol-conversion
+                ;; On DOS/Windows, it is important for the patch file
+                ;; to have the Unix EOL format, because Git expects
+                ;; that, even on Windows.
+                (or pcsw vc-git-commits-coding-system) 'unix)))
           (with-temp-file patch-file
             (insert vc-git-patch-string))
           (unwind-protect
diff --git a/lisp/vc/vc-hg.el b/lisp/vc/vc-hg.el
index b62420393aa..c3e563a1f10 100644
--- a/lisp/vc/vc-hg.el
+++ b/lisp/vc/vc-hg.el
@@ -249,7 +249,10 @@ If `ask', you will be prompted for a branch type."
                     (error nil)))))))
     (when (and (eq 0 status)
               (> (length out) 0)
-              (null (string-match ".*: No such file or directory$" out)))
+                         ;; Posix
+              (null (or (string-match ".*: No such file or directory$" out)
+                         ;; MS-Windows
+                         (string-match ".*: The system cannot find the file 
specified$" out))))
       (let ((state (aref out 0)))
        (cond
         ((eq state ?=) 'up-to-date)
diff --git a/lisp/vc/vc-hooks.el b/lisp/vc/vc-hooks.el
index e75165ea2e9..a4de0a6e791 100644
--- a/lisp/vc/vc-hooks.el
+++ b/lisp/vc/vc-hooks.el
@@ -87,6 +87,11 @@
   "Face for VC modeline state when the file is edited."
   :version "25.1")
 
+(defface vc-ignored-state
+  '((default :inherit vc-state-base))
+  "Face for VC modeline state when the file is registered, but ignored."
+  :version "30.1")
+
 ;; Customization Variables (the rest is in vc.el)
 
 (defcustom vc-ignore-dir-regexp
@@ -743,6 +748,10 @@ This function assumes that the file is registered."
             (setq state-echo "File tracked by the VC system, but missing from 
the file system")
            (setq face 'vc-missing-state)
             (concat backend-name "?" rev))
+           ((eq state 'ignored)
+            (setq state-echo "File tracked by the VC system, but ignored")
+            (setq face 'vc-ignored-state)
+            (concat backend-name "!" rev))
           (t
            ;; Not just for the 'edited state, but also a fallback
            ;; for all other states.  Think about different symbols
diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el
index be7fa46c28e..7f334397a5e 100644
--- a/lisp/vc/vc.el
+++ b/lisp/vc/vc.el
@@ -1883,7 +1883,9 @@ in the output buffer."
     (vc-run-delayed (vc-diff-finish (current-buffer) nil))))
 
 (defun vc-diff-internal (async vc-fileset rev1 rev2 &optional verbose buffer)
-  "Report diffs between two revisions of a fileset.
+  "Report diffs between revisions REV1 and REV2 of a fileset in VC-FILESET.
+ASYNC non-nil means run the backend's commands asynchronously if possible.
+VC-FILESET should have the format described in `vc-deduce-fileset'.
 Output goes to the buffer BUFFER, which defaults to *vc-diff*.
 BUFFER, if non-nil, should be a buffer or a buffer name.
 Return t if the buffer had changes, nil otherwise."
@@ -1899,15 +1901,26 @@ Return t if the buffer had changes, nil otherwise."
         ;; but the only way to set it for each file included would
         ;; be to call the back end separately for each file.
         (coding-system-for-read
-         (if files (vc-coding-system-for-diff (car files)) 'undecided))
+          ;; Force the EOL conversion to be -unix, in case the files
+          ;; to be compared have DOS EOLs.  In that case, EOL
+          ;; conversion will produce a patch file that will either
+          ;; fail to apply, or will change the EOL format of some of
+          ;; the lines in the patched file.
+          (coding-system-change-eol-conversion
+          (if files (vc-coding-system-for-diff (car files)) 'undecided)
+           'unix))
          (orig-diff-buffer-clone
           (if revert-buffer-in-progress-p
               (clone-buffer
                (generate-new-buffer-name " *vc-diff-clone*") nil))))
     ;; On MS-Windows and MS-DOS, Diff is likely to produce DOS-style
     ;; EOLs, which will look ugly if (car files) happens to have Unix
-    ;; EOLs.
-    (if (memq system-type '(windows-nt ms-dos))
+    ;; EOLs.  But for Git, we must force Unix EOLs in the diffs, since
+    ;; Git always produces Unix EOLs in the parts that didn't come
+    ;; from the file, and wants to see any CR characters when applying
+    ;; patches.
+    (if (and (memq system-type '(windows-nt ms-dos))
+             (not (eq (car vc-fileset) 'Git)))
        (setq coding-system-for-read
              (coding-system-change-eol-conversion coding-system-for-read
                                                   'dos)))
@@ -3663,8 +3676,7 @@ If successful, returns the string with the directory of 
the
 checkout.  If BACKEND is nil, iterate through every known backend
 in `vc-handled-backends' until one succeeds.  If REV is non-nil,
 it indicates a specific revision to check out."
-  (unless directory
-    (setq directory default-directory))
+  (setq directory (expand-file-name (or directory default-directory)))
   (if backend
       (progn
         (unless (memq backend vc-handled-backends)
diff --git a/lisp/wid-edit.el b/lisp/wid-edit.el
index ae060c92d0e..74412414113 100644
--- a/lisp/wid-edit.el
+++ b/lisp/wid-edit.el
@@ -282,16 +282,20 @@ The user is asked to choose between each NAME from ITEMS.
 If ITEMS has simple item definitions, then this function returns the VALUE of
 the chosen element.  If ITEMS is a keymap, then the return value is the symbol
 in the key vector, as in the argument of `define-key'."
-  ;; Apply quote substitution to customize choice menu item text,
-  ;; whether it occurs in a widget buffer or in a popup menu.
+  ;; Apply substitution to choice menu title and item text, whether it
+  ;; occurs in a widget buffer or in a popup menu.
   (let ((items (mapc (lambda (x)
-                       (when (consp x)
-                         (dotimes (i (1- (length x)))
-                           (when (stringp (nth i x))
-                             (setcar (nthcdr i x)
-                                     (substitute-command-keys
-                                      (car (nthcdr i x))))))))
-                    items)))
+                       (if (proper-list-p x)
+                           (dotimes (i (1- (length x)))
+                             (when (stringp (nth i x))
+                               (setcar (nthcdr i x)
+                                       (substitute-command-keys
+                                        (car (nthcdr i x))))))
+                         ;; ITEMS has simple item definitions.
+                         (when (and (consp x) (stringp (car x)))
+                           (setcar x (substitute-command-keys (car x))))))
+                    items))
+        (title (substitute-command-keys title)))
     (cond ((and (< (length items) widget-menu-max-size)
                event (display-popup-menus-p))
           ;; Mouse click.
@@ -3677,7 +3681,9 @@ match-alternatives: %S"
                           :warning)
                          ;; Make sure we will `read' a string.
                          (setq value (prin1-to-string value)))
-                       (read value)))
+                       (if (string-empty-p value)
+                           value
+                       (read value))))
 
 (defun widget-restricted-sexp-match (widget value)
   (let ((alternatives (widget-get widget :match-alternatives))
diff --git a/lisp/window.el b/lisp/window.el
index b9b032c33e9..2f9b46ebb0a 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -4177,8 +4177,8 @@ a non-nil `no-other-window' parameter."
   "How to choose a frame's selected window after window deletion.
 When a frame's selected window gets deleted, Emacs has to choose
 another live window on that frame to serve as its selected
-window.  This option allows to control which window gets selected
-instead.
+window.  This option controls the window that is selected in such
+a situation.
 
 The possible choices are `mru' (the default) to select the most
 recently used window on that frame, and `pos' to choose the
@@ -8275,8 +8275,8 @@ This function tries to reuse or split a window such that 
the
 window produced this way is on the side of the reference window
 specified by the `direction' entry.
 
-Four special values for `direction' entries allow to implicitly
-specify the selected frame's main window as reference window:
+Four special values for `direction' entries allow implicitly
+specifying the selected frame's main window as reference window:
 `leftmost', `top', `rightmost' and `bottom'.  Hence, instead of
 `(direction . left) (window . main)' one can simply write
 `(direction . leftmost)'.
diff --git a/make-dist b/make-dist
index a3b7219af3a..2c4b6a79500 100755
--- a/make-dist
+++ b/make-dist
@@ -356,8 +356,9 @@ possibly_non_vc_files="
   $top_level_ChangeLog
   MANIFEST aclocal.m4 configure
   admin/charsets/jisx2131-filter
-  src/config.in exec/configure
-  exec/config.h.in
+  src/config.in
+  exec/configure exec/config.h.in
+  leim/small-ja-dic-option
 "$(
   find admin doc etc lisp \
    \( -name '*.el' -o -name '*.elc' -o -name '*.map' -o -name '*.stamp' \
diff --git a/nextstep/Makefile.in b/nextstep/Makefile.in
index 5e3465315af..89318a1efa8 100644
--- a/nextstep/Makefile.in
+++ b/nextstep/Makefile.in
@@ -71,11 +71,11 @@ ${ns_appbindir}/Emacs: ${ns_appdir} ${ns_check_file} 
../src/emacs${EXEEXT}
        ${MKDIR_P} ${ns_appbindir}
        cp -f ../src/emacs${EXEEXT} $@
 
-# FIXME: Don't install the dump file into the app bundle when
-# self-contained install is disabled.
 ${ns_applibexecdir}/Emacs.pdmp: ${ns_appdir} ${ns_check_file} 
../src/emacs${EXEEXT}.pdmp
        ${MKDIR_P} ${ns_applibexecdir}
+ifeq (${ns_self_contained},no)
        cp -f ../src/emacs${EXEEXT}.pdmp $@
+endif
 
 .PHONY: FORCE
 
diff --git a/nt/inc/ms-w32.h b/nt/inc/ms-w32.h
index fce15fcbd8c..c57549c96e7 100644
--- a/nt/inc/ms-w32.h
+++ b/nt/inc/ms-w32.h
@@ -208,7 +208,7 @@ extern struct tm * sys_localtime (const time_t *);
 
 /* Unlike MS and mingw.org, MinGW64 doesn't define gai_strerror as an
    inline function in a system header file, and instead seems to
-   require to link against ws2_32.a.  But we don't want to link with
+   require linking against ws2_32.a.  But we don't want to link with
    -lws2_32, as that would make Emacs dependent on the respective DLL.
    So MinGW64 is amply punished here by the following:  */
 #undef HAVE_GAI_STRERROR
diff --git a/src/ChangeLog.11 b/src/ChangeLog.11
index b2b776d491f..bfd4fef4e80 100644
--- a/src/ChangeLog.11
+++ b/src/ChangeLog.11
@@ -6278,7 +6278,7 @@
 2010-07-09  Michael Albinus  <michael.albinus@gmx.de>
 
        * dbusbind.c (xd_initialize): Add new argument RAISE_ERROR, which
-       allows to suppress errors when polling in Emacs' main loop.
+       allows suppressing errors when polling in Emacs' main loop.
        (Fdbus_init_bus, Fdbus_get_unique_name, Fdbus_call_method)
        (Fdbus_call_method_asynchronously, Fdbus_method_return_internal)
        (Fdbus_method_error_internal, Fdbus_send_signal)
diff --git a/src/alloc.c b/src/alloc.c
index c77bdc6372d..45a950c4f81 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -105,7 +105,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
    or a new vector block is allocated (allocate_vector_from_block).
    Accordingly, objects reused from the free list are unpoisoned.
 
-   This feature can be disabled wtih the run-time flag
+   This feature can be disabled with the run-time flag
    `allow_user_poisoning' set to zero.  */
 #if ADDRESS_SANITIZER && defined HAVE_SANITIZER_ASAN_INTERFACE_H \
   && !defined GC_ASAN_POISON_OBJECTS
@@ -3054,9 +3054,8 @@ enum { VECTOR_BLOCK_SIZE = 4096 };
 /* Vector size requests are a multiple of this.  */
 enum { roundup_size = COMMON_MULTIPLE (LISP_ALIGNMENT, word_size) };
 
-/* Verify assumptions described above.  */
+/* Verify assumption described above.  */
 verify (VECTOR_BLOCK_SIZE % roundup_size == 0);
-verify (VECTOR_BLOCK_SIZE <= (1 << PSEUDOVECTOR_SIZE_BITS));
 
 /* Round up X to nearest mult-of-ROUNDUP_SIZE --- use at compile time.  */
 #define vroundup_ct(x) ROUNDUP (x, roundup_size)
@@ -3067,6 +3066,11 @@ verify (VECTOR_BLOCK_SIZE <= (1 << 
PSEUDOVECTOR_SIZE_BITS));
 
 enum {VECTOR_BLOCK_BYTES = VECTOR_BLOCK_SIZE - vroundup_ct (sizeof (void *))};
 
+/* The current code expects to be able to represent an unused block by
+   a single PVEC_FREE object, whose size is limited by the header word.
+   (Of course we could use multiple such objects.)  */
+verify (VECTOR_BLOCK_BYTES <= (word_size << PSEUDOVECTOR_REST_BITS));
+
 /* Size of the minimal vector allocated from block.  */
 
 enum { VBLOCK_BYTES_MIN = vroundup_ct (header_size + sizeof (Lisp_Object)) };
@@ -3076,10 +3080,10 @@ enum { VBLOCK_BYTES_MIN = vroundup_ct (header_size + 
sizeof (Lisp_Object)) };
 enum { VBLOCK_BYTES_MAX = vroundup_ct ((VECTOR_BLOCK_BYTES / 2) - word_size) };
 
 /* We maintain one free list for each possible block-allocated
-   vector size, and this is the number of free lists we have.  */
-
-enum { VECTOR_MAX_FREE_LIST_INDEX =
-       (VECTOR_BLOCK_BYTES - VBLOCK_BYTES_MIN) / roundup_size + 1 };
+   vector size, one for blocks one word bigger,
+   and one for all free vectors larger than that.  */
+enum { VECTOR_FREE_LIST_ARRAY_SIZE =
+       (VBLOCK_BYTES_MAX - VBLOCK_BYTES_MIN) / roundup_size + 1 + 2 };
 
 /* Common shortcut to advance vector pointer over a block data.  */
 
@@ -3141,9 +3145,20 @@ struct vector_block
 static struct vector_block *vector_blocks;
 
 /* Vector free lists, where NTH item points to a chain of free
-   vectors of the same NBYTES size, so NTH == VINDEX (NBYTES).  */
+   vectors of the same NBYTES size, so NTH == VINDEX (NBYTES),
+   except for the last element which may contain larger vectors.
+
+   I.e., for each vector V in vector_free_lists[I] the following holds:
+   - V has type PVEC_FREE
+   - V's total size in bytes, BS(V) = PVSIZE(V) * word_size + header_size
+   - For I < VECTOR_FREE_LIST_ARRAY_SIZE-1, VINDEX(BS(V)) = I
+   - For I = VECTOR_FREE_LIST_ARRAY_SIZE-1, VINDEX(BS(V)) ≥ I */
+static struct Lisp_Vector *vector_free_lists[VECTOR_FREE_LIST_ARRAY_SIZE];
 
-static struct Lisp_Vector *vector_free_lists[VECTOR_MAX_FREE_LIST_INDEX];
+/* Index to the bucket in vector_free_lists into which we last inserted
+   or split a free vector.  We use this as a heuristic telling us where
+   to start looking for free vectors when the exact-size bucket is empty.  */
+static ptrdiff_t last_inserted_vector_free_idx = VECTOR_FREE_LIST_ARRAY_SIZE;
 
 /* Singly-linked list of large vectors.  */
 
@@ -3176,10 +3191,12 @@ setup_on_free_list (struct Lisp_Vector *v, ptrdiff_t 
nbytes)
   XSETPVECTYPESIZE (v, PVEC_FREE, 0, nwords);
   eassert (nbytes % roundup_size == 0);
   ptrdiff_t vindex = VINDEX (nbytes);
-  eassert (vindex < VECTOR_MAX_FREE_LIST_INDEX);
+  /* Anything too large goes into the last slot (overflow bin).  */
+  vindex = min(vindex, VECTOR_FREE_LIST_ARRAY_SIZE - 1);
   set_next_vector (v, vector_free_lists[vindex]);
   ASAN_POISON_VECTOR_CONTENTS (v, nbytes - header_size);
   vector_free_lists[vindex] = v;
+  last_inserted_vector_free_idx = vindex;
 }
 
 /* Get a new vector block.  */
@@ -3208,6 +3225,17 @@ init_vectors (void)
   staticpro (&zero_vector);
 }
 
+/* Memory footprint in bytes of a pseudovector other than a bool-vector.  */
+static ptrdiff_t
+pseudovector_nbytes (const union vectorlike_header *hdr)
+{
+  eassert (!PSEUDOVECTOR_TYPEP (hdr, PVEC_BOOL_VECTOR));
+  ptrdiff_t nwords = ((hdr->size & PSEUDOVECTOR_SIZE_MASK)
+                     + ((hdr->size & PSEUDOVECTOR_REST_MASK)
+                        >> PSEUDOVECTOR_SIZE_BITS));
+  return vroundup (header_size + word_size * nwords);
+}
+
 /* Allocate vector from a vector block.  */
 
 static struct Lisp_Vector *
@@ -3234,18 +3262,21 @@ allocate_vector_from_block (ptrdiff_t nbytes)
   /* Next, check free lists containing larger vectors.  Since
      we will split the result, we should have remaining space
      large enough to use for one-slot vector at least.  */
-  for (index = VINDEX (nbytes + VBLOCK_BYTES_MIN);
-       index < VECTOR_MAX_FREE_LIST_INDEX; index++)
+  for (index = max (VINDEX (nbytes + VBLOCK_BYTES_MIN),
+                   last_inserted_vector_free_idx);
+       index < VECTOR_FREE_LIST_ARRAY_SIZE; index++)
     if (vector_free_lists[index])
       {
        /* This vector is larger than requested.  */
        vector = vector_free_lists[index];
+       size_t vector_nbytes = pseudovector_nbytes (&vector->header);
+       eassert (vector_nbytes > nbytes);
        ASAN_UNPOISON_VECTOR_CONTENTS (vector, nbytes - header_size);
        vector_free_lists[index] = next_vector (vector);
 
        /* Excess bytes are used for the smaller vector,
           which should be set on an appropriate free list.  */
-       restbytes = index * roundup_size + VBLOCK_BYTES_MIN - nbytes;
+       restbytes = vector_nbytes - nbytes;
        eassert (restbytes % roundup_size == 0);
 #if GC_ASAN_POISON_OBJECTS
        /* Ensure that accessing excess bytes does not trigger ASan.  */
@@ -3299,9 +3330,7 @@ vectorlike_nbytes (const union vectorlike_header *hdr)
          nwords = (boolvec_bytes - header_size + word_size - 1) / word_size;
         }
       else
-       nwords = ((size & PSEUDOVECTOR_SIZE_MASK)
-                 + ((size & PSEUDOVECTOR_REST_MASK)
-                    >> PSEUDOVECTOR_SIZE_BITS));
+       return pseudovector_nbytes (hdr);
     }
   else
     nwords = size;
@@ -3324,93 +3353,134 @@ static void
 cleanup_vector (struct Lisp_Vector *vector)
 {
   detect_suspicious_free (vector);
-
-  if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_BIGNUM))
-    mpz_clear (PSEUDOVEC_STRUCT (vector, Lisp_Bignum)->value);
-  else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_OVERLAY))
-    {
-      struct Lisp_Overlay *ol = PSEUDOVEC_STRUCT (vector, Lisp_Overlay);
-      xfree (ol->interval);
-    }
-  else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_FINALIZER))
-    unchain_finalizer (PSEUDOVEC_STRUCT (vector, Lisp_Finalizer));
-  else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_FONT))
+  if ((vector->header.size & PSEUDOVECTOR_FLAG) == 0)
+    return;  /* nothing more to do for plain vectors */
+  switch (PSEUDOVECTOR_TYPE (vector))
     {
-      if ((vector->header.size & PSEUDOVECTOR_SIZE_MASK) == FONT_OBJECT_MAX)
-       {
-         struct font *font = PSEUDOVEC_STRUCT (vector, font);
-         struct font_driver const *drv = font->driver;
+    case PVEC_BIGNUM:
+      mpz_clear (PSEUDOVEC_STRUCT (vector, Lisp_Bignum)->value);
+      break;
+    case PVEC_OVERLAY:
+      {
+       struct Lisp_Overlay *ol = PSEUDOVEC_STRUCT (vector, Lisp_Overlay);
+       xfree (ol->interval);
+      }
+      break;
+    case PVEC_FINALIZER:
+      unchain_finalizer (PSEUDOVEC_STRUCT (vector, Lisp_Finalizer));
+      break;
+    case PVEC_FONT:
+      {
+       if ((vector->header.size & PSEUDOVECTOR_SIZE_MASK) == FONT_OBJECT_MAX)
+         {
+           struct font *font = PSEUDOVEC_STRUCT (vector, font);
+           struct font_driver const *drv = font->driver;
 
-         /* The font driver might sometimes be NULL, e.g. if Emacs was
-            interrupted before it had time to set it up.  */
-         if (drv)
-           {
-             /* Attempt to catch subtle bugs like Bug#16140.  */
-             eassert (valid_font_driver (drv));
-             drv->close_font (font);
-           }
-       }
+           /* The font driver might sometimes be NULL, e.g. if Emacs was
+              interrupted before it had time to set it up.  */
+           if (drv)
+             {
+               /* Attempt to catch subtle bugs like Bug#16140.  */
+               eassert (valid_font_driver (drv));
+               drv->close_font (font);
+             }
+         }
 
 #if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
-      /* The Android font driver needs the ability to associate extra
-        information with font entities.  */
-      if (((vector->header.size & PSEUDOVECTOR_SIZE_MASK)
-          == FONT_ENTITY_MAX)
-         && PSEUDOVEC_STRUCT (vector, font_entity)->is_android)
-       android_finalize_font_entity (PSEUDOVEC_STRUCT (vector, font_entity));
+       /* The Android font driver needs the ability to associate extra
+          information with font entities.  */
+       if (((vector->header.size & PSEUDOVECTOR_SIZE_MASK)
+            == FONT_ENTITY_MAX)
+           && PSEUDOVEC_STRUCT (vector, font_entity)->is_android)
+         android_finalize_font_entity (PSEUDOVEC_STRUCT (vector, font_entity));
 #endif
-    }
-  else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_THREAD))
-    finalize_one_thread (PSEUDOVEC_STRUCT (vector, thread_state));
-  else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_MUTEX))
-    finalize_one_mutex (PSEUDOVEC_STRUCT (vector, Lisp_Mutex));
-  else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_CONDVAR))
-    finalize_one_condvar (PSEUDOVEC_STRUCT (vector, Lisp_CondVar));
-  else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_MARKER))
-    {
+      }
+      break;
+    case PVEC_THREAD:
+      finalize_one_thread (PSEUDOVEC_STRUCT (vector, thread_state));
+      break;
+    case PVEC_MUTEX:
+      finalize_one_mutex (PSEUDOVEC_STRUCT (vector, Lisp_Mutex));
+      break;
+    case PVEC_CONDVAR:
+      finalize_one_condvar (PSEUDOVEC_STRUCT (vector, Lisp_CondVar));
+      break;
+    case PVEC_MARKER:
       /* sweep_buffer should already have unchained this from its buffer.  */
       eassert (! PSEUDOVEC_STRUCT (vector, Lisp_Marker)->buffer);
-    }
-  else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_USER_PTR))
-    {
-      struct Lisp_User_Ptr *uptr = PSEUDOVEC_STRUCT (vector, Lisp_User_Ptr);
-      if (uptr->finalizer)
-       uptr->finalizer (uptr->p);
-    }
+      break;
+    case PVEC_USER_PTR:
+      {
+       struct Lisp_User_Ptr *uptr = PSEUDOVEC_STRUCT (vector, Lisp_User_Ptr);
+       if (uptr->finalizer)
+         uptr->finalizer (uptr->p);
+      }
+      break;
+    case PVEC_TS_PARSER:
+#ifdef HAVE_TREE_SITTER
+      treesit_delete_parser (PSEUDOVEC_STRUCT (vector, Lisp_TS_Parser));
+#endif
+      break;
+    case PVEC_TS_COMPILED_QUERY:
 #ifdef HAVE_TREE_SITTER
-  else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_TS_PARSER))
-    treesit_delete_parser (PSEUDOVEC_STRUCT (vector, Lisp_TS_Parser));
-  else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_TS_COMPILED_QUERY))
-    treesit_delete_query (PSEUDOVEC_STRUCT (vector, Lisp_TS_Query));
+      treesit_delete_query (PSEUDOVEC_STRUCT (vector, Lisp_TS_Query));
 #endif
+      break;
+    case PVEC_MODULE_FUNCTION:
 #ifdef HAVE_MODULES
-  else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_MODULE_FUNCTION))
-    {
-      ATTRIBUTE_MAY_ALIAS struct Lisp_Module_Function *function
-        = (struct Lisp_Module_Function *) vector;
-      module_finalize_function (function);
-    }
+      {
+       ATTRIBUTE_MAY_ALIAS struct Lisp_Module_Function *function
+         = (struct Lisp_Module_Function *) vector;
+       module_finalize_function (function);
+      }
 #endif
+      break;
+    case PVEC_NATIVE_COMP_UNIT:
 #ifdef HAVE_NATIVE_COMP
-  else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_NATIVE_COMP_UNIT))
-    {
-      struct Lisp_Native_Comp_Unit *cu =
-       PSEUDOVEC_STRUCT (vector, Lisp_Native_Comp_Unit);
-      unload_comp_unit (cu);
-    }
-  else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_SUBR))
-    {
-      struct Lisp_Subr *subr =
-       PSEUDOVEC_STRUCT (vector, Lisp_Subr);
-      if (!NILP (subr->native_comp_u))
-       {
-         /* FIXME Alternative and non invasive solution to this
-            cast?  */
-         xfree ((char *)subr->symbol_name);
-         xfree (subr->native_c_name);
-       }
-    }
+      {
+       struct Lisp_Native_Comp_Unit *cu =
+         PSEUDOVEC_STRUCT (vector, Lisp_Native_Comp_Unit);
+       unload_comp_unit (cu);
+      }
+#endif
+      break;
+    case PVEC_SUBR:
+#ifdef HAVE_NATIVE_COMP
+      {
+       struct Lisp_Subr *subr = PSEUDOVEC_STRUCT (vector, Lisp_Subr);
+       if (!NILP (subr->native_comp_u))
+         {
+           /* FIXME Alternative and non invasive solution to this cast?  */
+           xfree ((char *)subr->symbol_name);
+           xfree (subr->native_c_name);
+         }
+      }
 #endif
+      break;
+    /* Keep the switch exhaustive.  */
+    case PVEC_NORMAL_VECTOR:
+    case PVEC_FREE:
+    case PVEC_SYMBOL_WITH_POS:
+    case PVEC_MISC_PTR:
+    case PVEC_PROCESS:
+    case PVEC_FRAME:
+    case PVEC_WINDOW:
+    case PVEC_BOOL_VECTOR:
+    case PVEC_BUFFER:
+    case PVEC_HASH_TABLE:
+    case PVEC_TERMINAL:
+    case PVEC_WINDOW_CONFIGURATION:
+    case PVEC_OTHER:
+    case PVEC_XWIDGET:
+    case PVEC_XWIDGET_VIEW:
+    case PVEC_TS_NODE:
+    case PVEC_SQLITE:
+    case PVEC_COMPILED:
+    case PVEC_CHAR_TABLE:
+    case PVEC_SUB_CHAR_TABLE:
+    case PVEC_RECORD:
+      break;
+    }
 }
 
 /* Reclaim space used by unmarked vectors.  */
@@ -3426,6 +3496,7 @@ sweep_vectors (void)
   gcstat.total_vectors = 0;
   gcstat.total_vector_slots = gcstat.total_free_vector_slots = 0;
   memset (vector_free_lists, 0, sizeof (vector_free_lists));
+  last_inserted_vector_free_idx = VECTOR_FREE_LIST_ARRAY_SIZE;
 
   /* Looking through vector blocks.  */
 
@@ -3613,7 +3684,7 @@ allocate_pseudovector (int memlen, int lisplen,
   enum { size_max = (1 << PSEUDOVECTOR_SIZE_BITS) - 1 };
   enum { rest_max = (1 << PSEUDOVECTOR_REST_BITS) - 1 };
   verify (size_max + rest_max <= VECTOR_ELTS_MAX);
-  eassert (0 <= tag && tag <= PVEC_FONT);
+  eassert (0 <= tag && tag <= PVEC_TAG_MAX);
   eassert (0 <= lisplen && lisplen <= zerolen && zerolen <= memlen);
   eassert (lisplen <= size_max);
   eassert (memlen <= size_max + rest_max);
@@ -6595,13 +6666,6 @@ garbage_collect (void)
   image_prune_animation_caches (false);
 #endif
 
-  if (!NILP (Vpost_gc_hook))
-    {
-      specpdl_ref gc_count = inhibit_garbage_collection ();
-      safe_run_hooks (Qpost_gc_hook);
-      unbind_to (gc_count, Qnil);
-    }
-
   /* Accumulate statistics.  */
   if (FLOATP (Vgc_elapsed))
     {
@@ -6620,6 +6684,13 @@ garbage_collect (void)
       if (tot_after < tot_before)
        malloc_probe (min (tot_before - tot_after, SIZE_MAX));
     }
+
+  if (!NILP (Vpost_gc_hook))
+    {
+      specpdl_ref gc_count = inhibit_garbage_collection ();
+      safe_run_hooks (Qpost_gc_hook);
+      unbind_to (gc_count, Qnil);
+    }
 }
 
 DEFUN ("garbage-collect", Fgarbage_collect, Sgarbage_collect, 0, 0, "",
diff --git a/src/android-asset.h b/src/android-asset.h
index 4fb309f1645..d7cf0f0cfed 100644
--- a/src/android-asset.h
+++ b/src/android-asset.h
@@ -340,7 +340,7 @@ android_asset_read_internal (AAsset *asset, int nbytes, 
char *buffer)
       /* Detect error conditions.  */
 
       if ((*env)->ExceptionCheck (env))
-       goto out;
+       goto out_errno;
 
       /* Detect EOF.  */
 
@@ -363,6 +363,14 @@ android_asset_read_internal (AAsset *asset, int nbytes, 
char *buffer)
   (*env)->ExceptionClear (env);
   (*env)->DeleteLocalRef (env, stash);
   return total;
+
+ out_errno:
+  /* Return an error indication if an exception arises while the file
+     is being read.  */
+  (*env)->ExceptionClear (env);
+  (*env)->DeleteLocalRef (env, stash);
+  errno = EIO;
+  return -1;
 }
 
 static long
@@ -399,7 +407,7 @@ AAsset_getBuffer (AAsset *asset)
   if (android_asset_read_internal (asset, length, buffer)
       != length)
     {
-      xfree (buffer);
+      free (buffer);
       return NULL;
     }
 
diff --git a/src/android.c b/src/android.c
index ed304baf0e6..aa4033c676f 100644
--- a/src/android.c
+++ b/src/android.c
@@ -18,6 +18,7 @@ You should have received a copy of the GNU General Public 
License
 along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
 
 #include <config.h>
+
 #include <allocator.h>
 #include <assert.h>
 #include <careadlinkat.h>
@@ -31,12 +32,15 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include <pthread.h>
 #include <semaphore.h>
 #include <signal.h>
+#include <stat-time.h>
 #include <stdckdint.h>
 #include <string.h>
-#include <sys/param.h>
 #include <timespec.h>
 #include <unistd.h>
 
+#include <sys/param.h>
+#include <sys/stat.h>
+
 /* Old NDK versions lack MIN and MAX.  */
 #include <minmax.h>
 
@@ -47,6 +51,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include "blockinput.h"
 #include "coding.h"
 #include "epaths.h"
+#include "systime.h"
 
 /* Whether or not Emacs is running inside the application process and
    Android windowing should be enabled.  */
@@ -68,7 +73,6 @@ bool android_init_gui;
 struct android_emacs_pixmap
 {
   jclass class;
-  jmethodID constructor;
   jmethodID constructor_mutable;
 };
 
@@ -82,7 +86,6 @@ struct android_emacs_drawable
 {
   jclass class;
   jmethodID get_bitmap;
-  jmethodID damage_rect;
 };
 
 struct android_emacs_window
@@ -106,6 +109,7 @@ struct android_emacs_window
   jmethodID set_dont_accept_focus;
   jmethodID set_dont_focus_on_map;
   jmethodID define_cursor;
+  jmethodID damage_rect;
 };
 
 struct android_emacs_cursor
@@ -187,6 +191,10 @@ static struct android_emacs_window window_class;
 /* Various methods associated with the EmacsCursor class.  */
 static struct android_emacs_cursor cursor_class;
 
+/* The time at which Emacs was installed, which also supplies the
+   mtime of asset files.  */
+struct timespec emacs_installation_time;
+
 /* The last event serial used.  This is a 32 bit value, but it is
    stored in unsigned long to be consistent with X.  */
 unsigned int event_serial;
@@ -273,6 +281,46 @@ static volatile sig_atomic_t android_pselect_interrupted;
 
 #endif
 
+/* Set the task name of the current task to NAME, a string at most 16
+   characters in length.
+
+   This name is displayed as that of the task (LWP)'s pthread in
+   GDB.  */
+
+static void
+android_set_task_name (const char *name)
+{
+  char proc_name[INT_STRLEN_BOUND (long)
+                + sizeof "/proc/self/task//comm"];
+  int fd;
+  pid_t lwp;
+  size_t length;
+
+  lwp = gettid ();
+  sprintf (proc_name, "/proc/self/task/%ld/comm", (long) lwp);
+  fd = open (proc_name, O_WRONLY | O_TRUNC);
+
+  if (fd < 0)
+    goto failure;
+
+  length = strlen (name);
+
+  if (write (fd, name, MIN (16, length)) < 0)
+    goto failure;
+
+  close (fd);
+  return;
+
+ failure:
+  __android_log_print (ANDROID_LOG_WARN, __func__,
+                      "Failed to set task name for LWP %ld: %s",
+                      (long) lwp, strerror (errno));
+
+  /* Close the file descriptor if it is already set.  */
+  if (fd >= 0)
+    close (fd);
+}
+
 static void *
 android_run_select_thread (void *data)
 {
@@ -289,6 +337,9 @@ android_run_select_thread (void *data)
   int sig;
 #endif
 
+  /* Set the name of this thread's LWP for debugging purposes.  */
+  android_set_task_name ("`android_select'");
+
 #if __ANDROID_API__ < 16
   /* A completely different implementation is used when building for
      Android versions earlier than 16, because pselect with a signal
@@ -788,6 +839,9 @@ android_run_debug_thread (void *data)
   char *line;
   size_t n;
 
+  /* Set the name of this thread's LWP for debugging purposes.  */
+  android_set_task_name ("`android_debug'");
+
   fd = (int) (intptr_t) data;
   file = fdopen (fd, "r");
 
@@ -824,22 +878,18 @@ android_user_full_name (struct passwd *pw)
     return (char *) "Android user";
 
   return pw->pw_gecos;
-#else
+#else /* !HAVE_STRUCT_PASSWD_PW_GECOS */
   return "Android user";
-#endif
+#endif /* HAVE_STRUCT_PASSWD_PW_GECOS */
 }
 
 
 
-/* Determine whether or not the specified file NAME describes a file
-   in the directory DIR, which should be an absolute file name.  NAME
-   must be in canonical form.
-
-   Value is NULL if not.  Otherwise, it is a pointer to the first
-   character in NAME after the part containing DIR and its trailing
-   directory separator.  */
+/* Return whether or not the specified file NAME designates a file in
+   the directory DIR, which should be an absolute file name.  NAME
+   must be in canonical form.  */
 
-const char *
+bool
 android_is_special_directory (const char *name, const char *dir)
 {
   size_t len;
@@ -848,7 +898,7 @@ android_is_special_directory (const char *name, const char 
*dir)
 
   len = strlen (dir);
   if (strncmp (name, dir, len))
-    return NULL;
+    return false;
 
   /* Now see if the character of NAME after len is either a directory
      separator or a terminating NULL.  */
@@ -856,20 +906,13 @@ android_is_special_directory (const char *name, const 
char *dir)
   name += len;
   switch (*name)
     {
-    case '\0':
-      /* Return the empty string if this is the end of the file
-        name.  */
-      return name;
-
-    case '/':
-      /* Return NAME (with the separator removed) if it describes a
-        file.  */
-      return name + 1;
-
-    default:
-      /* The file name doesn't match.  */
-      return NULL;
+    case '\0': /* NAME is an exact match for DIR.  */
+    case '/':  /* NAME is a constituent of DIR.  */
+      return true;
     }
+
+  /* The file name doesn't match.  */
+  return false;
 }
 
 #if 0
@@ -1247,6 +1290,7 @@ NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object,
   int pipefd[2];
   pthread_t thread;
   const char *java_string;
+  struct stat statb;
 
   /* Set the Android API level early, as it is used by
      `android_vfs_init'.  */
@@ -1341,13 +1385,24 @@ NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject 
object,
 
       android_class_path = strdup ((const char *) java_string);
 
-      if (!android_files_dir)
+      if (!android_class_path)
        emacs_abort ();
 
       (*env)->ReleaseStringUTFChars (env, (jstring) class_path,
                                     java_string);
     }
 
+  /* Derive the installation date from the modification time of the
+     file constitituing the class path.  */
+
+  emacs_installation_time = invalid_timespec ();
+
+  if (class_path)
+    {
+      if (!stat (android_class_path, &statb))
+       emacs_installation_time = get_stat_mtime (&statb);
+    }
+
   /* Calculate the site-lisp path.  */
 
   android_site_load_path = malloc (PATH_MAX + 1);
@@ -1497,7 +1552,7 @@ android_init_emacs_service (void)
               "(Lorg/gnu/emacs/EmacsWindow;)V");
   FIND_METHOD (clear_area, "clearArea",
               "(Lorg/gnu/emacs/EmacsWindow;IIII)V");
-  FIND_METHOD (ring_bell, "ringBell", "()V");
+  FIND_METHOD (ring_bell, "ringBell", "(I)V");
   FIND_METHOD (query_tree, "queryTree",
               "(Lorg/gnu/emacs/EmacsWindow;)[S");
   FIND_METHOD (get_screen_width, "getScreenWidth", "(Z)I");
@@ -1593,7 +1648,6 @@ android_init_emacs_pixmap (void)
                                        name, signature);       \
   assert (pixmap_class.c_name);
 
-  FIND_METHOD (constructor, "<init>", "(S[IIII)V");
   FIND_METHOD (constructor_mutable, "<init>", "(SIII)V");
 
 #undef FIND_METHOD
@@ -1656,7 +1710,6 @@ android_init_emacs_drawable (void)
   assert (drawable_class.c_name);
 
   FIND_METHOD (get_bitmap, "getBitmap", "()Landroid/graphics/Bitmap;");
-  FIND_METHOD (damage_rect, "damageRect", "(Landroid/graphics/Rect;)V");
 #undef FIND_METHOD
 }
 
@@ -1710,6 +1763,12 @@ android_init_emacs_window (void)
   FIND_METHOD (set_dont_accept_focus, "setDontAcceptFocus", "(Z)V");
   FIND_METHOD (define_cursor, "defineCursor",
               "(Lorg/gnu/emacs/EmacsCursor;)V");
+
+  /* In spite of the declaration of this function being located within
+     EmacsDrawable, the ID of the `damage_rect' method is retrieved
+     from EmacsWindow, which avoids virtual function dispatch within
+     android_damage_window.  */
+  FIND_METHOD (damage_rect, "damageRect", "(IIII)V");
 #undef FIND_METHOD
 }
 
@@ -2267,6 +2326,12 @@ NATIVE_NAME (shouldForwardMultimediaButtons) (JNIEnv 
*env,
   return !android_pass_multimedia_buttons_to_system;
 }
 
+JNIEXPORT jboolean JNICALL
+NATIVE_NAME (shouldForwardCtrlSpace) (JNIEnv *env, jobject object)
+{
+  return !android_intercept_control_space;
+}
+
 JNIEXPORT void JNICALL
 NATIVE_NAME (blitRect) (JNIEnv *env, jobject object,
                        jobject src, jobject dest,
@@ -3337,86 +3402,91 @@ android_create_pixmap_from_bitmap_data (char *data, 
unsigned int width,
                                        unsigned long background,
                                        unsigned int depth)
 {
-  android_handle prev_max_handle;
-  jobject object;
-  jintArray colors;
   android_pixmap pixmap;
+  jobject object;
+  AndroidBitmapInfo info;
+  unsigned int *depth_24;
+  unsigned char *depth_8;
+  void *bitmap_data;
   unsigned int x, y;
-  jint *region;
+  unsigned int r, g, b;
 
-  USE_SAFE_ALLOCA;
+  /* Create a pixmap with the right dimensions and depth.  */
+  pixmap = android_create_pixmap (width, height, depth);
 
-  /* Create the color array holding the data.  */
-  colors = (*android_java_env)->NewIntArray (android_java_env,
-                                            width * height);
-  android_exception_check ();
+  /* Lock the bitmap data.  */
+  bitmap_data = android_lock_bitmap (pixmap, &info, &object);
+
+  /* Merely return if locking the bitmap fails.  */
+  if (!bitmap_data)
+    return pixmap;
 
-  SAFE_NALLOCA (region, sizeof *region, width);
+  eassert (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888
+          || info.format == ANDROID_BITMAP_FORMAT_A_8);
 
-  for (y = 0; y < height; ++y)
+  /* Begin copying each line.  */
+
+  switch (info.format)
     {
-      for (x = 0; x < width; ++x)
+    case ANDROID_BITMAP_FORMAT_RGBA_8888:
+
+      /* Swizzle the pixels into ABGR format.  Android uses Skia's
+        ``native color type'', which is ABGR.  This is despite the
+        format being named ``ARGB'', and more confusingly
+        `ANDROID_BITMAP_FORMAT_RGBA_8888' in bitmap.h.  */
+
+      r = background & 0x00ff0000;
+      g = background & 0x0000ff00;
+      b = background & 0x000000ff;
+      background = (r >> 16) | g | (b << 16) | 0xff000000;
+      r = foreground & 0x00ff0000;
+      g = foreground & 0x0000ff00;
+      b = foreground & 0x000000ff;
+      foreground = (r >> 16) | g | (b << 16) | 0xff000000;
+
+      for (y = 0; y < height; ++y)
        {
-         if (depth == 24)
-           {
-             /* The alpha channels must be set, or otherwise, the
-                pixmap will be created entirely transparent.  */
+         depth_24 = (void *) ((char *) bitmap_data + y * info.stride);
 
-             if (data[x / 8] & (1 << (x % 8)))
-               region[x] = foreground | 0xff000000;
-             else
-               region[x] = background | 0xff000000;
-           }
-         else
-           {
-             if (data[x / 8] & (1 << (x % 8)))
-               region[x] = foreground;
-             else
-               region[x] = background;
-           }
+         for (x = 0; x < width; ++x)
+           depth_24[x] = ((data[x / 8] & (1 << (x % 8)))
+                          ? foreground : background);
+
+         data += (width + 7) / 8;
        }
 
-      (*android_java_env)->SetIntArrayRegion (android_java_env,
-                                             colors,
-                                             width * y, width,
-                                             region);
-      data += width / 8;
-    }
+      break;
 
-  /* First, allocate the pixmap handle.  */
-  prev_max_handle = max_handle;
-  pixmap = android_alloc_id ();
+    case ANDROID_BITMAP_FORMAT_A_8:
 
-  if (!pixmap)
-    {
-      ANDROID_DELETE_LOCAL_REF ((jobject) colors);
-      error ("Out of pixmap handles!");
-    }
+      /* 8-bit pixmaps are created, but in spite of that they are
+        employed only to represent bitmaps.  */
 
-  object = (*android_java_env)->NewObject (android_java_env,
-                                          pixmap_class.class,
-                                          pixmap_class.constructor,
-                                          (jshort) pixmap, colors,
-                                          (jint) width, (jint) height,
-                                          (jint) depth);
-  (*android_java_env)->ExceptionClear (android_java_env);
-  ANDROID_DELETE_LOCAL_REF ((jobject) colors);
+      foreground = (foreground ? 255 : 0);
+      background = (background ? 255 : 0);
 
-  if (!object)
-    {
-      max_handle = prev_max_handle;
-      memory_full (0);
+      for (y = 0; y < height; ++y)
+       {
+         depth_8 = (void *) ((char *) bitmap_data + y * info.stride);
+
+         for (x = 0; x < width; ++x)
+           depth_8[x] = ((data[x / 8] & (1 << (x % 8)))
+                         ? foreground : background);
+
+         data += (width + 7) / 8;
+       }
+
+      break;
+
+    default:
+      emacs_abort ();
     }
 
-  android_handles[pixmap].type = ANDROID_HANDLE_PIXMAP;
-  android_handles[pixmap].handle
-    = (*android_java_env)->NewGlobalRef (android_java_env, object);
+  /* Unlock the bitmap itself.  */
+  AndroidBitmap_unlockPixels (android_java_env, object);
   ANDROID_DELETE_LOCAL_REF (object);
 
-  if (!android_handles[pixmap].handle)
-    memory_full (0);
-
-  SAFE_FREE ();
+  /* Return the pixmap.  */
   return pixmap;
 }
 
@@ -4830,10 +4900,17 @@ android_put_image (android_pixmap handle, struct 
android_image *image)
 void
 android_bell (void)
 {
+  jint duration;
+
+  /* Restrict android_keyboard_bell_duration to values between 10 and
+     1000.  */
+  duration = MIN (1000, MAX (0, android_keyboard_bell_duration));
+
   (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
                                                 emacs_service,
                                                 service_class.class,
-                                                service_class.ring_bell);
+                                                service_class.ring_bell,
+                                                duration);
   android_exception_check ();
 }
 
@@ -4896,15 +4973,17 @@ android_query_tree (android_window handle, 
android_window *root_return,
   jsize nelements, i;
   android_window *children;
   jshort *shorts;
+  jmethodID method;
 
   window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
 
   /* window can be NULL, so this is a service method.  */
+  method = service_class.query_tree;
   array
-    = (*android_java_env)->CallObjectMethod (android_java_env,
-                                            emacs_service,
-                                            service_class.query_tree,
-                                            window);
+    = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env,
+                                                      emacs_service,
+                                                      service_class.class,
+                                                      method, window);
   android_exception_check ();
 
   /* The first element of the array is the parent window.  The rest
@@ -4957,9 +5036,10 @@ android_get_geometry (android_window handle,
   get_geometry = window_class.get_window_geometry;
 
   window_geometry
-    = (*android_java_env)->CallObjectMethod (android_java_env,
-                                            window,
-                                            get_geometry);
+    = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env,
+                                                      window,
+                                                      window_class.class,
+                                                      get_geometry);
   android_exception_check ();
 
   /* window_geometry is an array containing x, y, width and
@@ -5017,9 +5097,11 @@ android_translate_coordinates (android_window src, int x,
   window = android_resolve_handle (src, ANDROID_HANDLE_WINDOW);
   method = window_class.translate_coordinates;
   coordinates
-    = (*android_java_env)->CallObjectMethod (android_java_env,
-                                            window, method,
-                                            (jint) x, (jint) y);
+    = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env,
+                                                      window,
+                                                      window_class.class,
+                                                      method, (jint) x,
+                                                      (jint) y);
   android_exception_check ();
 
   /* The array must contain two elements: X, Y translated to the root
@@ -5057,7 +5139,9 @@ android_wc_lookup_string (android_key_pressed_event 
*event,
   const jchar *characters;
   jsize size;
   size_t i;
+  JNIEnv *env;
 
+  env = android_java_env;
   status = ANDROID_LOOKUP_NONE;
   rc = 0;
 
@@ -5108,9 +5192,10 @@ android_wc_lookup_string (android_key_pressed_event 
*event,
     {
       window = android_handles[event->window].handle;
       string
-       = (*android_java_env)->CallObjectMethod (android_java_env, window,
-                                                window_class.lookup_string,
-                                                (jint) event->serial);
+       = (*env)->CallNonvirtualObjectMethod (env, window,
+                                             window_class.class,
+                                             window_class.lookup_string,
+                                             (jint) event->serial);
       android_exception_check ();
 
       if (!string)
@@ -5118,13 +5203,11 @@ android_wc_lookup_string (android_key_pressed_event 
*event,
       else
        {
          /* Now return this input method string.  */
-         characters = (*android_java_env)->GetStringChars (android_java_env,
-                                                           string, NULL);
+         characters = (*env)->GetStringChars (env, string, NULL);
          android_exception_check_nonnull ((void *) characters, string);
 
-         /* Figure out how big the string is.  */
-         size = (*android_java_env)->GetStringLength (android_java_env,
-                                                      string);
+         /* Establish the size of the the string.  */
+         size = (*env)->GetStringLength (env, string);
 
          /* Copy over the string data.  */
          for (i = 0; i < MIN ((unsigned int) wchars_buffer, size); ++i)
@@ -5143,8 +5226,7 @@ android_wc_lookup_string (android_key_pressed_event 
*event,
          else
            rc = size;
 
-         (*android_java_env)->ReleaseStringChars (android_java_env, string,
-                                                  characters);
+         (*env)->ReleaseStringChars (env, string, characters);
          ANDROID_DELETE_LOCAL_REF (string);
        }
     }
@@ -5218,29 +5300,22 @@ void
 android_damage_window (android_drawable handle,
                       struct android_rectangle *damage)
 {
-  jobject drawable, rect;
+  jobject drawable;
 
   drawable = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
 
-  /* Now turn DAMAGE into a Java rectangle.  */
-  rect = (*android_java_env)->NewObject (android_java_env,
-                                        android_rect_class,
-                                        android_rect_constructor,
-                                        (jint) damage->x,
-                                        (jint) damage->y,
-                                        (jint) (damage->x
-                                                + damage->width),
-                                        (jint) (damage->y
-                                                + damage->height));
-  android_exception_check ();
-
   /* Post the damage to the drawable.  */
-  (*android_java_env)->CallVoidMethod (android_java_env,
-                                      drawable,
-                                      drawable_class.damage_rect,
-                                      rect);
-  android_exception_check_1 (rect);
-  ANDROID_DELETE_LOCAL_REF (rect);
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                drawable,
+                                                window_class.class,
+                                                window_class.damage_rect,
+                                                (jint) damage->x,
+                                                (jint) damage->y,
+                                                (jint) (damage->x
+                                                        + damage->width),
+                                                (jint) (damage->y
+                                                        + damage->height));
+  android_exception_check ();
 }
 
 
@@ -5365,11 +5440,15 @@ android_get_keysym_name (int keysym, char *name_return, 
size_t size)
 {
   jobject string;
   const char *buffer;
+  jmethodID method;
 
-  string = (*android_java_env)->CallObjectMethod (android_java_env,
-                                                 emacs_service,
-                                                 service_class.name_keysym,
-                                                 (jint) keysym);
+  method = service_class.name_keysym;
+  string
+    = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env,
+                                                      emacs_service,
+                                                      service_class.class,
+                                                      method,
+                                                      (jint) keysym);
   android_exception_check ();
 
   buffer = (*android_java_env)->GetStringUTFChars (android_java_env,
@@ -5650,8 +5729,9 @@ android_exception_check (void)
 }
 
 /* Check for JNI exceptions.  If there is one such exception, clear
-   it, then delete the local reference to OBJECT and call
-   memory_full.  */
+   it, then delete the local reference to OBJECT and call memory_full.
+   OBJECT can be NULL, which is a valid local reference to the Java
+   null object.  */
 
 void
 android_exception_check_1 (jobject object)
@@ -5665,7 +5745,10 @@ android_exception_check_1 (jobject object)
   /* Describe exactly what went wrong.  */
   (*android_java_env)->ExceptionDescribe (android_java_env);
   (*android_java_env)->ExceptionClear (android_java_env);
-  ANDROID_DELETE_LOCAL_REF (object);
+
+  if (object)
+    ANDROID_DELETE_LOCAL_REF (object);
+
   memory_full (0);
 }
 
@@ -5684,8 +5767,13 @@ android_exception_check_2 (jobject object, jobject 
object1)
   /* Describe exactly what went wrong.  */
   (*android_java_env)->ExceptionDescribe (android_java_env);
   (*android_java_env)->ExceptionClear (android_java_env);
-  ANDROID_DELETE_LOCAL_REF (object);
-  ANDROID_DELETE_LOCAL_REF (object1);
+
+  if (object)
+    ANDROID_DELETE_LOCAL_REF (object);
+
+  if (object1)
+    ANDROID_DELETE_LOCAL_REF (object1);
+
   memory_full (0);
 }
 
@@ -5705,9 +5793,16 @@ android_exception_check_3 (jobject object, jobject 
object1,
   /* Describe exactly what went wrong.  */
   (*android_java_env)->ExceptionDescribe (android_java_env);
   (*android_java_env)->ExceptionClear (android_java_env);
-  ANDROID_DELETE_LOCAL_REF (object);
-  ANDROID_DELETE_LOCAL_REF (object1);
-  ANDROID_DELETE_LOCAL_REF (object2);
+
+  if (object)
+    ANDROID_DELETE_LOCAL_REF (object);
+
+  if (object1)
+    ANDROID_DELETE_LOCAL_REF (object1);
+
+  if (object2)
+    ANDROID_DELETE_LOCAL_REF (object2);
+
   memory_full (0);
 }
 
@@ -5727,10 +5822,19 @@ android_exception_check_4 (jobject object, jobject 
object1,
   /* Describe exactly what went wrong.  */
   (*android_java_env)->ExceptionDescribe (android_java_env);
   (*android_java_env)->ExceptionClear (android_java_env);
-  ANDROID_DELETE_LOCAL_REF (object);
-  ANDROID_DELETE_LOCAL_REF (object1);
-  ANDROID_DELETE_LOCAL_REF (object2);
-  ANDROID_DELETE_LOCAL_REF (object3);
+
+  if (object)
+    ANDROID_DELETE_LOCAL_REF (object);
+
+  if (object1)
+    ANDROID_DELETE_LOCAL_REF (object1);
+
+  if (object2)
+    ANDROID_DELETE_LOCAL_REF (object2);
+
+  if (object3)
+    ANDROID_DELETE_LOCAL_REF (object3);
+
   memory_full (0);
 }
 
@@ -6051,11 +6155,13 @@ android_browse_url (Lisp_Object url, Lisp_Object send)
   const char *buffer;
 
   string = android_build_string (url);
-  value = (*android_java_env)->CallObjectMethod (android_java_env,
-                                                emacs_service,
-                                                service_class.browse_url,
-                                                string,
-                                                (jboolean) !NILP (send));
+  value
+    = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env,
+                                                      emacs_service,
+                                                      service_class.class,
+                                                      service_class.browse_url,
+                                                      string,
+                                                      (jboolean) !NILP (send));
   android_exception_check ();
 
   ANDROID_DELETE_LOCAL_REF (string);
@@ -6067,7 +6173,7 @@ android_browse_url (Lisp_Object url, Lisp_Object send)
   buffer = (*android_java_env)->GetStringUTFChars (android_java_env,
                                                   (jstring) value,
                                                   NULL);
-  android_exception_check_1 (string);
+  android_exception_check_1 (value);
 
   /* Otherwise, build the string describing the error.  */
   tem = build_string_from_utf8 (buffer);
@@ -6120,10 +6226,14 @@ android_query_battery (struct android_battery_state 
*status)
 {
   jlongArray array;
   jlong *longs;
+  jmethodID method;
 
-  array = (*android_java_env)->CallObjectMethod (android_java_env,
-                                                emacs_service,
-                                                service_class.query_battery);
+  method = service_class.query_battery;
+  array
+    = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env,
+                                                      emacs_service,
+                                                      service_class.class,
+                                                      method);
   android_exception_check ();
 
   /* A NULL return with no exception means that battery information
diff --git a/src/android.h b/src/android.h
index e865d7da665..d4605c11ad0 100644
--- a/src/android.h
+++ b/src/android.h
@@ -54,7 +54,7 @@ extern char *android_user_full_name (struct passwd *);
 /* File I/O operations.  Many of these are defined in
    androidvfs.c.  */
 
-extern const char *android_is_special_directory (const char *, const char *);
+extern bool android_is_special_directory (const char *, const char *);
 extern const char *android_get_home_directory (void);
 
 extern void android_vfs_init (JNIEnv *, jobject);
@@ -238,7 +238,7 @@ extern int android_rewrite_spawn_argv (const char ***);
 
 /* Define a substitute for use during Emacs compilation.  */
 
-#define android_is_special_directory(name, dir) ((const char *) NULL)
+#define android_is_special_directory(name, dir) (false)
 
 #endif /* !ANDROID_STUBIFY */
 
@@ -299,6 +299,10 @@ extern jobject emacs_service;
 /* Various methods associated with the EmacsService.  */
 extern struct android_emacs_service service_class;
 
+/* The time at which Emacs was installed, which also supplies the
+   mtime of asset files.  */
+extern struct timespec emacs_installation_time;
+
 #define ANDROID_DELETE_LOCAL_REF(ref)                          \
   ((*android_java_env)->DeleteLocalRef (android_java_env,      \
                                        (ref)))
diff --git a/src/androidfns.c b/src/androidfns.c
index 9e8372f524b..3ee9f7634aa 100644
--- a/src/androidfns.c
+++ b/src/androidfns.c
@@ -1326,9 +1326,9 @@ DEFUN ("x-display-backing-store", 
Fx_display_backing_store,
 {
   check_android_display_info (terminal);
 
-  /* The Java part is implemented in a way that it always does the
-     equivalent of backing store.  */
-  return Qalways;
+  /* Window contents are preserved insofar as they remain mapped, in a
+     fashion tantamount to WhenMapped.  */
+  return Qwhen_mapped;
 }
 
 DEFUN ("x-display-visual-class", Fx_display_visual_class,
@@ -3102,7 +3102,7 @@ syms_of_androidfns (void)
 {
   /* Miscellaneous symbols used by some functions here.  */
   DEFSYM (Qtrue_color, "true-color");
-  DEFSYM (Qalways, "always");
+  DEFSYM (Qwhen_mapped, "when-mapped");
 
   DEFVAR_LISP ("x-pointer-shape", Vx_pointer_shape,
     doc: /* SKIP: real text in xfns.c.  */);
@@ -3205,6 +3205,19 @@ Note that if you set this, you will no longer be able to 
quit Emacs
 using the volume down button.  */);
   android_pass_multimedia_buttons_to_system = false;
 
+  DEFVAR_BOOL ("android-intercept-control-space",
+              android_intercept_control_space,
+    doc: /* Whether Emacs should intercept C-SPC.
+When this variable is set, Emacs intercepts C-SPC events as they are
+delivered to a frame before they are registered and filtered by the
+input method.
+
+For no apparent purpose, Android input methods customarily discard SPC
+events with the Ctrl modifier set without delivering them to Emacs
+afterwards, which is an impediment to typing key sequences
+incorporating such keys.  */);
+  android_intercept_control_space = true;
+
   DEFVAR_BOOL ("android-use-exec-loader", android_use_exec_loader,
     doc: /* Whether or not to bypass system restrictions on program execution.
 
@@ -3219,6 +3232,14 @@ restrictions.
 This option has no effect on Android 9 and earlier.  */);
   android_use_exec_loader = true;
 
+  DEFVAR_INT ("android-keyboard-bell-duration",
+             android_keyboard_bell_duration,
+    doc: /* Number of milliseconds to vibrate after ringing the keyboard bell.
+The keyboard bell under Android systems takes the form of a vibrating
+element that is activated for a given number of milliseconds upon the
+bell being rung.  */);
+  android_keyboard_bell_duration = 50;
+
   /* Functions defined.  */
   defsubr (&Sx_create_frame);
   defsubr (&Sxw_color_defined_p);
diff --git a/src/androidfont.c b/src/androidfont.c
index db2f94008f2..9a1bf5652fc 100644
--- a/src/androidfont.c
+++ b/src/androidfont.c
@@ -204,11 +204,11 @@ android_init_font_driver (void)
   FIND_METHOD (open_font, "openFont", "(Lorg/gnu/emacs/EmacsFontDriver$Font"
               "Entity;I)Lorg/gnu/emacs/EmacsFontDriver$FontObject;");
   FIND_METHOD (has_char, "hasChar", "(Lorg/gnu/emacs/EmacsFontDriver$Font"
-              "Spec;C)I");
+              "Spec;I)I");
   FIND_METHOD (text_extents, "textExtents", "(Lorg/gnu/emacs/EmacsFontDriver"
               "$FontObject;[ILorg/gnu/emacs/EmacsFontDriver$FontMetrics;)V");
   FIND_METHOD (encode_char, "encodeChar", "(Lorg/gnu/emacs/EmacsFontDriver"
-              "$FontObject;C)I");
+              "$FontObject;I)I");
   FIND_METHOD (draw, "draw", "(Lorg/gnu/emacs/EmacsFontDriver$FontObject;"
               "Lorg/gnu/emacs/EmacsGC;Lorg/gnu/emacs/EmacsDrawable;[IIIIZ)I");
 
@@ -776,7 +776,7 @@ androidfont_open_font (struct frame *f, Lisp_Object 
font_entity,
 #undef DO_CARDINAL_FIELD
 
   /* This should eventually become unnecessary.  */
-  font->props[FONT_NAME_INDEX] = Ffont_xlfd_name (font_object, Qnil);
+  font->props[FONT_NAME_INDEX] = Ffont_xlfd_name (font_object, Qnil, Qt);
 
   return font_object;
 }
diff --git a/src/androidmenu.c b/src/androidmenu.c
index 94e3f646b44..ed26bdafa85 100644
--- a/src/androidmenu.c
+++ b/src/androidmenu.c
@@ -162,10 +162,11 @@ android_dismiss_menu (void *pointer)
   struct android_dismiss_menu_data *data;
 
   data = pointer;
-  (*android_java_env)->CallVoidMethod (android_java_env,
-                                      data->menu,
-                                      menu_class.dismiss,
-                                      data->window);
+  (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+                                                data->menu,
+                                                menu_class.class,
+                                                menu_class.dismiss,
+                                                data->window);
   popup_activated_flag = 0;
 }
 
@@ -247,7 +248,6 @@ android_menu_show (struct frame *f, int x, int y, int 
menuflags,
   jobject title_string, help_string, temp;
   size_t i;
   Lisp_Object pane_name, prefix;
-  const char *pane_string;
   specpdl_ref count, count1;
   Lisp_Object item_name, enable, def, tem, entry, type, selected;
   Lisp_Object help;
@@ -261,9 +261,11 @@ android_menu_show (struct frame *f, int x, int y, int 
menuflags,
   struct android_menu_subprefix *subprefix_1;
   bool checkmark;
   unsigned int serial;
+  JNIEnv *env;
 
   count = SPECPDL_INDEX ();
   serial = ++current_menu_serial;
+  env = android_java_env;
 
   block_input ();
 
@@ -313,9 +315,10 @@ android_menu_show (struct frame *f, int x, int y, int 
menuflags,
             context menu.  */
          store = current_context_menu;
          current_context_menu
-           = (*android_java_env)->CallObjectMethod (android_java_env,
-                                                    current_context_menu,
-                                                    menu_class.parent);
+           = (*env)->CallNonvirtualObjectMethod (env,
+                                                 current_context_menu,
+                                                 menu_class.class,
+                                                 menu_class.parent);
          android_exception_check ();
 
          if (store != context_menu)
@@ -353,20 +356,26 @@ android_menu_show (struct frame *f, int x, int y, int 
menuflags,
          /* Now figure out the title of this pane.  */
          pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
          prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
-         pane_string = (NILP (pane_name)
-                        ? "" : SSDATA (pane_name));
-         if ((menuflags & MENU_KEYMAPS) && !NILP (prefix))
-           pane_string++;
+
+         /* PANE_NAME may be nil, in which case it must be set to an
+            empty string.  */
+
+         if (NILP (pane_name))
+           pane_name = empty_unibyte_string;
+
+         /* Remove the leading prefix character if need be.  */
+
+         if ((menuflags & MENU_KEYMAPS) && !NILP (prefix)
+             && SCHARS (prefix))
+           pane_name = Fsubstring (pane_name, make_fixnum (1), Qnil);
 
          /* Add the pane.  */
-         temp = (*android_java_env)->NewStringUTF (android_java_env,
-                                                   pane_string);
+         temp = android_build_string (pane_name);
          android_exception_check ();
 
-         (*android_java_env)->CallVoidMethod (android_java_env,
-                                              current_context_menu,
-                                              menu_class.add_pane,
-                                              temp);
+         (*env)->CallNonvirtualVoidMethod (env, current_context_menu,
+                                           menu_class.class,
+                                           menu_class.add_pane, temp);
          android_exception_check ();
          ANDROID_DELETE_LOCAL_REF (temp);
 
@@ -403,11 +412,12 @@ android_menu_show (struct frame *f, int x, int y, int 
menuflags,
 
              store = current_context_menu;
              current_context_menu
-               = (*android_java_env)->CallObjectMethod (android_java_env,
-                                                        current_context_menu,
-                                                        menu_class.add_submenu,
-                                                        title_string,
-                                                        help_string);
+               = (*env)->CallNonvirtualObjectMethod (env,
+                                                     current_context_menu,
+                                                     menu_class.class,
+                                                     menu_class.add_submenu,
+                                                     title_string,
+                                                     help_string);
              android_exception_check ();
 
              if (store != context_menu)
@@ -449,17 +459,18 @@ android_menu_show (struct frame *f, int x, int y, int 
menuflags,
              checkmark = (EQ (type, QCtoggle)
                           || EQ (type, QCradio));
 
-             (*android_java_env)->CallVoidMethod (android_java_env,
-                                                  current_context_menu,
-                                                  menu_class.add_item,
-                                                  (jint) item_id,
-                                                  title_string,
-                                                  (jboolean) !NILP (enable),
-                                                  (jboolean) checkmark,
-                                                  (jboolean) !NILP (selected),
-                                                  help_string,
-                                                  (jboolean) (EQ (type,
-                                                                  QCradio)));
+             (*env)->CallNonvirtualVoidMethod (env,
+                                               current_context_menu,
+                                               menu_class.class,
+                                               menu_class.add_item,
+                                               (jint) item_id,
+                                               title_string,
+                                               (jboolean) !NILP (enable),
+                                               (jboolean) checkmark,
+                                               (jboolean) !NILP (selected),
+                                               help_string,
+                                               (jboolean) (EQ (type,
+                                                               QCradio)));
              android_exception_check ();
 
              if (title_string)
@@ -479,12 +490,12 @@ android_menu_show (struct frame *f, int x, int y, int 
menuflags,
   /* Now, display the context menu.  */
   window = android_resolve_handle (FRAME_ANDROID_WINDOW (f),
                                   ANDROID_HANDLE_WINDOW);
-  rc = (*android_java_env)->CallBooleanMethod (android_java_env,
-                                              context_menu,
-                                              menu_class.display,
-                                              window, (jint) x,
-                                              (jint) y,
-                                              (jint) serial);
+  rc = (*env)->CallNonvirtualBooleanMethod (env, context_menu,
+                                           menu_class.class,
+                                           menu_class.display,
+                                           window, (jint) x,
+                                           (jint) y,
+                                           (jint) serial);
   android_exception_check ();
 
   if (!rc)
@@ -652,6 +663,7 @@ android_dialog_show (struct frame *f, Lisp_Object title,
   int id;
   jmethodID method;
   unsigned int serial;
+  JNIEnv *env;
 
   /* Generate a unique ID for events from this dialog box.  */
   serial = ++current_menu_serial;
@@ -690,6 +702,11 @@ android_dialog_show (struct frame *f, Lisp_Object title,
     ANDROID_DELETE_LOCAL_REF (java_header);
   ANDROID_DELETE_LOCAL_REF (java_title);
 
+  /* Save the JNI environment pointer prior to constructing the
+     dialog, as typing (*android_java_env)->... gives rise to very
+     long lines.  */
+  env = android_java_env;
+
   /* Create the buttons.  */
   i = MENU_ITEMS_PANE_LENGTH;
   while (i < menu_items_used)
@@ -722,11 +739,11 @@ android_dialog_show (struct frame *f, Lisp_Object title,
 
          /* Add the button.  */
          temp = android_build_string (item_name);
-         (*android_java_env)->CallVoidMethod (android_java_env,
-                                              dialog,
-                                              dialog_class.add_button,
-                                              temp, (jint) i,
-                                              (jboolean) NILP (enable));
+         (*env)->CallNonvirtualVoidMethod (env, dialog,
+                                           dialog_class.class,
+                                           dialog_class.add_button,
+                                           temp, (jint) i,
+                                           (jboolean) NILP (enable));
          android_exception_check ();
          ANDROID_DELETE_LOCAL_REF (temp);
          i += MENU_ITEMS_ITEM_LENGTH;
@@ -734,9 +751,9 @@ android_dialog_show (struct frame *f, Lisp_Object title,
     }
 
   /* The dialog is now built.  Run it.  */
-  rc = (*android_java_env)->CallBooleanMethod (android_java_env,
-                                              dialog,
-                                              dialog_class.display);
+  rc = (*env)->CallNonvirtualBooleanMethod (env, dialog,
+                                           dialog_class.class,
+                                           dialog_class.display);
   android_exception_check ();
 
   if (!rc)
diff --git a/src/androidselect.c b/src/androidselect.c
index 5735eda2dd5..cf2265d4cf4 100644
--- a/src/androidselect.c
+++ b/src/androidselect.c
@@ -660,12 +660,18 @@ keywords is understood:
   :icon         The name of a drawable resource to display as the
                 notification's icon.
 
-The notification group and urgency are ignored on Android 7.1 and
-earlier versions of Android.  Outside such older systems, it
-identifies a category that will be displayed in the system Settings
-menu.  The urgency provided always extends to affect all notifications
-displayed within that category.  If the group is not provided, it
-defaults to the string "Desktop Notifications".
+The notification group is ignored on Android 7.1 and earlier versions
+of Android.  Outside such older systems, it identifies a category that
+will be displayed in the system Settings menu, and the urgency
+provided always extends to affect all notifications displayed within
+that category.  If the group is not provided, it defaults to the
+string "Desktop Notifications".
+
+Each caller should strive to provide one unchanging combination of
+notification group and urgency for each kind of notification it sends,
+inasmuch as the system may, subject to user configuration, disregard
+the urgency specified within a notification, should it not be the
+first notification sent to its notification group.
 
 The provided icon should be the name of a "drawable resource" present
 within the "android.R.drawable" class designating an icon with a
diff --git a/src/androidterm.c b/src/androidterm.c
index a60dd50e5db..438f8ce1fbb 100644
--- a/src/androidterm.c
+++ b/src/androidterm.c
@@ -2440,7 +2440,8 @@ android_reset_clip_rectangles (struct frame *f, struct 
android_gc *gc)
 
 static void
 android_clip_to_row (struct window *w, struct glyph_row *row,
-                    enum glyph_row_area area, struct android_gc *gc)
+                    enum glyph_row_area area, struct android_gc *gc,
+                    struct android_rectangle *rect_return)
 {
   struct android_rectangle clip_rect;
   int window_x, window_y, window_width;
@@ -2454,6 +2455,9 @@ android_clip_to_row (struct window *w, struct glyph_row 
*row,
   clip_rect.height = row->visible_height;
 
   android_set_clip_rectangles (gc, 0, 0, &clip_rect, 1);
+
+  if (rect_return)
+    *rect_return = clip_rect;
 }
 
 static void
@@ -2463,9 +2467,10 @@ android_draw_fringe_bitmap (struct window *w, struct 
glyph_row *row,
   struct frame *f = XFRAME (WINDOW_FRAME (w));
   struct android_gc *gc = f->output_data.android->normal_gc;
   struct face *face = p->face;
+  struct android_rectangle clip_rect;
 
   /* Must clip because of partially visible lines.  */
-  android_clip_to_row (w, row, ANY_AREA, gc);
+  android_clip_to_row (w, row, ANY_AREA, gc, &clip_rect);
 
   if (p->bx >= 0 && !p->overlay_p)
     {
@@ -2499,6 +2504,8 @@ android_draw_fringe_bitmap (struct window *w, struct 
glyph_row *row,
       struct android_gc_values gcv;
       unsigned long background, cursor_pixel;
       int depth;
+      struct android_rectangle image_rect, dest;
+      int px, py, pwidth, pheight;
 
       drawable = FRAME_ANDROID_DRAWABLE (f);
       clipmask = ANDROID_NONE;
@@ -2506,6 +2513,27 @@ android_draw_fringe_bitmap (struct window *w, struct 
glyph_row *row,
       cursor_pixel = f->output_data.android->cursor_pixel;
       depth = FRAME_DISPLAY_INFO (f)->n_planes;
 
+      /* Intersect the destination rectangle with that of the row.
+        Setting a clip mask overrides the clip rectangles provided by
+        x_clip_to_row, so clipping must be performed by hand.  */
+
+      image_rect.x = p->x;
+      image_rect.y = p->y;
+      image_rect.width = p->wd;
+      image_rect.height = p->h;
+
+      if (!gui_intersect_rectangles (&clip_rect, &image_rect, &dest))
+       /* The entire destination rectangle falls outside the row.  */
+       goto undo_clip;
+
+      /* Extrapolate the source rectangle from the difference between
+        the destination and image rectangles.  */
+
+      px = dest.x - image_rect.x;
+      py = dest.y - image_rect.y;
+      pwidth = dest.width;
+      pheight = dest.height;
+
       if (p->wd > 8)
        bits = (char *) (p->bits + p->dh);
       else
@@ -2533,8 +2561,8 @@ android_draw_fringe_bitmap (struct window *w, struct 
glyph_row *row,
                             &gcv);
        }
 
-      android_copy_area (pixmap, drawable, gc, 0, 0, p->wd, p->h,
-                        p->x, p->y);
+      android_copy_area (pixmap, drawable, gc, px, py,
+                        pwidth, pheight, dest.x, dest.y);
       android_free_pixmap (pixmap);
 
       if (p->overlay_p)
@@ -2545,6 +2573,7 @@ android_draw_fringe_bitmap (struct window *w, struct 
glyph_row *row,
        }
     }
 
+ undo_clip:
   android_reset_clip_rectangles (f, gc);
 }
 
@@ -4327,7 +4356,7 @@ android_draw_hollow_cursor (struct window *w, struct 
glyph_row *row)
        wd -= 1;
     }
   /* Set clipping, draw the rectangle, and reset clipping again.  */
-  android_clip_to_row (w, row, TEXT_AREA, gc);
+  android_clip_to_row (w, row, TEXT_AREA, gc, NULL);
   android_draw_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, x, y, wd, h - 1);
   android_reset_clip_rectangles (f, gc);
 }
@@ -4385,7 +4414,7 @@ android_draw_bar_cursor (struct window *w, struct 
glyph_row *row, int width,
          FRAME_DISPLAY_INFO (f)->scratch_cursor_gc = gc;
        }
 
-      android_clip_to_row (w, row, TEXT_AREA, gc);
+      android_clip_to_row (w, row, TEXT_AREA, gc, NULL);
 
       if (kind == BAR_CURSOR)
        {
diff --git a/src/androidvfs.c b/src/androidvfs.c
index d6b832d6caf..0e5bbf8a13e 100644
--- a/src/androidvfs.c
+++ b/src/androidvfs.c
@@ -26,6 +26,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include <errno.h>
 #include <minmax.h>
 #include <string.h>
+#include <systime.h>
 #include <semaphore.h>
 
 #include <sys/stat.h>
@@ -1920,6 +1921,21 @@ android_afs_open (struct android_vnode *vnode, int flags,
       /* Size of the file.  */
       info->statb.st_size = AAsset_getLength (asset);
 
+      /* If the installation date can be ascertained, return that as
+        the file's modification time.  */
+
+      if (timespec_valid_p (emacs_installation_time))
+       {
+#ifdef STAT_TIMESPEC
+         STAT_TIMESPEC (&info->statb, st_mtim) = emacs_installation_time;
+#else /* !STAT_TIMESPEC */
+          /* Headers supplied by the NDK r10b contain a `struct stat'
+            without POSIX fields for nano-second timestamps.  */
+         info->statb.st_mtime = emacs_installation_time.tv_sec;
+         info->statb.st_mtime_nsec = emacs_installation_time.tv_nsec;
+#endif /* STAT_TIMESPEC */
+       }
+
       /* Chain info onto afs_file_descriptors.  */
       afs_file_descriptors = info;
 
@@ -2058,7 +2074,7 @@ android_afs_stat (struct android_vnode *vnode, struct 
stat *statb)
       /* Concoct a nonexistent device and an inode number.  */
       statb->st_dev = -1;
       statb->st_ino = 0;
-      return 0;
+      goto set_file_times;
     }
 
   /* AASSET_MODE_STREAMING is fastest here.  */
@@ -2083,6 +2099,24 @@ android_afs_stat (struct android_vnode *vnode, struct 
stat *statb)
 
   /* Close the asset.  */
   AAsset_close (asset_desc);
+
+ set_file_times:
+
+  /* If the installation date can be ascertained, return that as the
+     file's modification time.  */
+
+  if (timespec_valid_p (emacs_installation_time))
+    {
+#ifdef STAT_TIMESPEC
+      STAT_TIMESPEC (statb, st_mtim) = emacs_installation_time;
+#else /* !STAT_TIMESPEC */
+      /* Headers supplied by the NDK r10b contain a `struct stat'
+        without POSIX fields for nano-second timestamps.  */
+      statb->st_mtime = emacs_installation_time.tv_sec;
+      statb->st_mtime_nsec = emacs_installation_time.tv_nsec;
+#endif /* STAT_TIMESPEC */
+    }
+
   return 0;
 }
 
@@ -3014,6 +3048,7 @@ android_authority_open (struct android_vnode *vnode, int 
flags,
   size_t length;
   jobject string;
   int fd;
+  JNIEnv *env;
 
   vp = (struct android_authority_vnode *) vnode;
 
@@ -3025,39 +3060,40 @@ android_authority_open (struct android_vnode *vnode, 
int flags,
       return -1;
     }
 
+  /* Save the JNI environment within `env', to make wrapping
+     subsequent lines referencing CallNonvirtualIntMethod
+     feasible.  */
+  env = android_java_env;
+
   /* Allocate a buffer to hold the file name.  */
   length = strlen (vp->uri);
-  string = (*android_java_env)->NewByteArray (android_java_env,
-                                             length);
+  string = (*env)->NewByteArray (env, length);
   if (!string)
     {
-      (*android_java_env)->ExceptionClear (android_java_env);
+      (*env)->ExceptionClear (env);
       errno = ENOMEM;
       return -1;
     }
 
   /* Copy the URI into this byte array.  */
-  (*android_java_env)->SetByteArrayRegion (android_java_env,
-                                          string, 0, length,
-                                          (jbyte *) vp->uri);
+  (*env)->SetByteArrayRegion (env, string, 0, length,
+                             (jbyte *) vp->uri);
 
   /* Try to open the file descriptor.  */
 
-  fd
-    = (*android_java_env)->CallIntMethod (android_java_env,
-                                         emacs_service,
-                                         service_class.open_content_uri,
-                                         string,
-                                         (jboolean) ((mode & O_WRONLY
-                                                      || mode & O_RDWR)
-                                                     != 0),
-                                         (jboolean) !(mode & O_WRONLY),
-                                         (jboolean) ((mode & O_TRUNC)
-                                                     != 0));
-
-  if ((*android_java_env)->ExceptionCheck (android_java_env))
+  fd = (*env)->CallNonvirtualIntMethod (env, emacs_service,
+                                       service_class.class,
+                                       service_class.open_content_uri,
+                                       string,
+                                       (jboolean) ((mode & O_WRONLY
+                                                    || mode & O_RDWR)
+                                                   != 0),
+                                       (jboolean) !(mode & O_WRONLY),
+                                       (jboolean) ((mode & O_TRUNC)
+                                                   != 0));
+  if ((*env)->ExceptionCheck (env))
     {
-      (*android_java_env)->ExceptionClear (android_java_env);
+      (*env)->ExceptionClear (env);
       errno = ENOMEM;
       ANDROID_DELETE_LOCAL_REF (string);
       return -1;
@@ -4233,10 +4269,11 @@ android_saf_delete_document (const char *tree, const 
char *doc_id,
 
   /* Now, try to delete the document.  */
   method = service_class.delete_document;
-  rc = (*android_java_env)->CallIntMethod (android_java_env,
-                                          emacs_service,
-                                          method, uri, id,
-                                          name);
+  rc = (*android_java_env)->CallNonvirtualIntMethod (android_java_env,
+                                                    emacs_service,
+                                                    service_class.class,
+                                                    method, uri, id,
+                                                    name);
 
   if (android_saf_exception_check (3, id, uri, name))
     return -1;
@@ -4391,11 +4428,13 @@ android_saf_move_document (const char *uri, char 
**doc_id,
 
   /* Do the rename.  */
   method = service_class.move_document;
-  result = (*android_java_env)->CallObjectMethod (android_java_env,
-                                                 emacs_service,
-                                                 method, uri1,
-                                                 doc_id1, dir_name1,
-                                                 dst_id1, src_id1);
+  result
+    = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env,
+                                                      emacs_service,
+                                                      service_class.class,
+                                                      method, uri1,
+                                                      doc_id1, dir_name1,
+                                                      dst_id1, src_id1);
   if (android_saf_exception_check (5, src_id1, dst_id1, dir_name1,
                                   doc_id1, uri1))
     {
@@ -7341,6 +7380,21 @@ android_asset_fstat (struct android_fd_or_asset asset,
   statb->st_uid = 0;
   statb->st_gid = 0;
 
+  /* If the installation date can be ascertained, return that as the
+     file's modification time.  */
+
+  if (timespec_valid_p (emacs_installation_time))
+    {
+#ifdef STAT_TIMESPEC
+      STAT_TIMESPEC (statb, st_mtim) = emacs_installation_time;
+#else /* !STAT_TIMESPEC */
+      /* Headers supplied by the NDK r10b contain a `struct stat'
+        without POSIX fields for nano-second timestamps.  */
+      statb->st_mtime = emacs_installation_time.tv_sec;
+      statb->st_mtime_nsec = emacs_installation_time.tv_nsec;
+#endif /* STAT_TIMESPEC */
+    }
+
   /* Size of the file.  */
   statb->st_size = AAsset_getLength (asset.asset);
   return 0;
diff --git a/src/buffer.c b/src/buffer.c
index 9facc4b7ab8..a7299f4a49e 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -4099,7 +4099,7 @@ report_overlay_modification (Lisp_Object start, 
Lisp_Object end, bool after,
            }
          /* Test for intersecting intervals.  This does the right thing
             for both insertion and deletion.  */
-         if (! insertion || (end_arg > obegin && begin_arg < oend))
+         if (end_arg > obegin && begin_arg < oend)
            {
              Lisp_Object prop = Foverlay_get (overlay, Qmodification_hooks);
              if (!NILP (prop))
diff --git a/src/buffer.h b/src/buffer.h
index e71ffe28045..b2bd15657dc 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -656,9 +656,9 @@ struct buffer
   ptrdiff_t last_window_start;
 
   /* If the long line scan cache is enabled (i.e. the buffer-local
-     variable cache-long-line-scans is non-nil), newline_cache
-     points to the newline cache, and width_run_cache points to the
-     width run cache.
+     variable cache-long-scans is non-nil), newline_cache points to
+     the newline cache, and width_run_cache points to the width run
+     cache.
 
      The newline cache records which stretches of the buffer are
      known *not* to contain newlines, so that they can be skipped
diff --git a/src/callproc.c b/src/callproc.c
index 082c65c4f14..96db52402c8 100644
--- a/src/callproc.c
+++ b/src/callproc.c
@@ -1735,6 +1735,10 @@ getenv_internal (const char *var, ptrdiff_t varlen, char 
**value,
   }
 #endif
 
+  /* Setting DISPLAY under Android hinders attempts to display other
+     programs within X servers that are available for Android.  */
+
+#ifndef HAVE_ANDROID
   /* For DISPLAY try to get the values from the frame or the initial env.  */
   if (strcmp (var, "DISPLAY") == 0)
     {
@@ -1747,12 +1751,13 @@ getenv_internal (const char *var, ptrdiff_t varlen, 
char **value,
          *valuelen = SBYTES (display);
          return 1;
        }
-#endif
+#endif /* !HAVE_PGTK */
       /* If still not found, Look for DISPLAY in Vinitial_environment.  */
       if (getenv_internal_1 (var, varlen, value, valuelen,
                             Vinitial_environment))
        return *value ? 1 : 0;
     }
+#endif /* !HAVE_ANDROID */
 
   return 0;
 }
@@ -1845,7 +1850,9 @@ make_environment_block (Lisp_Object current_dir)
     register char **new_env;
     char **p, **q;
     register int new_length;
+#ifndef HAVE_ANDROID
     Lisp_Object display = Qnil;
+#endif /* !HAVE_ANDROID */
 
     new_length = 0;
 
@@ -1853,14 +1860,20 @@ make_environment_block (Lisp_Object current_dir)
         CONSP (tem) && STRINGP (XCAR (tem));
         tem = XCDR (tem))
       {
+#ifndef HAVE_ANDROID
        if (strncmp (SSDATA (XCAR (tem)), "DISPLAY", 7) == 0
            && (SDATA (XCAR (tem)) [7] == '\0'
                || SDATA (XCAR (tem)) [7] == '='))
          /* DISPLAY is specified in process-environment.  */
          display = Qt;
+#endif /* !HAVE_ANDROID */
        new_length++;
       }
 
+    /* Setting DISPLAY under Android hinders attempts to display other
+       programs within X servers that are available for Android.  */
+
+#ifndef HAVE_ANDROID
     /* If not provided yet, use the frame's DISPLAY.  */
     if (NILP (display))
       {
@@ -1875,7 +1888,7 @@ make_environment_block (Lisp_Object current_dir)
            && strcmp (G_OBJECT_TYPE_NAME (FRAME_X_DISPLAY (SELECTED_FRAME ())),
                       "GdkX11Display"))
          tmp = Qnil;
-#endif
+#endif /* HAVE_PGTK */
 
        if (!STRINGP (tmp) && CONSP (Vinitial_environment))
          /* If still not found, Look for DISPLAY in Vinitial_environment.  */
@@ -1887,6 +1900,7 @@ make_environment_block (Lisp_Object current_dir)
            new_length++;
          }
       }
+#endif /* !HAVE_ANDROID */
 
     /* new_length + 2 to include PWD and terminating 0.  */
     env = new_env = xnmalloc (new_length + 2, sizeof *env);
@@ -1896,6 +1910,7 @@ make_environment_block (Lisp_Object current_dir)
     if (egetenv ("PWD"))
       *new_env++ = pwd_var;
 
+#ifndef HAVE_ANDROID
     if (STRINGP (display))
       {
        char *vdata = xmalloc (sizeof "DISPLAY=" + SBYTES (display));
@@ -1903,6 +1918,7 @@ make_environment_block (Lisp_Object current_dir)
        lispstpcpy (stpcpy (vdata, "DISPLAY="), display);
        new_env = add_env (env, new_env, vdata);
       }
+#endif /* !HAVE_ANDROID */
 
     /* Overrides.  */
     for (tem = Vprocess_environment;
diff --git a/src/chartab.c b/src/chartab.c
index 6f0bc28f31b..58bb1658504 100644
--- a/src/chartab.c
+++ b/src/chartab.c
@@ -580,7 +580,8 @@ DEFUN ("char-table-range", Fchar_table_range, 
Schar_table_range,
        2, 2, 0,
        doc: /* Return the value in CHAR-TABLE for a range of characters RANGE.
 RANGE should be nil (for the default value),
-a cons of character codes (for characters in the range), or a character code.  
*/)
+a cons of character codes (for characters in the range), or a character code.
+If RANGE is a cons (FROM . TO), the function returns the value for FROM.  */)
   (Lisp_Object char_table, Lisp_Object range)
 {
   Lisp_Object val;
diff --git a/src/data.c b/src/data.c
index 8bf1402d500..6702eb1a223 100644
--- a/src/data.c
+++ b/src/data.c
@@ -886,7 +886,7 @@ add_to_function_history (Lisp_Object symbol, Lisp_Object 
olddef)
   Lisp_Object past = Fget (symbol, Qfunction_history);
   Lisp_Object file = Qnil;
   /* FIXME: Sadly, `Vload_file_name` gives less precise information
-     (it's sometimes non-nil when it shoujld be nil).  */
+     (it's sometimes non-nil when it should be nil).  */
   Lisp_Object tail = Vcurrent_load_list;
   FOR_EACH_TAIL_SAFE (tail)
     if (NILP (XCDR (tail)) && STRINGP (XCAR (tail)))
@@ -1635,8 +1635,6 @@ set_internal (Lisp_Object symbol, Lisp_Object newval, 
Lisp_Object where,
   bool voide = BASE_EQ (newval, Qunbound);
 
   /* If restoring in a dead buffer, do nothing.  */
-  /* if (BUFFERP (where) && NILP (XBUFFER (where)->name))
-      return; */
 
   CHECK_SYMBOL (symbol);
   struct Lisp_Symbol *sym = XSYMBOL (symbol);
@@ -4398,8 +4396,6 @@ syms_of_data (void)
   defsubr (&Sbool_vector_count_consecutive);
   defsubr (&Sbool_vector_count_population);
 
-  set_symbol_function (Qwholenump, XSYMBOL (Qnatnump)->u.s.function);
-
   DEFVAR_LISP ("most-positive-fixnum", Vmost_positive_fixnum,
               doc: /* The greatest integer that is represented efficiently.
 This variable cannot be set; trying to do so will signal an error.  */);
diff --git a/src/fileio.c b/src/fileio.c
index 23e1a83d8bf..8919e08e1fd 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -4805,7 +4805,7 @@ by calling `format-decode', which see.  */)
 
        /* 'try' is reserved in some compilers (Microsoft C).  */
        ptrdiff_t trytry = min (gap_size, READ_BUF_SIZE);
-       if (!NILP (end))
+       if (seekable || !NILP (end))
          trytry = min (trytry, total - inserted);
 
        if (!seekable && NILP (end))
diff --git a/src/fns.c b/src/fns.c
index ae9969a5432..a3f89637dfd 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -141,6 +141,10 @@ efficient.  */)
 
   if (STRINGP (sequence))
     val = SCHARS (sequence);
+  else if (CONSP (sequence))
+    val = list_length (sequence);
+  else if (NILP (sequence))
+    val = 0;
   else if (VECTORP (sequence))
     val = ASIZE (sequence);
   else if (CHAR_TABLE_P (sequence))
@@ -149,10 +153,6 @@ efficient.  */)
     val = bool_vector_size (sequence);
   else if (COMPILEDP (sequence) || RECORDP (sequence))
     val = PVSIZE (sequence);
-  else if (CONSP (sequence))
-    val = list_length (sequence);
-  else if (NILP (sequence))
-    val = 0;
   else
     wrong_type_argument (Qsequencep, sequence);
 
@@ -2104,7 +2104,27 @@ changing the value of a sequence `foo'.  See also 
`remove', which
 does not modify the argument.  */)
   (Lisp_Object elt, Lisp_Object seq)
 {
-  if (VECTORP (seq))
+  if (NILP (seq))
+    ;
+  else if (CONSP (seq))
+    {
+      Lisp_Object prev = Qnil, tail = seq;
+
+      FOR_EACH_TAIL (tail)
+       {
+         if (!NILP (Fequal (elt, XCAR (tail))))
+           {
+             if (NILP (prev))
+               seq = XCDR (tail);
+             else
+               Fsetcdr (prev, XCDR (tail));
+           }
+         else
+           prev = tail;
+       }
+      CHECK_LIST_END (tail, seq);
+    }
+  else if (VECTORP (seq))
     {
       ptrdiff_t n = 0;
       ptrdiff_t size = ASIZE (seq);
@@ -2193,23 +2213,7 @@ does not modify the argument.  */)
        }
     }
   else
-    {
-      Lisp_Object prev = Qnil, tail = seq;
-
-      FOR_EACH_TAIL (tail)
-       {
-         if (!NILP (Fequal (elt, XCAR (tail))))
-           {
-             if (NILP (prev))
-               seq = XCDR (tail);
-             else
-               Fsetcdr (prev, XCDR (tail));
-           }
-         else
-           prev = tail;
-       }
-      CHECK_LIST_END (tail, seq);
-    }
+    wrong_type_argument (Qsequencep, seq);
 
   return seq;
 }
@@ -2222,8 +2226,6 @@ This function may destructively modify SEQ to produce the 
value.  */)
 {
   if (NILP (seq))
     return seq;
-  else if (STRINGP (seq))
-    return Freverse (seq);
   else if (CONSP (seq))
     {
       Lisp_Object prev, tail, next;
@@ -2263,6 +2265,8 @@ This function may destructively modify SEQ to produce the 
value.  */)
          bool_vector_set (seq, size - i - 1, tem);
        }
     }
+  else if (STRINGP (seq))
+    return Freverse (seq);
   else
     wrong_type_argument (Qarrayp, seq);
   return seq;
@@ -2334,9 +2338,9 @@ See also the function `nreverse', which is used more 
often.  */)
 
 
 /* Stably sort LIST ordered by PREDICATE using the TIMSORT
-   algorithm. This converts the list to a vector, sorts the vector,
-   and returns the result converted back to a list.  The input list is
-   destructively reused to hold the sorted result.  */
+   algorithm.  This converts the list to a vector, sorts the vector,
+   and returns the result converted back to a list.  The input list
+   is destructively reused to hold the sorted result.  */
 
 static Lisp_Object
 sort_list (Lisp_Object list, Lisp_Object predicate)
@@ -2774,10 +2778,13 @@ internal_equal (Lisp_Object o1, Lisp_Object o2, enum 
equal_kind equal_kind,
 
   /* A symbol with position compares the contained symbol, and is
      `equal' to the corresponding ordinary symbol.  */
-  if (SYMBOL_WITH_POS_P (o1))
-    o1 = SYMBOL_WITH_POS_SYM (o1);
-  if (SYMBOL_WITH_POS_P (o2))
-    o2 = SYMBOL_WITH_POS_SYM (o2);
+  if (symbols_with_pos_enabled)
+    {
+      if (SYMBOL_WITH_POS_P (o1))
+       o1 = SYMBOL_WITH_POS_SYM (o1);
+      if (SYMBOL_WITH_POS_P (o2))
+       o2 = SYMBOL_WITH_POS_SYM (o2);
+    }
 
   if (BASE_EQ (o1, o2))
     return true;
@@ -2825,8 +2832,8 @@ internal_equal (Lisp_Object o1, Lisp_Object o2, enum 
equal_kind equal_kind,
        if (ASIZE (o2) != size)
          return false;
 
-       /* Compare bignums, overlays, markers, and boolvectors
-          specially, by comparing their values.  */
+       /* Compare bignums, overlays, markers, boolvectors, and
+          symbols with position specially, by comparing their values.  */
        if (BIGNUMP (o1))
          return mpz_cmp (*xbignum_val (o1), *xbignum_val (o2)) == 0;
        if (OVERLAYP (o1))
@@ -2858,6 +2865,11 @@ internal_equal (Lisp_Object o1, Lisp_Object o2, enum 
equal_kind equal_kind,
        if (TS_NODEP (o1))
          return treesit_node_eq (o1, o2);
 #endif
+       if (SYMBOL_WITH_POS_P(o1)) /* symbols_with_pos_enabled is false.  */
+         return (BASE_EQ (XSYMBOL_WITH_POS (o1)->sym,
+                          XSYMBOL_WITH_POS (o2)->sym)
+                 && BASE_EQ (XSYMBOL_WITH_POS (o1)->pos,
+                             XSYMBOL_WITH_POS (o2)->pos));
 
        /* Aside from them, only true vectors, char-tables, compiled
           functions, and fonts (font-spec, font-entity, font-object)
diff --git a/src/font.c b/src/font.c
index ff81fefcad0..d9367929467 100644
--- a/src/font.c
+++ b/src/font.c
@@ -252,12 +252,20 @@ font_build_object (int vectorsize, Lisp_Object type,
 {
   int len;
   char name[256];
-  Lisp_Object font_object = font_make_object (vectorsize, entity, pixelsize);
+  char *xlfd_name;
+  Lisp_Object font_object;
+
+  font_object = font_make_object (vectorsize, entity, pixelsize);
 
   ASET (font_object, FONT_TYPE_INDEX, type);
-  len = font_unparse_xlfd (entity, pixelsize, name, sizeof name);
-  if (len > 0)
-    ASET (font_object, FONT_NAME_INDEX, make_string (name, len));
+  xlfd_name = font_dynamic_unparse_xlfd (entity, pixelsize);
+
+  if (xlfd_name)
+    {
+      ASET (font_object, FONT_NAME_INDEX, build_string (xlfd_name));
+      xfree (xlfd_name);
+    }
+
   len = font_unparse_fcname (entity, pixelsize, name, sizeof name);
   if (len > 0)
     ASET (font_object, FONT_FULLNAME_INDEX, make_string (name, len));
@@ -1067,8 +1075,8 @@ font_parse_xlfd_1 (char *name, ptrdiff_t len, Lisp_Object 
font, int segments)
   Lisp_Object val;
   char *p;
 
-  if (len > 255 || !len)
-    /* Maximum XLFD name length is 255. */
+  /* Reject empty XLFDs.  */
+  if (!len)
     return -1;
 
   /* Accept "*-.." as a fully specified XLFD. */
@@ -1276,6 +1284,167 @@ font_parse_xlfd (char *name, ptrdiff_t len, Lisp_Object 
font)
     return -1;
 }
 
+/* Return the XLFD name of FONT as a NULL terminated string, or NULL
+   if the font is invalid.  If FONT is a scalable font, return
+   PIXEL_SIZE as the XLFD's pixel size in lieu of its
+   FONT_SIZE_INDEX.  */
+
+char *
+font_dynamic_unparse_xlfd (Lisp_Object font, int pixel_size)
+{
+  char *p;
+  const char *f[XLFD_REGISTRY_INDEX + 1];
+  Lisp_Object val;
+  int i, j;
+  char *name;
+  USE_SAFE_ALLOCA;
+
+  eassert (FONTP (font));
+
+  for (i = FONT_FOUNDRY_INDEX, j = XLFD_FOUNDRY_INDEX; i <= 
FONT_REGISTRY_INDEX;
+       i++, j++)
+    {
+      if (i == FONT_ADSTYLE_INDEX)
+       j = XLFD_ADSTYLE_INDEX;
+      else if (i == FONT_REGISTRY_INDEX)
+       j = XLFD_REGISTRY_INDEX;
+      val = AREF (font, i);
+      if (NILP (val))
+       {
+         if (j == XLFD_REGISTRY_INDEX)
+           f[j] = "*-*";
+         else
+           f[j] = "*";
+       }
+      else
+       {
+         if (SYMBOLP (val))
+           val = SYMBOL_NAME (val);
+         if (j == XLFD_REGISTRY_INDEX
+             && ! strchr (SSDATA (val), '-'))
+           {
+             ptrdiff_t alloc = SBYTES (val) + 4;
+
+             /* Change "jisx0208*" and "jisx0208" to "jisx0208*-*".  */
+             f[j] = p = SAFE_ALLOCA (alloc);
+             sprintf (p, "%s%s-*", SDATA (val),
+                      &"*"[SDATA (val)[SBYTES (val) - 1] == '*']);
+           }
+         else
+           f[j] = SSDATA (val);
+       }
+    }
+
+  for (i = FONT_WEIGHT_INDEX, j = XLFD_WEIGHT_INDEX; i <= FONT_WIDTH_INDEX;
+       i++, j++)
+    {
+      val = font_style_symbolic (font, i, 0);
+      if (NILP (val))
+       f[j] = "*";
+      else
+       {
+         int c, k, l;
+         ptrdiff_t alloc;
+
+         val = SYMBOL_NAME (val);
+         alloc = SBYTES (val) + 1;
+         f[j] = p = SAFE_ALLOCA (alloc);
+         /* Copy the name while excluding '-', '?', ',', and '"'.  */
+         for (k = l = 0; k < alloc; k++)
+           {
+             c = SREF (val, k);
+             if (c != '-' && c != '?' && c != ',' && c != '"')
+               p[l++] = c;
+           }
+       }
+    }
+
+  val = AREF (font, FONT_SIZE_INDEX);
+  eassert (NUMBERP (val) || NILP (val));
+  char font_size_index_buf[sizeof "-*"
+                          + max (INT_STRLEN_BOUND (EMACS_INT),
+                                 1 + DBL_MAX_10_EXP + 1)];
+  if (INTEGERP (val))
+    {
+      intmax_t v;
+      if (! (integer_to_intmax (val, &v) && 0 < v))
+       v = pixel_size;
+      if (v > 0)
+       {
+         f[XLFD_PIXEL_INDEX] = p = font_size_index_buf;
+         sprintf (p, "%"PRIdMAX"-*", v);
+       }
+      else
+       f[XLFD_PIXEL_INDEX] = "*-*";
+    }
+  else if (FLOATP (val))
+    {
+      double v = XFLOAT_DATA (val) * 10;
+      f[XLFD_PIXEL_INDEX] = p = font_size_index_buf;
+      sprintf (p, "*-%.0f", v);
+    }
+  else
+    f[XLFD_PIXEL_INDEX] = "*-*";
+
+  char dpi_index_buf[sizeof "-" + 2 * INT_STRLEN_BOUND (EMACS_INT)];
+  if (FIXNUMP (AREF (font, FONT_DPI_INDEX)))
+    {
+      EMACS_INT v = XFIXNUM (AREF (font, FONT_DPI_INDEX));
+      f[XLFD_RESX_INDEX] = p = dpi_index_buf;
+      sprintf (p, "%"pI"d-%"pI"d", v, v);
+    }
+  else
+    f[XLFD_RESX_INDEX] = "*-*";
+
+  if (FIXNUMP (AREF (font, FONT_SPACING_INDEX)))
+    {
+      EMACS_INT spacing = XFIXNUM (AREF (font, FONT_SPACING_INDEX));
+
+      f[XLFD_SPACING_INDEX] = (spacing <= FONT_SPACING_PROPORTIONAL ? "p"
+                              : spacing <= FONT_SPACING_DUAL ? "d"
+                              : spacing <= FONT_SPACING_MONO ? "m"
+                              : "c");
+    }
+  else
+    f[XLFD_SPACING_INDEX] = "*";
+
+  char avgwidth_index_buf[INT_BUFSIZE_BOUND (EMACS_INT)];
+  if (FIXNUMP (AREF (font,  FONT_AVGWIDTH_INDEX)))
+    {
+      f[XLFD_AVGWIDTH_INDEX] = p = avgwidth_index_buf;
+      sprintf (p, "%"pI"d", XFIXNUM (AREF (font, FONT_AVGWIDTH_INDEX)));
+    }
+  else
+    f[XLFD_AVGWIDTH_INDEX] = "*";
+
+  /* Allocate a buffer large enough to accommodate the entire
+     XLFD.  */
+
+  name = xmalloc (strlen (f[XLFD_FOUNDRY_INDEX])
+                 + strlen (f[XLFD_FAMILY_INDEX])
+                 + strlen (f[XLFD_WEIGHT_INDEX])
+                 + strlen (f[XLFD_SLANT_INDEX])
+                 + strlen (f[XLFD_SWIDTH_INDEX])
+                 + strlen (f[XLFD_ADSTYLE_INDEX])
+                 + strlen (f[XLFD_PIXEL_INDEX])
+                 + strlen (f[XLFD_RESX_INDEX])
+                 + strlen (f[XLFD_SPACING_INDEX])
+                 + strlen (f[XLFD_AVGWIDTH_INDEX])
+                 + strlen (f[XLFD_REGISTRY_INDEX])
+                 + sizeof "-----------");
+
+  /* Return the XLFD.  */
+
+  sprintf (name, "-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s-%s",
+          f[XLFD_FOUNDRY_INDEX], f[XLFD_FAMILY_INDEX],
+          f[XLFD_WEIGHT_INDEX], f[XLFD_SLANT_INDEX],
+          f[XLFD_SWIDTH_INDEX], f[XLFD_ADSTYLE_INDEX],
+          f[XLFD_PIXEL_INDEX], f[XLFD_RESX_INDEX],
+          f[XLFD_SPACING_INDEX], f[XLFD_AVGWIDTH_INDEX],
+          f[XLFD_REGISTRY_INDEX]);
+  SAFE_FREE ();
+  return name;
+}
 
 /* Store XLFD name of FONT (font-spec or font-entity) in NAME (NBYTES
    length), and return the name length.  If FONT_SIZE_INDEX of FONT is
@@ -1878,7 +2047,7 @@ font_rescale_ratio (Lisp_Object font_entity)
          if (STRINGP (XCAR (elt)))
            {
              if (NILP (name))
-               name = Ffont_xlfd_name (font_entity, Qnil);
+               name = Ffont_xlfd_name (font_entity, Qnil, Qt);
 
              /* N.B. that `name' is set to nil if the resulting XLFD
                 is too long.  */
@@ -2490,12 +2659,30 @@ font_delete_unmatched (Lisp_Object vec, Lisp_Object 
spec, int size)
       entity = AREF (vec, i);
       if (! NILP (Vface_ignored_fonts))
        {
-         char name[256];
+         char name[256], *xlfd;
          ptrdiff_t namelen;
          namelen = font_unparse_xlfd (entity, 0, name, 256);
+
          if (namelen >= 0)
-            if (font_is_ignored (name, namelen))
+           {
+             if (font_is_ignored (name, namelen))
                 continue;
+           }
+         else
+           {
+             /* The font family or foundry is too long for a 256
+                character xlfd to accommodate.  */
+
+             xlfd = font_dynamic_unparse_xlfd (entity, 0);
+
+             if (xlfd && font_is_ignored (xlfd, sizeof (xlfd)))
+               {
+                 xfree (xlfd);
+                 continue;
+               }
+
+             xfree (xlfd);
+           }
        }
       if (NILP (spec))
        {
@@ -4239,16 +4426,20 @@ Optional 2nd argument FRAME, if non-nil, specifies the 
target frame.  */)
   return val;
 }
 
-DEFUN ("font-xlfd-name", Ffont_xlfd_name, Sfont_xlfd_name, 1, 2, 0,
+DEFUN ("font-xlfd-name", Ffont_xlfd_name, Sfont_xlfd_name, 1, 3, 0,
        doc: /*  Return XLFD name of FONT.
 FONT is a font-spec, font-entity, or font-object.
-If the name is too long for XLFD (maximum 255 chars), return nil.
+
+If the name is too long to be represented as an XLFD (maximum 255
+chars) and LONG_XLFDS is nil, return nil.
+
 If the 2nd optional arg FOLD-WILDCARDS is non-nil,
 the consecutive wildcards are folded into one.  */)
-  (Lisp_Object font, Lisp_Object fold_wildcards)
+  (Lisp_Object font, Lisp_Object fold_wildcards, Lisp_Object long_xlfds)
 {
-  char name[256];
+  char name_buffer[256], *name;
   int namelen, pixel_size = 0;
+  Lisp_Object string;
 
   CHECK_FONT (font);
 
@@ -4261,15 +4452,32 @@ the consecutive wildcards are folded into one.  */)
        {
          if (NILP (fold_wildcards))
            return font_name;
+         name = name_buffer;
          lispstpcpy (name, font_name);
          namelen = SBYTES (font_name);
          goto done;
        }
       pixel_size = XFONT_OBJECT (font)->pixel_size;
     }
-  namelen = font_unparse_xlfd (font, pixel_size, name, 256);
-  if (namelen < 0)
-    return Qnil;
+
+  if (NILP (long_xlfds))
+    {
+      name = name_buffer;
+      namelen = font_unparse_xlfd (font, pixel_size, name, 256);
+      if (namelen < 0)
+       return Qnil;
+    }
+  else
+    {
+      /* Dynamically allocate the XLFD.  */
+      name = font_dynamic_unparse_xlfd (font, pixel_size);
+
+      if (!name)
+       return Qnil;
+
+      namelen = strlen (name);
+    }
+
  done:
   if (! NILP (fold_wildcards))
     {
@@ -4283,7 +4491,14 @@ the consecutive wildcards are folded into one.  */)
        }
     }
 
-  return make_string (name, namelen);
+  /* If NAME is dynamically allocated, free it.  */
+
+  string = make_string (name, namelen);
+
+  if (name != name_buffer)
+    xfree (name);
+
+  return string;
 }
 
 void
@@ -5487,7 +5702,7 @@ font_add_log (const char *action, Lisp_Object arg, 
Lisp_Object result)
       Lisp_Object tail, elt;
       AUTO_STRING (equal, "=");
 
-      val = Ffont_xlfd_name (arg, Qt);
+      val = Ffont_xlfd_name (arg, Qt, Qt);
       for (tail = AREF (arg, FONT_EXTRA_INDEX); CONSP (tail);
           tail = XCDR (tail))
        {
@@ -5515,7 +5730,7 @@ font_add_log (const char *action, Lisp_Object arg, 
Lisp_Object result)
     result = font_vconcat_entity_vectors (result);
   if (FONTP (result))
     {
-      val = Ffont_xlfd_name (result, Qt);
+      val = Ffont_xlfd_name (result, Qt, Qt);
       if (! FONT_SPEC_P (result))
        {
          AUTO_STRING (colon, ":");
@@ -5532,7 +5747,7 @@ font_add_log (const char *action, Lisp_Object arg, 
Lisp_Object result)
        {
          val = XCAR (tail);
          if (FONTP (val))
-           val = Ffont_xlfd_name (val, Qt);
+           val = Ffont_xlfd_name (val, Qt, Qt);
          XSETCAR (tail, val);
        }
     }
@@ -5543,7 +5758,7 @@ font_add_log (const char *action, Lisp_Object arg, 
Lisp_Object result)
        {
          val = AREF (result, i);
          if (FONTP (val))
-           val = Ffont_xlfd_name (val, Qt);
+           val = Ffont_xlfd_name (val, Qt, Qt);
          ASET (result, i, val);
        }
     }
diff --git a/src/font.h b/src/font.h
index ed3b17db994..9c9f7952560 100644
--- a/src/font.h
+++ b/src/font.h
@@ -303,10 +303,16 @@ struct font
      SPACE glyph, the value is 0.  */
   int space_width;
 
-  /* Average width of glyphs in the font.  If the font itself doesn't
-     have that information, but has glyphs of ASCII characters, the
-     value is the average width of those glyphs.  Otherwise, the value
-     is 0.  */
+  /* Average width of glyphs in the font.  Should be the average width
+     of the glyphs of ASCII characters.  The value for the default
+     face's font is used to determine the canonical character width of
+     the frame (see FRAME_COLUMN_WIDTH).  For fonts that are not
+     fixed-pitch, the font backend should actually calculate the value
+     from the glyphs of ASCII characters in the range 32..126
+     inclusively; relying on the average-width attribute recorded in
+     the font is unreliable in this case, especially in fonts that
+     support CJK scripts, where many characters are wide.  Value can
+     be zero if the font doesn't have glyphs for ASCII characters.  */
   int average_width;
 
   /* Ascent and descent of the font (in pixels).  */
@@ -885,8 +891,8 @@ extern void font_parse_family_registry (Lisp_Object family,
                                         Lisp_Object spec);
 
 extern int font_parse_xlfd (char *name, ptrdiff_t len, Lisp_Object font);
-extern ptrdiff_t font_unparse_xlfd (Lisp_Object font, int pixel_size,
-                                   char *name, int bytes);
+extern char *font_dynamic_unparse_xlfd (Lisp_Object, int);
+extern ptrdiff_t font_unparse_xlfd (Lisp_Object, int, char *, int);
 extern void register_font_driver (struct font_driver const *, struct frame *);
 extern void free_font_driver_list (struct frame *f);
 #ifdef ENABLE_CHECKING
diff --git a/src/fontset.c b/src/fontset.c
index 139dcb6eb89..54d1347f54f 100644
--- a/src/fontset.c
+++ b/src/fontset.c
@@ -1546,7 +1546,7 @@ overwrites the previous settings.  */)
 
       font_parse_family_registry (XCAR (font_spec), XCDR (font_spec), spec);
       font_spec = spec;
-      fontname = Ffont_xlfd_name (font_spec, Qnil);
+      fontname = Ffont_xlfd_name (font_spec, Qnil, Qt);
     }
   else if (STRINGP (font_spec))
     {
@@ -1554,7 +1554,7 @@ overwrites the previous settings.  */)
       font_spec = CALLN (Ffont_spec, QCname, fontname);
     }
   else if (FONT_SPEC_P (font_spec))
-    fontname = Ffont_xlfd_name (font_spec, Qnil);
+    fontname = Ffont_xlfd_name (font_spec, Qnil, Qt);
   else if (! NILP (font_spec))
     Fsignal (Qfont, list2 (build_string ("Invalid font-spec"), font_spec));
 
@@ -1740,6 +1740,7 @@ FONT-SPEC is a vector, a cons, or a string.  See the 
documentation of
 {
   Lisp_Object fontset, tail;
   int id;
+  char *string;
 
   CHECK_STRING (name);
 
@@ -1749,8 +1750,6 @@ FONT-SPEC is a vector, a cons, or a string.  See the 
documentation of
     {
       Lisp_Object font_spec = Ffont_spec (0, NULL);
       Lisp_Object short_name;
-      char xlfd[256];
-      int len;
 
       if (font_parse_xlfd (SSDATA (name), SBYTES (name), font_spec) < 0)
        error ("Fontset name must be in XLFD format");
@@ -1762,10 +1761,11 @@ FONT-SPEC is a vector, a cons, or a string.  See the 
documentation of
                                    Vfontset_alias_alist);
       ASET (font_spec, FONT_REGISTRY_INDEX, Qiso8859_1);
       fontset = make_fontset (Qnil, name, Qnil);
-      len = font_unparse_xlfd (font_spec, 0, xlfd, 256);
-      if (len < 0)
+      string = font_dynamic_unparse_xlfd (font_spec, 0);
+      if (!string)
        error ("Invalid fontset name (perhaps too long): %s", SDATA (name));
-      set_fontset_ascii (fontset, make_unibyte_string (xlfd, len));
+      set_fontset_ascii (fontset, build_unibyte_string (string));
+      xfree (string);
     }
   else
     {
@@ -1816,7 +1816,7 @@ fontset_from_font (Lisp_Object font_object)
   Lisp_Object font_spec = copy_font_spec (font_object);
   Lisp_Object registry = AREF (font_spec, FONT_REGISTRY_INDEX);
   Lisp_Object fontset_spec, alias, name, fontset;
-  Lisp_Object val;
+  Lisp_Object val, xlfd;
 
   val = assoc_no_quit (font_spec, auto_fontset_alist);
   if (CONSP (val))
@@ -1832,14 +1832,19 @@ fontset_from_font (Lisp_Object font_object)
     }
   fontset_spec = copy_font_spec (font_spec);
   ASET (fontset_spec, FONT_REGISTRY_INDEX, alias);
-  name = Ffont_xlfd_name (fontset_spec, Qnil);
+  name = Ffont_xlfd_name (fontset_spec, Qnil, Qt);
   eassert (!NILP (name));
   fontset = make_fontset (Qnil, name, Qnil);
   Vfontset_alias_alist = Fcons (Fcons (name, SYMBOL_NAME (alias)),
                                Vfontset_alias_alist);
-  alias = Fdowncase (AREF (font_object, FONT_NAME_INDEX));
-  Vfontset_alias_alist = Fcons (Fcons (name, alias), Vfontset_alias_alist);
-  auto_fontset_alist = Fcons (Fcons (font_spec, fontset), auto_fontset_alist);
+
+  xlfd = AREF (font_object, FONT_NAME_INDEX);
+  alias = Fdowncase (xlfd);
+  Vfontset_alias_alist
+    = Fcons (Fcons (name, alias), Vfontset_alias_alist);
+  auto_fontset_alist
+    = Fcons (Fcons (font_spec, fontset), auto_fontset_alist);
+
   font_spec = Ffont_spec (0, NULL);
   ASET (font_spec, FONT_REGISTRY_INDEX, registry);
   {
@@ -2006,7 +2011,7 @@ format is the same as above.  */)
              for (; CONSP (alist); alist = XCDR (alist))
                {
                  elt = XCAR (alist);
-                 XSETCAR (elt, Ffont_xlfd_name (XCAR (elt), Qnil));
+                 XSETCAR (elt, Ffont_xlfd_name (XCAR (elt), Qnil, Qt));
                }
            }
          c = to + 1;
diff --git a/src/gtkutil.c b/src/gtkutil.c
index 22b2a70f279..f9d9a22a06b 100644
--- a/src/gtkutil.c
+++ b/src/gtkutil.c
@@ -4141,7 +4141,7 @@ xg_update_frame_menubar (struct frame *f)
   g_signal_connect (x->menubar_widget, "map", G_CALLBACK (menubar_map_cb), f);
   gtk_widget_show_all (x->menubar_widget);
   gtk_widget_get_preferred_size (x->menubar_widget, NULL, &req);
-  req.height *= xg_get_scale (f);
+  req.height *= scale;
 
 #if !defined HAVE_PGTK && defined HAVE_GTK3
   if (FRAME_DISPLAY_INFO (f)->n_planes == 32)
@@ -4154,9 +4154,9 @@ xg_update_frame_menubar (struct frame *f)
     }
 #endif
 
-  if (FRAME_MENUBAR_HEIGHT (f) != (req.height * scale))
+  if (FRAME_MENUBAR_HEIGHT (f) != req.height)
     {
-      FRAME_MENUBAR_HEIGHT (f) = req.height * scale;
+      FRAME_MENUBAR_HEIGHT (f) = req.height;
       adjust_frame_size (f, -1, -1, 2, 0, Qmenu_bar_lines);
     }
   unblock_input ();
diff --git a/src/haiku_support.cc b/src/haiku_support.cc
index 12a84687180..3fc90385af3 100644
--- a/src/haiku_support.cc
+++ b/src/haiku_support.cc
@@ -741,8 +741,6 @@ public:
   EmacsWindow *parent;
   BRect pre_fullscreen_rect;
   BRect pre_zoom_rect;
-  int x_before_zoom;
-  int y_before_zoom;
   bool shown_flag;
   volatile bool was_shown_p;
   bool menu_bar_active_p;
@@ -760,8 +758,6 @@ public:
                            B_NORMAL_WINDOW_FEEL, 
B_NO_SERVER_SIDE_WINDOW_MODIFIERS),
                   subset_windows (NULL),
                   parent (NULL),
-                  x_before_zoom (INT_MIN),
-                  y_before_zoom (INT_MIN),
                   shown_flag (false),
                   was_shown_p (false),
                   menu_bar_active_p (false),
diff --git a/src/haikufont.c b/src/haikufont.c
index b6a9cb34c4d..c67502d3600 100644
--- a/src/haikufont.c
+++ b/src/haikufont.c
@@ -873,7 +873,8 @@ haikufont_open (struct frame *f, Lisp_Object font_entity, 
int pixel_size)
   font->baseline_offset = 0;
   font->relative_compose = 0;
 
-  font->props[FONT_NAME_INDEX] = Ffont_xlfd_name (font_object, Qnil);
+  font->props[FONT_NAME_INDEX]
+    = Ffont_xlfd_name (font_object, Qnil, Qt);
 
   unblock_input ();
   return font_object;
diff --git a/src/image.c b/src/image.c
index a4b8d21cee6..84db9bfb3b8 100644
--- a/src/image.c
+++ b/src/image.c
@@ -12075,6 +12075,18 @@ svg_load_image (struct frame *f, struct image *img, 
char *contents,
         img->background_valid = 1;
       }
 
+#if HAVE_NTGUI
+    /* Windows stores the image colours in BGR format, and SVG expects
+       them in RGB.  */
+    foreground = (foreground & 0x0000FF) << 16
+      | (foreground & 0xFF0000) >> 16
+      | (foreground & 0x00FF00);
+
+    background = (background & 0x0000FF) << 16
+      | (background & 0xFF0000) >> 16
+      | (background & 0x00FF00);
+#endif
+
     wrapped_contents = xmalloc (buffer_size);
 
     if (buffer_size <= snprintf (wrapped_contents, buffer_size, wrapper,
diff --git a/src/itree.c b/src/itree.c
index ecf7d67422a..259cc99d3af 100644
--- a/src/itree.c
+++ b/src/itree.c
@@ -278,7 +278,7 @@ check_subtree (struct itree_node *node,
 
    This runs in constant time when ENABLE_OVERLAY_CHECKING is 0
    (i.e. Emacs is not configured with
-   "--enable_checking=yes,overlays").  In this mode it can't check all
+   "--enable-checking=yes,overlays").  In this mode it can't check all
    the invariants.  When ENABLE_OVERLAY_CHECKING is 1 it checks the
    entire tree and validates all invariants.
 */
diff --git a/src/keyboard.c b/src/keyboard.c
index 6ab1cdebc12..f756f163e87 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -4989,6 +4989,10 @@ static const char *const lispy_accent_keys[] =
 #ifdef HAVE_ANDROID
 #define FUNCTION_KEY_OFFSET 0
 
+/* Mind that Android designates 23 KEYCODE_DPAD_CENTER, but it is
+   merely abstruse terminology for the ``select'' key frequently
+   located in certain physical keyboards.  */
+
 const char *const lispy_function_keys[] =
   {
     /* All elements in this array default to 0, except for the few
@@ -5025,6 +5029,7 @@ const char *const lispy_function_keys[] =
     [218] = "kana",
     [21]  = "left",
     [22]  = "right",
+    [23]  = "select",
     [24]  = "volume-up",
     [259] = "help",
     [25]  = "volume-down",
diff --git a/src/keymap.c b/src/keymap.c
index 1f863885003..d710bae02e0 100644
--- a/src/keymap.c
+++ b/src/keymap.c
@@ -2885,7 +2885,7 @@ You type        Translation\n\
     {
       Lisp_Object msg = build_unibyte_string ("Key translations");
       CALLN (Ffuncall,
-            Qdescribe_map_tree,
+            Qhelp__describe_map_tree,
             Vkey_translation_map, Qnil, Qnil, prefix,
             msg, nomenu, Qt, Qnil, Qnil, buffer);
     }
@@ -2899,7 +2899,7 @@ You type        Translation\n\
     {
       Lisp_Object msg = build_unibyte_string ("\f\nOverriding Bindings");
       CALLN (Ffuncall,
-            Qdescribe_map_tree,
+            Qhelp__describe_map_tree,
             start1, Qt, shadow, prefix,
             msg, nomenu, Qnil, Qnil, Qnil, buffer);
       shadow = Fcons (start1, shadow);
@@ -2912,7 +2912,7 @@ You type        Translation\n\
     {
       Lisp_Object msg = build_unibyte_string ("\f\nOverriding Bindings");
       CALLN (Ffuncall,
-            Qdescribe_map_tree,
+            Qhelp__describe_map_tree,
             start1, Qt, shadow, prefix,
             msg, nomenu, Qnil, Qnil, Qnil, buffer);
       shadow = Fcons (start1, shadow);
@@ -2935,7 +2935,7 @@ You type        Translation\n\
        {
          Lisp_Object msg = build_unibyte_string ("\f\n`keymap' Property 
Bindings");
          CALLN (Ffuncall,
-                Qdescribe_map_tree,
+                Qhelp__describe_map_tree,
                 start1, Qt, shadow, prefix,
                 msg, nomenu, Qnil, Qnil, Qnil, buffer);
          shadow = Fcons (start1, shadow);
@@ -2946,7 +2946,7 @@ You type        Translation\n\
        {
          /* The title for a minor mode keymap
             is constructed at run time.
-            We let describe-map-tree do the actual insertion
+            We let `help--describe-map-tree' do the actual insertion
             because it takes care of other features when doing so.  */
          char *title, *p;
 
@@ -2968,7 +2968,7 @@ You type        Translation\n\
 
          Lisp_Object msg = build_unibyte_string (title);
          CALLN (Ffuncall,
-                Qdescribe_map_tree,
+                Qhelp__describe_map_tree,
                 maps[i], Qt, shadow, prefix,
                 msg, nomenu, Qnil, Qnil, Qnil, buffer);
          shadow = Fcons (maps[i], shadow);
@@ -2986,7 +2986,7 @@ You type        Translation\n\
                       build_unibyte_string ("\f\n`%s' Major Mode Bindings"),
                       XBUFFER (buffer)->major_mode_);
              CALLN (Ffuncall,
-                    Qdescribe_map_tree,
+                    Qhelp__describe_map_tree,
                     start1, Qt, shadow, prefix,
                     msg, nomenu, Qnil, Qnil, Qnil, buffer);
            }
@@ -2994,7 +2994,7 @@ You type        Translation\n\
            {
              Lisp_Object msg = build_unibyte_string ("\f\n`local-map' Property 
Bindings");
              CALLN (Ffuncall,
-                    Qdescribe_map_tree,
+                    Qhelp__describe_map_tree,
                     start1, Qt, shadow, prefix,
                     msg, nomenu, Qnil, Qnil, Qnil, buffer);
            }
@@ -3005,7 +3005,7 @@ You type        Translation\n\
 
   Lisp_Object msg = build_unibyte_string ("\f\nGlobal Bindings");
   CALLN (Ffuncall,
-        Qdescribe_map_tree,
+        Qhelp__describe_map_tree,
         current_global_map, Qt, shadow, prefix,
         msg, nomenu, Qnil, Qt, Qnil, buffer);
 
@@ -3014,7 +3014,7 @@ You type        Translation\n\
     {
       Lisp_Object msg = build_unibyte_string ("\f\nFunction key map 
translations");
       CALLN (Ffuncall,
-            Qdescribe_map_tree,
+            Qhelp__describe_map_tree,
             KVAR (current_kboard, Vlocal_function_key_map), Qnil, Qnil, prefix,
             msg, nomenu, Qt, Qnil, Qnil, buffer);
     }
@@ -3024,7 +3024,7 @@ You type        Translation\n\
     {
       Lisp_Object msg = build_unibyte_string ("\f\nInput decoding map 
translations");
       CALLN (Ffuncall,
-            Qdescribe_map_tree,
+            Qhelp__describe_map_tree,
             KVAR (current_kboard, Vinput_decode_map), Qnil, Qnil, prefix,
             msg, nomenu, Qt, Qnil, Qnil, buffer);
     }
@@ -3341,7 +3341,7 @@ void
 syms_of_keymap (void)
 {
   DEFSYM (Qkeymap, "keymap");
-  DEFSYM (Qdescribe_map_tree, "describe-map-tree");
+  DEFSYM (Qhelp__describe_map_tree, "help--describe-map-tree");
 
   DEFSYM (Qkeymap_canonicalize, "keymap-canonicalize");
 
diff --git a/src/lisp.h b/src/lisp.h
index 6b8b5c8910f..fb9800732d9 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -808,10 +808,11 @@ INLINE void
 }
 
 /* Extract A's pointer value, assuming A's Lisp type is TYPE and the
-   extracted pointer's type is CTYPE *.  */
-
-#define XUNTAG(a, type, ctype) ((ctype *) \
-                               ((char *) XLP (a) - LISP_WORD_TAG (type)))
+   extracted pointer's type is CTYPE *.  When !USE_LSB_TAG this simply
+   extracts A's low-order bits, as (uintptr_t) LISP_WORD_TAG (type) is
+   always zero then.  */
+#define XUNTAG(a, type, ctype) \
+  ((ctype *) ((uintptr_t) XLP (a) - (uintptr_t) LISP_WORD_TAG (type)))
 
 /* A forwarding pointer to a value.  It uses a generic pointer to
    avoid alignment bugs that could occur if it used a pointer to a
@@ -918,20 +919,11 @@ verify (GCALIGNED (struct Lisp_Symbol));
 #define DEFUN_ARGS_8   (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, \
                         Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object)
 
-/* untagged_ptr represents a pointer before tagging, and Lisp_Word_tag
-   contains a possibly-shifted tag to be added to an untagged_ptr to
-   convert it to a Lisp_Word.  */
+/* Lisp_Word_tag is big enough for a possibly-shifted tag, to be
+   added to a pointer value for conversion to a Lisp_Word.  */
 #if LISP_WORDS_ARE_POINTERS
-/* untagged_ptr is a pointer so that the compiler knows that TAG_PTR
-   yields a pointer.  It is char * so that adding a tag uses simple
-   machine addition.  */
-typedef char *untagged_ptr;
 typedef uintptr_t Lisp_Word_tag;
 #else
-/* untagged_ptr is an unsigned integer instead of a pointer, so that
-   it can be added to the possibly-wider Lisp_Word_tag type without
-   losing information.  */
-typedef uintptr_t untagged_ptr;
 typedef EMACS_UINT Lisp_Word_tag;
 #endif
 
@@ -941,7 +933,7 @@ typedef EMACS_UINT Lisp_Word_tag;
 
 /* An initializer for a Lisp_Object that contains TAG along with PTR.  */
 #define TAG_PTR(tag, ptr) \
-  LISP_INITIALLY ((Lisp_Word) ((untagged_ptr) (ptr) + LISP_WORD_TAG (tag)))
+  LISP_INITIALLY ((Lisp_Word) ((uintptr_t) (ptr) + LISP_WORD_TAG (tag)))
 
 /* LISPSYM_INITIALLY (Qfoo) is equivalent to Qfoo except it is
    designed for use as an initializer, even for a constant initializer.  */
@@ -994,25 +986,35 @@ typedef EMACS_UINT Lisp_Word_tag;
    number of members has been reduced to one.  */
 union vectorlike_header
   {
-    /* The main member contains various pieces of information:
-       - The MSB (ARRAY_MARK_FLAG) holds the gcmarkbit.
-       - The next bit (PSEUDOVECTOR_FLAG) indicates whether this is a plain
-         vector (0) or a pseudovector (1).
-       - If PSEUDOVECTOR_FLAG is 0, the rest holds the size (number
-         of slots) of the vector.
-       - If PSEUDOVECTOR_FLAG is 1, the rest is subdivided into three fields:
-        - a) pseudovector subtype held in PVEC_TYPE_MASK field;
-        - b) number of Lisp_Objects slots at the beginning of the object
-          held in PSEUDOVECTOR_SIZE_MASK field.  These objects are always
-          traced by the GC;
-        - c) size of the rest fields held in PSEUDOVECTOR_REST_MASK and
-          measured in word_size units.  Rest fields may also include
-          Lisp_Objects, but these objects usually needs some special treatment
-          during GC.
-        There are some exceptions.  For PVEC_FREE, b) is always zero.  For
-        PVEC_BOOL_VECTOR and PVEC_SUBR, both b) and c) are always zero.
-        Current layout limits the pseudovectors to 63 PVEC_xxx subtypes,
-        4095 Lisp_Objects in GC-ed area and 4095 word-sized other slots.  */
+    /* The `size' header word, W bits wide, has one of two forms
+       discriminated by the second-highest bit (PSEUDOVECTOR_FLAG):
+
+         1   1                    W-2
+       +---+---+-------------------------------------+
+       | M | 0 |                 SIZE                |  vector
+       +---+---+-------------------------------------+
+
+         1   1    W-32      6       12         12
+       +---+---+--------+------+----------+----------+
+       | M | 1 | unused | TYPE | RESTSIZE | LISPSIZE |  pseudovector
+       +---+---+--------+------+----------+----------+
+
+       M (ARRAY_MARK_FLAG) holds the GC mark bit.
+
+       SIZE     is the length (number of slots) of a regular Lisp vector,
+                and the object layout is struct Lisp_Vector.
+
+       TYPE     is the pseudovector subtype (enum pvec_type).
+
+       LISPSIZE is the number of Lisp_Object fields at the beginning of the
+                object (after the header).  These are always traced by the GC.
+
+       RESTSIZE is the number of fields (in word_size units) following.
+                These are not automatically traced by the GC.
+                For PVEC_BOOL and statically allocated PVEC_SUBR, RESTSIZE is 
0.
+                (The block size for PVEC_BOOL is computed from its own size
+                field, to avoid being restricted by the 12-bit RESTSIZE field.)
+    */
     ptrdiff_t size;
   };
 
@@ -1076,7 +1078,8 @@ enum pvec_type
   PVEC_CHAR_TABLE,
   PVEC_SUB_CHAR_TABLE,
   PVEC_RECORD,
-  PVEC_FONT /* Should be last because it's used for range checking.  */
+  PVEC_FONT,
+  PVEC_TAG_MAX = PVEC_FONT  /* Keep this equal to the highest member.  */
 };
 
 enum More_Lisp_Bits
diff --git a/src/nsfns.m b/src/nsfns.m
index a79892f73b6..082e06698b2 100644
--- a/src/nsfns.m
+++ b/src/nsfns.m
@@ -799,6 +799,26 @@ ns_set_child_frame_border_width (struct frame *f, 
Lisp_Object arg,
     }
 }
 
+static void
+ns_set_inhibit_double_buffering (struct frame *f,
+                                 Lisp_Object new_value,
+                                 Lisp_Object old_value)
+{
+#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MIN_REQUIRED >= 101400
+  if (!EQ (new_value, old_value))
+    {
+      FRAME_DOUBLE_BUFFERED (f) = NILP (new_value);
+
+      /* If the view or layer haven't been created yet this will be a
+         noop.  */
+      [(EmacsLayer *)[FRAME_NS_VIEW (f) layer]
+          setDoubleBuffered:FRAME_DOUBLE_BUFFERED (f)];
+
+      SET_FRAME_GARBAGED (f);
+    }
+#endif
+}
+
 static void
 ns_set_internal_border_width (struct frame *f, Lisp_Object arg, Lisp_Object 
oldval)
 {
@@ -1073,7 +1093,7 @@ frame_parm_handler ns_frame_parm_handlers[] =
   gui_set_alpha,
   0, /* x_set_sticky */
   ns_set_tool_bar_position,
-  0, /* x_set_inhibit_double_buffering */
+  ns_set_inhibit_double_buffering,
   ns_set_undecorated,
   ns_set_parent_frame,
   0, /* x_set_skip_taskbar */
@@ -1461,6 +1481,14 @@ DEFUN ("x-create-frame", Fx_create_frame, 
Sx_create_frame,
   gui_default_parameter (f, parms, Qtitle, Qnil, "title", "Title",
                          RES_TYPE_STRING);
 
+#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MIN_REQUIRED >= 101400
+  tem = gui_display_get_arg (dpyinfo, parms, Qinhibit_double_buffering, NULL, 
NULL,
+                             RES_TYPE_BOOLEAN);
+  FRAME_DOUBLE_BUFFERED (f) = NILP (tem) || EQ (tem, Qunbound);
+  store_frame_param (f, Qinhibit_double_buffering,
+                     FRAME_DOUBLE_BUFFERED (f) ? Qnil : Qt);
+#endif
+
   parms = get_geometry_from_preferences (dpyinfo, parms);
   window_prompting = gui_figure_window_size (f, parms, false, true);
 
diff --git a/src/nsmenu.m b/src/nsmenu.m
index 2c1f575bdf2..4a86864176d 100644
--- a/src/nsmenu.m
+++ b/src/nsmenu.m
@@ -477,6 +477,14 @@ set_frame_menubar (struct frame *f, bool deep_p)
    call to ns_update_menubar.  */
 - (void)menuNeedsUpdate: (NSMenu *)menu
 {
+
+  /* The context menu is built and then displayed, as opposed to the
+     top-menu, which is partially built and then updated and filled in
+     when it's time to display it.  Therefore, we don't call
+     ns_update_menubar if a context menu is active. */
+  if (context_menu_value != 0)
+    return;
+
 #ifdef NS_IMPL_GNUSTEP
   static int inside = 0;
 #endif
diff --git a/src/nsterm.h b/src/nsterm.h
index b6e5a813a6d..cb162039ad8 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -746,9 +746,11 @@ enum ns_return_frame_mode
   CGColorSpaceRef colorSpace;
   IOSurfaceRef currentSurface;
   CGContextRef context;
+  bool doubleBuffered;
 }
-- (id) initWithColorSpace: (CGColorSpaceRef)cs;
+- (id) initWithDoubleBuffered: (bool)db;
 - (void) setColorSpace: (CGColorSpaceRef)cs;
+- (void) setDoubleBuffered: (bool)db;
 - (CGContextRef) getContext;
 @end
 #endif
@@ -996,6 +998,11 @@ struct ns_output
   /* Non-zero if we are doing an animation, e.g. toggling the tool bar.  */
   int in_animation;
 
+#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MIN_REQUIRED >= 101400
+  /* Is the frame double buffered?  */
+  bool double_buffered;
+#endif
+
 #ifdef NS_IMPL_GNUSTEP
   /* Zero if this is the first time a toolbar has been updated on this
      frame. */
@@ -1030,6 +1037,10 @@ struct x_output
 
 #define FRAME_FONT(f) ((f)->output_data.ns->font)
 
+#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MIN_REQUIRED >= 101400
+#define FRAME_DOUBLE_BUFFERED(f) ((f)->output_data.ns->double_buffered)
+#endif
+
 #ifdef __OBJC__
 #define XNS_SCROLL_BAR(vec) ((id) xmint_pointer (vec))
 #else
diff --git a/src/nsterm.m b/src/nsterm.m
index 78089906752..4e0dfa58c63 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -2704,12 +2704,11 @@ ns_scroll_run (struct window *w, struct run *run)
   {
     NSRect srcRect = NSMakeRect (x, from_y, width, height);
     NSPoint dest = NSMakePoint (x, to_y);
-    NSRect destRect = NSMakeRect (x, from_y, width, height);
     EmacsView *view = FRAME_NS_VIEW (f);
 
     [view copyRect:srcRect to:dest];
-#ifdef NS_IMPL_COCOA
-    [view setNeedsDisplayInRect:destRect];
+#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED < 101400
+    [view setNeedsDisplayInRect:srcRect];
 #endif
   }
 
@@ -4561,21 +4560,6 @@ ns_send_appdefined (int value)
   /* Only post this event if we haven't already posted one.  This will end
      the [NXApp run] main loop after having processed all events queued at
      this moment.  */
-
-#ifdef NS_IMPL_COCOA
-  if (! send_appdefined)
-    {
-      /* OS X 10.10.1 swallows the AppDefined event we are sending ourselves
-         in certain situations (rapid incoming events).
-         So check if we have one, if not add one.  */
-      NSEvent *appev = [NSApp 
nextEventMatchingMask:NSEventMaskApplicationDefined
-                                          untilDate:[NSDate distantPast]
-                                             inMode:NSDefaultRunLoopMode
-                                            dequeue:NO];
-      if (! appev) send_appdefined = YES;
-    }
-#endif
-
   if (send_appdefined)
     {
       NSEvent *nxev;
@@ -7922,8 +7906,6 @@ ns_in_echo_area (void)
   maximizing_resize = NO;
 #endif
 
-  [[EmacsWindow alloc] initWithEmacsFrame:f];
-
 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MIN_REQUIRED >= 101400
   /* These settings mean AppKit will retain the contents of the frame
      on resize.  Unfortunately it also means the frame will not be
@@ -7934,9 +7916,16 @@ ns_in_echo_area (void)
           NSViewLayerContentsRedrawOnSetNeedsDisplay];
   [self setLayerContentsPlacement:NSViewLayerContentsPlacementTopLeft];
 
-  /* initWithEmacsFrame can't create the toolbar before the layer is
-     set, so have another go at creating the toolbar here.  */
-  [(EmacsWindow*)[self window] createToolbar:f];
+  [[EmacsWindow alloc] initWithEmacsFrame:f];
+
+  /* Now the NSWindow has been created, we can finish up configuring
+     the layer.  */
+  [(EmacsLayer *)[self layer] setColorSpace:
+                   [[[self window] colorSpace] CGColorSpace]];
+  [(EmacsLayer *)[self layer] setContentsScale:
+                   [[self window] backingScaleFactor]];
+#else
+  [[EmacsWindow alloc] initWithEmacsFrame:f];
 #endif
 
   if (ns_drag_types)
@@ -8607,9 +8596,9 @@ ns_in_echo_area (void)
 - (CALayer *)makeBackingLayer
 {
   EmacsLayer *l = [[EmacsLayer alloc]
-                    initWithColorSpace:[[[self window] colorSpace] 
CGColorSpace]];
+                    initWithDoubleBuffered:FRAME_DOUBLE_BUFFERED (emacsframe)];
+
   [l setDelegate:(id)self];
-  [l setContentsScale:[[self window] backingScaleFactor]];
 
   return l;
 }
@@ -8664,8 +8653,10 @@ ns_in_echo_area (void)
                                NSHeight (srcRect));
 
 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MIN_REQUIRED >= 101400
-  double scale = [[self window] backingScaleFactor];
   CGContextRef context = [(EmacsLayer *)[self layer] getContext];
+  CGContextFlush (context);
+
+  double scale = [[self window] backingScaleFactor];
   int bpp = CGBitmapContextGetBitsPerPixel (context) / 8;
   void *pixels = CGBitmapContextGetData (context);
   int rowSize = CGBitmapContextGetBytesPerRow (context);
@@ -10435,22 +10426,19 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c)
    cache.  If no free surfaces are found in the cache then a new one
    is created.  */
 
-#define CACHE_MAX_SIZE 2
-
-- (id) initWithColorSpace: (CGColorSpaceRef)cs
+- (id) initWithDoubleBuffered: (bool)db
 {
-  NSTRACE ("[EmacsLayer initWithColorSpace:]");
+  NSTRACE ("[EmacsLayer initWithDoubleBuffered:]");
 
   self = [super init];
   if (self)
     {
-      cache = [[NSMutableArray arrayWithCapacity:CACHE_MAX_SIZE] retain];
-      [self setColorSpace:cs];
+      [self setColorSpace:nil];
+      [self setDoubleBuffered:db];
+      cache = [[NSMutableArray arrayWithCapacity:(doubleBuffered ? 2 : 1)] 
retain];
     }
   else
-    {
-      return nil;
-    }
+    return nil;
 
   return self;
 }
@@ -10467,6 +10455,15 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c)
 }
 
 
+- (void) setDoubleBuffered: (bool)db
+{
+  if (doubleBuffered != db)
+    [self releaseSurfaces];
+
+  doubleBuffered = db;
+}
+
+
 - (void) dealloc
 {
   [self releaseSurfaces];
@@ -10538,7 +10535,7 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c)
             }
         }
 
-      if (!surface && [cache count] >= CACHE_MAX_SIZE)
+      if (!surface && [cache count] >= (doubleBuffered ? 2 : 1))
         {
           /* Just grab the first one off the cache.  This may result
              in tearing effects.  The alternative is to wait for one
@@ -10591,7 +10588,7 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c)
           return nil;
         }
 
-      CGContextTranslateCTM(context, 0, IOSurfaceGetHeight (currentSurface));
+      CGContextTranslateCTM(context, 0, IOSurfaceGetHeight (surface));
       CGContextScaleCTM(context, scale, -scale);
     }
 
@@ -10608,6 +10605,7 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c)
   if (!context)
     return;
 
+  CGContextFlush (context);
   CGContextRelease (context);
   context = NULL;
 
@@ -10621,26 +10619,18 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c)
 {
   NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "[EmacsLayer display]");
 
-  if (context)
+  if (context && context != [[NSGraphicsContext currentContext] CGContext])
     {
       [self releaseContext];
 
-#if CACHE_MAX_SIZE == 1
-      /* This forces the layer to see the surface as updated.  */
+      /* This forces the layer to see the surface as updated even if
+         we replace it with itself.  */
       [self setContents:nil];
-#endif
-
       [self setContents:(id)currentSurface];
 
       /* Put currentSurface back on the end of the cache.  */
       [cache addObject:(id)currentSurface];
       currentSurface = NULL;
-
-      /* Schedule a run of getContext so that if Emacs is idle it will
-         perform the buffer copy, etc.  */
-      [self performSelectorOnMainThread:@selector (getContext)
-                             withObject:nil
-                          waitUntilDone:NO];
     }
 }
 
diff --git a/src/pdumper.c b/src/pdumper.c
index 85adf4147f9..ce4faefdaea 100644
--- a/src/pdumper.c
+++ b/src/pdumper.c
@@ -2555,7 +2555,7 @@ static dump_off
 dump_vectorlike_generic (struct dump_context *ctx,
                         const union vectorlike_header *header)
 {
-#if CHECK_STRUCTS && !defined (HASH_vectorlike_header_00A5A4BFB2)
+#if CHECK_STRUCTS && !defined (HASH_vectorlike_header_785E52047B)
 # error "vectorlike_header changed. See CHECK_STRUCTS comment in config.h."
 #endif
   const struct Lisp_Vector *v = (const struct Lisp_Vector *) header;
@@ -2747,7 +2747,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_6C25F9C3BC
+#if CHECK_STRUCTS && !defined HASH_buffer_EB0A5191C5
 # error "buffer changed. See CHECK_STRUCTS comment in config.h."
 #endif
   struct buffer munged_buffer = *in_buffer;
@@ -2998,7 +2998,7 @@ dump_vectorlike (struct dump_context *ctx,
                  Lisp_Object lv,
                  dump_off offset)
 {
-#if CHECK_STRUCTS && !defined HASH_pvec_type_5F2059C47E
+#if CHECK_STRUCTS && !defined HASH_pvec_type_D8A254BC70
 # error "pvec_type changed. See CHECK_STRUCTS comment in config.h."
 #endif
   const struct Lisp_Vector *v = XVECTOR (lv);
diff --git a/src/process.c b/src/process.c
index 08cb810ec13..2376d0f288d 100644
--- a/src/process.c
+++ b/src/process.c
@@ -2206,9 +2206,10 @@ create_process (Lisp_Object process, char **new_argv, 
Lisp_Object current_dir)
       inchannel = p->open_fd[READ_FROM_SUBPROCESS];
       forkout = p->open_fd[SUBPROCESS_STDOUT];
 
-#if defined(GNU_LINUX) && defined(F_SETPIPE_SZ)
+#if (defined (GNU_LINUX) || defined __ANDROID__)       \
+  && defined (F_SETPIPE_SZ)
       fcntl (inchannel, F_SETPIPE_SZ, read_process_output_max);
-#endif
+#endif /* (GNU_LINUX || __ANDROID__) && F_SETPIPE_SZ */
     }
 
   if (!NILP (p->stderrproc))
@@ -7414,8 +7415,31 @@ child_signal_notify (void)
   int fd = child_signal_write_fd;
   eassert (0 <= fd);
   char dummy = 0;
-  if (emacs_write (fd, &dummy, 1) != 1)
-    emacs_perror ("writing to child signal FD");
+  /* We used to error out here, like this:
+
+     if (emacs_write (fd, &dummy, 1) != 1)
+       emacs_perror ("writing to child signal FD");
+
+     But this calls `emacs_perror', which in turn invokes a localized
+     version of strerror, which is not reentrant and must not be
+     called within a signal handler:
+
+       __lll_lock_wait_private () at /lib64/libc.so.6
+       malloc () at /lib64/libc.so.6
+       _nl_make_l10nflist.localalias () at /lib64/libc.so.6
+       _nl_find_domain () at /lib64/libc.so.6
+       __dcigettext () at /lib64/libc.so.6
+       strerror_l () at /lib64/libc.so.6
+       emacs_perror (message=message@entry=0x6babc2)
+       child_signal_notify () at process.c:7419
+       handle_child_signal (sig=17) at process.c:7533
+       deliver_process_signal (sig=17, handler=0x6186b0>)
+       <signal handler called> () at /lib64/libc.so.6
+       _int_malloc () at /lib64/libc.so.6
+       in malloc () at /lib64/libc.so.6.
+
+     So we no longer check errors of emacs_write here.  */
+  emacs_write (fd, &dummy, 1);
 #endif
 }
 
diff --git a/src/regex-emacs.c b/src/regex-emacs.c
index 7e75f0ac597..95c3366652d 100644
--- a/src/regex-emacs.c
+++ b/src/regex-emacs.c
@@ -268,7 +268,9 @@ typedef enum
   on_failure_jump,
 
        /* Like on_failure_jump, but pushes a placeholder instead of the
-          current string position when executed.  */
+          current string position when executed.  Upon failure,
+          the current string position is thus not restored.
+          Used only for single-char loops that don't require backtracking.  */
   on_failure_keep_string_jump,
 
        /* Just like 'on_failure_jump', except that it checks that we
@@ -338,11 +340,12 @@ typedef enum
 
 /* Store NUMBER in two contiguous bytes starting at DESTINATION.  */
 
-#define STORE_NUMBER(destination, number)                              \
-  do {                                                                 \
-    (destination)[0] = (number) & 0377;                                        
\
-    (destination)[1] = (number) >> 8;                                  \
-  } while (false)
+static void
+STORE_NUMBER (unsigned char *destination, int16_t number)
+{
+  (destination)[0] = (number) & 0377;
+  (destination)[1] = (number) >> 8;
+}
 
 /* Same as STORE_NUMBER, except increment DESTINATION to
    the byte after where the number is stored.  Therefore, DESTINATION
@@ -367,6 +370,12 @@ extract_number (re_char *source)
   return leading_byte * 256 + source[0];
 }
 
+static re_char *
+extract_address (re_char *source)
+{
+  return source + 2 + extract_number (source);
+}
+
 /* Same as EXTRACT_NUMBER, except increment SOURCE to after the number.
    SOURCE must be an lvalue.  */
 
@@ -434,38 +443,27 @@ extract_number_and_incr (re_char **source)
 /* If REGEX_EMACS_DEBUG is defined, print many voluminous messages
    (if the variable regex_emacs_debug is positive).  */
 
-#ifdef REGEX_EMACS_DEBUG
+#if defined REGEX_EMACS_DEBUG || ENABLE_CHECKING
 
 /* Use standard I/O for debugging.  */
 # include "sysstdio.h"
 
-static int regex_emacs_debug = -100000;
-
-# define DEBUG_STATEMENT(e) e
-# define DEBUG_PRINT(...)                                       \
-  if (regex_emacs_debug > 0) fprintf (stderr, __VA_ARGS__)
-# define DEBUG_COMPILES_ARGUMENTS
-# define DEBUG_PRINT_COMPILED_PATTERN(p, s, e)                         \
-  if (regex_emacs_debug > 0) print_partial_compiled_pattern (s, e)
-# define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2)                        
\
-  if (regex_emacs_debug > 0) print_double_string (w, s1, sz1, s2, sz2)
-
 static void
-debug_putchar (int c)
+debug_putchar (FILE *dest, int c)
 {
   if (c >= 32 && c <= 126)
-    putc (c, stderr);
+    putc (c, dest);
   else
     {
       unsigned int uc = c;
-      fprintf (stderr, "{%02x}", uc);
+      fprintf (dest, "{%02x}", uc);
     }
 }
 
 /* Print the fastmap in human-readable form.  */
 
 static void
-print_fastmap (char *fastmap)
+print_fastmap (FILE *dest, char *fastmap)
 {
   bool was_a_range = false;
   int i = 0;
@@ -475,7 +473,7 @@ print_fastmap (char *fastmap)
       if (fastmap[i++])
        {
          was_a_range = false;
-         debug_putchar (i - 1);
+         debug_putchar (dest, i - 1);
          while (i < (1 << BYTEWIDTH)  &&  fastmap[i])
            {
              was_a_range = true;
@@ -483,12 +481,12 @@ print_fastmap (char *fastmap)
            }
          if (was_a_range)
            {
-             debug_putchar ('-');
-             debug_putchar (i - 1);
+             debug_putchar (dest, '-');
+             debug_putchar (dest, i - 1);
            }
        }
     }
-  putc ('\n', stderr);
+  putc ('\n', dest);
 }
 
 
@@ -496,7 +494,7 @@ print_fastmap (char *fastmap)
    the START pointer into it and ending just before the pointer END.  */
 
 static void
-print_partial_compiled_pattern (re_char *start, re_char *end)
+print_partial_compiled_pattern (FILE *dest, re_char *start, re_char *end)
 {
   int mcnt, mcnt2;
   re_char *p = start;
@@ -504,50 +502,50 @@ print_partial_compiled_pattern (re_char *start, re_char 
*end)
 
   if (start == NULL)
     {
-      fputs ("(null)\n", stderr);
+      fputs ("(null)\n", dest);
       return;
     }
 
   /* Loop over pattern commands.  */
   while (p < pend)
     {
-      fprintf (stderr, "%td:\t", p - start);
+      fprintf (dest, "%td:\t", p - start);
 
       switch ((re_opcode_t) *p++)
        {
        case no_op:
-         fputs ("/no_op", stderr);
+         fputs ("/no_op", dest);
          break;
 
        case succeed:
-         fputs ("/succeed", stderr);
+         fputs ("/succeed", dest);
          break;
 
        case exactn:
          mcnt = *p++;
-         fprintf (stderr, "/exactn/%d", mcnt);
+         fprintf (dest, "/exactn/%d", mcnt);
          do
            {
-             debug_putchar ('/');
-             debug_putchar (*p++);
+             debug_putchar (dest, '/');
+             debug_putchar (dest, *p++);
            }
          while (--mcnt);
          break;
 
        case start_memory:
-         fprintf (stderr, "/start_memory/%d", *p++);
+         fprintf (dest, "/start_memory/%d", *p++);
          break;
 
        case stop_memory:
-         fprintf (stderr, "/stop_memory/%d", *p++);
+         fprintf (dest, "/stop_memory/%d", *p++);
          break;
 
        case duplicate:
-         fprintf (stderr, "/duplicate/%d", *p++);
+         fprintf (dest, "/duplicate/%d", *p++);
          break;
 
        case anychar:
-         fputs ("/anychar", stderr);
+         fputs ("/anychar", dest);
          break;
 
        case charset:
@@ -558,11 +556,11 @@ print_partial_compiled_pattern (re_char *start, re_char 
*end)
            int length = CHARSET_BITMAP_SIZE (p - 1);
            bool has_range_table = CHARSET_RANGE_TABLE_EXISTS_P (p - 1);
 
-           fprintf (stderr, "/charset [%s",
+           fprintf (dest, "/charset [%s",
                     (re_opcode_t) *(p - 1) == charset_not ? "^" : "");
 
            if (p + (*p & 0x7f) >= pend)
-             fputs (" !extends past end of pattern! ", stderr);
+             fputs (" !extends past end of pattern! ", dest);
 
            for (c = 0; c < 256; c++)
              if (c / 8 < length
@@ -571,33 +569,33 @@ print_partial_compiled_pattern (re_char *start, re_char 
*end)
                  /* Are we starting a range?  */
                  if (last + 1 == c && ! in_range)
                    {
-                     debug_putchar ('-');
+                     debug_putchar (dest, '-');
                      in_range = true;
                    }
                  /* Have we broken a range?  */
                  else if (last + 1 != c && in_range)
                    {
-                     debug_putchar (last);
+                     debug_putchar (dest, last);
                      in_range = false;
                    }
 
                  if (! in_range)
-                   debug_putchar (c);
+                   debug_putchar (dest, c);
 
                  last = c;
              }
 
            if (in_range)
-             debug_putchar (last);
+             debug_putchar (dest, last);
 
-           debug_putchar (']');
+           debug_putchar (dest, ']');
 
            p += 1 + length;
 
            if (has_range_table)
              {
                int count;
-               fputs ("has-range-table", stderr);
+               fputs ("has-range-table", dest);
 
                /* ??? Should print the range table; for now, just skip it.  */
                p += 2;         /* skip range table bits */
@@ -608,160 +606,175 @@ print_partial_compiled_pattern (re_char *start, re_char 
*end)
          break;
 
        case begline:
-         fputs ("/begline", stderr);
+         fputs ("/begline", dest);
          break;
 
        case endline:
-         fputs ("/endline", stderr);
+         fputs ("/endline", dest);
          break;
 
        case on_failure_jump:
          EXTRACT_NUMBER_AND_INCR (mcnt, p);
-         fprintf (stderr, "/on_failure_jump to %td", p + mcnt - start);
+         fprintf (dest, "/on_failure_jump to %td", p + mcnt - start);
          break;
 
        case on_failure_keep_string_jump:
          EXTRACT_NUMBER_AND_INCR (mcnt, p);
-         fprintf (stderr, "/on_failure_keep_string_jump to %td",
+         fprintf (dest, "/on_failure_keep_string_jump to %td",
                   p + mcnt - start);
          break;
 
        case on_failure_jump_nastyloop:
          EXTRACT_NUMBER_AND_INCR (mcnt, p);
-         fprintf (stderr, "/on_failure_jump_nastyloop to %td",
+         fprintf (dest, "/on_failure_jump_nastyloop to %td",
                   p + mcnt - start);
          break;
 
        case on_failure_jump_loop:
          EXTRACT_NUMBER_AND_INCR (mcnt, p);
-         fprintf (stderr, "/on_failure_jump_loop to %td",
+         fprintf (dest, "/on_failure_jump_loop to %td",
                   p + mcnt - start);
          break;
 
        case on_failure_jump_smart:
          EXTRACT_NUMBER_AND_INCR (mcnt, p);
-         fprintf (stderr, "/on_failure_jump_smart to %td",
+         fprintf (dest, "/on_failure_jump_smart to %td",
                   p + mcnt - start);
          break;
 
        case jump:
          EXTRACT_NUMBER_AND_INCR (mcnt, p);
-         fprintf (stderr, "/jump to %td", p + mcnt - start);
+         fprintf (dest, "/jump to %td", p + mcnt - start);
          break;
 
        case succeed_n:
          EXTRACT_NUMBER_AND_INCR (mcnt, p);
          EXTRACT_NUMBER_AND_INCR (mcnt2, p);
-         fprintf (stderr, "/succeed_n to %td, %d times",
+         fprintf (dest, "/succeed_n to %td, %d times",
                   p - 2 + mcnt - start, mcnt2);
          break;
 
        case jump_n:
          EXTRACT_NUMBER_AND_INCR (mcnt, p);
          EXTRACT_NUMBER_AND_INCR (mcnt2, p);
-         fprintf (stderr, "/jump_n to %td, %d times",
+         fprintf (dest, "/jump_n to %td, %d times",
                   p - 2 + mcnt - start, mcnt2);
          break;
 
        case set_number_at:
          EXTRACT_NUMBER_AND_INCR (mcnt, p);
          EXTRACT_NUMBER_AND_INCR (mcnt2, p);
-         fprintf (stderr, "/set_number_at location %td to %d",
+         fprintf (dest, "/set_number_at location %td to %d",
                   p - 2 + mcnt - start, mcnt2);
          break;
 
        case wordbound:
-         fputs ("/wordbound", stderr);
+         fputs ("/wordbound", dest);
          break;
 
        case notwordbound:
-         fputs ("/notwordbound", stderr);
+         fputs ("/notwordbound", dest);
          break;
 
        case wordbeg:
-         fputs ("/wordbeg", stderr);
+         fputs ("/wordbeg", dest);
          break;
 
        case wordend:
-         fputs ("/wordend", stderr);
+         fputs ("/wordend", dest);
          break;
 
        case symbeg:
-         fputs ("/symbeg", stderr);
+         fputs ("/symbeg", dest);
          break;
 
        case symend:
-         fputs ("/symend", stderr);
+         fputs ("/symend", dest);
          break;
 
        case syntaxspec:
-         fputs ("/syntaxspec", stderr);
+         fputs ("/syntaxspec", dest);
          mcnt = *p++;
-         fprintf (stderr, "/%d", mcnt);
+         fprintf (dest, "/%d", mcnt);
          break;
 
        case notsyntaxspec:
-         fputs ("/notsyntaxspec", stderr);
+         fputs ("/notsyntaxspec", dest);
          mcnt = *p++;
-         fprintf (stderr, "/%d", mcnt);
+         fprintf (dest, "/%d", mcnt);
          break;
 
        case at_dot:
-         fputs ("/at_dot", stderr);
+         fputs ("/at_dot", dest);
          break;
 
        case categoryspec:
-         fputs ("/categoryspec", stderr);
+         fputs ("/categoryspec", dest);
          mcnt = *p++;
-         fprintf (stderr, "/%d", mcnt);
+         fprintf (dest, "/%d", mcnt);
          break;
 
        case notcategoryspec:
-         fputs ("/notcategoryspec", stderr);
+         fputs ("/notcategoryspec", dest);
          mcnt = *p++;
-         fprintf (stderr, "/%d", mcnt);
+         fprintf (dest, "/%d", mcnt);
          break;
 
        case begbuf:
-         fputs ("/begbuf", stderr);
+         fputs ("/begbuf", dest);
          break;
 
        case endbuf:
-         fputs ("/endbuf", stderr);
+         fputs ("/endbuf", dest);
          break;
 
        default:
-         fprintf (stderr, "?%d", *(p-1));
+         fprintf (dest, "?%d", *(p-1));
        }
 
-      putc ('\n', stderr);
+      putc ('\n', dest);
     }
 
-  fprintf (stderr, "%td:\tend of pattern.\n", p - start);
+  fprintf (dest, "%td:\tend of pattern.\n", p - start);
 }
 
-
-static void
-print_compiled_pattern (struct re_pattern_buffer *bufp)
+void
+print_compiled_pattern (FILE *dest, struct re_pattern_buffer *bufp)
 {
+  if (!dest)
+    dest = stderr;
   re_char *buffer = bufp->buffer;
 
-  print_partial_compiled_pattern (buffer, buffer + bufp->used);
-  fprintf (stderr, "%td bytes used/%td bytes allocated.\n",
+  print_partial_compiled_pattern (dest, buffer, buffer + bufp->used);
+  fprintf (dest, "%td bytes used/%td bytes allocated.\n",
            bufp->used, bufp->allocated);
 
   if (bufp->fastmap_accurate && bufp->fastmap)
     {
-      fputs ("fastmap: ", stderr);
-      print_fastmap (bufp->fastmap);
+      fputs ("fastmap: ", dest);
+      print_fastmap (dest, bufp->fastmap);
     }
 
-  fprintf (stderr, "re_nsub: %td\t", bufp->re_nsub);
-  fprintf (stderr, "regs_alloc: %d\t", bufp->regs_allocated);
-  fprintf (stderr, "can_be_null: %d\n", bufp->can_be_null);
+  fprintf (dest, "re_nsub: %td\t", bufp->re_nsub);
+  fprintf (dest, "regs_alloc: %d\t", bufp->regs_allocated);
+  fprintf (dest, "can_be_null: %d\n", bufp->can_be_null);
   /* Perhaps we should print the translate table?  */
 }
 
+#endif
+
+#ifdef REGEX_EMACS_DEBUG
+
+static int regex_emacs_debug = -100000;
+
+# define DEBUG_STATEMENT(e) e
+# define DEBUG_PRINT(...)                                       \
+  if (regex_emacs_debug > 0) fprintf (stderr, __VA_ARGS__)
+# define DEBUG_COMPILES_ARGUMENTS
+# define DEBUG_PRINT_COMPILED_PATTERN(p, s, e)                         \
+  if (regex_emacs_debug > 0) print_partial_compiled_pattern (stderr, s, e)
+# define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2)                        
\
+  if (regex_emacs_debug > 0) print_double_string (w, s1, sz1, s2, sz2)
 
 static void
 print_double_string (re_char *where, re_char *string1, ptrdiff_t size1,
@@ -775,12 +788,12 @@ print_double_string (re_char *where, re_char *string1, 
ptrdiff_t size1,
       if (FIRST_STRING_P (where))
        {
          for (i = 0; i < string1 + size1 - where; i++)
-           debug_putchar (where[i]);
+           debug_putchar (stderr, where[i]);
          where = string2;
        }
 
       for (i = 0; i < string2 + size2 - where; i++)
-        debug_putchar (where[i]);
+        debug_putchar (stderr, where[i]);
     }
 }
 
@@ -1166,8 +1179,8 @@ static void insert_op2 (re_opcode_t op, unsigned char 
*loc,
 static bool at_begline_loc_p (re_char *pattern, re_char *p);
 static bool at_endline_loc_p (re_char *p, re_char *pend);
 static re_char *skip_one_char (re_char *p);
-static int analyze_first (re_char *p, re_char *pend,
-                         char *fastmap, bool multibyte);
+static bool analyze_first (struct re_pattern_buffer *bufp,
+                           re_char *p, re_char *pend, char *fastmap);
 
 /* Fetch the next character in the uncompiled pattern, with no
    translation.  */
@@ -1760,7 +1773,7 @@ regex_compile (re_char *pattern, ptrdiff_t size,
   if (regex_emacs_debug > 0)
     {
       for (ptrdiff_t debug_count = 0; debug_count < size; debug_count++)
-       debug_putchar (pattern[debug_count]);
+       debug_putchar (stderr, pattern[debug_count]);
       putc ('\n', stderr);
     }
 #endif
@@ -1917,18 +1930,28 @@ regex_compile (re_char *pattern, ptrdiff_t size,
                    ptrdiff_t startoffset = 0;
                    re_opcode_t ofj =
                      /* Check if the loop can match the empty string.  */
-                     (simple || !analyze_first (laststart, b, NULL, false))
+                     (simple || !analyze_first (bufp, laststart, b, NULL))
                      ? on_failure_jump : on_failure_jump_loop;
                    eassert (skip_one_char (laststart) <= b);
 
                    if (!zero_times_ok && simple)
                      { /* Since simple * loops can be made faster by using
-                          on_failure_keep_string_jump, we turn simple P+
-                          into PP* if P is simple.  */
+                          on_failure_keep_string_jump, we turn P+ into PP*
+                           if P is simple.
+                          We can't use `top: <BODY>; OFJS exit; J top; exit:`
+                          because the OFJS needs to be at the beginning
+                          so we can replace
+                              top: OFJS exit; <BODY>; J top; exit
+                          with
+                              OFKSJ exit; loop: <BODY>; J loop; exit
+                          i.e. a single OFJ at the beginning of the loop
+                          rather than once per iteration.  */
                        unsigned char *p1, *p2;
                        startoffset = b - laststart;
                        GET_BUFFER_SPACE (startoffset);
                        p1 = b; p2 = laststart;
+                       /* We presume that the code skipped
+                          by `skip_one_char` is position-independent.  */
                        while (p2 < p1)
                          *b++ = *p2++;
                        zero_times_ok = 1;
@@ -1964,7 +1987,7 @@ regex_compile (re_char *pattern, ptrdiff_t size,
                GET_BUFFER_SPACE (7); /* We might use less.  */
                if (many_times_ok)
                  {
-                   bool emptyp = !!analyze_first (laststart, b, NULL, false);
+                   bool emptyp = analyze_first (bufp, laststart, b, NULL);
 
                    /* The non-greedy multiple match looks like
                       a repeat..until: we only need a conditional jump
@@ -2670,7 +2693,7 @@ regex_compile (re_char *pattern, ptrdiff_t size,
     {
       re_compile_fastmap (bufp);
       DEBUG_PRINT ("\nCompiled pattern:\n");
-      print_compiled_pattern (bufp);
+      print_compiled_pattern (stderr, bufp);
     }
   regex_emacs_debug--;
 #endif
@@ -2799,309 +2822,469 @@ group_in_compile_stack (compile_stack_type 
compile_stack, regnum_t regnum)
   return false;
 }
 
-/* analyze_first.
-   If fastmap is non-NULL, go through the pattern and fill fastmap
-   with all the possible leading chars.  If fastmap is NULL, don't
-   bother filling it up (obviously) and only return whether the
-   pattern could potentially match the empty string.
-
-   Return 1  if p..pend might match the empty string.
-   Return 0  if p..pend matches at least one char.
-   Return -1 if fastmap was not updated accurately.  */
-
-static int
-analyze_first (re_char *p, re_char *pend, char *fastmap, bool multibyte)
+/* Iterate through all the char-matching operations directly reachable from P.
+   This is the inner loop of `forall_firstchar`, which see.
+   LOOP_BEG..LOOP_END delimit the currentl "block" of code (we assume
+   the code is made of syntactically nested loops).
+   LOOP_END is blindly assumed to be "safe".
+   To guarantee termination, at each iteration, either LOOP_BEG should
+   get bigger, or it should stay the same and P should get bigger.  */
+static bool
+forall_firstchar_1 (re_char *p, re_char *pend,
+                    re_char *loop_beg, re_char *loop_end,
+                    bool f (const re_char *p, void *arg), void *arg)
 {
-  int j, k;
-  int nbits;
-  bool not;
-
-  /* If all elements for base leading-codes in fastmap is set, this
-     flag is set true.  */
-  bool match_any_multibyte_characters = false;
-
-  eassert (p);
-
-  /* The loop below works as follows:
-     - It has a working-list kept in the PATTERN_STACK and which basically
-       starts by only containing a pointer to the first operation.
-     - If the opcode we're looking at is a match against some set of
-       chars, then we add those chars to the fastmap and go on to the
-       next work element from the worklist (done via 'break').
-     - If the opcode is a control operator on the other hand, we either
-       ignore it (if it's meaningless at this point, such as 'start_memory')
-       or execute it (if it's a jump).  If the jump has several destinations
-       (i.e. 'on_failure_jump'), then we push the other destination onto the
-       worklist.
-     We guarantee termination by ignoring backward jumps (more or less),
-     so that P is monotonically increasing.  More to the point, we
-     never set P (or push) anything '<= p1'.  */
+  eassert (p >= loop_beg);
+  eassert (p <= loop_end);
 
-  while (p < pend)
+  while (true)
     {
-      /* P1 is used as a marker of how far back a 'on_failure_jump'
-        can go without being ignored.  It is normally equal to P
-        (which prevents any backward 'on_failure_jump') except right
-        after a plain 'jump', to allow patterns such as:
-           0: jump 10
-           3..9: <body>
-           10: on_failure_jump 3
-        as used for the *? operator.  */
-      re_char *p1 = p;
+      re_char *newp1, *newp2, *tmp;
+      re_char *p_orig = p;
+      int offset;
 
-      switch (*p++)
-       {
-       case succeed:
-         return 1;
+      if (p == pend)
+        return false;
+      else if (p == loop_end)
+        return true;
+      else if (p > loop_end)
+        {
+#if ENABLE_CHECKING
+          fprintf (stderr, "FORALL_FIRSTCHAR: Broken assumption1!!\n");
+#endif
+          return false; /* FIXME: Broken assumption about the code shape.  */
+        }
+      else
+        switch (*p)
+          {
+          /* Cases which stop the iteration.  */
+          case succeed:
+          case exactn:
+          case charset:
+          case charset_not:
+          case anychar:
+          case syntaxspec:
+          case notsyntaxspec:
+          case categoryspec:
+          case notcategoryspec:
+            return f (p, arg);
+
+          /* Cases which may match the empty string.  */
+          case at_dot:
+          case begbuf:
+          case no_op:
+          case wordbound:
+          case notwordbound:
+          case begline:
+            p++;
+            continue;
+
+          /* Cases which may match the empty string and may
+             tell us something about the next char.  */
+          case endline:
+          case endbuf:
+          case wordbeg:
+          case wordend:
+          case symbeg:
+          case symend:
+            if (f (p, arg))
+              return true;
+            p++;
+            continue;
+
+          case jump:
+          case jump_n:
+           newp1 = extract_address (p + 1);
+           if (newp1 > p)
+             { /* Forward jump, boring.  */
+               p = newp1;
+               continue;
+             }
+           switch (*newp1)
+             {
+             case on_failure_jump:
+             case on_failure_keep_string_jump:
+             case on_failure_jump_nastyloop:
+             case on_failure_jump_loop:
+             case on_failure_jump_smart:
+             case succeed_n:
+               newp2 = extract_address (newp1 + 1);
+               goto do_twoway_jump;
+             default:
+               newp2 = loop_end; /* "Safe" choice.  */
+               goto do_jump;
+             }
 
-       case duplicate:
-         /* If the first character has to match a backreference, that means
-            that the group was empty (since it already matched).  Since this
-            is the only case that interests us here, we can assume that the
-            backreference must match the empty string.  */
-         p++;
-         continue;
+         case on_failure_jump:
+         case on_failure_keep_string_jump:
+         case on_failure_jump_nastyloop:
+         case on_failure_jump_loop:
+         case on_failure_jump_smart:
+           newp1 = extract_address (p + 1);
+           newp2 = p + 3;
+           /* For `+` loops, we often have an `on_failure_jump` that skips
+               forward over a subsequent `jump`.  Recognize this pattern
+               since that subsequent `jump` is the one that jumps to the
+               loop-entry.  */
+           newp2 = ((re_opcode_t) *newp2 == jump)
+                   ? extract_address (newp2 + 1) : newp2;
+
+         do_twoway_jump:
+           /* We have to check that both destinations are safe.
+              Arrange for `newp1` to be the smaller of the two.  */
+           if (newp1 > newp2)
+             (tmp = newp1, newp1 = newp2, newp2 = tmp);
+
+           if (newp2 <= p_orig) /* Both destinations go backward!  */
+             {
+#if ENABLE_CHECKING
+               fprintf (stderr, "FORALL_FIRSTCHAR: Broken assumption2!!\n");
+#endif
+               return false;
+              }
 
+            if (!forall_firstchar_1 (newp2, pend, loop_beg, loop_end, f, arg))
+              return false;
 
-      /* Following are the cases which match a character.  These end
-        with 'break'.  */
+         do_jump:
+           eassert (newp2 <= loop_end);
+            if (newp1 <= p_orig)
+             {
+               if (newp1 < loop_beg)
+                 {
+#if ENABLE_CHECKING
+                   fprintf (stderr, "FORALL_FIRSTCHAR: Broken 
assumption3!!\n");
+#endif
+                   return false;
+                 }
+               else if (newp1 == loop_beg)
+                 /* If we jump backward to the entry point of the current loop
+                    it means it's a zero-length cycle through that loop;
+                    this cycle itself does not break safety.  */
+                 return true;
+               else
+                 /* We jump backward to a new loop, nested within the current
+                    one. `newp1` is the entry point and `newp2` the exit of
+                    that inner loop.  */
+                 /* `p` gets smaller, but termination is still ensured because
+                    `loop_beg` gets bigger. */
+                 (loop_beg = newp1, loop_end = newp2);
+             }
+           p = newp1;
+           continue;
+
+          case succeed_n:
+           newp1 = extract_address (p + 1);
+           newp2 = p + 5;      /* Skip the two bytes containing the count.  */
+           goto do_twoway_jump;
+
+          case set_number_at:
+            offset = extract_number (p + 1);
+            DEBUG_STATEMENT (eassert (extract_number (p + 3)));
+            /* If we're setting the counter of an immediately following
+               `succeed_n`, then this next execution of `succeed_n` will do
+               nothing but decrement its counter and "fall through".
+               So we do the fall through here to avoid considering the
+               "on failure" part of the `succeed_n` which should only be
+               considered when coming from the `jump(_n)` at the end of
+               the loop.  */
+            p += (offset == 5 && p[5] == succeed_n) ? 10 : 5;
+            continue;
+
+          case start_memory:
+          case stop_memory:
+            p += 2;
+            continue;
+
+          /* This could match the empty string, so we may need to continue,
+             but in most cases, this can match "anything", so we should
+             return `false` unless told otherwise.  */
+          case duplicate:
+            if (!f (p, arg))
+              return false;
+            p += 2;
+            continue;
+
+          default:
+            abort (); /* We have listed all the cases.  */
+          }
+      }
+}
 
-       case exactn:
-         if (fastmap)
-           {
-             /* If multibyte is nonzero, the first byte of each
-                character is an ASCII or a leading code.  Otherwise,
-                each byte is a character.  Thus, this works in both
-                cases. */
-             fastmap[p[1]] = 1;
-             if (multibyte)
-               {
-                 /* Cover the case of matching a raw char in a
-                    multibyte regexp against unibyte.  */
-                 if (CHAR_BYTE8_HEAD_P (p[1]))
-                   fastmap[CHAR_TO_BYTE8 (STRING_CHAR (p + 1))] = 1;
-               }
-             else
-               {
-                 /* For the case of matching this unibyte regex
-                    against multibyte, we must set a leading code of
-                    the corresponding multibyte character.  */
-                 int c = RE_CHAR_TO_MULTIBYTE (p[1]);
+/* Iterate through all the char-matching operations directly reachable from P.
+   Return true if P is "safe", meaning that PEND cannot be reached directly
+   from P and all calls to F returned true.
+   Return false if PEND *may* be directly reachable from P or if one of
+   the calls to F returned false.
+   PEND can be NULL (and hence never reachable).
+
+   Call `F (POS, ARG)` for every POS directly reachable from P,
+   before reaching PEND, where POS is the position of a char-matching
+   operation (`exactn`, `charset`, ...).
+
+   For operations that match the empty string (`wordbeg`, ...), if F
+   returns true we stop going down that path immediately but if it returns
+   false we don't consider it as a failure and we simply look for the
+   next char-matching operations on that path.
+   For `duplicate`, it is the reverse: a false is an immediate failure
+   whereas a true just lets the analysis continue with the rest of the path.
+
+   This function can be used while building the bytecode (in which case
+   you should pass NULL for bufp), but if so, P and PEND need to delimit
+   a valid block such that there is not jump to a location outside
+   of [P...PEND].  */
+static bool
+forall_firstchar (struct re_pattern_buffer *bufp, re_char *p, re_char *pend,
+                  bool f (re_char *p, void *arg), void *arg)
+{
+  eassert (!bufp || bufp->used);
+  eassert (pend || bufp->used);
+  return forall_firstchar_1 (p, pend,
+                             bufp ? bufp->buffer - 1 : p,
+                             bufp ? bufp->buffer + bufp->used + 1 : pend,
+                             f, arg);
+}
 
-                 fastmap[CHAR_LEADING_CODE (c)] = 1;
-               }
-           }
-         break;
+struct anafirst_data {
+  bool multibyte;
+  char *fastmap;
+  bool match_any_multibyte_characters;
+};
 
+static bool
+analyze_first_fastmap (const re_char *p, void *arg)
+{
+  struct anafirst_data *data = arg;
 
-       case anychar:
-         /* We could put all the chars except for \n (and maybe \0)
-            but we don't bother since it is generally not worth it.  */
-         if (!fastmap) break;
-         return -1;
+  int j, k;
+  int nbits;
+  bool not;
+
+  switch (*p)
+    {
+    case succeed:
+      return false;
 
+    case duplicate:
+      /* If the first character has to match a backreference, that means
+         that the group was empty (since it already matched).  Since this
+         is the only case that interests us here, we can assume that the
+         backreference must match the empty string and we need to
+         build the fastmap from the rest of the path.  */
+      return true;
 
-       case charset_not:
-         if (!fastmap) break;
-         {
-           /* Chars beyond end of bitmap are possible matches.  */
-           for (j = CHARSET_BITMAP_SIZE (&p[-1]) * BYTEWIDTH;
-                j < (1 << BYTEWIDTH); j++)
-             fastmap[j] = 1;
-         }
-         FALLTHROUGH;
-       case charset:
-         if (!fastmap) break;
-         not = (re_opcode_t) *(p - 1) == charset_not;
-         nbits = CHARSET_BITMAP_SIZE (&p[-1]) * BYTEWIDTH;
-         p++;
-         for (j = 0; j < nbits; j++)
-           if (!!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))) ^ not)
-             fastmap[j] = 1;
-
-         /* To match raw bytes (in the 80..ff range) against multibyte
-            strings, add their leading bytes to the fastmap.  */
-         for (j = 0x80; j < nbits; j++)
-           if (!!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))) ^ not)
-             fastmap[CHAR_LEADING_CODE (BYTE8_TO_CHAR (j))] = 1;
-
-         if (/* Any leading code can possibly start a character
-                which doesn't match the specified set of characters.  */
-             not
-             ||
-             /* If we can match a character class, we can match any
-                multibyte characters.  */
-             (CHARSET_RANGE_TABLE_EXISTS_P (&p[-2])
-              && CHARSET_RANGE_TABLE_BITS (&p[-2]) != 0))
+    /* Following are the cases which match a character.  These end
+       with 'break'.  */
 
-           {
-             if (match_any_multibyte_characters == false)
-               {
-                 for (j = MIN_MULTIBYTE_LEADING_CODE;
-                      j <= MAX_MULTIBYTE_LEADING_CODE; j++)
-                   fastmap[j] = 1;
-                 match_any_multibyte_characters = true;
-               }
-           }
+    case exactn:
+      p++;
+      /* If multibyte is nonzero, the first byte of each
+        character is an ASCII or a leading code.  Otherwise,
+        each byte is a character.  Thus, this works in both
+        cases. */
+      data->fastmap[p[1]] = 1;
+      if (data->multibyte)
+       {
+         /* Cover the case of matching a raw char in a
+            multibyte regexp against unibyte.  */
+         if (CHAR_BYTE8_HEAD_P (p[1]))
+           data->fastmap[CHAR_TO_BYTE8 (STRING_CHAR (p + 1))] = 1;
+       }
+      else
+       {
+         /* For the case of matching this unibyte regex
+            against multibyte, we must set a leading code of
+            the corresponding multibyte character.  */
+         int c = RE_CHAR_TO_MULTIBYTE (p[1]);
 
-         else if (!not && CHARSET_RANGE_TABLE_EXISTS_P (&p[-2])
-                  && match_any_multibyte_characters == false)
-           {
-             /* Set fastmap[I] to 1 where I is a leading code of each
-                multibyte character in the range table. */
-             int c, count;
-             unsigned char lc1, lc2;
-
-             /* Make P points the range table.  '+ 2' is to skip flag
-                bits for a character class.  */
-             p += CHARSET_BITMAP_SIZE (&p[-2]) + 2;
-
-             /* Extract the number of ranges in range table into COUNT.  */
-             EXTRACT_NUMBER_AND_INCR (count, p);
-             for (; count > 0; count--, p += 3)
-               {
-                 /* Extract the start and end of each range.  */
-                 EXTRACT_CHARACTER (c, p);
-                 lc1 = CHAR_LEADING_CODE (c);
-                 p += 3;
-                 EXTRACT_CHARACTER (c, p);
-                 lc2 = CHAR_LEADING_CODE (c);
-                 for (j = lc1; j <= lc2; j++)
-                   fastmap[j] = 1;
-               }
-           }
-         break;
+         data->fastmap[CHAR_LEADING_CODE (c)] = 1;
+       }
+      return true;
 
-       case syntaxspec:
-       case notsyntaxspec:
-         if (!fastmap) break;
-         /* This match depends on text properties.  These end with
-            aborting optimizations.  */
-         return -1;
+    case anychar:
+      /* We could put all the chars except for \n (and maybe \0)
+        but we don't bother since it is generally not worth it.  */
+      return false;
 
-       case categoryspec:
-       case notcategoryspec:
-         if (!fastmap) break;
-         not = (re_opcode_t)p[-1] == notcategoryspec;
-         k = *p++;
-         for (j = (1 << BYTEWIDTH); j >= 0; j--)
-           if ((CHAR_HAS_CATEGORY (j, k)) ^ not)
-             fastmap[j] = 1;
-
-         /* Any leading code can possibly start a character which
-            has or doesn't has the specified category.  */
-         if (match_any_multibyte_characters == false)
+    case charset_not:
+      {
+       /* Chars beyond end of bitmap are possible matches.  */
+       for (j = CHARSET_BITMAP_SIZE (p) * BYTEWIDTH;
+             j < (1 << BYTEWIDTH); j++)
+         data->fastmap[j] = 1;
+      }
+      FALLTHROUGH;
+    case charset:
+      not = (re_opcode_t) *(p) == charset_not;
+      nbits = CHARSET_BITMAP_SIZE (p) * BYTEWIDTH;
+      p += 2;
+      for (j = 0; j < nbits; j++)
+       if (!!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))) ^ not)
+         data->fastmap[j] = 1;
+
+      /* To match raw bytes (in the 80..ff range) against multibyte
+        strings, add their leading bytes to the fastmap.  */
+      for (j = 0x80; j < nbits; j++)
+       if (!!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))) ^ not)
+         data->fastmap[CHAR_LEADING_CODE (BYTE8_TO_CHAR (j))] = 1;
+
+      if (/* Any leading code can possibly start a character
+            which doesn't match the specified set of characters.  */
+         not
+         ||
+         /* If we can match a character class, we can match any
+            multibyte characters.  */
+         (CHARSET_RANGE_TABLE_EXISTS_P (&p[-2])
+          && CHARSET_RANGE_TABLE_BITS (&p[-2]) != 0))
+
+       {
+         if (!data->match_any_multibyte_characters)
            {
              for (j = MIN_MULTIBYTE_LEADING_CODE;
-                  j <= MAX_MULTIBYTE_LEADING_CODE; j++)
-               fastmap[j] = 1;
-             match_any_multibyte_characters = true;
+                   j <= MAX_MULTIBYTE_LEADING_CODE; j++)
+               data->fastmap[j] = 1;
+             data->match_any_multibyte_characters = true;
            }
-         break;
-
-      /* All cases after this match the empty string.  These end with
-        'continue'.  */
-
-       case at_dot:
-       case no_op:
-       case begline:
-       case endline:
-       case begbuf:
-       case endbuf:
-       case wordbound:
-       case notwordbound:
-       case wordbeg:
-       case wordend:
-       case symbeg:
-       case symend:
-         continue;
-
+       }
 
-       case jump:
-         EXTRACT_NUMBER_AND_INCR (j, p);
-         if (j < 0)
-           /* Backward jumps can only go back to code that we've already
-              visited.  're_compile' should make sure this is true.  */
-           break;
-         p += j;
-         switch (*p)
+      else if (!not && CHARSET_RANGE_TABLE_EXISTS_P (&p[-2])
+              && data->match_any_multibyte_characters == false)
+       {
+         /* Set fastmap[I] to 1 where I is a leading code of each
+            multibyte character in the range table. */
+         int c, count;
+         unsigned char lc1, lc2;
+
+         /* Make P points the range table.  '+ 2' is to skip flag
+            bits for a character class.  */
+         p += CHARSET_BITMAP_SIZE (&p[-2]) + 2;
+
+         /* Extract the number of ranges in range table into COUNT.  */
+         EXTRACT_NUMBER_AND_INCR (count, p);
+         for (; count > 0; count--, p += 3)
            {
-           case on_failure_jump:
-           case on_failure_keep_string_jump:
-           case on_failure_jump_loop:
-           case on_failure_jump_nastyloop:
-           case on_failure_jump_smart:
-             p++;
-             break;
-           default:
-             continue;
-           };
-         /* Keep P1 to allow the 'on_failure_jump' we are jumping to
-            to jump back to "just after here".  */
-         FALLTHROUGH;
-       case on_failure_jump:
-       case on_failure_keep_string_jump:
-       case on_failure_jump_nastyloop:
-       case on_failure_jump_loop:
-       case on_failure_jump_smart:
-         EXTRACT_NUMBER_AND_INCR (j, p);
-         if (p + j <= p1)
-           ; /* Backward jump to be ignored.  */
-         else
-           { /* We have to look down both arms.
-                We first go down the "straight" path so as to minimize
-                stack usage when going through alternatives.  */
-             int r = analyze_first (p, pend, fastmap, multibyte);
-             if (r) return r;
-             p += j;
+             /* Extract the start and end of each range.  */
+             EXTRACT_CHARACTER (c, p);
+             lc1 = CHAR_LEADING_CODE (c);
+             p += 3;
+             EXTRACT_CHARACTER (c, p);
+             lc2 = CHAR_LEADING_CODE (c);
+             for (j = lc1; j <= lc2; j++)
+               data->fastmap[j] = 1;
            }
-         continue;
+       }
+      return true;
 
+    case syntaxspec:
+    case notsyntaxspec:
+      /* This match depends on text properties.  These end with
+        aborting optimizations.  */
+      return false;
 
-       case jump_n:
-         /* This code simply does not properly handle forward jump_n.  */
-         DEBUG_STATEMENT (EXTRACT_NUMBER (j, p); eassert (j < 0));
-         p += 4;
-         /* jump_n can either jump or fall through.  The (backward) jump
-            case has already been handled, so we only need to look at the
-            fallthrough case.  */
-         continue;
+    case categoryspec:
+    case notcategoryspec:
+      not = (re_opcode_t)p[0] == notcategoryspec;
+      p++;
+      k = *p++;
+      for (j = (1 << BYTEWIDTH); j >= 0; j--)
+       if ((CHAR_HAS_CATEGORY (j, k)) ^ not)
+         data->fastmap[j] = 1;
+
+      /* Any leading code can possibly start a character which
+        has or doesn't has the_malloc_fn specified category.  */
+      if (!data->match_any_multibyte_characters)
+       {
+         for (j = MIN_MULTIBYTE_LEADING_CODE;
+               j <= MAX_MULTIBYTE_LEADING_CODE; j++)
+           data->fastmap[j] = 1;
+         data->match_any_multibyte_characters = true;
+       }
+      return true;
 
-       case succeed_n:
-         /* If N == 0, it should be an on_failure_jump_loop instead.  */
-         DEBUG_STATEMENT (EXTRACT_NUMBER (j, p + 2); eassert (j > 0));
-         p += 4;
-         /* We only care about one iteration of the loop, so we don't
-            need to consider the case where this behaves like an
-            on_failure_jump.  */
-         continue;
+    case endline:
+    case endbuf:
+    case wordbeg:
+    case wordend:
+    case symbeg:
+    case symend:
+      /* This false doesn't mean failure but rather "not succeeded yet".  */
+      return false;
 
+    default:
+#if ENABLE_CHECKING
+      abort (); /* We have listed all the cases.  */
+#endif
+      return false;
+    }
+}
 
-       case set_number_at:
-         p += 4;
-         continue;
+static bool
+analyze_first_null (const re_char *p, void *arg)
+{
+  switch (*p)
+    {
+    case succeed:
+      /* This is safe: we can't reach `pend` at all from here.  */
+      return true;
 
+    case duplicate:
+      /* Either `duplicate` ends up matching a non-empty string, in which
+         case we're good, or it matches the empty string, in which case we
+         need to continue checking the rest of this path, which is exactly
+         what returning `true` does, here.  */
+      return true;
 
-       case start_memory:
-       case stop_memory:
-         p += 1;
-         continue;
+    case exactn:
+    case anychar:
+    case charset_not:
+    case charset:
+    case syntaxspec:
+    case notsyntaxspec:
+    case categoryspec:
+    case notcategoryspec:
+      return true;
 
+    case endline:
+    case endbuf:
+    case wordbeg:
+    case wordend:
+    case symbeg:
+    case symend:
+      /* This false doesn't mean failure but rather "not succeeded yet".  */
+      return false;
 
-       default:
-         abort (); /* We have listed all the cases.  */
-       } /* switch *p++ */
+    default:
+#if ENABLE_CHECKING
+      abort (); /* We have listed all the cases.  */
+#endif
+      return false;
+    }
+}
 
-      /* Getting here means we have found the possible starting
-        characters for one path of the pattern -- and that the empty
-        string does not match.  We need not follow this path further.  */
-      return 0;
-    } /* while p */
+/* analyze_first.
+   If fastmap is non-NULL, go through the pattern and fill fastmap
+   with all the possible leading chars.  If fastmap is NULL, don't
+   bother filling it up (obviously) and only return whether the
+   pattern could potentially match the empty string.
+
+   Return false if p matches at least one char before reaching pend.
+   Return true  if p..pend might match the empty string
+                or if fastmap was not updated accurately.  */
 
-  /* We reached the end without matching anything.  */
-  return 1;
+static bool
+analyze_first (struct re_pattern_buffer *bufp,
+               re_char *p, re_char *pend, char *fastmap)
+{
+  eassert (pend);
+  struct anafirst_data data = { bufp ? bufp->multibyte : false,
+                                fastmap, false };
+  bool safe = forall_firstchar (bufp->used ? bufp : NULL, p, pend,
+                                fastmap ? analyze_first_fastmap
+                                : analyze_first_null,
+                                &data);
+  return !safe;
+}
 
-} /* analyze_first */
 
 /* Compute a fastmap for the compiled pattern in BUFP.
    A fastmap records which of the (1 << BYTEWIDTH) possible
@@ -3122,7 +3305,6 @@ static void
 re_compile_fastmap (struct re_pattern_buffer *bufp)
 {
   char *fastmap = bufp->fastmap;
-  int analysis;
 
   eassert (fastmap && bufp->buffer);
 
@@ -3131,9 +3313,8 @@ re_compile_fastmap (struct re_pattern_buffer *bufp)
   /* FIXME: Is the following assignment correct even when ANALYSIS < 0?  */
   bufp->fastmap_accurate = 1;      /* It will be when we're done.  */
 
-  analysis = analyze_first (bufp->buffer, bufp->buffer + bufp->used,
-                           fastmap, RE_MULTIBYTE_P (bufp));
-  bufp->can_be_null = (analysis != 0);
+  bufp->can_be_null = analyze_first (bufp, bufp->buffer,
+                                    bufp->buffer + bufp->used, fastmap);
 } /* re_compile_fastmap */
 
 /* Set REGS to hold NUM_REGS registers, storing them in STARTS and
@@ -3171,7 +3352,7 @@ re_set_registers (struct re_pattern_buffer *bufp, struct 
re_registers *regs,
 /* Searching routines.  */
 
 /* Like re_search_2, below, but only one string is specified, and
-   doesn't let you say where to stop matching. */
+   doesn't let you say where to stop matching.  */
 
 ptrdiff_t
 re_search (struct re_pattern_buffer *bufp, const char *string, ptrdiff_t size,
@@ -3548,33 +3729,6 @@ skip_one_char (re_char *p)
 }
 
 
-/* Jump over non-matching operations.  */
-static re_char *
-skip_noops (re_char *p, re_char *pend)
-{
-  int mcnt;
-  while (p < pend)
-    {
-      switch (*p)
-       {
-       case start_memory:
-       case stop_memory:
-         p += 2; break;
-       case no_op:
-         p += 1; break;
-       case jump:
-         p += 1;
-         EXTRACT_NUMBER_AND_INCR (mcnt, p);
-         p += mcnt;
-         break;
-       default:
-         return p;
-       }
-    }
-  eassert (p == pend);
-  return p;
-}
-
 /* Test if C matches charset op.  *PP points to the charset or charset_not
    opcode.  When the function finishes, *PP will be advanced past that opcode.
    C is character to test (possibly after translations) and CORIG is original
@@ -3643,92 +3797,55 @@ execute_charset (re_char **pp, int c, int corig, bool 
unibyte,
   return not;
 }
 
-/* True if "p1 matches something" implies "p2 fails".  */
+/* Case where `p2` points to an `exactn` or `endline`.  */
 static bool
-mutually_exclusive_p (struct re_pattern_buffer *bufp, re_char *p1,
-                     re_char *p2)
+mutually_exclusive_exactn (struct re_pattern_buffer *bufp, re_char *p1,
+                          re_char *p2)
 {
-  re_opcode_t op2;
   bool multibyte = RE_MULTIBYTE_P (bufp);
-  unsigned char *pend = bufp->buffer + bufp->used;
-  re_char *p2_orig = p2;
+  int c
+    = (re_opcode_t) *p2 == endline ? '\n'
+      : RE_STRING_CHAR (p2 + 2, multibyte);
 
-  eassert (p1 >= bufp->buffer && p1 < pend
-          && p2 >= bufp->buffer && p2 <= pend);
-
-  /* Skip over open/close-group commands.
-     If what follows this loop is a ...+ construct,
-     look at what begins its body, since we will have to
-     match at least one of that.  */
-  p2 = skip_noops (p2, pend);
-  /* The same skip can be done for p1, except that this function
-     is only used in the case where p1 is a simple match operator.  */
-  /* p1 = skip_noops (p1, pend); */
-
-  eassert (p1 >= bufp->buffer && p1 < pend
-          && p2 >= bufp->buffer && p2 <= pend);
-
-  op2 = p2 == pend ? succeed : *p2;
-
-  switch (op2)
+  if ((re_opcode_t) *p1 == exactn)
     {
-    case succeed:
-    case endbuf:
-      /* If we're at the end of the pattern, we can change.  */
-      if (skip_one_char (p1))
+      if (c != RE_STRING_CHAR (p1 + 2, multibyte))
        {
-         DEBUG_PRINT ("  End of pattern: fast loop.\n");
+         DEBUG_PRINT ("  '%c' != '%c' => fast loop.\n", c, p1[2]);
          return true;
        }
-      break;
-
-    case endline:
-    case exactn:
-      {
-       int c
-         = (re_opcode_t) *p2 == endline ? '\n'
-         : RE_STRING_CHAR (p2 + 2, multibyte);
-
-       if ((re_opcode_t) *p1 == exactn)
-         {
-           if (c != RE_STRING_CHAR (p1 + 2, multibyte))
-             {
-               DEBUG_PRINT ("  '%c' != '%c' => fast loop.\n", c, p1[2]);
-               return true;
-             }
-         }
-
-       else if ((re_opcode_t) *p1 == charset
-                || (re_opcode_t) *p1 == charset_not)
-         {
-           if (!execute_charset (&p1, c, c, !multibyte || ASCII_CHAR_P (c),
-                                  Qnil))
-             {
-               DEBUG_PRINT ("   No match => fast loop.\n");
-               return true;
-             }
-         }
-       else if ((re_opcode_t) *p1 == anychar
-                && c == '\n')
-         {
-           DEBUG_PRINT ("   . != \\n => fast loop.\n");
-           return true;
-         }
-      }
-      break;
+    }
 
-    case charset:
-      {
-       if ((re_opcode_t) *p1 == exactn)
-         /* Reuse the code above.  */
-         return mutually_exclusive_p (bufp, p2, p1);
-
-      /* It is hard to list up all the character in charset
-        P2 if it includes multibyte character.  Give up in
-        such case.  */
-      else if (!multibyte || !CHARSET_RANGE_TABLE_EXISTS_P (p2))
+  else if ((re_opcode_t) *p1 == charset
+          || (re_opcode_t) *p1 == charset_not)
+    {
+      if (!execute_charset (&p1, c, c, !multibyte || ASCII_CHAR_P (c),
+                            Qnil))
        {
-         /* Now, we are sure that P2 has no range table.
+         DEBUG_PRINT ("         No match => fast loop.\n");
+         return true;
+       }
+    }
+  else if ((re_opcode_t) *p1 == anychar
+          && c == '\n')
+    {
+      DEBUG_PRINT ("   . != \\n => fast loop.\n");
+      return true;
+    }
+  return false;
+}
+
+/* Case where `p2` points to an `charset`.  */
+static bool
+mutually_exclusive_charset (struct re_pattern_buffer *bufp, re_char *p1,
+                           re_char *p2)
+{
+  /* It is hard to list up all the character in charset
+     P2 if it includes multibyte character.  Give up in
+     such case.  */
+  if (!RE_MULTIBYTE_P (bufp) || !CHARSET_RANGE_TABLE_EXISTS_P (p2))
+    {
+      /* Now, we are sure that P2 has no range table.
             So, for the size of bitmap in P2, 'p2[1]' is
             enough.  But P1 may have range table, so the
             size of bitmap table of P1 is extracted by
@@ -3739,113 +3856,154 @@ mutually_exclusive_p (struct re_pattern_buffer *bufp, 
re_char *p1,
             bitmap table.  So, in both cases, it is enough to test
             only the bitmap table of P1.  */
 
-         if ((re_opcode_t) *p1 == charset)
-           {
-             int idx;
-             /* We win if the charset inside the loop
+      if ((re_opcode_t) *p1 == charset)
+       {
+         int idx;
+         /* We win if the charset inside the loop
                 has no overlap with the one after the loop.  */
-             for (idx = 0;
-                  (idx < (int) p2[1]
-                   && idx < CHARSET_BITMAP_SIZE (p1));
-                  idx++)
-               if ((p2[2 + idx] & p1[2 + idx]) != 0)
-                 break;
+         for (idx = 0;
+               (idx < (int) p2[1]
+                && idx < CHARSET_BITMAP_SIZE (p1));
+               idx++)
+           if ((p2[2 + idx] & p1[2 + idx]) != 0)
+             break;
 
-             if (idx == p2[1]
-                 || idx == CHARSET_BITMAP_SIZE (p1))
-               {
-                 DEBUG_PRINT ("         No match => fast loop.\n");
-                 return true;
-               }
-           }
-         else if ((re_opcode_t) *p1 == charset_not)
+         if (idx == p2[1]
+             || idx == CHARSET_BITMAP_SIZE (p1))
            {
-             int idx;
-             /* We win if the charset_not inside the loop lists
+             DEBUG_PRINT ("     No match => fast loop.\n");
+             return true;
+           }
+       }
+      else if ((re_opcode_t) *p1 == charset_not)
+       {
+         int idx;
+         /* We win if the charset_not inside the loop lists
                 every character listed in the charset after.  */
-             for (idx = 0; idx < (int) p2[1]; idx++)
-               if (! (p2[2 + idx] == 0
-                      || (idx < CHARSET_BITMAP_SIZE (p1)
-                          && ((p2[2 + idx] & ~ p1[2 + idx]) == 0))))
-                 break;
+         for (idx = 0; idx < (int) p2[1]; idx++)
+           if (! (p2[2 + idx] == 0
+                  || (idx < CHARSET_BITMAP_SIZE (p1)
+                      && ((p2[2 + idx] & ~ p1[2 + idx]) == 0))))
+             break;
 
-             if (idx == p2[1])
-               {
-                 DEBUG_PRINT ("         No match => fast loop.\n");
-                 return true;
-               }
-             }
-         }
+         if (idx == p2[1])
+           {
+             DEBUG_PRINT ("     No match => fast loop.\n");
+             return true;
+           }
+       }
+    }
+  return false;
+}
+
+struct mutexcl_data {
+  struct re_pattern_buffer *bufp;
+  re_char *p1;
+  bool unconstrained;
+};
+
+static bool
+mutually_exclusive_one (re_char *p2, void *arg)
+{
+  struct mutexcl_data *data = arg;
+  switch (*p2)
+    {
+    case succeed:
+      /* If `p1` matches, `succeed` can still match, so we should return
+         `false`.  *BUT* when N iterations of `p1` and N+1 iterations of `p1`
+         match, the `succeed` that comes after N+1 always takes precedence
+         over the one after N because we always prefer a longer match, so
+         the succeed after N can actually be replaced by a "fail" without
+         changing the end result.
+         In this sense, "if `p1` matches, `succeed` can't match".
+         So we can return `true`.
+         *BUT* this only holds if we're sure that the N+1 will indeed succeed,
+         so we need to make sure there is no other matching operator between
+         the exit of the iteration and the `succeed`.  */
+      return data->unconstrained;
+
+/* Remember that there may be an empty matching operator on the way.
+   If we return true, this is the "end" of this control flow path,
+   so it can't get in the way of a subsequent `succeed.  */
+#define RETURN_CONSTRAIN(v)        \
+  { bool tmp = (v);                \
+    if (!tmp)                      \
+      data->unconstrained = false; \
+    return tmp;                    \
+  }
+
+    case endline:
+      RETURN_CONSTRAIN (mutually_exclusive_exactn (data->bufp, data->p1, p2));
+    case exactn:
+      return mutually_exclusive_exactn (data->bufp, data->p1, p2);
+    case charset:
+      {
+       if (*data->p1 == exactn)
+         return mutually_exclusive_exactn (data->bufp, p2, data->p1);
+       else
+         return mutually_exclusive_charset (data->bufp, data->p1, p2);
       }
-      break;
 
     case charset_not:
-      switch (*p1)
+      switch (*data->p1)
        {
        case exactn:
+         return mutually_exclusive_exactn (data->bufp, p2, data->p1);
        case charset:
-         /* Reuse the code above.  */
-         return mutually_exclusive_p (bufp, p2, p1);
+         return mutually_exclusive_charset (data->bufp, p2, data->p1);
        case charset_not:
          /* When we have two charset_not, it's very unlikely that
             they don't overlap.  The union of the two sets of excluded
             chars should cover all possible chars, which, as a matter of
             fact, is virtually impossible in multibyte buffers.  */
-         break;
+         return false;
        }
-      break;
-
-    case wordend:
-      return ((re_opcode_t) *p1 == syntaxspec && p1[1] == Sword);
-    case symend:
-      return ((re_opcode_t) *p1 == syntaxspec
-              && (p1[1] == Ssymbol || p1[1] == Sword));
+      return false;
+    case anychar:
+      return false;             /* FIXME: exactn \n ? */
+    case syntaxspec:
+      return (*data->p1 == notsyntaxspec && data->p1[1] == p2[1]);
     case notsyntaxspec:
-      return ((re_opcode_t) *p1 == syntaxspec && p1[1] == p2[1]);
+      return (*data->p1 == syntaxspec && data->p1[1] == p2[1]);
+    case categoryspec:
+      return (*data->p1 == notcategoryspec && data->p1[1] == p2[1]);
+    case notcategoryspec:
+      return (*data->p1 == categoryspec && data->p1[1] == p2[1]);
 
+    case endbuf:
+      return true;
     case wordbeg:
-      return ((re_opcode_t) *p1 == notsyntaxspec && p1[1] == Sword);
+      RETURN_CONSTRAIN (*data->p1 == notsyntaxspec && data->p1[1] == Sword);
+    case wordend:
+      RETURN_CONSTRAIN (*data->p1 == syntaxspec && data->p1[1] == Sword);
     case symbeg:
-      return ((re_opcode_t) *p1 == notsyntaxspec
-              && (p1[1] == Ssymbol || p1[1] == Sword));
-    case syntaxspec:
-      return ((re_opcode_t) *p1 == notsyntaxspec && p1[1] == p2[1]);
-
-    case wordbound:
-      return (((re_opcode_t) *p1 == notsyntaxspec
-              || (re_opcode_t) *p1 == syntaxspec)
-             && p1[1] == Sword);
-
-    case categoryspec:
-      return ((re_opcode_t) *p1 == notcategoryspec && p1[1] == p2[1]);
-    case notcategoryspec:
-      return ((re_opcode_t) *p1 == categoryspec && p1[1] == p2[1]);
+      RETURN_CONSTRAIN (*data->p1 == notsyntaxspec
+                        && (data->p1[1] == Ssymbol || data->p1[1] == Sword));
+    case symend:
+      RETURN_CONSTRAIN (*data->p1 == syntaxspec
+                        && (data->p1[1] == Ssymbol || data->p1[1] == Sword));
 
-    case on_failure_jump_nastyloop:
-    case on_failure_jump_smart:
-    case on_failure_jump_loop:
-    case on_failure_keep_string_jump:
-    case on_failure_jump:
-      {
-        int mcnt;
-       p2++;
-       EXTRACT_NUMBER_AND_INCR (mcnt, p2);
-       /* Don't just test `mcnt > 0` because non-greedy loops have
-          their test at the end with an unconditional jump at the start.  */
-       if (p2 + mcnt > p2_orig) /* Ensure forward progress.  */
-         return (mutually_exclusive_p (bufp, p1, p2)
-                 && mutually_exclusive_p (bufp, p1, p2 + mcnt));
-       break;
-      }
+    case duplicate:
+      /* At this point, we know nothing about what this can match, sadly.  */
+      return false;
 
     default:
-      ;
+#if ENABLE_CHECKING
+      abort (); /* We have listed all the cases.  */
+#endif
+      return false;
     }
-
-  /* Safe default.  */
-  return false;
 }
 
+/* True if "p1 matches something" implies "p2 fails".  */
+
+static bool
+mutually_exclusive_p (struct re_pattern_buffer *bufp, re_char *p1,
+                     re_char *p2)
+{
+  struct mutexcl_data data = { bufp, p1, true };
+  return forall_firstchar (bufp, p2, NULL, mutually_exclusive_one, &data);
+}
 
 /* Matching routines.  */
 
@@ -3974,6 +4132,9 @@ re_match_2_internal (struct re_pattern_buffer *bufp,
   /* This keeps track of how many buffer/string positions we examined.  */
   ptrdiff_t nchars = 0;
 
+  /* Final return value of the function.  */
+  ptrdiff_t retval = -1;        /* Presumes failure to match for now.  */
+
 #ifdef DEBUG_COMPILES_ARGUMENTS
   /* Counts the total number of registers pushed.  */
   ptrdiff_t num_regs_pushed = 0;
@@ -4228,15 +4389,8 @@ re_match_2_internal (struct re_pattern_buffer *bufp,
 
          DEBUG_PRINT ("Returning %td from re_match_2.\n", dcnt);
 
-         unbind_to (count, Qnil);
-         SAFE_FREE ();
-         /* The factor of 50 below is a heuristic that needs to be tuned.  It
-            means we consider 50 buffer positions examined by this function
-            roughly equivalent to the display engine iterating over a single
-            buffer position.  */
-         if (max_redisplay_ticks > 0 && nchars > 0)
-           update_redisplay_ticks (nchars / 50 + 1, NULL);
-         return dcnt;
+         retval = dcnt;
+         goto endof_re_match;
        }
 
       /* Otherwise match next pattern command.  */
@@ -4729,7 +4883,7 @@ re_match_2_internal (struct re_pattern_buffer *bufp,
 
 
        /* Have to succeed matching what follows at least n times.
-          After that, handle like 'on_failure_jump'.  */
+          After that, handle like 'on_failure_jump_loop'.  */
        case succeed_n:
          /* Signedness doesn't matter since we only compare MCNT to 0.  */
          EXTRACT_NUMBER (mcnt, p + 2);
@@ -5077,8 +5231,7 @@ re_match_2_internal (struct re_pattern_buffer *bufp,
            case succeed_n:
              d = str;
            continue_failure_jump:
-             EXTRACT_NUMBER_AND_INCR (mcnt, pat);
-             p = pat + mcnt;
+             p = extract_address (pat);
              break;
 
            case no_op:
@@ -5101,13 +5254,18 @@ re_match_2_internal (struct re_pattern_buffer *bufp,
   if (best_regs_set)
     goto restore_best_regs;
 
+endof_re_match:
   unbind_to (count, Qnil);
   SAFE_FREE ();
 
+  /* The factor of 50 below is a heuristic that needs to be tuned.
+     It means we consider 50 buffer positions examined by this function
+     roughly equivalent to the display engine iterating over a single
+     buffer position.  */
   if (max_redisplay_ticks > 0 && nchars > 0)
     update_redisplay_ticks (nchars / 50 + 1, NULL);
 
-  return -1;                           /* Failure to match.  */
+  return retval;
 }
 
 /* Subroutine definitions for re_match_2.  */
diff --git a/src/regex-emacs.h b/src/regex-emacs.h
index bc357633135..d9adcc69443 100644
--- a/src/regex-emacs.h
+++ b/src/regex-emacs.h
@@ -195,4 +195,8 @@ extern bool re_iswctype (int ch, re_wctype_t cc);
 extern re_wctype_t re_wctype_parse (const unsigned char **strp,
                                    ptrdiff_t limit);
 
+#if ENABLE_CHECKING
+extern void print_compiled_pattern (FILE *dest, struct re_pattern_buffer 
*bufp);
+#endif
+
 #endif /* EMACS_REGEX_H */
diff --git a/src/search.c b/src/search.c
index 742a78cb0cd..e9b29bb7179 100644
--- a/src/search.c
+++ b/src/search.c
@@ -3376,6 +3376,46 @@ the buffer.  If the buffer doesn't have a cache, the 
value is nil.  */)
     set_buffer_internal_1 (old);
   return val;
 }
+
+DEFUN ("re--describe-compiled", Fre__describe_compiled, Sre__describe_compiled,
+       1, 2, 0,
+       doc: /* Return a string describing the compiled form of REGEXP.
+If RAW is non-nil, just return the actual bytecode.  */)
+  (Lisp_Object regexp, Lisp_Object raw)
+{
+  struct regexp_cache *cache_entry
+    = compile_pattern (regexp, NULL,
+                       (!NILP (BVAR (current_buffer, case_fold_search))
+                        ? BVAR (current_buffer, case_canon_table) : Qnil),
+                       false,
+                       !NILP (BVAR (current_buffer,
+                                    enable_multibyte_characters)));
+  if (!NILP (raw))
+    return make_unibyte_string ((char *) cache_entry->buf.buffer,
+                                cache_entry->buf.used);
+  else
+    {                           /* FIXME: Why ENABLE_CHECKING?  */
+#if !defined ENABLE_CHECKING
+      error ("Not available: rebuild with --enable-checking");
+#elif HAVE_OPEN_MEMSTREAM
+      char *buffer = NULL;
+      size_t size = 0;
+      FILE* f = open_memstream (&buffer, &size);
+      if (!f)
+        report_file_error ("open_memstream failed", regexp);
+      print_compiled_pattern (f, &cache_entry->buf);
+      fclose (f);
+      if (!buffer)
+        return Qnil;
+      Lisp_Object description = make_unibyte_string (buffer, size);
+      free (buffer);
+      return description;
+#else /* ENABLE_CHECKING && !HAVE_OPEN_MEMSTREAM */
+      print_compiled_pattern (stderr, &cache_entry->buf);
+      return build_string ("Description was sent to standard error");
+#endif /* !ENABLE_CHECKING */
+    }
+}
 
 
 static void syms_of_search_for_pdumper (void);
@@ -3455,6 +3495,7 @@ is to bind it with `let' around a small expression.  */);
   defsubr (&Smatch_data__translate);
   defsubr (&Sregexp_quote);
   defsubr (&Snewline_cache_check);
+  defsubr (&Sre__describe_compiled);
 
   pdumper_do_now_and_after_load (syms_of_search_for_pdumper);
 }
diff --git a/src/sfnt.c b/src/sfnt.c
index b66613eaa53..360b0cd2d4d 100644
--- a/src/sfnt.c
+++ b/src/sfnt.c
@@ -93,6 +93,9 @@ xfree (void *ptr)
 /* Needed for tests.  */
 #define ARRAYELTS(arr) (sizeof (arr) / sizeof (arr)[0])
 
+/* Also necessary.  */
+#define AVOID _Noreturn ATTRIBUTE_COLD void
+
 #else
 #define TEST_STATIC
 #include "lisp.h"
@@ -154,6 +157,7 @@ static uint32_t sfnt_table_names[] =
     [SFNT_TABLE_GVAR] = 0x67766172,
     [SFNT_TABLE_CVAR] = 0x63766172,
     [SFNT_TABLE_AVAR] = 0x61766172,
+    [SFNT_TABLE_OS_2] = 0x4f532f32,
   };
 
 /* Swap values from TrueType to system byte order.  */
@@ -198,9 +202,9 @@ sfnt_read_table_directory (int fd)
                       range_shift, uint16_t);
   rc = read (fd, subtable, offset);
 
-  if (rc < offset)
+  if (rc == -1 || rc < offset)
     {
-      if (rc >= sizeof (uint32_t))
+      if (rc != -1 && rc >= sizeof (uint32_t))
        {
          /* Detect a TTC file.  In that case, the first long will be
             ``ttcf''.  */
@@ -243,7 +247,7 @@ sfnt_read_table_directory (int fd)
 
   rc = read (fd, subtable->subtables, subtable_size);
 
-  if (rc < offset)
+  if (rc == -1 || rc < offset)
     {
       xfree (subtable);
       return NULL;
@@ -307,7 +311,7 @@ sfnt_read_cmap_format_0 (int fd,
                             language));
   rc = read (fd, &format0->language, wanted_size);
 
-  if (rc < wanted_size)
+  if (rc == -1 || rc < wanted_size)
     {
       xfree (format0);
       return (struct sfnt_cmap_format_0 *) -1;
@@ -345,7 +349,7 @@ sfnt_read_cmap_format_2 (int fd,
   /* Read the part before the variable length data.  */
   min_bytes -= offsetof (struct sfnt_cmap_format_2, language);
   rc = read (fd, &format2->language, min_bytes);
-  if (rc < min_bytes)
+  if (rc == -1 || rc < min_bytes)
     {
       xfree (format2);
       return (struct sfnt_cmap_format_2 *) -1;
@@ -379,7 +383,7 @@ sfnt_read_cmap_format_2 (int fd,
               - SFNT_ENDOF (struct sfnt_cmap_format_2,
                             sub_header_keys, uint16_t[256]));
   rc = read (fd, format2 + 1, min_bytes);
-  if (rc < min_bytes)
+  if (rc == -1 || rc < min_bytes)
     {
       xfree (format2);
       return (struct sfnt_cmap_format_2 *) -1;
@@ -450,7 +454,7 @@ sfnt_read_cmap_format_4 (int fd,
   /* Read the initial data.  */
   min_bytes -= offsetof (struct sfnt_cmap_format_4, language);
   rc = read (fd, &format4->language, min_bytes);
-  if (rc < min_bytes)
+  if (rc == -1 || rc < min_bytes)
     {
       xfree (format4);
       return (struct sfnt_cmap_format_4 *) -1;
@@ -486,7 +490,7 @@ sfnt_read_cmap_format_4 (int fd,
 
   /* Read the rest of the bytes to the end of format4.  */
   rc = read (fd, format4 + 1, bytes_minus_format4);
-  if (rc < bytes_minus_format4)
+  if (rc == -1 || rc < bytes_minus_format4)
     {
       xfree (format4);
       return (struct sfnt_cmap_format_4 *) -1;
@@ -555,7 +559,7 @@ sfnt_read_cmap_format_6 (int fd,
   /* Read the fixed size data.  */
   min_size -= offsetof (struct sfnt_cmap_format_6, language);
   rc = read (fd, &format6->language, min_size);
-  if (rc < min_size)
+  if (rc == -1 || rc < min_size)
     {
       xfree (format6);
       return (struct sfnt_cmap_format_6 *) -1;
@@ -579,7 +583,8 @@ sfnt_read_cmap_format_6 (int fd,
   rc = read (fd, format6 + 1,
             (format6->entry_count
              * sizeof *format6->glyph_index_array));
-  if (rc < format6->entry_count * sizeof *format6->glyph_index_array)
+  if (rc == -1 || (rc < (format6->entry_count
+                        * sizeof *format6->glyph_index_array)))
     {
       xfree (format6);
       return (struct sfnt_cmap_format_6 *) -1;
@@ -607,7 +612,7 @@ sfnt_read_cmap_format_8 (int fd,
   uint32_t length, i;
 
   /* Read the 32-bit length field.  */
-  if (read (fd, &length, sizeof (length)) < sizeof (length))
+  if (read (fd, &length, sizeof length) < (int) sizeof length)
     return (struct sfnt_cmap_format_8 *) -1;
 
   /* Swap the 32-bit length field.  */
@@ -629,7 +634,7 @@ sfnt_read_cmap_format_8 (int fd,
   /* Read the fixed length data.  */
   min_size -= offsetof (struct sfnt_cmap_format_8, language);
   rc = read (fd, &format8->language, min_size);
-  if (rc < min_size)
+  if (rc == -1 || rc < min_size)
     {
       xfree (format8);
       return (struct sfnt_cmap_format_8 *) -1;
@@ -665,7 +670,7 @@ sfnt_read_cmap_format_8 (int fd,
 
   /* Now read the variable length data.  */
   rc = read (fd, format8 + 1, temp);
-  if (rc < temp)
+  if (rc == -1 || rc < temp)
     {
       xfree (format8);
       return (struct sfnt_cmap_format_8 *) -1;
@@ -699,7 +704,7 @@ sfnt_read_cmap_format_12 (int fd,
   uint32_t length, i;
 
   /* Read the 32-bit length field.  */
-  if (read (fd, &length, sizeof (length)) < sizeof (length))
+  if (read (fd, &length, sizeof length) < (int) sizeof length)
     return (struct sfnt_cmap_format_12 *) -1;
 
   /* Swap the 32-bit length field.  */
@@ -721,7 +726,7 @@ sfnt_read_cmap_format_12 (int fd,
   /* Read the fixed length data.  */
   min_size -= offsetof (struct sfnt_cmap_format_12, language);
   rc = read (fd, &format12->language, min_size);
-  if (rc < min_size)
+  if (rc == -1 || rc < min_size)
     {
       xfree (format12);
       return (struct sfnt_cmap_format_12 *) -1;
@@ -757,7 +762,7 @@ sfnt_read_cmap_format_12 (int fd,
 
   /* Now read the variable length data.  */
   rc = read (fd, format12 + 1, temp);
-  if (rc < temp)
+  if (rc == -1 || rc < temp)
     {
       xfree (format12);
       return (struct sfnt_cmap_format_12 *) -1;
@@ -804,12 +809,12 @@ sfnt_read_cmap_format_14 (int fd,
   uint32_t buffer1[2];
   size_t size, temp;
   char buffer[3 + 4 + 4];
-  int i;
+  uint32_t i;
 
   /* Read the length field and number of variation selector
      records.  */
 
-  if (read (fd, buffer1, sizeof buffer1) < sizeof buffer1)
+  if (read (fd, buffer1, sizeof buffer1) < (int) sizeof buffer1)
     return NULL;
 
   length = buffer1[0];
@@ -843,7 +848,7 @@ sfnt_read_cmap_format_14 (int fd,
 
   for (i = 0; i < num_records; ++i)
     {
-      if (read (fd, buffer, sizeof buffer) < sizeof buffer)
+      if (read (fd, buffer, sizeof buffer) < (int) sizeof buffer)
        {
          xfree (format14);
          return NULL;
@@ -889,7 +894,7 @@ sfnt_read_cmap_table_1 (int fd, uint32_t directory_offset,
     return (struct sfnt_cmap_encoding_subtable_data *) -1;
 
   if (read (fd, &header.format, sizeof header.format)
-      < sizeof header.format)
+      < (int) sizeof header.format)
     return (struct sfnt_cmap_encoding_subtable_data *) -1;
 
   sfnt_swap16 (&header.format);
@@ -901,7 +906,7 @@ sfnt_read_cmap_table_1 (int fd, uint32_t directory_offset,
   if (header.format != 14)
     {
       if (read (fd, &header.length, sizeof header.length)
-         < sizeof header.length)
+         < (int) sizeof header.length)
        return (struct sfnt_cmap_encoding_subtable_data *) -1;
 
       sfnt_swap16 (&header.length);
@@ -980,7 +985,7 @@ sfnt_read_cmap_table (int fd, struct sfnt_offset_subtable 
*subtable,
   cmap = xmalloc (sizeof *cmap);
   rc = read (fd, cmap, sizeof *cmap);
 
-  if (rc < sizeof *cmap)
+  if (rc < (int) sizeof *cmap)
     {
       xfree (cmap);
       return NULL;
@@ -1007,7 +1012,7 @@ sfnt_read_cmap_table (int fd, struct sfnt_offset_subtable 
*subtable,
       /* Read the common part of the new subtable.  */
       rc = read (fd, &(*subtables)[i], sizeof (*subtables)[i]);
 
-      if (rc < sizeof (*subtables)[i])
+      if (rc < (int) sizeof (*subtables)[i])
        {
          xfree (cmap);
          xfree (*subtables);
@@ -1426,7 +1431,7 @@ sfnt_read_head_table (int fd, struct sfnt_offset_subtable 
*subtable)
   head = xmalloc (sizeof *head);
   rc = read (fd, head, sizeof *head);
 
-  if (rc < sizeof *head)
+  if (rc < (int) sizeof *head)
     {
       xfree (head);
       return NULL;
@@ -1502,7 +1507,7 @@ sfnt_read_hhea_table (int fd, struct sfnt_offset_subtable 
*subtable)
   hhea = xmalloc (sizeof *hhea);
   rc = read (fd, hhea, sizeof *hhea);
 
-  if (rc < sizeof *hhea)
+  if (rc < (int) sizeof *hhea)
     {
       xfree (hhea);
       return NULL;
@@ -1665,7 +1670,7 @@ sfnt_read_maxp_table (int fd, struct sfnt_offset_subtable 
*subtable)
   size = MIN (directory->length, sizeof *maxp);
   rc = read (fd, maxp, size);
 
-  if (rc < size)
+  if (rc == -1 || rc < size)
     {
       xfree (maxp);
       return NULL;
@@ -1745,7 +1750,7 @@ sfnt_read_glyf_table (int fd, struct sfnt_offset_subtable 
*subtable)
 
   /* Read the glyph data.  */
   rc = read (fd, glyf->glyphs, glyf->size);
-  if (rc < glyf->size)
+  if (rc == -1 || rc < glyf->size)
     {
       xfree (glyf);
       return NULL;
@@ -2944,16 +2949,11 @@ sfnt_decompose_compound_glyph (struct sfnt_glyph *glyph,
          /* When an anchor point is being used to translate the
             glyph, and the subglyph in question is actually a
             compound glyph, it is impossible to know which offset to
-            use until the compound subglyph has actually been
-            loaded.
+            use until the compound subglyph has actually been loaded.
 
-            As a result, the offset is calculated here, using the
-            points in the loaded child compound glyph.  But first, X
-            and Y must be reset to 0, as otherwise the translation
-            might be applied twice if defer_offsets is not set.  */
-
-         x = 0;
-         y = 0;
+            defer_offsets is set to true if these conditions apply,
+            whereupon the offset is calculated here, using the points
+            in the loaded child compound glyph.  */
 
          if (defer_offsets)
            {
@@ -4638,7 +4638,7 @@ sfnt_read_hmtx_table (int fd, struct sfnt_offset_subtable 
*subtable,
 
   /* Read into hmtx + 1.  */
   rc = read (fd, hmtx + 1, size);
-  if (rc < size)
+  if (rc == -1 || rc < size)
     {
       xfree (hmtx);
       return NULL;
@@ -4802,7 +4802,7 @@ sfnt_read_name_table (int fd, struct sfnt_offset_subtable 
*subtable)
 
   /* Read the fixed length data.  */
   rc = read (fd, name, required);
-  if (rc < required)
+  if (rc == -1 || rc < required)
     {
       xfree (name);
       return NULL;
@@ -4836,8 +4836,8 @@ sfnt_read_name_table (int fd, struct sfnt_offset_subtable 
*subtable)
   rc = read (fd, name->name_records,
             (name->count
              * sizeof *name->name_records));
-  if (rc < (name->count
-           * sizeof *name->name_records))
+  if (rc == -1 || (rc < (name->count
+                        * sizeof *name->name_records)))
     {
       xfree (name);
       return NULL;
@@ -4893,7 +4893,7 @@ sfnt_read_name_table (int fd, struct sfnt_offset_subtable 
*subtable)
   name->data = (unsigned char *) (name->name_records
                                  + name->count);
   rc = read (fd, name->data, required);
-  if (rc < required)
+  if (rc == -1 || rc < required)
     {
       xfree (name);
       return NULL;
@@ -4975,7 +4975,7 @@ sfnt_read_meta_table (int fd, struct sfnt_offset_subtable 
*subtable)
 
   /* Read the header.  */
   rc = read (fd, meta, required);
-  if (rc < required)
+  if (rc == -1 || rc < required)
     {
       xfree (meta);
       return NULL;
@@ -5121,7 +5121,7 @@ sfnt_read_ttc_header (int fd)
   size = SFNT_ENDOF (struct sfnt_ttc_header, num_fonts,
                     uint32_t);
   rc = read (fd, ttc, size);
-  if (rc < size)
+  if (rc == -1 || rc < size)
     {
       xfree (ttc);
       return NULL;
@@ -5153,7 +5153,7 @@ sfnt_read_ttc_header (int fd)
   ttc = xrealloc (ttc, sizeof *ttc + size);
   ttc->offset_table = (uint32_t *) (ttc + 1);
   rc = read (fd, ttc->offset_table, size);
-  if (rc < size)
+  if (rc == -1 || rc < size)
     {
       xfree (ttc);
       return NULL;
@@ -5176,7 +5176,7 @@ sfnt_read_ttc_header (int fd)
                      uint32_t)
          - offsetof (struct sfnt_ttc_header, ul_dsig_tag));
   rc = read (fd, &ttc->ul_dsig_offset, size);
-  if (rc < size)
+  if (rc == -1 || rc < size)
     {
       xfree (ttc);
       return NULL;
@@ -5805,7 +5805,7 @@ enum sfnt_interpreter_run_context
    After this is called, it is probably okay to reuse INTERPRETER.
    However, instructions must always be reloaded.  */
 
-_Noreturn static void
+static AVOID
 sfnt_interpret_trap (struct sfnt_interpreter *interpreter,
                     const char *reason)
 {
@@ -12313,7 +12313,7 @@ sfnt_read_default_uvs_table (int fd, off_t offset)
 {
   struct sfnt_default_uvs_table *uvs;
   uint32_t num_ranges, i, j;
-  size_t size, temp;
+  ssize_t size, temp;
   char data[512];
 
   /* First, seek to the given offset.  */
@@ -12323,7 +12323,8 @@ sfnt_read_default_uvs_table (int fd, off_t offset)
 
   /* Next, read the number of ranges present.  */
 
-  if (read (fd, &num_ranges, sizeof num_ranges) != sizeof num_ranges)
+  if (read (fd, &num_ranges, sizeof num_ranges)
+      != (int) sizeof num_ranges)
     return NULL;
 
   /* Swap the number of ranges present.  */
@@ -12383,7 +12384,7 @@ sfnt_read_nondefault_uvs_table (int fd, off_t offset)
 {
   struct sfnt_nondefault_uvs_table *uvs;
   uint32_t num_mappings, i, j;
-  size_t size, temp;
+  ssize_t size, temp;
   char data[500];
 
   /* First, seek to the given offset.  */
@@ -12885,7 +12886,7 @@ sfnt_read_fvar_table (int fd, struct 
sfnt_offset_subtable *subtable)
   /* Read the fvar table header.  */
   buffer = NULL;
   rc = read (fd, fvar, min_bytes);
-  if (rc != min_bytes)
+  if (rc == -1 || rc != min_bytes)
     goto bail;
 
   /* Swap what was read.  */
@@ -12995,7 +12996,7 @@ sfnt_read_fvar_table (int fd, struct 
sfnt_offset_subtable *subtable)
     goto bail;
 
   rc = read (fd, fvar->axis, sizeof *fvar->axis * fvar->axis_count);
-  if (rc != sizeof *fvar->axis * fvar->axis_count)
+  if (rc == -1 || rc != sizeof *fvar->axis * fvar->axis_count)
     goto bail;
 
   /* Swap each axis.  */
@@ -13114,7 +13115,7 @@ sfnt_read_gvar_table (int fd, struct 
sfnt_offset_subtable *subtable)
 
   /* Read the gvar table header.  */
   rc = read (fd, gvar, min_bytes);
-  if (rc != min_bytes)
+  if (rc == -1 || rc != min_bytes)
     goto bail;
 
   /* Swap what was read.  */
@@ -13180,7 +13181,7 @@ sfnt_read_gvar_table (int fd, struct 
sfnt_offset_subtable *subtable)
     {
       gvar->u.offset_long = (uint32_t *) (gvar + 1);
       rc = read (fd, gvar->u.offset_long, off_size);
-      if (rc != off_size)
+      if (rc == -1 || rc != off_size)
        goto bail;
 
       for (i = 0; i <= gvar->glyph_count; ++i)
@@ -13201,8 +13202,9 @@ sfnt_read_gvar_table (int fd, struct 
sfnt_offset_subtable *subtable)
       if (lseek (fd, offset, SEEK_SET) != offset)
        goto bail;
 
-      if (read (fd, gvar->global_coords, coordinate_size)
-         != coordinate_size)
+      rc = read (fd, gvar->global_coords, coordinate_size);
+
+      if (rc == -1 || rc != coordinate_size)
        goto bail;
 
       for (i = 0; i < coordinate_size / sizeof *gvar->global_coords; ++i)
@@ -13225,8 +13227,9 @@ sfnt_read_gvar_table (int fd, struct 
sfnt_offset_subtable *subtable)
       if (lseek (fd, offset, SEEK_SET) != offset)
        goto bail;
 
-      if (read (fd, gvar->glyph_variation_data,
-               gvar->data_size) != gvar->data_size)
+      rc = read (fd, gvar->glyph_variation_data, gvar->data_size);
+
+      if (rc == -1 || rc != gvar->data_size)
        goto bail;
     }
 
@@ -13277,7 +13280,7 @@ sfnt_read_avar_table (int fd, struct 
sfnt_offset_subtable *subtable)
 
   /* Read the avar table header.  */
   rc = read (fd, avar, min_size);
-  if (rc != min_size)
+  if (rc == -1 || rc != min_size)
     goto bail;
 
   /* Swap what was read.  */
@@ -13294,7 +13297,7 @@ sfnt_read_avar_table (int fd, struct 
sfnt_offset_subtable *subtable)
   size = directory->length - min_size;
   buffer = xmalloc (size);
   rc = read (fd, buffer, size);
-  if (rc != size)
+  if (rc == -1 || rc != size)
     goto bail1;
 
   /* Swap each word.  */
@@ -13595,7 +13598,7 @@ sfnt_read_cvar_table (int fd, struct 
sfnt_offset_subtable *subtable,
   size = directory->length - min_size;
   buffer = xmalloc (size);
   rc = read (fd, buffer, size);
-  if (rc != size)
+  if (rc == -1 || rc != size)
     goto bail;
 
   /* Now figure out how large cvar must be by reading the tuples.  */
@@ -15293,6 +15296,110 @@ sfnt_vary_interpreter (struct sfnt_interpreter 
*interpreter,
 
 
 
+/* OS/2 metadata retrieval.
+
+   A font's `OS/2' table incorporates some miscellaneous information
+   that is consulted by the font scaler on MS-Windows.  Emacs requires
+   one fragment of this information: the font foundry name.  */
+
+/* Read an OS/2 table from the given font FD.  Use the table directory
+   provided in SUBTABLE.
+
+   Return the OS/2 table if successful, NULL otherwise.  */
+
+TEST_STATIC struct sfnt_OS_2_table *
+sfnt_read_OS_2_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+  struct sfnt_OS_2_table *OS_2;
+  struct sfnt_table_directory *directory;
+  ssize_t rc;
+  size_t minimum, wanted;
+
+  /* Search for the OS/2 table within SUBTABLE.  */
+
+  directory = sfnt_find_table (subtable, SFNT_TABLE_OS_2);
+
+  if (!directory)
+    return NULL;
+
+  /* Calculate how large the table must be.  The field `panose' is the
+     last field aligned to natural boundaries, and thus contents must
+     be read twice: once to populate the table with information up to
+     `panose', and once again to retrieve the information
+     afterwards.  */
+
+  minimum = (SFNT_ENDOF (struct sfnt_OS_2_table, panose,
+                        unsigned char[10])
+            + SFNT_ENDOF (struct sfnt_OS_2_table, fs_last_char_index,
+                          uint16_t)
+            - offsetof (struct sfnt_OS_2_table, ul_unicode_range));
+
+  /* If the table is too short, return.  */
+  if (directory->length < minimum)
+    return NULL;
+
+  /* Seek to the location given in the directory.  */
+  if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+    return NULL;
+
+  OS_2 = xmalloc (sizeof *OS_2);
+
+  /* Read data up to the end of `panose'.  */
+
+  wanted = SFNT_ENDOF (struct sfnt_OS_2_table, panose,
+                      unsigned char[10]);
+  rc = read (fd, OS_2, wanted);
+
+  if (rc == -1 || rc != wanted)
+    {
+      xfree (OS_2);
+      return NULL;
+    }
+
+  /* Byte swap that data.  */
+
+  sfnt_swap16 (&OS_2->version);
+  sfnt_swap16 (&OS_2->x_avg_char_width);
+  sfnt_swap16 (&OS_2->us_weight_class);
+  sfnt_swap16 (&OS_2->us_width_class);
+  sfnt_swap16 (&OS_2->fs_type);
+  sfnt_swap16 (&OS_2->y_subscript_x_size);
+  sfnt_swap16 (&OS_2->y_subscript_y_size);
+  sfnt_swap16 (&OS_2->y_subscript_x_offset);
+  sfnt_swap16 (&OS_2->y_subscript_y_offset);
+  sfnt_swap16 (&OS_2->y_superscript_x_size);
+  sfnt_swap16 (&OS_2->y_superscript_y_size);
+  sfnt_swap16 (&OS_2->y_superscript_x_offset);
+  sfnt_swap16 (&OS_2->y_superscript_y_offset);
+  sfnt_swap16 (&OS_2->y_strikeout_size);
+  sfnt_swap16 (&OS_2->y_strikeout_position);
+  sfnt_swap16 (&OS_2->s_family_class);
+
+  /* Read fields between ul_unicode_range and fs_last_char_index.  */
+  wanted = (SFNT_ENDOF (struct sfnt_OS_2_table, fs_last_char_index,
+                       uint16_t)
+           - offsetof (struct sfnt_OS_2_table, ul_unicode_range));
+  rc = read (fd, &OS_2->ul_unicode_range, wanted);
+
+  if (rc == -1 || rc != wanted)
+    {
+      xfree (OS_2);
+      return NULL;
+    }
+
+  /* Swap the remainder and return the table.  */
+  sfnt_swap32 (&OS_2->ul_unicode_range[0]);
+  sfnt_swap32 (&OS_2->ul_unicode_range[1]);
+  sfnt_swap32 (&OS_2->ul_unicode_range[2]);
+  sfnt_swap32 (&OS_2->ul_unicode_range[3]);
+  sfnt_swap16 (&OS_2->fs_selection);
+  sfnt_swap16 (&OS_2->fs_first_char_index);
+  sfnt_swap16 (&OS_2->fs_last_char_index);
+  return OS_2;
+}
+
+
+
 #ifdef TEST
 
 struct sfnt_test_dcontext
@@ -19155,6 +19262,7 @@ main (int argc, char **argv)
   struct sfnt_gvar_table *gvar;
   struct sfnt_avar_table *avar;
   struct sfnt_cvar_table *cvar;
+  struct sfnt_OS_2_table *OS_2;
   sfnt_fixed scale;
   char *fancy;
   int *advances;
@@ -19185,7 +19293,7 @@ main (int argc, char **argv)
 
   fd = open (argv[1], O_RDONLY);
 
-  if (fd < 1)
+  if (fd < 0)
     return 1;
 
   ttc = NULL;
@@ -19290,6 +19398,7 @@ main (int argc, char **argv)
   fvar = sfnt_read_fvar_table (fd, font);
   gvar = sfnt_read_gvar_table (fd, font);
   avar = sfnt_read_avar_table (fd, font);
+  OS_2 = sfnt_read_OS_2_table (fd, font);
   cvar = NULL;
   hmtx = NULL;
 
@@ -19306,6 +19415,10 @@ main (int argc, char **argv)
   loca_long = NULL;
   loca_short = NULL;
 
+  if (OS_2)
+    fprintf (stderr, "OS/2 table found!\nach_vendor_id: %.4s\n",
+            OS_2->ach_vendor_id);
+
   if (fvar)
     {
       fprintf (stderr, "FVAR table found!\n"
@@ -19968,6 +20081,7 @@ main (int argc, char **argv)
   xfree (gvar);
   xfree (avar);
   xfree (cvar);
+  xfree (OS_2);
 
   return 0;
 }
diff --git a/src/sfnt.h b/src/sfnt.h
index 1a6b2209abc..6602d240051 100644
--- a/src/sfnt.h
+++ b/src/sfnt.h
@@ -52,6 +52,7 @@ enum sfnt_table
     SFNT_TABLE_GVAR,
     SFNT_TABLE_CVAR,
     SFNT_TABLE_AVAR,
+    SFNT_TABLE_OS_2,
   };
 
 #define SFNT_ENDOF(type, field, type1)                 \
@@ -1333,6 +1334,85 @@ struct sfnt_metrics_distortion
 
 
 
+/* OS/2 font metadata.  */
+
+struct sfnt_OS_2_table
+{
+  /* Table version number.  */
+  uint16_t version;
+
+  /* Average weighted advance width of lower case letters and
+     space.  */
+  int16_t x_avg_char_width;
+
+  /* Wisual weight (degree of blackness or thickness) of stroke in
+     glyphs.  */
+  uint16_t us_weight_class;
+
+  /* Relative change from the normal aspect ratio (width to height
+     ratio) as specified by a font designer for the glyphs in the
+     font.  */
+  uint16_t us_width_class;
+
+  /* Miscellaneous font attributes.  */
+  int16_t fs_type;
+
+  /* Recommended horizontal size in pixels for subscripts.  */
+  int16_t y_subscript_x_size;
+
+  /* Recommended vertical subscript size.  */
+  int16_t y_subscript_y_size;
+
+  /* Recommended horizontal offset for subscripts.  */
+  int16_t y_subscript_x_offset;
+
+  /* Recommended vertical offset from the baseline for subscripts.  */
+  int16_t y_subscript_y_offset;
+
+  /* Recommended horizontal size in pixels for superscripts.  */
+  int16_t y_superscript_x_size;
+
+  /* Recommended vertical superscript size.  */
+  int16_t y_superscript_y_size;
+
+  /* Recommended horizontal offset for superscripts.  */
+  int16_t y_superscript_x_offset;
+
+  /* Recommended vertical offset from the baseline for superscripts.  */
+  int16_t y_superscript_y_offset;
+
+  /* Width of the strikeout stroke.  */
+  int16_t y_strikeout_size;
+
+  /* Position of the strikeout stroke relative to the baseline.  */
+  int16_t y_strikeout_position;
+
+  /* Font family classification.  */
+  int16_t s_family_class;
+
+  /* Microsoft ``panose'' classification.  */
+  unsigned char panose[10];
+
+  /* Alignment boundary! */
+
+  /* Unicode range specification.  */
+  uint32_t ul_unicode_range[4];
+
+  /* Font foundry name.  */
+  char ach_vendor_id[4];
+
+  /* Two byte bitfield providing the nature of font patterns.  */
+  uint16_t fs_selection;
+
+  /* The minimum Unicode codepoint covered.  */
+  uint16_t fs_first_char_index;
+
+  /* The maximum Unicode codepoint covered.  */
+  uint16_t fs_last_char_index;
+};
+
+
+
 #define SFNT_CEIL_FIXED(fixed) (((fixed) + 0177777) & 037777600000)
 #define SFNT_FLOOR_FIXED(fixed) ((fixed) & 037777600000)
 
@@ -1500,6 +1580,14 @@ extern int sfnt_vary_compound_glyph (struct sfnt_blend 
*, sfnt_glyph,
                                     struct sfnt_glyph *,
                                     struct sfnt_metrics_distortion *);
 
+
+
+#define PROTOTYPE int, struct sfnt_offset_subtable *
+
+extern struct sfnt_OS_2_table *sfnt_read_OS_2_table (PROTOTYPE);
+
+#undef PROTOTYPE
+
 #endif /* TEST */
 
 
diff --git a/src/sfntfont.c b/src/sfntfont.c
index f2dc05c886e..3506742a92b 100644
--- a/src/sfntfont.c
+++ b/src/sfntfont.c
@@ -109,7 +109,7 @@ struct sfnt_font_desc
   /* Style name of the font.  */
   Lisp_Object style;
 
-  /* Designer (foundry) of the font.  */
+  /* The font foundry name, or `misc' if not present.  */
   Lisp_Object designer;
 
   /* Style tokens that could not be parsed.  */
@@ -365,28 +365,6 @@ sfnt_decode_family_style (struct sfnt_name_table *name,
   return (!NILP (*family) && !NILP (*style)) ? 0 : 1;
 }
 
-/* Decode the foundry names from the name table NAME.  Return the
-   foundry name, or nil upon failure.  */
-
-static Lisp_Object
-sfnt_decode_foundry_name (struct sfnt_name_table *name)
-{
-  struct sfnt_name_record designer_rec;
-  unsigned char *designer_data;
-
-  designer_data = sfnt_find_name (name, SFNT_NAME_DESIGNER,
-                                 &designer_rec);
-
-  if (!designer_data)
-    return Qnil;
-
-  return sfnt_decode_font_string (designer_data,
-                                 designer_rec.platform_id,
-                                 designer_rec.platform_specific_id,
-                                 designer_rec.language_id,
-                                 designer_rec.length);
-}
-
 /* Decode the name of the specified font INSTANCE using the given NAME
    table.  Return the name of that instance, or nil upon failure.  */
 
@@ -490,6 +468,12 @@ sfnt_parse_style (Lisp_Object style_name, struct 
sfnt_font_desc *desc)
     {
       style = NULL;
 
+      if (!strcmp (single, "regular"))
+       /* ``Regular'' within a font family can represent either the
+          weight, slant or width of the font.  Leave each value as
+          its default, but never append it to the adstyle.  */
+       goto next;
+
       if (desc->weight == 80)
        {
          /* Weight hasn't been found yet.  Scan through the weight
@@ -561,6 +545,11 @@ sfnt_parse_style (Lisp_Object style_name, struct 
sfnt_font_desc *desc)
       continue;
     }
 
+  /* The adstyle must be a symbol, so intern it if it is set.  */
+
+  if (!NILP (desc->adstyle))
+    desc->adstyle = Fintern (desc->adstyle, Qnil);
+
   SAFE_FREE ();
 }
 
@@ -972,9 +961,11 @@ sfnt_enum_font_1 (int fd, const char *file,
   struct sfnt_meta_table *meta;
   struct sfnt_maxp_table *maxp;
   struct sfnt_fvar_table *fvar;
+  struct sfnt_OS_2_table *OS_2;
   struct sfnt_font_desc temp;
   Lisp_Object family, style, instance, style1;
   int i;
+  char buffer[5];
 
   /* Create the font desc and copy in the file name.  */
   desc = xzalloc (sizeof *desc + strlen (file) + 1);
@@ -1013,10 +1004,33 @@ sfnt_enum_font_1 (int fd, const char *file,
 
   /* Set the family.  */
   desc->family = family;
-  desc->designer = sfnt_decode_foundry_name (name);
   desc->char_cache = Qnil;
   desc->subtable.platform_id = 500;
 
+  /* Now set the font foundry name.  This information is located
+     within the OS/2 table's `ach_vendor_id' field, but use `misc' as
+     a recourse if it is not present.  */
+
+  OS_2 = sfnt_read_OS_2_table (fd, subtables);
+
+  if (OS_2)
+    {
+      memcpy (buffer, OS_2->ach_vendor_id,
+             sizeof OS_2->ach_vendor_id);
+      buffer[sizeof OS_2->ach_vendor_id] = '\0';
+
+      /* If the foundry name is empty, use `misc' instead.  */
+
+      if (!buffer[0])
+       desc->designer = Qmisc;
+      else
+       desc->designer = intern (buffer);
+
+      xfree (OS_2);
+    }
+  else
+    desc->designer = Qmisc;
+
   /* Set the largest glyph identifier.  */
   desc->num_glyphs = maxp->num_glyphs;
 
@@ -1135,7 +1149,9 @@ sfnt_enum_font_1 (int fd, const char *file,
 int
 sfnt_enum_font (const char *file)
 {
-  int fd, rc;
+  int fd;
+  int rc;
+  off_t seek;
   struct sfnt_offset_subtable *subtables;
   struct sfnt_ttc_header *ttc;
   size_t i;
@@ -1166,8 +1182,9 @@ sfnt_enum_font (const char *file)
 
       for (i = 0; i < ttc->num_fonts; ++i)
        {
-         if (lseek (fd, ttc->offset_table[i], SEEK_SET)
-             != ttc->offset_table[i])
+         seek = lseek (fd, ttc->offset_table[i], SEEK_SET);
+
+         if (seek == -1 || seek != ttc->offset_table[i])
            continue;
 
          subtables = sfnt_read_table_directory (fd);
@@ -1643,7 +1660,7 @@ sfntfont_list_1 (struct sfnt_font_desc *desc, Lisp_Object 
spec,
   if (NILP (desc->instances))
     {
       tem = AREF (spec, FONT_ADSTYLE_INDEX);
-      if (!NILP (tem) && NILP (Fequal (tem, desc->adstyle)))
+      if (!NILP (tem) && !EQ (tem, desc->adstyle))
        return 0;
 
       if (FONT_WIDTH_NUMERIC (spec) != -1
@@ -1843,11 +1860,7 @@ sfntfont_desc_to_entity (struct sfnt_font_desc *desc, 
int instance)
   entity = font_make_entity ();
 
   ASET (entity, FONT_TYPE_INDEX, sfnt_vendor_name);
-
-  if (!NILP (desc->designer))
-    ASET (entity, FONT_FOUNDRY_INDEX,
-         Fintern (desc->designer, Qnil));
-
+  ASET (entity, FONT_FOUNDRY_INDEX, desc->designer);
   ASET (entity, FONT_FAMILY_INDEX, Fintern (desc->family, Qnil));
   ASET (entity, FONT_ADSTYLE_INDEX, Qnil);
   ASET (entity, FONT_REGISTRY_INDEX,
@@ -2610,7 +2623,7 @@ sfntfont_probe_widths (struct sfnt_font_info *font_info)
 
   /* Next, loop through the common ASCII characters.  Tally up their
      advance widths and set space_width if necessary.  */
-  for (i = 0; i < 127; ++i)
+  for (i = 32; i < 127; ++i)
     {
       glyph = sfntfont_lookup_glyph (font_info, i);
 
@@ -2667,6 +2680,18 @@ sfntfont_setup_interpreter (struct sfnt_font_info *info,
   struct sfnt_interpreter *interpreter;
   const char *error;
   struct sfnt_graphics_state state;
+  Lisp_Object regexp;
+
+  /* If Vsfnt_uninstructable_family_regexp matches this font, then
+     return.  */
+
+  regexp = Vsfnt_uninstructable_family_regexp;
+
+  if (STRINGP (regexp)
+      && (fast_string_match_ignore_case (regexp,
+                                        desc->family)
+         >= 0))
+    return;
 
   /* Load the cvt, fpgm and prep already read.  */
 
@@ -3174,10 +3199,12 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity,
   /* Figure out the font ascent and descent.  */
   font->ascent
     = ceil (font_info->hhea->ascent
-           * pixel_size * 1.0 / font_info->head->units_per_em);
+           * pixel_size
+           * (1.0 / font_info->head->units_per_em));
   font->descent
-    = -floor (font_info->hhea->descent
-             * pixel_size * 1.0 / font_info->head->units_per_em);
+    = ceil ((-font_info->hhea->descent)
+           * pixel_size
+           * (1.0 / font_info->head->units_per_em));
   font->height = font->ascent + font->descent;
 
   /* Set font->max_width to the maximum advance width.  */
@@ -3186,11 +3213,7 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity,
 
   /* Set generic attributes such as type and style.  */
   ASET (font_object, FONT_TYPE_INDEX, sfnt_vendor_name);
-
-  if (!NILP (desc->designer))
-    ASET (font_object, FONT_FOUNDRY_INDEX,
-         Fintern (desc->designer, Qnil));
-
+  ASET (font_object, FONT_FOUNDRY_INDEX, desc->designer);
   ASET (font_object, FONT_FAMILY_INDEX, Fintern (desc->family, Qnil));
   ASET (font_object, FONT_ADSTYLE_INDEX, Qnil);
   ASET (font_object, FONT_REGISTRY_INDEX,
@@ -3293,7 +3316,7 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity,
 
  cancel_blend:
   /* Calculate the xfld name.  */
-  font->props[FONT_NAME_INDEX] = Ffont_xlfd_name (font_object, Qnil);
+  font->props[FONT_NAME_INDEX] = Ffont_xlfd_name (font_object, Qnil, Qt);
 
 #ifdef HAVE_HARFBUZZ
   /* HarfBuzz will potentially read font tables after the font has
@@ -3313,6 +3336,21 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity,
   open_fonts = font_info;
 #endif /* HAVE_MMAP */
 
+  /* Now ascertain if vertical centering is desired by matching the
+     font XLFD against vertical-centering-font-regexp.  */
+
+  if (!NILP (font->props[FONT_NAME_INDEX]))
+    font->vertical_centering
+      = (STRINGP (Vvertical_centering_font_regexp)
+        && (fast_string_match_ignore_case
+            (Vvertical_centering_font_regexp,
+             font->props[FONT_NAME_INDEX]) >= 0));
+
+  /* And set a reasonable full name, namely the name of the font
+     file.  */
+  font->props[FONT_FULLNAME_INDEX]
+    = DECODE_FILE (build_unibyte_string (desc->path));
+
   /* All done.  */
   unblock_input ();
   return font_object;
@@ -3616,7 +3654,7 @@ sfntfont_draw (struct glyph_string *s, int from, int to,
 Lisp_Object
 sfntfont_list_family (struct frame *f)
 {
-  Lisp_Object families;
+  Lisp_Object families, tem, next;
   struct sfnt_font_desc *desc;
 
   families = Qnil;
@@ -3625,8 +3663,30 @@ sfntfont_list_family (struct frame *f)
     /* Add desc->family to the list.  */
     families = Fcons (desc->family, families);
 
-  /* Not sure if deleting duplicates is worth it.  Is this ever
-     called? */
+  /* Sort families in preparation for removing duplicates.  */
+  families = Fsort (families, Qstring_lessp);
+
+  /* Remove each duplicate within families.  */
+
+  tem = families;
+  while (!NILP (tem) && !NILP ((next = XCDR (tem))))
+    {
+      /* If the two strings are equal.  */
+      if (!NILP (Fstring_equal (XCAR (tem), XCAR (next))))
+       /* Set tem's cdr to the cons after the next item.  */
+       XSETCDR (tem, XCDR (next));
+      else
+       /* Otherwise, start considering the next item.  */
+       tem = next;
+    }
+
+  /* Intern each font family.  */
+
+  tem = families;
+
+  FOR_EACH_TAIL (tem)
+    XSETCAR (tem, Fintern (XCAR (tem), Qnil));
+
   return families;
 }
 
@@ -3929,6 +3989,12 @@ syms_of_sfntfont (void)
   /* Char-table purpose.  */
   DEFSYM (Qfont_lookup_cache, "font-lookup-cache");
 
+  /* Default foundry name.  */
+  DEFSYM (Qmisc, "misc");
+
+  /* Predicated employed for sorting font family lists.  */
+  DEFSYM (Qstring_lessp, "string-lessp");
+
   /* Set up staticpros.  */
   sfnt_vendor_name = Qnil;
   staticpro (&sfnt_vendor_name);
@@ -3937,12 +4003,26 @@ syms_of_sfntfont (void)
      of the font backend.  */
   DEFVAR_LISP ("sfnt-default-family-alist", Vsfnt_default_family_alist,
     doc: /* Alist between "emulated" and actual font family names.
-
 Much Emacs code assumes that font families named "Monospace" and "Sans
 Serif" exist, and map to the default monospace and Sans Serif fonts on
 a system.  When the `sfnt' font driver is asked to look for a font
 with one of the families in this alist, it uses its value instead.  */);
   Vsfnt_default_family_alist = Qnil;
+
+  DEFVAR_LISP ("sfnt-uninstructable-family-regexp",
+              Vsfnt_uninstructable_family_regexp,
+    doc: /* Regexp matching font families whose glyphs must not be instructed.
+If nil, instruction code supplied by all fonts will be executed.  This
+variable takes effect when a font entity is opened, not after, and
+therefore won't affect the scaling of realized faces until their
+frames' font caches are cleared (see `clear-font-cache').
+
+TrueType fonts incorporate instruction code executed to fit each glyph
+to a pixel grid, so as to improve the visual fidelity of each glyph by
+eliminating artifacts and chance effects consequent upon the direct
+upscaling of glyph outline data.  Instruction code is occasionally
+incompatible with Emacs and must be disregarded.  */);
+  Vsfnt_uninstructable_family_regexp = Qnil;
 }
 
 void
diff --git a/src/sysdep.c b/src/sysdep.c
index 52fbfbd1eb1..f49fed7da1e 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -3452,7 +3452,7 @@ make_lisp_timeval (struct timeval t)
 
 #endif
 
-#if defined (GNU_LINUX) || defined (CYGWIN)
+#if defined (GNU_LINUX) || defined (CYGWIN) || defined __ANDROID__
 
 static Lisp_Object
 time_from_jiffies (unsigned long long ticks, Lisp_Object hz, Lisp_Object form)
@@ -3500,7 +3500,7 @@ get_up_time (void)
   return up;
 }
 
-# ifdef GNU_LINUX
+# if defined GNU_LINUX || defined __ANDROID__
 #define MAJOR(d) (((unsigned)(d) >> 8) & 0xfff)
 #define MINOR(d) (((unsigned)(d) & 0xff) | (((unsigned)(d) & 0xfff00000) >> 
12))
 
@@ -3546,7 +3546,7 @@ procfs_ttyname (int rdev)
   unblock_input ();
   return build_string (name);
 }
-# endif        /* GNU_LINUX */
+# endif        /* GNU_LINUX || __ANDROID__ */
 
 static uintmax_t
 procfs_get_total_memory (void)
@@ -3695,9 +3695,9 @@ system_process_attributes (Lisp_Object pid)
          attrs = Fcons (Fcons (Qppid, INT_TO_INTEGER (ppid)), attrs);
          attrs = Fcons (Fcons (Qpgrp, INT_TO_INTEGER (pgrp)), attrs);
          attrs = Fcons (Fcons (Qsess, INT_TO_INTEGER (sess)), attrs);
-# ifdef GNU_LINUX
+# if defined GNU_LINUX || defined __ANDROID__
          attrs = Fcons (Fcons (Qttname, procfs_ttyname (tty)), attrs);
-# endif
+# endif /* GNU_LINUX || __ANDROID__ */
          attrs = Fcons (Fcons (Qtpgid, INT_TO_INTEGER (tpgid)), attrs);
          attrs = Fcons (Fcons (Qminflt, INT_TO_INTEGER (minflt)), attrs);
          attrs = Fcons (Fcons (Qmajflt, INT_TO_INTEGER (majflt)), attrs);
diff --git a/src/term.c b/src/term.c
index 9bcb2cb1386..25184101b78 100644
--- a/src/term.c
+++ b/src/term.c
@@ -76,7 +76,7 @@ static void set_tty_hooks (struct terminal *terminal);
 static void dissociate_if_controlling_tty (int fd);
 static void delete_tty (struct terminal *);
 
-#endif
+#endif /* !HAVE_ANDROID */
 
 static AVOID maybe_fatal (bool, struct terminal *, const char *, const char *,
                          ...)
@@ -2356,7 +2356,7 @@ A suspended tty may be resumed by calling `resume-tty' on 
it.  */)
       if (f != t->display_info.tty->output)
         emacs_fclose (t->display_info.tty->output);
       emacs_fclose (f);
-#endif
+#endif /* !MSDOS */
 
       t->display_info.tty->input = 0;
       t->display_info.tty->output = 0;
@@ -2368,10 +2368,11 @@ A suspended tty may be resumed by calling `resume-tty' 
on it.  */)
 
   /* Clear display hooks to prevent further output.  */
   clear_tty_hooks (t);
-#else
-  /* This will always signal on Android.  */
-  decode_tty_terminal (tty);
-#endif
+#else /* HAVE_ANDROID */
+  /* Android doesn't support TTY terminal devices, so unconditionally
+     signal.  */
+  error ("Attempt to suspend a non-text terminal device");
+#endif /* !HAVE_ANDROID */
 
   return Qnil;
 }
@@ -2428,7 +2429,7 @@ frame's terminal). */)
 
       if (!O_IGNORE_CTTY && strcmp (t->display_info.tty->name, DEV_TTY) != 0)
         dissociate_if_controlling_tty (fd);
-#endif
+#endif /* MSDOS */
 
       add_keyboard_wait_descriptor (fd);
 
@@ -2457,9 +2458,11 @@ frame's terminal). */)
     }
 
   set_tty_hooks (t);
-#else
-  decode_tty_terminal (tty);
-#endif
+#else /* HAVE_ANDROID */
+  /* Android doesn't support TTY terminal devices, so unconditionally
+     signal.  */
+  error ("Attempt to suspend a non-text terminal device");
+#endif /* !HAVE_ANDROID */
 
   return Qnil;
 }
@@ -2504,7 +2507,7 @@ A value of zero means TTY uses the system's default 
value.  */)
   error ("Not a tty terminal");
 }
 
-#endif
+#endif /* !HAVE_ANDROID */
 
 
 /***********************************************************************
diff --git a/src/treesit.c b/src/treesit.c
index 550bc8b9ee0..69b59fca111 100644
--- a/src/treesit.c
+++ b/src/treesit.c
@@ -80,6 +80,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #undef ts_tree_cursor_current_node
 #undef ts_tree_cursor_delete
 #undef ts_tree_cursor_goto_first_child
+#undef ts_tree_cursor_goto_first_child_for_byte
 #undef ts_tree_cursor_goto_next_sibling
 #undef ts_tree_cursor_goto_parent
 #undef ts_tree_cursor_new
@@ -147,6 +148,7 @@ DEF_DLL_FN (TSTreeCursor, ts_tree_cursor_copy, (const 
TSTreeCursor *));
 DEF_DLL_FN (TSNode, ts_tree_cursor_current_node, (const TSTreeCursor *));
 DEF_DLL_FN (void, ts_tree_cursor_delete, (const TSTreeCursor *));
 DEF_DLL_FN (bool, ts_tree_cursor_goto_first_child, (TSTreeCursor *));
+DEF_DLL_FN (int64_t, ts_tree_cursor_goto_first_child_for_byte, (TSTreeCursor 
*, uint32_t));
 DEF_DLL_FN (bool, ts_tree_cursor_goto_next_sibling, (TSTreeCursor *));
 DEF_DLL_FN (bool, ts_tree_cursor_goto_parent, (TSTreeCursor *));
 DEF_DLL_FN (TSTreeCursor, ts_tree_cursor_new, (TSNode));
@@ -210,6 +212,7 @@ init_treesit_functions (void)
   LOAD_DLL_FN (library, ts_tree_cursor_current_node);
   LOAD_DLL_FN (library, ts_tree_cursor_delete);
   LOAD_DLL_FN (library, ts_tree_cursor_goto_first_child);
+  LOAD_DLL_FN (library, ts_tree_cursor_goto_first_child_for_byte);
   LOAD_DLL_FN (library, ts_tree_cursor_goto_next_sibling);
   LOAD_DLL_FN (library, ts_tree_cursor_goto_parent);
   LOAD_DLL_FN (library, ts_tree_cursor_new);
@@ -267,6 +270,7 @@ init_treesit_functions (void)
 #define ts_tree_cursor_current_node fn_ts_tree_cursor_current_node
 #define ts_tree_cursor_delete fn_ts_tree_cursor_delete
 #define ts_tree_cursor_goto_first_child fn_ts_tree_cursor_goto_first_child
+#define ts_tree_cursor_goto_first_child_for_byte 
fn_ts_tree_cursor_goto_first_child_for_byte
 #define ts_tree_cursor_goto_next_sibling fn_ts_tree_cursor_goto_next_sibling
 #define ts_tree_cursor_goto_parent fn_ts_tree_cursor_goto_parent
 #define ts_tree_cursor_new fn_ts_tree_cursor_new
@@ -1149,7 +1153,8 @@ treesit_read_buffer (void *parser, uint32_t byte_index,
    machine.  */
 Lisp_Object
 make_treesit_parser (Lisp_Object buffer, TSParser *parser,
-                    TSTree *tree, Lisp_Object language_symbol)
+                    TSTree *tree, Lisp_Object language_symbol,
+                    Lisp_Object tag)
 {
   struct Lisp_TS_Parser *lisp_parser;
 
@@ -1158,6 +1163,8 @@ make_treesit_parser (Lisp_Object buffer, TSParser *parser,
 
   lisp_parser->language_symbol = language_symbol;
   lisp_parser->after_change_functions = Qnil;
+  lisp_parser->tag = tag;
+  lisp_parser->last_set_ranges = Qnil;
   lisp_parser->buffer = buffer;
   lisp_parser->parser = parser;
   lisp_parser->tree = tree;
@@ -1168,7 +1175,6 @@ make_treesit_parser (Lisp_Object buffer, TSParser *parser,
   lisp_parser->visible_end = BUF_ZV_BYTE (XBUFFER (buffer));
   lisp_parser->timestamp = 0;
   lisp_parser->deleted = false;
-  lisp_parser->has_range = false;
   eassert (lisp_parser->visible_beg <= lisp_parser->visible_end);
   return make_lisp_ptr (lisp_parser, Lisp_Vectorlike);
 }
@@ -1375,24 +1381,29 @@ DEFUN ("treesit-node-parser",
 
 DEFUN ("treesit-parser-create",
        Ftreesit_parser_create, Streesit_parser_create,
-       1, 3, 0,
-       doc: /* Create and return a parser in BUFFER for LANGUAGE.
+       1, 4, 0,
+       doc: /* Create and return a parser in BUFFER for LANGUAGE with TAG.
 
 The parser is automatically added to BUFFER's parser list, as returned
 by `treesit-parser-list'.  LANGUAGE is a language symbol.  If BUFFER
 is nil or omitted, it defaults to the current buffer.  If BUFFER
-already has a parser for LANGUAGE, return that parser, but if NO-REUSE
-is non-nil, always create a new parser.
+already has a parser for LANGUAGE with TAG, return that parser, but if
+NO-REUSE is non-nil, always create a new parser.
+
+TAG can be any symbol except t, and defaults to nil.  Different
+parsers can have the same tag.
 
 If that buffer is an indirect buffer, its base buffer is used instead.
 That is, indirect buffers use their base buffer's parsers.  Lisp
 programs should widen as necessary should they want to use a parser in
 an indirect buffer.  */)
-  (Lisp_Object language, Lisp_Object buffer, Lisp_Object no_reuse)
+  (Lisp_Object language, Lisp_Object buffer, Lisp_Object no_reuse,
+   Lisp_Object tag)
 {
   treesit_initialize ();
 
   CHECK_SYMBOL (language);
+  CHECK_SYMBOL (tag);
   struct buffer *buf;
   if (NILP (buffer))
     buf = current_buffer;
@@ -1404,6 +1415,9 @@ an indirect buffer.  */)
   if (buf->base_buffer)
     buf = buf->base_buffer;
 
+  if (EQ (tag, Qt))
+    xsignal2(Qwrong_type_argument, list2(Qnot, Qt), Qt);
+
   treesit_check_buffer_size (buf);
 
   /* See if we can reuse a parser.  */
@@ -1413,7 +1427,8 @@ an indirect buffer.  */)
       FOR_EACH_TAIL (tail)
       {
        struct Lisp_TS_Parser *parser = XTS_PARSER (XCAR (tail));
-       if (EQ (parser->language_symbol, language))
+       if (EQ (parser->tag, tag)
+           && EQ (parser->language_symbol, language))
          return XCAR (tail);
       }
     }
@@ -1433,7 +1448,7 @@ an indirect buffer.  */)
   /* Create parser.  */
   Lisp_Object lisp_parser = make_treesit_parser (Fcurrent_buffer (),
                                                 parser, NULL,
-                                                language);
+                                                language, tag);
 
   /* Update parser-list.  */
   BVAR (buf, ts_parser_list) = Fcons (lisp_parser, BVAR (buf, ts_parser_list));
@@ -1462,13 +1477,19 @@ See `treesit-parser-list' for the buffer's parser list. 
 */)
 
 DEFUN ("treesit-parser-list",
        Ftreesit_parser_list, Streesit_parser_list,
-       0, 1, 0,
-       doc: /* Return BUFFER's parser list.
+       0, 3, 0,
+       doc: /* Return BUFFER's parser list, filtered by LANGUAGE and TAG.
 
 BUFFER defaults to the current buffer.  If that buffer is an indirect
 buffer, its base buffer is used instead.  That is, indirect buffers
-use their base buffer's parsers.  */)
-  (Lisp_Object buffer)
+use their base buffer's parsers.
+
+If LANGUAGE is non-nil, only return parsers for that language.
+
+The returned list only contain parsers with TAG.  TAG defaults to nil.
+If TAG is t, include parsers in the returned list regardless of their
+tag.  */)
+  (Lisp_Object buffer, Lisp_Object language, Lisp_Object tag)
 {
   struct buffer *buf;
   if (NILP (buffer))
@@ -1489,7 +1510,12 @@ use their base buffer's parsers.  */)
   tail = BVAR (buf, ts_parser_list);
 
   FOR_EACH_TAIL (tail)
-    return_list = Fcons (XCAR (tail), return_list);
+    {
+      struct Lisp_TS_Parser *parser = XTS_PARSER (XCAR (tail));
+      if ((NILP (language) || EQ (language, parser->language_symbol))
+         && (EQ (tag, Qt) || EQ (tag, parser->tag)))
+       return_list = Fcons (XCAR (tail), return_list);
+    }
 
   return Freverse (return_list);
 }
@@ -1517,6 +1543,16 @@ This symbol is the one used to create the parser.  */)
   return XTS_PARSER (parser)->language_symbol;
 }
 
+DEFUN ("treesit-parser-tag",
+       Ftreesit_parser_tag, Streesit_parser_tag,
+       1, 1, 0,
+       doc: /* Return PARSER's tag.  */)
+  (Lisp_Object parser)
+{
+  treesit_check_parser (parser);
+  return XTS_PARSER (parser)->tag;
+}
+
 /* Return true if PARSER is not deleted and its buffer is live.  */
 static bool
 treesit_parser_live_p (Lisp_Object parser)
@@ -1620,6 +1656,10 @@ buffer.  */)
   treesit_check_parser (parser);
   if (!NILP (ranges))
     CHECK_CONS (ranges);
+
+  if (!NILP (Fequal (XTS_PARSER (parser)->last_set_ranges, ranges)))
+    return Qnil;
+
   treesit_check_range_argument (ranges);
 
   treesit_initialize ();
@@ -1627,10 +1667,10 @@ buffer.  */)
   treesit_check_buffer_size (XBUFFER (XTS_PARSER (parser)->buffer));
   treesit_sync_visible_region (parser);
 
+  XTS_PARSER (parser)->last_set_ranges = ranges;
   bool success;
   if (NILP (ranges))
     {
-      XTS_PARSER (parser)->has_range = false;
       /* If RANGES is nil, make parser to parse the whole document.
         To do that we give tree-sitter a 0 length, the range is a
         dummy.  */
@@ -1641,8 +1681,6 @@ buffer.  */)
   else
     {
       /* Set ranges for PARSER.  */
-      XTS_PARSER (parser)->has_range = true;
-
       if (list_length (ranges) > UINT32_MAX)
        xsignal (Qargs_out_of_range, list2 (ranges, Flength (ranges)));
       uint32_t len = (uint32_t) list_length (ranges);
@@ -1698,10 +1736,10 @@ See also `treesit-parser-set-included-ranges'.  */)
 
   /* When the parser doesn't have a range set and we call
      ts_parser_included_ranges on it, it doesn't return an empty list,
-     but rather return some garbled data. (A single range where
-     start_byte = 0, end_byte = UINT32_MAX).  So we need to track
-     whether the parser is ranged ourselves.  */
-  if (!XTS_PARSER (parser)->has_range)
+     but rather return DEFAULT_RANGE. (A single range where start_byte
+     = 0, end_byte = UINT32_MAX).  So we need to track whether the
+     parser is ranged ourselves.  */
+  if (NILP (XTS_PARSER (parser)->last_set_ranges))
     return Qnil;
 
   uint32_t len;
@@ -2170,7 +2208,10 @@ return nil.  */)
 static bool treesit_cursor_first_child_for_byte
 (TSTreeCursor *cursor, ptrdiff_t pos, bool named)
 {
-  if (!ts_tree_cursor_goto_first_child (cursor))
+  /* ts_tree_cursor_goto_first_child_for_byte is significantly faster,
+     so despite it having problems, we try it first.  */
+  if (ts_tree_cursor_goto_first_child_for_byte (cursor, pos) == -1
+      && !ts_tree_cursor_goto_first_child (cursor))
     return false;
 
   TSNode node = ts_tree_cursor_current_node (cursor);
@@ -2771,7 +2812,7 @@ static Lisp_Object treesit_resolve_node (Lisp_Object obj)
   else if (SYMBOLP (obj))
     {
       Lisp_Object parser
-       = Ftreesit_parser_create (obj, Fcurrent_buffer (), Qnil);
+       = Ftreesit_parser_create (obj, Fcurrent_buffer (), Qnil, Qnil);
       return Ftreesit_parser_root_node (parser);
     }
   else
@@ -3027,7 +3068,8 @@ treesit_assume_true (bool val)
    limit.  */
 static bool
 treesit_cursor_helper_1 (TSTreeCursor *cursor, TSNode *target,
-                        uint32_t end_pos, ptrdiff_t limit)
+                        uint32_t start_pos, uint32_t end_pos,
+                        ptrdiff_t limit)
 {
   if (limit <= 0)
     return false;
@@ -3036,23 +3078,21 @@ treesit_cursor_helper_1 (TSTreeCursor *cursor, TSNode 
*target,
   if (ts_node_eq (cursor_node, *target))
     return true;
 
-  if (!ts_tree_cursor_goto_first_child (cursor))
+  /* ts_tree_cursor_goto_first_child_for_byte is significantly faster,
+     so despite it having problems (see bug#60127), we try it
+     first.  */
+  if (ts_tree_cursor_goto_first_child_for_byte (cursor, start_pos) == -1
+      && !ts_tree_cursor_goto_first_child (cursor))
     return false;
 
-  /* Skip nodes that definitely don't contain TARGET.  */
-  while (ts_node_end_byte (cursor_node) < end_pos)
-    {
-      if (!ts_tree_cursor_goto_next_sibling (cursor))
-       break;
-      cursor_node = ts_tree_cursor_current_node (cursor);
-    }
-
   /* Go through each sibling that could contain TARGET.  Because of
      missing nodes (their width is 0), there could be multiple
      siblings that could contain TARGET.  */
   while (ts_node_start_byte (cursor_node) <= end_pos)
     {
-      if (treesit_cursor_helper_1 (cursor, target, end_pos, limit - 1))
+      if (ts_node_end_byte (cursor_node) >= end_pos
+         && treesit_cursor_helper_1 (cursor, target, start_pos, end_pos,
+                                     limit - 1))
        return true;
 
       if (!ts_tree_cursor_goto_next_sibling (cursor))
@@ -3084,11 +3124,12 @@ treesit_cursor_helper_1 (TSTreeCursor *cursor, TSNode 
*target,
 static bool
 treesit_cursor_helper (TSTreeCursor *cursor, TSNode node, Lisp_Object parser)
 {
+  uint32_t start_pos = ts_node_start_byte (node);
   uint32_t end_pos = ts_node_end_byte (node);
   TSNode root = ts_tree_root_node (XTS_PARSER (parser)->tree);
   *cursor = ts_tree_cursor_new (root);
-  bool success = treesit_cursor_helper_1 (cursor, &node, end_pos,
-                                         TREESIT_RECURSION_LIMIT);
+  bool success = treesit_cursor_helper_1 (cursor, &node, start_pos,
+                                         end_pos, TREESIT_RECURSION_LIMIT);
   if (!success)
     ts_tree_cursor_delete (cursor);
   return success;
@@ -3220,7 +3261,7 @@ treesit_traverse_child_helper (TSTreeCursor *cursor,
 }
 
 /* Given a symbol THING, and a language symbol LANGUAGE, find the
-   corresponding predicate definition in treesit-things-settings.
+   corresponding predicate definition in treesit-thing-settings.
    Don't check for the type of THING and LANGUAGE.
 
    If there isn't one, return Qnil.  */
@@ -3242,12 +3283,13 @@ treesit_traverse_get_predicate (Lisp_Object thing, 
Lisp_Object language)
 }
 
 /* Validate the PRED passed to treesit_traverse_match_predicate.  If
-   there's an error, set SIGNAL_DATA to something signal accepts, and
-   return false, otherwise return true.  This function also check for
+   there's an error, set SIGNAL_DATA to (ERR . DATA), where ERR is an
+   error symbol, and DATA is something signal accepts, and return
+   false, otherwise return true.  This function also check for
    recusion levels: we place a arbitrary 100 level limit on recursive
    predicates.  RECURSION_LEVEL is the current recursion level (that
-   starts at 0), if it goes over 99, return false and set
-   SIGNAL_DATA.  LANGUAGE is a LANGUAGE symbol.  */
+   starts at 0), if it goes over 99, return false and set SIGNAL_DATA.
+   LANGUAGE is a LANGUAGE symbol.  */
 static bool
 treesit_traverse_validate_predicate (Lisp_Object pred,
                                     Lisp_Object language,
@@ -3256,7 +3298,8 @@ treesit_traverse_validate_predicate (Lisp_Object pred,
 {
   if (recursion_level > 99)
     {
-      *signal_data = list1 (build_string ("Predicate recursion level "
+      *signal_data = list2 (Qtreesit_invalid_predicate,
+                           build_string ("Predicate recursion level "
                                          "exceeded: it must not exceed "
                                          "100 levels"));
       return false;
@@ -3271,7 +3314,8 @@ treesit_traverse_validate_predicate (Lisp_Object pred,
                                                               language);
       if (NILP (definition))
        {
-         *signal_data = list2 (build_string ("Cannot find the definition "
+         *signal_data = list3 (Qtreesit_predicate_not_found,
+                               build_string ("Cannot find the definition "
                                              "of the predicate in "
                                              "`treesit-thing-settings'"),
                                pred);
@@ -3290,7 +3334,8 @@ treesit_traverse_validate_predicate (Lisp_Object pred,
        {
          if (!CONSP (cdr))
            {
-             *signal_data = list2 (build_string ("Invalide `not' "
+             *signal_data = list3 (Qtreesit_invalid_predicate,
+                                   build_string ("Invalide `not' "
                                                  "predicate"),
                                    pred);
              return false;
@@ -3298,7 +3343,8 @@ treesit_traverse_validate_predicate (Lisp_Object pred,
          /* At this point CDR must be a cons.  */
          if (XFIXNUM (Flength (cdr)) != 1)
            {
-             *signal_data = list2 (build_string ("`not' can only "
+             *signal_data = list3 (Qtreesit_invalid_predicate,
+                                   build_string ("`not' can only "
                                                  "have one argument"),
                                    pred);
              return false;
@@ -3312,7 +3358,8 @@ treesit_traverse_validate_predicate (Lisp_Object pred,
        {
          if (!CONSP (cdr) || NILP (cdr))
            {
-             *signal_data = list2 (build_string ("`or' must have a list "
+             *signal_data = list3 (Qtreesit_invalid_predicate,
+                                   build_string ("`or' must have a list "
                                                  "of patterns as "
                                                  "arguments "),
                                    pred);
@@ -3331,7 +3378,8 @@ treesit_traverse_validate_predicate (Lisp_Object pred,
       else if (STRINGP (car) && FUNCTIONP (cdr))
        return true;
     }
-  *signal_data = list2 (build_string ("Invalid predicate, see 
`treesit-thing-settings' for valid forms of predicate"),
+  *signal_data = list3 (Qtreesit_invalid_predicate,
+                       build_string ("Invalid predicate, see 
`treesit-thing-settings' for valid forms of predicate"),
                        pred);
   return false;
 }
@@ -3512,8 +3560,13 @@ DEFUN ("treesit-search-subtree",
        doc: /* Traverse the parse tree of NODE depth-first using PREDICATE.
 
 Traverse the subtree of NODE, and match PREDICATE with each node along
-the way.  PREDICATE is a regexp string that matches against each
-node's type, or a function that takes a node and returns nil/non-nil.
+the way.
+
+PREDICATE can be a regexp string that matches against each node's
+type, a predicate function, and more.  See `treesit-thing-settings'
+for the possible predicates.  PREDICATE can also be a thing defined in
+`treesit-thing-settings'.  Using an undefined thing doesn't raise an
+error.
 
 By default, only traverse named nodes, but if ALL is non-nil, traverse
 all nodes.  If BACKWARD is non-nil, traverse backwards.  If DEPTH is
@@ -3545,7 +3598,13 @@ Return the first matched node, or nil if none matches.  
*/)
   Lisp_Object signal_data = Qnil;
   if (!treesit_traverse_validate_predicate (predicate, language,
                                            &signal_data, 0))
-    xsignal1 (Qtreesit_invalid_predicate, signal_data);
+    {
+      Lisp_Object err_symbol = XCAR (signal_data);
+      Lisp_Object data = XCDR (signal_data);
+      if (EQ (err_symbol, Qtreesit_predicate_not_found))
+       return Qnil;
+      xsignal1 (err_symbol, data);
+    }
 
   Lisp_Object return_value = Qnil;
   TSTreeCursor cursor;
@@ -3571,9 +3630,13 @@ DEFUN ("treesit-search-forward",
        doc: /* Search for node matching PREDICATE in the parse tree of START.
 
 Start traversing the tree from node START, and match PREDICATE with
-each node (except START itself) along the way.  PREDICATE is a regexp
-string that matches against each node's type, or a function that takes
-a node and returns non-nil if it matches.
+each node (except START itself) along the way.
+
+PREDICATE can be a regexp string that matches against each node's
+type, a predicate function, and more.  See `treesit-thing-settings'
+for the possible predicates.  PREDICATE can also be a thing defined in
+`treesit-thing-settings'.  Using an undefined thing doesn't raise an
+error.
 
 By default, only search for named nodes, but if ALL is non-nil, search
 for all nodes.  If BACKWARD is non-nil, search backwards.
@@ -3610,7 +3673,13 @@ always traverse leaf nodes first, then upwards.  */)
   Lisp_Object signal_data = Qnil;
   if (!treesit_traverse_validate_predicate (predicate, language,
                                            &signal_data, 0))
-    xsignal1 (Qtreesit_invalid_predicate, signal_data);
+    {
+      Lisp_Object err_symbol = XCAR (signal_data);
+      Lisp_Object data = XCDR (signal_data);
+      if (EQ (err_symbol, Qtreesit_predicate_not_found))
+       return Qnil;
+      xsignal1 (err_symbol, data);
+    }
 
   Lisp_Object return_value = Qnil;
   TSTreeCursor cursor;
@@ -3683,9 +3752,14 @@ DEFUN ("treesit-induce-sparse-tree",
        Streesit_induce_sparse_tree, 2, 4, 0,
        doc: /* Create a sparse tree of ROOT's subtree.
 
-This takes the subtree under ROOT, and combs it so only the nodes
-that match PREDICATE are left, like picking out grapes on the vine.
-PREDICATE is a regexp string that matches against each node's type.
+This takes the subtree under ROOT, and combs it so only the nodes that
+match PREDICATE are left, like picking out grapes on the vine.
+
+PREDICATE can be a regexp string that matches against each node's
+type, a predicate function, and more.  See `treesit-thing-settings'
+for the possible predicates.  PREDICATE can also be a thing defined in
+`treesit-thing-settings'.  Using an undefined thing doesn't raise an
+error.
 
 For a subtree on the left that consist of both numbers and letters, if
 PREDICATE is "is letter", the returned tree is the one on the right.
@@ -3712,11 +3786,7 @@ ROOT.  If DEPTH is nil or omitted, it defaults to 1000.
 Each node in the returned tree looks like (NODE . (CHILD ...)).  The
 root of this tree might be nil, if ROOT doesn't match PREDICATE.
 
-If no node matches PREDICATE, return nil.
-
-PREDICATE can also be a function that takes a node and returns
-nil/non-nil, but it is slower and more memory consuming than using
-a regexp.  */)
+If no node matches PREDICATE, return nil.  */)
   (Lisp_Object root, Lisp_Object predicate, Lisp_Object process_fn,
    Lisp_Object depth)
 {
@@ -3742,7 +3812,13 @@ a regexp.  */)
   Lisp_Object signal_data = Qnil;
   if (!treesit_traverse_validate_predicate (predicate, language,
                                            &signal_data, 0))
-    xsignal1 (Qtreesit_invalid_predicate, signal_data);
+    {
+      Lisp_Object err_symbol = XCAR (signal_data);
+      Lisp_Object data = XCDR (signal_data);
+      if (EQ (err_symbol, Qtreesit_predicate_not_found))
+       return Qnil;
+      xsignal1 (err_symbol, data);
+    }
 
   Lisp_Object parent = Fcons (Qnil, Qnil);
   /* In this function we never traverse above NODE, so we don't need
@@ -3767,13 +3843,20 @@ a regexp.  */)
 
 DEFUN ("treesit-node-match-p",
        Ftreesit_node_match_p,
-       Streesit_node_match_p, 2, 2, 0,
+       Streesit_node_match_p, 2, 3, 0,
        doc: /* Check whether NODE matches PREDICATE.
 
-PREDICATE can be a regexp matching node type, a predicate function,
-and more, see `treesit-thing-settings' for detail.  Return non-nil
-if NODE matches PRED, nil otherwise.  */)
-  (Lisp_Object node, Lisp_Object predicate)
+PREDICATE can be a symbol representing a thing in
+`treesit-thing-settings', or a predicate, like regexp matching node
+type, etc.  See `treesit-thing-settings' for more details.
+
+Return non-nil if NODE matches PREDICATE, nil otherwise.
+
+Signals `treesit-invalid-predicate' if there's no definition of THING
+in `treesit-thing-settings', or if PREDICATE is malformed.  If
+IGNORE-MISSING is non-nil, don't signal an error for missing THING
+definition, but still signal for malformed PREDICATE.  */)
+  (Lisp_Object node, Lisp_Object predicate, Lisp_Object ignore_missing)
 {
   CHECK_TS_NODE (node);
 
@@ -3783,7 +3866,16 @@ if NODE matches PRED, nil otherwise.  */)
   Lisp_Object signal_data = Qnil;
   if (!treesit_traverse_validate_predicate (predicate, language,
                                            &signal_data, 0))
-    xsignal1 (Qtreesit_invalid_predicate, signal_data);
+    {
+      Lisp_Object err_symbol = XCAR (signal_data);
+      Lisp_Object data = XCDR (signal_data);
+
+      if (!NILP (ignore_missing)
+         && EQ (err_symbol, Qtreesit_predicate_not_found))
+       return Qnil;
+
+      xsignal1 (err_symbol, data);
+    }
 
   TSTreeCursor cursor = ts_tree_cursor_new (XTS_NODE (node)->node);
 
@@ -3922,6 +4014,7 @@ syms_of_treesit (void)
   DEFSYM (Qtreesit_parser_deleted, "treesit-parser-deleted");
   DEFSYM (Qtreesit_pattern_expand, "treesit-pattern-expand");
   DEFSYM (Qtreesit_invalid_predicate, "treesit-invalid-predicate");
+  DEFSYM (Qtreesit_predicate_not_found, "treesit-predicate-not-found");
 
   DEFSYM (Qor, "or");
 
@@ -3994,20 +4087,20 @@ LANGUAGE is a language symbol, and DEFINITIONS is a 
list of
     (THING PRED)
 
 THING is a symbol representing the thing, like `defun', `sexp', or
-`block'; PRED defines what kind of node can be qualified as THING.
+`sentence'; PRED defines what kind of node can be qualified as THING.
 
 PRED can be a regexp string that matches the type of the node; it can
 be a predicate function that takes the node as the sole argument and
-returns t if the node is the thing; it can be a cons (REGEXP . FN),
-which is a combination of a regexp and a predicate function, and the
-node has to match both to qualify as the thing.
+returns t if the node is the thing, and nil otherwise; it can be a
+cons (REGEXP . FN), which is a combination of a regexp and a predicate
+function, and the node has to match both to qualify as the thing.
 
 PRED can also be recursively defined.  It can be (or PRED...), meaning
 satisfying anyone of the inner PREDs qualifies the node; or (not
 PRED), meaning not satisfying the inner PRED qualifies the node.
 
 Finally, PRED can refer to other THINGs defined in this list by using
-the symbol of that THING.  For example, (or block sexp).  */);
+the symbol of that THING.  For example, (or sexp sentence).  */);
   Vtreesit_thing_settings = Qnil;
 
   staticpro (&Vtreesit_str_libtree_sitter);
@@ -4066,6 +4159,7 @@ the symbol of that THING.  For example, (or block sexp).  
*/);
   defsubr (&Streesit_parser_list);
   defsubr (&Streesit_parser_buffer);
   defsubr (&Streesit_parser_language);
+  defsubr (&Streesit_parser_tag);
 
   defsubr (&Streesit_parser_root_node);
   /* defsubr (&Streesit_parse_string); */
diff --git a/src/treesit.h b/src/treesit.h
index 5382bc58817..ef7e2e15317 100644
--- a/src/treesit.h
+++ b/src/treesit.h
@@ -34,13 +34,21 @@ INLINE_HEADER_BEGIN
 struct Lisp_TS_Parser
 {
   union vectorlike_header header;
-  /* A symbol representing the language this parser uses.  See the
+    /* A symbol representing the language this parser uses.  See the
      manual for more explanation.  */
   Lisp_Object language_symbol;
   /* A list of functions to call after re-parse.  Every function is
      called with the changed ranges and the parser.  The changed
      ranges is a list of (BEG . END).  */
   Lisp_Object after_change_functions;
+  /* A tag (symbol) for the parser.  Different parsers can have the
+     same tag.  A tag is primarily used to differentiate between
+     parsers for the same language.  */
+  Lisp_Object tag;
+  /* The Lisp ranges last set.  This is use to compare to the new
+     ranges the users wants to set, and avoid reparse if the new
+     ranges is the same as the last set one.  */
+  Lisp_Object last_set_ranges;
   /* The buffer associated with this parser.  */
   Lisp_Object buffer;
   /* The pointer to the tree-sitter parser.  Never NULL.  */
@@ -72,9 +80,6 @@ struct Lisp_TS_Parser
   /* If this field is true, parser functions raises
      treesit-parser-deleted signal.  */
   bool deleted;
-  /* If this field is true, the parser has ranges set.  See
-     Ftreesit_parser_included_ranges for why we need this.  */
-  bool has_range;
 };
 
 /* A wrapper around a tree-sitter node.  */
@@ -183,7 +188,7 @@ INLINE_HEADER_END
 
 extern void treesit_record_change (ptrdiff_t, ptrdiff_t, ptrdiff_t);
 extern Lisp_Object make_treesit_parser (Lisp_Object, TSParser *, TSTree *,
-                                       Lisp_Object);
+                                       Lisp_Object, Lisp_Object);
 extern Lisp_Object make_treesit_node (Lisp_Object, TSNode);
 
 extern bool treesit_node_uptodate_p (Lisp_Object);
diff --git a/src/w32font.c b/src/w32font.c
index 0371b24e1d1..45b6c499745 100644
--- a/src/w32font.c
+++ b/src/w32font.c
@@ -1072,7 +1072,7 @@ w32font_open_internal (struct frame *f, Lisp_Object 
font_entity,
      name to be usable in x-list-fonts. Eventually we expect to change
      x-list-fonts and other places that use fonts so that this can be
      an fcname or similar.  */
-  font->props[FONT_NAME_INDEX] = Ffont_xlfd_name (font_object, Qnil);
+  font->props[FONT_NAME_INDEX] = Ffont_xlfd_name (font_object, Qnil, Qt);
 
   return 1;
 }
diff --git a/src/w32heap.c b/src/w32heap.c
index 628fc28e3c5..96881c3923d 100644
--- a/src/w32heap.c
+++ b/src/w32heap.c
@@ -123,7 +123,7 @@ typedef struct _RTL_HEAP_PARAMETERS {
 # if defined _WIN64 || defined WIDE_EMACS_INT
 #  define DUMPED_HEAP_SIZE (28*1024*1024)
 # else
-#  define DUMPED_HEAP_SIZE (18*1024*1024)
+#  define DUMPED_HEAP_SIZE (24*1024*1024)
 # endif
 #endif
 
diff --git a/src/xdisp.c b/src/xdisp.c
index 8970d5aaaf2..f1980c4f20c 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -11448,7 +11448,6 @@ window_text_pixel_size (Lisp_Object window, Lisp_Object 
from, Lisp_Object to,
              else
                move_it_in_display_line (&it, start, it1_x + 1,
                                         MOVE_TO_POS | MOVE_TO_X);
-             move_it_to (&it, start - 1, -1, -1, -1, MOVE_TO_POS);
              start_x = it.current_x;
              /* If we didn't change our buffer position, the pixel
                 width of what's here was not yet accounted for; do it
@@ -11809,7 +11808,7 @@ vadd_to_log (char const *format, va_list ap)
   eassert (nargs <= ARRAYELTS (args));
   AUTO_STRING (args0, format);
   args[0] = args0;
-  for (ptrdiff_t i = 1; i <= nargs; i++)
+  for (ptrdiff_t i = 1; i < nargs; i++)
     args[i] = va_arg (ap, Lisp_Object);
   Lisp_Object msg = Qnil;
   msg = Fformat_message (nargs, args);
@@ -19171,7 +19170,7 @@ try_cursor_movement (Lisp_Object window, struct 
text_pos startp,
       && !f->cursor_type_changed
       && NILP (Vshow_trailing_whitespace)
       /* When display-line-numbers is in relative mode, moving point
-        requires to redraw the entire window.  */
+        requires redrawing the entire window.  */
       && !EQ (Vdisplay_line_numbers, Qrelative)
       && !EQ (Vdisplay_line_numbers, Qvisual)
       /* When the current line number should be displayed in a
@@ -37814,13 +37813,13 @@ composed on display.  */);
   DEFVAR_INT ("max-redisplay-ticks", max_redisplay_ticks,
     doc: /* Maximum number of redisplay ticks before aborting redisplay of a 
window.
 
-This allows to abort the display of a window if the amount of low-level
-redisplay operations exceeds the value of this variable.  When display of
-a window is aborted due to this reason, the buffer shown in that window
-will not have its windows redisplayed until the buffer is modified or until
-you type \\[recenter-top-bottom] with one of its windows selected.
-You can also decide to kill the buffer and visit it in some
-other way, like under `so-long-mode' or literally.
+This enables aborting the display of a window if the amount of
+low-level redisplay operations exceeds the value of this variable.
+When display of a window is aborted due to this reason, the buffer
+shown in that window will not have its windows redisplayed until the
+buffer is modified or until you type \\[recenter-top-bottom] with one
+of its windows selected.  You can also decide to kill the buffer and
+visit it in some other way, like under `so-long-mode' or literally.
 
 The default value is zero, which disables this feature.
 The recommended non-zero value is between 100000 and 1000000,
diff --git a/src/xfaces.c b/src/xfaces.c
index 30487c0e8fb..96382e78397 100644
--- a/src/xfaces.c
+++ b/src/xfaces.c
@@ -1629,7 +1629,7 @@ the face font sort order, see 
`face-font-selection-order'.  */)
                                          make_fixnum
                                          (FONT_SPACING_PROPORTIONAL)))
                             ? Qnil : Qt,
-                            Ffont_xlfd_name (font, Qnil),
+                            Ffont_xlfd_name (font, Qnil, Qt),
                             AREF (font, FONT_REGISTRY_INDEX));
       result = Fcons (v, result);
     }
@@ -1738,7 +1738,7 @@ the WIDTH times as wide as FACE on FRAME.  */)
          ASET (font_entity, FONT_SIZE_INDEX,
                AREF (font_spec, FONT_SIZE_INDEX));
        }
-      XSETCAR (tail, Ffont_xlfd_name (font_entity, Qnil));
+      XSETCAR (tail, Ffont_xlfd_name (font_entity, Qnil, Qt));
     }
   if (NILP (frame))
     /* We don't have to check fontsets.  */
@@ -4018,7 +4018,8 @@ x_update_menu_appearance (struct frame *f)
              || !UNSPECIFIEDP (LFACE_SLANT (lface))
              || !UNSPECIFIEDP (LFACE_HEIGHT (lface))))
        {
-         Lisp_Object xlfd = Ffont_xlfd_name (LFACE_FONT (lface), Qnil);
+         Lisp_Object xlfd = Ffont_xlfd_name (LFACE_FONT (lface), Qnil,
+                                             Qnil);
 #ifdef USE_MOTIF
          const char *suffix = "List";
          bool motif = true;
diff --git a/src/xfns.c b/src/xfns.c
index aea2f4b880e..0b1e94af9f0 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -4021,6 +4021,7 @@ initial_set_up_x_back_buffer (struct frame *f)
 }
 
 #if defined HAVE_XINPUT2
+
 static void
 setup_xi_event_mask (struct frame *f)
 {
@@ -4069,8 +4070,7 @@ setup_xi_event_mask (struct frame *f)
       XISetMask (m, XI_GesturePinchEnd);
     }
 #endif /* HAVE_XINPUT2_4 */
-  XISelectEvents (FRAME_X_DISPLAY (f),
-                 FRAME_X_WINDOW (f),
+  XISelectEvents (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
                  &mask, 1);
 
   /* Fortunately `xi_masks' isn't used on GTK 3, where we really have
@@ -4085,11 +4085,8 @@ setup_xi_event_mask (struct frame *f)
 #ifdef USE_X_TOOLKIT
   XISetMask (m, XI_KeyPress);
   XISetMask (m, XI_KeyRelease);
-  XISetMask (m, XI_FocusIn);
-  XISetMask (m, XI_FocusOut);
 
-  XISelectEvents (FRAME_X_DISPLAY (f),
-                 FRAME_OUTER_WINDOW (f),
+  XISelectEvents (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
                  &mask, 1);
   memset (m, 0, l);
 #endif /* USE_X_TOOLKIT */
@@ -4135,6 +4132,7 @@ setup_xi_event_mask (struct frame *f)
 
   unblock_input ();
 }
+
 #endif
 
 #ifdef USE_X_TOOLKIT
diff --git a/src/xterm.c b/src/xterm.c
index d826eec2419..517bdf57aab 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -1167,7 +1167,7 @@ static struct terminal *x_create_terminal (struct 
x_display_info *);
 static void x_frame_rehighlight (struct x_display_info *);
 
 static void x_clip_to_row (struct window *, struct glyph_row *,
-                          enum glyph_row_area, GC);
+                          enum glyph_row_area, GC, XRectangle *);
 static struct scroll_bar *x_window_to_scroll_bar (Display *, Window, int);
 static struct frame *x_window_to_frame (struct x_display_info *, int);
 static void x_scroll_bar_report_motion (struct frame **, Lisp_Object *,
@@ -7842,9 +7842,10 @@ x_draw_fringe_bitmap (struct window *w, struct glyph_row 
*row,
   Display *display = FRAME_X_DISPLAY (f);
   GC gc = f->output_data.x->normal_gc;
   struct face *face = p->face;
+  XRectangle clip_rect;
 
   /* Must clip because of partially visible lines.  */
-  x_clip_to_row (w, row, ANY_AREA, gc);
+  x_clip_to_row (w, row, ANY_AREA, gc, &clip_rect);
 
   if (p->bx >= 0 && !p->overlay_p)
     {
@@ -7914,6 +7915,29 @@ x_draw_fringe_bitmap (struct window *w, struct glyph_row 
*row,
 
       memset (&attrs, 0, sizeof attrs);
 #endif
+      XRectangle image_rect, dest;
+      int px, py, pwidth, pheight;
+
+      /* Intersect the destination rectangle with that of the row.
+        Setting a clip mask overrides the clip rectangles provided by
+        x_clip_to_row, so clipping must be performed by hand.  */
+
+      image_rect.x = p->x;
+      image_rect.y = p->y;
+      image_rect.width = p->wd;
+      image_rect.height = p->h;
+
+      if (!gui_intersect_rectangles (&clip_rect, &image_rect, &dest))
+       /* The entire destination rectangle falls outside the row.  */
+       goto undo_clip;
+
+      /* Extrapolate the source rectangle from the difference between
+        the destination and image rectangles.  */
+
+      px = dest.x - image_rect.x;
+      py = dest.y - image_rect.y;
+      pwidth = dest.width;
+      pheight = dest.height;
 
       if (p->wd > 8)
        bits = (char *) (p->bits + p->dh);
@@ -7985,15 +8009,16 @@ x_draw_fringe_bitmap (struct window *w, struct 
glyph_row *row,
          x_xr_apply_ext_clip (f, gc);
          XRenderComposite (display, PictOpSrc, picture,
                            None, FRAME_X_PICTURE (f),
-                           0, 0, 0, 0, p->x, p->y, p->wd, p->h);
+                           px, py, px, py, dest.x, dest.y,
+                           pwidth, pheight);
          x_xr_reset_ext_clip (f);
 
          XRenderFreePicture (display, picture);
        }
       else
 #endif
-       XCopyArea (display, pixmap, drawable, gc, 0, 0,
-                  p->wd, p->h, p->x, p->y);
+       XCopyArea (display, pixmap, drawable, gc, px, py,
+                  pwidth, pheight, dest.x, dest.y);
       XFreePixmap (display, pixmap);
 
       if (p->overlay_p)
@@ -8003,6 +8028,8 @@ x_draw_fringe_bitmap (struct window *w, struct glyph_row 
*row,
          XFreePixmap (display, clipmask);
        }
     }
+
+  undo_clip:
 #endif  /* not USE_CAIRO */
 
   x_reset_clip_rectangles (f, gc);
@@ -13170,6 +13197,12 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, 
Atom xaction,
 
 #ifdef HAVE_XINPUT2
 
+/* Disable per-device keyboard focus tracking within X toolkit and GTK
+   2.x builds, given that these builds receive updates to the keyboard
+   input focus as core events.  */
+
+#if !defined USE_X_TOOLKIT && (!defined USE_GTK || defined HAVE_GTK3)
+
 /* Since the input extension assigns a keyboard focus to each master
    device, there is no longer a 1:1 correspondence between the
    selected frame and the focus frame immediately after the keyboard
@@ -13381,6 +13414,8 @@ xi_focus_handle_for_device (struct x_display_info 
*dpyinfo,
   xi_handle_focus_change (dpyinfo);
 }
 
+#endif /* !USE_X_TOOLKIT && (!USE_GTK || HAVE_GTK3) */
+
 static void
 xi_handle_delete_frame (struct x_display_info *dpyinfo,
                        struct frame *f)
@@ -13409,6 +13444,7 @@ xi_handle_interaction (struct x_display_info *dpyinfo,
                       struct frame *f, struct xi_device_t *device,
                       Time time)
 {
+#if !defined USE_X_TOOLKIT && (!defined USE_GTK || defined HAVE_GTK3)
   bool change;
 
   /* If DEVICE is a pointer, use its attached keyboard device.  */
@@ -13435,6 +13471,7 @@ xi_handle_interaction (struct x_display_info *dpyinfo,
   /* If F isn't currently focused, update the focus state.  */
   if (change && f != dpyinfo->x_focus_frame)
     xi_handle_focus_change (dpyinfo);
+#endif /* !USE_X_TOOLKIT && (!USE_GTK || HAVE_GTK3) */
 }
 
 /* Return whether or not XEV actually represents a change in the
@@ -20260,20 +20297,23 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
          /* See if keysym should make Emacs quit.  */
 
-         if (keysym == dpyinfo->quit_keysym
-             && (xkey.time - dpyinfo->quit_keysym_time
-                 <= 350))
+         if (dpyinfo->quit_keysym)
            {
-             Vquit_flag = Qt;
-             goto done_keysym;
-           }
+             if (keysym == dpyinfo->quit_keysym
+                 && (xkey.time - dpyinfo->quit_keysym_time
+                     <= 350))
+               {
+                 Vquit_flag = Qt;
+                 goto done_keysym;
+               }
 
-         if (keysym == dpyinfo->quit_keysym)
-           {
-             /* Otherwise, set the last time that keysym was
-                pressed.  */
-             dpyinfo->quit_keysym_time = xkey.time;
-             goto done_keysym;
+             if (keysym == dpyinfo->quit_keysym)
+               {
+                 /* Otherwise, set the last time that keysym was
+                    pressed.  */
+                 dpyinfo->quit_keysym_time = xkey.time;
+                 goto done_keysym;
+               }
            }
 
           /* If not using XIM/XIC, and a compose sequence is in progress,
@@ -20575,6 +20615,14 @@ handle_one_xevent (struct x_display_info *dpyinfo,
       }
 #endif
 
+      /* Apply the fix for bug#57468 on GTK 3.x and no toolkit builds,
+        but not GTK+ 2.x and X toolkit builds, where it is required
+        to treat implicit focus correctly.  (bug#65919) */
+#if defined USE_X_TOOLKIT || (defined USE_GTK && !defined HAVE_GTK3)
+      if (x_top_window_to_frame (dpyinfo, event->xcrossing.window))
+       x_detect_focus_change (dpyinfo, any, event, &inev.ie);
+#endif /* defined USE_X_TOOLKIT || (defined USE_GTK && !defined HAVE_GTK3) */
+
 #ifdef HAVE_XINPUT2
       /* For whatever reason, the X server continues to deliver
         EnterNotify and LeaveNotify events despite us selecting for
@@ -20585,10 +20633,14 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
       if (dpyinfo->supports_xi2)
        goto OTHER;
-#endif
+#endif /* HAVE_XINPUT2 */
 
+      /* Apply the fix for bug#57468 on GTK 3.x and no toolkit
+        builds.  */
+#if !defined USE_X_TOOLKIT || (!defined USE_GTK || defined HAVE_GTK3)
       if (x_top_window_to_frame (dpyinfo, event->xcrossing.window))
        x_detect_focus_change (dpyinfo, any, event, &inev.ie);
+#endif /* !defined USE_X_TOOLKIT || (!defined USE_GTK || defined HAVE_GTK3) */
 
       f = any;
 
@@ -20649,8 +20701,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
         https://lists.gnu.org/archive/html/emacs-devel/2017-02/msg00133.html.
         That is fixed above but bites us here again.
 
-        The option x_set_frame_visibility_more_laxly allows to override
-        the default behavior (Bug#49955, Bug#53298).  */
+        The option x_set_frame_visibility_more_laxly enables
+        overriding the default behavior (Bug#49955, Bug#53298).  */
       if (EQ (x_set_frame_visibility_more_laxly, Qfocus_in)
          || EQ (x_set_frame_visibility_more_laxly, Qt))
 #endif /* USE_GTK */
@@ -20673,6 +20725,14 @@ handle_one_xevent (struct x_display_info *dpyinfo,
       x_display_set_last_user_time (dpyinfo, event->xcrossing.time,
                                    event->xcrossing.send_event, false);
 
+      /* Apply the fix for bug#57468 on GTK 3.x and no toolkit builds,
+        but not GTK+ 2.x and X toolkit builds, where it is required
+        to treat implicit focus correctly.  */
+#if defined USE_X_TOOLKIT || (defined USE_GTK && !defined HAVE_GTK3)
+      if (x_top_window_to_frame (dpyinfo, event->xcrossing.window))
+       x_detect_focus_change (dpyinfo, any, event, &inev.ie);
+#endif /* defined USE_X_TOOLKIT || (defined USE_GTK && !defined HAVE_GTK3) */
+
 #ifdef HAVE_XINPUT2
       /* For whatever reason, the X server continues to deliver
         EnterNotify and LeaveNotify events despite us selecting for
@@ -20685,7 +20745,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
        {
 #if !defined USE_X_TOOLKIT && (!defined USE_GTK || defined HAVE_GTK3)
          goto OTHER;
-#else
+#else /* USE_X_TOOLKIT || (USE_GTK && !HAVE_GTK3) */
          /* Unfortunately, X toolkit popups generate LeaveNotify
             events due to the core grabs they acquire (and our
             releasing of the device grab).  This leads to the mouse
@@ -20694,9 +20754,16 @@ handle_one_xevent (struct x_display_info *dpyinfo,
             outside the frame, in which case no XI_Enter event is
             generated for the grab.  */
          goto just_clear_mouse_face;
-#endif
+#endif /* !USE_X_TOOLKIT && (!USE_GTK || HAVE_GTK3) */
        }
-#endif
+#endif /* HAVE_XINPUT2 */
+
+      /* Apply the fix for bug#57468 on GTK 3.x and no toolkit
+        builds.  */
+#if !defined USE_X_TOOLKIT || (!defined USE_GTK || defined HAVE_GTK3)
+      if (x_top_window_to_frame (dpyinfo, event->xcrossing.window))
+       x_detect_focus_change (dpyinfo, any, event, &inev.ie);
+#endif /* !defined USE_X_TOOLKIT || (!defined USE_GTK || defined HAVE_GTK3) */
 
 #ifdef HAVE_XWIDGETS
       {
@@ -20712,9 +20779,6 @@ handle_one_xevent (struct x_display_info *dpyinfo,
       }
 #endif
 
-      if (x_top_window_to_frame (dpyinfo, event->xcrossing.window))
-       x_detect_focus_change (dpyinfo, any, event, &inev.ie);
-
 #if defined HAVE_XINPUT2                                               \
   && (defined USE_X_TOOLKIT || (defined USE_GTK && !defined HAVE_GTK3))
     just_clear_mouse_face:
@@ -22082,6 +22146,10 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
        switch (event->xcookie.evtype)
          {
+           /* XI focus events aren't employed under X toolkit or GTK+
+              2.x because windows created by these two toolkits are
+              incompatible with input extension focus events.  */
+#if !defined USE_X_TOOLKIT && (!defined USE_GTK || defined HAVE_GTK3)
          case XI_FocusIn:
            {
              XIFocusInEvent *focusin;
@@ -22091,17 +22159,19 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
 #ifdef USE_GTK
              /* Some WMs (e.g. Mutter in Gnome Shell), don't unmap
-                minimized/iconified windows; thus, for those WMs we won't get
-                a MapNotify when unminimizing/deiconifying.  Check here if we
-                are deiconizing a window (Bug42655).
+                minimized/iconified windows; thus, for those WMs we
+                won't get a MapNotify when unminimizing/deiconifying.
+                Check here if we are deiconizing a window (Bug42655).
 
-                But don't do that by default on GTK since it may cause a plain
-                invisible frame get reported as iconified, compare
+                But don't do that by default on GTK since it may
+                cause a plain invisible frame get reported as
+                iconified, compare
                 
https://lists.gnu.org/archive/html/emacs-devel/2017-02/msg00133.html.
                 That is fixed above but bites us here again.
 
-                The option x_set_frame_visibility_more_laxly allows to override
-                the default behavior (Bug#49955, Bug#53298).  */
+                The option x_set_frame_visibility_more_laxly enables
+                overriding the default behavior (Bug#49955,
+                Bug#53298).  */
              if (EQ (x_set_frame_visibility_more_laxly, Qfocus_in)
                  || EQ (x_set_frame_visibility_more_laxly, Qt))
 #endif /* USE_GTK */
@@ -22132,6 +22202,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
              goto XI_OTHER;
            }
+#endif /* !USE_X_TOOLKIT && (!USE_GTK || HAVE_GTK3) */
 
          case XI_Enter:
            {
@@ -22177,8 +22248,11 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                 passive focus from non-top windows at all, since they
                 are an inferiors of the frame's top window, which will
                 get virtual events.  */
+
+#if !defined USE_X_TOOLKIT && (!defined USE_GTK || defined HAVE_GTK3)
              if (any)
                xi_focus_handle_for_device (dpyinfo, any, xi_event);
+#endif /* !USE_X_TOOLKIT && (!USE_GTK || HAVE_GTK3) */
 
              if (!any)
                any = x_any_window_to_frame (dpyinfo, enter->event);
@@ -22358,8 +22432,10 @@ handle_one_xevent (struct x_display_info *dpyinfo,
              }
 #endif
 
+#if !defined USE_X_TOOLKIT && (!defined USE_GTK || defined HAVE_GTK3)
              if (any)
                xi_focus_handle_for_device (dpyinfo, any, xi_event);
+#endif /* !USE_X_TOOLKIT && (!USE_GTK || HAVE_GTK3) */
 
 #ifndef USE_X_TOOLKIT
              f = x_top_window_to_frame (dpyinfo, leave->event);
@@ -24154,20 +24230,23 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
                  /* See if keysym should make Emacs quit.  */
 
-                 if (keysym == dpyinfo->quit_keysym
-                     && (xev->time - dpyinfo->quit_keysym_time
-                         <= 350))
+                 if (dpyinfo->quit_keysym)
                    {
-                     Vquit_flag = Qt;
-                     goto xi_done_keysym;
-                   }
+                     if (keysym == dpyinfo->quit_keysym
+                         && (xev->time - dpyinfo->quit_keysym_time
+                             <= 350))
+                       {
+                         Vquit_flag = Qt;
+                         goto xi_done_keysym;
+                       }
 
-                 if (keysym == dpyinfo->quit_keysym)
-                   {
-                     /* Otherwise, set the last time that keysym was
-                        pressed.  */
-                     dpyinfo->quit_keysym_time = xev->time;
-                     goto xi_done_keysym;
+                     if (keysym == dpyinfo->quit_keysym)
+                       {
+                         /* Otherwise, set the last time that keysym
+                            was pressed.  */
+                         dpyinfo->quit_keysym_time = xev->time;
+                         goto xi_done_keysym;
+                       }
                    }
 
                  /* First deal with keysyms which have defined
@@ -24430,9 +24509,11 @@ handle_one_xevent (struct x_display_info *dpyinfo,
              XIDeviceInfo *info;
              int i, ndevices, n_disabled, *disabled;
              struct xi_device_t *device;
+#if !defined USE_X_TOOLKIT && (!defined USE_GTK || defined HAVE_GTK3)
              bool any_changed;
 
              any_changed = false;
+#endif /* !USE_X_TOOLKIT && (!USE_GTK || HAVE_GTK3) */
              hev = (XIHierarchyEvent *) xi_event;
              disabled = SAFE_ALLOCA (sizeof *disabled * hev->num_info);
              n_disabled = 0;
@@ -24449,10 +24530,12 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                          xi_disable_devices (dpyinfo, disabled, n_disabled);
                          n_disabled = 0;
 
+#if !defined USE_X_TOOLKIT && (!defined USE_GTK || defined HAVE_GTK3)
                          /* This flag really just means that disabled
                             devices were handled early and should be
                             used in conjunction with n_disabled.  */
                          any_changed = true;
+#endif /* !USE_X_TOOLKIT && (!USE_GTK || HAVE_GTK3) */
                        }
 
                      /* Under unknown circumstances, multiple
@@ -24519,12 +24602,14 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                 event.  */
              xi_disable_devices (dpyinfo, disabled, n_disabled);
 
+#if !defined USE_X_TOOLKIT && (!defined USE_GTK || defined HAVE_GTK3)
              /* If the device hierarchy has been changed, recompute
                 focus.  This might seem like a micro-optimization but
                 it actually keeps the focus from changing in some
                 cases where it would be undesierable.  */
              if (any_changed || n_disabled)
                xi_handle_focus_change (dpyinfo);
+#endif /* !USE_X_TOOLKIT && (!USE_GTK || HAVE_GTK3) */
 
              goto XI_OTHER;
            }
@@ -25594,13 +25679,17 @@ XTread_socket (struct terminal *terminal, struct 
input_event *hold_quit)
 /* Set clipping for output in glyph row ROW.  W is the window in which
    we operate.  GC is the graphics context to set clipping in.
 
+   If RECT_RETURN is non-NULL, return the clip rectangle within
+   *RECT_RETURN.
+
    ROW may be a text row or, e.g., a mode line.  Text rows must be
    clipped to the interior of the window dedicated to text display,
    mode lines must be clipped to the whole window.  */
 
 static void
 x_clip_to_row (struct window *w, struct glyph_row *row,
-              enum glyph_row_area area, GC gc)
+              enum glyph_row_area area, GC gc,
+              XRectangle *rect_return)
 {
   struct frame *f = XFRAME (WINDOW_FRAME (w));
   XRectangle clip_rect;
@@ -25615,6 +25704,9 @@ x_clip_to_row (struct window *w, struct glyph_row *row,
   clip_rect.height = row->visible_height;
 
   x_set_clip_rectangles (f, gc, &clip_rect, 1);
+
+  if (rect_return)
+    *rect_return = clip_rect;
 }
 
 
@@ -25663,7 +25755,7 @@ x_draw_hollow_cursor (struct window *w, struct 
glyph_row *row)
        wd -= 1;
     }
   /* Set clipping, draw the rectangle, and reset clipping again.  */
-  x_clip_to_row (w, row, TEXT_AREA, gc);
+  x_clip_to_row (w, row, TEXT_AREA, gc, NULL);
   x_draw_rectangle (f, gc, x, y, wd, h - 1);
   x_reset_clip_rectangles (f, gc);
 }
@@ -25733,7 +25825,7 @@ x_draw_bar_cursor (struct window *w, struct glyph_row 
*row, int width, enum text
          FRAME_DISPLAY_INFO (f)->scratch_cursor_gc = gc;
        }
 
-      x_clip_to_row (w, row, TEXT_AREA, gc);
+      x_clip_to_row (w, row, TEXT_AREA, gc, NULL);
 
       if (kind == BAR_CURSOR)
        {
@@ -29452,6 +29544,7 @@ x_free_frame_resources (struct frame *f)
     dpyinfo->last_mouse_frame = NULL;
 
 #ifdef HAVE_XINPUT2
+#if !defined USE_X_TOOLKIT && (!defined USE_GTK || defined HAVE_GTK3)
   /* Consider a frame being unfocused with no following FocusIn event
      while an older focus from another seat exists.  The client
      pointer should then revert to the other seat, so handle potential
@@ -29459,7 +29552,8 @@ x_free_frame_resources (struct frame *f)
 
   if (dpyinfo->supports_xi2)
     xi_handle_focus_change (dpyinfo);
-#endif
+#endif /* !USE_X_TOOLKIT && (!USE_GTK || HAVE_GTK3) */
+#endif /* HAVE_XINPUT2 */
 
   unblock_input ();
 }
@@ -32724,17 +32818,12 @@ frame placement via frame parameters, 
`set-frame-position', and
 
 This is used to support quitting on devices that do not have any kind
 of physical keyboard, or where the physical keyboard is incapable of
-entering `C-g'.  It defaults to `XF86XK_AudioLowerVolume' on XFree86
-and X.Org servers, and is unset.
+entering `C-g'.
 
 The value is an alist associating between strings, describing X server
 vendor names, and a single number describing the keysym to use.  The
 keysym to use for each display connection is determined upon
 connection setup, and does not reflect further changes to this
 variable.  */);
-  Vx_quit_keysym
-    = list2 (Fcons (build_string ("The X.Org Foundation"),
-                   make_int (269025041)),
-            Fcons (build_string ("The XFree86 Project, Inc."),
-                   make_int (269025041)));
+  Vx_quit_keysym = Qnil;
 }
diff --git a/test/infra/Dockerfile.emba b/test/infra/Dockerfile.emba
index 584e4444dc1..e29098ec270 100644
--- a/test/infra/Dockerfile.emba
+++ b/test/infra/Dockerfile.emba
@@ -126,6 +126,7 @@ RUN src/emacs -Q --batch \
       (java "https://github.com/tree-sitter/tree-sitter-java";) \
       (javascript "https://github.com/tree-sitter/tree-sitter-javascript";) \
       (json "https://github.com/tree-sitter/tree-sitter-json";) \
+      (lua "https://github.com/MunifTanjim/tree-sitter-lua";) \
       (python "https://github.com/tree-sitter/tree-sitter-python";) \
       (ruby "https://github.com/tree-sitter/tree-sitter-ruby";) \
       (tsx "https://github.com/tree-sitter/tree-sitter-typescript"; "master" 
"tsx/src") \
diff --git a/test/infra/test-jobs.yml b/test/infra/test-jobs.yml
index 2f6e0dab4d5..1f5d607eda4 100644
--- a/test/infra/test-jobs.yml
+++ b/test/infra/test-jobs.yml
@@ -580,6 +580,7 @@ test-src-inotify:
       lisp/progmodes/go-ts-mode-tests.log
       lisp/progmodes/heex-ts-mode-tests.log
       lisp/progmodes/java-ts-mode-tests.log
+      lisp/progmodes/lua-ts-mode-tests.log
       lisp/progmodes/ruby-ts-mode-tests.log
       lisp/progmodes/typescript-ts-mode-tests.log
       src/treesit-tests.log
diff --git a/test/lisp/autorevert-tests.el b/test/lisp/autorevert-tests.el
index 8dbb5d2a496..e01ce82858b 100644
--- a/test/lisp/autorevert-tests.el
+++ b/test/lisp/autorevert-tests.el
@@ -257,7 +257,7 @@ This expects `auto-revert--messages' to be bound by
   ;; Repeated unpredictable failures, bug#32645.
   :tags '(:unstable)
   ;; Unlikely to be hydra-specific?
-  ;; (skip-unless (not (getenv "EMACS_HYDRA_CI")))
+  ;; (skip-when (getenv "EMACS_HYDRA_CI"))
   (with-auto-revert-test
    (ert-with-temp-file tmpfile
      (let (;; Try to catch bug#32645.
diff --git a/test/lisp/emacs-lisp/benchmark-tests.el 
b/test/lisp/emacs-lisp/benchmark-tests.el
index 99b5b142c37..7fe3be2157f 100644
--- a/test/lisp/emacs-lisp/benchmark-tests.el
+++ b/test/lisp/emacs-lisp/benchmark-tests.el
@@ -25,8 +25,8 @@
 (ert-deftest benchmark-tests ()
   ;; Avoid fork failures on Cygwin.  See bug#62450 and etc/PROBLEMS
   ;; ("Fork failures in a build with native compilation").
-  (skip-unless (not (and (eq system-type 'cygwin)
-                         (featurep 'native-compile))))
+  (skip-when (and (eq system-type 'cygwin)
+                  (featurep 'native-compile)))
   (let (str t-long t-short m)
     (should (consp (benchmark-run nil (setq m (1+ 0)))))
     (should (consp (benchmark-run 1 (setq m (1+ 0)))))
diff --git a/test/lisp/emacs-lisp/byte-run-tests.el 
b/test/lisp/emacs-lisp/byte-run-tests.el
new file mode 100644
index 00000000000..59ce24ad251
--- /dev/null
+++ b/test/lisp/emacs-lisp/byte-run-tests.el
@@ -0,0 +1,32 @@
+;;; byte-run-tests.el --- Tests for byte-run.el  -*- lexical-binding: t -*-
+
+;; Copyright (C) 2023 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(require 'ert)
+
+(ert-deftest make-obsolete ()
+  (should-error (make-obsolete nil 'foo "30.1"))
+  (should-error (make-obsolete t 'foo "30.1") ))
+
+(ert-deftest make-obsolete-variable ()
+  (should-error (make-obsolete-variable nil 'foo "30.1"))
+  (should-error (make-obsolete-variable t 'foo "30.1")))
+
+;;; byte-run-tests.el ends here
diff --git a/test/lisp/emacs-lisp/bytecomp-tests.el 
b/test/lisp/emacs-lisp/bytecomp-tests.el
index 262cbd62fa0..8469313bd24 100644
--- a/test/lisp/emacs-lisp/bytecomp-tests.el
+++ b/test/lisp/emacs-lisp/bytecomp-tests.el
@@ -838,6 +838,11 @@ byte-compiled.  Run with dynamic binding."
         (should (equal (bytecomp-tests--eval-interpreted form)
                        (bytecomp-tests--eval-compiled form)))))))
 
+(defmacro bytecomp-tests--with-fresh-warnings (&rest body)
+  `(let ((macroexp--warned            ; oh dear
+          (make-hash-table :test #'equal :weakness 'key)))
+     ,@body))
+
 (defun test-byte-comp-compile-and-load (compile &rest forms)
   (declare (indent 1))
   (ert-with-temp-file elfile
@@ -852,7 +857,8 @@ byte-compiled.  Run with dynamic binding."
       (if compile
           (let ((byte-compile-dest-file-function
                  (lambda (e) elcfile)))
-            (byte-compile-file elfile)))
+            (bytecomp-tests--with-fresh-warnings
+             (byte-compile-file elfile))))
       (load elfile nil 'nomessage))))
 
 (ert-deftest test-byte-comp-macro-expansion ()
@@ -923,23 +929,25 @@ byte-compiled.  Run with dynamic binding."
   (declare (indent 1))
   (with-current-buffer (get-buffer-create "*Compile-Log*")
      (let ((inhibit-read-only t)) (erase-buffer))
-     (let ((text-quoting-style 'grave)
-           (macroexp--warned            ; oh dear
-            (make-hash-table :test #'equal :weakness 'key)))
        (ert-info ((prin1-to-string form) :prefix "form: ")
-         (byte-compile form)
+         (let ((text-quoting-style 'grave))
+           (bytecomp-tests--with-fresh-warnings
+            (byte-compile form)))
          (ert-info ((prin1-to-string (buffer-string)) :prefix "buffer: ")
            (should (re-search-forward
-                    (string-replace " " "[ \n]+" re-warning))))))))
+                    (string-replace " " "[ \n]+" re-warning)))))))
+
+(defun bytecomp--without-warning-test (form)
+  (bytecomp--with-warning-test "\\`\\'" form))
 
 (ert-deftest bytecomp-warn--ignore ()
   (bytecomp--with-warning-test "unused"
     '(lambda (y) 6))
-  (bytecomp--with-warning-test "\\`\\'" ;No warning!
+  (bytecomp--without-warning-test
     '(lambda (y) (ignore y) 6))
   (bytecomp--with-warning-test "assq"
     '(lambda (x y) (progn (assq x y) 5)))
-  (bytecomp--with-warning-test "\\`\\'" ;No warning!
+  (bytecomp--without-warning-test
     '(lambda (x y) (progn (ignore (assq x y)) 5))))
 
 (ert-deftest bytecomp-warn-wrong-args ()
@@ -964,6 +972,34 @@ byte-compiled.  Run with dynamic binding."
   (bytecomp--with-warning-test "defvar.*foo.*wider than.*characters"
     `(defvar foo t ,bytecomp-tests--docstring)))
 
+(ert-deftest bytecomp-warn-wide-docstring/cl-defsubst ()
+  (bytecomp--without-warning-test
+   `(cl-defsubst short-name ()
+      "Do something."))
+  (bytecomp--without-warning-test
+   `(cl-defsubst long-name-with-less-80-characters-but-still-quite-a-bit ()
+      "Do something."))
+  (bytecomp--with-warning-test "wider than.*characters"
+   `(cl-defsubst 
long-name-with-more-than-80-characters-yes-this-is-a-very-long-name-but-why-not!!
 ()
+      "Do something.")))
+
+(ert-deftest bytecomp-warn-wide-docstring/cl-defstruct ()
+  (bytecomp--without-warning-test
+   `(cl-defstruct short-name
+      field))
+  (bytecomp--without-warning-test
+   `(cl-defstruct short-name
+      long-name-with-less-80-characters-but-still-quite-a-bit))
+  (bytecomp--without-warning-test
+   `(cl-defstruct long-name-with-less-80-characters-but-still-quite-a-bit
+      field))
+  (bytecomp--with-warning-test "wider than.*characters"
+    `(cl-defstruct short-name
+       
long-name-with-more-than-80-characters-yes-this-is-a-very-long-name-but-why-not!!))
+  (bytecomp--with-warning-test "wider than.*characters"
+    `(cl-defstruct 
long-name-with-more-than-80-characters-yes-this-is-a-very-long-name-but-why-not!!
+       field)))
+
 (ert-deftest bytecomp-warn-quoted-condition ()
   (bytecomp--with-warning-test
       "Warning: `condition-case' condition should not be quoted: 'arith-error"
@@ -1064,7 +1100,7 @@ byte-compiled.  Run with dynamic binding."
                             "fails to specify containing group")
 
 (bytecomp--define-warning-file-test "warn-defcustom-notype.el"
-                            "fails to specify type")
+                            "missing :type keyword parameter")
 
 (bytecomp--define-warning-file-test "warn-defvar-lacks-prefix.el"
                             "var.*foo.*lacks a prefix")
@@ -1507,6 +1543,15 @@ literals (Bug#20852)."
    '((suspicious unwind-protect))
    "Warning: `unwind-protect' without unwind forms")
 
+  (test-suppression
+   '(defun zot (x)
+      (cond
+       ((zerop x) 'zero)
+       (t 'nonzero)
+       (happy puppy)))
+   '((suspicious cond))
+   "Warning: Useless clause following default `cond' clause")
+
   (test-suppression
    '(defun zot ()
       (let ((_ 1))
@@ -1829,12 +1874,53 @@ EXPECTED-POINT BINDINGS (MODES \\='\\='(ruby-mode 
js-mode python-mode)) \
 (TEST-IN-COMMENTS t) (TEST-IN-STRINGS t) (TEST-IN-CODE t) \
 (FIXTURE-FN \\='#\\='electric-pair-mode))" fill-column)))
 
-(ert-deftest bytecomp-test-defcustom-type-quoted ()
-  (should-not (byte-compile--defcustom-type-quoted 'integer))
-  (should-not (byte-compile--defcustom-type-quoted
-               '(choice (const :tag "foo" bar))))
-  (should (byte-compile--defcustom-type-quoted
-           '(choice (const :tag "foo" 'bar)))))
+(ert-deftest bytecomp-test-defcustom-type ()
+  (cl-flet ((dc (type) `(defcustom mytest nil "doc" :type ',type :group 
'test)))
+    (bytecomp--with-warning-test
+     (rx "type should not be quoted") (dc ''integer))
+    (bytecomp--with-warning-test
+     (rx "type should not be quoted") (dc '(choice '(repeat boolean))))
+    (bytecomp--with-warning-test
+     (rx "misplaced :tag keyword") (dc '(choice (const b :tag "a"))))
+    (bytecomp--with-warning-test
+     (rx "`choice' without any types inside") (dc '(choice :tag "a")))
+    (bytecomp--with-warning-test
+     (rx "`other' not last in `choice'")
+     (dc '(choice (const a) (other b) (const c))))
+    (bytecomp--with-warning-test
+     (rx "duplicated value in `choice': `a'")
+     (dc '(choice (const a) (const b) (const a))))
+    (bytecomp--with-warning-test
+     (rx "duplicated :tag string in `choice': \"X\"")
+     (dc '(choice (const :tag "X" a) (const :tag "Y" b) (other :tag "X" c))))
+    (bytecomp--with-warning-test
+     (rx "`cons' requires 2 type specs, found 1")
+     (dc '(cons :tag "a" integer)))
+    (bytecomp--with-warning-test
+     (rx "`repeat' without type specs")
+     (dc '(repeat :tag "a")))
+    (bytecomp--with-warning-test
+     (rx "`const' with too many values")
+     (dc '(const :tag "a" x y)))
+    (bytecomp--with-warning-test
+     (rx "`const' with quoted value")
+     (dc '(const :tag "a" 'x)))
+    (bytecomp--with-warning-test
+     (rx "`bool' is not a valid type")
+     (dc '(bool :tag "a")))
+    (bytecomp--with-warning-test
+     (rx "irregular type `:tag'")
+     (dc '(:tag "a")))
+    (bytecomp--with-warning-test
+     (rx "irregular type `\"string\"'")
+     (dc '(list "string")))
+    (bytecomp--with-warning-test
+     (rx "`list' without arguments")
+     (dc 'list))
+    (bytecomp--with-warning-test
+     (rx "`integerp' is not a valid type")
+     (dc 'integerp))
+    ))
 
 (ert-deftest bytecomp-function-attributes ()
   ;; Check that `byte-compile' keeps the declarations, interactive spec and
diff --git a/test/lisp/emacs-lisp/cl-lib-tests.el 
b/test/lisp/emacs-lisp/cl-lib-tests.el
index b14731c4d0a..0995e71db4e 100644
--- a/test/lisp/emacs-lisp/cl-lib-tests.el
+++ b/test/lisp/emacs-lisp/cl-lib-tests.el
@@ -530,27 +530,29 @@
 
 (ert-deftest old-struct ()
   (cl-defstruct foo x)
-  (let ((x (vector 'cl-struct-foo))
-        (saved cl-old-struct-compat-mode))
-    (cl-old-struct-compat-mode -1)
-    (should (eq (type-of x) 'vector))
+  (with-suppressed-warnings ((obsolete cl-old-struct-compat-mode))
+    (let ((x (vector 'cl-struct-foo))
+          (saved cl-old-struct-compat-mode))
+      (cl-old-struct-compat-mode -1)
+      (should (eq (type-of x) 'vector))
 
-    (cl-old-struct-compat-mode 1)
-    (defvar cl-struct-foo)
-    (let ((cl-struct-foo (cl--struct-get-class 'foo)))
-      (setf (symbol-function 'cl-struct-foo) :quick-object-witness-check)
-      (should (eq (type-of x) 'foo))
-      (should (eq (type-of (vector 'foo)) 'vector)))
+      (cl-old-struct-compat-mode 1)
+      (defvar cl-struct-foo)
+      (let ((cl-struct-foo (cl--struct-get-class 'foo)))
+        (setf (symbol-function 'cl-struct-foo) :quick-object-witness-check)
+        (should (eq (type-of x) 'foo))
+        (should (eq (type-of (vector 'foo)) 'vector)))
 
-    (cl-old-struct-compat-mode (if saved 1 -1))))
+      (cl-old-struct-compat-mode (if saved 1 -1)))))
 
 (ert-deftest cl-lib-old-struct ()
-  (let ((saved cl-old-struct-compat-mode))
-    (cl-old-struct-compat-mode -1)
-    (cl-struct-define 'foo "" 'cl-structure-object nil nil nil
-                      'cl-struct-foo-tags 'cl-struct-foo t)
-    (should cl-old-struct-compat-mode)
-    (cl-old-struct-compat-mode (if saved 1 -1))))
+  (with-suppressed-warnings ((obsolete cl-old-struct-compat-mode))
+    (let ((saved cl-old-struct-compat-mode))
+      (cl-old-struct-compat-mode -1)
+      (cl-struct-define 'foo "" 'cl-structure-object nil nil nil
+                        'cl-struct-foo-tags 'cl-struct-foo t)
+      (should cl-old-struct-compat-mode)
+      (cl-old-struct-compat-mode (if saved 1 -1)))))
 
 (ert-deftest cl-constantly ()
   (should (equal (mapcar (cl-constantly 3) '(a b c d))
diff --git a/test/lisp/emacs-lisp/cl-print-tests.el 
b/test/lisp/emacs-lisp/cl-print-tests.el
index 3073a42e39d..631dd834a68 100644
--- a/test/lisp/emacs-lisp/cl-print-tests.el
+++ b/test/lisp/emacs-lisp/cl-print-tests.el
@@ -60,18 +60,20 @@
 
 (ert-deftest cl-print-tests-ellipsis-string ()
   "Ellipsis expansion works in strings."
-  (let ((print-length 4)
-        (print-level 3))
+  (let ((cl-print-string-length 4))
     (cl-print-tests-check-ellipsis-expansion
      "abcdefg" "\"abcd...\"" "efg")
     (cl-print-tests-check-ellipsis-expansion
      "abcdefghijk" "\"abcd...\"" "efgh...")
-    (cl-print-tests-check-ellipsis-expansion
-     '(1 (2 (3 #("abcde" 0 5 (test t)))))
-     "(1 (2 (3 ...)))" "#(\"abcd...\" 0 5 (test t))")
-    (cl-print-tests-check-ellipsis-expansion
-     #("abcd" 0 1 (bold t) 1 2 (invisible t) 3 4 (italic t))
-     "#(\"abcd\" 0 1 (bold t) ...)" "1 2 (invisible t) ...")))
+    (let ((print-length 4)
+          (print-level 3))
+      (cl-print-tests-check-ellipsis-expansion
+       '(1 (2 (3 #("abcde" 0 5 (test t)))))
+       "(1 (2 (3 ...)))" "#(\"abcd...\" 0 5 (test t))"))
+    (let ((print-length 4))
+      (cl-print-tests-check-ellipsis-expansion
+       #("abcd" 0 1 (bold t) 1 2 (invisible t) 3 4 (italic t))
+       "#(\"abcd\" 0 1 (bold t) ...)" "1 2 (invisible t) ..."))))
 
 (ert-deftest cl-print-tests-ellipsis-struct ()
   "Ellipsis expansion works in structures."
diff --git a/test/lisp/emacs-lisp/ert-tests.el 
b/test/lisp/emacs-lisp/ert-tests.el
index 7713a0f6e38..bb3de111e3e 100644
--- a/test/lisp/emacs-lisp/ert-tests.el
+++ b/test/lisp/emacs-lisp/ert-tests.el
@@ -304,6 +304,20 @@ failed or if there was a problem."
   (cl-macrolet ((test () (error "Foo")))
     (should-error (test))))
 
+(ert-deftest ert-test-skip-when ()
+  ;; Don't skip.
+  (let ((test (make-ert-test :body (lambda () (skip-when nil)))))
+    (let ((result (ert-run-test test)))
+      (should (ert-test-passed-p result))))
+  ;; Skip.
+  (let ((test (make-ert-test :body (lambda () (skip-when t)))))
+    (let ((result (ert-run-test test)))
+      (should (ert-test-skipped-p result))))
+  ;; Skip in case of error.
+  (let ((test (make-ert-test :body (lambda () (skip-when (error "Foo"))))))
+    (let ((result (ert-run-test test)))
+      (should (ert-test-skipped-p result)))))
+
 (ert-deftest ert-test-skip-unless ()
   ;; Don't skip.
   (let ((test (make-ert-test :body (lambda () (skip-unless t)))))
diff --git a/test/lisp/emacs-lisp/find-func-tests.el 
b/test/lisp/emacs-lisp/find-func-tests.el
index 7251b76157b..59ecb5ab187 100644
--- a/test/lisp/emacs-lisp/find-func-tests.el
+++ b/test/lisp/emacs-lisp/find-func-tests.el
@@ -32,7 +32,7 @@
 (ert-deftest find-func-tests--library-completion () ;bug#43393
   ;; FIXME: How can we make this work in batch (see also
   ;; `mule-cmds--test-universal-coding-system-argument')?
-  ;; (skip-unless (not noninteractive))
+  ;; (skip-when noninteractive)
   ;; Check that `partial-completion' works when completing library names.
   (should (equal "org/org"
                  (ert-simulate-keys
diff --git a/test/lisp/emacs-lisp/package-tests.el 
b/test/lisp/emacs-lisp/package-tests.el
index 113b4ec12a8..e44ad3677d1 100644
--- a/test/lisp/emacs-lisp/package-tests.el
+++ b/test/lisp/emacs-lisp/package-tests.el
@@ -125,6 +125,7 @@
             abbreviated-home-dir
             package--initialized
             package-alist
+            package-selected-packages
             ,@(if update-news
                   '(package-update-news-on-upload t)
                 (list (cl-gensym)))
@@ -307,6 +308,21 @@ Must called from within a `tar-mode' buffer."
       (package-delete (cadr (assq 'v7-withsub package-alist))))
     ))
 
+(ert-deftest package-test-bug65475 ()
+  "Deleting the last package clears `package-selected-packages'."
+  (with-package-test (:basedir (ert-resource-directory))
+    (package-initialize)
+    (let* ((pkg-el "simple-single-1.3.el")
+           (source-file (expand-file-name pkg-el (ert-resource-directory))))
+      (package-install-file source-file)
+      (should package-alist)
+      (should package-selected-packages)
+      (let ((desc (cadr (assq 'simple-single package-alist))))
+        (should desc)
+        (package-delete desc))
+      (should-not package-alist)
+      (should-not package-selected-packages))))
+
 (ert-deftest package-test-install-file-EOLs ()
   "Install same file multiple time with `package-install-file'
 but with a different end of line convention (bug#48137)."
diff --git a/test/lisp/erc/erc-scenarios-scrolltobottom-relaxed.el 
b/test/lisp/erc/erc-scenarios-scrolltobottom-relaxed.el
new file mode 100644
index 00000000000..7d256bf711b
--- /dev/null
+++ b/test/lisp/erc/erc-scenarios-scrolltobottom-relaxed.el
@@ -0,0 +1,140 @@
+;;; erc-scenarios-scrolltobottom-relaxed.el --- erc-scrolltobottom-relaxed -*- 
lexical-binding: t -*-
+
+;; Copyright (C) 2023 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;; TODO assert behavior of prompt input spanning multiple lines, with
+;; and without line endings.
+
+;;; Code:
+
+(require 'ert-x)
+(eval-and-compile
+  (let ((load-path (cons (ert-resource-directory) load-path)))
+    (require 'erc-scenarios-common)))
+
+(require 'erc-goodies)
+
+(ert-deftest erc-scenarios-scrolltobottom--relaxed ()
+  :tags '(:expensive-test)
+  (when (version< emacs-version "29") (ert-skip "Times out"))
+
+  (should-not erc-scrolltobottom-all)
+
+  (erc-scenarios-common-with-noninteractive-in-term
+      ((erc-scenarios-common-dialog "scrolltobottom")
+       (dumb-server (erc-d-run "localhost" t 'help))
+       (port (process-contact dumb-server :service))
+       (erc-modules `(scrolltobottom fill-wrap ,@erc-modules))
+       (erc-scrolltobottom-all t)
+       (erc-scrolltobottom-relaxed t)
+       (erc-server-flood-penalty 0.1)
+       (expect (erc-d-t-make-expecter))
+       lower upper)
+
+    (ert-info ("Connect")
+      (with-current-buffer (erc :server "127.0.0.1"
+                                :port port
+                                :full-name "tester"
+                                :nick "tester")
+        (funcall expect 10 "debug mode")))
+
+    (with-current-buffer "foonet"
+      (should (looking-at " and"))
+      (set-window-buffer nil (current-buffer))
+      (delete-other-windows)
+      (split-window-below 15)
+      (recenter 0)
+
+      (ert-info ("Moving into prompt does not trigger scroll")
+        (with-selected-window (next-window)
+          (should-not (erc-scenarios-common--at-win-end-p))
+          (recenter 0)
+          (goto-char (1- erc-insert-marker))
+          (execute-kbd-macro "\C-n")
+          (should-not (erc-scenarios-common--at-win-end-p))
+          (should (= (point) (point-max)))
+          (setq lower (count-screen-lines (window-start) (window-point)))))
+
+      (ert-info ("Module `move-to-prompt' still works")
+        ;; Prompt is somewhere in the middle of the window.
+        (should (erc-scenarios-common--above-win-end-p))
+        (should-not (= (point-max) (point)))
+        ;; Hitting a self-insert key triggers `move-to-prompt' but not
+        ;; a scroll (to bottom).
+        (execute-kbd-macro "hi")
+        ;; Prompt and input appear on same line.
+        (should (= (point-max) (point)))
+        (setq upper (count-screen-lines (window-start) (window-point)))
+        (should-not (= upper (window-body-height))))
+
+      (ert-info ("Command `recenter-top-bottom' allowed at prompt")
+        ;; Hitting C-l recenters the window.
+        (should (= upper (count-screen-lines (window-start) (window-point))))
+        (let ((lines (list upper)))
+          (erc-scenarios-common--recenter-top-bottom)
+          (push (count-screen-lines (window-start) (window-point)) lines)
+          (erc-scenarios-common--recenter-top-bottom)
+          (push (count-screen-lines (window-start) (window-point)) lines)
+          (erc-scenarios-common--recenter-top-bottom)
+          (push (count-screen-lines (window-start) (window-point)) lines)
+          (setq lines (delete-dups lines))
+          (should (= (length lines) 4))))
+
+      (ert-info ("Command `beginning-of-buffer' allowed at prompt")
+        ;; Hitting C-< goes to beginning of buffer.
+        (execute-kbd-macro "\M-<")
+        (should (= 1 (point)))
+        (redisplay)
+        (should (zerop (count-screen-lines (window-start) (window-point))))
+        (should (erc-scenarios-common--prompt-past-win-end-p)))
+
+      (ert-info ("New message doesn't trigger scroll when away from prompt")
+        ;; Arriving insertions don't trigger a scroll when away from the
+        ;; prompt.  New output not seen.
+        (erc-cmd-MSG "NickServ help register")
+        (save-excursion (erc-d-t-search-for 10 "End of NickServ"))
+        (should (= 1 (point)))
+        (should (zerop (count-screen-lines (window-start) (window-point))))
+        (should (erc-scenarios-common--prompt-past-win-end-p)))
+
+      (ert-info ("New insertion keeps prompt stationary in other window")
+        (let ((w (next-window)))
+          ;; We're at prompt and completely stationary.
+          (should (>= (window-point w) erc-input-marker))
+          (erc-d-t-wait-for 10
+              (= lower (count-screen-lines (window-start w) (window-point w))))
+          (erc-d-t-ensure-for 0.5
+              (= lower (count-screen-lines (window-start w)
+                                           (window-point w))))))
+
+      (should (= 2 (length (window-list))))
+      (ert-info ("New message does not trigger a scroll when at prompt")
+        ;; Recenter so prompt is above rather than at window's end.
+        (funcall expect 10 "End of NickServ HELP")
+        (recenter 0)
+        (set-window-point nil (point-max))
+        (setq upper (count-screen-lines (window-start) (window-point)))
+        ;; Prompt is somewhere in the middle of the window.
+        (erc-d-t-wait-for 10 (erc-scenarios-common--above-win-end-p))
+        (erc-scenarios-common-say "/msg NickServ help identify")
+        ;; New arriving messages don't move prompt.
+        (erc-d-t-ensure-for 1
+            (= upper (count-screen-lines (window-start) (window-point))))
+        (funcall expect 10 "IDENTIFY lets you login")))))
+
+;;; erc-scenarios-scrolltobottom-relaxed.el ends here
diff --git a/test/lisp/erc/erc-scenarios-scrolltobottom.el 
b/test/lisp/erc/erc-scenarios-scrolltobottom.el
new file mode 100644
index 00000000000..206687ccab5
--- /dev/null
+++ b/test/lisp/erc/erc-scenarios-scrolltobottom.el
@@ -0,0 +1,66 @@
+;;; erc-scenarios-scrolltobottom.el --- erc-scrolltobottom-mode -*- 
lexical-binding: t -*-
+
+;; Copyright (C) 2023 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(require 'ert-x)
+(eval-and-compile
+  (let ((load-path (cons (ert-resource-directory) load-path)))
+    (require 'erc-scenarios-common)))
+
+(require 'erc-goodies)
+
+;; These two actually seem to run fine on Emacs 28, but skip them for
+;; now to stay in sync with `erc-scenarios-scrolltobottom--relaxed'.
+
+(ert-deftest erc-scenarios-scrolltobottom--normal ()
+  :tags '(:expensive-test)
+  (when (version< emacs-version "29") (ert-skip "Times out"))
+
+  (should-not erc-scrolltobottom-all)
+
+  (erc-scenarios-common-scrolltobottom--normal
+   (lambda ()
+     (ert-info ("New insertion doesn't anchor prompt in other window")
+       (let ((w (next-window)))
+         ;; We're at prompt but not aligned to bottom.
+         (should (>= (window-point w) erc-input-marker))
+         (erc-d-t-wait-for 10
+             (not (erc-scenarios-common--at-win-end-p w))))))))
+
+(ert-deftest erc-scenarios-scrolltobottom--all ()
+  :tags '(:expensive-test)
+  (when (version< emacs-version "29") (ert-skip "Times out"))
+
+  (should-not erc-scrolltobottom-all)
+
+  (let ((erc-scrolltobottom-all t))
+
+    (erc-scenarios-common-scrolltobottom--normal
+     (lambda ()
+       (ert-info ("New insertion anchors prompt in other window")
+         (let ((w (next-window)))
+           ;; We're at prompt and aligned to bottom.
+           (should (>= (window-point w) erc-input-marker))
+           (erc-d-t-wait-for 10
+               (erc-scenarios-common--at-win-end-p w))
+           (erc-d-t-ensure-for 0.5
+               (erc-scenarios-common--at-win-end-p w))))))))
+
+;;; erc-scenarios-scrolltobottom.el ends here
diff --git a/test/lisp/erc/erc-stamp-tests.el b/test/lisp/erc/erc-stamp-tests.el
index c448416cd69..46a05729066 100644
--- a/test/lisp/erc/erc-stamp-tests.el
+++ b/test/lisp/erc/erc-stamp-tests.el
@@ -274,4 +274,37 @@
       (when noninteractive
         (kill-buffer)))))
 
+(ert-deftest erc-echo-timestamp ()
+  :tags (and (null (getenv "CI")) '(:unstable))
+
+  (should-not erc-echo-timestamps)
+  (should-not erc-stamp--last-stamp)
+  (insert (propertize "abc" 'erc-timestamp 433483200))
+  (goto-char (point-min))
+  (let ((inhibit-message t)
+        (erc-echo-timestamp-format "%Y-%m-%d %H:%M:%S %Z")
+        (erc-echo-timestamp-zone (list (* 60 60 -4) "EDT")))
+
+    ;; No-op when non-interactive and option is nil
+    (should-not (erc--echo-ts-csf nil nil 'entered))
+    (should-not erc-stamp--last-stamp)
+
+    ;; Non-interactive (cursor sensor function)
+    (let ((erc-echo-timestamps t))
+      (should (equal (erc--echo-ts-csf nil nil 'entered)
+                     "1983-09-27 00:00:00 EDT")))
+    (should (= 433483200 erc-stamp--last-stamp))
+
+    ;; Interactive
+    (should (equal (call-interactively #'erc-echo-timestamp)
+                   "1983-09-27 00:00:00 EDT"))
+    ;; Interactive with zone
+    (let ((current-prefix-arg '(4)))
+      (should (member (call-interactively #'erc-echo-timestamp)
+                      '("1983-09-27 04:00:00 GMT"
+                        "1983-09-27 04:00:00 UTC"))))
+    (let ((current-prefix-arg -7))
+      (should (equal (call-interactively #'erc-echo-timestamp)
+                     "1983-09-26 21:00:00 -07")))))
+
 ;;; erc-stamp-tests.el ends here
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index 9fdad823059..8a68eca6196 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -292,7 +292,7 @@
                                (cl-incf counter))))
          erc-accidental-paste-threshold-seconds
          erc-insert-modify-hook
-         erc--input-review-functions
+         (erc--input-review-functions erc--input-review-functions)
          erc-send-completed-hook)
 
     (ert-info ("Server buffer")
@@ -357,6 +357,9 @@
         (should (= (point) erc-input-marker))
         (insert "/query bob")
         (erc-send-current-line)
+        ;; Last command not inserted
+        (save-excursion (forward-line -1)
+                        (should (looking-at "<tester> Howdy")))
         ;; Query does not redraw (nor /help, only message input)
         (should (looking-back "#chan@ServNet 11> "))
         ;; No sign of old prompts
@@ -560,6 +563,36 @@
                        (pop calls)))
         (should-not calls)))
 
+    ;; Mimic simplistic version of example in "(erc) display-buffer".
+    (when (>= emacs-major-version 29)
+      (let ((proc erc-server-process))
+        (with-temp-buffer
+          (should-not (eq (window-buffer) (current-buffer)))
+          (erc-mode)
+          (setq erc-server-process proc)
+
+          (cl-letf (((symbol-function 'erc--test-fun-p)
+                     (lambda (buf action)
+                       (should (eql 1 (alist-get 'erc-buffer-display action)))
+                       (push (cons 'erc--test-fun-p buf) calls)))
+                    ((symbol-function 'action-fn)
+                     (lambda (buf action)
+                       (should (eql 1 (alist-get 'erc-buffer-display action)))
+                       (should (eql 42 (alist-get 'foo action)))
+                       (push (cons 'action-fn buf) calls)
+                       (selected-window))))
+
+            (let ((erc--display-context '((erc-buffer-display . 1)))
+                  (display-buffer-alist
+                   `(((and (major-mode . erc-mode) erc--test-fun-p)
+                      action-fn (foo . 42))))
+                  (erc-buffer-display 'display-buffer))
+
+              (erc-setup-buffer (current-buffer))
+              (should (equal 'action-fn (car (pop calls))))
+              (should (equal 'erc--test-fun-p (car (pop calls))))
+              (should-not calls))))))
+
     (should (eq owin (selected-window)))
     (should (eq obuf (window-buffer)))))
 
@@ -576,6 +609,39 @@
     (setq erc-lurker-ignore-chars "_-`") ; set of chars, not character alts
     (should (string= "nick" (erc-lurker-maybe-trim "nick-_`")))))
 
+(ert-deftest erc-parse-user ()
+  (should (equal erc--parse-user-regexp erc--parse-user-regexp-legacy))
+
+  (should (equal '("" "" "") (erc-parse-user "!@")))
+  (should (equal '("" "!" "") (erc-parse-user "!!@")))
+  (should (equal '("" "" "@") (erc-parse-user "!@@")))
+  (should (equal '("" "!" "@") (erc-parse-user "!!@@")))
+
+  (should (equal '("abc" "" "") (erc-parse-user "abc")))
+  (should (equal '("" "123" "fake") (erc-parse-user "!123@fake")))
+  (should (equal '("abc" "" "123") (erc-parse-user "abc!123")))
+
+  (should (equal '("abc" "123" "fake") (erc-parse-user "abc!123@fake")))
+  (should (equal '("abc" "!123" "@xy") (erc-parse-user "abc!!123@@xy")))
+
+  (should (equal '("de" "fg" "xy") (erc-parse-user "abc\nde!fg@xy")))
+
+  (ert-info ("`erc--parse-user-regexp-pedantic'")
+    (let ((erc--parse-user-regexp erc--parse-user-regexp-pedantic))
+      (should (equal '("" "" "") (erc-parse-user "!@")))
+      (should (equal '("" "!" "") (erc-parse-user "!!@")))
+      (should (equal '("" "@" "") (erc-parse-user "!@@")))
+      (should (equal '("" "!@" "") (erc-parse-user "!!@@")))
+
+      (should (equal '("abc" "" "") (erc-parse-user "abc")))
+      (should (equal '("" "123" "fake") (erc-parse-user "!123@fake")))
+      (should (equal '("abc" "" "123") (erc-parse-user "abc!123")))
+
+      (should (equal '("abc" "123" "fake") (erc-parse-user "abc!123@fake")))
+      (should (equal '("abc" "!123@" "xy") (erc-parse-user "abc!!123@@xy")))
+
+      (should (equal '("de" "" "fg@xy") (erc-parse-user "abc\nde!fg@xy"))))))
+
 (ert-deftest erc--parse-isupport-value ()
   (should (equal (erc--parse-isupport-value "a,b") '("a" "b")))
   (should (equal (erc--parse-isupport-value "a,b,c") '("a" "b" "c")))
@@ -814,11 +880,12 @@
   (with-current-buffer (get-buffer-create "*#fake*")
     (erc-mode)
     (erc-tests--send-prep)
+    (setq erc-server-current-nick "tester")
     (setq-local erc-last-input-time 0)
     (should-not (local-variable-if-set-p 'erc-send-completed-hook))
     (set (make-local-variable 'erc-send-completed-hook) nil) ; skip t (globals)
     ;; Just in case erc-ring-mode is already on
-    (setq-local erc--input-review-functions nil)
+    (setq-local erc--input-review-functions erc--input-review-functions)
     (add-hook 'erc--input-review-functions #'erc-add-to-input-ring)
     ;;
     (cl-letf (((symbol-function 'erc-process-input-line)
@@ -993,43 +1060,6 @@
     (should (equal '("" "" "") (split-string "\n\n" p)))
     (should (equal '("" "" "") (split-string "\n\r" p)))))
 
-(ert-deftest erc--blank-in-multiline-input-p ()
-  (let ((check (lambda (s)
-                 (erc--blank-in-multiline-input-p
-                  (split-string s erc--input-line-delim-regexp)))))
-
-    (ert-info ("With `erc-send-whitespace-lines'")
-      (let ((erc-send-whitespace-lines t))
-        (should (funcall check ""))
-        (should-not (funcall check "\na"))
-        (should-not (funcall check "/msg a\n")) ; real /cmd
-        (should-not (funcall check "a\n\nb")) ; "" allowed
-        (should-not (funcall check "/msg a\n\nb")) ; non-/cmd
-        (should-not (funcall check " "))
-        (should-not (funcall check "\t"))
-        (should-not (funcall check "a\nb"))
-        (should-not (funcall check "a\n "))
-        (should-not (funcall check "a\n \t"))
-        (should-not (funcall check "a\n \f"))
-        (should-not (funcall check "a\n \nb"))
-        (should-not (funcall check "a\n \t\nb"))
-        (should-not (funcall check "a\n \f\nb"))))
-
-    (should (funcall check ""))
-    (should (funcall check " "))
-    (should (funcall check "\t"))
-    (should (funcall check "a\n\nb"))
-    (should (funcall check "a\n\nb"))
-    (should (funcall check "a\n "))
-    (should (funcall check "a\n \t"))
-    (should (funcall check "a\n \f"))
-    (should (funcall check "a\n \nb"))
-    (should (funcall check "a\n \t\nb"))
-
-    (should-not (funcall check "a\rb"))
-    (should-not (funcall check "a\nb"))
-    (should-not (funcall check "a\r\nb"))))
-
 (defun erc-tests--with-process-input-spy (test)
   (with-current-buffer (get-buffer-create "FakeNet")
     (let* ((erc--input-review-functions
@@ -1075,7 +1105,7 @@
        (delete-region (point) (point-max))
        (insert "one\n")
        (let ((e (should-error (erc-send-current-line))))
-         (should (equal "Blank line - ignoring..." (cadr e))))
+         (should (string-prefix-p "Trailing line detected" (cadr e))))
        (goto-char (point-max))
        (ert-info ("Input remains untouched")
          (should (save-excursion (goto-char erc-input-marker)
@@ -1117,6 +1147,137 @@
 
      (should (consp erc-last-input-time)))))
 
+(ert-deftest erc--discard-trailing-multiline-nulls ()
+  (pcase-dolist (`(,input ,want) '((("") (""))
+                                   (("" "") (""))
+                                   (("a") ("a"))
+                                   (("a" "") ("a"))
+                                   (("" "a") ("" "a"))
+                                   (("" "a" "") ("" "a"))))
+    (ert-info ((format "Input: %S, want: %S" input want))
+      (let ((s (make-erc--input-split :lines input)))
+        (erc--discard-trailing-multiline-nulls s)
+        (should (equal (erc--input-split-lines s) want))))))
+
+(ert-deftest erc--count-blank-lines ()
+  (pcase-dolist (`(,input ,want) '((() (0 0 0))
+                                   (("") (1 1 0))
+                                   (("" "") (2 1 1))
+                                   (("" "" "") (3 1 2))
+                                   ((" " "") (2 0 1))
+                                   ((" " "" "") (3 0 2))
+                                   (("" " " "") (3 1 1))
+                                   (("" "" " ") (3 2 0))
+                                   (("a") (0 0 0))
+                                   (("a" "") (1 0 1))
+                                   (("a" " " "") (2 0 1))
+                                   (("a" "" "") (2 0 2))
+                                   (("a" "b") (0 0 0))
+                                   (("a" "" "b") (1 1 0))
+                                   (("a" " " "b") (1 0 0))
+                                   (("" "a") (1 1 0))
+                                   ((" " "a") (1 0 0))
+                                   (("" "a" "") (2 1 1))
+                                   (("" " " "a" "" " ") (4 2 0))
+                                   (("" " " "a" "" " " "") (5 2 1))))
+    (ert-info ((format "Input: %S, want: %S" input want))
+      (should (equal (erc--count-blank-lines input) want)))))
+
+;; Opt `wb': `erc-warn-about-blank-lines'
+;; Opt `sw': `erc-send-whitespace-lines'
+;; `s': " \n",`a': "a\n",`b': "b\n"
+(defvar erc-tests--check-prompt-input--expect
+  ;;  opts     ""  " "   "\n"  "\n "   " \n" "\n\n" "a\n" "a\n " "a\n \nb"
+  '(((+wb -sw) err err   err   err     err   err    err   err    err)
+    ((-wb -sw) nop nop   nop   nop     nop   nop    nop   nop    nop)
+    ((+wb +sw) err (s)   (0 s) (1 s s) (s)   (0 s)  (0 a) (a s)  (a s b))
+    ((-wb +sw) nop (s)   (s)   (s s)   (s)   (s)    (a)   (a s)  (a s b))))
+
+;; Help messages echoed (not IRC message) was emitted
+(defvar erc-tests--check-prompt-input-messages
+  '("Stripping" "Padding"))
+
+(ert-deftest erc--check-prompt-input-for-multiline-blanks ()
+  (erc-tests--with-process-input-spy
+   (lambda (next)
+     (erc-tests--set-fake-server-process "sleep" "1")
+     (should-not erc-send-whitespace-lines)
+     (should erc-warn-about-blank-lines)
+
+     (pcase-dolist (`((,wb ,sw) . ,ex) erc-tests--check-prompt-input--expect)
+       (let ((print-escape-newlines t)
+             (erc-warn-about-blank-lines (eq wb '+wb))
+             (erc-send-whitespace-lines (eq sw '+sw))
+             (samples '("" " " "\n" "\n " " \n" "\n\n"
+                        "a\n" "a\n " "a\n \nb")))
+         (setq ex `(,@ex (a) (a b)) ; baseline, same for all combos
+               samples `(,@samples "a" "a\nb"))
+         (dolist (input samples)
+           (insert input)
+           (ert-info ((format "Opts: %S, Input: %S, want: %S"
+                              (list wb sw) input (car ex)))
+             (ert-with-message-capture messages
+               (pcase-exhaustive (pop ex)
+                 ('err (let ((e (should-error (erc-send-current-line))))
+                         (should (string-match (rx (| "trailing" "blank"))
+                                               (cadr e))))
+                       (should (equal (erc-user-input) input))
+                       (should-not (funcall next)))
+                 ('nop (erc-send-current-line)
+                       (should (equal (erc-user-input) input))
+                       (should-not (funcall next)))
+                 ('clr (erc-send-current-line)
+                       (should (string-empty-p (erc-user-input)))
+                       (should-not (funcall next)))
+                 ((and (pred consp) v)
+                  (erc-send-current-line)
+                  (should (string-empty-p (erc-user-input)))
+                  (setq v (reverse v)) ; don't use `nreverse' here
+                  (while v
+                    (pcase (pop v)
+                      ((and (pred integerp) n)
+                       (should (string-search
+                                (nth n erc-tests--check-prompt-input-messages)
+                                messages)))
+                      ('s (should (equal " \n" (car (funcall next)))))
+                      ('a (should (equal "a\n" (car (funcall next)))))
+                      ('b (should (equal "b\n" (car (funcall next)))))))
+                  (should-not (funcall next))))))
+           (delete-region erc-input-marker (point-max))))))))
+
+(ert-deftest erc--check-prompt-input-for-multiline-blanks/explanations ()
+  (should erc-warn-about-blank-lines)
+  (should-not erc-send-whitespace-lines)
+
+  (let ((erc-send-whitespace-lines t))
+    (pcase-dolist (`(,input ,msg)
+                   '((("") "Padding (1) blank line")
+                     (("" " ") "Padding (1) blank line")
+                     ((" " "") "Stripping (1) blank line")
+                     (("a" "") "Stripping (1) blank line")
+                     (("" "") "Stripping (1) and padding (1) blank lines")
+                     (("" "" "") "Stripping (2) and padding (1) blank lines")
+                     (("" "a" "" "b" "" "c" "" "")
+                      "Stripping (2) and padding (3) blank lines")))
+      (ert-info ((format "Input: %S, Msg: %S" input msg))
+        (let (erc--check-prompt-explanation)
+          (should-not (erc--check-prompt-input-for-multiline-blanks nil input))
+          (should (equal (list msg) erc--check-prompt-explanation))))))
+
+  (pcase-dolist (`(,input ,msg)
+                 '((("") "Blank line detected")
+                   (("" " ") "2 blank lines detected")
+                   ((" " "") "2 blank (1 trailing) lines detected")
+                   (("a" "") "Trailing line detected")
+                   (("" "") "2 blank (1 trailing) lines detected")
+                   (("a" "" "") "2 trailing lines detected")
+                   (("" "a" "" "b" "" "c" "" "")
+                    "5 blank (2 trailing) lines detected")))
+    (ert-info ((format "Input: %S, Msg: %S" input msg))
+      (let ((rv (erc--check-prompt-input-for-multiline-blanks nil input)))
+        (should (equal (concat msg " (see `erc-send-whitespace-lines')")
+                       rv ))))))
+
 (ert-deftest erc-send-whitespace-lines ()
   (erc-tests--with-process-input-spy
    (lambda (next)
@@ -1133,7 +1294,7 @@
          (erc-bol)
          (should (eq (point) (point-max))))
        (should (equal (funcall next) '("two\n" nil t)))
-       (should (equal (funcall next) '("\n" nil t)))
+       (should (equal (funcall next) '(" \n" nil t)))
        (should (equal (funcall next) '("one\n" nil t))))
 
      (ert-info ("Multiline hunk with trailing newline filtered")
@@ -1155,17 +1316,12 @@
        (should-not (funcall next)))
 
      (ert-info ("Multiline command with trailing blank filtered")
-       (pcase-dolist (`(,p . ,q)
-                      '(("/a b\r" "/a b\n") ("/a b\n" "/a b\n")
-                        ("/a b\n\n" "/a b\n") ("/a b\r\n" "/a b\n")
-                        ("/a b\n\n\n" "/a b\n")))
+       (dolist (p '("/a b" "/a b\n" "/a b\n\n" "/a b\n\n\n"))
          (insert p)
          (erc-send-current-line)
          (erc-bol)
          (should (eq (point) (point-max)))
-         (while q
-           (should (pcase (funcall next)
-                     (`(,cmd ,_ nil) (equal cmd (pop q))))))
+         (should (pcase (funcall next) (`(,cmd ,_ nil) (equal cmd "/a b\n"))))
          (should-not (funcall next))))
 
      (ert-info ("Multiline command with non-blanks errors")
@@ -2038,8 +2194,7 @@
          ;; This is for integrations testing with managed configs
          ;; ("starter kits") that use a different package manager.
          (init (and-let* ((found (getenv "ERC_TESTS_INIT"))
-                          (files (split-string found ","))
-                          ((seq-every-p #'file-exists-p files)))
+                          (files (split-string found ",")))
                  (mapcan (lambda (f) (list "-l" f)) files)))
          (prog
           `(progn
diff --git a/test/lisp/erc/resources/base/assoc/bouncer-history/barnet.eld 
b/test/lisp/erc/resources/base/assoc/bouncer-history/barnet.eld
index 35a9a570b6d..060f4178723 100644
--- a/test/lisp/erc/resources/base/assoc/bouncer-history/barnet.eld
+++ b/test/lisp/erc/resources/base/assoc/bouncer-history/barnet.eld
@@ -17,7 +17,7 @@
  (0 ":irc.barnet.org 266 tester 3 3 :Current global users 3, max 3")
  (0 ":irc.barnet.org 422 tester :MOTD File is missing"))
 
-((mode-user 3.2 "MODE tester +i")
+((mode-user 10 "MODE tester +i")
  ;; No mode answer ^
  (0 ":irc.znc.in 306 tester :You have been marked as being away")
  (0 ":tester!~u@xrir8fpe4d7ak.irc JOIN #chan")
@@ -34,7 +34,7 @@
  (0 ":irc.barnet.org NOTICE tester :[07:00:01] This server is in debug mode 
and is logging all user I/O. If you do not wish for everything you send to be 
readable by the server owner(s), please disconnect.")
  (0 ":irc.barnet.org 305 tester :You are no longer marked as being away"))
 
-((mode 6 "MODE #chan")
+((mode 10 "MODE #chan")
  (0 ":irc.barnet.org 324 tester #chan +nt")
  (0 ":irc.barnet.org 329 tester #chan 1619593200")
  (0.25 ":joe!~u@svpn88yjcdj42.irc PRIVMSG #chan :mike: But, in defense, by 
mercy, 'tis most just.")
diff --git a/test/lisp/erc/resources/base/assoc/bouncer-history/foonet.eld 
b/test/lisp/erc/resources/base/assoc/bouncer-history/foonet.eld
index f34ae02f4e4..ecde8adaec4 100644
--- a/test/lisp/erc/resources/base/assoc/bouncer-history/foonet.eld
+++ b/test/lisp/erc/resources/base/assoc/bouncer-history/foonet.eld
@@ -17,7 +17,7 @@
  (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3")
  (0 ":irc.foonet.org 422 tester :MOTD File is missing"))
 
-((mode-user 3.2 "MODE tester +i")
+((mode-user 10 "MODE tester +i")
  ;; No mode answer ^
  (0 ":irc.znc.in 306 tester :You have been marked as being away")
  (0 ":tester!~u@nvfhxvqm92rm6.irc JOIN #chan")
@@ -39,7 +39,7 @@
  (0 ":irc.foonet.org NOTICE tester :[07:00:32] This server is in debug mode 
and is logging all user I/O. If you do not wish for everything you send to be 
readable by the server owner(s), please disconnect.")
  (0 ":irc.foonet.org 305 tester :You are no longer marked as being away"))
 
-((mode 6 "MODE #chan")
+((mode 10 "MODE #chan")
  (0 ":irc.foonet.org 324 tester #chan +nt")
  (0 ":irc.foonet.org 329 tester #chan 1619593200")
  (0.9 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #chan :bob: Grows, lives, and dies, 
in single blessedness.")
diff --git a/test/lisp/erc/resources/base/assoc/bumped/again.eld 
b/test/lisp/erc/resources/base/assoc/bumped/again.eld
index ab3c7b06214..aef164b6237 100644
--- a/test/lisp/erc/resources/base/assoc/bumped/again.eld
+++ b/test/lisp/erc/resources/base/assoc/bumped/again.eld
@@ -1,10 +1,10 @@
 ;; -*- mode: lisp-data; -*-
-((nick 1 "NICK tester"))
-((user 1 "USER user 0 * :tester")
+((nick 10 "NICK tester"))
+((user 10 "USER user 0 * :tester")
  (0.0 ":irc.foonet.org 433 * tester :Nickname is reserved by a different 
account")
  (0.0 ":irc.foonet.org FAIL NICK NICKNAME_RESERVED tester :Nickname is 
reserved by a different account"))
 
-((nick 3 "NICK tester`")
+((nick 10 "NICK tester`")
  (0.1 ":irc.foonet.org 001 tester` :Welcome to the foonet IRC Network tester`")
  (0.0 ":irc.foonet.org 002 tester` :Your host is irc.foonet.org, running 
version oragono-2.6.1-937b9b02368748e5")
  (0.0 ":irc.foonet.org 003 tester` :This server was created Fri, 24 Sep 2021 
01:38:36 UTC")
@@ -21,10 +21,10 @@
  (0.2 ":irc.foonet.org 266 tester` 3 3 :Current global users 3, max 3")
  (0.0 ":irc.foonet.org 422 tester` :MOTD File is missing"))
 
-((mode-user 3.2 "MODE tester` +i")
+((mode-user 10 "MODE tester` +i")
  (0.0 ":irc.foonet.org 221 tester` +i")
  (0.0 ":irc.foonet.org NOTICE tester` :This server is in debug mode and is 
logging all user I/O. If you do not wish for everything you send to be readable 
by the server owner(s), please disconnect."))
 
-((privmsg 42.6 "PRIVMSG NickServ :IDENTIFY tester changeme")
+((privmsg 10 "PRIVMSG NickServ :IDENTIFY tester changeme")
  (0.01 ":tester`!~u@rpaau95je67ci.irc NICK tester")
  (0.0 ":NickServ!NickServ@localhost NOTICE tester :You're now logged in as 
tester"))
diff --git a/test/lisp/erc/resources/base/assoc/bumped/foisted.eld 
b/test/lisp/erc/resources/base/assoc/bumped/foisted.eld
index 5c36e58d9d3..0f7aadac564 100644
--- a/test/lisp/erc/resources/base/assoc/bumped/foisted.eld
+++ b/test/lisp/erc/resources/base/assoc/bumped/foisted.eld
@@ -1,6 +1,6 @@
 ;; -*- mode: lisp-data; -*-
-((nick 1 "NICK tester"))
-((user 1 "USER user 0 * :tester")
+((nick 10 "NICK tester"))
+((user 10 "USER user 0 * :tester")
  (0.0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester")
  (0.0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running 
version oragono-2.6.1-937b9b02368748e5")
  (0.0 ":irc.foonet.org 003 tester :This server was created Fri, 24 Sep 2021 
01:38:36 UTC")
@@ -17,14 +17,14 @@
  (0.0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3")
  (0.0 ":irc.foonet.org 422 tester :MOTD File is missing"))
 
-((mode-user 1.2 "MODE tester +i")
+((mode-user 10 "MODE tester +i")
  (0.0 ":irc.foonet.org 221 tester +i")
  (0.0 ":irc.foonet.org NOTICE tester :This server is in debug mode and is 
logging all user I/O. If you do not wish for everything you send to be readable 
by the server owner(s), please disconnect."))
 
-((privmsg 17.21 "PRIVMSG bob :hi")
+((privmsg 10 "PRIVMSG bob :hi")
  (0.02 ":bob!~u@ecnnh95wr67pv.net PRIVMSG tester :hola")
  (0.01 ":bob!~u@ecnnh95wr67pv.net PRIVMSG tester :how r u?"))
 
-((quit 18.19 "QUIT :" quit)
+((quit 10 "QUIT :" quit)
  (0.01 ":tester!~u@rpaau95je67ci.irc QUIT :Quit: " quit))
 ((drop 1 DROP))
diff --git a/test/lisp/erc/resources/base/assoc/bumped/refoisted.eld 
b/test/lisp/erc/resources/base/assoc/bumped/refoisted.eld
index 33e4168ac46..63366d3f576 100644
--- a/test/lisp/erc/resources/base/assoc/bumped/refoisted.eld
+++ b/test/lisp/erc/resources/base/assoc/bumped/refoisted.eld
@@ -1,6 +1,6 @@
 ;; -*- mode: lisp-data; -*-
-((nick 1 "NICK tester"))
-((user 1 "USER user 0 * :tester")
+((nick 10 "NICK tester"))
+((user 10 "USER user 0 * :tester")
  (0.1 ":irc.foonet.org 001 dummy :Welcome to the foonet IRC Network dummy")
  (0.0 ":irc.foonet.org 002 dummy :Your host is irc.foonet.org, running version 
oragono-2.6.1-937b9b02368748e5")
  (0.0 ":irc.foonet.org 003 dummy :This server was created Fri, 24 Sep 2021 
01:38:36 UTC")
@@ -22,10 +22,10 @@
  (0.01 ":bob!~u@ecnnh95wr67pv.net PRIVMSG dummy :back?")
  )
 
-((mode-user 1.2 "MODE dummy +i")
+((mode-user 10 "MODE dummy +i")
  (0.0 ":irc.foonet.org 221 dummy +i")
  (0.0 ":irc.foonet.org NOTICE dummy :This server is in debug mode and is 
logging all user I/O. If you do not wish for everything you send to be readable 
by the server owner(s), please disconnect."))
 
-((renick 42.6 "NICK tester")
+((renick 10 "NICK tester")
  (0.01 ":dummy!~u@rpaau95je67ci.irc NICK tester")
  (0.0 ":NickServ!NickServ@localhost NOTICE dummy :You're now logged in as 
tester"))
diff --git a/test/lisp/erc/resources/base/flood/soju.eld 
b/test/lisp/erc/resources/base/flood/soju.eld
index 05266ca9411..9e936499a2d 100644
--- a/test/lisp/erc/resources/base/flood/soju.eld
+++ b/test/lisp/erc/resources/base/flood/soju.eld
@@ -8,7 +8,7 @@
  (0.0 ":soju.im 005 tester CHATHISTORY=1000 CASEMAPPING=ascii NETWORK=Soju 
:are supported")
  (0.0 ":soju.im 422 tester :No MOTD"))
 
-((mode 1 "MODE tester +i")
+((mode 10 "MODE tester +i")
  (0.0 ":tester!tester@10.0.2.100 JOIN #chan/foonet")
  (0.25 ":soju.im 331 tester #chan/foonet :No topic is set")
  (0.0 ":soju.im 353 tester = #chan/foonet :@bob/foonet alice/foonet tester")
diff --git a/test/lisp/erc/resources/base/netid/bouncer/barnet.eld 
b/test/lisp/erc/resources/base/netid/bouncer/barnet.eld
index 204d01fef77..596383c2699 100644
--- a/test/lisp/erc/resources/base/netid/bouncer/barnet.eld
+++ b/test/lisp/erc/resources/base/netid/bouncer/barnet.eld
@@ -38,4 +38,4 @@
  (0.05 ":joe!~u@awyxgybtkx7uq.irc PRIVMSG #chan :mike: As he regards his aged 
father's life.")
  (0.05 ":mike!~u@awyxgybtkx7uq.irc PRIVMSG #chan :joe: It is a rupture that 
you may easily heal; and the cure of it not only saves your brother, but keeps 
you from dishonor in doing it."))
 
-((linger 1 LINGER))
+((linger 2 LINGER))
diff --git a/test/lisp/erc/resources/base/netid/bouncer/foonet.eld 
b/test/lisp/erc/resources/base/netid/bouncer/foonet.eld
index 4445350ca0c..2e1a3ac27da 100644
--- a/test/lisp/erc/resources/base/netid/bouncer/foonet.eld
+++ b/test/lisp/erc/resources/base/netid/bouncer/foonet.eld
@@ -43,4 +43,4 @@
  (0.1 ":alice!~u@ertp7idh9jtgi.irc PRIVMSG #chan :bob: Orlando, my liege; the 
youngest son of Sir Rowland de Boys.")
  (0.1 ":bob!~u@ertp7idh9jtgi.irc PRIVMSG #chan :alice: The ape is dead, and I 
must conjure him."))
 
-((linger 1 LINGER))
+((linger 2 LINGER))
diff --git a/test/lisp/erc/resources/base/reconnect/aborted-dupe.eld 
b/test/lisp/erc/resources/base/reconnect/aborted-dupe.eld
index 8e299ec44c0..35906f608b5 100644
--- a/test/lisp/erc/resources/base/reconnect/aborted-dupe.eld
+++ b/test/lisp/erc/resources/base/reconnect/aborted-dupe.eld
@@ -19,7 +19,7 @@
  (-0.02 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3")
  (-0.02 ":irc.foonet.org 422 tester :MOTD File is missing"))
 
-((~mode-user 3.2 "MODE tester +i")
+((~mode-user 10 "MODE tester +i")
  (-0.02 ":irc.foonet.org 221 tester +i")
  (-0.02 ":irc.foonet.org NOTICE tester :This server is in debug mode and is 
logging all user I/O. If you do not wish for everything you send to be readable 
by the server owner(s), please disconnect."))
 
diff --git a/test/lisp/erc/resources/base/reconnect/aborted.eld 
b/test/lisp/erc/resources/base/reconnect/aborted.eld
index 5c32070d85f..e3abcdf8415 100644
--- a/test/lisp/erc/resources/base/reconnect/aborted.eld
+++ b/test/lisp/erc/resources/base/reconnect/aborted.eld
@@ -18,7 +18,7 @@
  (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3")
  (0 ":irc.foonet.org 422 tester :MOTD File is missing"))
 
-((mode-user 3.2 "MODE tester +i")
+((mode-user 10 "MODE tester +i")
  (0 ":irc.foonet.org 221 tester +i")
  (0 ":irc.foonet.org NOTICE tester :This server is in debug mode and is 
logging all user I/O. If you do not wish for everything you send to be readable 
by the server owner(s), please disconnect."))
 
diff --git a/test/lisp/erc/resources/base/renick/self/qual-chester.eld 
b/test/lisp/erc/resources/base/renick/self/qual-chester.eld
index 75b50fe68bd..a224e0451d7 100644
--- a/test/lisp/erc/resources/base/renick/self/qual-chester.eld
+++ b/test/lisp/erc/resources/base/renick/self/qual-chester.eld
@@ -18,7 +18,7 @@
  (0 ":irc.foonet.org 266 chester 3 4 :Current global users 3, max 4")
  (0 ":irc.foonet.org 422 chester :MOTD File is missing"))
 
-((mode-user 1.2 "MODE chester +i")
+((mode-user 10 "MODE chester +i")
  (0 ":irc.foonet.org 221 chester +i")
  (0 ":irc.foonet.org NOTICE chester :This server is in debug mode and is 
logging all user I/O. If you do not wish for everything you send to be readable 
by the server owner(s), please disconnect."))
 
diff --git a/test/lisp/erc/resources/base/renick/self/qual-tester.eld 
b/test/lisp/erc/resources/base/renick/self/qual-tester.eld
index 25199226658..27061c65223 100644
--- a/test/lisp/erc/resources/base/renick/self/qual-tester.eld
+++ b/test/lisp/erc/resources/base/renick/self/qual-tester.eld
@@ -18,7 +18,7 @@
  (0 ":irc.foonet.org 266 tester 4 4 :Current global users 4, max 4")
  (0 ":irc.foonet.org 422 tester :MOTD File is missing"))
 
-((mode-user 1.2 "MODE tester +i")
+((mode-user 10 "MODE tester +i")
  (0 ":irc.foonet.org 221 tester +i")
  (0 ":irc.foonet.org NOTICE tester :This server is in debug mode and is 
logging all user I/O. If you do not wish for everything you send to be readable 
by the server owner(s), please disconnect."))
 
diff --git a/test/lisp/erc/resources/erc-d/erc-d-t.el 
b/test/lisp/erc/resources/erc-d/erc-d-t.el
index 7b2adf4f07b..cf869fb3c70 100644
--- a/test/lisp/erc/resources/erc-d/erc-d-t.el
+++ b/test/lisp/erc/resources/erc-d/erc-d-t.el
@@ -83,6 +83,8 @@ returning."
                (ignore-errors (kill-buffer buf)))))
          (sleep-for erc-d-t-cleanup-sleep-secs)))))
 
+(defvar erc-d-t--wait-message-prefix "Awaiting: ")
+
 (defmacro erc-d-t-wait-for (max-secs msg &rest body)
   "Wait for BODY to become non-nil.
 Or signal error with MSG after MAX-SECS.  When MAX-SECS is negative,
@@ -99,7 +101,7 @@ be desirable."
   (let ((inverted (make-symbol "inverted"))
         (time-out (make-symbol "time-out"))
         (result (make-symbol "result")))
-    `(ert-info ((concat "Awaiting: " ,msg))
+    `(ert-info ((concat erc-d-t--wait-message-prefix ,msg))
        (let ((,time-out (abs ,max-secs))
              (,inverted (< ,max-secs 0))
              (,result ',result))
@@ -120,7 +122,8 @@ On failure, emit MSG."
   (unless (or (stringp msg) (memq (car-safe msg) '(format concat)))
     (push msg body)
     (setq msg (prin1-to-string body)))
-  `(erc-d-t-wait-for (- (abs ,max-secs)) ,msg (not (progn ,@body))))
+  `(let ((erc-d-t--wait-message-prefix "Sustaining: "))
+     (erc-d-t-wait-for (- (abs ,max-secs)) ,msg (not (progn ,@body)))))
 
 (defun erc-d-t-search-for (timeout text &optional from on-success)
   "Wait for TEXT to appear in current buffer before TIMEOUT secs.
diff --git a/test/lisp/erc/resources/erc-d/erc-d-u.el 
b/test/lisp/erc/resources/erc-d/erc-d-u.el
index e26fa8b47dd..c7d6859e3e1 100644
--- a/test/lisp/erc/resources/erc-d/erc-d-u.el
+++ b/test/lisp/erc/resources/erc-d/erc-d-u.el
@@ -74,6 +74,7 @@
   (let ((hunks (erc-d-u-scan-e-sd info))
         (pos (erc-d-u-scan-e-pos info)))
     (or (and (erc-d-u-scan-d-hunks hunks)
+             (buffer-live-p (erc-d-u-scan-d-buf hunks))
              (with-current-buffer (erc-d-u-scan-d-buf hunks)
                (goto-char pos)
                (condition-case _err
diff --git a/test/lisp/erc/resources/erc-d/erc-d.el 
b/test/lisp/erc/resources/erc-d/erc-d.el
index e9d880644d4..f072c6b93b2 100644
--- a/test/lisp/erc/resources/erc-d/erc-d.el
+++ b/test/lisp/erc/resources/erc-d/erc-d.el
@@ -254,7 +254,7 @@ return a replacement.")
          (ending (process-get process :dialog-ending))
          (dialog (make-erc-d-dialog :name name
                                     :process process
-                                    :queue (make-ring 5)
+                                    :queue (make-ring 10)
                                     :exchanges (make-ring 10)
                                     :match-handlers mat-h
                                     :server-fqdn fqdn)))
@@ -292,33 +292,27 @@ With int SKIP, advance past that many exchanges."
 
 (defvar erc-d--m-debug (getenv "ERC_D_DEBUG"))
 
-(defmacro erc-d--m (process format-string &rest args)
-  "Output ARGS using FORMAT-STRING somewhere depending on context.
-PROCESS should be a client connection or a server network process."
-  `(let ((format-string (if erc-d--m-debug
-                            (concat (format-time-string "%s.%N: ")
-                                    ,format-string)
-                          ,format-string))
-         (want-insert (and ,process erc-d--in-process))
-         (buffer (process-buffer (process-get ,process :server))))
-     (when (and want-insert (buffer-live-p buffer))
-       (with-current-buffer buffer
-         (goto-char (point-max))
-         (insert (concat (format ,format-string ,@args) "\n"))))
-     (when (or erc-d--m-debug (not want-insert))
-       (message format-string ,@args))))
-
-(defmacro erc-d--log (process string &optional outbound)
-  "Log STRING sent to (OUTBOUND) or received from PROCESS peer."
-  `(let ((id (or (process-get ,process :log-id)
-                 (let ((port (erc-d-u--get-remote-port ,process)))
-                   (process-put ,process :log-id port)
-                   port)))
-         (name (erc-d-dialog-name (process-get ,process :dialog))))
-     (if ,outbound
-         (erc-d--m process "-> %s:%s %s" name id ,string)
-       (dolist (line (split-string ,string (process-get process :ending)))
-         (erc-d--m process "<- %s:%s %s" name id line)))))
+(defun erc-d--m (process format-string &rest args)
+  "Output ARGS using FORMAT-STRING to PROCESS's buffer or elsewhere."
+  (when erc-d--m-debug
+    (setq format-string (concat (format-time-string "%s.%N: ") format-string)))
+  (let ((insertp (and process erc-d--in-process))
+        (buffer (process-buffer (process-get process :server))))
+    (when (and insertp (buffer-live-p buffer))
+      (princ (concat (apply #'format format-string args) "\n") buffer))
+    (when (or erc-d--m-debug (not insertp))
+      (apply #'message format-string args))))
+
+(defun erc-d--log (process string &optional outbound)
+  "Log STRING received from or OUTBOUND to PROCESS peer."
+  (let ((id (or (process-get process :log-id)
+                (let ((port (erc-d-u--get-remote-port process)))
+                  (process-put process :log-id port) port)))
+        (name (erc-d-dialog-name (process-get process :dialog))))
+    (if outbound
+        (erc-d--m process "-> %s:%s %s" name id string)
+      (dolist (line (split-string string (process-get process :ending)))
+        (erc-d--m process "<- %s:%s %s" name id line)))))
 
 (defun erc-d--log-process-event (server process msg)
   (erc-d--m server "%s: %s" process (string-trim-right msg)))
@@ -463,7 +457,7 @@ including line delimiters."
 ;; Misc process properties:
 ;;
 ;; The server property `:dialog-dialogs' is an alist of (symbol
-;; . erc-d-u-scan-d) conses, each of which pairs a dialogs name with
+;; . erc-d-u-scan-d) conses, each of which pairs a dialog's name with
 ;; info on its read progress (described above in the Commentary).
 ;; This list is populated by `erc-d-run' at the start of each session.
 ;;
diff --git a/test/lisp/erc/resources/erc-d/resources/dynamic-barnet.eld 
b/test/lisp/erc/resources/erc-d/resources/dynamic-barnet.eld
index 4994e9c5503..e8feb2e6fd8 100644
--- a/test/lisp/erc/resources/erc-d/resources/dynamic-barnet.eld
+++ b/test/lisp/erc/resources/erc-d/resources/dynamic-barnet.eld
@@ -18,14 +18,14 @@
  (0. ":irc.barnet.org 266 tester 3 3 :Current global users 3, max 3")
  (0. ":irc.barnet.org 422 tester :MOTD File is missing"))
 
-((mode-user 1.2 "MODE tester +i")
+((mode-user 2 "MODE tester +i")
  (0. ":irc.barnet.org 221 tester +Zi")
  (0. ":irc.barnet.org 306 tester :You have been marked as being away")
  (0 ":tester!~u@awyxgybtkx7uq.irc JOIN #chan")
  (0 ":irc.barnet.org 353 joe = #chan :+joe!~joe@example.com 
@%+mike!~mike@example.org")
  (0 ":irc.barnet.org 366 joe #chan :End of NAMES list"))
 
-((mode 1 "MODE #chan")
+((mode 3 "MODE #chan")
  (0 ":irc.barnet.org 324 tester #chan +nt")
  (0 ":irc.barnet.org 329 tester #chan 1620805269")
  (0.1 ":joe!~u@awyxgybtkx7uq.irc PRIVMSG #chan :mike: Yes, a dozen; and as 
many to the vantage, as would store the world they played for.")
diff --git a/test/lisp/erc/resources/erc-d/resources/dynamic-foonet.eld 
b/test/lisp/erc/resources/erc-d/resources/dynamic-foonet.eld
index a47998e7d32..4855c178861 100644
--- a/test/lisp/erc/resources/erc-d/resources/dynamic-foonet.eld
+++ b/test/lisp/erc/resources/erc-d/resources/dynamic-foonet.eld
@@ -17,7 +17,7 @@
  (0. ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3")
  (0. ":irc.foonet.org 422 tester :MOTD File is missing"))
 
-((mode-user 1.2 "MODE tester +i")
+((mode-user 2 "MODE tester +i")
  (0. ":irc.foonet.org 221 tester +Zi")
  (0. ":irc.foonet.org 306 tester :You have been marked as being away")
  (0 ":tester!~u@awyxgybtkx7uq.irc JOIN #chan")
diff --git a/test/lisp/erc/resources/erc-d/resources/linger.eld 
b/test/lisp/erc/resources/erc-d/resources/linger.eld
index 36c81a3af4b..e456370a800 100644
--- a/test/lisp/erc/resources/erc-d/resources/linger.eld
+++ b/test/lisp/erc/resources/erc-d/resources/linger.eld
@@ -20,14 +20,14 @@
  (0 ":irc.example.org 266 tester 3 3 :Current global users 3, max 3")
  (0 ":irc.example.org 422 tester :MOTD File is missing"))
 
-((mode-user 1.2 "MODE tester +i")
+((mode-user 2 "MODE tester +i")
  (0 ":irc.example.org 221 tester +Zi")
  (0 ":irc.example.org 306 tester :You have been marked as being away")
  (0 ":tester!~tester@localhost JOIN #chan")
  (0 ":irc.example.org 353 alice = #chan :+alice!~alice@example.com 
@%+bob!~bob@example.org")
  (0 ":irc.example.org 366 alice #chan :End of NAMES list"))
 
-((mode-chan 1.2 "MODE #chan")
+((mode-chan 2 "MODE #chan")
  (0 ":bob!~bob@example.org PRIVMSG #chan :hey"))
 
 ((linger 1.0 LINGER))
diff --git a/test/lisp/erc/resources/erc-scenarios-common.el 
b/test/lisp/erc/resources/erc-scenarios-common.el
index 2eb040d28d9..5354b300b47 100644
--- a/test/lisp/erc/resources/erc-scenarios-common.el
+++ b/test/lisp/erc/resources/erc-scenarios-common.el
@@ -184,6 +184,112 @@ Dialog resource directories are located by expanding the 
variable
 
        ,@body)))
 
+(defvar erc-scenarios-common--term-size '(34 . 80))
+(declare-function term-char-mode "term" nil)
+(declare-function term-line-mode "term" nil)
+
+;; Much of this concerns accommodating test environments outside of
+;; the emacs.git tree, such as CI jobs running ERC's ELPA-package on
+;; older Emacsen.  See also `erc-tests--assert-printed-in-subprocess'.
+(defun erc-scenarios-common--run-in-term (&optional debug)
+  (require 'term)
+  (let* ((default-directory (or (getenv "EMACS_TEST_DIRECTORY")
+                                (expand-file-name
+                                 ".." erc-scenarios-common--resources-dir)))
+         ;; In the emacs.git tree, "HOME" will be "/nonexistent", which
+         ;; is fine because we don't need any ELPA packages.
+         (process-environment (cons "ERC_TESTS_SUBPROCESS=1"
+                                    process-environment))
+         (name (ert-test-name (ert-running-test)))
+         (temp-file (make-temp-file "erc-term-test-"))
+         (cmd `(let ((stats 1))
+                 (setq enable-dir-local-variables nil)
+                 (unwind-protect
+                     (setq stats (ert-run-tests-batch ',name))
+                   (unless ',debug
+                     (let ((buf (with-current-buffer (messages-buffer)
+                                  (buffer-string))))
+                       (with-temp-file ,temp-file
+                         (insert buf)))
+                     (kill-emacs (ert-stats-completed-unexpected stats))))))
+         ;; The `ert-test' object in Emacs 29 has a `file-name' field.
+         (file-name (symbol-file name 'ert--test))
+         (default-directory (expand-file-name (file-name-directory file-name)))
+         (package (if-let* ((found (getenv "ERC_PACKAGE_NAME"))
+                            ((string-prefix-p "erc-" found)))
+                      (intern found)
+                    'erc))
+         (init (and-let* ((found (getenv "ERC_TESTS_INIT"))
+                          (files (split-string found ",")))
+                 (mapcan (lambda (f) (list "-l" f)) files)))
+         (setup `(progn
+                   ,@(and (not init) (featurep 'compat)
+                          `((require 'package)
+                            (let ((package-load-list
+                                   '((compat t) (,package t))))
+                              (package-initialize))))
+                   (require 'erc)
+                   (cl-assert (equal erc-version ,erc-version) t)))
+         ;; Make subprocess terminal bigger than controlling.
+         (buf (cl-letf (((symbol-function 'window-screen-lines)
+                         (lambda () (car erc-scenarios-common--term-size)))
+                        ((symbol-function 'window-max-chars-per-line)
+                         (lambda () (cdr erc-scenarios-common--term-size))))
+                (apply #'make-term (symbol-name name)
+                       (expand-file-name invocation-name invocation-directory)
+                       nil `(,@(or init '("-Q")) "-nw"
+                             "-eval" ,(format "%S" setup)
+                             "-l" ,file-name
+                             "-eval" ,(format "%S" cmd)))))
+         (proc (get-buffer-process buf))
+         (err (lambda ()
+                (with-temp-buffer
+                  (insert-file-contents temp-file)
+                  (message "Subprocess: %s" (buffer-string))
+                  (delete-file temp-file)))))
+    (unless noninteractive
+      (set-window-buffer (selected-window) buf)
+      (delete-other-windows))
+    (with-current-buffer buf
+      (set-process-query-on-exit-flag proc nil)
+      (unless noninteractive (term-char-mode))
+      (erc-d-t-wait-for 30 (process-live-p proc))
+      (while (accept-process-output proc))
+      (term-line-mode)
+      (goto-char (point-min))
+      ;; Otherwise gives process exited abnormally with exit-code >0
+      (unless (search-forward (format "Process %s finished" name) nil t)
+        (funcall err)
+        (ert-fail (when (search-forward "exited" nil t)
+                    (buffer-substring-no-properties (line-beginning-position)
+                                                    (line-end-position)))))
+      (delete-file temp-file)
+      (when noninteractive
+        (kill-buffer)))))
+
+(defvar erc-scenarios-common-interactive-debug-term-p nil
+  "Non-nil means run test in an inferior Emacs, even if interactive.")
+
+(defmacro erc-scenarios-common-with-noninteractive-in-term (&rest body)
+  "Run BODY via `erc-scenarios-common-with-cleanup' in a `term' subprocess.
+Also do so when `erc-scenarios-common-interactive-debug-term-p'
+is non-nil.  When debugging, leave the `term-mode' buffer around
+for inspection and name it after the test, bounded by asterisks.
+When debugging, ensure the test always fails, as a reminder to
+disable `erc-scenarios-common-interactive-debug-term-p'.
+
+See Info node `(emacs) Term Mode' for the various commands."
+  (declare (indent 1))
+  `(if (and (or erc-scenarios-common-interactive-debug-term-p
+                noninteractive)
+            (not (getenv "ERC_TESTS_SUBPROCESS")))
+       (progn
+         (when (memq system-type '(windows-nt ms-dos cygwin haiku))
+           (ert-skip "System must be UNIX-like"))
+         (erc-scenarios-common--run-in-term
+          erc-scenarios-common-interactive-debug-term-p))
+     (erc-scenarios-common-with-cleanup ,@body)))
+
 (defun erc-scenarios-common-assert-initial-buf-name (id port)
   ;; Assert no limbo period when explicit ID given
   (should (string= (if id
@@ -210,9 +316,109 @@ Dialog resource directories are located by expanding the 
variable
     (insert str)
     (erc-send-current-line)))
 
+(defun erc-scenarios-common--at-win-end-p (&optional window)
+  (= (window-body-height window)
+     (count-screen-lines (window-start window) (point-max) nil window)))
+
+(defun erc-scenarios-common--above-win-end-p (&optional window)
+  (> (window-body-height window)
+     (count-screen-lines (window-start window) (point-max))))
+
+(defun erc-scenarios-common--prompt-past-win-end-p (&optional window)
+  (< (window-body-height window)
+     (count-screen-lines (window-start window) (point-max))))
+
+(defun erc-scenarios-common--recenter-top-bottom-around (orig &rest args)
+  (let (this-command last-command) (apply orig args)))
+
+(defun erc-scenarios-common--recenter-top-bottom ()
+  (advice-add 'recenter-top-bottom
+              :around #'erc-scenarios-common--recenter-top-bottom-around)
+  (execute-kbd-macro "\C-l")
+  (advice-remove 'recenter-top-bottom
+                 #'erc-scenarios-common--recenter-top-bottom-around))
+
 
 ;;;; Fixtures
 
+(defun erc-scenarios-common-scrolltobottom--normal (test)
+  (erc-scenarios-common-with-noninteractive-in-term
+      ((erc-scenarios-common-dialog "scrolltobottom")
+       (dumb-server (erc-d-run "localhost" t 'help))
+       (port (process-contact dumb-server :service))
+       (erc-modules `(scrolltobottom fill-wrap ,@erc-modules))
+       (erc-server-flood-penalty 0.1)
+       (expect (erc-d-t-make-expecter)))
+
+    (ert-info ("Connect")
+      (with-current-buffer (erc :server "127.0.0.1"
+                                :port port
+                                :full-name "tester"
+                                :nick "tester")
+        (funcall expect 10 "debug mode")))
+
+    (with-current-buffer "foonet"
+      (should (looking-at " and"))
+      (set-window-buffer nil (current-buffer))
+      (delete-other-windows)
+      (split-window-below 15)
+      (recenter 0)
+
+      (ert-info ("Moving into prompt in other window triggers scroll")
+        (with-selected-window (next-window)
+          (should-not (erc-scenarios-common--at-win-end-p))
+          (goto-char (1- erc-insert-marker))
+          (execute-kbd-macro "\C-n")
+          ;; Ensure point is at prompt and aligned to bottom.
+          (should (erc-scenarios-common--at-win-end-p))))
+
+      (ert-info ("Module `move-to-prompt' still works")
+        ;; Prompt is somewhere in the middle of the window.
+        (should (erc-scenarios-common--above-win-end-p))
+        ;; Hitting a self-insert key triggers `move-to-prompt' as well
+        ;; as a scroll (to bottom).
+        (execute-kbd-macro "hi")
+        ;; Prompt and input appear on last line of window.
+        (should (erc-scenarios-common--at-win-end-p)))
+
+      (ert-info ("Command `recenter-top-bottom' disallowed at prompt")
+        ;; Hitting C-l does not recenter the window.
+        (erc-scenarios-common--recenter-top-bottom)
+        (should (erc-scenarios-common--at-win-end-p))
+        (erc-scenarios-common--recenter-top-bottom)
+        (should (erc-scenarios-common--at-win-end-p)))
+
+      (ert-info ("Command `beginning-of-buffer' allowed at prompt")
+        ;; Hitting C-< goes to beginning of buffer.
+        (call-interactively #'beginning-of-buffer)
+        (should (= 1 (point)))
+        (redisplay)
+        (should (zerop (count-screen-lines (window-start) (point))))
+        (should (erc-scenarios-common--prompt-past-win-end-p)))
+
+      (ert-info ("New message doesn't trigger scroll when away from prompt")
+        ;; Arriving insertions don't trigger a scroll when away from the
+        ;; prompt.  New output not seen.
+        (erc-cmd-MSG "NickServ help register")
+        (save-excursion (erc-d-t-search-for 10 "End of NickServ"))
+        (should (= 1 (point)))
+        (redisplay)
+        (should (zerop (count-screen-lines (window-start) (window-point))))
+        (should (erc-scenarios-common--prompt-past-win-end-p)))
+
+      (funcall test)
+
+      (ert-info ("New message does trigger a scroll when at prompt")
+        ;; Recenter so prompt is above rather than at window's end.
+        (funcall expect 10 "If you are currently logged in")
+        (recenter 0)
+        ;; Prompt is somewhere in the middle of the window.
+        (erc-d-t-wait-for 10 (erc-scenarios-common--above-win-end-p))
+        (erc-scenarios-common-say "/msg NickServ help identify")
+        ;; New arriving messages trigger a snap when inserted.
+        (erc-d-t-wait-for 10 (erc-scenarios-common--at-win-end-p))
+        (funcall expect 10 "IDENTIFY lets you login")))))
+
 (cl-defun erc-scenarios-common--base-network-id-bouncer
     ((&key autop foo-id bar-id after
            &aux
diff --git a/test/lisp/erc/resources/join/legacy/foonet.eld 
b/test/lisp/erc/resources/join/legacy/foonet.eld
index 4025094a59c..5c0ea13b6a7 100644
--- a/test/lisp/erc/resources/join/legacy/foonet.eld
+++ b/test/lisp/erc/resources/join/legacy/foonet.eld
@@ -18,7 +18,7 @@
  (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3")
  (0 ":irc.foonet.org 422 tester :MOTD File is missing"))
 
-((mode-user 3.2 "MODE tester +i")
+((mode-user 10 "MODE tester +i")
  (0 ":irc.foonet.org 221 tester +i")
  (0 ":irc.foonet.org NOTICE tester :This server is in debug mode and is 
logging all user I/O. If you do not wish for everything you send to be readable 
by the server owner(s), please disconnect."))
 
diff --git a/test/lisp/erc/resources/scrolltobottom/help.eld 
b/test/lisp/erc/resources/scrolltobottom/help.eld
new file mode 100644
index 00000000000..ba44a0def39
--- /dev/null
+++ b/test/lisp/erc/resources/scrolltobottom/help.eld
@@ -0,0 +1,46 @@
+;; -*- mode: lisp-data; -*-
+((nick 10 "NICK tester"))
+((user 10 "USER user 0 * :tester")
+ (0.00 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester")
+ (0.01 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running 
version ergo-v2.11.1")
+ (0.01 ":irc.foonet.org 003 tester :This server was created Mon, 21 Aug 2023 
06:18:36 UTC")
+ (0.02 ":irc.foonet.org 004 tester irc.foonet.org ergo-v2.11.1 BERTZios 
CEIMRUabefhiklmnoqstuv Iabefhkloqv")
+ (0.00 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii 
CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# 
CHATHISTORY=1000 ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX :are supported by 
this server")
+ (0.01 ":irc.foonet.org 005 tester KICKLEN=390 MAXLIST=beI:60 MAXTARGETS=4 
MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ 
TARGMAX=NAMES:1,LIST:1,KICK:,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100
 TOPICLEN=390 UTF8ONLY WHOX :are supported by this server")
+ (0.01 ":irc.foonet.org 005 tester draft/CHATHISTORY=1000 :are supported by 
this server")
+ (0.01 ":irc.foonet.org 251 tester :There are 0 users and 4 invisible on 1 
server(s)")
+ (0.01 ":irc.foonet.org 252 tester 0 :IRC Operators online")
+ (0.01 ":irc.foonet.org 253 tester 0 :unregistered connections")
+ (0.01 ":irc.foonet.org 254 tester 2 :channels formed")
+ (0.01 ":irc.foonet.org 255 tester :I have 4 clients and 0 servers")
+ (0.01 ":irc.foonet.org 265 tester 4 4 :Current local users 4, max 4")
+ (0.01 ":irc.foonet.org 266 tester 4 4 :Current global users 4, max 4")
+ (0.01 ":irc.foonet.org 422 tester :MOTD File is missing"))
+
+((mode 10 "MODE tester +i")
+ (0.00 ":irc.foonet.org 221 tester +i")
+ (0.01 ":irc.foonet.org NOTICE tester :This server is in debug mode and is 
logging all user I/O. If you do not wish for everything you send to be readable 
by the server owner(s), please disconnect.")
+ (0.02 ":irc.foonet.org 221 tester +i"))
+
+((privmsg-help-register 10 "PRIVMSG NickServ :help register")
+ (0.05 ":NickServ!NickServ@localhost NOTICE tester :*** \2NickServ HELP\2 ***")
+ (0.02 ":NickServ!NickServ@localhost NOTICE tester :Syntax: \2REGISTER 
<password> [email]\2")
+ (0.02 ":NickServ!NickServ@localhost NOTICE tester :")
+ (0.01 ":NickServ!NickServ@localhost NOTICE tester :REGISTER lets you register 
your current nickname as a user account. If the")
+ (0.01 ":NickServ!NickServ@localhost NOTICE tester :server allows anonymous 
registration, you can omit the e-mail address.")
+ (0.01 ":NickServ!NickServ@localhost NOTICE tester :")
+ (0.01 ":NickServ!NickServ@localhost NOTICE tester :If you are currently 
logged in with a TLS client certificate and wish to use")
+ (0.02 ":NickServ!NickServ@localhost NOTICE tester :it instead of a password 
to log in, send * as the password.")
+ (0.02 ":NickServ!NickServ@localhost NOTICE tester :*** \2End of NickServ 
HELP\2 ***"))
+
+((privmsg-help-identify 20 "PRIVMSG NickServ :help identify")
+ (0.06 ":NickServ!NickServ@localhost NOTICE tester :*** \2NickServ HELP\2 ***")
+ (0.02 ":NickServ!NickServ@localhost NOTICE tester :Syntax: \2IDENTIFY 
<username> [password]\2")
+ (0.02 ":NickServ!NickServ@localhost NOTICE tester :")
+ (0.02 ":NickServ!NickServ@localhost NOTICE tester :IDENTIFY lets you login to 
the given username using either password auth, or")
+ (0.02 ":NickServ!NickServ@localhost NOTICE tester :certfp (your client 
certificate) if a password is not given.")
+ (0.02 ":NickServ!NickServ@localhost NOTICE tester :*** \2End of NickServ 
HELP\2 ***"))
+
+((quit 10 "QUIT :\2ERC\2 ")
+ (0.07 ":tester!~u@26axz8nh8zaag.irc QUIT :Quit: \2ERC\2")
+ (0.02 "ERROR :Quit: \2ERC\2"))
diff --git a/test/lisp/eshell/em-prompt-tests.el 
b/test/lisp/eshell/em-prompt-tests.el
index 93bf9d84ab3..46e74e64983 100644
--- a/test/lisp/eshell/em-prompt-tests.el
+++ b/test/lisp/eshell/em-prompt-tests.el
@@ -32,6 +32,11 @@
                            (file-name-directory (or load-file-name
                                                     default-directory))))
 
+(defmacro em-prompt-test--with-multiline (&rest body)
+  "Execute BODY with a multiline Eshell prompt."
+  `(let ((eshell-prompt-function (lambda () "multiline prompt\n$ ")))
+     ,@body))
+
 ;;; Tests:
 
 (ert-deftest em-prompt-test/field-properties ()
@@ -80,64 +85,108 @@ This tests the case when `eshell-highlight-prompt' is nil."
                 (apply #'propertize "hello\n"
                        eshell-command-output-properties)))))))
 
-(defmacro em-prompt-test--with-multiline (&rest body)
-  "Execute BODY with a multiline Eshell prompt."
-  `(let ((eshell-prompt-function (lambda () "multiline prompt\n$ ")))
-     ,@body))
+(ert-deftest em-prompt-test/after-failure ()
+  "Check that current prompt shows the exit code of the last failed command."
+  (with-temp-eshell
+   (let ((debug-on-error nil))
+     (eshell-insert-command "(zerop \"foo\")"))
+   (let ((current-prompt (field-string (1- (point)))))
+     (should (equal-including-properties
+              current-prompt
+              (propertize
+               (concat (directory-file-name default-directory)
+                       (unless (eshell-exit-success-p)
+                         (format " [%d]" eshell-last-command-status))
+                       (if (= (file-user-uid) 0) " # " " $ "))
+               'read-only t
+               'field 'prompt
+               'font-lock-face 'eshell-prompt
+               'front-sticky '(read-only field font-lock-face)
+               'rear-nonsticky '(read-only field font-lock-face)))))))
 
-(defun em-prompt-test/next-previous-prompt-with ()
+(defun em-prompt-test/next-previous-prompt-1 ()
   "Helper for checking forward/backward navigation of old prompts."
   (with-temp-eshell
    (eshell-insert-command "echo one")
    (eshell-insert-command "echo two")
    (eshell-insert-command "echo three")
+   (let ((debug-on-error nil))          ; A failed command.
+     (eshell-insert-command "(zerop \"foo\")"))
    (insert "echo fou")                  ; A partially-entered command.
-   ;; Go back one prompt.
-   (eshell-previous-prompt 1)
-   (should (equal (eshell-get-old-input) "echo three"))
-   ;; Go back two prompts, starting from the end of this line.
-   (end-of-line)
-   (eshell-previous-prompt 2)
-   (should (equal (eshell-get-old-input) "echo one"))
-   ;; Go forward three prompts.
-   (eshell-next-prompt 3)
-   (should (equal (eshell-get-old-input) "echo fou"))))
+   (ert-info ("Go back one prompt")
+     (eshell-previous-prompt)
+     (should (equal (point) (field-beginning)))
+     (should (equal (field-string) "(zerop \"foo\")\n")))
+   (ert-info ("Go back three prompts, starting from the end of the input")
+     (end-of-line)
+     (eshell-previous-prompt 3)
+     (should (equal (point) (field-beginning)))
+     (should (equal (field-string) "echo one\n")))
+   (ert-info ("Go to the current prompt, starting from the end of the input")
+     (end-of-line)
+     (eshell-previous-prompt 0)
+     (should (equal (point) (field-beginning)))
+     (should (equal (field-string) "echo one\n")))
+   (ert-info ("Go forward one prompt")
+     (eshell-next-prompt)
+     (should (equal (point) (field-beginning)))
+     (should (equal (field-string) "echo two\n")))
+   (ert-info ("Go forward three prompts")
+     (eshell-next-prompt 3)
+     (should (equal (point) (field-beginning)))
+     (should (equal (field-string) "echo fou")))
+   (ert-info ("Go back one prompt, starting from the beginning of the line")
+     (forward-line 0)
+     (eshell-previous-prompt 1)
+     (should (equal (point) (field-beginning)))
+     (should (equal (field-string) "(zerop \"foo\")\n")))
+   (ert-info ("Go back one prompt, starting from the previous prompt's output")
+     (forward-line -1)
+     (eshell-previous-prompt 1)
+     (should (equal (point) (field-beginning)))
+     (should (equal (field-string) "echo three\n")))))
 
 (ert-deftest em-prompt-test/next-previous-prompt ()
   "Check that navigating forward/backward through old prompts works correctly."
-  (em-prompt-test/next-previous-prompt-with))
+  (em-prompt-test/next-previous-prompt-1))
 
 (ert-deftest em-prompt-test/next-previous-prompt-multiline ()
   "Check old prompt forward/backward navigation for multiline prompts."
   (em-prompt-test--with-multiline
-   (em-prompt-test/next-previous-prompt-with)))
+   (em-prompt-test/next-previous-prompt-1)))
 
-(defun em-prompt-test/forward-backward-matching-input-with ()
+(defun em-prompt-test/forward-backward-matching-input-1 ()
   "Helper for checking forward/backward navigation via regexps."
   (with-temp-eshell
    (eshell-insert-command "echo one")
    (eshell-insert-command "printnl something else")
    (eshell-insert-command "echo two")
    (eshell-insert-command "echo three")
+   (let ((debug-on-error nil))          ; A failed command.
+     (eshell-insert-command "(zerop \"foo\")"))
    (insert "echo fou")                  ; A partially-entered command.
-   ;; Go back one prompt.
-   (eshell-backward-matching-input "echo" 1)
-   (should (equal (eshell-get-old-input) "echo three"))
-   ;; Go back two prompts, starting from the end of this line.
-   (end-of-line)
-   (eshell-backward-matching-input "echo" 2)
-   (should (equal (eshell-get-old-input) "echo one"))
-   ;; Go forward three prompts.
-   (eshell-forward-matching-input "echo" 3)
-   (should (equal (eshell-get-old-input) "echo fou"))))
+   (ert-info ("Search for \"echo\", back one prompt")
+     (eshell-backward-matching-input "echo" 1)
+     (should (equal (point) (field-beginning)))
+     (should (equal (field-string) "echo three\n")))
+   (ert-info ((concat "Search for \"echo\", back two prompts, "
+                      "starting from the end of this line"))
+     (end-of-line)
+     (eshell-backward-matching-input "echo" 2)
+     (should (equal (point) (field-beginning)))
+     (should (equal (field-string) "echo one\n")))
+   (ert-info ("Search for \"echo\", forward three prompts")
+     (eshell-forward-matching-input "echo" 3)
+     (should (equal (point) (field-beginning)))
+     (should (equal (field-string) "echo fou")))))
 
 (ert-deftest em-prompt-test/forward-backward-matching-input ()
   "Check that navigating forward/backward via regexps works correctly."
-  (em-prompt-test/forward-backward-matching-input-with))
+  (em-prompt-test/forward-backward-matching-input-1))
 
 (ert-deftest em-prompt-test/forward-backward-matching-input-multiline ()
   "Check forward/backward regexp navigation for multiline prompts."
   (em-prompt-test--with-multiline
-   (em-prompt-test/forward-backward-matching-input-with)))
+   (em-prompt-test/forward-backward-matching-input-1)))
 
 ;;; em-prompt-tests.el ends here
diff --git a/test/lisp/eshell/em-script-tests.el 
b/test/lisp/eshell/em-script-tests.el
index 74328844778..02e4125d827 100644
--- a/test/lisp/eshell/em-script-tests.el
+++ b/test/lisp/eshell/em-script-tests.el
@@ -63,6 +63,19 @@
         "\\`\\'"))
       (should (equal (buffer-string) "hibye")))))
 
+(ert-deftest em-script-test/source-script/background ()
+  "Test sourcing a script in the background."
+  (skip-unless (executable-find "echo"))
+  (ert-with-temp-file temp-file
+    :text "*echo hi\nif {[ foo = foo ]} {*echo bye}"
+    (eshell-with-temp-buffer bufname "old"
+      (with-temp-eshell
+       (eshell-match-command-output
+        (format "source %s > #<%s> &" temp-file bufname)
+        "\\`\\'")
+       (eshell-wait-for-subprocess t))
+      (should (equal (buffer-string) "hi\nbye\n")))))
+
 (ert-deftest em-script-test/source-script/arg-vars ()
   "Test sourcing script with $0, $1, ... variables."
   (ert-with-temp-file temp-file :text "printnl $0 \"$1 $2\""
diff --git a/test/lisp/eshell/esh-cmd-tests.el 
b/test/lisp/eshell/esh-cmd-tests.el
index a7208eb3a0b..e0783b26ad6 100644
--- a/test/lisp/eshell/esh-cmd-tests.el
+++ b/test/lisp/eshell/esh-cmd-tests.el
@@ -80,6 +80,12 @@ e.g. \"{(+ 1 2)} 3\" => 3"
    (eshell-match-command-output "echo ${echo $value}"
                                 "hello\n")))
 
+(ert-deftest esh-cmd-test/skip-leading-nils ()
+  "Test that Eshell skips leading nil arguments for named commands."
+  (eshell-command-result-equal "$eshell-test-value echo hello" "hello")
+  (eshell-command-result-equal
+   "$eshell-test-value $eshell-test-value echo hello" "hello"))
+
 (ert-deftest esh-cmd-test/let-rebinds-after-defer ()
   "Test that let-bound values are properly updated after `eshell-defer'.
 When inside a `let' block in an Eshell command form, we need to
@@ -98,6 +104,32 @@ bug#59469."
     "value\nexternal\nvalue\n")))
 
 
+;; Background command invocation
+
+(ert-deftest esh-cmd-test/background/simple-command ()
+  "Test invocation with a simple background command."
+  (skip-unless (executable-find "echo"))
+  (eshell-with-temp-buffer bufname ""
+    (with-temp-eshell
+     (eshell-match-command-output
+      (format "*echo hi > #<%s> &" bufname)
+      (rx "[echo" (? ".exe") "] " (+ digit) "\n"))
+     (eshell-wait-for-subprocess t))
+    (should (equal (buffer-string) "hi\n"))))
+
+(ert-deftest esh-cmd-test/background/subcommand ()
+  "Test invocation with a background command containing subcommands."
+  (skip-unless (and (executable-find "echo")
+                    (executable-find "rev")))
+  (eshell-with-temp-buffer bufname ""
+    (with-temp-eshell
+     (eshell-match-command-output
+      (format "*echo ${*echo hello | rev} > #<%s> &" bufname)
+      (rx "[echo" (? ".exe") "] " (+ digit) "\n"))
+     (eshell-wait-for-subprocess t))
+    (should (equal (buffer-string) "olleh\n"))))
+
+
 ;; Lisp forms
 
 (ert-deftest esh-cmd-test/quoted-lisp-form ()
@@ -138,6 +170,78 @@ bug#59469."
                                 "hi\n")))
 
 
+;; Pipelines
+
+(ert-deftest esh-cmd-test/pipeline-wait/head-proc ()
+  "Check that piping a non-process to a process command waits for the process."
+  (skip-unless (executable-find "cat"))
+  (with-temp-eshell
+   (eshell-match-command-output "echo hi | *cat"
+                                "hi")))
+
+(ert-deftest esh-cmd-test/pipeline-wait/tail-proc ()
+  "Check that piping a process to a non-process command waits for the process."
+  (skip-unless (executable-find "echo"))
+  (with-temp-eshell
+   (eshell-match-command-output "*echo hi | echo bye"
+                                "bye\nhi\n")))
+
+(ert-deftest esh-cmd-test/pipeline-wait/multi-proc ()
+  "Check that a pipeline waits for all its processes before returning."
+  (skip-unless (and (executable-find "echo")
+                    (executable-find "sh")
+                    (executable-find "rev")))
+  (with-temp-eshell
+   (eshell-match-command-output
+    "*echo hello | sh -c 'sleep 1; rev' 1>&2 | *echo goodbye"
+    "goodbye\nolleh\n")))
+
+(ert-deftest esh-cmd-test/pipeline-wait/subcommand ()
+  "Check that piping with an asynchronous subcommand waits for the subcommand."
+  (skip-unless (and (executable-find "echo")
+                    (executable-find "cat")))
+  (with-temp-eshell
+   (eshell-match-command-output "echo ${*echo hi} | *cat"
+                                "hi")))
+
+(ert-deftest esh-cmd-test/pipeline-wait/subcommand-with-pipe ()
+  "Check that piping with an asynchronous subcommand with its own pipe works.
+This should also wait for the subcommand."
+  (skip-unless (and (executable-find "echo")
+                    (executable-find "cat")))
+  (with-temp-eshell
+   (eshell-match-command-output "echo ${*echo hi | *cat} | *cat"
+                                "hi")))
+
+(ert-deftest esh-cmd-test/reset-in-pipeline/subcommand ()
+  "Check that subcommands reset `eshell-in-pipeline-p'."
+  (skip-unless (executable-find "cat"))
+  (dolist (template '("echo {%s} | *cat"
+                      "echo ${%s} | *cat"
+                      "*cat $<%s> | *cat"))
+    (eshell-command-result-equal
+     (format template "echo $eshell-in-pipeline-p")
+     nil)
+    (eshell-command-result-equal
+     (format template "echo | echo $eshell-in-pipeline-p")
+     "last")
+    (eshell-command-result-equal
+     (format template "echo $eshell-in-pipeline-p | echo")
+     "first")
+    (eshell-command-result-equal
+     (format template "echo | echo $eshell-in-pipeline-p | echo")
+     "t")))
+
+(ert-deftest esh-cmd-test/reset-in-pipeline/lisp ()
+  "Check that interpolated Lisp forms reset `eshell-in-pipeline-p'."
+  (skip-unless (executable-find "cat"))
+  (dolist (template '("echo (%s) | *cat"
+                      "echo $(%s) | *cat"))
+    (eshell-command-result-equal
+     (format template "format \"%s\" eshell-in-pipeline-p")
+     "nil")))
+
+
 ;; Control flow statements
 
 (ert-deftest esh-cmd-test/for-loop ()
@@ -364,4 +468,19 @@ This tests when `eshell-lisp-form-nil-is-failure' is nil."
   (eshell-command-result-equal "unless {[ foo = bar ]} {echo no} {echo yes}"
                                "no"))
 
+
+;; Error handling
+
+(ert-deftest esh-cmd-test/throw ()
+  "Test that calling `throw' as an Eshell command unwinds everything properly."
+  (with-temp-eshell
+   (should (= (catch 'tag
+                (eshell-insert-command
+                 "echo hi; (throw 'tag 42); echo bye"))
+              42))
+   (should (eshell-match-output "\\`hi\n\\'"))
+   (should-not eshell-foreground-command)
+   ;; Make sure we can call another command after throwing.
+   (eshell-match-command-output "echo again" "\\`again\n")))
+
 ;; esh-cmd-tests.el ends here
diff --git a/test/lisp/eshell/esh-io-tests.el b/test/lisp/eshell/esh-io-tests.el
index ce80f3a8f08..0201b6ab650 100644
--- a/test/lisp/eshell/esh-io-tests.el
+++ b/test/lisp/eshell/esh-io-tests.el
@@ -334,6 +334,17 @@ stdout originally pointed (the terminal)."
    (eshell-match-command-output "{echo foo; echo bar} | rev"
                                 "\\`raboof\n?")))
 
+(ert-deftest esh-io-test/pipeline/stdin-to-head ()
+  "Check that standard input is sent to the head process in a pipeline."
+  (skip-unless (and (executable-find "tr")
+                    (executable-find "rev")))
+  (with-temp-eshell
+   (eshell-insert-command "tr a-z A-Z | rev")
+   (eshell-insert-command "hello")
+   (eshell-send-eof-to-process)
+   (eshell-wait-for-subprocess)
+   (should (eshell-match-output "OLLEH\n"))))
+
 
 ;; Virtual targets
 
diff --git a/test/lisp/eshell/esh-proc-tests.el 
b/test/lisp/eshell/esh-proc-tests.el
index fa20efa71e1..9118bcd1c61 100644
--- a/test/lisp/eshell/esh-proc-tests.el
+++ b/test/lisp/eshell/esh-proc-tests.el
@@ -86,7 +86,7 @@
       "\\`\\'"))
     (should (equal (buffer-string) "stdout\nstderr\n"))))
 
-(ert-deftest esh-var-test/output/remote-redirect ()
+(ert-deftest esh-proc-test/output/remote-redirect ()
   "Check that redirecting stdout for a remote process works."
   (skip-unless (and (eshell-tests-remote-accessible-p)
                     (executable-find "echo")))
@@ -137,18 +137,19 @@
   (skip-unless (and (executable-find "sh")
                     (executable-find "echo")
                     (executable-find "sleep")))
-  (with-temp-eshell
-   (eshell-match-command-output
-    ;; The first command is like `yes' but slower.  This is to prevent
-    ;; it from taxing Emacs's process filter too much and causing a
-    ;; hang.  Note that we use "|&" to connect the processes so that
-    ;; Emacs doesn't create an extra pipe process for the first "sh"
-    ;; invocation.
-    (concat "sh -c 'while true; do echo y; sleep 1; done' |& "
-            "sh -c 'read NAME; echo ${NAME}'")
-    "y\n")
-   (eshell-wait-for-subprocess t)
-   (should (eq (process-list) nil))))
+  (let ((starting-process-list (process-list)))
+    (with-temp-eshell
+     (eshell-match-command-output
+      ;; The first command is like `yes' but slower.  This is to prevent
+      ;; it from taxing Emacs's process filter too much and causing a
+      ;; hang.  Note that we use "|&" to connect the processes so that
+      ;; Emacs doesn't create an extra pipe process for the first "sh"
+      ;; invocation.
+      (concat "sh -c 'while true; do echo y; sleep 1; done' |& "
+              "sh -c 'read NAME; echo ${NAME}'")
+      "y\n")
+     (eshell-wait-for-subprocess t)
+     (should (equal (process-list) starting-process-list)))))
 
 (ert-deftest esh-proc-test/pipeline-connection-type/no-pipeline ()
   "Test that all streams are PTYs when a command is not in a pipeline."
@@ -173,23 +174,17 @@
 pipeline."
   (skip-unless (and (executable-find "sh")
                     (executable-find "cat")))
-  ;; An `eshell-pipe-broken' signal might occur internally; let Eshell
-  ;; handle it!
-  (let ((debug-on-error nil))
-    (eshell-command-result-equal
-     (concat "echo hi | " esh-proc-test--detect-pty-cmd " | cat")
-     nil)))
+  (eshell-command-result-equal
+   (concat "(ignore) | " esh-proc-test--detect-pty-cmd " | cat")
+   nil))
 
 (ert-deftest esh-proc-test/pipeline-connection-type/last ()
   "Test that only output streams are PTYs when a command ends a pipeline."
   (skip-unless (executable-find "sh"))
-  ;; An `eshell-pipe-broken' signal might occur internally; let Eshell
-  ;; handle it!
-  (let ((debug-on-error nil))
-    (eshell-command-result-equal
-     (concat "echo hi | " esh-proc-test--detect-pty-cmd)
-     (unless (eq system-type 'windows-nt)
-       "stdout\nstderr\n"))))
+  (eshell-command-result-equal
+   (concat "(ignore) | " esh-proc-test--detect-pty-cmd)
+   (unless (eq system-type 'windows-nt)
+     "stdout\nstderr\n")))
 
 
 ;; Synchronous processes
@@ -281,7 +276,7 @@ prompt.  See bug#54136."
                     (executable-find "sleep")))
   ;; This test doesn't work on EMBA with AOT nativecomp, but works
   ;; fine elsewhere.
-  (skip-unless (not (getenv "EMACS_EMBA_CI")))
+  (skip-when (getenv "EMACS_EMBA_CI"))
   (with-temp-eshell
    (eshell-insert-command
     (concat "sh -c 'while true; do echo y; sleep 1; done' | "
@@ -312,4 +307,28 @@ write the exit status to the pipe.  See bug#54136."
                      output-start (eshell-end-of-output))
                     "")))))
 
+(ert-deftest esh-proc-test/kill-process/redirect-message ()
+  "Test that killing a process with a redirected stderr omits the exit status."
+  (skip-unless (executable-find "sleep"))
+  (eshell-with-temp-buffer bufname ""
+    (with-temp-eshell
+     (eshell-insert-command (format "sleep 100 2> #<buffer %s>" bufname))
+     (kill-process (eshell-head-process)))
+    (should (equal (buffer-string) ""))))
+
+
+;; Remote processes
+
+(ert-deftest esh-var-test/remote/remote-path ()
+  "Ensure that setting the remote PATH in Eshell doesn't interfere with Tramp.
+See bug#65551."
+  (skip-unless (and (eshell-tests-remote-accessible-p)
+                    (executable-find "echo")))
+  (let ((default-directory ert-remote-temporary-file-directory))
+    (with-temp-eshell
+     (eshell-insert-command "set PATH ''")
+     (eshell-match-command-output
+      (format "%s hello" (executable-find "echo" t))
+      "\\`hello\n"))))
+
 ;;; esh-proc-tests.el ends here
diff --git a/test/lisp/eshell/esh-util-tests.el 
b/test/lisp/eshell/esh-util-tests.el
index fe4eb9f31dd..7bd71b260ff 100644
--- a/test/lisp/eshell/esh-util-tests.el
+++ b/test/lisp/eshell/esh-util-tests.el
@@ -19,9 +19,15 @@
 
 ;;; Code:
 
+(require 'tramp)
 (require 'ert)
 (require 'esh-util)
 
+(require 'eshell-tests-helpers
+         (expand-file-name "eshell-tests-helpers"
+                           (file-name-directory (or load-file-name
+                                                    default-directory))))
+
 ;;; Tests:
 
 (ert-deftest esh-util-test/eshell-stringify/string ()
@@ -156,4 +162,28 @@
 (ert-deftest esh-util-test/eshell-printable-size/human-readable-arg ()
   (should-error (eshell-printable-size 0 999 nil t)))
 
+(ert-deftest esh-util-test/path/get ()
+  "Test that getting the Eshell path returns the expected results."
+  (let ((expected-path (butlast (exec-path))))
+    (should (equal (eshell-get-path)
+                   (if (eshell-under-windows-p)
+                       (cons "." expected-path)
+                     expected-path)))
+    (should (equal (eshell-get-path 'literal)
+                   expected-path))))
+
+(ert-deftest esh-util-test/path/get-remote ()
+  "Test that getting the remote Eshell path returns the expected results."
+  (let* ((default-directory ert-remote-temporary-file-directory)
+         (expected-path (butlast (exec-path))))
+    ;; Make sure we don't have a doubled directory separator.
+    (should (seq-every-p (lambda (i) (not (string-match-p "//" i)))
+                         (eshell-get-path)))
+    (should (equal (eshell-get-path)
+                   (mapcar (lambda (i)
+                             (concat (file-remote-p default-directory) i))
+                           expected-path)))
+    (should (equal (eshell-get-path 'literal)
+                   expected-path))))
+
 ;;; esh-util-tests.el ends here
diff --git a/test/lisp/eshell/esh-var-tests.el 
b/test/lisp/eshell/esh-var-tests.el
index ff646f5f977..83c0f480627 100644
--- a/test/lisp/eshell/esh-var-tests.el
+++ b/test/lisp/eshell/esh-var-tests.el
@@ -645,6 +645,14 @@ nil, use FUNCTION instead."
    (eshell-match-command-output "VAR=hello env" "VAR=hello\n")
    (should (equal (getenv "VAR") "value"))))
 
+(ert-deftest esh-var-test/local-variables/skip-nil ()
+  "Test that Eshell skips leading nil arguments after local variable setting."
+  (with-temp-eshell
+   (push "VAR=value" process-environment)
+   (eshell-match-command-output "VAR=hello $eshell-test-value env"
+                                "VAR=hello\n")
+   (should (equal (getenv "VAR") "value"))))
+
 
 ;; Variable aliases
 
diff --git a/test/lisp/eshell/eshell-tests-helpers.el 
b/test/lisp/eshell/eshell-tests-helpers.el
index 2c913d71cb4..c983c8fd2bb 100644
--- a/test/lisp/eshell/eshell-tests-helpers.el
+++ b/test/lisp/eshell/eshell-tests-helpers.el
@@ -54,6 +54,13 @@ beginning of the test file."
        (let* (;; We want no history file, so prevent Eshell from falling
               ;; back on $HISTFILE.
               (process-environment (cons "HISTFILE" process-environment))
+              ;; Enable process debug instrumentation.  We may be able
+              ;; to remove this eventually once we're confident that
+              ;; all the process bugs have been worked out.  (At that
+              ;; point, we can just enable this selectively when
+              ;; needed.)  See also `eshell-test-command-result'
+              ;; below.
+              (eshell-debug-command (cons 'process eshell-debug-command))
               (eshell-history-file-name nil)
               (eshell-last-dir-ring-file-name nil)
               (eshell-buffer (eshell t)))
@@ -96,6 +103,13 @@ raise an error."
    (lambda ()
      (not (if all eshell-process-list (eshell-interactive-process-p))))))
 
+(defun eshell-get-debug-logs ()
+  "Get debug command logs for displaying on test failures."
+  (when (get-buffer eshell-debug-command-buffer)
+    (let ((separator (make-string 40 ?-)))
+      (with-current-buffer eshell-debug-command-buffer
+        (string-replace "\f" separator (buffer-string))))))
+
 (defun eshell-insert-command (command &optional func)
   "Insert a COMMAND at the end of the buffer.
 After inserting, call FUNC.  If FUNC is nil, instead call
@@ -135,17 +149,21 @@ FUNC is the function to call after inserting the text (see
 
 If IGNORE-ERRORS is non-nil, ignore any errors signaled when
 inserting the command."
-  (let ((debug-on-error (and (not ignore-errors) debug-on-error)))
-    (eshell-insert-command command func))
-  (eshell-wait-for-subprocess)
-  (should (eshell-match-output regexp)))
+  (ert-info (#'eshell-get-debug-logs :prefix "Command logs: ")
+    (let ((debug-on-error (and (not ignore-errors) debug-on-error)))
+      (eshell-insert-command command func))
+    (eshell-wait-for-subprocess)
+    (should (eshell-match-output regexp))))
 
 (defvar eshell-history-file-name)
 
 (defun eshell-test-command-result (command)
   "Like `eshell-command-result', but not using HOME."
   (ert-with-temp-directory eshell-directory-name
-    (let ((eshell-history-file-name nil))
+    (let ((eshell-history-file-name nil)
+          ;; Enable process debug instrumentation.  See
+          ;; `with-temp-eshell' above.
+          (eshell-debug-command (cons 'process eshell-debug-command)))
       (eshell-command-result command))))
 
 (defun eshell-command-result--equal (_command actual expected)
@@ -164,10 +182,11 @@ inserting the command."
 
 (defun eshell-command-result-equal (command result)
   "Execute COMMAND non-interactively and compare it to RESULT."
-  (should (eshell-command-result--equal
-           command
-           (eshell-test-command-result command)
-           result)))
+  (ert-info (#'eshell-get-debug-logs :prefix "Command logs: ")
+    (should (eshell-command-result--equal
+             command
+             (eshell-test-command-result command)
+             result))))
 
 (provide 'eshell-tests-helpers)
 
diff --git a/test/lisp/eshell/eshell-tests.el b/test/lisp/eshell/eshell-tests.el
index 46c9482ecf4..b02e5fca592 100644
--- a/test/lisp/eshell/eshell-tests.el
+++ b/test/lisp/eshell/eshell-tests.el
@@ -38,75 +38,6 @@
 
 ;;; Tests:
 
-(ert-deftest eshell-test/pipe-headproc ()
-  "Check that piping a non-process to a process command waits for the process"
-  (skip-unless (executable-find "cat"))
-  (with-temp-eshell
-   (eshell-match-command-output "echo hi | *cat"
-                                "hi")))
-
-(ert-deftest eshell-test/pipe-tailproc ()
-  "Check that piping a process to a non-process command waits for the process"
-  (skip-unless (executable-find "echo"))
-  (with-temp-eshell
-   (eshell-match-command-output "*echo hi | echo bye"
-                                "bye\nhi\n")))
-
-(ert-deftest eshell-test/pipe-headproc-stdin ()
-  "Check that standard input is sent to the head process in a pipeline"
-  (skip-unless (and (executable-find "tr")
-                    (executable-find "rev")))
-  (with-temp-eshell
-   (eshell-insert-command "tr a-z A-Z | rev")
-   (eshell-insert-command "hello")
-   (eshell-send-eof-to-process)
-   (eshell-wait-for-subprocess)
-   (should (eshell-match-output "OLLEH\n"))))
-
-(ert-deftest eshell-test/pipe-subcommand ()
-  "Check that piping with an asynchronous subcommand works"
-  (skip-unless (and (executable-find "echo")
-                    (executable-find "cat")))
-  (with-temp-eshell
-   (eshell-match-command-output "echo ${*echo hi} | *cat"
-                                "hi")))
-
-(ert-deftest eshell-test/pipe-subcommand-with-pipe ()
-  "Check that piping with an asynchronous subcommand with its own pipe works"
-  (skip-unless (and (executable-find "echo")
-                    (executable-find "cat")))
-  (with-temp-eshell
-   (eshell-match-command-output "echo ${*echo hi | *cat} | *cat"
-                                "hi")))
-
-(ert-deftest eshell-test/subcommand-reset-in-pipeline ()
-  "Check that subcommands reset `eshell-in-pipeline-p'."
-  (skip-unless (executable-find "cat"))
-  (dolist (template '("echo {%s} | *cat"
-                      "echo ${%s} | *cat"
-                      "*cat $<%s> | *cat"))
-    (eshell-command-result-equal
-     (format template "echo $eshell-in-pipeline-p")
-     nil)
-    (eshell-command-result-equal
-     (format template "echo | echo $eshell-in-pipeline-p")
-     "last")
-    (eshell-command-result-equal
-     (format template "echo $eshell-in-pipeline-p | echo")
-     "first")
-    (eshell-command-result-equal
-     (format template "echo | echo $eshell-in-pipeline-p | echo")
-     "t")))
-
-(ert-deftest eshell-test/lisp-reset-in-pipeline ()
-  "Check that interpolated Lisp forms reset `eshell-in-pipeline-p'."
-  (skip-unless (executable-find "cat"))
-  (dolist (template '("echo (%s) | *cat"
-                      "echo $(%s) | *cat"))
-    (eshell-command-result-equal
-     (format template "format \"%s\" eshell-in-pipeline-p")
-     "nil")))
-
 (ert-deftest eshell-test/eshell-command/simple ()
   "Test that the `eshell-command' function writes to the current buffer."
   (skip-unless (executable-find "echo"))
@@ -127,6 +58,18 @@ This test uses a pipeline for the command."
         (eshell-command "*echo hi | *cat" t)
         (should (equal (buffer-string) "hi\n"))))))
 
+(ert-deftest eshell-test/eshell-command/pipeline-wait ()
+  "Check that `eshell-command' waits for all its processes before returning."
+  (skip-unless (and (executable-find "echo")
+                    (executable-find "sh")
+                    (executable-find "rev")))
+  (ert-with-temp-directory eshell-directory-name
+    (let ((eshell-history-file-name nil))
+      (with-temp-buffer
+        (eshell-command
+         "*echo hello | sh -c 'sleep 1; rev' 1>&2 | *echo goodbye" t)
+        (should (equal (buffer-string) "goodbye\nolleh\n"))))))
+
 (ert-deftest eshell-test/eshell-command/background ()
   "Test that `eshell-command' works for background commands."
   (skip-unless (executable-find "echo"))
@@ -144,13 +87,36 @@ This test uses a pipeline for the command."
   (skip-unless (and (executable-find "echo")
                     (executable-find "cat")))
   (ert-with-temp-directory eshell-directory-name
-    (let ((orig-processes (copy-tree (process-list)))
+    (let ((orig-processes (process-list))
           (eshell-history-file-name nil))
       (with-temp-buffer
         (eshell-command "*echo hi | *cat &" t)
         (eshell-wait-for (lambda () (equal (process-list) orig-processes)))
         (should (equal (buffer-string) "hi\n"))))))
 
+(ert-deftest eshell-test/eshell-command/output-buffer/sync ()
+  "Test that the `eshell-command' function writes to its output buffer."
+  (skip-unless (executable-find "echo"))
+  (ert-with-temp-directory eshell-directory-name
+    (let ((eshell-history-file-name nil))
+      (eshell-command "*echo 'hi\nbye'")
+      (with-current-buffer "*Eshell Command Output*"
+        (should (equal (buffer-string) "hi\nbye")))
+      (kill-buffer "*Eshell Command Output*"))))
+
+(ert-deftest eshell-test/eshell-command/output-buffer/async ()
+  "Test that the `eshell-command' function writes to its async output buffer."
+  (skip-unless (executable-find "echo"))
+  (ert-with-temp-directory eshell-directory-name
+    (let ((orig-processes (process-list))
+          (eshell-history-file-name nil))
+      (eshell-command "*echo hi &")
+      (eshell-wait-for (lambda () (equal (process-list) orig-processes)))
+      (with-current-buffer "*Eshell Async Command Output*"
+        (goto-char (point-min))
+        (forward-line)
+        (should (looking-at "hi\n"))))))
+
 (ert-deftest eshell-test/command-running-p ()
   "Modeline should show no command running"
   (with-temp-eshell
@@ -178,7 +144,7 @@ insert the queued one at the next prompt, and finally run 
it."
    (eshell-insert-command "sleep 1; echo slept")
    (eshell-insert-command "echo alpha" #'eshell-queue-input)
    (let ((start (marker-position (eshell-beginning-of-output))))
-     (eshell-wait-for (lambda () (not eshell-current-command)))
+     (eshell-wait-for (lambda () (not eshell-foreground-command)))
      (should (string-match "^slept\n.*echo alpha\nalpha\n$"
                            (buffer-substring-no-properties
                             start (eshell-end-of-output)))))))
diff --git a/test/lisp/filenotify-tests.el b/test/lisp/filenotify-tests.el
index 0873910ddf9..57099add08b 100644
--- a/test/lisp/filenotify-tests.el
+++ b/test/lisp/filenotify-tests.el
@@ -1583,7 +1583,7 @@ the file watch."
   :tags '(:expensive-test)
   (skip-unless (file-notify--test-local-enabled))
   ;; This test does not work for kqueue (yet).
-  (skip-unless (not (string-equal (file-notify--test-library) "kqueue")))
+  (skip-when (string-equal (file-notify--test-library) "kqueue"))
 
   (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
         file-notify--test-tmpfile1 (file-notify--test-make-temp-name))
diff --git a/test/lisp/help-tests.el b/test/lisp/help-tests.el
index 6c440f9e238..b0b487ab169 100644
--- a/test/lisp/help-tests.el
+++ b/test/lisp/help-tests.el
@@ -378,7 +378,7 @@ Key             Binding
                                      (foo menu-item "Foo" foo
                                           :enable mark-active
                                           :help "Help text"))))))
-      (describe-map-tree map nil nil nil nil t nil nil nil)
+      (help--describe-map-tree map nil nil nil nil t nil nil nil)
       (should (string-match "
 Key             Binding
 -+
@@ -393,7 +393,7 @@ C-a         foo\n"
                                      (foo menu-item "Foo" foo
                                           :enable mark-active
                                           :help "Help text"))))))
-      (describe-map-tree map nil nil nil nil nil nil nil nil)
+      (help--describe-map-tree map nil nil nil nil nil nil nil nil)
       (should (string-match "
 Key             Binding
 -+
@@ -408,7 +408,7 @@ C-a         foo
           (map '(keymap . ((1 . foo)
                            (2 . bar))))
           (shadow-maps '((keymap . ((1 . baz))))))
-      (describe-map-tree map t shadow-maps nil nil t nil nil t)
+      (help--describe-map-tree map t shadow-maps nil nil t nil nil t)
       (should (string-match "
 Key             Binding
 -+
@@ -423,7 +423,7 @@ C-b         bar\n"
           (map '(keymap . ((1 . foo)
                            (2 . bar))))
           (shadow-maps '((keymap . ((1 . baz))))))
-      (describe-map-tree map t shadow-maps nil nil t nil nil nil)
+      (help--describe-map-tree map t shadow-maps nil nil t nil nil nil)
       (should (string-match "
 Key             Binding
 -+
@@ -435,7 +435,7 @@ C-b         bar\n"
     (let ((standard-output (current-buffer))
           (map '(keymap . ((1 . foo)
                            (2 . undefined)))))
-      (describe-map-tree map t nil nil nil nil nil nil nil)
+      (help--describe-map-tree map t nil nil nil nil nil nil nil)
       (should (string-match "
 Key             Binding
 -+
@@ -447,7 +447,7 @@ C-a         foo\n"
     (let ((standard-output (current-buffer))
           (map '(keymap . ((1 . foo)
                            (2 . undefined)))))
-      (describe-map-tree map nil nil nil nil nil nil nil nil)
+      (help--describe-map-tree map nil nil nil nil nil nil nil nil)
       (should (string-match "
 Key             Binding
 -+
diff --git a/test/lisp/ibuffer-tests.el b/test/lisp/ibuffer-tests.el
index 83bfa1f68af..ec0ba85da39 100644
--- a/test/lisp/ibuffer-tests.el
+++ b/test/lisp/ibuffer-tests.el
@@ -34,7 +34,7 @@
 
 (ert-deftest ibuffer-0autoload ()       ; sort first
   "Tests to see whether ibuffer has been autoloaded"
-  (skip-unless (not (featurep 'ibuf-ext)))
+  (skip-when (featurep 'ibuf-ext))
   (should
    (fboundp 'ibuffer-mark-unsaved-buffers))
   (should
diff --git a/test/lisp/international/mule-tests.el 
b/test/lisp/international/mule-tests.el
index 3e0c5bf9f4b..4dc099a18af 100644
--- a/test/lisp/international/mule-tests.el
+++ b/test/lisp/international/mule-tests.el
@@ -49,6 +49,30 @@
                        (kbd "C-x RET c u t f - 8 RET C-u C-u c a b RET")
                      (read-string "prompt:"))))))
 
+;;Bug#65997, ensure that old-names haven't overriden new names.
+(ert-deftest mule-cmds-tests--ucs-names-old-name-override ()
+  (let (code-points)
+    (dotimes (u (1+ (max-char 'ucs)))
+      (when-let* ((name (get-char-code-property u 'name))
+                  (c (char-from-name name)))
+        (when (and (not (<= #xD800 u #xDFFF))
+                   (not (= c u)))
+          (push (format "%X" u) code-points))))
+    (setq code-points (nreverse code-points))
+    (should (null code-points))))
+
+;; Bug#65997, ensure that all codepoints with names are in '(ucs-names)'.
+(ert-deftest mule-cmds-tests--ucs-names-missing-names ()
+  (let (code-points)
+    (dotimes (u (1+ (max-char 'ucs)))
+      (when-let ((name (get-char-code-property u 'name)))
+        (when (and (not (<= #xD800 u #xDFFF))
+                   (not (<= #x18800 u #x18AFF))
+                   (not (char-from-name name)))
+          (push (format "%X" u) code-points))))
+    (setq code-points (nreverse code-points))
+    (should (null code-points))))
+
 (ert-deftest mule-utf-7 ()
   ;; utf-7 and utf-7-imap are not ASCII-compatible.
   (should-not (coding-system-get 'utf-7 :ascii-compatible-p))
diff --git a/test/lisp/international/ucs-normalize-tests.el 
b/test/lisp/international/ucs-normalize-tests.el
index 97b8fa66cd3..002aa3b8c21 100644
--- a/test/lisp/international/ucs-normalize-tests.el
+++ b/test/lisp/international/ucs-normalize-tests.el
@@ -184,43 +184,7 @@ Must be called with `ucs-normalize-tests--norm-buf' as 
current buffer."
   (should-not (ucs-normalize-tests--rule1-failing-for-partX 0)))
 
 (defconst ucs-normalize-tests--failing-lines-part1
-  (list 2412 2413 2414 15133 15134 15135 15136 15137
-        15138 15139 15140 15141 15142 15143 15144 15145
-        15146 15147 15148 15149 15150 15151 15152 15153
-        15154 15155 15156 15157 15158 15159 15160 15161
-        15162 15163 15164 15165 15166 15167 15168 15169
-        15170 15171 15172 15173 15174 15175 15176 15177
-        15178 15179 15180 15181 15182 15183 15184 15185
-        15186 15187 15188 15192 15193 15194 15195 15196
-        15197 15198 15199 15200 15201 16211 16212 16213
-        16214 16215 16216 16217 16218 16219 16220 16221
-        16222 16223 16224 16225 16226 16227 16228 16229
-        16230 16231 16232 16233 16234 16235 16236 16237
-        16238 16239 16240 16241 16242 16243 16244 16245
-        16246 16247 16248 16249 16250 16251 16252 16253
-        16254 16255 16256 16257 16258 16259 16260 16261
-        16262 16263 16264 16265 16266 16267 16268 16269
-        16270 16271 16272 16273 16274 16275 16276 16277
-        16278 16279 16280 16281 16282 16283 16284 16285
-        16286 16287 16288 16289 16290 16291 16292 16293
-        16294 16295 16296 16297 16298 16299 16300 16301
-        16302 16303 16304 16305 16306 16307 16308 16309
-        16310 16311 16312 16313 16314 16315 16316 16317
-        16318 16319 16320 16321 16322 16323 16324 16325
-        16326 16327 16328 16329 16330 16331 16332 16333
-        16334 16335 16336 16337 16338 16339 16340 16341
-        16342 16343 16344 16345 16346 16347 16348 16349
-        16350 16351 16352 16353 16354 16355 16356 16357
-        16358 16359 16360 16361 16362 16363 16364 16365
-        16366 16367 16368 16369 16370 16371 16372 16373
-        16374 16375 16376 16377 16378 16379 16380 16381
-        16382 16383 16384 16385 16386 16387 16388 16389
-        16390 16391 16392 16393 16394 16395 16396 16397
-        16398 16399 16400 16401 16402 16403 16404 16405
-        16406 16407 16408 16409 16410 16411 16412 16413
-        16550 16551 16552 16553 16554 16555 16556 16557
-        16488 16489 16490 16491 16492 16493 16494 16495
-        16496 16497 16558 16559))
+  (list ))
 
 ;; Keep a record of failures, for consulting afterwards (the ert
 ;; backtrace only shows a truncated version of these lists).
@@ -255,8 +219,8 @@ Must be called with `ucs-normalize-tests--norm-buf' as 
current buffer."
 
 (ert-deftest ucs-normalize-part1 ()
   :tags '(:expensive-test)
-  (skip-unless (not (or (getenv "EMACS_HYDRA_CI")
-                        (getenv "EMACS_EMBA_CI")))) ; SLOW ~ 1800s
+  (skip-when (or (getenv "EMACS_HYDRA_CI")
+                 (getenv "EMACS_EMBA_CI"))) ; SLOW ~ 1800s
   ;; This takes a long time, so make sure we're compiled.
   (dolist (fun '(ucs-normalize-tests--part1-rule2
                  ucs-normalize-tests--rule1-failing-for-partX
@@ -283,88 +247,20 @@ Must be called with `ucs-normalize-tests--norm-buf' as 
current buffer."
     ucs-normalize-tests--failing-lines-part1)))
 
 (defconst ucs-normalize-tests--failing-lines-part2
-  (list 17087 17088 17089 17090 17091 17092 17093 17094
-        17098 17099 17100 17101 17102 17103 17104 17105
-        17106 17107 17108 17113 17114 17115 17116 17117
-        17118 17119 17120 17125 17126 17127 17128 17129
-        17130 17131 17132 17133 17134 17135 17136 17137
-        17138 17139 17140 17141 17142 17143 17144 17145
-        17146 17157 17158 17159 17160 17161 17162 17163
-        17164 17185 17186 17187 17188 17189 17190 17197
-        17198 17199 17200 17207 17208 17209 17210 17211
-        17212 17213 17214 17219 17220 17221 17222 17275
-        17276 17285 17286 17295 17296 17309 17310 17311
-        17312 17313 17314 17315 17316 17317 17318 17319
-        17320 17325 17326 17373 17374 17419 17420 17421
-        17422 17433 17434 17439 17440 17465 17466 17473
-        17474 17479 17480 17485 17486 17491 17492 17497
-        17498 17499 17500 17501 17502 17505 17506 17507
-        17508 17511 17512 17519 17520 17523 17524 17527
-        17528 17531 17532 17551 17552 17555 17556 17599
-        17600 17601 17602 17603 17604 17605 17607 17608
-        17609 17610 17611 17612 17613 17615 17617 17619
-        17621 17623 17625 17627 17629 17631 17632 17633
-        17634 17635 17636 17637 17638 17639 17640 17669
-        17670 17675 17676 17681 17682 17689 17690 17691
-        17692 17693 17694 17707 17708 17713 17714 17715
-        17716 17727 17728 17733 17734 17739 17740 17745
-        17746 17749 17750 17753 17754 17759 17760 17767
-        17768 17789 17790 17801 17802 17807 17808 17809
-        17810 17811 17812 17813 17814 17815 17816 17821
-        17822 17829 17830 17843 17844 17845 17846 17851
-        17852 17861 17875 17876 17879 17880 17899 17900
-        17097 17907 17908 17911 17912 17913 17914 17915
-        17916 17917 17918 17919 17920 17921 17922 17927
-        17928 17929 17930 17931 17932 17933 17935 17937
-        17938 17939 17940 17941 17943 17945 17947 17949
-        17951 17952 17953 17955 17957 17959 17961 17962
-        17967 17968 17987 17988 17993 17994 18003 18004
-        18005 18006 18007 18008 18009 18010 18011 18012
-        18017 18018 18019 18020 18021 18022 18023 18024
-        18041 18042 18049 18050 18053 18054 18055 18056
-        18069 18070 18079 18080 18163 18164 18165 18166
-        18171 18172 18175 18176 18211 18212 18219 18220
-        18221 18222 18223 18224 18225 18226 18301 18302
-        18389 18390 18391 18392 18393 18394 18397 18398
-        18407 18408 18439 18440 18441 18442 18443 18444
-        18445 18446 18447 18448 18449 18450 18451 18452
-        18457 18458 18459 18460 18471 18472 18479 18480
-        18485 18486 18499 18500 18501 18502 18509 18510
-        18513 18514 18515 18516 18517 18518 18519 18520
-        18521 18523 18524 18525 18527 18528 18531 18537
-        18538 18539 18541 18543 18545 18547 18549 18550
-        18551 18553 18554 18555 18557 18558 18559 18560
-        18561 18562 18563 18564 18565 18566 18567 18569
-        18571 18573 18575 18577 18579 18581 18583 18585
-        18587 18589 18591 18593 18595 18596 18597 18599
-        18601 18602 18603 18605 18606 18607 18609 18611
-        18612 18613 18615 18617 18618 18619 18621 18622
-        18623 18624 18625 18626 18627 18628 18629 18631
-        18632 18633 18634 18635 18636 18637 18639 18641
-        18643 18645 18647 18649 18651 18653 18655 18657
-        18659 18661 18663 18664 18665 18667 18668 18669
-        18670 18671 18673 18674 18675 18676 18677 18679
-        18680 18681 18683 18685 18686 18687 18688 18689
-        18690 18691 18692 18693 18694 18695 18696 18697
-        18698 18699 18700 18701 18702 18703 18704 18705
-        18706 18707 18708 18709 18710 18711 18712 18713
-        18714 18715 18717 18719 18721 18722 18723 18724
-        18725 18727 18729 18731 18733 18735 18737 18739
-        18740 18741 18742 18743 18745 18747 18749 18751
-        18753 18755 18757 18759 18761 18763 18765 18767
-        18769 18771 18773 18775 18777 18779 18781 18783
-        18785 18787 18789 18791 18793 18795 18797 18799
-        18801 18803 18805 18807 18809 18811 18813 18815
-        18817 18819 18821 18823 18825 18827 18829 18831
-        18833 18835 18837 18839 18840 18841 18842 18843
-        18844 18845 18846 18847 18848 18849 18850 18851
-        18852 18853 18855 18857 18859 18861 18863 18865
-        18866 18867 18869 18871 18873 18875 18877 18879
-        18881 18883 18885 18887 18888 18889 18891 18893
-        18895 18897 18899 18901 18903 18905 18907 18909
-        18911 18913 18914 18915 18916 18917 18918 18919
-        18920 18921 18923 18925 18927 18929 18931 18933
-        18935 18937 18939 18941 18943 18945 18947 18948))
+  (list 17789 17790 17801 17802 17807 17808 17811 17812
+        17815 17816 17821 17822 17829 17830 17907 17908
+        18023 18024 18049 18050 18055 18056 18459 18460
+        18605 18606 18617 18618 18621 18622 18625 18626
+        18627 18628 18631 18632 18633 18634 18663 18664
+        18669 18670 18673 18674 18679 18680 18685 18686
+        18691 18692 18695 18697 18699 18701 18703 18704
+        18705 18707 18709 18711 18713 18715 18717 18719
+        18721 18723 18725 18727 18729 18731 18733 18735
+        18737 18739 18740 18741 18742 18743 18889 18891
+        18893 18895 18897 18899 18901 18903 18905 18907
+        18909 18911 18913 18914 18915 18916 18917 18919
+        18921 18923 18925 18927 18929 18931 18933 18935
+        18937 18939 18941 18943 18945 18947 18948))
 
 (ert-deftest ucs-normalize-part2 ()
   :tags '(:expensive-test)
diff --git a/test/lisp/minibuffer-tests.el b/test/lisp/minibuffer-tests.el
index ff58d35eb3e..4f92d7f841c 100644
--- a/test/lisp/minibuffer-tests.el
+++ b/test/lisp/minibuffer-tests.el
@@ -298,6 +298,19 @@
                   "jab" '("dabjabstabby" "many") nil 3)))
            6)))
 
+(ert-deftest completion-substring-test-5 ()
+  ;; merge-completions needs to work correctly when
+  (should (equal
+           (completion-pcm--merge-completions '("ab" "sab") '(prefix "b"))
+           '("b" "a" prefix)))
+  (should (equal
+           (completion-pcm--merge-completions '("ab" "ab") '(prefix "b"))
+           '("b" "a")))
+  ;; substring completion should successfully complete the entire string
+  (should (equal
+           (completion-substring-try-completion "b" '("ab" "ab") nil 0)
+           '("ab" . 2))))
+
 (ert-deftest completion-flex-test-1 ()
   ;; Fuzzy match
   (should (equal
diff --git a/test/lisp/net/mailcap-tests.el b/test/lisp/net/mailcap-tests.el
index e47ead98f42..175c3e88da9 100644
--- a/test/lisp/net/mailcap-tests.el
+++ b/test/lisp/net/mailcap-tests.el
@@ -537,5 +537,29 @@ help to verify the correct addition and merging of an 
entry."
                           ("minor" . ((viewer . "viewer")
                                       (edit . "edit")))))))))
 
+
+
+(ert-deftest mailcap-viewer-passes-test-w/o-test-returns-t ()
+  "A VIEWER-INFO without a test should return t with a valid viewer 
(Bug#65224)."
+
+  (should (equal t
+                 (let ((mailcap-viewer-test-cache)
+                       (viewer-info
+                        (list (cons 'viewer "viewer-w/o-test"))))
+                   (mailcap-viewer-passes-test viewer-info nil))))
+
+  (should (equal '(t t nil t)
+                 (let ((mailcap-viewer-test-cache)
+                       (viewer-infos
+                        (list
+                         (list (cons 'viewer "viewer-w/o-test"))
+                         (list (cons 'viewer "viewer-w/o-test"))
+                         (list (cons 'viewer "viewer-w/nil-test")
+                               (cons 'test    nil))
+                         (list (cons 'viewer "viewer-w/o-test"))
+                         )))
+                   (mapcar (lambda (vi)
+                             (mailcap-viewer-passes-test vi nil))
+                           viewer-infos)))))
 
 ;;; mailcap-tests.el ends here
diff --git a/test/lisp/net/network-stream-tests.el 
b/test/lisp/net/network-stream-tests.el
index 0fd9549c305..8b1ae398930 100644
--- a/test/lisp/net/network-stream-tests.el
+++ b/test/lisp/net/network-stream-tests.el
@@ -236,7 +236,7 @@
     (while (and (eq (process-status proc) 'connect)
                 (< (setq times (1+ times)) 10))
       (sit-for 0.1))
-    (skip-unless (not (eq (process-status proc) 'connect)))
+    (skip-when (eq (process-status proc) 'connect))
     (with-current-buffer (process-buffer proc)
       (process-send-string proc "echo foo")
       (sleep-for 0.1)
@@ -323,7 +323,7 @@
           (while (and (eq (process-status proc) 'connect)
                       (< (setq times (1+ times)) 10))
             (sit-for 0.1))
-          (skip-unless (not (eq (process-status proc) 'connect))))
+          (skip-when (eq (process-status proc) 'connect)))
       (if (process-live-p server) (delete-process server)))
     (setq status (gnutls-peer-status proc))
     (should (consp status))
@@ -336,7 +336,7 @@
 (ert-deftest connect-to-tls-ipv6-nowait ()
   (skip-unless (executable-find "gnutls-serv"))
   (skip-unless (gnutls-available-p))
-  (skip-unless (not (eq system-type 'windows-nt)))
+  (skip-when (eq system-type 'windows-nt))
   (skip-unless (featurep 'make-network-process '(:family ipv6)))
   (let ((server (make-tls-server 44333))
         (times 0)
@@ -368,7 +368,7 @@
           (while (and (eq (process-status proc) 'connect)
                       (< (setq times (1+ times)) 10))
             (sit-for 0.1))
-          (skip-unless (not (eq (process-status proc) 'connect))))
+          (skip-when (eq (process-status proc) 'connect)))
       (if (process-live-p server) (delete-process server)))
     (setq status (gnutls-peer-status proc))
     (should (consp status))
@@ -403,7 +403,7 @@
                       (< (setq times (1+ times)) 10))
             (sit-for 0.1))
           (should proc)
-          (skip-unless (not (eq (process-status proc) 'connect))))
+          (skip-when (eq (process-status proc) 'connect)))
       (if (process-live-p server) (delete-process server)))
     (setq status (gnutls-peer-status proc))
     (should (consp status))
@@ -446,7 +446,7 @@
           (while (and (eq (process-status proc) 'connect)
                       (< (setq times (1+ times)) 10))
             (sit-for 0.1))
-          (skip-unless (not (eq (process-status proc) 'connect))))
+          (skip-when (eq (process-status proc) 'connect)))
       (if (process-live-p server) (delete-process server)))
     (setq status (gnutls-peer-status proc))
     (should (consp status))
@@ -484,7 +484,7 @@
                       (< (setq times (1+ times)) 10))
             (sit-for 0.1))
           (should proc)
-          (skip-unless (not (eq (process-status proc) 'connect))))
+          (skip-when (eq (process-status proc) 'connect)))
       (if (process-live-p server) (delete-process server)))
     (setq status (gnutls-peer-status proc))
     (should (consp status))
@@ -523,7 +523,7 @@
                       (< (setq times (1+ times)) 10))
             (sit-for 0.1))
           (should proc)
-          (skip-unless (not (eq (process-status proc) 'connect))))
+          (skip-when (eq (process-status proc) 'connect)))
       (if (process-live-p server) (delete-process server)))
     (setq status (gnutls-peer-status proc))
     (should (consp status))
@@ -673,7 +673,7 @@
           (while (and (eq (process-status proc) 'connect)
                       (< (setq times (1+ times)) 10))
             (sit-for 0.1))
-          (skip-unless (not (eq (process-status proc) 'connect))))
+          (skip-when (eq (process-status proc) 'connect)))
       (if (process-live-p server) (delete-process server)))
     (setq status (gnutls-peer-status proc))
     (should (consp status))
@@ -712,7 +712,7 @@
           (while (and (eq (process-status proc) 'connect)
                       (< (setq times (1+ times)) 10))
             (sit-for 0.1))
-          (skip-unless (not (eq (process-status proc) 'connect))))
+          (skip-when (eq (process-status proc) 'connect)))
       (if (process-live-p server) (delete-process server)))
     (setq status (gnutls-peer-status proc))
     (should (consp status))
diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el
index 50687dfe993..0136e0abd5b 100644
--- a/test/lisp/net/tramp-tests.el
+++ b/test/lisp/net/tramp-tests.el
@@ -3441,12 +3441,12 @@ This tests also `file-directory-p' and 
`file-accessible-directory-p'."
                (rx-to-string
                 `(:
                   ;; There might be a summary line.
-                  (? "total" (+ nonl) (+ digit) (? blank)
+                  (? (* blank) "total" (+ nonl) (+ digit) (? blank)
                      (? (any "EGKMPTYZk")) (? "i") (? "B") "\n")
                   ;; We don't know in which order ".", ".." and "foo" appear.
                   (= ,(length (directory-files tmp-name1))
                      (+ nonl) blank
-                     (regexp ,(regexp-opt (directory-files tmp-name1)))
+                     (| . ,(directory-files tmp-name1))
                      (? " ->" (+ nonl)) "\n"))))))
 
            ;; Check error cases.
@@ -7020,7 +7020,7 @@ This is used in tests which we don't want to tag
     (ert--stats-selector ert--current-run-stats)
     (list (make-ert-test :name (ert-test-name (ert-running-test))
                          :body nil :tags '(:tramp-asynchronous-processes))))
-   ;; tramp-adb.el cannot apply multi-byte commands.
+   ;; tramp-adb.el cannot apply multibyte commands.
    (not (and (tramp--test-adb-p)
             (string-match-p (rx multibyte) default-directory)))))
 
@@ -7096,6 +7096,12 @@ This does not support external Emacs calls."
   (string-equal
    "mock" (file-remote-p ert-remote-temporary-file-directory 'method)))
 
+(defun tramp--test-netbsd-p ()
+  "Check, whether the remote host runs NetBSD."
+  ;; We must refill the cache.  `file-truename' does it.
+  (file-truename ert-remote-temporary-file-directory)
+  (ignore-errors (tramp-check-remote-uname tramp-test-vec "NetBSD")))
+
 (defun tramp--test-openbsd-p ()
   "Check, whether the remote host runs OpenBSD."
   ;; We must refill the cache.  `file-truename' does it.
@@ -7333,9 +7339,11 @@ This requires restrictions of file name syntax."
 
                ;; Check symlink in `directory-files-and-attributes'.
                ;; It does not work in the "smb" case, only relative
-               ;; symlinks to existing files are shown there.
+               ;; symlinks to existing files are shown there.  On
+               ;; NetBSD, there are problems with loooong file names,
+               ;; see Bug#65324.
                (tramp--test-ignore-make-symbolic-link-error
-                 (unless (tramp--test-smb-p)
+                 (unless (or (tramp--test-netbsd-p) (tramp--test-smb-p))
                    (make-symbolic-link file2 file3)
                    (should (file-symlink-p file3))
                    (should
@@ -7391,13 +7399,13 @@ This requires restrictions of file name syntax."
                  ;; of process output.  So we unset it temporarily.
                  (setenv "PS1")
                  (with-temp-buffer
-                   (should (zerop (process-file "printenv" nil t nil)))
-                   (goto-char (point-min))
-                   (should
-                    (search-forward-regexp
-                     (rx
-                      bol (literal envvar)
-                      "=" (literal (getenv envvar)) eol))))))))
+                   (when (zerop (process-file "printenv" nil t nil))
+                     (goto-char (point-min))
+                     (should
+                      (search-forward-regexp
+                       (rx
+                        bol (literal envvar)
+                        "=" (literal (getenv envvar)) eol)))))))))
 
        ;; Cleanup.
        (ignore-errors (kill-buffer buffer))
@@ -7868,7 +7876,7 @@ process sentinels.  They shall not disturb each other."
 
 (ert-deftest tramp-test47-read-password ()
   "Check Tramp password handling."
-  :tags '(:expensive-test :unstable)
+  :tags '(:expensive-test)
   (skip-unless (tramp--test-enabled))
   (skip-unless (tramp--test-mock-p))
   ;; Not all read commands understand argument "-s" or "-p".
@@ -7878,7 +7886,7 @@ process sentinels.  They shall not disturb each other."
       (shell-command-to-string "read -s -p Password: pass"))))
 
   (let ((pass "secret")
-       (mock-entry (copy-sequence (assoc "mock" tramp-methods)))
+       (mock-entry (copy-tree (assoc "mock" tramp-methods)))
        mocked-input tramp-methods)
     ;; We must mock `read-string', in order to avoid interactive
     ;; arguments.
@@ -7925,6 +7933,65 @@ process sentinels.  They shall not disturb each other."
          (let ((auth-sources `(,netrc-file)))
            (should (file-exists-p ert-remote-temporary-file-directory)))))))))
 
+(ert-deftest tramp-test47-read-otp-password ()
+  "Check Tramp one-time password handling."
+  :tags '(:expensive-test)
+  (skip-unless (tramp--test-mock-p))
+  ;; Not all read commands understand argument "-s" or "-p".
+  (skip-unless
+   (string-empty-p
+    (let ((shell-file-name "sh"))
+      (shell-command-to-string "read -s -p Password: pass"))))
+
+  (let ((pass "secret")
+       (mock-entry (copy-tree (assoc "mock" tramp-methods)))
+       mocked-input tramp-methods)
+    ;; We must mock `read-string', in order to avoid interactive
+    ;; arguments.
+    (cl-letf* (((symbol-function #'read-string)
+               (lambda (&rest _args) (pop mocked-input))))
+      (setcdr
+       (assq 'tramp-login-args mock-entry)
+       `((("-c")
+         (,(tramp-shell-quote-argument
+            (concat
+             "read -s -p 'Verification code: ' pass; echo; "
+             "(test \"pass$pass\" != \"pass" pass "\" && "
+             "echo \"Login incorrect\" || sh -i)"))))))
+      (setq tramp-methods `(,mock-entry))
+
+      ;; Reading password from stdin works.
+      (tramp-cleanup-connection tramp-test-vec 'keep-debug)
+      ;; We don't want to invalidate the password.
+      (setq mocked-input `(,(copy-sequence pass)))
+      (should (file-exists-p ert-remote-temporary-file-directory))
+
+      ;; Don't entering a password returns in error.
+      (tramp-cleanup-connection tramp-test-vec 'keep-debug)
+      (setq mocked-input nil)
+      (should-error (file-exists-p ert-remote-temporary-file-directory))
+
+      ;; A wrong password doesn't work either.
+      (tramp-cleanup-connection tramp-test-vec 'keep-debug)
+      (setq mocked-input `(,(concat pass pass)))
+      (should-error (file-exists-p ert-remote-temporary-file-directory))
+
+      ;; The password shouldn't be read from auth-source.
+      ;; Macro `ert-with-temp-file' was introduced in Emacs 29.1.
+      (with-no-warnings (when (symbol-plist 'ert-with-temp-file)
+       (tramp-cleanup-connection tramp-test-vec 'keep-debug)
+       (setq mocked-input nil)
+       (auth-source-forget-all-cached)
+       (ert-with-temp-file netrc-file
+         :prefix "tramp-test" :suffix ""
+         :text (format
+                "machine %s port mock password %s"
+                (file-remote-p ert-remote-temporary-file-directory 'host)
+                pass)
+         (let ((auth-sources `(,netrc-file)))
+           (should-error
+            (file-exists-p ert-remote-temporary-file-directory)))))))))
+
 ;; This test is inspired by Bug#29163.
 (ert-deftest tramp-test48-auto-load ()
   "Check that Tramp autoloads properly."
@@ -8150,6 +8217,7 @@ If INTERACTIVE is non-nil, the tests are run 
interactively."
 ;; * tramp-set-file-uid-gid
 
 ;; * Work on skipped tests.  Make a comment, when it is impossible.
+;; * Use `skip-when' starting with Emacs 30.1.
 ;; * Revisit expensive tests, once problems in `tramp-error' are solved.
 ;; * Fix `tramp-test06-directory-file-name' for "ftp".
 ;; * Check, why a process filter t doesn't work in
diff --git a/test/lisp/progmodes/cperl-mode-resources/cperl-bug-35925.pl 
b/test/lisp/progmodes/cperl-mode-resources/cperl-bug-35925.pl
new file mode 100644
index 00000000000..e3f96241ab7
--- /dev/null
+++ b/test/lisp/progmodes/cperl-mode-resources/cperl-bug-35925.pl
@@ -0,0 +1,36 @@
+# This resource file can be run with cperl--run-testcases from
+# cperl-tests.el and works with both perl-mode and cperl-mode.
+
+# -------- Bug#35925: input -------
+format FH =
+@### @.### @###
+42, 3.1415, 0
+.
+write FH;
+
+# -------- Bug#35925: expected output -------
+format FH =
+@### @.### @###
+42, 3.1415, 0
+.
+write FH;
+
+# -------- Bug#35925: end -------
+
+# -------- format not as top-level: input -------
+foo: {
+    format STDOUT =
+^<<<<
+$foo
+.
+write;
+}
+# -------- format not as top-level: expected output -------
+foo: {
+    format STDOUT =
+^<<<<
+$foo
+.
+    write;
+}
+# -------- format not as top-level: end -------
diff --git a/test/lisp/progmodes/cperl-mode-tests.el 
b/test/lisp/progmodes/cperl-mode-tests.el
index 8f334245c64..a29ee54b6b9 100644
--- a/test/lisp/progmodes/cperl-mode-tests.el
+++ b/test/lisp/progmodes/cperl-mode-tests.el
@@ -25,6 +25,10 @@
 ;;; Commentary:
 
 ;; This is a collection of tests for CPerl-mode.
+;; The maintainer would like to use this test file with cperl-mode.el
+;; also in older Emacs versions (currently: Emacs 26.1): Please don't
+;; use Emacs features which are not available in that version (unless
+;; they're already used in existing tests).
 
 ;;; Code:
 
@@ -1139,6 +1143,20 @@ Perl is not Lisp: An open paren in column 0 does not 
start a function."
      (cperl-indent-command)
      (forward-line 1))))
 
+(ert-deftest cperl-test-bug-35925 ()
+  "Check that indentation is correct after a terminating format declaration."
+  (cperl-set-style "PBP") ; Make cperl-mode use the same settings as perl-mode.
+  (cperl--run-test-cases
+   (ert-resource-file "cperl-bug-35925.pl")
+   (let ((tab-function
+          (if (equal cperl-test-mode 'perl-mode)
+              #'indent-for-tab-command
+            #'cperl-indent-command)))
+     (goto-char (point-max))
+     (forward-line -2)
+     (funcall tab-function)))
+  (cperl-set-style-back))
+
 (ert-deftest cperl-test-bug-37127 ()
   "Verify that closing a paren in a regex goes without a message.
 Also check that the message is issued if the regex terminator is
diff --git a/test/lisp/progmodes/eglot-tests.el 
b/test/lisp/progmodes/eglot-tests.el
index 725b877fd3c..575a6ac8ef1 100644
--- a/test/lisp/progmodes/eglot-tests.el
+++ b/test/lisp/progmodes/eglot-tests.el
@@ -415,7 +415,7 @@ directory hierarchy."
             (and (string= method "workspace/didChangeWatchedFiles")
                  (cl-destructuring-bind (&key uri type)
                      (elt (plist-get params :changes) 0)
-                   (and (string= (eglot--path-to-uri "Cargo.toml") uri)
+                   (and (string= (eglot-path-to-uri "Cargo.toml") uri)
                         (= type 3))))))))))
 
 (ert-deftest eglot-test-basic-diagnostics ()
@@ -927,7 +927,7 @@ int main() {
         (should-error (apply #'eglot--connect (eglot--guess-contact)))))))
 
 (ert-deftest eglot-test-capabilities ()
-  "Unit test for `eglot--server-capable'."
+  "Unit test for `eglot-server-capable'."
   (cl-letf (((symbol-function 'eglot--capabilities)
              (lambda (_dummy)
                ;; test data lifted from Golangserver example at
@@ -942,11 +942,11 @@ int main() {
                      :xdefinitionProvider t :xworkspaceSymbolByProperties t)))
             ((symbol-function 'eglot--current-server-or-lose)
              (lambda () nil)))
-    (should (eql 2 (eglot--server-capable :textDocumentSync)))
-    (should (eglot--server-capable :completionProvider :triggerCharacters))
-    (should (equal '(:triggerCharacters ["."]) (eglot--server-capable 
:completionProvider)))
-    (should-not (eglot--server-capable :foobarbaz))
-    (should-not (eglot--server-capable :textDocumentSync :foobarbaz))))
+    (should (eql 2 (eglot-server-capable :textDocumentSync)))
+    (should (eglot-server-capable :completionProvider :triggerCharacters))
+    (should (equal '(:triggerCharacters ["."]) (eglot-server-capable 
:completionProvider)))
+    (should-not (eglot-server-capable :foobarbaz))
+    (should-not (eglot-server-capable :textDocumentSync :foobarbaz))))
 
 (defmacro eglot--without-interface-warnings (&rest body)
   (let ((eglot-strict-mode nil))
@@ -1276,9 +1276,9 @@ GUESSED-MAJOR-MODES-SYM are bound to the useful return 
values of
 (ert-deftest eglot-test-path-to-uri-windows ()
   (skip-unless (eq system-type 'windows-nt))
   (should (string-prefix-p "file:///"
-                           (eglot--path-to-uri "c:/Users/Foo/bar.lisp")))
+                           (eglot-path-to-uri "c:/Users/Foo/bar.lisp")))
   (should (string-suffix-p "c%3A/Users/Foo/bar.lisp"
-                           (eglot--path-to-uri "c:/Users/Foo/bar.lisp"))))
+                           (eglot-path-to-uri "c:/Users/Foo/bar.lisp"))))
 
 (ert-deftest eglot-test-same-server-multi-mode ()
   "Check single LSP instance manages multiple modes in same project."
diff --git a/test/lisp/progmodes/elisp-mode-tests.el 
b/test/lisp/progmodes/elisp-mode-tests.el
index 5b6ef88dceb..4fa869c773f 100644
--- a/test/lisp/progmodes/elisp-mode-tests.el
+++ b/test/lisp/progmodes/elisp-mode-tests.el
@@ -128,7 +128,7 @@
 
 (ert-deftest eval-last-sexp-print-format-sym-echo ()
   ;; We can only check the echo area when running interactive.
-  (skip-unless (not noninteractive))
+  (skip-when noninteractive)
   (with-temp-buffer
     (let ((current-prefix-arg nil))
       (erase-buffer) (insert "t") (message nil)
@@ -147,7 +147,7 @@
       (should (equal (buffer-string) "?A65 (#o101, #x41, ?A)")))))
 
 (ert-deftest eval-last-sexp-print-format-small-int-echo ()
-  (skip-unless (not noninteractive))
+  (skip-when noninteractive)
   (with-temp-buffer
     (let ((current-prefix-arg nil))
       (erase-buffer) (insert "?A") (message nil)
@@ -171,7 +171,7 @@
         (should (equal (buffer-string) "?B66 (#o102, #x42, ?B)"))))))
 
 (ert-deftest eval-last-sexp-print-format-large-int-echo ()
-  (skip-unless (not noninteractive))
+  (skip-when noninteractive)
   (with-temp-buffer
     (let ((eval-expression-print-maximum-character ?A))
       (let ((current-prefix-arg nil))
@@ -186,7 +186,7 @@
 ;;; eval-defun
 
 (ert-deftest eval-defun-prints-edebug-when-instrumented ()
-  (skip-unless (not noninteractive))
+  (skip-when noninteractive)
   (with-temp-buffer
     (let ((current-prefix-arg '(4)))
       (erase-buffer) (insert "(defun foo ())") (message nil)
diff --git a/test/lisp/progmodes/elixir-ts-mode-resources/indent.erts 
b/test/lisp/progmodes/elixir-ts-mode-resources/indent.erts
index 1f855d3c977..fe09a37a32b 100644
--- a/test/lisp/progmodes/elixir-ts-mode-resources/indent.erts
+++ b/test/lisp/progmodes/elixir-ts-mode-resources/indent.erts
@@ -330,6 +330,22 @@ Name: Long tuple
  "October", "November", "December"}
 =-=-=
 
+Name: Doc
+
+=-=
+defmodule Foo do
+"""
+    bar
+    """
+end
+=-=
+defmodule Foo do
+  """
+    bar
+  """
+end
+=-=-=
+
 Name: Embedded HEEx
 
 =-=
diff --git a/test/lisp/progmodes/flymake-tests.el 
b/test/lisp/progmodes/flymake-tests.el
index f6608dffca2..c3ef4827ef2 100644
--- a/test/lisp/progmodes/flymake-tests.el
+++ b/test/lisp/progmodes/flymake-tests.el
@@ -213,6 +213,7 @@ SEVERITY-PREDICATE is used to setup
 
 (ert-deftest dummy-backends ()
   "Test many different kinds of backends."
+  (let ((debug-on-error nil))
   (with-temp-buffer
     (cl-letf
         (((symbol-function 'error-backend)
@@ -291,7 +292,7 @@ SEVERITY-PREDICATE is used to setup
         (should (eq 'flymake-warning (face-at-point))) ; dolor
         (flymake-goto-next-error)
         (should (eq 'flymake-error (face-at-point))) ; prognata
-        (should-error (flymake-goto-next-error nil nil t))))))
+        (should-error (flymake-goto-next-error nil nil t)))))))
 
 (ert-deftest recurrent-backend ()
   "Test a backend that calls REPORT-FN multiple times."
diff --git a/test/lisp/progmodes/lua-ts-mode-resources/indent.erts 
b/test/lisp/progmodes/lua-ts-mode-resources/indent.erts
new file mode 100644
index 00000000000..040225c8580
--- /dev/null
+++ b/test/lisp/progmodes/lua-ts-mode-resources/indent.erts
@@ -0,0 +1,152 @@
+Code:
+  (lambda ()
+    (setq indent-tabs-mode nil)
+    (setq lua-ts-indent-offset 2)
+    (lua-ts-mode)
+    (indent-region (point-min) (point-max)))
+
+Name: Basic Indent
+
+=-=
+      print(
+0,
+      1
+)
+
+local function f(o)
+   if o.x > o.y then
+ return o.x
+elseif o.y > o.z then
+           return o.y
+      else
+return o.z
+            end
+end
+
+f({
+ x = 1,
+  y = 2,
+   z = 3,
+})
+
+;(function()
+return false
+)()
+=-=
+print(
+  0,
+  1
+)
+
+local function f(o)
+  if o.x > o.y then
+    return o.x
+  elseif o.y > o.z then
+    return o.y
+  else
+    return o.z
+  end
+end
+
+f({
+  x = 1,
+  y = 2,
+  z = 3,
+})
+
+;(function()
+  return false
+)()
+=-=-=
+
+Name: Argument Indent
+
+=-=
+function h(
+string,
+number,
+options)
+print(string, number, options)
+end
+
+local p = h(
+"sring",
+        1000,
+      {
+cost = 2,
+length = 8,
+       parallelism = 4,
+})
+=-=
+function h(
+  string,
+  number,
+  options)
+  print(string, number, options)
+end
+
+local p = h(
+  "sring",
+  1000,
+  {
+    cost = 2,
+    length = 8,
+    parallelism = 4,
+  })
+=-=-=
+
+Name: Continuation Indent
+
+=-=
+function f()
+  local str = [[
+  multi-line
+       string
+    ]]
+--[[
+multi-line
+comment
+    ]]
+return true
+end
+=-=
+function f()
+  local str = [[
+  multi-line
+       string
+    ]]
+  --[[
+multi-line
+comment
+    ]]
+  return true
+end
+=-=-=
+
+Name: Loop Indent
+
+=-=
+for k, v in pairs({}) do
+        print(k, v)
+end
+
+while n < 10 do
+n = n + 1
+end
+
+repeat
+z = z * 2
+ until z > 12
+=-=
+for k, v in pairs({}) do
+  print(k, v)
+end
+
+while n < 10 do
+  n = n + 1
+end
+
+repeat
+  z = z * 2
+until z > 12
+=-=-=
diff --git a/test/lisp/progmodes/lua-ts-mode-resources/movement.erts 
b/test/lisp/progmodes/lua-ts-mode-resources/movement.erts
new file mode 100644
index 00000000000..770aa23b18d
--- /dev/null
+++ b/test/lisp/progmodes/lua-ts-mode-resources/movement.erts
@@ -0,0 +1,553 @@
+Code:
+  (lambda ()
+    (lua-ts-mode)
+    (beginning-of-defun 1))
+
+Point-Char: |
+
+Name: beginning-of-defun moves to start of function declaration
+
+=-=
+local function Test()
+  if true then
+    print(1)
+  else
+    print(0)
+  end|
+end
+=-=
+|local function Test()
+  if true then
+    print(1)
+  else
+    print(0)
+  end
+end
+=-=-=
+
+Name: beginning-of-defun moves to start of function definition
+
+=-=
+local t = {
+  f = function()
+    return true
+  end,
+}|
+=-=
+local t = {
+|  f = function()
+    return true
+  end,
+}
+=-=-=
+
+Code:
+  (lambda ()
+    (lua-ts-mode)
+    (end-of-defun 1))
+
+Point-Char: |
+
+Name: end-of-defun moves to end of function declaration
+
+=-=
+local function Test()
+  if true then
+    pr|int(1)
+  else
+    print(0)
+  end
+end
+
+local t = Test()
+=-=
+local function Test()
+  if true then
+    print(1)
+  else
+    print(0)
+  end
+end
+|
+local t = Test()
+=-=-=
+
+Name: end-of-defun moves to end of function definition
+
+=-=
+local t = {
+  f = function()
+    re|turn true
+  end,
+}
+=-=
+local t = {
+  f = function()
+    return true
+  end|,
+}
+=-=-=
+
+Code:
+  (lambda ()
+    (lua-ts-mode)
+    (forward-sentence 1))
+
+Point-Char: |
+
+Name: forward-sentence moves over if statements
+
+=-=
+function f()
+  |if true then
+    print(1)
+  elseif false then
+    print(0)
+  else
+    print(2)
+  end
+end
+=-=
+function f()
+  if true then
+    print(1)
+  elseif false then
+    print(0)
+  else
+    print(2)
+  end|
+end
+=-=-=
+
+Name: forward-sentence moves over variable declaration
+
+=-=
+|local n = 1
+
+print(n)
+=-=
+local n = 1|
+
+print(n)
+=-=-=
+
+Name: forward-sentence moves over for statements
+
+=-=
+|for k, v in pairs({}) do
+  print(k, v)
+end
+
+print(1)
+=-=
+for k, v in pairs({}) do
+  print(k, v)
+end|
+
+print(1)
+=-=-=
+
+Name: forward-sentence moves over for statements
+
+=-=
+|do
+  local x = 1
+  local y = 2
+
+  print(x, y)
+end
+
+print(1)
+=-=
+do
+  local x = 1
+  local y = 2
+
+  print(x, y)
+end|
+
+print(1)
+=-=-=
+
+Name: forward-sentence moves over while statements
+
+=-=
+local i = 0
+|while i < 9 do
+      print(i)
+      i = i + 1
+end
+
+print(1)
+=-=
+local i = 0
+while i < 9 do
+      print(i)
+      i = i + 1
+end|
+
+print(1)
+=-=-=
+
+Name: forward-sentence moves over repeat statements
+
+=-=
+local i = 0
+|repeat
+  print(i)
+  i = i + 1
+until i > 9
+
+print(1)
+=-=
+local i = 0
+repeat
+  print(i)
+  i = i + 1
+until i > 9|
+
+print(1)
+=-=-=
+
+Name: forward-sentence moves over function calls
+
+=-=
+|print(1)
+=-=
+print(1)|
+=-=-=
+
+Name: forward-sentence moves over return statements
+
+=-=
+function f()
+    |return math.random()
+end
+=-=
+function f()
+    return math.random()|
+end
+=-=-=
+
+Code:
+  (lambda ()
+    (lua-ts-mode)
+    (forward-sentence 2))
+
+Name: forward-sentence moves over table fields
+
+=-=
+local t = {
+  |a = 1,
+  b = 2,
+}
+=-=
+local t = {
+  a = 1,
+  b = 2|,
+}
+=-=-=
+
+Code:
+  (lambda ()
+    (lua-ts-mode)
+    (backward-sentence 1))
+
+Point-Char: |
+
+Name: backward-sentence moves over if statements
+
+=-=
+function f()
+  if true then
+    print(1)
+  elseif false then
+    print(0)
+  else
+    print(2)
+  end|
+end
+=-=
+function f()
+  |if true then
+    print(1)
+  elseif false then
+    print(0)
+  else
+    print(2)
+  end
+end
+=-=-=
+
+Name: backward-sentence moves over variable declaration
+
+=-=
+local n = 1|
+
+print(n)
+=-=
+|local n = 1
+
+print(n)
+=-=-=
+
+Name: backward-sentence moves over for statements
+
+=-=
+for k, v in pairs({}) do
+  print(k, v)
+end|
+
+print(1)
+=-=
+|for k, v in pairs({}) do
+  print(k, v)
+end
+
+print(1)
+=-=-=
+
+Name: backward-sentence moves over for statements
+
+=-=
+do
+  local x = 1
+  local y = 2
+
+  print(x, y)
+end|
+
+print(1)
+=-=
+|do
+  local x = 1
+  local y = 2
+
+  print(x, y)
+end
+
+print(1)
+=-=-=
+
+Name: backward-sentence moves over while statements
+
+=-=
+local i = 0
+while i < 9 do
+      print(i)
+      i = i + 1
+end|
+
+print(1)
+=-=
+local i = 0
+|while i < 9 do
+      print(i)
+      i = i + 1
+end
+
+print(1)
+=-=-=
+
+Name: backward-sentence moves over repeat statements
+
+=-=
+local i = 0
+repeat
+  print(i)
+  i = i + 1
+until i > 9|
+
+print(1)
+=-=
+local i = 0
+|repeat
+  print(i)
+  i = i + 1
+until i > 9
+
+print(1)
+=-=-=
+
+Name: backward-sentence moves over function calls
+
+=-=
+print(1)|
+=-=
+|print(1)
+=-=-=
+
+Name: backward-sentence moves over return statements
+
+=-=
+function f()
+    return math.random()|
+end
+=-=
+function f()
+    |return math.random()
+end
+=-=-=
+
+Code:
+  (lambda ()
+    (lua-ts-mode)
+    (backward-sentence 2))
+
+Point-Char: |
+
+Name: backward-sentence moves over table fields
+
+=-=
+local t = {
+  a = 1,
+  b = 2|,
+}
+=-=
+local t = {
+  |a = 1,
+  b = 2,
+}
+=-=-=
+
+Code:
+  (lambda ()
+    (lua-ts-mode)
+    (forward-sexp 1))
+
+Point-Char: |
+
+Name: forward-sexp moves over blocks
+
+=-=
+local function Test()
+  |local t = {
+    a = 1,
+  }
+
+  if true then
+    print(1)
+  else
+    print(0)
+  end
+end
+=-=
+local function Test()
+  local t = {
+    a = 1,
+  }
+
+  if true then
+    print(1)
+  else
+    print(0)
+  end|
+end
+=-=-=
+
+Name: forward-sexp moves over arguments
+
+=-=
+print|(1, 2, 3)
+=-=
+print(1, 2, 3)|
+=-=-=
+
+Name: forward-sexp moves over parameters
+
+=-=
+function f|(a, b) end
+=-=
+function f(a, b)| end
+=-=-=
+
+Name: forward-sexp moves over strings
+
+=-=
+print("|1, 2, 3")
+=-=
+print("1, 2, 3|")
+=-=-=
+
+Name: forward-sexp moves over tables
+
+=-=
+local t = |{ 1,
+  2,
+  3 }
+=-=
+local t = { 1,
+  2,
+  3 }|
+=-=-=
+
+Code:
+  (lambda ()
+    (lua-ts-mode)
+    (backward-sexp 1))
+
+Point-Char: |
+
+Name: backward-sexp moves over blocks
+
+=-=
+local function Test()
+  local t = {
+    a = 1,
+  }
+
+  if true then
+    print(1)
+  else
+    print(0)
+  end|
+end
+=-=
+local function Test()
+  |local t = {
+    a = 1,
+  }
+
+  if true then
+    print(1)
+  else
+    print(0)
+  end
+end
+=-=-=
+
+Name: backward-sexp moves over arguments
+
+=-=
+print(1, 2, 3)|
+=-=
+print|(1, 2, 3)
+=-=-=
+
+Name: backward-sexp moves over parameters
+
+=-=
+function f(a, b)| end
+=-=
+function f|(a, b) end
+=-=-=
+
+Name: backward-sexp moves over strings
+
+=-=
+print("1, 2, 3|")
+=-=
+print("|1, 2, 3")
+=-=-=
+
+Name: backward-sexp moves over tables
+
+=-=
+local t = { 1,
+  2,
+  3 }|
+=-=
+local t = |{ 1,
+  2,
+  3 }
+=-=-=
diff --git a/test/lisp/progmodes/lua-ts-mode-tests.el 
b/test/lisp/progmodes/lua-ts-mode-tests.el
new file mode 100644
index 00000000000..d2105b66f6d
--- /dev/null
+++ b/test/lisp/progmodes/lua-ts-mode-tests.el
@@ -0,0 +1,36 @@
+;;; lua-ts-mode-tests.el --- Tests for lua-ts-mode -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2023 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(require 'ert)
+(require 'ert-x)
+(require 'treesit)
+
+(ert-deftest lua-ts-mode-test-indentation ()
+  (skip-unless (treesit-ready-p 'lua))
+  (ert-test-erts-file (ert-resource-file "indent.erts")))
+
+(ert-deftest lua-ts-mode-test-movement ()
+  (skip-unless (treesit-ready-p 'lua))
+  (ert-test-erts-file (ert-resource-file "movement.erts")))
+
+(provide 'lua-ts-mode-tests)
+
+;;; lua-ts-mode-tests.el ends here
diff --git a/test/lisp/progmodes/perl-mode-tests.el 
b/test/lisp/progmodes/perl-mode-tests.el
index 3757ac25547..a47a6722e20 100644
--- a/test/lisp/progmodes/perl-mode-tests.el
+++ b/test/lisp/progmodes/perl-mode-tests.el
@@ -28,6 +28,23 @@
     (font-lock-ensure (point-min) (point-max))
     (should (equal (get-text-property 4 'face) 
'font-lock-variable-name-face))))
 
+(ert-deftest perl-test-bug-34245 ()
+  "Test correct indentation after a hanging paren, with and without comments."
+  (with-temp-buffer
+    (perl-mode)
+    (insert "my @foo = (\n\"bar\",\n\"baz\",\n);")
+    (insert "\n\n")
+    (insert "my @ofoo = (\t\t# A comment.\n\"obar\",\n\"obaz\",\n);")
+    (indent-region (point-min) (point-max))
+    (goto-char (point-min))
+    (forward-line)
+    (skip-chars-forward " \t")
+    (should (equal (current-column) perl-indent-level))
+    (search-forward "# A comment.")
+    (forward-line)
+    (skip-chars-forward " \t")
+    (should (equal (current-column) perl-indent-level))))
+
 ;;;; Re-use cperl-mode tests
 
 (defvar cperl-test-mode)
diff --git a/test/lisp/progmodes/project-tests.el 
b/test/lisp/progmodes/project-tests.el
index 5a206b67db1..d335864ca2e 100644
--- a/test/lisp/progmodes/project-tests.el
+++ b/test/lisp/progmodes/project-tests.el
@@ -137,6 +137,7 @@ When `project-ignores' includes a name matching project 
dir."
          (project-vc-extra-root-markers '("files-x-tests.*"))
          (project (project-current nil dir)))
     (should-not (null project))
+    (should (nth 1 project))
     (should (string-match-p "/test/lisp/\\'" (project-root project)))))
 
 (ert-deftest project-vc-supports-project-in-different-dir ()
diff --git a/test/lisp/progmodes/python-tests.el 
b/test/lisp/progmodes/python-tests.el
index 9f935f2748c..a44a11896f0 100644
--- a/test/lisp/progmodes/python-tests.el
+++ b/test/lisp/progmodes/python-tests.el
@@ -4918,7 +4918,7 @@ import abc
   ;; Skip the test on macOS, since the standard Python installation uses
   ;; libedit rather than readline which confuses the running of an inferior
   ;; interpreter in this case (see bug#59477 and bug#25753).
-  (skip-unless (not (eq system-type 'darwin)))
+  (skip-when (eq system-type 'darwin))
   (trace-function 'python-shell-output-filter)
   (python-tests-with-temp-buffer-with-shell
    "
diff --git a/test/lisp/shadowfile-tests.el b/test/lisp/shadowfile-tests.el
index 5edba039032..b1c06ad2d05 100644
--- a/test/lisp/shadowfile-tests.el
+++ b/test/lisp/shadowfile-tests.el
@@ -101,7 +101,7 @@ Per definition, all files are identical on the different 
hosts of
 a cluster (or site).  This is not tested here; it must be
 guaranteed by the originator of a cluster definition."
   :tags '(:expensive-test)
-  (skip-unless (not (memq system-type '(windows-nt ms-dos))))
+  (skip-when (memq system-type '(windows-nt ms-dos)))
   (skip-unless (file-remote-p ert-remote-temporary-file-directory))
 
   (let ((text-quoting-style 'grave) ;; We inspect the *Messages* buffer!
@@ -219,7 +219,7 @@ guaranteed by the originator of a cluster definition."
 Per definition, all files are identical on the different hosts of
 a cluster (or site).  This is not tested here; it must be
 guaranteed by the originator of a cluster definition."
-  (skip-unless (not (memq system-type '(windows-nt ms-dos))))
+  (skip-when (memq system-type '(windows-nt ms-dos)))
   (skip-unless (file-remote-p ert-remote-temporary-file-directory))
 
   (let ((shadow-info-file shadow-test-info-file)
@@ -320,7 +320,7 @@ guaranteed by the originator of a cluster definition."
 
 (ert-deftest shadow-test02-files ()
   "Check file manipulation functions."
-  (skip-unless (not (memq system-type '(windows-nt ms-dos))))
+  (skip-when (memq system-type '(windows-nt ms-dos)))
   (skip-unless (file-remote-p ert-remote-temporary-file-directory))
 
   (let ((shadow-info-file shadow-test-info-file)
@@ -391,7 +391,7 @@ guaranteed by the originator of a cluster definition."
 
 (ert-deftest shadow-test03-expand-cluster-in-file-name ()
   "Check canonical file name of a cluster or site."
-  (skip-unless (not (memq system-type '(windows-nt ms-dos))))
+  (skip-when (memq system-type '(windows-nt ms-dos)))
   (skip-unless (file-remote-p ert-remote-temporary-file-directory))
 
   (let ((shadow-info-file shadow-test-info-file)
@@ -456,7 +456,7 @@ guaranteed by the originator of a cluster definition."
 
 (ert-deftest shadow-test04-contract-file-name ()
   "Check canonical file name of a cluster or site."
-  (skip-unless (not (memq system-type '(windows-nt ms-dos))))
+  (skip-when (memq system-type '(windows-nt ms-dos)))
   (skip-unless (file-remote-p ert-remote-temporary-file-directory))
 
   (let ((shadow-info-file shadow-test-info-file)
@@ -511,7 +511,7 @@ guaranteed by the originator of a cluster definition."
 
 (ert-deftest shadow-test05-file-match ()
   "Check `shadow-same-site' and `shadow-file-match'."
-  (skip-unless (not (memq system-type '(windows-nt ms-dos))))
+  (skip-when (memq system-type '(windows-nt ms-dos)))
   (skip-unless (file-remote-p ert-remote-temporary-file-directory))
 
   (let ((shadow-info-file shadow-test-info-file)
@@ -563,7 +563,7 @@ guaranteed by the originator of a cluster definition."
 
 (ert-deftest shadow-test06-literal-groups ()
   "Check literal group definitions."
-  (skip-unless (not (memq system-type '(windows-nt ms-dos))))
+  (skip-when (memq system-type '(windows-nt ms-dos)))
   (skip-unless (file-remote-p ert-remote-temporary-file-directory))
 
   (let ((shadow-info-file shadow-test-info-file)
@@ -648,7 +648,7 @@ guaranteed by the originator of a cluster definition."
 
 (ert-deftest shadow-test07-regexp-groups ()
   "Check regexp group definitions."
-  (skip-unless (not (memq system-type '(windows-nt ms-dos))))
+  (skip-when (memq system-type '(windows-nt ms-dos)))
   (skip-unless (file-remote-p ert-remote-temporary-file-directory))
 
   (let ((shadow-info-file shadow-test-info-file)
@@ -710,7 +710,7 @@ guaranteed by the originator of a cluster definition."
 
 (ert-deftest shadow-test08-shadow-todo ()
   "Check that needed shadows are added to todo."
-  (skip-unless (not (memq system-type '(windows-nt ms-dos))))
+  (skip-when (memq system-type '(windows-nt ms-dos)))
   (skip-unless (file-remote-p ert-remote-temporary-file-directory))
   (skip-unless (file-writable-p ert-remote-temporary-file-directory))
 
@@ -855,7 +855,7 @@ guaranteed by the originator of a cluster definition."
 (ert-deftest shadow-test09-shadow-copy-files ()
   "Check that needed shadow files are copied."
   :tags '(:expensive-test)
-  (skip-unless (not (memq system-type '(windows-nt ms-dos))))
+  (skip-when (memq system-type '(windows-nt ms-dos)))
   (skip-unless (file-remote-p ert-remote-temporary-file-directory))
   (skip-unless (file-writable-p ert-remote-temporary-file-directory))
 
diff --git a/test/lisp/simple-tests.el b/test/lisp/simple-tests.el
index 7dabb735522..b632c908443 100644
--- a/test/lisp/simple-tests.el
+++ b/test/lisp/simple-tests.el
@@ -742,7 +742,7 @@ See Bug#21722."
 
 (ert-deftest eval-expression-print-format-sym-echo ()
   ;; We can only check the echo area when running interactive.
-  (skip-unless (not noninteractive))
+  (skip-when noninteractive)
   (with-temp-buffer
     (cl-letf (((symbol-function 'read--expression) (lambda (&rest _) t)))
       (let ((current-prefix-arg nil))
@@ -763,7 +763,7 @@ See Bug#21722."
         (should (equal (buffer-string) "65 (#o101, #x41, ?A)"))))))
 
 (ert-deftest eval-expression-print-format-small-int-echo ()
-  (skip-unless (not noninteractive))
+  (skip-when noninteractive)
   (with-temp-buffer
     (cl-letf (((symbol-function 'read--expression) (lambda (&rest _) ?A)))
       (let ((current-prefix-arg nil))
@@ -789,7 +789,7 @@ See Bug#21722."
         (should (equal (buffer-string) "66 (#o102, #x42, ?B)"))))))
 
 (ert-deftest eval-expression-print-format-large-int-echo ()
-  (skip-unless (not noninteractive))
+  (skip-when noninteractive)
   (with-temp-buffer
     (cl-letf (((symbol-function 'read--expression) (lambda (&rest _) ?B))
               (eval-expression-print-maximum-character ?A))
diff --git a/test/lisp/term-tests.el b/test/lisp/term-tests.el
index ee2bb6574ae..911d03d5628 100644
--- a/test/lisp/term-tests.el
+++ b/test/lisp/term-tests.el
@@ -110,7 +110,7 @@
         (buffer-substring (point-min) (point-max))))))
 
 (ert-deftest term-simple-lines ()
-  (skip-unless (not (memq system-type '(windows-nt ms-dos))))
+  (skip-when (memq system-type '(windows-nt ms-dos)))
   (let ((str "\
 first line\r
 next line\r\n"))
@@ -118,14 +118,14 @@ next line\r\n"))
                    (string-replace "\r" "" str)))))
 
 (ert-deftest term-carriage-return ()
-  (skip-unless (not (memq system-type '(windows-nt ms-dos))))
+  (skip-when (memq system-type '(windows-nt ms-dos)))
   (let ((str "\
 first line\r_next line\r\n"))
     (should (equal (term-test-screen-from-input 40 12 str)
                    "_next line\n"))))
 
 (ert-deftest term-line-wrap ()
-  (skip-unless (not (memq system-type '(windows-nt ms-dos))))
+  (skip-when (memq system-type '(windows-nt ms-dos)))
   (should (string-match-p
            ;; Don't be strict about trailing whitespace.
            "\\`a\\{40\\}\na\\{20\\} *\\'"
@@ -137,7 +137,7 @@ first line\r_next line\r\n"))
                                                 (list str str))))))
 
 (ert-deftest term-colors ()
-  (skip-unless (not (memq system-type '(windows-nt ms-dos))))
+  (skip-when (memq system-type '(windows-nt ms-dos)))
   (pcase-dolist (`(,str ,expected) ansi-test-strings)
     (let ((result (term-test-screen-from-input 40 12 str)))
       (should (equal result expected))
@@ -145,7 +145,7 @@ first line\r_next line\r\n"))
                      (text-properties-at 0 expected))))))
 
 (ert-deftest term-colors-bold-is-bright ()
-  (skip-unless (not (memq system-type '(windows-nt ms-dos))))
+  (skip-when (memq system-type '(windows-nt ms-dos)))
   (let ((ansi-color-bold-is-bright t))
     (pcase-dolist (`(,str ,expected ,bright-expected) ansi-test-strings)
       (let ((expected (or bright-expected expected))
@@ -155,7 +155,7 @@ first line\r_next line\r\n"))
                        (text-properties-at 0 expected)))))))
 
 (ert-deftest term-cursor-movement ()
-  (skip-unless (not (memq system-type '(windows-nt ms-dos))))
+  (skip-when (memq system-type '(windows-nt ms-dos)))
   ;; Absolute positioning.
   (should (equal "ab\ncd"
                  (term-test-screen-from-input
@@ -186,7 +186,7 @@ first line\r_next line\r\n"))
                                 "\e[D\e[Da")))))
 
 (ert-deftest term-scrolling-region ()
-  (skip-unless (not (memq system-type '(windows-nt ms-dos))))
+  (skip-when (memq system-type '(windows-nt ms-dos)))
   (should (equal "\
 line3
 line4
@@ -338,7 +338,7 @@ line6\r
 line7")))))
 
 (ert-deftest term-set-directory ()
-  (skip-unless (not (memq system-type '(windows-nt ms-dos))))
+  (skip-when (memq system-type '(windows-nt ms-dos)))
   (let ((term-ansi-at-user (user-real-login-name)))
     (should (equal (term-test-screen-from-input
                     40 12 "\eAnSiTc /foo/\n" 'default-directory)
@@ -354,7 +354,7 @@ A real-life example is the default zsh prompt which writes 
spaces
 to the end of line (triggering line-wrapping state), and then
 sends a carriage return followed by another space to overwrite
 the first character of the line."
-  (skip-unless (not (memq system-type '(windows-nt ms-dos))))
+  (skip-when (memq system-type '(windows-nt ms-dos)))
   (let* ((width 10)
          (strs (list "x" (make-string (1- width) ?_)
                      "\r_")))
@@ -364,7 +364,7 @@ the first character of the line."
 (ert-deftest term-to-margin ()
   "Test cursor movement at the scroll margin.
 This is a reduced example from GNU nano's initial screen."
-  (skip-unless (not (memq system-type '(windows-nt ms-dos))))
+  (skip-when (memq system-type '(windows-nt ms-dos)))
   (let* ((width 10)
          (x (make-string width ?x))
          (y (make-string width ?y)))
diff --git a/test/lisp/thread-tests.el b/test/lisp/thread-tests.el
index 4ba7b99719c..5d684a96a18 100644
--- a/test/lisp/thread-tests.el
+++ b/test/lisp/thread-tests.el
@@ -88,7 +88,7 @@
 
 (ert-deftest thread-tests-list-threads-error-when-not-configured ()
   "Signal an error running `list-threads' if threads are not configured."
-  (skip-unless (not (featurep 'threads)))
+  (skip-when (featurep 'threads))
   (should-error (list-threads)))
 
 (provide 'thread-tests)
diff --git a/test/lisp/vc/vc-tests.el b/test/lisp/vc/vc-tests.el
index 11c20d2783c..f40cee8cc5b 100644
--- a/test/lisp/vc/vc-tests.el
+++ b/test/lisp/vc/vc-tests.el
@@ -596,8 +596,9 @@ This checks also `vc-backend' and `vc-responsible-backend'."
     (let ((vc-handled-backends `(,backend))
           (default-directory
            (file-name-as-directory
-            (expand-file-name
-             (make-temp-name "vc-test") temporary-file-directory)))
+            (file-truename
+             (expand-file-name
+              (make-temp-name "vc-test") temporary-file-directory))))
           (process-environment process-environment)
           vc-test--cleanup-hook)
       (when (eq backend 'Bzr)
@@ -780,7 +781,7 @@ This checks also `vc-backend' and `vc-responsible-backend'."
           ;; CVS calls vc-delete-file, which insists on prompting
           ;; "Really want to delete ...?", and `vc-mtn.el' does not implement
           ;; `delete-file' at all.
-          (skip-unless (not (memq ',backend '(CVS Mtn))))
+          (skip-when (memq ',backend '(CVS Mtn)))
           (vc-test--rename-file ',backend))
 
         (ert-deftest
@@ -795,7 +796,7 @@ This checks also `vc-backend' and `vc-responsible-backend'."
                  (format "vc-test-%s01-register" backend-string))))))
           ;; `vc-mtn.el' gives me:
           ;; "Failed (status 1): mtn commit -m Testing vc-version-diff\n\n foo"
-          (skip-unless (not (memq ',backend '(Mtn))))
+          (skip-when (memq ',backend '(Mtn)))
           (vc-test--version-diff ',backend))
         ))))
 
diff --git a/test/lisp/wid-edit-tests.el b/test/lisp/wid-edit-tests.el
index ebfe729bc9a..66bff4ad2e3 100644
--- a/test/lisp/wid-edit-tests.el
+++ b/test/lisp/wid-edit-tests.el
@@ -380,4 +380,15 @@ return nil, even with a non-nil bubblep argument."
                                     :value (("1" . 1) ("2" . 2))))))
       (should (equal '(("1" . 1) ("2" . 2)) (widget-default-get w))))))
 
+(ert-deftest widget-test-restricted-sexp-empty-val ()
+  "Test that we handle an empty restricted-sexp widget just fine."
+  (with-temp-buffer
+    (let ((w (widget-create '(restricted-sexp
+                              :value 3
+                              :match-alternatives (integerp)))))
+      (widget-setup)
+      (widget-backward 1)
+      (delete-char 1)
+      (should (string= (widget-value w) "")))))
+
 ;;; wid-edit-tests.el ends here
diff --git a/test/manual/BidiCharacterTest.txt 
b/test/manual/BidiCharacterTest.txt
index 619d4b4412b..6b3ef016036 100644
--- a/test/manual/BidiCharacterTest.txt
+++ b/test/manual/BidiCharacterTest.txt
@@ -1,6 +1,6 @@
-# BidiCharacterTest-15.0.0.txt
-# Date: 2022-05-03, 18:46:00 GMT [LI]
-# © 2022 Unicode®, Inc.
+# BidiCharacterTest-15.1.0.txt
+# Date: 2023-01-05
+# © 2023 Unicode®, Inc.
 # For terms of use, see https://www.unicode.org/terms_of_use.html
 #
 # Unicode Character Database
diff --git a/test/manual/scroll-tests.el b/test/manual/scroll-tests.el
index c2a4ef17bff..b2cfeabfe64 100644
--- a/test/manual/scroll-tests.el
+++ b/test/manual/scroll-tests.el
@@ -80,25 +80,25 @@
        ,@body)))
 
 (ert-deftest scroll-tests-scroll-margin-0 ()
-  (skip-unless (not noninteractive))
+  (skip-when noninteractive)
   (scroll-tests-with-buffer-window
     (scroll-tests-up-and-down 0)))
 
 (ert-deftest scroll-tests-scroll-margin-negative ()
   "A negative `scroll-margin' should be the same as 0."
-  (skip-unless (not noninteractive))
+  (skip-when noninteractive)
   (scroll-tests-with-buffer-window
     (scroll-tests-up-and-down -10 0)))
 
 (ert-deftest scroll-tests-scroll-margin-max ()
-  (skip-unless (not noninteractive))
+  (skip-when noninteractive)
   (scroll-tests-with-buffer-window
     (let ((max-margin (/ (window-text-height) 4)))
       (scroll-tests-up-and-down max-margin))))
 
 (ert-deftest scroll-tests-scroll-margin-over-max ()
   "A `scroll-margin' more than max should be the same as max."
-  (skip-unless (not noninteractive))
+  (skip-when noninteractive)
   (scroll-tests-with-buffer-window 7
     (let ((max-margin (/ (window-text-height) 4)))
       (scroll-tests-up-and-down (+ max-margin 1) max-margin)
@@ -155,7 +155,7 @@ middle of the window."
       (should (scroll-tests--point-in-middle-of-window-p)))))
 
 (ert-deftest scroll-tests-scroll-margin-whole-window ()
-  (skip-unless (not noninteractive))
+  (skip-when noninteractive)
   (scroll-tests--scroll-margin-whole-window))
 
 (ert-deftest scroll-tests-scroll-margin-whole-window-line-spacing ()
diff --git a/test/misc/test-custom-libs.el b/test/misc/test-custom-libs.el
index eb565de8eee..d255a87eeaf 100644
--- a/test/misc/test-custom-libs.el
+++ b/test/misc/test-custom-libs.el
@@ -39,7 +39,7 @@
   :tags '(:expensive-test)
   :expected-result :failed ; FIXME: See above.
   ;; This test is very slow, and IMO not worth the time it takes.
-  (skip-unless (not (getenv "EMACS_HYDRA_CI")))
+  (skip-when (getenv "EMACS_HYDRA_CI"))
   (skip-unless (file-readable-p custom-test-admin-cus-test))
   (load custom-test-admin-cus-test)
   (cus-test-libs t)
diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el
index 45914b2b6b0..aa30533c6a0 100644
--- a/test/src/buffer-tests.el
+++ b/test/src/buffer-tests.el
@@ -142,6 +142,7 @@ properties."
                 (expected-calls . ((modification-hooks (nil 3 4))
                                    (modification-hooks (t 3 4 1)))))
                ((replace . "4"))
+               ((replace . "4") (overlay-beg . 4)) ;bug#65929
                ((replace . "12")
                 (expected-calls . ((modification-hooks (nil 1 3))
                                    (modification-hooks (t 1 2 2)))))
diff --git a/test/src/emacs-module-tests.el b/test/src/emacs-module-tests.el
index ac88011b543..59af5d9a4a8 100644
--- a/test/src/emacs-module-tests.el
+++ b/test/src/emacs-module-tests.el
@@ -457,7 +457,7 @@ See Bug#36226."
 
 (ert-deftest module/async-pipe ()
   "Check that writing data from another thread works."
-  (skip-unless (not (eq system-type 'windows-nt))) ; FIXME!
+  (skip-when (eq system-type 'windows-nt)) ; FIXME!
   (with-temp-buffer
     (let ((process (make-pipe-process :name "module/async-pipe"
                                       :buffer (current-buffer)
diff --git a/test/src/fileio-tests.el b/test/src/fileio-tests.el
index 50642420ce9..62a9be546f9 100644
--- a/test/src/fileio-tests.el
+++ b/test/src/fileio-tests.el
@@ -50,7 +50,7 @@ Also check that an encoding error can appear in a symlink."
   ;; Some Windows versions don't support symlinks, and those which do
   ;; will pop up UAC elevation prompts, so we disable this test on
   ;; MS-Windows.
-  (skip-unless (not (eq system-type 'windows-nt)))
+  (skip-when (eq system-type 'windows-nt))
   (should (equal nil (fileio-tests--symlink-failure))))
 
 (ert-deftest fileio-tests--directory-file-name ()
diff --git a/test/src/filelock-tests.el b/test/src/filelock-tests.el
index c5e77f70bb2..f4376b2a5b0 100644
--- a/test/src/filelock-tests.el
+++ b/test/src/filelock-tests.el
@@ -109,7 +109,7 @@ the case)."
 
 (ert-deftest filelock-tests-lock-spoiled ()
   "Check `lock-buffer'."
-  (skip-unless (not (eq system-type 'ms-dos))) ; no filelock support
+  (skip-when (eq system-type 'ms-dos)) ; no filelock support
   (filelock-tests--fixture
    (filelock-tests--spoil-lock-file buffer-file-truename)
    ;; FIXME: errors when locking a file are ignored; should they be?
@@ -119,7 +119,7 @@ the case)."
 
 (ert-deftest filelock-tests-file-locked-p-spoiled ()
   "Check that `file-locked-p' fails if the lockfile is \"spoiled\"."
-  (skip-unless (not (eq system-type 'ms-dos))) ; no filelock support
+  (skip-when (eq system-type 'ms-dos)) ; no filelock support
   (filelock-tests--fixture
    (filelock-tests--spoil-lock-file buffer-file-truename)
    (let ((err (should-error (file-locked-p (buffer-file-name)))))
@@ -130,7 +130,7 @@ the case)."
 
 (ert-deftest filelock-tests-unlock-spoiled ()
   "Check that `unlock-buffer' fails if the lockfile is \"spoiled\"."
-  (skip-unless (not (eq system-type 'ms-dos))) ; no filelock support
+  (skip-when (eq system-type 'ms-dos)) ; no filelock support
   (filelock-tests--fixture
    ;; Set the buffer modified with file locking temporarily disabled.
    (let ((create-lockfiles nil))
@@ -150,7 +150,7 @@ the case)."
 
 (ert-deftest filelock-tests-kill-buffer-spoiled ()
   "Check that `kill-buffer' fails if a lockfile is \"spoiled\"."
-  (skip-unless (not (eq system-type 'ms-dos))) ; no filelock support
+  (skip-when (eq system-type 'ms-dos)) ; no filelock support
   (filelock-tests--fixture
    ;; Set the buffer modified with file locking temporarily disabled.
    (let ((create-lockfiles nil))
@@ -176,7 +176,7 @@ the case)."
 
 (ert-deftest filelock-tests-detect-external-change ()
   "Check that an external file modification is reported."
-  (skip-unless (not (eq system-type 'ms-dos))) ; no filelock support
+  (skip-when (eq system-type 'ms-dos)) ; no filelock support
   (skip-unless (executable-find "touch"))
   (skip-unless (executable-find "echo"))
   (dolist (cl '(t nil))
diff --git a/test/src/fns-tests.el b/test/src/fns-tests.el
index 79ae4393f40..9c09e4f0c33 100644
--- a/test/src/fns-tests.el
+++ b/test/src/fns-tests.el
@@ -98,6 +98,26 @@
   (should-not (equal-including-properties #("a" 0 1 (k "v"))
                                           #("b" 0 1 (k "v")))))
 
+(ert-deftest fns-tests-equal-symbols-with-position ()
+  "Test `eq' and `equal' on symbols with position."
+  (let ((foo1 (position-symbol 'foo 42))
+        (foo2 (position-symbol 'foo 666))
+        (foo3 (position-symbol 'foo 42)))
+    (let (symbols-with-pos-enabled)
+      (should (eq foo1 foo1))
+      (should (equal foo1 foo1))
+      (should-not (eq foo1 foo2))
+      (should-not (equal foo1 foo2))
+      (should-not (eq foo1 foo3))
+      (should (equal foo1 foo3)))
+    (let ((symbols-with-pos-enabled t))
+      (should (eq foo1 foo1))
+      (should (equal foo1 foo1))
+      (should (eq foo1 foo2))
+      (should (equal foo1 foo2))
+      (should (eq foo1 foo3))
+      (should (equal foo1 foo3)))))
+
 (ert-deftest fns-tests-reverse ()
   (should-error (reverse))
   (should-error (reverse 1))
diff --git a/test/src/image-tests.el b/test/src/image-tests.el
index 317f4d3aab6..3eec2bb4c60 100644
--- a/test/src/image-tests.el
+++ b/test/src/image-tests.el
@@ -44,15 +44,15 @@
     (xpm . ,(find-image '((:file "splash.xpm" :type xpm))))))
 
 (ert-deftest image-tests-image-size/error-on-nongraphical-display ()
-  (skip-unless (not (display-images-p)))
+  (skip-when (display-images-p))
   (should-error (image-size 'invalid-spec)))
 
 (ert-deftest image-tests-image-mask-p/error-on-nongraphical-display ()
-  (skip-unless (not (display-images-p)))
+  (skip-when (display-images-p))
   (should-error (image-mask-p (cdr (assq 'xpm image-tests--images)))))
 
 (ert-deftest image-tests-image-metadata/error-on-nongraphical-display ()
-  (skip-unless (not (display-images-p)))
+  (skip-when (display-images-p))
   (should-error (image-metadata (cdr (assq 'xpm image-tests--images)))))
 
 (ert-deftest image-tests-imagemagick-types ()
diff --git a/test/src/process-tests.el b/test/src/process-tests.el
index e17e1c0d833..711a27f1cfb 100644
--- a/test/src/process-tests.el
+++ b/test/src/process-tests.el
@@ -231,7 +231,7 @@ process to complete."
   (with-timeout (60 (ert-fail "Test timed out"))
   ;; Frequent random (?) failures on hydra.nixos.org, with no process output.
   ;; Maybe this test should be tagged unstable?  See bug#31214.
-  (skip-unless (not (getenv "EMACS_HYDRA_CI")))
+  (skip-when (getenv "EMACS_HYDRA_CI"))
   (with-temp-buffer
     (let ((process (make-process
                     :name "mix-stderr"
@@ -723,7 +723,7 @@ FD_SETSIZE file descriptors (Bug#24325)."
   (skip-unless (featurep 'make-network-process '(:server t)))
   (skip-unless (featurep 'make-network-process '(:family local)))
   ;; Avoid hang due to connect/accept handshake on Cygwin (bug#49496).
-  (skip-unless (not (eq system-type 'cygwin)))
+  (skip-when (eq system-type 'cygwin))
   (with-timeout (60 (ert-fail "Test timed out"))
     (ert-with-temp-directory directory
       (process-tests--with-processes processes
@@ -763,7 +763,7 @@ FD_SETSIZE file descriptors (Bug#24325)."
   "Check that Emacs doesn't crash when trying to use more than
 FD_SETSIZE file descriptors (Bug#24325)."
   ;; This test cannot be run if PTYs aren't supported.
-  (skip-unless (not (eq system-type 'windows-nt)))
+  (skip-when (eq system-type 'windows-nt))
   (with-timeout (60 (ert-fail "Test timed out"))
     (process-tests--with-processes processes
       ;; In order to use `make-serial-process', we need to create some
diff --git a/test/src/regex-emacs-tests.el b/test/src/regex-emacs-tests.el
index 4e2c0f67a44..615d905e140 100644
--- a/test/src/regex-emacs-tests.el
+++ b/test/src/regex-emacs-tests.el
@@ -555,10 +555,10 @@ known/benign differences in behavior.")
 
 (defconst regex-tests-PTESTS-whitelist
   [
-   ;; emacs doesn't see DEL (0x7f) as a [:cntrl:] character
+   ;; Emacs doesn't see DEL (0x7f) as a [:cntrl:] character
    138
 
-   ;; emacs doesn't barf on weird ranges such as [b-a], but simply
+   ;; Emacs doesn't barf on weird ranges such as [b-a], but simply
    ;; fails to match
    168
   ]
@@ -872,16 +872,44 @@ This evaluates the TESTS test cases from glibc."
   (should (equal (string-match "\\`\\(?:ab\\)*\\'" "a") nil))
   (should (equal (string-match "\\`a\\{2\\}*\\'" "a") nil)))
 
-(ert-deftest regexp-tests-backtrack-optimization () ;bug#61514
+(ert-deftest regexp-tests-backtrack-optimization ()
   ;; Make sure we don't use up the regexp stack needlessly.
   (with-current-buffer (get-buffer-create "*bug*")
     (erase-buffer)
     (insert (make-string 1000000 ?x) "=")
     (goto-char (point-min))
+    ;; Make sure we do perform the optimization (if we don't, the
+    ;; below will burp with regexp-stack-overflow). ;bug#61514
     (should (looking-at "x*=*"))
     (should (looking-at "x*\\(=\\|:\\)"))
     (should (looking-at "x*\\(=\\|:\\)*"))
-    (should (looking-at "x*=*?"))))
+    (should (looking-at "x*=*?"))
+    ;; relint suppression: Repetition of expression matching an empty string
+    (should (looking-at "x*\\(=*\\|h\\)*?"))
+    ;; relint suppression: Repetition of expression matching an empty string
+    (should (looking-at "x*\\(=*\\|h\\)*"))
+    ;; relint suppression: Repetition of expression matching an empty string
+    (should (looking-at "x*\\(=*?\\|h\\)*"))
+    ;; relint suppression: Repetition of expression matching an empty string
+    (should (looking-at "x*\\(=*?\\|h\\)*?"))
+    ;; relint suppression: Repetition of expression matching an empty string
+    (should (looking-at "x*\\(=*\\|h\\)+?"))
+    ;; relint suppression: Repetition of expression matching an empty string
+    (should (looking-at "x*\\(=*\\|h\\)+"))
+    ;; relint suppression: Repetition of expression matching an empty string
+    (should (looking-at "x*\\(=*?\\|h\\)+"))
+    ;; relint suppression: Repetition of expression matching an empty string
+    (should (looking-at "x*\\(=*?\\|h\\)+?"))
+    (should (looking-at "x*\\(=+\\|h\\)+?"))
+    (should (looking-at "x*\\(=+\\|h\\)+"))
+    (should (looking-at "x*\\(=+?\\|h\\)+"))
+    (should (looking-at "x*\\(=+?\\|h\\)+?"))
+    ;; Regression check for overly optimistic optimization.
+    (should (eq 0 (string-match "\\(ca*\\|ab\\)+d" "cabd")))
+    (should (string-match "\\(aa*\\|b\\)*c" "ababc"))
+    (should (string-match " \\sw*\\bfoo" " foo"))
+    (should (string-match ".*\\>" "hello "))
+    ))
 
 (ert-deftest regexp-tests-zero-width-assertion-repetition ()
   ;; Check compatibility behaviour with repetition operators after
@@ -965,4 +993,9 @@ This evaluates the TESTS test cases from glibc."
                      (re-search-forward re nil t))
                    nil))))
 
+(ert-deftest regex-tests-mutual-exclusive-inf-rec ()
+  ;; Regression test for bug#65726, where this crashed Emacs.
+  ;; relint suppression: Repetition of expression matching an empty string
+  (should (equal (string-match "a*\\(?:c\\|b*\\)*" "a") 0)))
+
 ;;; regex-emacs-tests.el ends here
diff --git a/test/src/regex-resources/PTESTS b/test/src/regex-resources/PTESTS
index 68acc314d37..59dd4b3bc21 100644
--- a/test/src/regex-resources/PTESTS
+++ b/test/src/regex-resources/PTESTS
@@ -269,6 +269,7 @@
 #W the expected result for \([a-c]*\)\{2,\} is failure which isn't correct
 1¦3¦\([a-c]*\)\{2,\}¦abcdefg¦
 1¦3¦\([a-c]*\)\{1,\}¦abcdefg¦
+0¦0¦\([a-c]*\)\{2,\}¦gabcdefg¦
 -1¦-1¦a\{64,\}¦aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa¦
 # GA142
 1¦3¦a\{2,3\}¦aaaa¦
diff --git a/test/src/treesit-tests.el b/test/src/treesit-tests.el
index 65994ce608f..4308e4048f6 100644
--- a/test/src/treesit-tests.el
+++ b/test/src/treesit-tests.el
@@ -662,6 +662,20 @@ visible_end.)"
       ;; TODO: More tests.
       )))
 
+(ert-deftest treesit-range-offset ()
+  "Tests if range offsets work."
+  (skip-unless (treesit-language-available-p 'javascript))
+  (with-temp-buffer
+    (let ((query '(((call_expression (identifier) @_html_template_fn
+                                     (template_string) @capture)
+                    (:equal "html" @_html_template_fn)))))
+      (progn
+        (insert "const x = html`<p>Hello</p>`;")
+        (treesit-parser-create 'javascript))
+      (should (equal '((15 . 29)) (treesit-query-range 'javascript query)))
+      (should (equal '((16 . 28)) (treesit-query-range
+                                   'javascript query nil nil '(1 . -1)))))))
+
 ;;; Multiple language
 
 (ert-deftest treesit-multi-lang ()



reply via email to

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