emacs-diffs
[Top][All Lists]
Advanced

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

scratch/comp-static-data ade89cbd8c: Merge branch 'master' into scratch/


From: Vibhav Pant
Subject: scratch/comp-static-data ade89cbd8c: Merge branch 'master' into scratch/comp-static-data
Date: Mon, 14 Nov 2022 12:30:09 -0500 (EST)

branch: scratch/comp-static-data
commit ade89cbd8c24dab98177950dbfbddc5961450b8f
Merge: 5aa3db2f11 723ceaca1d
Author: Vibhav Pant <vibhavp@gmail.com>
Commit: Vibhav Pant <vibhavp@gmail.com>

    Merge branch 'master' into scratch/comp-static-data
---
 .clang-format                                      |    6 +-
 .dir-locals.el                                     |    5 +-
 .gitignore                                         |    2 +-
 CONTRIBUTE                                         |    7 +-
 ChangeLog.1                                        |    4 +-
 ChangeLog.2                                        |    2 +-
 ChangeLog.3                                        |   34 +-
 INSTALL                                            |    2 +-
 Makefile.in                                        |    8 +-
 admin/authors.el                                   |    2 +-
 admin/automerge                                    |   23 +-
 admin/charsets/mapfiles/stdenc.txt                 |    2 +-
 admin/charsets/mapfiles/symbol.txt                 |    2 +-
 admin/diff-tar-files                               |    8 +-
 admin/emacs-shell-lib                              |   87 +
 admin/emake                                        |   17 +
 admin/git-bisect-start                             |   40 +
 admin/make-manuals                                 |   13 +-
 admin/notes/repo                                   |   22 +
 admin/unidata/README                               |   14 +-
 admin/unidata/blocks.awk                           |    2 +-
 admin/unidata/copyright.html                       |   18 +-
 admin/update_autogen                               |   20 +-
 admin/upload-manuals                               |   10 +-
 configure.ac                                       |    8 +
 doc/emacs/ChangeLog.1                              |    2 +-
 doc/emacs/building.texi                            |   11 +-
 doc/emacs/custom.texi                              |   30 +-
 doc/emacs/display.texi                             |    2 +-
 doc/emacs/emacs.texi                               |    2 +-
 doc/emacs/haiku.texi                               |    2 +-
 doc/emacs/killing.texi                             |    4 +-
 doc/emacs/macos.texi                               |    6 +-
 doc/emacs/maintaining.texi                         |   11 +-
 doc/emacs/misc.texi                                |    2 +-
 doc/emacs/modes.texi                               |    2 +-
 doc/emacs/package.texi                             |   71 +
 doc/emacs/programs.texi                            |  202 +-
 doc/emacs/rmail.texi                               |    8 +
 doc/emacs/search.texi                              |   25 +-
 doc/emacs/text.texi                                |   19 +-
 doc/emacs/vc1-xtra.texi                            |    9 +-
 doc/lispref/backups.texi                           |    2 +-
 doc/lispref/commands.texi                          |    2 +-
 doc/lispref/compile.texi                           |   20 +-
 doc/lispref/control.texi                           |    2 +-
 doc/lispref/customize.texi                         |   24 +-
 doc/lispref/display.texi                           |    8 +-
 doc/lispref/edebug.texi                            |   29 +-
 doc/lispref/files.texi                             |    4 +-
 doc/lispref/frames.texi                            |  111 +-
 doc/lispref/functions.texi                         |   38 +
 doc/lispref/help.texi                              |    4 +-
 doc/lispref/intro.texi                             |    2 +
 doc/lispref/lists.texi                             |    8 +-
 doc/lispref/loading.texi                           |    2 +-
 doc/lispref/modes.texi                             |   17 +-
 doc/lispref/numbers.texi                           |   48 +-
 doc/lispref/processes.texi                         |    6 +-
 doc/lispref/searching.texi                         |   57 +-
 doc/lispref/strings.texi                           |    2 +-
 doc/lispref/symbols.texi                           |    8 +-
 doc/lispref/text.texi                              |   12 +-
 doc/lispref/variables.texi                         |  110 +-
 doc/lispref/windows.texi                           |    2 +-
 doc/misc/ChangeLog.1                               |    2 +-
 doc/misc/Makefile.in                               |    2 +-
 doc/misc/cc-mode.texi                              |   10 +-
 doc/misc/cl.texi                                   |    6 +-
 doc/misc/ede.texi                                  |  164 +-
 doc/misc/efaq-w32.texi                             |    8 +-
 doc/misc/efaq.texi                                 |    8 +-
 doc/misc/eglot.texi                                | 1160 ++++
 doc/misc/eshell.texi                               |   81 +-
 doc/misc/eudc.texi                                 |  143 +-
 doc/misc/flymake.texi                              |    6 +-
 doc/misc/gnus-faq.texi                             |    6 +-
 doc/misc/gnus.texi                                 |   75 +-
 doc/misc/mh-e.texi                                 |   10 +-
 doc/misc/modus-themes.org                          |  177 +-
 doc/misc/newsticker.texi                           |    2 +-
 doc/misc/octave-mode.texi                          |    7 +-
 doc/misc/org.org                                   |   28 +-
 doc/misc/rcirc.texi                                |    4 +-
 doc/misc/reftex.texi                               |    2 +-
 doc/misc/remember.texi                             |    7 -
 doc/misc/sem-user.texi                             |    2 +-
 doc/misc/semantic.texi                             |    2 +-
 doc/misc/sieve.texi                                |    2 +-
 doc/misc/tramp.texi                                |    7 +-
 doc/misc/transient.texi                            |    4 +-
 doc/misc/url.texi                                  |    2 +-
 doc/misc/vhdl-mode.texi                            |    2 +-
 etc/DEBUG                                          |    2 +-
 etc/ERC-NEWS                                       |   18 +-
 etc/NEWS                                           |  267 +-
 etc/NEWS.21                                        |    2 +-
 etc/NEWS.22                                        |    2 +-
 etc/NEWS.25                                        |    2 +-
 etc/NEWS.26                                        |    2 +-
 etc/NEWS.27                                        |    2 +-
 etc/NEWS.28                                        |    2 +-
 etc/PROBLEMS                                       |   11 +
 etc/TODO                                           |    4 +-
 etc/images/gud/README                              |    4 +-
 etc/publicsuffix.txt                               |  259 +-
 etc/srecode/ede-autoconf.srt                       |    2 +-
 etc/srecode/ede-make.srt                           |    4 +-
 etc/themes/adwaita-theme.el                        |    5 +-
 etc/themes/deeper-blue-theme.el                    |    5 +-
 etc/themes/dichromacy-theme.el                     |    5 +-
 etc/themes/leuven-dark-theme.el                    |    8 +-
 etc/themes/leuven-theme.el                         |    8 +-
 etc/themes/light-blue-theme.el                     |    5 +-
 etc/themes/manoj-dark-theme.el                     |    5 +-
 etc/themes/misterioso-theme.el                     |    5 +-
 etc/themes/modus-operandi-theme.el                 |    5 +-
 etc/themes/modus-themes.el                         |  198 +-
 etc/themes/modus-vivendi-theme.el                  |    7 +-
 etc/themes/tango-dark-theme.el                     |    7 +-
 etc/themes/tango-theme.el                          |    6 +-
 etc/themes/tsdh-dark-theme.el                      |    6 +-
 etc/themes/tsdh-light-theme.el                     |    6 +-
 etc/themes/wheatgrass-theme.el                     |    5 +-
 etc/themes/whiteboard-theme.el                     |    5 +-
 etc/themes/wombat-theme.el                         |    5 +-
 lib-src/rcs2log                                    |    2 +-
 lisp/ChangeLog.10                                  |    2 +-
 lisp/ChangeLog.14                                  |    4 +-
 lisp/ChangeLog.15                                  |    6 +-
 lisp/ChangeLog.16                                  |    6 +-
 lisp/ChangeLog.17                                  |    4 +-
 lisp/ChangeLog.7                                   |    8 +-
 lisp/abbrev.el                                     |    2 +-
 lisp/allout-widgets.el                             |    2 +-
 lisp/ansi-color.el                                 |    2 +-
 lisp/ansi-osc.el                                   |   14 +-
 lisp/apropos.el                                    |    3 +-
 lisp/autoinsert.el                                 |    4 +-
 lisp/autorevert.el                                 |    5 +-
 lisp/bindings.el                                   |    8 +
 lisp/bs.el                                         |   12 +-
 lisp/calendar/diary-lib.el                         |    4 +-
 lisp/cedet/ede.el                                  |    2 +-
 lisp/cedet/ede/locate.el                           |    4 +-
 lisp/cedet/pulse.el                                |    2 +-
 lisp/cedet/semantic/symref/grep.el                 |   11 +-
 lisp/cedet/semantic/wisent.el                      |    2 +-
 lisp/cus-edit.el                                   |   60 +-
 lisp/cus-theme.el                                  |   42 +-
 lisp/custom.el                                     |   73 +-
 lisp/dired-aux.el                                  |   17 +-
 lisp/dired.el                                      |   14 +-
 lisp/dom.el                                        |   68 +-
 lisp/ecomplete.el                                  |   49 +-
 lisp/elide-head.el                                 |    4 +-
 lisp/emacs-lisp/backtrace.el                       |    2 +-
 lisp/emacs-lisp/bindat.el                          |    6 +-
 lisp/emacs-lisp/byte-opt.el                        |    2 +-
 lisp/emacs-lisp/byte-run.el                        |    5 +
 lisp/emacs-lisp/bytecomp.el                        |   87 +-
 lisp/emacs-lisp/cconv.el                           |  150 +-
 lisp/emacs-lisp/cl-extra.el                        |    4 +-
 lisp/emacs-lisp/cl-generic.el                      |    6 +-
 lisp/emacs-lisp/cl-macs.el                         |    2 +
 lisp/emacs-lisp/comp-cstr.el                       |    2 +-
 lisp/emacs-lisp/comp.el                            |  200 +-
 lisp/emacs-lisp/crm.el                             |    2 +-
 lisp/emacs-lisp/eldoc.el                           |  127 +-
 lisp/emacs-lisp/gv.el                              |    7 +-
 lisp/emacs-lisp/hierarchy.el                       |   85 +-
 lisp/emacs-lisp/icons.el                           |   25 +-
 lisp/emacs-lisp/loaddefs-gen.el                    |   15 +-
 lisp/emacs-lisp/map.el                             |  164 +-
 lisp/emacs-lisp/memory-report.el                   |    7 +-
 lisp/emacs-lisp/multisession.el                    |   11 +
 lisp/emacs-lisp/oclosure.el                        |    6 +-
 lisp/emacs-lisp/package-vc.el                      |  726 ++
 lisp/emacs-lisp/package.el                         |  288 +-
 lisp/emacs-lisp/re-builder.el                      |   31 +-
 lisp/emacs-lisp/smie.el                            |    2 +-
 lisp/emacs-lisp/tabulated-list.el                  |    2 +-
 lisp/emacs-lisp/text-property-search.el            |   10 +-
 lisp/erc/ChangeLog.1                               |    8 +-
 lisp/erc/erc-backend.el                            |  129 +-
 lisp/erc/erc-capab.el                              |    2 +-
 lisp/erc/erc-common.el                             |  271 +
 lisp/erc/erc-compat.el                             |   12 +
 lisp/erc/erc-dcc.el                                |    7 +-
 lisp/erc/erc-goodies.el                            |   17 +-
 lisp/erc/erc-networks.el                           |   30 +-
 lisp/erc/erc.el                                    |  365 +-
 lisp/eshell/em-tramp.el                            |   94 +-
 lisp/eshell/em-unix.el                             |    4 +-
 lisp/eshell/esh-cmd.el                             |    9 +-
 lisp/eshell/esh-ext.el                             |   23 +-
 lisp/eshell/esh-proc.el                            |   18 +-
 lisp/eshell/esh-util.el                            |   68 +-
 lisp/eshell/esh-var.el                             |  156 +-
 lisp/face-remap.el                                 |   30 +-
 lisp/files-x.el                                    |  118 +-
 lisp/files.el                                      |   13 +-
 lisp/follow.el                                     |    2 +-
 lisp/gnus/ChangeLog.1                              |    2 +-
 lisp/gnus/ChangeLog.3                              |    4 +-
 lisp/gnus/gnus-art.el                              |    1 -
 lisp/gnus/gnus-bookmark.el                         |    2 +-
 lisp/gnus/gnus-cus.el                              |    5 +-
 lisp/gnus/gnus-start.el                            |    2 +-
 lisp/gnus/message.el                               |   16 +-
 lisp/gnus/mm-bodies.el                             |   20 +-
 lisp/gnus/mm-uu.el                                 |    2 +-
 lisp/gnus/nndoc.el                                 |    2 +-
 lisp/gnus/nnimap.el                                |    2 +-
 lisp/gnus/smime.el                                 |    2 +-
 lisp/help-fns.el                                   |    2 +-
 lisp/help-macro.el                                 |   12 +-
 lisp/help.el                                       |  261 +-
 lisp/icomplete.el                                  |    4 +-
 lisp/ido.el                                        |    2 +-
 lisp/image/image-dired-external.el                 |    2 +-
 lisp/info-look.el                                  |    1 +
 lisp/info.el                                       |    5 +-
 lisp/international/emoji.el                        |    3 +-
 lisp/international/mule-cmds.el                    |   93 +-
 lisp/international/mule-diag.el                    |    2 +-
 lisp/international/textsec.el                      |    2 +-
 lisp/jka-compr.el                                  |    2 +-
 lisp/language/ind-util.el                          |    4 +-
 lisp/language/misc-lang.el                         |    2 +-
 lisp/ldefs-boot.el                                 |  181 +-
 lisp/leim/quail/indian.el                          |  163 +-
 lisp/leim/quail/misc-lang.el                       |    2 +-
 lisp/leim/quail/slovak.el                          |  125 +-
 lisp/loadup.el                                     |   10 +-
 lisp/mail/feedmail.el                              |   11 +-
 lisp/mail/mail-hist.el                             |   23 +-
 lisp/mail/rmail.el                                 |   10 +-
 lisp/mail/rmailsum.el                              |   96 +-
 lisp/mail/sendmail.el                              |    8 +-
 lisp/man.el                                        |   83 +-
 lisp/menu-bar.el                                   |   10 +-
 lisp/mh-e/ChangeLog.1                              |    2 +-
 lisp/mh-e/mh-junk.el                               |    4 +-
 lisp/minibuffer.el                                 |   99 +-
 lisp/net/ange-ftp.el                               |    4 +-
 lisp/net/dbus.el                                   |    1 +
 lisp/net/dictionary.el                             |   12 +-
 lisp/net/eudc-vars.el                              |   16 +-
 lisp/net/eudc.el                                   |   85 +-
 lisp/net/eudcb-ecomplete.el                        |  108 +
 lisp/net/eudcb-mailabbrev.el                       |  127 +
 lisp/net/eww.el                                    |    6 +-
 lisp/net/ldap.el                                   |   13 +-
 lisp/net/network-stream.el                         |    4 +
 lisp/net/newst-backend.el                          |    1 -
 lisp/net/rcirc.el                                  |   19 +-
 lisp/net/sieve-manage.el                           |    2 +-
 lisp/net/tramp-cache.el                            |   27 +-
 lisp/net/tramp-container.el                        |   25 +-
 lisp/net/tramp-integration.el                      |   23 +-
 lisp/net/tramp-sh.el                               |   48 +-
 lisp/net/tramp-sudoedit.el                         |   57 +-
 lisp/net/tramp.el                                  |   32 +-
 lisp/nxml/rng-cmpct.el                             |   22 +-
 lisp/nxml/rng-uri.el                               |    8 +-
 lisp/org/ChangeLog.1                               |    8 +-
 lisp/org/ob-matlab.el                              |    2 +-
 lisp/org/ob-plantuml.el                            |    2 +-
 lisp/org/org-agenda.el                             |    2 +-
 lisp/org/org-ctags.el                              |    4 +-
 lisp/org/org-protocol.el                           |    3 +-
 lisp/org/ox-ascii.el                               |    2 +-
 lisp/outline.el                                    |  278 +-
 lisp/play/zone.el                                  |   38 +-
 lisp/printing.el                                   |    6 +-
 lisp/profiler.el                                   |  131 +-
 lisp/progmodes/antlr-mode.el                       |    8 +-
 lisp/progmodes/cc-bytecomp.el                      |    2 +-
 lisp/progmodes/cc-defs.el                          |    3 +-
 lisp/progmodes/cc-engine.el                        |  182 +-
 lisp/progmodes/cc-fonts.el                         |  319 +-
 lisp/progmodes/cc-langs.el                         |  236 +-
 lisp/progmodes/cc-mode.el                          |   19 +-
 lisp/progmodes/compile.el                          |    2 +-
 lisp/progmodes/cperl-mode.el                       |   68 +-
 lisp/progmodes/cpp.el                              |  101 +-
 lisp/progmodes/dcl-mode.el                         |   60 +-
 lisp/progmodes/ebnf2ps.el                          |    2 -
 lisp/progmodes/eglot.el                            | 3477 ++++++++++
 lisp/progmodes/elisp-mode.el                       |    4 +-
 lisp/progmodes/etags.el                            |   22 +-
 lisp/progmodes/flymake.el                          |    5 +-
 lisp/progmodes/fortran.el                          |   59 +-
 lisp/progmodes/gdb-mi.el                           |    2 +-
 lisp/progmodes/gud.el                              |    7 +-
 lisp/progmodes/hideshow.el                         |  106 +-
 lisp/progmodes/make-mode.el                        |  298 +-
 lisp/progmodes/mixal-mode.el                       |    3 -
 lisp/progmodes/modula2.el                          |   63 +-
 lisp/progmodes/octave.el                           |   72 +-
 lisp/progmodes/perl-mode.el                        |   13 +-
 lisp/progmodes/project.el                          |   43 +-
 lisp/progmodes/prolog.el                           |    2 +-
 lisp/progmodes/ps-mode.el                          |   50 +-
 lisp/progmodes/python.el                           |   44 +-
 lisp/progmodes/ruby-mode.el                        |    4 +-
 lisp/progmodes/simula.el                           |   42 +-
 lisp/progmodes/sql.el                              |   62 +-
 lisp/progmodes/verilog-mode.el                     |    4 +-
 lisp/progmodes/xref.el                             |    2 +-
 lisp/replace.el                                    |    2 +-
 lisp/rot13.el                                      |   11 +-
 lisp/savehist.el                                   |    6 +-
 lisp/simple.el                                     |   33 +-
 lisp/startup.el                                    |    8 +-
 lisp/subr.el                                       |   47 +-
 lisp/tab-bar.el                                    |  292 +-
 lisp/tab-line.el                                   |   37 +-
 lisp/textmodes/css-mode.el                         |   38 +-
 lisp/textmodes/emacs-news-mode.el                  |    9 +-
 lisp/textmodes/flyspell.el                         |    4 +-
 lisp/textmodes/less-css-mode.el                    |    2 +-
 lisp/textmodes/page-ext.el                         |   38 +-
 lisp/textmodes/sgml-mode.el                        |    4 +-
 lisp/textmodes/table.el                            |    9 +-
 lisp/thingatpt.el                                  |    2 +-
 lisp/thread.el                                     |   26 +-
 lisp/transient.el                                  |  209 +-
 lisp/url/url-util.el                               |   37 +-
 lisp/url/url.el                                    |    2 +-
 lisp/vc/smerge-mode.el                             |    5 +-
 lisp/vc/vc-bzr.el                                  |    6 +
 lisp/vc/vc-git.el                                  |   50 +-
 lisp/vc/vc-hg.el                                   |   42 +-
 lisp/vc/vc-svn.el                                  |    9 +-
 lisp/vc/vc.el                                      |  112 +-
 lisp/vcursor.el                                    |   82 +-
 lisp/whitespace.el                                 |  287 +-
 lisp/winner.el                                     |    8 +
 lisp/x-dnd.el                                      |   15 +-
 lisp/xwidget.el                                    |   12 +-
 nt/INSTALL                                         |   24 +-
 nt/INSTALL.W64                                     |    2 +-
 nt/inc/ms-w32.h                                    |    2 +-
 oldXMenu/Activate.c                                |   10 -
 oldXMenu/XMenu.h                                   |    2 -
 src/ChangeLog.13                                   |    6 +-
 src/ChangeLog.7                                    |    8 +-
 src/Makefile.in                                    |   39 +-
 src/alloc.c                                        |   60 +-
 src/buffer.c                                       | 1539 ++---
 src/buffer.h                                       |  111 +-
 src/callproc.c                                     |   32 +-
 src/comp.c                                         |   17 +-
 src/dired.c                                        |    2 +-
 src/dispnew.c                                      |    9 +
 src/editfns.c                                      |   67 +-
 src/emacs-module.c                                 |    2 +-
 src/emacs.c                                        |   11 +-
 src/eval.c                                         |   40 +-
 src/fileio.c                                       |    5 +-
 src/fns.c                                          |  156 +-
 src/gnutls.c                                       |   12 +-
 src/haiku_support.cc                               |   18 +
 src/haikuterm.c                                    |   32 +-
 src/haikuterm.h                                    |    5 +
 src/image.c                                        |   30 +-
 src/indent.c                                       |   13 +-
 src/insdel.c                                       |   69 +-
 src/intervals.c                                    |    4 +-
 src/itree.c                                        | 1449 ++++
 src/itree.h                                        |  186 +
 src/keyboard.c                                     |   16 +-
 src/lisp.h                                         |   23 +-
 src/lread.c                                        |    6 +-
 src/menu.c                                         |   12 +
 src/msdos.h                                        |    1 -
 src/nsimage.m                                      |    2 +
 src/nsterm.m                                       |   43 +-
 src/pdumper.c                                      |   68 +-
 src/pgtkterm.c                                     |  257 +-
 src/pgtkterm.h                                     |   11 +-
 src/print.c                                        |   18 +-
 src/process.c                                      |    3 +-
 src/regex-emacs.c                                  |   14 +-
 src/search.c                                       |   37 +-
 src/sqlite.c                                       |   98 +-
 src/sysdep.c                                       |    2 +-
 src/textprop.c                                     |   57 +-
 src/w32fns.c                                       |    5 +-
 src/window.c                                       |   17 +-
 src/window.h                                       |   10 +
 src/xdisp.c                                        |  290 +-
 src/xfaces.c                                       |   17 +-
 src/xfns.c                                         |  139 +-
 src/xmenu.c                                        |   67 +-
 src/xselect.c                                      |   39 +-
 src/xsettings.c                                    |   29 +-
 src/xterm.c                                        | 1417 +++-
 src/xterm.h                                        |   85 +-
 test/lisp/apropos-tests.el                         |   17 +-
 test/lisp/autorevert-tests.el                      |    2 +-
 test/lisp/calendar/icalendar-tests.el              |    2 +-
 test/lisp/cedet/semantic-utest.el                  |    1 -
 test/lisp/dired-tests.el                           |   11 +
 test/lisp/dnd-tests.el                             |    2 +-
 test/lisp/elide-head-tests.el                      |   21 +-
 test/lisp/emacs-lisp/bindat-tests.el               |   19 +-
 test/lisp/emacs-lisp/cconv-tests.el                |   17 +-
 test/lisp/emacs-lisp/cl-extra-tests.el             |   24 +-
 test/lisp/emacs-lisp/cl-generic-tests.el           |   22 +
 test/lisp/emacs-lisp/cl-macs-tests.el              |    6 +
 test/lisp/emacs-lisp/comp-tests.el                 |   77 +
 test/lisp/emacs-lisp/gv-tests.el                   |   75 +-
 test/lisp/emacs-lisp/hierarchy-tests.el            |  143 +
 test/lisp/emacs-lisp/map-tests.el                  |  204 +-
 test/lisp/erc/erc-dcc-tests.el                     |  119 +-
 test/lisp/erc/erc-networks-tests.el                |    2 +-
 test/lisp/erc/erc-services-tests.el                |   24 +-
 test/lisp/erc/erc-tests.el                         |   22 -
 test/lisp/eshell/em-tramp-tests.el                 |   75 +
 test/lisp/eshell/esh-ext-tests.el                  |   76 +
 test/lisp/eshell/esh-util-tests.el                 |   57 +
 test/lisp/eshell/esh-var-tests.el                  |  222 +-
 test/lisp/eshell/eshell-tests-helpers.el           |   45 +-
 test/lisp/files-x-tests.el                         |  152 +-
 test/lisp/image/wallpaper-tests.el                 |    7 +-
 test/lisp/international/textsec-tests.el           |    2 +-
 test/lisp/net/eudc-resources/ecompleterc           |    7 +
 test/lisp/net/eudc-resources/mailrc                |    3 +
 test/lisp/net/eudc-tests.el                        |  271 +
 test/lisp/net/puny-resources/IdnaTestV2.txt        |    4 +-
 test/lisp/net/tramp-tests.el                       |   11 +-
 .../progmodes/cperl-mode-resources/here-docs.pl    |   66 +
 test/lisp/progmodes/python-tests.el                |  158 +-
 test/lisp/progmodes/ruby-mode-resources/ruby.rb    |    4 +-
 test/lisp/simple-tests.el                          |   23 +
 test/lisp/subr-tests.el                            |    5 +-
 test/lisp/thingatpt-tests.el                       |    3 +
 test/lisp/time-stamp-tests.el                      |    8 +-
 test/manual/image-circular-tests.el                |   41 +-
 test/manual/noverlay/.gitignore                    |    1 +
 test/manual/noverlay/Makefile.in                   |   47 +
 test/manual/noverlay/check-sanitize.sh             |   33 +
 test/manual/noverlay/emacs-compat.h                |   76 +
 test/manual/noverlay/itree-tests.c                 | 1289 ++++
 test/manual/noverlay/many-errors.py                | 2480 +++++++
 test/manual/noverlay/overlay-perf.el               |  764 +++
 test/src/buffer-tests.el                           | 7112 +++++++++++++++++++-
 test/src/comp-tests.el                             |    2 +-
 test/src/emacs-module-tests.el                     |    2 +-
 test/src/fns-tests.el                              |   90 +-
 test/src/lcms-tests.el                             |    2 +-
 test/src/regex-emacs-tests.el                      |    5 +
 455 files changed, 30974 insertions(+), 6832 deletions(-)

diff --git a/.clang-format b/.clang-format
index 44200a3995..464375bd41 100644
--- a/.clang-format
+++ b/.clang-format
@@ -6,7 +6,10 @@ BreakBeforeBinaryOperators: All
 BreakBeforeBraces: GNU
 ColumnLimit: 70
 ContinuationIndentWidth: 2
-ForEachMacros: [FOR_EACH_TAIL, FOR_EACH_TAIL_SAFE]
+ForEachMacros: [FOR_EACH_TAIL,
+                FOR_EACH_TAIL_SAFE,
+                FOR_EACH_LIVE_BUFFER,
+                ITREE_FOREACH]
 IncludeCategories:
   - Regex: '^<config\.h>$'
     Priority: -1
@@ -21,6 +24,7 @@ MaxEmptyLinesToKeep: 1
 PenaltyBreakBeforeFirstCallParameter: 2000
 SpaceAfterCStyleCast: true
 SpaceBeforeParens: Always
+UseTab: Always
 
 # Local Variables:
 # mode: yaml
diff --git a/.dir-locals.el b/.dir-locals.el
index 84617a7980..a85769b534 100644
--- a/.dir-locals.el
+++ b/.dir-locals.el
@@ -1,4 +1,4 @@
-;;; Directory Local Variables
+;;; Directory Local Variables         -*- no-byte-compile: t; -*-
 ;;; For more information see (info "(emacs) Directory Variables")
 
 ((nil . ((tab-width . 8)
@@ -9,7 +9,8 @@
          (bug-reference-url-format . "https://debbugs.gnu.org/%s";)
         (diff-add-log-use-relative-names . t)))
  (c-mode . ((c-file-style . "GNU")
-            (c-noise-macro-names . ("INLINE" "ATTRIBUTE_NO_SANITIZE_UNDEFINED" 
"UNINIT" "CALLBACK" "ALIGN_STACK"))
+            (c-noise-macro-names . ("INLINE" "NO_INLINE" 
"ATTRIBUTE_NO_SANITIZE_UNDEFINED"
+                                    "UNINIT" "CALLBACK" "ALIGN_STACK"))
             (electric-quote-comment . nil)
             (electric-quote-string . nil)
             (indent-tabs-mode . t)
diff --git a/.gitignore b/.gitignore
index c10b3b33d3..a7828d3386 100644
--- a/.gitignore
+++ b/.gitignore
@@ -324,7 +324,7 @@ lib-src/seccomp-filter-exec.pfc
 /etc/*.gschema.valid
 
 # Ignore directory made by admin/make-manuals.
-manual/
+/manual/
 
 # Ignore Finder files on MacOS.
 .DS_Store
diff --git a/CONTRIBUTE b/CONTRIBUTE
index 94d757daaf..c226645bd7 100644
--- a/CONTRIBUTE
+++ b/CONTRIBUTE
@@ -202,9 +202,10 @@ them right the first time, so here are guidelines for 
formatting them:
   you can put a paragraph (after the empty line and before the
   individual ChangeLog entries) that further describes the commit.
 
-- Limit lines in commit messages to 78 characters, unless they consist
-  of a single word of at most 140 characters; this is enforced by a
-  commit hook.
+- Lines in ChangeLog entries should preferably be not longer than 63
+  characters, and must not exceed 78 characters, unless they consist
+  of a single word of at most 140 characters; this 78/140 limit is
+  enforced by a commit hook.
 
 - If only a single file is changed, the summary line can be the normal
   file first line (starting with the asterisk).  Then there is no
diff --git a/ChangeLog.1 b/ChangeLog.1
index cd31aacafb..a8df1c0420 100644
--- a/ChangeLog.1
+++ b/ChangeLog.1
@@ -930,7 +930,7 @@
        (mostlyclean_dirs, clean_dirs, distclean_dirs, maintainer_clean_dirs):
        New variables.
        (mostlyclean, clean, distclean, bootstrap-clean, maintainer-clean)
-       (extraclean): Define using each subdirectory as a prequisite.
+       (extraclean): Define using each subdirectory as a prerequisite.
        * lib/Makefile.am (bootstrap-clean): New.
 
 2014-06-15  Paul Eggert  <eggert@cs.ucla.edu>
@@ -5685,7 +5685,7 @@
        (__mktime_internal): Use it systematically for all isdst comparisons.
        This completes the fix for libc BZ #6723, and removes the need for
        normalizing tm_isdst.
-       See <http://sourceware.org/bugzilla/show_bug.cgi?id=6723>
+       See <https://sourceware.org/bugzilla/show_bug.cgi?id=6723>
        (not_equal_tm) [DEBUG]: Use isdst_differ here, too.
 
        mktime: fix some integer overflow issues and sidestep the rest
diff --git a/ChangeLog.2 b/ChangeLog.2
index 5a73d53b8b..fc038033ec 100644
--- a/ChangeLog.2
+++ b/ChangeLog.2
@@ -26343,7 +26343,7 @@
        (verilog-type-font-keywords): Cycle delay operators like ##1 and
        ##[0:$] are now highlighted in their entirety similarly to the #
        delay-control operator.  Likewise, the followed-by operators #-#
-       and #=# are no longer partially highlighed.
+       and #=# are no longer partially highlighted.
        (verilog-backward-syntactic-ws-quick)
        (verilog-skip-backward-comments): Minor performance improvements
        to buffer traversal functions for reduced latency.
diff --git a/ChangeLog.3 b/ChangeLog.3
index a09dc29cbe..d90b261da7 100644
--- a/ChangeLog.3
+++ b/ChangeLog.3
@@ -5731,7 +5731,7 @@
 
 2021-10-09  Dmitry Gutov  <dgutov@yandex.ru>
 
-       Slight simplificaiton
+       Slight simplification
 
        * lisp/progmodes/xref.el (xref--insert-xrefs):
        Compute log only once.  Use 'dolist'.
@@ -35728,7 +35728,7 @@
        (comp-arithm-cmp-fun-p, comp-negate-arithm-cmp-fun)
        (comp-reverse-arithm-fun): Rename and add '=' '!='.
        (comp-emit-assume, comp-add-cond-cstrs, comp-fwprop-insn): Update
-       for new function nameing and to handle '='.
+       for new function naming and to handle '='.
        * lisp/emacs-lisp/comp-cstr.el (comp-cstr-=): New function.
        * test/src/comp-tests.el (comp-tests-type-spec-tests): Add a bunch
        of '=' specific tests.
@@ -39854,7 +39854,7 @@
 
        * lisp/net/mairix.el: Use lexical-binding.
        Remove redundant `:group` args.
-       (mairix-widget-create-query): Remove unnused var `allwidgets`.
+       (mairix-widget-create-query): Remove unused var `allwidgets`.
 
 2021-02-09  Juri Linkov  <juri@linkov.net>
 
@@ -49106,7 +49106,7 @@
        (comp-split-pos-neg): Minor.
        (comp-normalize-typeset): Logic update.
        (comp-union-typesets): Minor.
-       (comp-intersect-two-typesets): New functio.
+       (comp-intersect-two-typesets): New function.
        (comp-intersect-typesets): Logic update.
        (comp-range-union, comp-range-intersection): Minor.
        (comp-cstr-union-homogeneous, comp-cstr-union-1-no-mem)
@@ -50911,7 +50911,7 @@
 
 2020-12-12  Lars Ingebrigtsen  <larsi@gnus.org>
 
-       Alter the "Redundant pcase patter" warning message
+       Alter the "Redundant pcase pattern" warning message
 
        * lisp/emacs-lisp/pcase.el (pcase--expand): Make the "Redundant
        pcase pattern" warning less vague (bug#31350).
@@ -77634,7 +77634,7 @@
 
        Add an initial implementation to support dynamic scope.  Arg
        parsing/binding it's done using the existing code in use for
-       bytecode (no ad-hoc code is synthetized for that).
+       bytecode (no ad-hoc code is synthesized for that).
 
        * src/lisp.h (struct Lisp_Subr): Add lambda_list field.
        (SUBR_NATIVE_COMPILED_DYNP): New inliner.
@@ -118316,7 +118316,7 @@
 
        * lisp/mh-e/mh-speed.el (mh-speed-parse-flists-output):
        * lisp/mh-e/mh-search.el (mh-index-parse-search-regexp): Avoid
-       warning about `values-list' by using `cl-values-list' insead.
+       warning about `values-list' by using `cl-values-list' instead.
 
 2019-07-29  Lars Ingebrigtsen  <larsi@gnus.org>
 
@@ -126255,7 +126255,7 @@
 
        Suppress warning about non-prefixed variable in mailalias.el
 
-       * lisp/mail/mailalias.el (patters): Suppress warning about
+       * lisp/mail/mailalias.el (pattern): Suppress warning about
        non-prefixed variable used by `mail-complete-alist'.
 
 2019-06-15  Lars Ingebrigtsen  <larsi@gnus.org>
@@ -130524,7 +130524,7 @@
 
 2019-05-16  Lars Ingebrigtsen  <larsi@gnus.org>
 
-       Avoind string-as-multibyte in ps-output-string-prim
+       Avoid string-as-multibyte in ps-output-string-prim
 
        * lisp/ps-print.el (ps-output-string-prim): Avoid
        `string-as-multibyte', and encode as utf-8 instead if multibyte.
@@ -156879,7 +156879,7 @@
        Merge from origin/emacs-26
 
        90bea37 ; * etc/PROBLEMS: Fix fvwm version number in last commit
-       af82d1f * etc/PROBLEMS: Document stickyness problem with FVWM (Bug#31...
+       af82d1f * etc/PROBLEMS: Document stickiness problem with FVWM (Bug#31...
        4a3aed2 Update Emacs Lisp Intro to match current behavior
        21f2247 Merge branch 'emacs-26' of git.savannah.gnu.org:/srv/git/emac...
        3257085 Fix previous commit
@@ -176909,7 +176909,7 @@
 
 2018-06-02  Martin Rudalics  <rudalics@gmx.at>
 
-       * etc/PROBLEMS: Document stickyness problem with FVWM (Bug#31650)
+       * etc/PROBLEMS: Document stickiness problem with FVWM (Bug#31650)
 
 2018-06-01  Eli Zaretskii  <eliz@gnu.org>
 
@@ -185373,7 +185373,7 @@
 
        * lisp/image.el (image-type-header-regexps):
        Allow two or more CRs or LFs in initial whitespace sequences.  See:
-       http://netpbm.sourceforge.net/doc/pbm.html
+       https://netpbm.sourceforge.net/doc/pbm.html
 
 2017-10-16  Paul Eggert  <eggert@cs.ucla.edu>
 
@@ -197097,7 +197097,7 @@
        Clang on macOS warns about these with -Wtautological-compare.  POSIX
        guarantees that rlim_t is
        unsigned (cf.
-       
http://pubs.opengroup.org/onlinepubs/009695399/basedefs/sys/resource.h.html),
+       
https://pubs.opengroup.org/onlinepubs/009695399/basedefs/sys/resource.h.html),
        so these resource limits can never be negative.
 
        * src/emacs.c (main): Remove tautological comparisons.
@@ -199197,7 +199197,7 @@
 
        ' is commonly used as an apostrophe in the prose sections of spec
        files, which was erroneously highlighted as strings. See for example
-       http://kmymoney2.sourceforge.net/phb/rpm-example.html
+       https://kmymoney2.sourceforge.net/phb/rpm-example.html
 
        * lisp/progmodes/sh-script.el (sh-mode-syntax-table): Treat ' as
          punctuation in RPM spec files.
@@ -221327,8 +221327,8 @@
        is never used.  Hardcode the syntax so that the compilar can detect such
        dead code and remove it from compiled code.
 
-       The only exception is RE_NO_POSIX_BACKTRACKING which can be separatelly
-       specified.  Handle this separatelly with a function argument (replacing
+       The only exception is RE_NO_POSIX_BACKTRACKING which can be separately
+       specified.  Handle this separately with a function argument (replacing
        now unnecessary syntax argument).
 
        With this patchset, size of Emacs binary on x86_64 machine is reduced by
@@ -227801,7 +227801,7 @@
 
        * lisp/auth-source.el
        (auth-source-macos-keychain-search-items): Handle keychain
-       output correctly when has special chararcters (bug#22824).
+       output correctly when has special characters (bug#22824).
 
 2016-04-24  Magnus Henoch  <magnus.henoch@gmail.com>
 
diff --git a/INSTALL b/INSTALL
index 95d2dbda80..c0323f770b 100644
--- a/INSTALL
+++ b/INSTALL
@@ -187,7 +187,7 @@ X11 is being used.
     libz (for PNG):   https://www.zlib.net/
   X libjpeg for JPEG: https://www.ijg.org/
   X libtiff for TIFF: http://www.simplesystems.org/libtiff/
-  X libgif for GIF:   http://giflib.sourceforge.net/
+  X libgif for GIF:   https://giflib.sourceforge.net/
     librsvg2 for SVG: https://wiki.gnome.org/Projects/LibRsvg
     libwebp for WebP: https://developers.google.com/speed/webp/
 
diff --git a/Makefile.in b/Makefile.in
index 2d617e2294..45b4a59e3d 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -52,14 +52,14 @@
 # make bootstrap
 #      Removes all the compiled files to force a new bootstrap from a
 #      clean slate, and then build in the normal way.  If the FAST Make
-#      variable is set, then the config.cache file isn't removed.  This
-#      allows you to say
+#      variable is set, then the autom4te.cache directory and the
+#      config.cache file are not removed.  This lets you say
 #
 #      ./configure -C
 #      make FAST=true bootstrap
 #
 #      and use the cached results from the configure run, which is much
-#      faster.
+#      faster though it does not work in general.
 #
 # make docs
 #      Make Emacs documentation files from their sources; requires makeinfo.
@@ -1040,7 +1040,7 @@ bootstrap-clean: $(distclean_dirs:=_bootstrap-clean)
        rm -f ${srcdir}/etc/refcards/emacsver.tex
        rm -rf native-lisp/ lisp/leim/ja-dic/
 ifndef FAST
-       rm -f config.cache
+       rm -fr autom4te.cache config.cache
 endif
        ${top_bootclean}
 
diff --git a/admin/authors.el b/admin/authors.el
index 12fe25fa4e..fd8ba9cb01 100644
--- a/admin/authors.el
+++ b/admin/authors.el
@@ -990,7 +990,7 @@ in the repository.")
 ;; to how a file was mentioned in the respective ChangeLog.  It is
 ;; advisable to run a Grep command such as
 ;;
-;;   fgrep -R BASENAME . --include='ChangeLog*'
+;;   grep -F -R BASENAME . --include='ChangeLog*'
 ;;
 ;; where BASENAME is the old basename of the renamed file.  This will
 ;; show all the different reference forms of the file in the various
diff --git a/admin/automerge b/admin/automerge
index c7c17dfb5e..d2c92948e1 100755
--- a/admin/automerge
+++ b/admin/automerge
@@ -35,18 +35,7 @@
 ## it with the -d option in the repository directory, in case a pull
 ## updates this script while it is working.
 
-set -o nounset
-
-die ()                 # write error to stderr and exit
-{
-    [ $# -gt 0 ] && echo "$PN: $*" >&2
-    exit 1
-}
-
-PN=${0##*/}                     # basename of script
-PD=${0%/*}
-
-[ "$PD" = "$0" ] && PD=.        # if PATH includes PWD
+source "${0%/*}/emacs-shell-lib"
 
 usage ()
 {
@@ -129,13 +118,7 @@ OPTIND=1
 [ "$test" ] && build=1
 
 
-if [ -x "$(command -v mktemp)" ]; then
-    tempfile=$(mktemp "/tmp/$PN.XXXXXXXXXX")
-else
-    tempfile=/tmp/$PN.$$
-fi
-
-trap 'rm -f $tempfile 2> /dev/null' EXIT
+tempfile="$(emacs_mktemp)"
 
 
 [ -e Makefile ] && [ "$build" ] && {
@@ -263,5 +246,3 @@ git push || die "push error"
 
 
 exit 0
-
-### automerge ends here
diff --git a/admin/charsets/mapfiles/stdenc.txt 
b/admin/charsets/mapfiles/stdenc.txt
index e39486a319..1c898bac0b 100644
--- a/admin/charsets/mapfiles/stdenc.txt
+++ b/admin/charsets/mapfiles/stdenc.txt
@@ -54,7 +54,7 @@
 #    
 #    [v0.1, 5 May 1995] First release.
 #
-#  Use the Unicode reporting form <http://www.unicode.org/reporting.html>
+#  Use the Unicode reporting form <https://www.unicode.org/reporting.html>
 #    for any questions or comments or to report errors in the data.
 #
 0020   20      # SPACE # space
diff --git a/admin/charsets/mapfiles/symbol.txt 
b/admin/charsets/mapfiles/symbol.txt
index b98baf6cf0..0a5aac8b61 100644
--- a/admin/charsets/mapfiles/symbol.txt
+++ b/admin/charsets/mapfiles/symbol.txt
@@ -57,7 +57,7 @@
 #
 #    [v0.1, 5 May 1995] First release.
 #
-#  Use the Unicode reporting form <http://www.unicode.org/reporting.html>
+#  Use the Unicode reporting form <https://www.unicode.org/reporting.html>
 #    for any questions or comments or to report errors in the data.
 #
 0020   20      # SPACE # space
diff --git a/admin/diff-tar-files b/admin/diff-tar-files
index 6ab39eab2f..869c942150 100755
--- a/admin/diff-tar-files
+++ b/admin/diff-tar-files
@@ -1,4 +1,4 @@
-#! /bin/sh
+#!/bin/bash
 
 # Copyright (C) 2001-2022 Free Software Foundation, Inc.
 
@@ -17,6 +17,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/>.
 
+source "${0%/*}/emacs-shell-lib"
 
 if [ $# != 2 ]; then
     cat <<EOF
@@ -31,9 +32,8 @@ fi
 old_tar=$1
 new_tar=$2
 
-old_tmp=/tmp/old.$$
-new_tmp=/tmp/new.$$
-trap "rm -f $old_tmp $new_tmp; exit 1" 1 2 15
+old_tmp="$(emacs_mktemp ${PN}-old)"
+new_tmp="$(emacs_mktemp ${PN}-new)"
 
 tar tf "$old_tar" | sed -e 's,^[^/]*,,' | sort > $old_tmp
 tar tf "$new_tar" | sed -e 's,^[^/]*,,' | sort > $new_tmp
diff --git a/admin/emacs-shell-lib b/admin/emacs-shell-lib
new file mode 100644
index 0000000000..750f81e057
--- /dev/null
+++ b/admin/emacs-shell-lib
@@ -0,0 +1,87 @@
+#!/bin/bash
+### emacs-shell-lib - shared code for Emacs shell scripts
+
+## Copyright (C) 2022 Free Software Foundation, Inc.
+
+## Author: Stefan Kangas <stefankangas@gmail.com>
+
+## 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:
+
+# Set an explicit umask.
+umask 077
+
+# Treat unset variables as an error.
+set -o nounset
+
+# Exit immediately on error.
+set -o errexit
+
+# Avoid non-standard command output from non-C locales.
+unset LANG LC_ALL LC_MESSAGES
+
+PN=${0##*/}                     # basename of script
+PD=${0%/*}                      # script directory
+
+[ "$PD" = "$0" ] && PD=.        # if PATH includes PWD
+
+die ()                 # write error to stderr and exit
+{
+    [ $# -gt 0 ] && echo "$PN: $@" >&2
+    exit 1
+}
+
+emacs_tempfiles=()
+
+emacs_tempfiles_cleanup ()
+{
+    for file in ${emacs_tempfiles[@]}; do
+        rm -f "${file}" 2> /dev/null
+    done
+}
+
+trap '
+  ret=$?
+  emacs_tempfiles_cleanup
+  exit $ret
+' EXIT
+
+emacs_mktemp ()
+{
+    local readonly file="${1-}"
+    local tempfile
+    local prefix
+
+    if [ -z "$file" ]; then
+        prefix="$PN"
+    else
+        prefix="$1"
+    fi
+
+    if [ -x "$(command -v mktemp)" ]; then
+        tempfile=$(mktemp "${TMPDIR-/tmp}/${prefix}.XXXXXXXXXX")
+    else
+        tempfile="${TMPDIR-/tmp}/${prefix}.$RANDOM$$"
+        (umask 077 && touch "$tempfile")
+    fi
+
+    [ -z "${tempfile}" ] && die "Creating temporary file failed"
+
+    emacs_tempfiles+=("${tempfile}")
+
+    echo "$tempfile"
+}
diff --git a/admin/emake b/admin/emake
index e2f38501e9..09f7410779 100755
--- a/admin/emake
+++ b/admin/emake
@@ -1,5 +1,22 @@
 #!/bin/bash
 
+# Copyright (C) 2022 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/>.
+
 # This script is meant to be used as ./admin/emake, and will compile
 # the Emacs tree with virtually all of the informational messages
 # removed, and with errors/warnings highlighted in red.  It'll give a
diff --git a/admin/git-bisect-start b/admin/git-bisect-start
new file mode 100755
index 0000000000..cf0c8cde41
--- /dev/null
+++ b/admin/git-bisect-start
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+### Start a git bisection, and prune the branches that are the result of
+### merging external trees into the Emacs repository.
+
+## Copyright (C) 2022 Free Software Foundation, Inc.
+
+## This file is part of GNU Emacs.
+
+## GNU Emacs is free software: you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+
+## GNU Emacs is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+
+## You should have received a copy of the GNU General Public License
+## along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+### Commentary:
+
+## Marking the last commits of external tree merges as "good" ensures
+## that all the commits between the external tree root and the merge
+## (excluding the merge-commit itself) are regarded as "good", so "git
+## bisect" will never descend into these branches, which only have the
+## files of the external tree, and in which Emacs can therefore not be
+## built.  The last commit is the parent of the merge commit in the
+## external tree, that is, the parent of the merge commit that is not
+## on master.
+
+### Code:
+
+git bisect start
+
+# Prune commits 1e5b753bf4..806734c1b1 introduced by 0186faf2a1 (Eglot
+# merge on Oct 20 2022)
+git bisect good 806734c1b1f433de43d59d9a5e3a1e89d64315f6
diff --git a/admin/make-manuals b/admin/make-manuals
index cb0c00a423..a252bf20f1 100755
--- a/admin/make-manuals
+++ b/admin/make-manuals
@@ -33,15 +33,7 @@
 
 ### Code:
 
-set -o nounset
-
-die ()                          # write error to stderr and exit
-{
-    [ $# -gt 0 ] && echo "$PN: $@" >&2
-    exit 1
-}
-
-PN=${0##*/}                     # basename of script
+source "${0%/*}/emacs-shell-lib"
 
 usage ()
 {
@@ -96,8 +88,7 @@ OPTIND=1
 [ -e admin/admin.el ] || die "admin/admin.el not found"
 
 
-tempfile=/tmp/$PN.$$
-trap "rm -f $tempfile 2> /dev/null" EXIT
+tempfile="$(emacs_mktemp)"
 
 
 [ "$continue" ] || rm -rf $outdir
diff --git a/admin/notes/repo b/admin/notes/repo
index c2d7f993a0..97f02ab605 100644
--- a/admin/notes/repo
+++ b/admin/notes/repo
@@ -42,6 +42,24 @@ yet another fun excursion into the exciting world of version 
control.
 
 https://lists.gnu.org/r/emacs-devel/2010-04/msg00086.html
 
+* feature and scratch branches
+
+Besides the master branch, which is where development takes place, and
+the "emacs-NN" release branches, we also have branches whose names
+start with "scratch/" and "feature/".  The "feature/" prefix is used
+for feature branches that are intended to live for some time, while
+"scratch/" is for one-off throw-away-after-use branches.
+
+We do not intend to "git merge" from scratch branches, so force-pushes
+are tolerated, as well as commits with poor style, incomplete commit
+messages, etc.
+
+We do expect to "git merge" from feature branches so: no force push,
+and no commits that don't have a proper commit message.
+
+Automatic tests are run for feature/* branches on EMBA.
+See: https://emba.gnu.org/emacs/emacs/-/pipelines
+
 * Installing changes from gnulib
 
 Some of the files in Emacs are copied from gnulib.  To synchronize
@@ -110,6 +128,10 @@ again.
 This is a semi-automated way to find the revision that introduced a bug.
 Browse 'git help bisect' for technical instructions.
 
+It is recommended to start a bisection with the admin/git-bisect-start
+script.  This script prunes the branches that are the result of
+merging external trees into the Emacs repository.
+
 * Maintaining ChangeLog history
 
 Older ChangeLog entries are kept in history files named ChangeLog.1,
diff --git a/admin/unidata/README b/admin/unidata/README
index 2da01402b7..2d421dfb6b 100644
--- a/admin/unidata/README
+++ b/admin/unidata/README
@@ -6,31 +6,31 @@ copyright.html.
 The names, URLs, and dates for these files are as follows.
 
 BidiBrackets.txt
-http://www.unicode.org/Public/UNIDATA/BidiBrackets.txt
+https://www.unicode.org/Public/UNIDATA/BidiBrackets.txt
 2021-06-30
 
 BidiMirroring.txt
-http://www.unicode.org/Public/UNIDATA/BidiMirroring.txt
+https://www.unicode.org/Public/UNIDATA/BidiMirroring.txt
 2021-08-08
 
 Blocks.txt
-http://www.unicode.org/Public/8.0.0/ucd/Blocks.txt
+https://www.unicode.org/Public/8.0.0/ucd/Blocks.txt
 2021-01-22
 
 IVD_Sequences.txt
-http://www.unicode.org/ivd/
+https://www.unicode.org/ivd/
 2020-11-06
 
 NormalizationTest.txt
-http://www.unicode.org/Public/UNIDATA/NormalizationTest.txt
+https://www.unicode.org/Public/UNIDATA/NormalizationTest.txt
 2021-05-28
 
 SpecialCasing.txt
-http://unicode.org/Public/UNIDATA/SpecialCasing.txt
+https://unicode.org/Public/UNIDATA/SpecialCasing.txt
 2021-03-08
 
 UnicodeData.txt
-http://www.unicode.org/Public/UNIDATA/UnicodeData.txt
+https://www.unicode.org/Public/UNIDATA/UnicodeData.txt
 2021-07-06
 
 emoji-data.txt
diff --git a/admin/unidata/blocks.awk b/admin/unidata/blocks.awk
index 1c571feff3..48a14ec3ca 100755
--- a/admin/unidata/blocks.awk
+++ b/admin/unidata/blocks.awk
@@ -23,7 +23,7 @@
 ### Commentary:
 
 ## This script takes as input Unicode's Blocks.txt
-## (http://www.unicode.org/Public/UNIDATA/Blocks.txt)
+## (https://www.unicode.org/Public/UNIDATA/Blocks.txt)
 ## and produces output for Emacs's lisp/international/charscript.el.
 
 ## It lumps together all the blocks belonging to the same language.
diff --git a/admin/unidata/copyright.html b/admin/unidata/copyright.html
index 0ae01c11ad..567c54e72a 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="http://www.unicode.org/webscripts/standard_styles.css";>
+href="https://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="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="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="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,7 +112,7 @@ pre {
 
             <p>For the general privacy policy governing access to this site, 
see
             the&nbsp;
-            <a href="http://www.unicode.org/policies/privacy_policy.html";>
+            <a href="https://www.unicode.org/policies/privacy_policy.html";>
             Unicode Privacy Policy</a>.</p>
 
             <ol type="A">
@@ -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="http://www.unicode.org/versions/Unicode5.0.0/Title.pdf";>title 
page</a>.</li>
+                                <a 
href="https://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="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><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>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="http://www.unicode.org/copyright.html";>
-                  <img src="http://www.unicode.org/img/hb_notice.gif";
+                  <td><a href="https://www.unicode.org/copyright.html";>
+                  <img src="https://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="http://www.unicode.org/webscripts/lastModified.js";>
+              src="https://www.unicode.org/webscripts/lastModified.js";>
                 </script>
 
               </center>
diff --git a/admin/update_autogen b/admin/update_autogen
index d1f49d9f25..55e11be95c 100755
--- a/admin/update_autogen
+++ b/admin/update_autogen
@@ -32,18 +32,7 @@
 
 ### Code:
 
-set -o nounset
-
-die ()                 # write error to stderr and exit
-{
-    [ $# -gt 0 ] && echo "$PN: $@" >&2
-    exit 1
-}
-
-PN=${0##*/}                     # basename of script
-PD=${0%/*}
-
-[ "$PD" = "$0" ] && PD=.        # if PATH includes PWD
+source "${0%/*}/emacs-shell-lib"
 
 ## This should be the admin directory.
 cd $PD || exit
@@ -102,10 +91,7 @@ done
 
 [ "$basegen" ] || die "internal error"
 
-tempfile=/tmp/$PN.$$
-
-trap 'rm -f $tempfile 2> /dev/null' EXIT
-
+tempfile="$(emacs_mktemp)"
 
 while getopts ":hcfqA:CL" option ; do
     case $option in
@@ -312,5 +298,3 @@ commit "loaddefs" $modified || die "commit error"
 
 
 exit 0
-
-### update_autogen ends here
diff --git a/admin/upload-manuals b/admin/upload-manuals
index 50336ee64c..04f7c3acc7 100755
--- a/admin/upload-manuals
+++ b/admin/upload-manuals
@@ -36,15 +36,7 @@
 
 ### Code:
 
-set -o nounset
-
-die ()                          # write error to stderr and exit
-{
-    [ $# -gt 0 ] && echo "$PN: $@" >&2
-    exit 1
-}
-
-PN=${0##*/}                     # basename of script
+source "${0%/*}/emacs-shell-lib"
 
 usage ()
 {
diff --git a/configure.ac b/configure.ac
index 2d84344050..b656dba4d9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1170,6 +1170,13 @@ if test "$emacs_cv_clang" = yes; then
   gl_WARN_ADD([-Wno-tautological-constant-out-of-range-compare])
 fi
 
+# Suppress deprecation warnings from using sprintf variants,
+# starting with Xcode 14.1 on macOS 13.
+# These warnings are false alarms, as Emacs usage of sprintf is safe.
+if test $opsys = darwin; then
+  gl_WARN_ADD([-Wno-deprecated-declarations])
+fi
+
 # Use a slightly smaller set of warning options for lib/.
 nw=
 nw="$nw -Wunused-macros"
@@ -6708,6 +6715,7 @@ if test -f "$srcdir/$opt_makefile.in"; then
   dnl Again, it's best not to use a variable.  Though you can add
   dnl ", [], [opt_makefile='$opt_makefile']" and it should work.
   AC_CONFIG_FILES([test/Makefile])
+  AC_CONFIG_FILES([test/manual/noverlay/Makefile])
 fi
 opt_makefile=test/infra/Makefile
 if test -f "$srcdir/$opt_makefile.in"; then
diff --git a/doc/emacs/ChangeLog.1 b/doc/emacs/ChangeLog.1
index 048b7bd99a..5647538a24 100644
--- a/doc/emacs/ChangeLog.1
+++ b/doc/emacs/ChangeLog.1
@@ -2711,7 +2711,7 @@
        of list-faces-display here, from Standard Faces node.
        Note special role of `default' background.
        (Standard Faces): Note special role of `default' background.
-       Note that region face may be taken fom GTK.  Add xref to Text Display.
+       Note that region face may be taken from GTK.  Add xref to Text Display.
        (Text Scale): Rename from "Temporary Face Changes".
        Callers changed.  Don't bother documenting variable-pitch-mode.
        (Font Lock): Copyedits.  Remove font-lock-maximum-size.
diff --git a/doc/emacs/building.texi b/doc/emacs/building.texi
index b79fa0a755..93ad4145cc 100644
--- a/doc/emacs/building.texi
+++ b/doc/emacs/building.texi
@@ -537,10 +537,13 @@ C/C++ files this is usually the C compiler.  Flymake can 
also use
 build tools such as @code{make} for checking complicated projects.
 
   To enable Flymake mode, type @kbd{M-x flymake-mode}.  You can jump
-to the errors that it finds by using @kbd{M-x flymake-goto-next-error}
-and @kbd{M-x flymake-goto-prev-error}.  To display any error messages
-associated with the current line, type @kbd{M-x
-flymake-display-err-menu-for-current-line}.
+to the errors that it finds by using @w{@kbd{M-x
+flymake-goto-next-error}} and @w{@kbd{M-x flymake-goto-prev-error}}.
+To display a detailed overview of the diagnostics for the current
+buffer, use the command @w{@kbd{M-x flymake-show-buffer-diagnostics}};
+to display a similar overview of diagnostics for the entire project
+(@pxref{Projects}), use @w{@kbd{M-x
+flymake-show-project-diagnostics}}.
 
   For more details about using Flymake,
 @ifnottex
diff --git a/doc/emacs/custom.texi b/doc/emacs/custom.texi
index ff7ab83190..08ada2a70b 100644
--- a/doc/emacs/custom.texi
+++ b/doc/emacs/custom.texi
@@ -667,6 +667,16 @@ type @kbd{M-x disable-theme}.
 the @file{*Custom Themes*} buffer; or type @kbd{M-x describe-theme}
 anywhere in Emacs and enter the theme name.
 
+@findex theme-choose-variant
+Some themes have variants (most often just two: light and dark).  You
+can switch to another variant using @kbd{M-x theme-choose-variant}.
+If the currently active theme has only one other variant, it will be
+selected; if there are more variants, the command will prompt you
+which one to switch to.
+
+Note that @code{theme-choose-variant} only works if a single theme
+is active.
+
 @node Creating Custom Themes
 @subsection Creating Custom Themes
 @cindex custom themes, creating
@@ -1261,7 +1271,7 @@ mode: my-new
 disable a minor mode in a local variables list, use the @code{eval}
 keyword with a Lisp expression that runs the mode command
 (@pxref{Minor Modes}).  For example, the following local variables
-list enables ElDoc mode (@pxref{Lisp Doc}) by calling
+list enables ElDoc mode (@pxref{Programming Language Doc}) by calling
 @code{eldoc-mode} with no argument (calling it with an argument of 1
 would do the same), and disables Font Lock mode (@pxref{Font Lock}) by
 calling @code{font-lock-mode} with an argument of @minus{}1.
@@ -2811,6 +2821,24 @@ strings incorrectly.  You should then avoid adding Emacs 
Lisp code
 that modifies the coding system in other ways, such as calls to
 @code{set-language-environment}.
 
+  An alternative to using non-@acronym{ASCII} characters directly is
+to use one of the character escape syntaxes described in
+@pxref{General Escape Syntax,,, elisp, The Emacs Lisp Reference
+Manual}, as they allow all Unicode codepoints to be specified using
+only @acronym{ASCII} characters.
+
+  To bind non-@acronym{ASCII} keys, you must use a vector (@pxref{Init
+Rebinding}).  The string syntax cannot be used, since the
+non-@acronym{ASCII} characters will be interpreted as meta keys.  For
+instance:
+
+@example
+(global-set-key [?@var{char}] 'some-function)
+@end example
+
+@noindent
+Type @kbd{C-q}, followed by the key you want to bind, to insert @var{char}.
+
 @node Early Init File
 @subsection The Early Init File
 @cindex early init file
diff --git a/doc/emacs/display.texi b/doc/emacs/display.texi
index b7c8825efa..cf4f041452 100644
--- a/doc/emacs/display.texi
+++ b/doc/emacs/display.texi
@@ -270,7 +270,7 @@ either at the top or bottom of the window depending on the 
scroll
 direction.  By default, @code{scroll-conservatively} is@tie{}0, which
 means to always center point in the window.
 This said, in minibuffer windows, scrolling is always conservative by
-default because @code{scroll-minibuffer-conservatively} is non-nil,
+default because @code{scroll-minibuffer-conservatively} is non-@code{nil},
 which takes precedence over @code{scroll-conservatively}.
 
 @vindex scroll-step
diff --git a/doc/emacs/emacs.texi b/doc/emacs/emacs.texi
index 727f5f93bf..5c81641bf6 100644
--- a/doc/emacs/emacs.texi
+++ b/doc/emacs/emacs.texi
@@ -718,7 +718,7 @@ Documentation Lookup
 
 * Info Lookup::        Looking up library functions and commands in Info files.
 * Man Page::           Looking up man pages of library functions and commands.
-* Lisp Doc::           Looking up Emacs Lisp functions, etc.
+* Programming Language Doc:: Looking up program functions, variables, etc.
 
 C and Related Modes
 
diff --git a/doc/emacs/haiku.texi b/doc/emacs/haiku.texi
index ac631a39a6..33c81b6f17 100644
--- a/doc/emacs/haiku.texi
+++ b/doc/emacs/haiku.texi
@@ -108,7 +108,7 @@ You can create such a file with the @command{xmlbmessage} 
tool.
 @cindex crashes, Haiku
 @cindex haiku debugger
 @vindex haiku-debug-on-fatal-error
-  If the variable @code{haiku-debug-on-fatal-error} is non-nil, Emacs
+  If the variable @code{haiku-debug-on-fatal-error} is non-@code{nil}, Emacs
 will launch the system debugger when a fatal signal is received.  It
 defaults to @code{t}.  If GDB cannot be used on your system, please
 attach the report generated by the system debugger when reporting a
diff --git a/doc/emacs/killing.texi b/doc/emacs/killing.texi
index bb8d51158a..bac2f7ff78 100644
--- a/doc/emacs/killing.texi
+++ b/doc/emacs/killing.texi
@@ -570,8 +570,8 @@ change the variable @code{select-enable-clipboard} to 
@code{nil}.
 instance, a web browser will usually let you choose ``Copy Image'' on
 images, and this image will be put on the clipboard.  On capable
 platforms, Emacs can yank these objects with the @code{yank-media}
-command---but only in modes that have support for it (@w{@pxref{Yanking
-Media,,, elisp, The Emacs Lisp Reference Manual}}).
+command---but only in modes that have support for it (@pxref{Yanking
+Media,,, elisp, The Emacs Lisp Reference Manual}).
 
 @cindex clipboard manager
 @vindex x-select-enable-clipboard-manager
diff --git a/doc/emacs/macos.texi b/doc/emacs/macos.texi
index d7c432d420..1457a8bc3a 100644
--- a/doc/emacs/macos.texi
+++ b/doc/emacs/macos.texi
@@ -159,7 +159,7 @@ dialogue on quitting.
 @vindex ns-auto-hide-menu-bar
 @item ns-auto-hide-menu-bar
 This variable specifies whether the macOS menu bar is hidden when an
-Emacs frame is selected.  If non-nil the menu bar is not shown unless
+Emacs frame is selected.  If non-@code{nil} the menu bar is not shown unless
 the mouse pointer is moved near to the top of the screen.
 
 @vindex ns-use-native-fullscreen
@@ -178,14 +178,14 @@ These variables only apply to macOS 10.7 (Lion) and above.
 @item ns-use-mwheel-acceleration
 This variable controls whether Emacs ignores the system mousewheel
 acceleration.  When nil each `click' of the mousewheel will correspond
-exactly with one mousewheel event.  When non-nil, the default, each
+exactly with one mousewheel event.  When non-@code{nil}, the default, each
 `click' may correspond with more than one mousewheel event, depending
 on the user's input.
 
 @vindex ns-use-mwheel-momentum
 @item ns-use-mwheel-momentum
 This variable controls whether Emacs ignores the system `momentum'
-when scrolling using a trackpad.  When non-nil, the default, scrolling
+when scrolling using a trackpad.  When non-@code{nil}, the default, scrolling
 rapidly may result in the buffer continuing to scroll for a short
 while after the user has lifted their fingers off the trackpad.
 
diff --git a/doc/emacs/maintaining.texi b/doc/emacs/maintaining.texi
index ad4a3ea350..3e03bd817a 100644
--- a/doc/emacs/maintaining.texi
+++ b/doc/emacs/maintaining.texi
@@ -4,6 +4,8 @@
 @c See file emacs.texi for copying conditions.
 @node Maintaining
 @chapter Maintaining Large Programs
+@cindex maintaining large programs
+@cindex large programming projects, maintaining
 
   This chapter describes Emacs features for maintaining medium- to
 large-size programs and packages.  These features include:
@@ -2094,6 +2096,13 @@ definitions of symbols.  (One disadvantage of this kind 
of backend is
 that it only knows about subunits that were loaded into the
 interpreter.)
 
+@item
+If Eglot is activated for the current buffer's project
+(@pxref{Projects}) and the current buffer's major mode, Eglot consults
+an external language server program and provides the data supplied by
+the server regarding the definitions of the identifiers in the
+project.  @xref{Eglot Features,,, eglot, Eglot: The Emacs LSP Client}.
+
 @item
 An external program can extract references by scanning the relevant
 files, and build a database of these references.  A backend can then
@@ -3289,7 +3298,7 @@ according to @code{bug-reference-setup-from-mail-alist},
 and @code{bug-reference-maybe-setup-from-irc} which does the setup
 according to @code{bug-reference-setup-from-irc-alist}.
 @end itemize
-A setup function should return non-nil if it could setup bug-reference
+A setup function should return non-@code{nil} if it could setup bug-reference
 mode which is the case if the last thing the function does is calling
 one of the helper functions above.
 
diff --git a/doc/emacs/misc.texi b/doc/emacs/misc.texi
index 1514e316f8..29c0bed19c 100644
--- a/doc/emacs/misc.texi
+++ b/doc/emacs/misc.texi
@@ -1540,7 +1540,7 @@ color is supported.  Therefore, Emacs provides an option
 @code{comint-terminfo-terminal} to let you choose a terminal with more
 advanced features, as defined in your system's terminfo database.
 Emacs will use this option as the value for @env{TERM} so long as
-@code{system-uses-terminfo} is non-nil.
+@code{system-uses-terminfo} is non-@code{nil}.
 
 Both @code{comint-terminfo-terminal} and @code{system-uses-terminfo}
 can be declared as connection-local variables to adjust these options
diff --git a/doc/emacs/modes.texi b/doc/emacs/modes.texi
index 56b779f8de..1a32f61947 100644
--- a/doc/emacs/modes.texi
+++ b/doc/emacs/modes.texi
@@ -127,7 +127,7 @@ see which mode is actually being entered.
 Modes}).  For example, you can put the following lines in your init
 file to enable Flyspell minor mode in all text-based major modes
 (@pxref{Spelling}), and ElDoc minor mode in Emacs Lisp mode
-(@pxref{Lisp Doc}):
+(@pxref{Programming Language Doc}):
 
 @example
 (add-hook 'text-mode-hook 'flyspell-mode)
diff --git a/doc/emacs/package.texi b/doc/emacs/package.texi
index 420da09097..f9fa28074f 100644
--- a/doc/emacs/package.texi
+++ b/doc/emacs/package.texi
@@ -49,6 +49,7 @@ Manual}.
 * Package Statuses::     Which statuses a package can have.
 * Package Installation:: Options for package installation.
 * Package Files::        Where packages are installed.
+* Fetching Package Sources::  Managing packages directly from source.
 @end menu
 
 @node Package Menu
@@ -530,3 +531,73 @@ are laid out in the same way as in @code{package-user-dir}.
 corresponding package subdirectory.  This only works for packages
 installed in @code{package-user-dir}; if told to act on a package in a
 system-wide package directory, the deletion command signals an error.
+
+@node Fetching Package Sources
+@section Fetching Package Sources
+@cindex package development source
+@cindex upstream source, for packages
+@cindex git source of package @c "git" is not technically correct
+
+  By default @code{package-install} downloads a Tarball from a package
+archive and installs its files.  This might be inadequate if you wish
+to hack on the package sources and share your changes with others.  In
+that case, you may prefer to directly fetch and work on the upstream
+source.  This often makes it easier to develop patches and report
+bugs.
+
+@findex package-vc-install
+@findex package-vc-checkout
+  One way to do this is to use @code{package-vc-install}, to fetch the
+source code for a package directly from source.  The command will also
+automatically ensure that all files are byte-compiled and auto-loaded,
+just like with a regular package.  Packages installed this way behave
+just like any other package.  You can update them using
+@code{package-update} or @code{package-update-all} and delete them
+again using @code{package-delete}.  They are even displayed in the
+regular package listing.  If you just wish to clone the source of a
+package, without adding it to the package list, use
+@code{package-vc-checkout}.
+
+@vindex package-vc-selected-packages
+@findex package-vc-ensure-packages
+  An alternative way to use @code{package-vc-install} is via the
+@code{package-vc-selected-packages} user option.  This is an alist of
+packages to install, where each key is a package name and the value is
+@code{nil}, indicating that any revision is to install, a string,
+indicating a specific revision or a package specification plist.  The
+side effect of setting the user option is to install the package, but
+the process can also be manually triggered using the function
+@code{package-vc-ensure-packages}.  Here is an example of how the user
+option:
+
+@example
+@group
+(setopt package-vc-selected-packages
+        '((modus-themes . "0f39eb3fd9") ;specific revision
+          (auctex . nil)                ;any revision
+          (foo                          ;a package specification
+           :url "https://git.sv.gnu.org/r/foo-mode.git";
+           :branch "trunk")))
+@end group
+@end example
+
+@findex package-report-bug
+@findex package-vc-prepare-patch
+  With the source checkout, you might want to reproduce a bug against
+the current development head or implement a new feature to scratch an
+itch.  If the package metadata indicates how to contact the
+maintainer, you can use the command @code{package-report-bug} to
+report a bug via Email.  This report will include all the user options
+that you have customized.  If you have made a change you wish to share
+with the maintainers, first commit your changes then use the command
+@code{package-vc-prepare-patch} to share it.  @xref{Preparing Patches}.
+
+@findex package-vc-install-from-checkout
+@findex package-vc-refresh
+  If you maintain your own packages you might want to use a local
+checkout instead of cloning a remote repository.  You can do this by
+using @code{package-vc-install-from-checkout}, which creates a symbolic link
+from the package directory (@pxref{Package Files}) to your checkout
+and initializes the code.  Note that you might have to use
+@code{package-vc-refresh} to repeat the initialization and update the
+autoloads.
diff --git a/doc/emacs/programs.texi b/doc/emacs/programs.texi
index 30c7f106e5..6abf29c009 100644
--- a/doc/emacs/programs.texi
+++ b/doc/emacs/programs.texi
@@ -287,6 +287,13 @@ they occur in the buffer; if you want alphabetic sorting, 
use the
 symbol @code{imenu--sort-by-name} as the value.  You can also
 define your own comparison function by writing Lisp code.
 
+  If Eglot is activated for the current buffer's project
+(@pxref{Projects}) and the current buffer's major mode, Eglot provides
+its own facility for producing the buffer's index based on the
+analysis of the program source by the language-server which manages
+the current buffer.  @xref{Eglot Features,,, eglot, Eglot: The Emacs
+LSP Client}.
+
   Imenu provides the information to guide Which Function mode
 @ifnottex
 (@pxref{Which Function}).
@@ -1190,7 +1197,7 @@ use in your program.
 @menu
 * Info Lookup::         Looking up library functions and commands in Info 
files.
 * Man Page::            Looking up man pages of library functions and commands.
-* Lisp Doc::            Looking up Emacs Lisp functions, etc.
+* Programming Language Doc:: Looking up program functions, variables, etc.
 @end menu
 
 @node Info Lookup
@@ -1291,8 +1298,10 @@ WoMan Manual}.
 the WoMan Info manual, which is distributed with Emacs.
 @end ifnotinfo
 
-@node Lisp Doc
-@subsection Emacs Lisp Documentation Lookup
+@node Programming Language Doc
+@subsection Programming Language Documentation Lookup
+@cindex documentation for program symbols
+@cindex program functions and variables, documentation lookup
 
   When editing Emacs Lisp code, you can use the commands @kbd{C-h f}
 (@code{describe-function}) and @kbd{C-h v} (@code{describe-variable})
@@ -1300,43 +1309,120 @@ to view the built-in documentation for the Lisp 
functions and
 variables that you want to use.  @xref{Name Help}.
 
 @cindex ElDoc mode
+@cindex at-point documentation for program symbols
 @findex eldoc-mode
 @findex global-eldoc-mode
-  ElDoc is a buffer-local minor mode that helps with looking up Lisp
-documentation.  When it is enabled, the echo area displays some useful
-information whenever there is a Lisp function or variable at point;
-for a function, it shows the argument list, and for a variable it
-shows the first line of the variable's documentation string.  To
-toggle ElDoc mode, type @kbd{M-x eldoc-mode}.  There's also a Global
-ElDoc mode, which is turned on by default, and affects buffers whose
-major mode sets the variables described below.  Use @w{@kbd{M-x
-global-eldoc-mode}} to turn it off globally.
-
-@vindex eldoc-documentation-strategy
-@vindex eldoc-documentation-functions
-  These variables can be used to configure ElDoc mode:
+  ElDoc@footnote{
+The name ``ElDoc'' is a historical accident: this mode started by
+supporting Emacs Lisp buffers.
+} is a buffer-local minor mode that helps with looking up
+documentation of symbols (functions, methods, classes, variables,
+etc.) in your program.  When this mode is enabled, the echo area
+displays useful information whenever there is a documented symbol at
+point.  For example, in buffers under the Emacs Lisp mode, it shows
+the argument list of a function at point, and for a Lisp variable it
+shows the first line of the variable's documentation string.
+
+To toggle ElDoc mode, type @kbd{M-x eldoc-mode}.  There's also a
+Global ElDoc mode, which is turned on by default, and turns on the
+ElDoc mode in buffers whose major mode sets the variables described
+below.  Use @w{@kbd{M-x global-eldoc-mode}} to turn it off globally.
+
+Various major modes configure the Global ElDoc mode to use their
+documentation functions.  Examples include Emacs Lisp mode, Python
+mode, and Cfengine mode.  In addition, Emacs features that provide
+support for several major modes configure ElDoc to use their
+facilities for retrieving the documentation.  Examples include Eglot
+(@pxref{Eglot Features,,, eglot, Eglot: The Emacs LSP Client}), which
+provides documentation based on information from language servers;
+Semantic's Idle Summary mode (@pxref{Idle Summary Mode,,, semantic,
+Semantic Manual}); and Flymake, which uses ElDoc to show diagnostics
+at point (@pxref{Finding diagnostics,,, flymake, GNU Flymake manual}).
+
+The ElDoc mode works by scheduling the display of the available
+documentation for the symbol at point after Emacs has been idle for
+some short time.  This avoids annoying flickering of documentation
+messages in the echo area or the mode line when you type quickly and
+without delay.
+
+@findex eldoc-print-current-symbol-info
+You can also trigger the display of documentation for a symbol at
+point by using the command @kbd{M-x eldoc-print-current-symbol-info}.
+
+  The following variables can be used to configure ElDoc mode:
+
+@vtable @code
+@item eldoc-idle-delay
+The value of this user option controls the amount of idle time before
+the at-point documentation is displayed.  It should be set to the
+number of seconds to wait; the value of zero means to display without
+any delay.  The default is 0.5 sec.
+
+@item eldoc-print-after-edit
+If this user option is non-@code{nil}, ElDoc will show documentation
+only after some editing command, like inserting or deleting some
+text.  This comes in handy if you want Emacs to display documentation
+only about symbols that you type, but not about symbols that are
+already in the buffer (so just reading the source code will not show
+documentation).  The default value is @code{nil}.  If you change the
+value, you need to toggle @code{eldoc-mode} off and on again.
+
+@item eldoc-echo-area-use-multiline-p
+This user option controls whether and how to truncate documentation
+text if it is longer than the echo-area can display as a single screen
+line.  If the value is a positive number, it specifies the number of
+screen lines that ElDoc is allowed to display in the echo area without
+truncating the documentation.  A positive integer specifies the
+absolute maximum number of screen lines to use; a floating-point
+number specifies the number of screen lines as a fraction of the
+frame's height.  The value of @code{t} means never truncate the
+documentation (the echo-area will be resized up to the height allowed
+by @code{max-mini-window-height}, @pxref{Minibuffer Edit}), whereas
+the value of @code{nil} means truncate if the documentation is longer
+than a single screen line.  Finally, the special value
+@code{truncate-sym-name-if-fit} (the default) means to truncate the
+part of the documentation that represents a symbol's name if doing
+that will allow the documentation to fit on a single screen line.
+
+@item eldoc-echo-area-display-truncation-message
+If non-@code{nil} (the default), and documentation shown in the echo
+area is truncated because it's too long, follow the documentation by
+instructions about how to view the complete documentation text.  If
+@code{nil}, just indicate with @samp{@dots{}} that the documentation
+was truncated.
+
+@findex eldoc-doc-buffer
+@item eldoc-echo-area-prefer-doc-buffer
+If the value of this user option is @code{t}, ElDoc will not show the
+documentation in the echo area if the ElDoc buffer with the
+documentation is already displayed in some window.  (You can use the
+command @kbd{M-x eldoc-doc-buffer} any time to show the ElDoc buffer.)
+If the value of this option is the symbol @code{maybe}, the
+documentation will not be displayed in the echo area if the ElDoc
+buffer is shown in some window, and the documentation text has to be
+truncated if displayed in the echo area.  Finally, the value of
+@code{nil} (the default) means always show the documentation in the
+echo area.
 
-@table @code
 @item eldoc-documentation-strategy
-This variable holds the function which is used to retrieve
-documentation for the item at point from the functions in the hook
-@code{eldoc-documentation-functions}.  By default,
-@code{eldoc-documentation-strategy} returns the first documentation
-string produced by the @code{eldoc-documentation-functions} hook, but
-it may be customized to compose those functions' results in other
-ways.
+This customizable variable's value is the function which is used to
+retrieve and display documentation for the symbol at point.  The
+documentation is produced by the functions in the hook
+@code{eldoc-documentation-functions}.  The default value of
+@code{eldoc-documentation-strategy} specifies that ElDoc should
+display the first documentation text produced by functions in the
+@code{eldoc-documentation-functions} hook, but you can customize
+@code{eldoc-documentation-strategy} to work in other ways, such as
+displaying all of the documentation texts concatenated together.
 
 @item eldoc-documentation-functions
-This abnormal hook holds documentation functions.  It acts as a
-collection of backends for ElDoc.  This is what modes should use to
-register their documentation functions with ElDoc.
-
-@vindex eldoc-echo-area-display-truncation-message
-@item eldoc-echo-area-display-truncation-message
-If non-@code{nil} (the default), display a verbose message about how
-to view a complete documentation (if it has been truncated in the echo
-area).  If @code{nil}, just mark truncated messages with @samp{...}.
-@end table
+This abnormal hook's value is a list of functions that can produce
+documentation for the symbol at point as appropriate for the current
+buffer's major-mode.  These functions act as a collection of backends
+for ElDoc.  Major mode register their documentation lookup functions
+with ElDoc by adding their functions to the buffer-local value of this
+variable.
+@end vtable
 
 @node Hideshow
 @section Hideshow minor mode
@@ -1421,27 +1507,45 @@ nor comments).  The default value is @code{code}.
   Completion is normally done in the minibuffer (@pxref{Completion}),
 but you can also complete symbol names in ordinary Emacs buffers.
 
+@cindex tags-based completion
 @kindex M-TAB
 @kindex C-M-i
-  In programming language modes, type @kbd{C-M-i} or @kbd{M-@key{TAB}}
-to complete the partial symbol before point.  On graphical displays,
-the @kbd{M-@key{TAB}} key is usually reserved by the window manager
-for switching graphical windows, so you should type @kbd{C-M-i} or
-@kbd{@key{ESC} @key{TAB}} instead.
-
-@cindex tags-based completion
 @findex completion-at-point@r{, in programming language modes}
 @cindex Lisp symbol completion
 @cindex completion (Lisp symbols)
   In most programming language modes, @kbd{C-M-i} (or
-@kbd{M-@key{TAB}}) invokes the command @code{completion-at-point},
-which generates its completion list in a flexible way.  If Semantic
-mode is enabled, it tries to use the Semantic parser data for
-completion (@pxref{Semantic}).  If Semantic mode is not enabled or
-fails at performing completion, it tries to complete using the
-selected tags table (@pxref{Tags Tables}).  If in Emacs Lisp mode, it
-performs completion using the function, variable, or property names
-defined in the current Emacs session.
+@kbd{M-@key{TAB}}@footnote{
+On graphical displays, the @kbd{M-@key{TAB}} key is usually reserved
+by the window manager for switching graphical windows, so you should
+type @kbd{C-M-i} or @kbd{@key{ESC} @key{TAB}} instead.
+}) invokes the command @code{completion-at-point}, which generates the
+list of possible completions for the symbol at point.  This command
+uses the available support facilities to come up with the completion
+candidates:
+
+@itemize @bullet
+@item
+If Eglot is activated for the current buffer's project
+(@pxref{Projects}) and the current buffer's major mode, the command
+tries to use the corresponding language server for producing the list
+of completion candidates.  @xref{Eglot Features,,, eglot, Eglot: The
+Emacs LSP Client}.
+
+@item
+If Semantic mode is enabled (@pxref{Semantic}), the command tries to
+use the Semantic parser data for completion.
+
+@item
+If Semantic mode is not enabled or fails at performing completion, the
+command tries to complete using the selected tags table (@pxref{Tags
+Tables}); you need to visit the tags table with @w{@kbd{M-x
+visit-tags-table}} for that to work.
+
+@item
+In Emacs Lisp mode, the command performs completion using the
+function, variable, or property names defined in the current Emacs
+session.
+@end itemize
 
   In all other respects, in-buffer symbol completion behaves like
 minibuffer completion.  For instance, if Emacs cannot complete to
diff --git a/doc/emacs/rmail.texi b/doc/emacs/rmail.texi
index e38bde036a..7414cdb079 100644
--- a/doc/emacs/rmail.texi
+++ b/doc/emacs/rmail.texi
@@ -1409,6 +1409,14 @@ might use rot13 to hide important plot points.
 rot13-other-window}.  This displays the current buffer in another window
 which applies the code when displaying the text.
 
+@findex rot13-region
+  If you are only interested in a region, the command @kbd{M-x
+rot13-region} might be preferable.  This will encrypt/decrypt the
+active region in-place.  If the buffer is read-only, it will attempt
+to display the plain text in the echo area.  If the text is too long
+for the echo area, the command will pop up a temporary buffer with the
+encrypted/decrypted text.
+
 @node Movemail
 @section @command{movemail} program
 @cindex @command{movemail} program
diff --git a/doc/emacs/search.texi b/doc/emacs/search.texi
index 582e764c55..63541d78a5 100644
--- a/doc/emacs/search.texi
+++ b/doc/emacs/search.texi
@@ -1348,18 +1348,19 @@ tailor them to your needs.
 @kindex SPC @r{(Incremental search)}
 @findex isearch-toggle-lax-whitespace
 @vindex search-whitespace-regexp
-  By default, search commands perform @dfn{lax space matching}:
-each space, or sequence of spaces, matches any sequence of one or more
-whitespace characters in the text.  (Incremental regexp search has a
-separate default; see @ref{Regexp Search}.)  Hence, @w{@samp{foo bar}}
-matches @w{@samp{foo bar}}, @w{@samp{foo@ @ bar}},
-@w{@samp{foo@ @ @ bar}}, and so on (but not @samp{foobar}).  More
-precisely, Emacs matches each sequence of space characters in the
-search string to a regular expression specified by the variable
-@code{search-whitespace-regexp}.  For example, to make spaces match
-sequences of newlines as well as spaces, set it to the regular expression
-@samp{[[:space:]\n]+}.  The default value of this variable considers
-any sequence of spaces and tab characters as whitespace.
+  By default, search commands perform @dfn{lax space matching}: each
+space, or sequence of spaces, matches any sequence of one or more
+whitespace characters in the text.  More precisely, Emacs matches each
+sequence of space characters in the search string to a regular
+expression specified by the user option
+@code{search-whitespace-regexp}.  The default value of this option
+considers any sequence of spaces and tab characters as whitespace.
+Hence, @w{@samp{foo bar}} matches @w{@samp{foo bar}}, @w{@samp{foo@ @
+bar}}, @w{@samp{foo@ @ @ bar}}, and so on (but not @samp{foobar}).  If
+you want to make spaces match sequences of newlines as well as spaces
+and tabs, customize the option to make its value be the regular
+expression @samp{[ \t\n]+}.  (The default behavior of the
+incremental regexp search is different; see @ref{Regexp Search}.)
 
   If you want whitespace characters to match exactly, you can turn lax
 space matching off by typing @kbd{M-s @key{SPC}}
diff --git a/doc/emacs/text.texi b/doc/emacs/text.texi
index b103e22e39..27abe5caaa 100644
--- a/doc/emacs/text.texi
+++ b/doc/emacs/text.texi
@@ -460,7 +460,7 @@ variables.
 
 @vindex electric-quote-replace-double
   You can also set the option @code{electric-quote-replace-double} to
-a non-@code{nil} value.  Then, typing @kbd{"} insert an appropriate
+a non-@code{nil} value.  Then, typing @kbd{"} inserts an appropriate
 curved double quote depending on context: @t{“} at the beginning of
 the buffer or after a line break, whitespace, opening parenthesis, or
 quote character, and @t{”} otherwise.
@@ -998,15 +998,14 @@ major mode's special commands.  (The variable
 
 @vindex outline-minor-mode-use-buttons
   If @code{outline-minor-mode-use-buttons} is non-@code{nil}, Outline
-minor mode will use buttons (at the start of the header lines) in
-addition to ellipsis to show that a section is hidden.  Using
-@kbd{RET} (or clicking on the button with a mouse) will toggle
-displaying the section.
-
-@vindex outline-minor-mode-use-margins
-  If @code{outline-minor-mode-use-margins} is non-@code{nil}, Outline
-minor mode will use the window margins in addition to ellipsis to show
-that a section is hidden.
+minor mode will use buttons at the beginning of the heading lines, in
+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.
 
 @vindex outline-minor-mode-cycle
   If the @code{outline-minor-mode-cycle} user option is
diff --git a/doc/emacs/vc1-xtra.texi b/doc/emacs/vc1-xtra.texi
index 66d3f51c30..23d5f50432 100644
--- a/doc/emacs/vc1-xtra.texi
+++ b/doc/emacs/vc1-xtra.texi
@@ -291,8 +291,13 @@ When collaborating on projects it is common to send 
patches via email,
 to share changes.  If you wish to do this using VC, you can use the
 @code{vc-prepare-patch} command.  This will prompt you for the
 revisions you wish to share, and which destination email address(es)
-to use.  The command will then prepare those revisions using your
-@abbr{MUA, Mail User Agent} for you to review and send.
+to use.  The revisions are separated using commas (or whatever was
+configured by @var{crm-separator}).  The command will then prepare
+those revisions using your @abbr{MUA, Mail User Agent} for you to
+review and send.
+
+When invoked interactively in a Log View buffer with marked revisions,
+these revisions will be used.
 
 @vindex vc-prepare-patches-separately
 Depending on the value of the user option
diff --git a/doc/lispref/backups.texi b/doc/lispref/backups.texi
index d99487368b..f0b154e495 100644
--- a/doc/lispref/backups.texi
+++ b/doc/lispref/backups.texi
@@ -474,7 +474,7 @@ Each transform is a list of the form @w{@code{(@var{regexp}
 @var{replacement} [@var{uniquify}])}}.  @var{regexp} is a regular
 expression to match against the file name; if it matches,
 @code{replace-match} is used to replace the matching part with
-@var{replacement}.  If the optional element @var{uniquify} is non-nil,
+@var{replacement}.  If the optional element @var{uniquify} is non-@code{nil},
 the auto-save file name is constructed by concatenating the directory
 part of the transformed file name with the buffer's file name in which
 all directory separators were changed to @samp{!} to prevent clashes.
diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi
index ede1c4d762..377b433cae 100644
--- a/doc/lispref/commands.texi
+++ b/doc/lispref/commands.texi
@@ -2726,7 +2726,7 @@ coordinates @var{x} and @var{y} in a specified frame or 
window,
 @var{frame-or-window}, which defaults to the selected window.
 The coordinates @var{x} and @var{y} are relative to the
 text area of the selected window.
-If @var{whole} is @code{non-nil}, the @var{x} coordinate is relative
+If @var{whole} is non-@code{nil}, the @var{x} coordinate is relative
 to the entire window area including scroll bars, margins and fringes.
 @end defun
 
diff --git a/doc/lispref/compile.texi b/doc/lispref/compile.texi
index 70e0f98f44..d1d281d709 100644
--- a/doc/lispref/compile.texi
+++ b/doc/lispref/compile.texi
@@ -843,6 +843,14 @@ native compilation of that file.  In addition, a similar 
variable
 file.  If both @code{no-byte-compile} and @code{no-native-compile} are
 specified, the former takes precedence.
 
+@cindex native compilation, prevent writing @file{*.eln} files
+  Sometimes there could be a need to prevent the native compilation
+from writing its results, the @file{*.eln} files, into a subdirectory
+of @code{user-emacs-directory} (@pxref{Init File}).  You can do that
+by either changing the value of @code{native-comp-eln-load-path}
+(@pxref{Native-Compilation Variables}) or by temporarily pointing the
+@env{HOME} environment variable to a non-existing directory.
+
 @menu
 * Native-Compilation Functions::  Functions to natively-compile Lisp.
 * Native-Compilation Variables::  Variables controlling native compilation.
@@ -1069,9 +1077,19 @@ pristine environment, that may not be true for the 
subprocess.
 @end defopt
 
 @defopt native-comp-async-query-on-exit
-If this variable's value is non-nil, Emacs will query upon exiting
+If this variable's value is non-@code{nil}, Emacs will query upon exiting
 whether to exit and kill any asynchronous native-compilation
 subprocesses that are still running, thus preventing the corresponding
 @file{.eln} files from being written.  If the value is @code{nil}, the
 default, Emacs will kill these subprocesses without querying.
 @end defopt
+
+The variable @code{native-comp-eln-load-path} holds the list of
+directories where Emacs looks for the @file{*.eln} files
+(@pxref{Library Search}); in that role it is the equivalent of
+@code{load-path} used to look for @file{*.el} and @file{*.elc} files.
+The directories in this list are also used for writing the
+@file{*.eln} files produced by asynchronous native-compilation;
+specifically, Emacs will write these files into the first writable
+directory in the list.  Thus, you can control where native-compilation
+stores the results by changing the value of this variable.
diff --git a/doc/lispref/control.texi b/doc/lispref/control.texi
index 9035e7f6bb..3c874ee3fe 100644
--- a/doc/lispref/control.texi
+++ b/doc/lispref/control.texi
@@ -1534,7 +1534,7 @@ iterator with @code{iter-next} for anything interesting 
to happen.
 Each call to a generator function produces a @emph{different}
 iterator, each with its own state.
 
-@defun iter-next iterator value
+@defun iter-next iterator &optional value
 Retrieve the next value from @var{iterator}.  If there are no more
 values to be generated (because @var{iterator}'s generator function
 returned), @code{iter-next} signals the @code{iter-end-of-sequence}
diff --git a/doc/lispref/customize.texi b/doc/lispref/customize.texi
index 6ba35cffff..204719e942 100644
--- a/doc/lispref/customize.texi
+++ b/doc/lispref/customize.texi
@@ -1428,12 +1428,32 @@ emacs, The GNU Emacs Manual}.)
 be a call to @code{deftheme}, and the last form should be a call to
 @code{provide-theme}.
 
-@defmac deftheme theme &optional doc
+@defmac deftheme theme &optional doc &rest properties
 This macro declares @var{theme} (a symbol) as the name of a Custom
 theme.  The optional argument @var{doc} should be a string describing
 the theme; this is the description shown when the user invokes the
 @code{describe-theme} command or types @kbd{?} in the @samp{*Custom
-Themes*} buffer.
+Themes*} buffer.  The remaining arguments @var{properties} are used
+pass a property list with theme attributes.
+
+The following attributes are supported:
+
+@table @code
+@item :family
+A symbol designating what ``family'' a theme belongs to.  A
+@dfn{family} of themes is a set of similar themes that differ by minor
+aspects, such as face colors that are meant for the light vs dark
+background of the frame.
+@item :kind
+A symbol.  If a theme is enabled and this property has the value
+@code{color-scheme}, then the @code{theme-choose-variant} command will
+look for other available themes that belong to the same family in
+order to switch the themes.  Other values are currently unspecified
+and should not be used.
+@item :background-mode
+A symbol, either @code{light} or @code{dark}.  This attribute is
+currently unused, but should still be specified.
+@end table
 
 Two special theme names are disallowed (using them causes an error):
 @code{user} is a dummy theme that stores the user's direct
diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index 64400ef931..c75107fb58 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -548,7 +548,7 @@ previous example as follows:
 @end example
 @end defmac
 
-@defmac dolist-with-progress-reporter (var count [result]) reporter-or-message 
body@dots{}
+@defmac dolist-with-progress-reporter (var list [result]) reporter-or-message 
body@dots{}
 This is another convenience macro that works the same way as @code{dolist}
 does, but also reports loop progress using the functions described
 above.  As in @code{dotimes-with-progress-reporter},
@@ -2587,7 +2587,7 @@ meaning the foreground color of the face.  Omitting the 
attribute
 @code{:color} means to use the foreground color of the face.
 @var{style} should be a symbol @code{line} or @code{wave}, meaning to
 use a straight or wavy line.  Omitting the attribute @code{:style}
-means to use a straight line.  @var{position}, if non-nil, means to
+means to use a straight line.  @var{position}, if non-@code{nil}, means to
 display the underline at the descent of the text, instead of at the
 baseline level.  If it is a number, then it specifies the amount of
 pixels above the descent to display the underline.
@@ -7347,7 +7347,7 @@ Display the next search result in @var{xwidget}.  This 
function will
 signal an error if a search query has not been already started in
 @var{xwidget} through @code{xwidget-webkit-search}.
 
-If @code{wrap-around} was non-nil when @code{xwidget-webkit-search}
+If @code{wrap-around} was non-@code{nil} when @code{xwidget-webkit-search}
 was called, then the search will restart from the beginning of the
 document when its end is reached.
 @end defun
@@ -7357,7 +7357,7 @@ Display the previous search result in @var{xwidget}.  
This function
 signals an error if a search query has not been already started in
 @var{xwidget} through @code{xwidget-webkit-search}.
 
-If @code{wrap-around} was non-nil when @code{xwidget-webkit-search}
+If @code{wrap-around} was non-@code{nil} when @code{xwidget-webkit-search}
 was called, then the search will restart from the end of the
 document when its beginning is reached.
 @end defun
diff --git a/doc/lispref/edebug.texi b/doc/lispref/edebug.texi
index 6a51489d8a..1562a37842 100644
--- a/doc/lispref/edebug.texi
+++ b/doc/lispref/edebug.texi
@@ -1076,16 +1076,25 @@ current buffer, are saved and restored.
 @cindex window configuration (Edebug)
 The outside window configuration is saved and restored if
 @code{edebug-save-windows} is non-@code{nil} (@pxref{Edebug Options}).
+If the value of @code{edebug-save-windows} is a list, only the listed
+windows are saved and restored.
 
 The window configuration is not restored on error or quit, but the
 outside selected window @emph{is} reselected even on error or quit in
-case a @code{save-excursion} is active.  If the value of
-@code{edebug-save-windows} is a list, only the listed windows are saved
-and restored.
+case a @code{save-excursion} is active.
 
 The window start and horizontal scrolling of the source code buffer are
 not restored, however, so that the display remains coherent within Edebug.
 
+@cindex buffer point changed by Edebug
+@cindex edebug overwrites buffer point position
+Saving and restoring the outside window configuration can sometimes
+change the positions of point in the buffers on which the Lisp program
+you are debugging operates, especially if your program moves point.
+If this happens and interferes with your debugging, we recommend to
+set @code{edebug-save-windows} to @code{nil}
+(@pxref{Edebug Options}).
+
 @item
 The value of point in each displayed buffer is saved and restored if
 @code{edebug-save-displayed-buffer-points} is non-@code{nil}.
@@ -1655,10 +1664,16 @@ specify an Edebug form specification.
 If this is non-@code{nil}, Edebug saves and restores the window
 configuration.  That takes some time, so if your program does not care
 what happens to the window configurations, it is better to set this
-variable to @code{nil}.
-
-If the value is a list, only the listed windows are saved and
-restored.
+variable to @code{nil}.  We also recommend to set this to @code{nil}
+if the default value causes Edebug to overwrite the positions of point
+in buffers that are involved in the program you are debugging, as
+result of saving and restoring the window configuration; this could
+happen if your program moves point in one or more of those buffers.
+Another option to try to customize in this case is
+@code{edebug-save-displayed-buffer-points}, described below.
+
+If the value of @code{edebug-save-windows} is a list, only the listed
+windows are saved and restored.
 
 You can use the @kbd{W} command in Edebug to change this variable
 interactively.  @xref{Edebug Display Update}.
diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi
index 2467364dc6..183b2786ea 100644
--- a/doc/lispref/files.texi
+++ b/doc/lispref/files.texi
@@ -1413,7 +1413,7 @@ to distinguish remote filesystems from local ones.
 The file's inode and device together give enough information
 to distinguish any two files on the system---no two files can have the
 same values for both of these attributes.  This tuple that uniquely
-identifies the file is returned by @code{file-attribute-file-number}.
+identifies the file is returned by @code{file-attribute-file-identifier}.
 
 For example, here are the file attributes for @file{files.texi}:
 
@@ -3136,7 +3136,7 @@ a list of file names that match it.
 
 @var{pattern} is, by default, a ``glob''/wildcard string, e.g.,
 @samp{"/tmp/*.png"} or @samp{"/*/*/foo.png"}, but can also be a
-regular expression if the optional @var{regexp} parameter is non-nil.
+regular expression if the optional @var{regexp} parameter is non-@code{nil}.
 In any case, the matches are applied per sub-directory, so a match
 can't span a parent/sub directory.
 
diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi
index 8db6ad0fd3..b3f1a29ae8 100644
--- a/doc/lispref/frames.texi
+++ b/doc/lispref/frames.texi
@@ -4058,70 +4058,89 @@ amount of different data types on the clipboard.
   When the user drops something from another application over Emacs,
 Emacs will try to insert any text and open any URL that was dropped.
 If text was dropped, then it will always be inserted at the location
-of the mouse pointer when the drop happened, or saved in the kill ring
-if insertion failed (which can happen if the buffer is read-only).  If
-it was an URL, then Emacs tries to call an appropriate handler
-function by first matching the URL against regexps defined in
-@code{dnd-protocol-alist}, and then against @code{browse-url-handlers}
-and @code{browse-url-default-handlers}, and failing that, inserting
-the URL as plain text.
+of the mouse pointer where the drop happened, or saved in the kill
+ring if insertion failed, which could happen if the buffer was
+read-only.  If a URL was dropped instead, then Emacs will first try to
+call an appropriate handler function by matching the URL against
+regexps defined in the variable @code{dnd-protocol-alist}, and then
+against those defined in the variables @code{browse-url-handlers} and
+@code{browse-url-default-handlers}.  Should no suitable handler be
+located, Emacs will fall back to inserting the URL as plain text.
 
 @defvar dnd-protocol-alist
   This variable is a list of cons cells of the form
 @w{@code{(@var{pattern} . @var{action})}}.  @var{pattern} is a regexp
 that URLs are matched against after being dropped.  @var{action} is a
-function that is called with two arguments should a URL being dropped
+function that is called with two arguments, should a URL being dropped
 match @var{pattern}: the URL being dropped, and the action being
-performed for the drop (one of the symbols @code{copy}, @code{move},
-@code{link}, @code{private} or @code{ask}).
+performed for the drop, which is one of the symbols @code{copy},
+@code{move}, @code{link}, @code{private} or @code{ask}.
+
+If @var{action} is @var{private}, then it means the program that
+initiated the drop wants Emacs to perform an unspecified action with
+the URL; a reasonable action to perform in that case is to open the URL
+or copy its contents into the current buffer.  Otherwise, @var{action}
+has the same meaning as the @var{action} argument to
+@code{dnd-begin-file-drag}.
 @end defvar
 
 @cindex drag and drop, X
 @cindex drag and drop, other formats
-  Emacs implements drag-and-drop for text and URLs individually for
-each window system, and does not by default support the dropping of
-anything else.  Code that wishes to support the dropping of content
-types not supported by Emacs can utilize the X-specific interface
-described below:
+  Emacs implements receiving text and URLs individually for each
+window system, and does not by default support receiving other kinds
+of data as drops.  To support receiving other kinds of data, use the
+X-specific interface described below:
 
 @vindex x-dnd-test-function
 @vindex x-dnd-known-types
-  When a user drags something from another application over Emacs on
-the X Window System, that other application expects Emacs to tell it
-if Emacs can handle the data that was dragged.  The variable
-@code{x-dnd-test-function} is used by Emacs to determine what to
-reply.  The default value is @code{x-dnd-default-test-function} which
-accepts drops if the type of the data to be dropped is present in
-@code{x-dnd-known-types}.  You can customize
-@code{x-dnd-test-function} and/or @code{x-dnd-known-types} if you want
-Emacs to accept or reject drops based on some other criteria.
+  When a user drags something from another application over Emacs
+under the X Window System, that other application expects Emacs to
+tell it if Emacs understands the data being dragged.  The function in
+the variable @code{x-dnd-test-function} is called by Emacs to
+determine what to reply to any such inquiry.  The default value is
+@code{x-dnd-default-test-function}, which accepts drops if the type of
+the data to be dropped is present in @code{x-dnd-known-types}.
+Changing the variables @code{x-dnd-test-function} and
+@code{x-dnd-known-types} can make Emacs accept or reject drops based
+on some other criteria.
 
 @vindex x-dnd-types-alist
-  If you want to change the way Emacs handles drop of different types
-or add a new type, customize @code{x-dnd-types-alist}.  This requires
-detailed knowledge of what types other applications use for drag and
-drop.
-
-  Those data types are typically implemented as special data types an
-X selection provided by the other application can be converted to.
-They can either be the same data types that are typically accepted by
-@code{gui-set-selection}, or they can be MIME types, depending on the
-specific drag-n-drop protocol being used.  Plain text may be
-@code{"STRING"} or @code{"text/plain"}, for example.
+  If you want to change the way Emacs receives drops of different data
+types, or you want to enable it to understand a new type, change the variable
+@code{x-dnd-types-alist}.  Doing so correctly requires detailed
+knowledge of what data types other applications use for drag and drop.
+
+  These data types are typically implemented as special data types
+that can be obtained from an X selection provided by the other
+application.  In most cases, they are either the same data types that
+are typically accepted by @code{gui-set-selection}, or MIME types,
+depending on the specific drag-and-drop protocol being used.  For
+example, the data type used for plain text may be either
+@code{"STRING"} or @code{"text/plain"}.
 
 @vindex x-dnd-direct-save-function
+@c FIXME: This description is overly-complicated and confusing.  In
+@c particular, the two calls to the function basically sound
+@c identical, so it is unclear how should the function distinguish
+@c between the first and the second one.  The description of who asks
+@c whom to do what is also very hard to understand.  Needs rewording,
+@c and needs shorter sentences.  Perhaps examples could help.
   However, @code{x-dnd-types-alist} does not handle a special kind of
-drop sent by a program which wants Emacs to save a file in a location
-Emacs must determine by itself.  These drops are handled via the
-variable @code{x-dnd-direct-save-function}, which should be a function
-that accepts two arguments.  If the first argument is non-@code{nil},
-then the second argument is a string describing the name (with no
-leading directory) that the other program recommends the file be saved
-under, and the function should return the complete file name under
-which it will be saved.  Otherwise, the file has already been saved,
-and the second argument is the complete name of the file.  The
-function should then perform whatever action is appropriate (i.e.,
-open the file or refresh the directory listing.)
+drop sent by a program that wants Emacs to tell it where to save a
+file in a specific location determined by the user.  These drops are
+instead handled by a function that is the value of the variable
+@code{x-dnd-direct-save-function}.  This function should accept two arguments.
+If the first argument is non-@code{nil}, then the second argument is a
+file name to save (with leading directories) that the other
+program recommends, and the
+function should return the full file name under which it should be
+saved.  After the function completes, Emacs will ask the other program
+to save the file under the name that was returned, and if the file was
+successfully saved, call the function again with the first argument
+set to a non-@code{nil} value and the second argument set to the file
+name that was returned.  The function should then perform whatever
+action is appropriate (i.e., opening the file or refreshing a
+directory listing.)
 
 @cindex initiating drag-and-drop
   On capable window systems, Emacs also supports dragging contents
diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi
index 8b858e0aa0..7ffde7d43d 100644
--- a/doc/lispref/functions.texi
+++ b/doc/lispref/functions.texi
@@ -533,6 +533,44 @@ Instead, use the @code{advertised-calling-convention} 
declaration
 compiler emit a warning message when it compiles Lisp programs which
 use the deprecated calling convention.
 
+@cindex computed documentation string
+@kindex :documentation
+Documentation strings are usually static, but occasionally it can be
+necessary to generate them dynamically.  In some cases you can do so
+by writing a macro which generates at compile time the code of the
+function, including the desired documentation string.  But you can
+also generate the docstring dynamically by writing
+@code{(:documentation @var{form})} instead of the documentation
+string.  This will evaluate @var{form} at run-time when the function
+is defined and use it as the documentation string@footnote{This only
+works in code using @code{lexical-binding}.}.  You can also compute
+the documentation string on the fly when it is requested, by setting
+the @code{function-documentation} property of the function's symbol to
+a Lisp form that evaluates to a string.
+
+For example:
+@example
+@group
+(defun adder (x)
+  (lambda (y)
+    (:documentation (format "Add %S to the argument Y." x))
+    (+ x y)))
+(defalias 'adder5 (adder 5))
+(documentation 'adder5)
+    @result{} "Add 5 to the argument Y."
+@end group
+
+@group
+(put 'adder5 'function-documentation
+     '(concat (documentation (symbol-function 'adder5) 'raw)
+              "  Consulted at " (format-time-string "%H:%M:%S")))
+(documentation 'adder5)
+    @result{} "Add 5 to the argument Y.  Consulted at 15:52:13"
+(documentation 'adder5)
+    @result{} "Add 5 to the argument Y.  Consulted at 15:52:18"
+@end group
+@end example
+
 @node Function Names
 @section Naming a Function
 @cindex function definition
diff --git a/doc/lispref/help.texi b/doc/lispref/help.texi
index 65ad5f0554..ee6fdb0dbb 100644
--- a/doc/lispref/help.texi
+++ b/doc/lispref/help.texi
@@ -980,8 +980,8 @@ In addition to function descriptions, the list can also 
have string
 elements, which are used to divide a documentation group into
 sections.
 
-@defun shortdoc-add-function shortdoc-add-function group section elem
-Lisp packages can add functions to groups with this command.  Each
+@defun shortdoc-add-function group section elem
+Lisp packages can add functions to groups with this function.  Each
 @var{elem} should be a function description, as described above.
 @var{group} is the function group, and @var{section} is what section
 in the function group to insert the function into.
diff --git a/doc/lispref/intro.texi b/doc/lispref/intro.texi
index 975215d697..eccc8deb63 100644
--- a/doc/lispref/intro.texi
+++ b/doc/lispref/intro.texi
@@ -34,7 +34,9 @@ specifically to editing.
 
   This is
 @iftex
+@ifset VERSION
 edition @value{VERSION} of
+@end ifset
 @end iftex
 the @cite{GNU Emacs Lisp Reference Manual},
 corresponding to Emacs version @value{EMACSVER}.
diff --git a/doc/lispref/lists.texi b/doc/lispref/lists.texi
index 5c5c615f85..30f65e359a 100644
--- a/doc/lispref/lists.texi
+++ b/doc/lispref/lists.texi
@@ -1961,12 +1961,12 @@ and later discarded; this is not possible with a 
property list.
 @cindex accessing plist properties
 
   The following functions can be used to manipulate property lists.
-They all compare property names using @code{eq}.
+They all default to comparing property names using @code{eq}.
 
 @defun plist-get plist property &optional predicate
 This returns the value of the @var{property} property stored in the
 property list @var{plist}.  Comparisons are done with @var{predicate},
-and defaults to @code{eq}.  It accepts a malformed @var{plist}
+which defaults to @code{eq}.  It accepts a malformed @var{plist}
 argument.  If @var{property} is not found in the @var{plist}, it
 returns @code{nil}.  For example,
 
@@ -1985,7 +1985,7 @@ returns @code{nil}.  For example,
 @defun plist-put plist property value &optional predicate
 This stores @var{value} as the value of the @var{property} property in
 the property list @var{plist}.  Comparisons are done with @var{predicate},
-and defaults to @code{eq}.  It may modify @var{plist} destructively,
+which defaults to @code{eq}.  It may modify @var{plist} destructively,
 or it may construct a new list structure without altering the old.  The
 function returns the modified property list, so you can store that back
 in the place where you got @var{plist}.  For example,
@@ -2012,7 +2012,7 @@ compares properties using @code{equal} instead of 
@code{eq}.
 
 @defun plist-member plist property &optional predicate
 This returns non-@code{nil} if @var{plist} contains the given
-@var{property}.  Comparisons are done with @var{predicate}, and
+@var{property}.  Comparisons are done with @var{predicate}, which
 defaults to @code{eq}.  Unlike @code{plist-get}, this allows you to
 distinguish between a missing property and a property with the value
 @code{nil}.  The value is actually the tail of @var{plist} whose
diff --git a/doc/lispref/loading.texi b/doc/lispref/loading.texi
index 4e4f12dc32..c7fbdac1d7 100644
--- a/doc/lispref/loading.texi
+++ b/doc/lispref/loading.texi
@@ -662,7 +662,7 @@ and @code{define-overloadable-function} (see the commentary 
in
 and @code{define-global-minor-mode}.
 
 @item Other definition types:
-@code{defcustom}, @code{defgroup}, @code{defclass}
+@code{defcustom}, @code{defgroup}, @code{deftheme}, @code{defclass}
 (@pxref{Top,EIEIO,,eieio,EIEIO}), and @code{define-skeleton}
 (@pxref{Top,Autotyping,,autotype,Autotyping}).
 @end table
diff --git a/doc/lispref/modes.texi b/doc/lispref/modes.texi
index 75eb21522f..9527df33b8 100644
--- a/doc/lispref/modes.texi
+++ b/doc/lispref/modes.texi
@@ -198,7 +198,7 @@ on the hook.  The optional argument @var{depth} lets you 
indicate where the
 function should be inserted in the list: it should then be a number
 between -100 and 100 where the higher the value, the closer to the end of the
 list the function should go.  The @var{depth} defaults to 0 and for backward
-compatibility when @var{depth} is a non-nil symbol it is interpreted as a depth
+compatibility when @var{depth} is a non-@code{nil} symbol it is interpreted as 
a depth
 of 90.  Furthermore, when @var{depth} is strictly greater than 0 the function
 is added @emph{after} rather than before functions of the same depth.
 One should never use a depth of 100 (or -100), because one can never be
@@ -1256,7 +1256,7 @@ will be a vector for the ID at @var{pos}.  If there is no 
entry at
 
 @vindex tabulated-list-use-header-line
 @defun tabulated-list-header-overlay-p &optional POS
-This @code{defsubst} returns non-nil if there is a fake header at
+This @code{defsubst} returns non-@code{nil} if there is a fake header at
 @var{pos}.  A fake header is used if
 @code{tabulated-list-use-header-line} is @code{nil} to put the column
 names at the beginning of the buffer.  If omitted or @code{nil},
@@ -1269,7 +1269,7 @@ This function puts @var{tag} in the padding area of the 
current line.
 The padding area can be empty space at the beginning of the line, the
 width of which is governed by @code{tabulated-list-padding}.
 @var{tag} should be a string, with a length less than or equal to
-@code{tabulated-list-padding}.  If @var{advance} is non-nil, this
+@code{tabulated-list-padding}.  If @var{advance} is non-@code{nil}, this
 function advances point by one line.
 @end defun
 
@@ -1284,7 +1284,7 @@ This function changes the tabulated list entry at point, 
setting
 the name of the column to change.  @var{desc} is the new column
 descriptor, which is inserted via @code{tabulated-list-print-col}.
 
-If @var{change-entry-data} is non-nil, this function modifies the
+If @var{change-entry-data} is non-@code{nil}, this function modifies the
 underlying data (usually the column descriptor in the list
 @code{tabulated-list-entries}) by setting the column descriptor of the
 vector to @code{desc}.
@@ -1851,7 +1851,9 @@ to enable or disable the buffer-local minor mode 
@var{mode} in all (or
 some; see below) buffers.  It also executes the @var{body} forms.  To
 turn on the minor mode in a buffer, it uses the function
 @var{turn-on}; to turn off the minor mode, it calls @var{mode} with
-@minus{}1 as argument.
+@minus{}1 as argument.  (The function @var{turn-on} is a separate
+function so it could determine whether to enable the minor mode or not
+when it is not a priori clear that it should always be enabled.)
 
 Globally enabling the mode also affects buffers subsequently created
 by visiting files, and buffers that use a major mode other than
@@ -3146,9 +3148,10 @@ match found by @var{matcher} acts as the anchor for 
further searches
 specified by @var{anchored-highlighter}.  @var{anchored-highlighter}
 is a list of the following form:
 
+@c Don't wrap the line in the next @example, because that tends to
+@c produce ugly indentation in Info
 @example
-(@var{anchored-matcher} @var{pre-form} @var{post-form}
-                        @var{subexp-highlighters}@dots{})
+(@var{anchored-matcher} @var{pre-form} @var{post-form} 
@var{subexp-highlighters}@dots{})
 @end example
 
 Here, @var{anchored-matcher}, like @var{matcher}, is either a regular
diff --git a/doc/lispref/numbers.texi b/doc/lispref/numbers.texi
index fdcda328d8..2c7a1d3266 100644
--- a/doc/lispref/numbers.texi
+++ b/doc/lispref/numbers.texi
@@ -1238,6 +1238,9 @@ any given seed, the @code{random} function always 
generates the same
 sequence of numbers.  By default, Emacs initializes the random seed at
 startup, in such a way that the sequence of values of @code{random}
 (with overwhelming likelihood) differs in each Emacs run.
+The random seed is typically initialized from system entropy;
+however, on obsolescent platforms lacking entropy pools,
+the seed is taken from less-random volatile data such as the current time.
 
   Sometimes you want the random number sequence to be repeatable.  For
 example, when debugging a program whose behavior depends on the random
@@ -1256,12 +1259,45 @@ nonnegative and less than @var{limit}.  Otherwise, the 
value might be
 any fixnum, i.e., any integer from @code{most-negative-fixnum} through
 @code{most-positive-fixnum} (@pxref{Integer Basics}).
 
-If @var{limit} is @code{t}, it means to choose a new seed as if Emacs
-were restarting, typically from the system entropy.  On systems
-lacking entropy pools, choose the seed from less-random volatile data
-such as the current time.
-
 If @var{limit} is a string, it means to choose a new seed based on the
-string's contents.
+string's contents.  This causes later calls to @code{random} to return
+a reproducible sequence of results.
+
+If @var{limit} is @code{t}, it means to choose a new seed as if Emacs
+were restarting.  This causes later calls to @code{random} to return
+an unpredictable sequence of results.
 
 @end defun
+
+If you need a random nonce for cryptographic purposes, using
+@code{random} is typically not the best approach, for several reasons:
+
+@itemize @bullet
+@item
+Although you can use @code{(random t)} to consult system entropy,
+doing so can adversely affect other parts of your program that benefit
+from reproducible results.
+
+@item
+The system-dependent pseudo-random number generator (PRNG) used by
+@code{random} is not necessarily suitable for cryptography.
+
+@item
+A call to @code{(random t)} does not give direct access to system
+entropy; the entropy is passed through the system-dependent PRNG, thus
+possibly biasing the results.
+
+@item
+On typical platforms the random seed contains only 32 bits, which is
+typically narrower than an Emacs fixnum, and is not nearly enough for
+cryptographic purposes.
+
+@item
+A @code{(random t)} call leaves information about the nonce scattered
+about Emacs's internal state, increasing the size of the internal
+attack surface.
+
+@item
+On obsolescent platforms lacking entropy pools, @code{(random t)} is
+seeded from a cryptographically weak source.
+@end itemize
diff --git a/doc/lispref/processes.texi b/doc/lispref/processes.texi
index db6b4c35ef..adc6909aca 100644
--- a/doc/lispref/processes.texi
+++ b/doc/lispref/processes.texi
@@ -507,7 +507,7 @@ string describing this signal.
 Since there are processes violating this rule, returning exit codes
 greater than 128 which are not bound to a signal, @code{process-file}
 returns always the exit code as natural number for remote processes.
-Setting this user option to non-nil forces @code{process-file} to
+Setting this user option to non-@code{nil} forces @code{process-file} to
 interpret such exit codes as signals, and to return a corresponding
 string.
 @end defopt
@@ -2585,6 +2585,10 @@ that are mainly relevant to encrypted connections:
 @item :nowait @var{boolean}
 If non-@code{nil}, try to make an asynchronous connection.
 
+@item :noquery @var{query-flag}
+Initialize the process query flag to @var{query-flag}.
+@xref{Query Before Exit}.
+
 @item :coding @var{coding}
 Use this to set the coding systems used by the network process, in
 preference to binding @code{coding-system-for-read} or
diff --git a/doc/lispref/searching.texi b/doc/lispref/searching.texi
index 5ee139a11d..ad7f2856de 100644
--- a/doc/lispref/searching.texi
+++ b/doc/lispref/searching.texi
@@ -395,13 +395,12 @@ range should not be the starting point of another one; 
for example,
 @samp{[a-m-z]} should be avoided.
 
 A character alternative can also specify named character classes
-(@pxref{Char Classes}).  This is a POSIX feature.  For example,
-@samp{[[:ascii:]]} matches any @acronym{ASCII} character.
-Using a character class is equivalent to mentioning each of the
-characters in that class; but the latter is not feasible in practice,
-since some classes include thousands of different characters.
-A character class should not appear as the lower or upper bound
-of a range.
+(@pxref{Char Classes}).  For example, @samp{[[:ascii:]]} matches any
+@acronym{ASCII} character.  Using a character class is equivalent to
+mentioning each of the characters in that class; but the latter is not
+feasible in practice, since some classes include thousands of
+different characters.  A character class should not appear as the
+lower or upper bound of a range.
 
 The usual regexp special characters are not special inside a
 character alternative.  A completely different set of characters is
@@ -617,7 +616,7 @@ This matches any character whose code is in the range 0--31.
 This matches @samp{0} through @samp{9}.  Thus, @samp{[-+[:digit:]]}
 matches any digit, as well as @samp{+} and @samp{-}.
 @item [:graph:]
-This matches graphic characters---everything except whitespace,
+This matches graphic characters---everything except spaces,
 @acronym{ASCII} and non-@acronym{ASCII} control characters,
 surrogates, and codepoints unassigned by Unicode, as indicated by the
 Unicode @samp{general-category} property (@pxref{Character
@@ -625,29 +624,39 @@ Properties}).
 @item [:lower:]
 This matches any lower-case letter, as determined by the current case
 table (@pxref{Case Tables}).  If @code{case-fold-search} is
-non-@code{nil}, this also matches any upper-case letter.
+non-@code{nil}, this also matches any upper-case letter.  Note that a
+buffer can have its own local case table different from the default
+one.
 @item [:multibyte:]
 This matches any multibyte character (@pxref{Text Representations}).
 @item [:nonascii:]
 This matches any non-@acronym{ASCII} character.
 @item [:print:]
-This matches any printing character---either whitespace, or a graphic
-character matched by @samp{[:graph:]}.
+This matches any printing character---either spaces or graphic
+characters matched by @samp{[:graph:]}.
 @item [:punct:]
 This matches any punctuation character.  (At present, for multibyte
-characters, it matches anything that has non-word syntax.)
+characters, it matches anything that has non-word syntax, and thus its
+exact definition can vary from one major mode to another, since the
+syntax of a character depends on the major mode.)
 @item [:space:]
 This matches any character that has whitespace syntax
-(@pxref{Syntax Class Table}).
+(@pxref{Syntax Class Table}).  Note that the syntax of a character,
+and thus which characters are considered ``whitespace'',
+depends on the major mode.
 @item [:unibyte:]
 This matches any unibyte character (@pxref{Text Representations}).
 @item [:upper:]
 This matches any upper-case letter, as determined by the current case
 table (@pxref{Case Tables}).  If @code{case-fold-search} is
-non-@code{nil}, this also matches any lower-case letter.
+non-@code{nil}, this also matches any lower-case letter.  Note that a
+buffer can have its own local case table different from the default
+one.
 @item [:word:]
 This matches any character that has word syntax (@pxref{Syntax Class
-Table}).
+Table}).  Note that the syntax of a character, and thus which
+characters are considered ``word-constituent'', depends on the major
+mode.
 @item [:xdigit:]
 This matches the hexadecimal digits: @samp{0} through @samp{9}, @samp{a}
 through @samp{f} and @samp{A} through @samp{F}.
@@ -1052,11 +1061,15 @@ customization.
 @subsubsection Constructs in @code{rx} regexps
 
 The various forms in @code{rx} regexps are described below.  The
-shorthand @var{rx} represents any @code{rx} form, and @var{rx}@dots{}
-means zero or more @code{rx} forms.  These are all valid arguments to
-the @code{rx} macro.  Where the corresponding string
-regexp syntax is given, @var{A}, @var{B}, @dots{} are string regexp
-subexpressions.
+shorthand @var{rx} represents any @code{rx} form.  @var{rx}@dots{}
+means zero or more @code{rx} forms and, unless stated otherwise,
+matches these forms in sequence as if wrapped in a @code{(seq @dots{})}
+subform.
+
+These are all valid arguments to the @code{rx} macro.  All forms are
+defined by their described semantics; the corresponding string regexps
+are provided for ease of understanding only.  @var{A}, @var{B}, @dots{}
+denote (suitably bracketed) string regexp subexpressions therein.
 
 @subsubheading Literals
 
@@ -1290,12 +1303,12 @@ Match any character that has whitespace syntax
 
 @item @code{lower}, @code{lower-case}
 Match anything lower-case, as determined by the current case table.
-If @code{case-fold-search} is non-nil, this also matches any
+If @code{case-fold-search} is non-@code{nil}, this also matches any
 upper-case letter.
 
 @item @code{upper}, @code{upper-case}
 Match anything upper-case, as determined by the current case table.
-If @code{case-fold-search} is non-nil, this also matches any
+If @code{case-fold-search} is non-@code{nil}, this also matches any
 lower-case letter.
 
 @item @code{graph}, @code{graphic}
diff --git a/doc/lispref/strings.texi b/doc/lispref/strings.texi
index cf961e9e7c..4454188cc4 100644
--- a/doc/lispref/strings.texi
+++ b/doc/lispref/strings.texi
@@ -484,7 +484,7 @@ is a multibyte string, we recommend to make sure 
@var{string} is also
 multibyte, even if it's pure-@acronym{ASCII}.
 
 Since it is impossible to change the number of characters in an
-existing string, it is en error if @var{obj} consists of more
+existing string, it is an error if @var{obj} consists of more
 characters than would fit in @var{string} starting at character index
 @var{idx}.
 @end defun
diff --git a/doc/lispref/symbols.texi b/doc/lispref/symbols.texi
index ea1e086ebf..2ef4f8c291 100644
--- a/doc/lispref/symbols.texi
+++ b/doc/lispref/symbols.texi
@@ -718,9 +718,9 @@ byte-compiling any of the two files has equivalent results. 
 The
 shorthands @code{snu-split} and @code{snu-lines} used in the second
 version are @emph{not} interned in the obarray.  This is easily seen
 by moving point to the location where the shorthands are used and
-waiting for ElDoc (@pxref{Lisp Doc, , Local Variables in Files, emacs,
-The GNU Emacs Manual}) to hint at the true full name of the symbol
-under point in the echo area.
+waiting for ElDoc (@pxref{Programming Language Doc, , Local Variables
+in Files, emacs, The GNU Emacs Manual}) to hint at the true full name
+of the symbol under point in the echo area.
 
 Since @code{read-symbol-shorthands} is a file-local variable, it is
 possible that multiple libraries depending on
@@ -794,7 +794,7 @@ this case.
 @end defvar
 
 @defvar print-symbols-bare
-When bound to non-nil, the Lisp printer prints only the bare symbol of
+When bound to non-@code{nil}, the Lisp printer prints only the bare symbol of
 a symbol with position, ignoring the position.
 @end defvar
 
diff --git a/doc/lispref/text.texi b/doc/lispref/text.texi
index 509ce56725..793c22949c 100644
--- a/doc/lispref/text.texi
+++ b/doc/lispref/text.texi
@@ -1525,7 +1525,7 @@ Some commands leave the region active after execution in 
such a way that
 it interferes with selective undo of that command.  To make @code{undo}
 ignore the active region when invoked immediately after such a command,
 set the property @code{undo-inhibit-region} of the command's function
-symbol to a non-nil value.  @xref{Standard Properties}.
+symbol to a non-@code{nil} value.  @xref{Standard Properties}.
 
 @node Maintaining Undo
 @section Maintaining Undo Lists
@@ -4876,7 +4876,7 @@ If the optional argument @var{no-pad} is non-@code{nil} 
then this
 function doesn't generate the padding.
 @end defun
 
-@deffn Command base64-decode-region beg end &optional base64url
+@deffn Command base64-decode-region beg end &optional base64url ignore-invalid
 This function converts the region from @var{beg} to @var{end} from base
 64 code into the corresponding decoded text.  It returns the length of
 the decoded text.
@@ -4885,9 +4885,11 @@ The decoding functions ignore newline characters in the 
encoded text.
 
 If optional argument @var{base64url} is non-@code{nil}, then padding
 is optional, and the URL variant of base 64 encoding is used.
+If optional argument @var{ignore-invalid} is non-@code{nil}, then any
+unrecognized characters are ignored.
 @end deffn
 
-@defun base64-decode-string string &optional base64url
+@defun base64-decode-string string &optional base64url ignore-invalid
 This function converts the string @var{string} from base 64 code into
 the corresponding decoded text.  It returns a unibyte string containing the
 decoded text.
@@ -4897,6 +4899,8 @@ The decoding functions ignore newline characters in the 
encoded text.
 
 If optional argument @var{base64url} is non-@code{nil}, then padding
 is optional, and the URL variant of base 64 encoding is used.
+If optional argument @var{ignore-invalid} is non-@code{nil}, then any
+unrecognized characters are ignored.
 @end defun
 
 @node Checksum/Hash
@@ -5922,7 +5926,7 @@ methods if these concepts apply to the transport.  If 
they do, then
 any system resources (e.g.@: processes, timers, etc.) used to listen for
 messages on the wire should be released in @code{jsonrpc-shutdown},
 i.e.@: they should only be needed while @code{jsonrpc-running-p} is
-non-nil.
+non-@code{nil}.
 
 @end enumerate
 
diff --git a/doc/lispref/variables.texi b/doc/lispref/variables.texi
index 1d891618da..7206f2acd2 100644
--- a/doc/lispref/variables.texi
+++ b/doc/lispref/variables.texi
@@ -1183,7 +1183,7 @@ Here is an example:
 (let ((x 0))             ; @r{@code{x} is lexically bound.}
   (setq my-ticker (lambda ()
                     (setq x (1+ x)))))
-    @result{} (closure ((x . 0) t) ()
+    @result{} (closure ((x . 0)) ()
           (setq x (1+ x)))
 
 (funcall my-ticker)
@@ -2239,9 +2239,26 @@ still respecting file-local variables (@pxref{File Local 
Variables}).
 @cindex connection local variables
 
   Connection-local variables provide a general mechanism for different
-variable settings in buffers with a remote connection.  They are bound
+variable settings in buffers with a remote connection (@pxref{Remote
+Files,, Remote Files, emacs, The GNU Emacs Manual}).  They are bound
 and set depending on the remote connection a buffer is dedicated to.
 
+@menu
+* Connection Local Profiles::            Storing variable settings to
+                                         apply to connections.
+* Applying Connection Local Variables::  Using connection-local values
+                                         in your code.
+@end menu
+
+@node Connection Local Profiles
+@subsection Connection Local Profiles
+@cindex connection local profiles
+
+  Emacs uses connection-local profiles to store the variable settings
+to apply to particular connections.  You can then associate these with
+remote connections by defining the criteria when they should apply,
+using @code{connection-local-set-profiles}.
+
 @defun connection-local-set-profile-variables profile variables
 This function defines a set of variable settings for the connection
 @var{profile}, which is a symbol.  You can later assign the connection
@@ -2311,13 +2328,13 @@ always applies.  Example:
 @example
 @group
 (connection-local-set-profiles
-  '(:application 'tramp :protocol "ssh" :machine "localhost")
+  '(:application tramp :protocol "ssh" :machine "localhost")
   'remote-bash 'remote-null-device)
 @end group
 
 @group
 (connection-local-set-profiles
-  '(:application 'tramp :protocol "sudo"
+  '(:application tramp :protocol "sudo"
     :user "root" :machine "localhost")
   'remote-ksh 'remote-null-device)
 @end group
@@ -2329,13 +2346,13 @@ Therefore, the example above would be equivalent to
 @example
 @group
 (connection-local-set-profiles
-  '(:application 'tramp :protocol "ssh" :machine "localhost")
+  '(:application tramp :protocol "ssh" :machine "localhost")
   'remote-bash)
 @end group
 
 @group
 (connection-local-set-profiles
-  '(:application 'tramp :protocol "sudo"
+  '(:application tramp :protocol "sudo"
     :user "root" :machine "localhost")
   'remote-ksh)
 @end group
@@ -2356,6 +2373,14 @@ names.  The function 
@code{connection-local-set-profiles} updates this
 list.
 @end deffn
 
+@node Applying Connection Local Variables
+@subsection Applying Connection Local Variables
+@cindex connection local variables, applying
+
+  When writing connection-aware code, you'll need to collect, and
+possibly apply, any connection-local variables.  There are several
+ways to do this, as described below.
+
 @defun hack-connection-local-variables criteria
 This function collects applicable connection-local variables
 associated with @var{criteria} in
@@ -2365,7 +2390,7 @@ Example:
 @example
 @group
 (hack-connection-local-variables
-  '(:application 'tramp :protocol "ssh" :machine "localhost"))
+  '(:application tramp :protocol "ssh" :machine "localhost"))
 @end group
 
 @group
@@ -2384,9 +2409,9 @@ This function looks for connection-local variables 
according to
 @var{criteria}, and immediately applies them in the current buffer.
 @end defun
 
-@defmac with-connection-local-variables &rest body
-All connection-local variables, which are specified by
-@code{default-directory}, are applied.
+@defmac with-connection-local-application-variables application &rest body
+Apply all connection-local variables for @code{application}, which are
+specified by @code{default-directory}.
 
 After that, @var{body} is executed, and the connection-local variables
 are unwound.  Example:
@@ -2394,20 +2419,20 @@ are unwound.  Example:
 @example
 @group
 (connection-local-set-profile-variables
-  'remote-perl
-  '((perl-command-name . "/usr/local/bin/perl")
+  'my-remote-perl
+  '((perl-command-name . "/usr/local/bin/perl5")
     (perl-command-switch . "-e %s")))
 @end group
 
 @group
 (connection-local-set-profiles
-  '(:application 'tramp :protocol "ssh" :machine "remotehost")
-  'remote-perl)
+  '(:application my-app :protocol "ssh" :machine "remotehost")
+  'my-remote-perl)
 @end group
 
 @group
 (let ((default-directory "/ssh:remotehost:/working/dir/"))
-  (with-connection-local-variables
+  (with-connection-local-application-variables 'my-app
     do something useful))
 @end group
 @end example
@@ -2416,30 +2441,59 @@ are unwound.  Example:
 @defvar connection-local-default-application
 The default application, a symbol, to be applied in
 @code{with-connection-local-variables}.  It defaults to @code{tramp},
-but in case you want to overwrite Tramp's settings temporarily, you
-could let-bind it like
+but you can let-bind it to change the application temporarily
+(@pxref{Local Variables}).
+
+This variable must not be changed globally.
+@end defvar
+
+@defmac with-connection-local-variables &rest body
+This is equivalent to
+@code{with-connection-local-application-variables}, but uses
+@code{connection-local-default-application} for the application.
+@end defmac
+
+@defmac setq-connection-local [symbol form]@dots{}
+This macro sets each @var{symbol} connection-locally to the result of
+evaluating the corresponding @var{form}, using the connection-local
+profile specified in @code{connection-local-profile-name-for-setq}; if
+the profile name is @code{nil}, this macro will just set the variables
+normally, as with @code{setq} (@pxref{Setting Variables}).
+
+For example, you can use this macro in combination with
+@code{with-connection-local-variables} or
+@code{with-connection-local-application-variables} to lazily
+initialize connection-local settings:
 
 @example
 @group
+(defvar my-app-variable nil)
+
 (connection-local-set-profile-variables
-  'my-remote-perl
-  '((perl-command-name . "/usr/local/bin/perl5")
-    (perl-command-switch . "-e %s")))
-@end group
+ 'my-app-connection-default-profile
+ '((my-app-variable . nil)))
 
-@group
 (connection-local-set-profiles
-  '(:application 'my-app :protocol "ssh" :machine "remotehost")
-  'my-remote-perl)
+ '(:application my-app)
+ 'my-app-connection-default-profile)
 @end group
 
 @group
-(let ((default-directory "/ssh:remotehost:/working/dir/")
-      (connection-local-default-application 'my-app))
-  (with-connection-local-variables
-    do something useful))
+(defun my-app-get-variable ()
+  (with-connection-local-application-variables 'my-app
+    (or my-app-variable
+        (setq-connection-local my-app-variable
+                               do something useful))))
 @end group
 @end example
+@end defmac
+
+@defvar connection-local-profile-name-for-setq
+The connection-local profile name, a symbol, to use when setting
+variables via @code{setq-connection-local}.  This is let-bound in the
+body of @code{with-connection-local-variables}, but you can also
+let-bind it yourself if you'd like to set variables on a different
+profile.
 
 This variable must not be changed globally.
 @end defvar
diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi
index e946a408fd..37884faec7 100644
--- a/doc/lispref/windows.texi
+++ b/doc/lispref/windows.texi
@@ -2624,7 +2624,7 @@ default value is an empty display action, i.e., 
@w{@code{(nil . nil)}}.
 The value of this option is an alist mapping conditions to display
 actions.  Each condition is passed to @code{buffer-match-p}, along
 with the buffer name and the @var{action} argument passed to
-@code{display-buffer}.  If it returns a non-nil value, then
+@code{display-buffer}.  If it returns a non-@code{nil} value, then
 @code{display-buffer} uses the corresponding display action to display
 the buffer.
 @end defopt
diff --git a/doc/misc/ChangeLog.1 b/doc/misc/ChangeLog.1
index 1ee3c14fb9..48637ab608 100644
--- a/doc/misc/ChangeLog.1
+++ b/doc/misc/ChangeLog.1
@@ -1460,7 +1460,7 @@
 
 2013-10-24  Michael Albinus  <michael.albinus@gmx.de>
 
-       * ert.texi (Running Tests Interactively): Adapt examle output.
+       * ert.texi (Running Tests Interactively): Adapt example output.
        (Tests and Their Environment): Mention skip-unless.
 
 2013-10-23  Glenn Morris  <rgm@gnu.org>
diff --git a/doc/misc/Makefile.in b/doc/misc/Makefile.in
index 1d881a5fc7..b6eef7ea79 100644
--- a/doc/misc/Makefile.in
+++ b/doc/misc/Makefile.in
@@ -68,7 +68,7 @@ DOCMISC_W32 = @DOCMISC_W32@
 
 ## Info files to build and install on all platforms.
 INFO_COMMON = auth autotype bovine calc ccmode cl \
-       dbus dired-x ebrowse ede ediff edt eieio \
+       dbus dired-x ebrowse ede ediff edt eglot eieio \
        emacs-mime epa erc ert eshell eudc efaq eww \
        flymake forms gnus emacs-gnutls htmlfontify idlwave ido info.info \
        mairix-el message mh-e modus-themes newsticker nxml-mode octave-mode \
diff --git a/doc/misc/cc-mode.texi b/doc/misc/cc-mode.texi
index 1f12c30b1f..bade04fb95 100644
--- a/doc/misc/cc-mode.texi
+++ b/doc/misc/cc-mode.texi
@@ -580,7 +580,7 @@ you are going to be editing AWK files, @file{README} 
describes how to
 configure your (X)Emacs so that @ccmode{} will supersede the obsolete
 @code{awk-mode.el} which might have been supplied with your (X)Emacs.
 @ccmode{} might not work with older versions of Emacs or XEmacs.  See
-the @ccmode{} release notes at @uref{http://cc-mode.sourceforge.net}
+the @ccmode{} release notes at @uref{https://cc-mode.sourceforge.net}
 for the latest information on Emacs version and package compatibility
 (@pxref{Updating CC Mode}).
 
@@ -2191,7 +2191,7 @@ foo& bar
 
 @defvar c-asymmetry-fontification-flag
 @vindex asymmetry-fontification-flag @r{(c-)}
-When @code{c-asymmetry-fontification-flag} is non-nil (which it is by
+When @code{c-asymmetry-fontification-flag} is non-@code{nil} (which it is by
 default), code like the above, with white space either before or after
 the operator, but not both, is fontified as a declaration.  When the
 variable is nil, such a construct gets the default face.
@@ -3170,7 +3170,7 @@ E. Jones' Filladapt package@footnote{It's available from
 lack a feature that makes it work suboptimally when
 @code{c-comment-prefix-regexp} matches the empty string (which it does
 by default).  A patch for that is available from
-@uref{http://cc-mode.sourceforge.net/,, the CC Mode web site}.},
+@uref{https://cc-mode.sourceforge.net/,, the CC Mode web site}.},
 @c 2005/11/22:  The above is still believed to be the case.
 which handles things like bulleted lists nicely.  There's a convenience
 function @code{c-setup-filladapt} that tunes the relevant variables in
@@ -7583,7 +7583,7 @@ have old versions of @ccmode{} and so should be upgraded. 
 Access to the
 compatibility, etc.@: are all available on the web site:
 
 @quotation
-@uref{http://cc-mode.sourceforge.net/}
+@uref{https://cc-mode.sourceforge.net/}
 @end quotation
 
 
@@ -7617,7 +7617,7 @@ the GNU Bug Tracker at @url{https://debbugs.gnu.org}, 
then sends it on
 to @email{bug-cc-mode@@gnu.org}.  You can also send reports, other
 questions, and suggestions (kudos?@: @t{;-)} to that address.  It's a
 mailing list which you can join or browse an archive of; see the web site at
-@uref{http://cc-mode.sourceforge.net/} for further details.
+@uref{https://cc-mode.sourceforge.net/} for further details.
 
 @cindex announcement mailing list
 If you want to get announcements of new @ccmode{} releases, send the
diff --git a/doc/misc/cl.texi b/doc/misc/cl.texi
index a6747b1096..41499d1953 100644
--- a/doc/misc/cl.texi
+++ b/doc/misc/cl.texi
@@ -3381,9 +3381,9 @@ true for all elements.
 
 @defun cl-reduce function seq @t{&key :from-end :start :end :initial-value 
:key}
 This function returns the result of calling @var{function} on the
-first and second element of @var{seq}, then calling @var{function}
+first and second elements of @var{seq}, then calling @var{function}
 with that result and the third element of @var{seq}, then with that
-result and the third element of @var{seq}, etc.
+result and the fourth element of @var{seq}, etc.
 
 Here is an example.  Suppose @var{function} is @code{*} and @var{seq}
 is the list @code{(2 3 4 5)}.  The first two elements of the list are
@@ -4051,7 +4051,7 @@ following keywords can be used:
 
 @table @code
 @item :read-only
-A non-nil value means the slot should not be @code{setf}-able;
+A non-@code{nil} value means the slot should not be @code{setf}-able;
 the slot's value is determined when the object is created and does
 not change afterward.
 
diff --git a/doc/misc/ede.texi b/doc/misc/ede.texi
index c0c2ef93d9..7a26fe0e57 100644
--- a/doc/misc/ede.texi
+++ b/doc/misc/ede.texi
@@ -1432,7 +1432,7 @@ See @file{ede-proj-obj.el} for examples of the 
combination.
 @item ede-project-placeholder
 @table @asis
 @item Children:
-@w{@xref{ede-project}.}
+@xref{ede-project}.
 @end table
 @end table
 @end table
@@ -1515,12 +1515,12 @@ Make sure placeholder @var{THIS} is replaced with the 
real thing, and pass throu
 @table @code
 @item eieio-speedbar-directory-button
 @table @code
-@item @w{@xref{ede-project-placeholder}.}
+@item @xref{ede-project-placeholder}.
 @table @code
 @item ede-project
 @table @asis
 @item Children:
-@w{@xref{ede-cpp-root-project},} @w{ede-emacs-project,} @w{ede-linux-project,} 
@w{ede-maven-project,} @w{@xref{ede-simple-project},} 
@w{@xref{ede-simple-base-project},} @w{@xref{ede-proj-project},} 
@w{@xref{project-am-makefile},} @w{@xref{ede-step-project}.}
+@xref{ede-cpp-root-project}, @w{ede-emacs-project,} @w{ede-linux-project,} 
@w{ede-maven-project,} @xref{ede-simple-project}, 
@xref{ede-simple-base-project}, @xref{ede-proj-project}, 
@xref{project-am-makefile}, @xref{ede-step-project}.
 @end table
 @end table
 @end table
@@ -1801,9 +1801,9 @@ Commit change to local variables in @var{PROJ}.
 @table @code
 @item eieio-speedbar-directory-button
 @table @code
-@item @w{@xref{ede-project-placeholder}.}
+@item @xref{ede-project-placeholder}.
 @table @code
-@item @w{@xref{ede-project}.}
+@item @xref{ede-project}.
 @table @code
 @item ede-cpp-root-project
 No children
@@ -1923,9 +1923,9 @@ This knows details about or source tree.
 @table @code
 @item eieio-speedbar-directory-button
 @table @code
-@item @w{@xref{ede-project-placeholder}.}
+@item @xref{ede-project-placeholder}.
 @table @code
-@item @w{@xref{ede-project}.}
+@item @xref{ede-project}.
 @table @code
 @item ede-simple-project
 No children
@@ -1953,9 +1953,9 @@ Commit any change to @var{PROJ} to its file.
 @table @code
 @item eieio-speedbar-directory-button
 @table @code
-@item @w{@xref{ede-project-placeholder}.}
+@item @xref{ede-project-placeholder}.
 @table @code
-@item @w{@xref{ede-project}.}
+@item @xref{ede-project}.
 @table @code
 @item ede-simple-base-project
 No children
@@ -1983,9 +1983,9 @@ This one project could control a tree of subdirectories.
 @table @code
 @item eieio-speedbar-directory-button
 @table @code
-@item @w{@xref{ede-project-placeholder}.}
+@item @xref{ede-project-placeholder}.
 @table @code
-@item @w{@xref{ede-project}.}
+@item @xref{ede-project}.
 @table @code
 @item ede-proj-project
 No children
@@ -2173,9 +2173,9 @@ Commit change to local variables in @var{PROJ}.
 @table @code
 @item eieio-speedbar-directory-button
 @table @code
-@item @w{@xref{ede-project-placeholder}.}
+@item @xref{ede-project-placeholder}.
 @table @code
-@item @w{@xref{ede-project}.}
+@item @xref{ede-project}.
 @table @code
 @item project-am-makefile
 No children
@@ -2215,9 +2215,9 @@ buffer being in order to provide a smart default target 
type.
 @table @code
 @item eieio-speedbar-directory-button
 @table @code
-@item @w{@xref{ede-project-placeholder}.}
+@item @xref{ede-project-placeholder}.
 @table @code
-@item @w{@xref{ede-project}.}
+@item @xref{ede-project}.
 @table @code
 @item ede-step-project
 No children
@@ -2371,7 +2371,7 @@ Commit change to local variables in @var{PROJ}.
 @item ede-target
 @table @asis
 @item Children:
-@w{ede-cpp-root-target,} @w{ede-emacs-target-c,} @w{ede-emacs-target-el,} 
@w{ede-emacs-target-misc,} @w{ede-linux-target-c,} @w{ede-linux-target-misc,} 
@w{ede-maven-target-java,} @w{ede-maven-target-c,} @w{ede-maven-target-misc,} 
@w{ede-simple-target,} @w{@xref{ede-proj-target},} @w{@xref{project-am-target}.}
+@w{ede-cpp-root-target,} @w{ede-emacs-target-c,} @w{ede-emacs-target-el,} 
@w{ede-emacs-target-misc,} @w{ede-linux-target-c,} @w{ede-linux-target-misc,} 
@w{ede-maven-target-java,} @w{ede-maven-target-c,} @w{ede-maven-target-misc,} 
@w{ede-simple-target,} @xref{ede-proj-target}, @xref{project-am-target}.
 @end table
 @end table
 @end table
@@ -2577,12 +2577,12 @@ Retrieves the slot @code{menu} from an object of class 
@code{ede-target}
 @table @code
 @item eieio-speedbar-directory-button
 @table @code
-@item @w{@xref{ede-target}.}
+@item @xref{ede-target}.
 @table @code
 @item ede-proj-target
 @table @asis
 @item Children:
-@w{@xref{ede-proj-target-makefile},} @w{ede-proj-target-aux,} 
@w{@xref{ede-proj-target-scheme}.}
+@xref{ede-proj-target-makefile}, @w{ede-proj-target-aux,} 
@xref{ede-proj-target-scheme}.
 @end table
 @end table
 @end table
@@ -2766,14 +2766,14 @@ sources variable.
 @table @code
 @item eieio-speedbar-directory-button
 @table @code
-@item @w{@xref{ede-target}.}
+@item @xref{ede-target}.
 @table @code
-@item @w{@xref{ede-proj-target}.}
+@item @xref{ede-proj-target}.
 @table @code
 @item ede-proj-target-makefile
 @table @asis
 @item Children:
-@w{@xref{semantic-ede-proj-target-grammar},} 
@w{@xref{ede-proj-target-makefile-objectcode},} 
@w{@xref{ede-proj-target-elisp},} 
@w{@xref{ede-proj-target-makefile-miscelaneous},} 
@w{@xref{ede-proj-target-makefile-info}.}
+@xref{semantic-ede-proj-target-grammar}, 
@xref{ede-proj-target-makefile-objectcode}, @xref{ede-proj-target-elisp}, 
@xref{ede-proj-target-makefile-miscelaneous}, 
@xref{ede-proj-target-makefile-info}.
 @end table
 @end table
 @end table
@@ -2864,11 +2864,11 @@ Use @var{CONFIGURATION} as the current configuration to 
query.
 @table @code
 @item eieio-speedbar-directory-button
 @table @code
-@item @w{@xref{ede-target}.}
+@item @xref{ede-target}.
 @table @code
-@item @w{@xref{ede-proj-target}.}
+@item @xref{ede-proj-target}.
 @table @code
-@item @w{@xref{ede-proj-target-makefile}.}
+@item @xref{ede-proj-target-makefile}.
 @table @code
 @item semantic-ede-proj-target-grammar
 No children
@@ -2918,16 +2918,16 @@ Argument @var{THIS} is the target that should insert 
stuff.
 @table @code
 @item eieio-speedbar-directory-button
 @table @code
-@item @w{@xref{ede-target}.}
+@item @xref{ede-target}.
 @table @code
-@item @w{@xref{ede-proj-target}.}
+@item @xref{ede-proj-target}.
 @table @code
-@item @w{@xref{ede-proj-target-makefile}.}
+@item @xref{ede-proj-target-makefile}.
 @table @code
 @item ede-proj-target-makefile-objectcode
 @table @asis
 @item Children:
-@w{@xref{ede-proj-target-makefile-archive},} 
@w{@xref{ede-proj-target-makefile-program}.}
+@xref{ede-proj-target-makefile-archive}, 
@xref{ede-proj-target-makefile-program}.
 @end table
 @end table
 @end table
@@ -2980,13 +2980,13 @@ Argument @var{THIS} is the target to get sources from.
 @table @code
 @item eieio-speedbar-directory-button
 @table @code
-@item @w{@xref{ede-target}.}
+@item @xref{ede-target}.
 @table @code
-@item @w{@xref{ede-proj-target}.}
+@item @xref{ede-proj-target}.
 @table @code
-@item @w{@xref{ede-proj-target-makefile}.}
+@item @xref{ede-proj-target-makefile}.
 @table @code
-@item @w{@xref{ede-proj-target-makefile-objectcode}.}
+@item @xref{ede-proj-target-makefile-objectcode}.
 @table @code
 @item ede-proj-target-makefile-archive
 No children
@@ -3023,18 +3023,18 @@ Makefile.am generator, so use it to add this important 
bin program.
 @table @code
 @item eieio-speedbar-directory-button
 @table @code
-@item @w{@xref{ede-target}.}
+@item @xref{ede-target}.
 @table @code
-@item @w{@xref{ede-proj-target}.}
+@item @xref{ede-proj-target}.
 @table @code
-@item @w{@xref{ede-proj-target-makefile}.}
+@item @xref{ede-proj-target-makefile}.
 @table @code
-@item @w{@xref{ede-proj-target-makefile-objectcode}.}
+@item @xref{ede-proj-target-makefile-objectcode}.
 @table @code
 @item ede-proj-target-makefile-program
 @table @asis
 @item Children:
-@w{@xref{ede-proj-target-makefile-shared-object}.}
+@xref{ede-proj-target-makefile-shared-object}.
 @end table
 @end table
 @end table
@@ -3102,15 +3102,15 @@ Insert bin_PROGRAMS variables needed by target 
@var{THIS}.
 @table @code
 @item eieio-speedbar-directory-button
 @table @code
-@item @w{@xref{ede-target}.}
+@item @xref{ede-target}.
 @table @code
-@item @w{@xref{ede-proj-target}.}
+@item @xref{ede-proj-target}.
 @table @code
-@item @w{@xref{ede-proj-target-makefile}.}
+@item @xref{ede-proj-target-makefile}.
 @table @code
-@item @w{@xref{ede-proj-target-makefile-objectcode}.}
+@item @xref{ede-proj-target-makefile-objectcode}.
 @table @code
-@item @w{@xref{ede-proj-target-makefile-program}.}
+@item @xref{ede-proj-target-makefile-program}.
 @table @code
 @item ede-proj-target-makefile-shared-object
 No children
@@ -3162,16 +3162,16 @@ Makefile.am generator, so use it to add this important 
bin program.
 @table @code
 @item eieio-speedbar-directory-button
 @table @code
-@item @w{@xref{ede-target}.}
+@item @xref{ede-target}.
 @table @code
-@item @w{@xref{ede-proj-target}.}
+@item @xref{ede-proj-target}.
 @table @code
-@item @w{@xref{ede-proj-target-makefile}.}
+@item @xref{ede-proj-target-makefile}.
 @table @code
 @item ede-proj-target-elisp
 @table @asis
 @item Children:
-@w{@xref{ede-proj-target-elisp-autoloads}.}
+@xref{ede-proj-target-elisp-autoloads}.
 @end table
 @end table
 @end table
@@ -3238,13 +3238,13 @@ is found, such as a @code{-version} variable, or the 
standard header.
 @table @code
 @item eieio-speedbar-directory-button
 @table @code
-@item @w{@xref{ede-target}.}
+@item @xref{ede-target}.
 @table @code
-@item @w{@xref{ede-proj-target}.}
+@item @xref{ede-proj-target}.
 @table @code
-@item @w{@xref{ede-proj-target-makefile}.}
+@item @xref{ede-proj-target-makefile}.
 @table @code
-@item @w{@xref{ede-proj-target-elisp}.}
+@item @xref{ede-proj-target-elisp}.
 @table @code
 @item ede-proj-target-elisp-autoloads
 No children
@@ -3353,11 +3353,11 @@ sources variable.
 @table @code
 @item eieio-speedbar-directory-button
 @table @code
-@item @w{@xref{ede-target}.}
+@item @xref{ede-target}.
 @table @code
-@item @w{@xref{ede-proj-target}.}
+@item @xref{ede-proj-target}.
 @table @code
-@item @w{@xref{ede-proj-target-makefile}.}
+@item @xref{ede-proj-target-makefile}.
 @table @code
 @item ede-proj-target-makefile-miscelaneous
 No children
@@ -3409,11 +3409,11 @@ Return a list of files which @var{THIS} target depends 
on.
 @table @code
 @item eieio-speedbar-directory-button
 @table @code
-@item @w{@xref{ede-target}.}
+@item @xref{ede-target}.
 @table @code
-@item @w{@xref{ede-proj-target}.}
+@item @xref{ede-proj-target}.
 @table @code
-@item @w{@xref{ede-proj-target-makefile}.}
+@item @xref{ede-proj-target-makefile}.
 @table @code
 @item ede-proj-target-makefile-info
 No children
@@ -3495,9 +3495,9 @@ when working in Automake mode.
 @table @code
 @item eieio-speedbar-directory-button
 @table @code
-@item @w{@xref{ede-target}.}
+@item @xref{ede-target}.
 @table @code
-@item @w{@xref{ede-proj-target}.}
+@item @xref{ede-proj-target}.
 @table @code
 @item ede-proj-target-scheme
 No children
@@ -3539,12 +3539,12 @@ Tweak the configure file (current buffer) to 
accommodate @var{THIS}.
 @table @code
 @item eieio-speedbar-directory-button
 @table @code
-@item @w{@xref{ede-target}.}
+@item @xref{ede-target}.
 @table @code
 @item project-am-target
 @table @asis
 @item Children:
-@w{@xref{project-am-objectcode},} @w{project-am-header,} 
@w{@xref{project-am-lisp},} @w{@xref{project-am-texinfo},} 
@w{@xref{project-am-man}.}
+@xref{project-am-objectcode}, @w{project-am-header,} @xref{project-am-lisp}, 
@xref{project-am-texinfo}, @xref{project-am-man}.
 @end table
 @end table
 @end table
@@ -3577,14 +3577,14 @@ Edit the target associated w/ this file.
 @table @code
 @item eieio-speedbar-directory-button
 @table @code
-@item @w{@xref{ede-target}.}
+@item @xref{ede-target}.
 @table @code
-@item @w{@xref{project-am-target}.}
+@item @xref{project-am-target}.
 @table @code
 @item project-am-objectcode
 @table @asis
 @item Children:
-@w{@xref{project-am-program},} @w{project-am-lib.}
+@xref{project-am-program}, @w{project-am-lib.}
 @end table
 @end table
 @end table
@@ -3622,11 +3622,11 @@ There are no default header files.
 @table @code
 @item eieio-speedbar-directory-button
 @table @code
-@item @w{@xref{ede-target}.}
+@item @xref{ede-target}.
 @table @code
-@item @w{@xref{project-am-target}.}
+@item @xref{project-am-target}.
 @table @code
-@item @w{@xref{project-am-objectcode}.}
+@item @xref{project-am-objectcode}.
 @table @code
 @item project-am-program
 No children
@@ -3660,9 +3660,9 @@ Additional LD args.
 @table @code
 @item eieio-speedbar-directory-button
 @table @code
-@item @w{@xref{ede-target}.}
+@item @xref{ede-target}.
 @table @code
-@item @w{@xref{project-am-target}.}
+@item @xref{project-am-target}.
 @table @code
 @item @w{project-am-header.}
 @table @code
@@ -3693,9 +3693,9 @@ Return the default macro to 'edit' for this object.
 @table @code
 @item eieio-speedbar-directory-button
 @table @code
-@item @w{@xref{ede-target}.}
+@item @xref{ede-target}.
 @table @code
-@item @w{@xref{project-am-target}.}
+@item @xref{project-am-target}.
 @table @code
 @item @w{project-am-header.}
 @table @code
@@ -3726,9 +3726,9 @@ Return the default macro to 'edit' for this object.
 @table @code
 @item eieio-speedbar-directory-button
 @table @code
-@item @w{@xref{ede-target}.}
+@item @xref{ede-target}.
 @table @code
-@item @w{@xref{project-am-target}.}
+@item @xref{project-am-target}.
 @table @code
 @item project-am-lisp
 No children
@@ -3756,9 +3756,9 @@ Return the default macro to 'edit' for this object.
 @table @code
 @item eieio-speedbar-directory-button
 @table @code
-@item @w{@xref{ede-target}.}
+@item @xref{ede-target}.
 @table @code
-@item @w{@xref{project-am-target}.}
+@item @xref{project-am-target}.
 @table @code
 @item project-am-texinfo
 No children
@@ -3808,9 +3808,9 @@ files in the project.
 @table @code
 @item eieio-speedbar-directory-button
 @table @code
-@item @w{@xref{ede-target}.}
+@item @xref{ede-target}.
 @table @code
-@item @w{@xref{project-am-target}.}
+@item @xref{project-am-target}.
 @table @code
 @item project-am-man
 No children
@@ -3963,7 +3963,7 @@ compile commands.
 @item ede-compilation-program
 @table @asis
 @item Children:
-@w{@xref{ede-compiler},} @w{@xref{ede-linker}.}
+@xref{ede-compiler}, @xref{ede-linker}.
 @end table
 @end table
 @end table
@@ -4071,12 +4071,12 @@ Tweak the configure file (current buffer) to 
accommodate @var{THIS}.
 @table @code
 @item eieio-instance-inheritor
 @table @code
-@item @w{@xref{ede-compilation-program}.}
+@item @xref{ede-compilation-program}.
 @table @code
 @item ede-compiler
 @table @asis
 @item Children:
-@w{@xref{ede-object-compiler},} @w{semantic-ede-grammar-compiler-class.}
+@xref{ede-object-compiler}, @w{semantic-ede-grammar-compiler-class.}
 @end table
 
 @end table
@@ -4179,9 +4179,9 @@ Return a string based on @var{THIS} representing a make 
object variable.
 @table @code
 @item eieio-instance-inheritor
 @table @code
-@item @w{@xref{ede-compilation-program}.}
+@item @xref{ede-compilation-program}.
 @table @code
-@item @w{@xref{ede-compiler}.}
+@item @xref{ede-compiler}.
 @table @code
 @item ede-object-compiler
 No children
@@ -4222,7 +4222,7 @@ Insert variables needed by the compiler @var{THIS}.
 @table @code
 @item eieio-instance-inheritor
 @table @code
-@item @w{@xref{ede-compilation-program}.}
+@item @xref{ede-compilation-program}.
 @table @code
 @item ede-linker
 No children
diff --git a/doc/misc/efaq-w32.texi b/doc/misc/efaq-w32.texi
index 46c257e42e..b58f6be758 100644
--- a/doc/misc/efaq-w32.texi
+++ b/doc/misc/efaq-w32.texi
@@ -1744,7 +1744,7 @@ date now, so no concrete pointers are available.
 
 You will need an implementation of TeX for Windows.
 A number of implementations are listed on the
-@uref{http://www.tug.org/interest.html#free, TeX Users Group} website.
+@uref{https://www.tug.org/interest.html#free, TeX Users Group} website.
 
 @node Spell check
 @section How do I perform spell checks?
@@ -1899,7 +1899,7 @@ Christopher Payne wrote a Visual Studio add-in that makes 
Emacs the
 default text editor, this has now been taken over by Jeff Paquette.
 See the following two URLs for details:
 @itemize
-@item @uref{http://sourceforge.net/projects/visemacs/} for the latest version.
+@item @uref{https://sourceforge.net/projects/visemacs/} for the latest version.
 @item @uref{http://www.smathers.net/VisEmacs.htm} for notes on usage.
 @end itemize
 
@@ -2039,7 +2039,7 @@ this option is set. (I don't see it on VC++ 4.0.)
 @cindex Borland C++, integration with Emacs
 
 Jonathan Arnold has written an
-@uref{http://www.buddydog.org/C++Builder/c++builder.html, EmacsEdit
+@uref{https://www.buddydog.org/C++Builder/c++builder.html, EmacsEdit
 ``expert''} for interfacing C++ Builder and Emacs.
 
 @node Version control
@@ -2194,7 +2194,7 @@ to port software to Windows.
 @cindex image libraries, gnuwin32
 @cindex image libraries, development
 
-@uref{http://gnuwin32.sourceforge.net/}
+@uref{https://gnuwin32.sourceforge.net/}
 
 GnuWin32 provides precompiled native Windows ports of a wide selection
 of Free software and libraries.  Unfortunately, the ports are
diff --git a/doc/misc/efaq.texi b/doc/misc/efaq.texi
index 0da397919d..3f5c2bc1a7 100644
--- a/doc/misc/efaq.texi
+++ b/doc/misc/efaq.texi
@@ -2668,7 +2668,7 @@ To disable or change the way backups are made,
 @cindex Backup files in a single directory
 You can control where Emacs puts backup files by customizing the
 variable @code{backup-directory-alist}.  This variable's value
-specifies that files whose names match specific patters should have
+specifies that files whose names match specific patterns should have
 their backups put in certain directories.  A typical use is to add the
 element @code{("." . @var{dir})} to force Emacs to put @strong{all}
 backup files in the directory @file{dir}.
@@ -3622,13 +3622,13 @@ To build Emacs from source for MS-DOS, see the 
instructions in the file
 on plain DOS, and also on all versions of MS-Windows from version 3.X
 onwards, including Windows XP and Vista. Pre-built binaries may be
 available at
-@uref{http://www.delorie.com/pub/djgpp/current/v2gnu/emacs.README}
+@uref{https://www.delorie.com/pub/djgpp/current/v2gnu/emacs.README}
 
 For a list of other implementations of Emacs (and Emacs
 look-alikes), consult the list of ``Emacs implementations and literature,''
 available at
 
-@uref{http://www.finseth.com/emacs.html}
+@uref{https://www.finseth.com/emacs.html}
 
 Note that while many of these programs look similar to Emacs, they often
 lack certain features, such as the Emacs Lisp extension language.
@@ -3757,7 +3757,7 @@ Various spell-checkers are compatible with Emacs, 
including:
 @table @b
 
 @item Hunspell
-@uref{http://hunspell.sourceforge.net/}
+@uref{https://hunspell.github.io/}
 
 @item GNU Aspell
 @uref{http://aspell.net/}
diff --git a/doc/misc/eglot.texi b/doc/misc/eglot.texi
new file mode 100644
index 0000000000..04bdcc6161
--- /dev/null
+++ b/doc/misc/eglot.texi
@@ -0,0 +1,1160 @@
+\input texinfo  @c -*-texinfo-*-
+@c %**start of header
+@setfilename ../../eglot.info
+@settitle Eglot: The Emacs Client for the Language Server Protocol
+@include docstyle.texi
+@syncodeindex vr cp
+@syncodeindex fn cp
+@c %**end of header
+
+@copying
+This manual is for Eglot, the Emacs LSP client.
+
+Copyright @copyright{} 2022 Free Software Foundation, Inc.
+
+@quotation
+Permission is granted to copy, distribute and/or modify this document
+under the terms of the GNU Free Documentation License, Version 1.3 or
+any later version published by the Free Software Foundation; with no
+Invariant Sections, with the Front-Cover Texts being ``A GNU Manual'',
+and with the Back-Cover Texts as in (a) below.  A copy of the license
+is included in the section entitled ``GNU Free Documentation License''.
+
+(a) The FSF's Back-Cover Text is: ``You have the freedom to copy and
+modify this GNU manual.''
+@end quotation
+@end copying
+
+@dircategory Emacs misc features
+@direntry
+* Eglot: (eglot).             Language Server Protocol client for Emacs.
+@end direntry
+
+@titlepage
+@sp 4
+@c The title is printed in a large font.
+@center @titlefont{User's Guide}
+@sp 1
+@center @titlefont{to}
+@sp 1
+@center @titlefont{Eglot: The Emacs LSP Client}
+@ignore
+@sp 2
+@center release 1.8
+@c -release-
+@end ignore
+@sp 3
+@center Jo@~ao T@'avora & Eli Zaretskii
+@c -date-
+
+@page
+@vskip 0pt plus 1filll
+@insertcopying
+@end titlepage
+
+@contents
+
+@ifnottex
+@node Top
+@top Eglot
+
+@cindex LSP
+@cindex language server protocol
+Eglot is the Emacs client for the @dfn{Language Server Protocol}
+(@acronym{LSP}).  The name ``Eglot'' is an acronym that stands for
+@ifhtml
+``@emph{E}macs Poly@emph{glot}''.
+@end ifhtml
+@ifnothtml
+``Emacs polyGLOT''.
+@end ifnothtml
+@footnote{
+A @dfn{polyglot} is a
+person who is able to use several languages.
+} Eglot provides infrastructure and a set of commands for enriching
+the source code editing capabilities of Emacs via LSP@.  LSP is a
+standardized communications protocol between source code editors (such
+as Emacs) and language servers---programs external to Emacs which
+analyze the source code on behalf of Emacs.  The protocol allows Emacs
+to receive various source code services from the server, such as
+description and location of function calls, types of variables, class
+definitions, syntactic errors, etc.  This way, Emacs doesn't need to
+implement the language-specific parsing and analysis capabilities in
+its own code, but is still capable of providing sophisticated editing
+features that rely on such capabilities, such as automatic code
+completion, go-to definition of function/class, documentation of
+symbol at-point, refactoring, on-the-fly diagnostics, and more.
+
+Eglot itself is completely language-agnostic, but it can support any
+programming language for which there is a language server and an Emacs
+major mode.
+
+This manual documents how to configure, use, and customize Eglot.
+
+@insertcopying
+
+@menu
+* Quick Start::                 For the impatient.
+* Eglot and LSP Servers::       How to work with language servers.
+* Using Eglot::                 Important Eglot commands and variables.
+* Customizing Eglot::           Eglot customization and advanced features.
+* Troubleshooting Eglot::       Troubleshooting and reporting bugs.
+* GNU Free Documentation License::  The license for this manual.
+* Index::
+@end menu
+@end ifnottex
+
+@node Quick Start
+@chapter Quick Start
+@cindex quick start
+
+This chapter provides concise instructions for setting up and using
+Eglot with your programming project in common usage scenarios.  For
+more detailed instructions regarding Eglot setup, @pxref{Eglot and LSP
+Servers}.  @xref{Using Eglot}, for detailed description of using Eglot,
+and see @ref{Customizing Eglot}, for adapting Eglot to less common use
+patterns.
+
+Here's how to start using Eglot with your programming project:
+
+@enumerate
+@item
+Select and install a language server.
+
+Eglot comes pre-configured with many popular language servers, see the
+value of @code{eglot-server-programs}.  If the server(s) mentioned
+there satisfy your needs for the programming language(s) with which
+you want to use Eglot, you just need to make sure those servers are
+installed on your system.  Alternatively, install one or more servers
+of your choice and add them to the value of
+@code{eglot-server-programs}, as described in @ref{Setting Up LSP
+Servers}.
+
+@item
+Turn on Eglot for your project.
+
+To start using Eglot for a project, type @kbd{M-x eglot @key{RET}} in
+a buffer visiting any file that belongs to the project.  This starts
+the language server configured for the programming language of that
+buffer, and causes Eglot to start managing all the files of the
+project which use the same programming language.  The notion of a
+``project'' used by Eglot is the same Emacs uses (@pxref{Projects,,,
+emacs, GNU Emacs Manual}): in the simplest case, the ``project'' is
+the single file you are editing, but it can also be all the files in a
+single directory or a directory tree under some version control
+system, such as Git.
+
+Alternatively, you can start Eglot automatically from the major-mode
+hook of the mode used for the programming language; see @ref{Starting
+Eglot}.
+
+@item
+Use Eglot.
+
+Most Eglot facilities are integrated into Emacs features, such as
+ElDoc, Flymake, Xref, and Imenu.  However, Eglot also provides
+commands of its own, mainly to perform tasks by the LSP server, such
+as @kbd{M-x eglot-rename} (to rename an identifier across the entire
+project), @kbd{M-x eglot-format} (to reformat and reindent code), and
+some others.  @xref{Eglot Commands}, for the detailed list of Eglot
+commands.
+
+@item
+That's it!
+@end enumerate
+
+@node Eglot and LSP Servers
+@chapter Eglot and LSP Servers
+
+This chapter describes how to set up Eglot for your needs, and how to
+start it.
+
+@menu
+* Setting Up LSP Servers::   How to configure LSP servers for your needs.
+* Starting Eglot::              Ways of starting Eglot for your project.
+* Shutting Down LSP Servers::
+@end menu
+
+@node Setting Up LSP Servers
+@section Setting Up LSP Servers
+@cindex setting up LSP server for Eglot
+@cindex language server for Eglot
+
+For Eglot to be useful, it must first be combined with a suitable
+language server.  Usually, that means running the server program
+locally as a child process of Emacs (@pxref{Processes,,, elisp, GNU
+Emacs Lisp Reference Manual}) and communicating with it via the
+standard input and output streams.
+
+The language server program must be installed separately, and is not
+further discussed in this manual; refer to the documentation of the
+particular server(s) you want to install.
+
+To use a language server, Eglot must know how to start it and which
+programming languages each server supports.  This information is
+provided by the variable @code{eglot-server-programs}.
+
+@defvar eglot-server-programs
+This variable associates major modes with names and command-line
+arguments of the language server programs corresponding to the
+programming language of each major mode.  It provides all the
+information that Eglot needs to know about the programming language of
+the source you are editing.
+
+The value of the variable is an alist, whose elements are of the form
+@w{@code{(@var{major-mode} . @var{server})}}.
+
+The @var{major-mode} of the alist elements can be either a symbol of
+an Emacs major mode or a list of the form @w{@code{(@var{mode}
+:language-id @var{id})}}, with @var{mode} being a major-mode symbol
+and @var{id} a string that identifies the language to the server (if
+Eglot cannot by itself convert the major-mode to the language
+identifier string required by the server).  In addition,
+@var{major-mode} can be a list of several major modes specified in one
+of the above forms -- this means a running instance of the associated
+server is responsible for files of multiple major modes or languages
+in the project.
+
+The @var{server} part of the alist elements can be one of the
+following:
+
+@table @code
+@item (@var{program} @var{args}@dots{})
+This says to invoke @var{program} with zero or more arguments
+@var{args}; the program is expected to communicate with Emacs via the
+standard input and standard output streams.
+
+@item (@var{program} @var{args}@dots{} :initializationOptions 
@var{options}@dots{})
+Like above, but with @var{options} specifying the options to be
+used for constructing the @samp{initializationOptions} JSON object for
+the server.  @var{options} can also be a function of one argument, in
+which case it will be called with the server instance as the argument,
+and should return the JSON object to use for initialization.
+
+@item (@var{host} @var{port} @var{args}@dots{})
+Here @var{host} is a string and @var{port} is a positive integer
+specifying a TCP connection to a remote server.  The @var{args} are
+passed to @code{open-network-stream}, e.g.@: if the connection needs
+to use encryption or other non-default parameters (@pxref{Network,,,
+elisp, GNU Emacs Lisp Reference Manual}).
+
+@item (@var{program} @var{args}@dots{} :autoport @var{moreargs}@dots{})
+@var{program} is started with a command line constructed from
+@var{args} followed by an available server port and the rest of
+arguments in @var{moreargs}; Eglot then establishes a TCP connection
+with the server via that port on the local host.
+
+@item @var{function}
+This should be a function of a single argument: non-@code{nil} if the
+connection was requested interactively (e.g., by the @code{eglot}
+command), otherwise @code{nil}.  The function should return a value of
+any of the forms described above.  This allows interaction with the
+user for determining the program to start and its command-line
+arguments.
+@end table
+
+@end defvar
+
+Eglot comes with a fairly complete set of associations of major-modes
+to popular language servers predefined.  If you need to add server
+associations to the default list, use @code{add-to-list}.  For
+example, if there is a hypothetical language server program
+@command{fools} for the language @code{Foo} which is supported by an
+Emacs major-mode @code{foo-mode}, you can add it to the alist like
+this:
+
+@lisp
+(with-eval-after-load 'eglot
+  (add-to-list 'eglot-server-programs
+               '(foo-mode . ("fools" "--stdio"))))
+@end lisp
+
+This will invoke the program @command{fools} with the command-line
+argument @option{--stdio} in support of editing source files for which
+Emacs turns on @code{foo-mode}, and will communicate with the program
+via the standard streams.  As usual with invoking programs, the
+executable file @file{fools} should be in one of the directories
+mentioned by the @code{exec-path} variable (@pxref{Subprocess
+Creation,,, elisp, GNU Emacs Lisp Reference Manual}), for Eglot to be
+able to find it.
+
+Sometimes, multiple servers are acceptable alternatives for handling a
+given major-mode.  In those cases, you may combine the helper function
+@code{eglot-alternatives} with the funcional form of
+@code{eglot-server-programs}.
+
+@lisp
+(with-eval-after-load 'eglot
+  (add-to-list 'eglot-server-programs
+               `(foo-mode . ,(eglot-alternatives
+                               '(("fools" "--stdio")
+                                 ("phewls" "--fast"))))))
+@end lisp
+
+If you have @command{fools} and @command{phewls} installed, the
+function produced by @code{eglot-alternatives} will prompt for the
+server to use in @code{foo-mode} buffers.  Else it will use whichever
+is available.
+
+@node Starting Eglot
+@section Starting Eglot
+@cindex starting Eglot
+@cindex activating Eglot for a project
+
+@findex eglot
+The most common way to start Eglot is to simply visit a source file of
+a given language and use the command @kbd{M-x eglot}.  This starts the
+language server suitable for the visited file's major-mode, and
+attempts to connect to it.  If the connection to the language server
+is successful, you will see the @code{[eglot:@var{project}]} indicator
+on the mode line which reflects the server that was started.  If the
+server program couldn't be started or connection to it failed, you
+will see an error message; in that case, try to troubleshoot the
+problem as described in @ref{Troubleshooting Eglot}.  Once a language
+server was successfully started and Eglot connected to it, you can
+immediately start using the Emacs features supported by Eglot, as
+described in @ref{Eglot Features}.
+
+A single Eglot session for a certain major-mode usually serves all the
+buffers under that mode which visit files from the same project, so
+you don't need to invoke @kbd{M-x eglot} again when you visit another
+file from the same project which is edited using the same major-mode.
+This is because Eglot uses the Emacs project infrastructure, as
+described in @ref{Eglot and Buffers}, and this knows about files that
+belong to the same project.  Thus, after starting an Eglot session for
+some buffer, that session is automatically reused when visiting files
+in the same project with the same major-mode.
+
+@findex eglot-ensure
+Alternatively, you could configure Eglot to start automatically for
+one or more major-modes from the respective mode hooks.  Here's an
+example for a hypothetical @code{foo-mode}:
+
+@lisp
+ (add-hook 'foo-mode-hook 'eglot-ensure)
+@end lisp
+
+@noindent
+The function @code{eglot-ensure} will start an Eglot session for each
+buffer in which @code{foo-mode} is turned on, if there isn't already
+an Eglot session that handles the buffer.  Note that this variant of
+starting an Eglot session is non-interactive, so it should be used
+only when you are confident that Eglot can be started reliably for any
+file which may be visited with the major-mode in question.
+
+When Eglot connects to a language server for the first time in an
+Emacs session, it runs the hook @code{eglot-connect-hook}
+(@pxref{Eglot Variables}).
+
+@node Shutting Down LSP Servers
+@section Shutting Down LSP Servers
+@cindex shutting down LSP server
+
+When Eglot is turned on, it arranges for turning itself off
+automatically if the language server process terminates.  Turning off
+Eglot means that it shuts down the server connection, ceases its
+management of all the buffers that use the server connection which was
+terminated, deactivates its minor mode, and restores the original
+values of the Emacs variables that Eglot changed when it was turned
+on.  @xref{Eglot and Buffers}, for more details of what Eglot
+management of a buffer entails.
+
+@findex eglot-shutdown
+You can also shut down a language server manually, by using the
+command @kbd{M-x eglot-shutdown}.  This prompts for the server (unless
+there's only one connection and it's used in the current buffer), and
+then shuts it down.  By default, it also kills the server's events
+buffer (@pxref{Troubleshooting Eglot}), but a prefix argument prevents
+that.
+
+Alternatively, you can customize the variable
+@code{eglot-autoshutdown} to a non-@code{nil} value, in which case
+Eglot will automatically shut down the language server process when
+the last buffer served by that language server is killed.  The default
+of this variable is @code{nil}, so that visiting another file would
+automatically activate Eglot even when the project which started Eglot
+with the server no longer has any buffer associated with it.  This
+default allows you to start a server only once in each Emacs session.
+
+@node Using Eglot
+@chapter Using Eglot
+
+This chapter describes in detail the features that Eglot provides and
+how it does that.  It also provides reference sections for Eglot
+commands and variables.
+
+@menu
+* Eglot Features::
+* Eglot and Buffers::
+* Eglot Commands::
+* Eglot Variables::
+@end menu
+
+@node Eglot Features
+@section Eglot Features
+@cindex features in buffers supported by Eglot
+
+Once Eglot is enabled in a buffer, it uses LSP and the language-server
+capabilities to activate, enable, and enhance modern IDE features in
+Emacs.  The features themselves are usually provided via other Emacs
+packages.  Here's the list of the main features that Eglot enables and
+provides:
+
+@itemize @bullet
+@item
+At-point documentation: when point is at or near a symbol or an
+identifier, the information about the symbol/identifier, such as the
+signature of a function or class method and server-generated
+diagnostics, is made available via the ElDoc package (@pxref{Lisp
+Doc,,, emacs, GNU Emacs Manual}).  This allows major modes to provide
+extensive help and documentation about the program identifiers.
+
+@item
+On-the-fly diagnostic annotations with server-suggested fixes, via the
+Flymake package (@pxref{Top,,, flymake, GNU Flymake manual}).  This
+improves and enhances the Flymake diagnostics, replacing the other
+Flymake backends.
+
+@item
+Finding definitions and uses of identifiers, via Xref (@pxref{Xref,,,
+emacs, GNU Emacs Manual}).  Eglot provides a backend for the Xref
+capabilities which uses the language-server understanding of the
+program source.  In particular, it eliminates the need to generate
+tags tables (@pxref{Tags tables,,, emacs, GNU Emacs Manual}) for
+languages which are only supported by the @code{etags} backend.
+
+@item
+Buffer navigation by name of function, class, method, etc., via Imenu
+(@pxref{Imenu,,, emacs, GNU Emacs Manual}).  Eglot provides its own
+variant of @code{imenu-create-index-function}, which generates the
+index for the buffer based on language-server program source analysis.
+
+@item
+Enhanced completion of symbol at point by the
+@code{completion-at-point} command (@pxref{Symbol Completion,,, emacs,
+GNU Emacs Manual}).  This uses the language-server's parser data for
+the completion candidates.
+
+@item
+Automatic reformatting of source code as you type it.  This is similar
+to what the @code{eglot-format} command does (see below), but is
+activated automatically as you type.
+
+@item
+If a completion package such as @code{company-mode}, a popular
+third-party completion package (or any other completion package), is
+installed, Eglot enhances it by providing completion candidates based
+on the language-server analysis of the source code.
+(@code{company-mode} can be installed from GNU ELPA.)
+
+@item
+If @code{yasnippet}, a popular third-party package for automatic
+insertion of code templates (snippets), is installed, and the language
+server supports snippet completion candidates, Eglot arranges for the
+completion package to instantiate these snippets using
+@code{yasnippet}.  (@code{yasnippet} can be installed from GNU ELPA.)
+
+@item
+If the popular third-party package @code{markdown-mode} is installed,
+and the server provides at-point documentation formatted as Markdown
+in addition to plain text, Eglot arranges for the ElDoc package to
+enrich this text with fontifications and other nice formatting before
+displaying it to the user.  This makes the documentation shown by
+ElDoc look nicer on display.
+
+@item
+In addition to enabling and enhancing other features and packages,
+Eglot also provides a small number of user commands based directly on
+the capabilities of language servers.  These commands are:
+
+@table @kbd
+@item M-x eglot-rename
+This prompts for a new name for the symbol at point, and then modifies
+all the project source files to rename the symbol to the new name,
+based on editing data received from the language-server.  @xref{Eglot
+and Buffers}, for the details of how project files are defined.
+
+@item M-x eglot-format
+This reformats and prettifies the current active region according to
+source formatting rules of the language-server.  If the region is not
+active, it reformats the entire buffer instead.
+
+@item M-x eglot-format-buffer
+This reformats and prettifies the current buffer according to source
+formatting rules of the language-server.
+
+@cindex code actions
+@item M-x eglot-code-actions
+@itemx M-x eglot-code-action-organize-imports
+@itemx M-x eglot-code-action-quickfix
+@itemx M-x eglot-code-action-extract
+@itemx M-x eglot-code-action-inline
+@itemx M-x eglot-code-action-rewrite
+These command allow you to invoke the so-called @dfn{code actions}:
+requests for the language-server to provide editing commands for
+various code fixes, typically either to fix an error diagnostic or to
+beautify/refactor code.  For example,
+@code{eglot-code-action-organize-imports} rearranges the program
+@dfn{imports}---declarations of modules whose capabilities the program
+uses.  These commands affect all the files that belong to the
+project.  The command @kbd{M-x eglot-code-actions} will pop up a menu
+of code applicable actions at point.
+@end table
+
+@end itemize
+
+Not all servers support the full set of LSP capabilities, but most of
+them support enough to enable the basic set of features mentioned
+above.  Conversely, some servers offer capabilities for which no
+equivalent Emacs package exists yet, and so Eglot cannot (yet) expose
+these capabilities to Emacs users.
+
+@node Eglot and Buffers
+@section Buffers, Projects, and Eglot
+@cindex buffers managed by Eglot
+@cindex projects and Eglot
+
+@cindex workspace
+One of the main strong points of using a language server is that a
+language server has a broad view of the program: it considers more
+than just the single source file you are editing.  Ideally, the
+language server should know about all the source files of your program
+which are written in the language supported by the server.  In the
+language-server parlance, the set of the source files of a program is
+known as a @dfn{workspace}.  The Emacs equivalent of a workspace is a
+@dfn{project} (@pxref{Projects,,, emacs, GNU Emacs Manual}).  Eglot
+fully supports Emacs projects, and considers the file in whose buffer
+Eglot is turned on as belonging to a project.  In the simplest case,
+that file is the entire project, i.e.@: your project consists of a
+single file.  But there are other more complex projects:
+
+@itemize @bullet
+@item
+A single-directory project: several source files in a single common
+directory.
+
+@item
+A VC project: source files in a directory hierarchy under some VCS,
+e.g.@: a VCS repository (@pxref{Version Control,,, emacs, GNU Emacs
+Manual}).
+
+@item
+An EDE project: source files in a directory hierarchy managed via the
+Emacs Development Environment (@pxref{EDE,,, emacs, GNU Emacs
+Manual}).
+@end itemize
+
+Eglot uses Emacs's project management infrastructure to figure out
+which files and buffers belong to what project, so any kind of project
+supported by that infrastructure is automatically supported by Eglot.
+
+When Eglot starts a server program, it does so in the project's root
+directory, which is usually the top-level directory of the project's
+directory hierarchy.  This ensures the language server has the same
+comprehensive view of the project's files as you do.
+
+For example, if you visit the file @file{~/projects/fooey/lib/x.foo}
+and @file{x.foo} belongs to a project rooted at
+@file{~/projects/fooey} (perhaps because a @file{.git} directory
+exists there), then @kbd{M-x eglot} causes the server program to start
+with that root as the current working directory.  The server then will
+analyze not only the file @file{lib/x.foo} you visited, but likely
+also all the other @file{*.foo} files under the
+@file{~/projects/fooey} directory.
+
+In some cases, additional information specific to a given project will
+need to be provided to the language server when starting it.  The
+variable @code{eglot-workspace-configuration} (@pxref{Customizing
+Eglot}) exists for that purpose.  It specifies the parameters and
+their values to communicate to each language server which needs that.
+
+When Eglot is active for a project, it performs several background
+activities on behalf of the project and its buffers:
+
+@itemize @bullet
+@cindex mode-line indication of language server
+@cindex mouse clicks on mode-line, and Eglot
+@vindex eglot-menu
+@item
+All of the project's file-visiting buffers under the same major-mode
+are served by a single language-server connection.  (If the project
+uses several programming languages, there will usually be a separate
+server connection for each group of files written in the same language
+and using the same Emacs major-mode.)  Eglot adds the
+@samp{[eglot:@var{project}]} indication to the mode line of
+each such buffer, where @var{server} is the name of the server and
+@var{project} identifies the project by its root directory.  Clicking
+the mouse on the Eglot mode-line indication activates a menu with
+server-specific items.
+
+@item
+For each buffer in which Eglot is active, it notifies the language
+server that Eglot is @dfn{managing} the file visited by that buffer.
+This tells the language server that the file's contents on disk may no
+longer be up-to-date due to unsaved edits.  Eglot reports to the
+server any changes in the text of each managed buffer, to make the
+server aware of unsaved changes.  This includes your editing of the
+buffer and also changes done automatically by other Emacs features and
+commands.  Killing a buffer relinquishes its management by Eglot and
+notifies the server that the file on disk is up-to-date.
+
+@vindex eglot-managed-mode-hook
+@vindex eglot-managed-p
+@item
+Eglot turns on a special minor mode in each buffer it manages.  This
+minor mode ensures the server is notified about files Eglot manages,
+and also arranges for other Emacs features supported by Eglot
+(@pxref{Eglot Features}) to receive information from the language
+server, by changing the settings of these features.  Unlike other
+minor-modes, this special minor mode is not activated manually by the
+user, but automatically, as the result of starting an Eglot session
+for the buffer.  However, this minor mode provides a hook variable
+@code{eglot-managed-mode-hook} that can be used to customize the Eglot
+management of the buffer.  This hook is run both when the minor mode
+is turned on and when it's turned off; use the variable
+@code{eglot-managed-p} to tell if current buffer is still being
+managed or not.  When Eglot stops managing the buffer, this minor mode
+is turned off, and all the settings that Eglot changed are restored to
+their original values.
+
+@item
+When you visit a file under the same project, whether an existing or a
+new file, its buffer is automatically added to the set of buffers
+managed by Eglot, and the server which supports the buffer's
+major-mode is notified about that.  Thus, visiting a non-existent file
+@file{/home/joe/projects/fooey/lib/y.foo} in the above example will
+notify the server of the @file{*.foo} files' language that a new file
+was added to the project, even before the file appears on disk.  The
+special Eglot minor mode is also turned on automatically in the buffer
+visiting the file.
+@end itemize
+
+@node Eglot Commands
+@section Eglot Commands
+@cindex commands, Eglot
+
+This section provides a reference for the most commonly used Eglot
+commands:
+
+@ftable @code
+@item M-x eglot
+This command adds the current buffer and the file it visits to the
+group of buffers and files managed by Eglot on behalf of a suitable
+language server.  If a language server for the buffer's
+@code{major-mode} (@pxref{Major Modes,,, emacs, GNU Emacs Manual}) is
+not yet running, it will be started; otherwise the buffer and its file
+will be added to those managed by an existing server session.
+
+The command attempts to figure out the buffer's major mode and the
+suitable language server; in case it fails, it might prompt for the
+major mode to use and for the server program to start.  If invoked
+with @kbd{C-u}, it always prompts for the server program, and if
+invoked with @kbd{C-u C-u}, it also prompts for the major mode.
+
+If the language server is successfully started and contacted, this
+command arranges for any other buffers belonging to the same project
+and using the same major mode to use the same language-server session.
+That includes any buffers created by visiting files after this command
+succeeds to connect to a language server.
+
+All the Emacs features that are capable of using Eglot services
+(@pxref{Eglot Features}) are automatically configured by this command
+to start using the language server via Eglot.  To customize which
+Emacs features will be configured to use Eglot, use the
+@code{eglot-stay-out-of} option (@pxref{Customizing Eglot}).
+
+@item M-x eglot-reconnect
+This command shuts down the current connection to the language
+server and immediately restarts it using the same options used
+originally.  This can sometimes be useful to unclog a partially
+malfunctioning server connection.
+
+@item M-x eglot-shutdown
+This command shuts down a language server.  It prompts for a language
+server to shut down (unless there's only one server session, and it
+manages the current buffer).  Then the command shuts down the server
+and stops managing the buffers the server was used for.  Emacs
+features (@pxref{Eglot Features}) that Eglot configured to work with
+the language server are restored back to their original configuration.
+
+Normally, this command kills the buffers used for communicating with
+the language server, but if invoked with a prefix argument @kbd{C-u},
+the command doesn't kill those buffers, allowing them to be used for
+diagnostics and problem reporting (@pxref{Troubleshooting Eglot}).
+
+@item M-x eglot-shutdown-all
+This command shuts down all the language servers active in the current
+Emacs session.  As with @code{eglot-shutdown}, invoking this command
+with a prefix argument avoids killing the buffers used for
+communications with the language servers.
+
+@item M-x eglot-rename
+This command renames the program symbol (a.k.a.@: @dfn{identifier}) at
+point to another name.  It prompts for the new name of the symbol, and
+then modifies all the files in the project which are managed by the
+language server of the current buffer to implement the renaming.
+
+@item M-x eglot-format
+This command reformats the active region according to the
+language-server rules.  If no region is active, it reformats the
+entire current buffer.
+
+@item M-x eglot-format-buffer
+This command reformats the current buffer, in the same manner as
+@code{eglot-format} does.
+
+@item M-x eglot-code-actions
+@itemx mouse-1
+This command asks the server for any @dfn{code actions} applicable at
+point.  It can also be invoked by @kbd{mouse-1} clicking on
+diagnostics provided by the server.
+
+@item M-x eglot-code-action-organize-imports
+@itemx M-x eglot-code-action-quickfix
+@itemx M-x eglot-code-action-extract
+@itemx M-x eglot-code-action-inline
+@itemx M-x eglot-code-action-rewrite
+These commands invoke specific code actions supported by the language
+server.
+@c FIXME: Need more detailed description of each action.
+@end ftable
+
+The following Eglot commands are used less commonly, mostly for
+diagnostic and troubleshooting purposes:
+
+@ftable @code
+@item M-x eglot-events-buffer
+This command pops up the events buffer used for communication with the
+language server of the current buffer.
+
+@item M-x eglot-stderr-buffer
+This command pops up the buffer with the debug info printed by the
+language server to its standard error stream.
+
+@item M-x eglot-forget-pending-continuations
+Forget pending requests for the server of the current buffer.
+@c FIXME: Better description of the need.
+
+@item M-x eglot-signal-didChangeConfiguration
+This command updates the language server configuration according to
+the current value of the variable @code{eglot-workspace-configuration}
+(@pxref{Customizing Eglot}).
+
+@item M-x eglot-clear-status
+Clear the last JSONRPC error for the server of the current buffer.
+Eglot keeps track of erroneous situations encountered by the server in
+its mode-line indication so that the user may inspect the
+communication leading up to it (@pxref{Troubleshooting Eglot}).  If
+the situation is deemed uninteresting or temporary, this command can
+be used to ``forget'' the error.  Note that the command @code{M-x
+eglot-reconnect} can sometimes be used to unclog a temporarily
+malfunctioning server.
+@end ftable
+
+As described in @ref{Eglot Features} most features associated with
+Eglot are actually provided by other Emacs packages and features, and
+Eglot only enhances them by allowing them to use the information
+coming from the language servers.  For completeness, here's the list
+of commands of those other packages that are very commonly used in
+Eglot-managed buffers:
+
+@c Not @ftable, because the index entries should mention Eglot
+@table @code
+@cindex eldoc, and Eglot
+@cindex documentation using Eglot
+@item M-x eldoc
+Ask the ElDoc system for help at point.
+
+@cindex flymake, and Eglot
+@cindex on-the-fly diagnostics using Eglot
+@item M-x flymake-show-buffer-diagnostics
+Ask Flymake system to display diagnostics for the current buffer.
+
+@item M-x flymake-show-project-diagnostics
+Ask Flymake to list diagnostics for all the files in the current
+project.
+
+@cindex xref, and Eglot
+@cindex finding definitions of identifiers using Eglot
+@item M-x xref-find-definitions
+Ask Xref to go the definition of the identifier at point.
+
+@cindex imenu navigation using Eglot
+@item M-x imenu
+Let the user navigate the program source code using buffer index,
+categorizing program elements by syntactic class (class, method,
+variable, etc.) and offering completion.
+
+@cindex symbol completion using Eglot
+@item M-x completion-at-point
+Request completion of the symbol at point.
+@end table
+
+@node Eglot Variables
+@section Eglot Variables
+@cindex variables, Eglot
+
+This section provides a reference for the Eglot user options.
+
+@vtable @code
+@item eglot-autoreconnect
+This option controls the ability to reconnect automatically to the
+language server when Eglot detects that the server process terminated
+unexpectedly.  The default value @code{3} means to attempt reconnection only
+if the previous successful connection lasted for more than that number
+of seconds; a different positive value changes the minimal length of
+the connection to trigger reconnection.  A value of @code{t} means
+always reconnect automatically, and @code{nil} means never reconnect
+(in which case you will need to reconnect manually using @kbd{M-x
+eglot}).
+
+@item eglot-connect-timeout
+This specifies the number of seconds before connection attempt to a
+language server times out.  The value of @code{nil} means never time
+out.  The default is 30 seconds.
+
+@item eglot-sync-connect
+This setting is mainly important for connections which are slow to
+establish.  Whereas the variable @code{eglot-connect-timeout} controls
+how long to wait for, this variable controls whether to block Emacs's
+user interface while waiting.  The default value is @code{3}; a positive
+value means block for that many seconds, then wait for the connection
+in the background.  The value of @code{t} means block during the whole
+waiting period.  The value of @code{nil} or @code{0} means don't block at
+all during the waiting period.
+
+@item eglot-events-buffer-size
+This determines the size of the Eglot events buffer.  @xref{Eglot
+Commands, eglot-events-buffer}, for how to display that buffer.  If
+the value is changed, for it to take effect the connection should be
+restarted using @kbd{M-x eglot-reconnect}.
+@c FIXME: Shouldn't the defcustom do this by itself using the :set
+@c attribute?
+@xref{Troubleshooting Eglot}, for when this could be useful.
+
+@item eglot-autoshutdown
+If this is non-@code{nil}, Eglot shuts down a language server when the
+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
+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.
+
+@item eglot-ignored-server-capabilities
+This variable's value is a list of language server capabilities that
+Eglot should not use.  The default is @code{nil}: Eglot uses all of
+the capabilities supported by each server.
+
+@item eglot-extend-to-xref
+If this is non-@code{nil}, and @kbd{M-.}
+(@code{xref-find-definitions}) lands you in a file outside of your
+project, such as a system-installed library or header file,
+transiently consider that file as managed by the same language server.
+That file is still outside your project (i.e. @code{project-find-file}
+won't find it), but Eglot and the server will consider it to be part
+of the workspace.  The default is @code{nil}.
+
+@item eglot-mode-map
+This variable is the keymap for binding Eglot-related command.  It is
+in effect only as long as the buffer is managed by Eglot.  By default,
+it is empty, with the single exception: @kbd{C-h .} is remapped to
+invoke @code{eldoc-doc-buffer}.  You can bind additional commands in
+this map.  For example:
+
+@lisp
+  (define-key eglot-mode-map (kbd "C-c r") 'eglot-rename)
+  (define-key eglot-mode-map (kbd "C-c o") 'eglot-code-action-organize-imports)
+  (define-key eglot-mode-map (kbd "C-c h") 'eldoc)
+  (define-key eglot-mode-map (kbd "<f6>") 'xref-find-definitions)
+@end lisp
+
+@end vtable
+
+Additional variables, which are relevant for customizing the server
+connections, are documented in @ref{Customizing Eglot}.
+
+@node Customizing Eglot
+@chapter Customizing Eglot
+@cindex customizing Eglot
+
+Eglot itself has a relatively small number of customization options.
+A large part of customizing Eglot to your needs and preferences should
+actually be done via options of the Emacs packages and features which
+Eglot supports and enhances (@pxref{Eglot Features}).  For example:
+
+@itemize @bullet
+@item
+To configure the face used for server-derived errors and warnings,
+customize the Flymake faces @code{flymake-error} and
+@code{flymake-warning}.
+
+@item
+To configure the amount of space taken up by documentation in the
+echo area, customize the ElDoc variable
+@code{eldoc-echo-area-use-multiline-p}.
+
+@item
+To completely change how ElDoc displays the at-point documentation
+destination, customize the ElDoc variable
+@code{eldoc-display-functions}.
+@end itemize
+
+For this reason, this manual describes only how to customize
+Eglot's own operation, which mainly has to do with the server
+connections and the server features to be used by Eglot.
+
+@c @table, not @vtable, because some of the variables are indexed
+@c elsewhere
+@table @code
+@item eglot-server-programs
+This variable determines which language server to start for each
+supported major mode, and how to invoke that server's program.
+@xref{Setting Up LSP Servers}, for the details.
+
+@vindex eglot-strict-mode
+@item eglot-strict-mode
+This is @code{nil} by default, meaning that Eglot is generally lenient
+about non-conforming servers.  If you need to debug a server, set this
+to @w{@code{(disallow-non-standard-keys enforce-required-keys)}}.
+
+@vindex eglot-server-initialized-hook
+@item eglot-server-initialized-hook
+A hook run after the server object is successfully initialized.
+
+@vindex eglot-connect-hook
+@item eglot-connect-hook
+A hook run after connection to the server is successfully
+established.  @xref{Starting Eglot}.
+
+@item eglot-managed-mode-hook
+A hook run after Eglot started or stopped managing a buffer.
+@xref{Eglot and Buffers}, for details of its usage.
+
+@vindex eglot-stay-out-of
+@item eglot-stay-out-of
+This variable's value lists Emacs features that Eglot shouldn't
+automatically try to manage on the user's behalf.  It is useful, for
+example, when you need to use non-LSP Flymake or Company back-ends.
+To have Eglot stay away from some Emacs feature, add that feature's
+symbol or a regexp that will match a symbol's name to the list: for
+example, the symbol @code{xref} to leave Xref alone, or the string
+@samp{company} to stay away from your Company customizations.  Here's an
+example:
+
+@lisp
+(add-to-list 'eglot-stay-out-of 'flymake)
+@end lisp
+
+Note that you can still configure the excluded Emacs features manually
+to use Eglot in your @code{eglot-managed-mode-hook} or via some other
+mechanism.
+
+@vindex eglot-workspace-configuration
+@cindex server workspace configuration
+@item eglot-workspace-configuration
+This variable is meant to be set in the @file{.dir-locals.el} file, to
+provide per-project settings, as described below in more detail.
+@end table
+
+Some language servers need to know project-specific settings, which
+the LSP calls @dfn{workspace configuration}.  Eglot allows such fine
+tuning of per-project settings via the variable
+@code{eglot-workspace-configuration}.  Eglot sends the settings in
+this variable to each server, and each server applies the portion of the
+settings relevant to it and ignores the rest.  These settings are
+communicated to the server initially (upon establishing the
+connection) or when the settings are changed, or in response to a
+configuration request from the server.
+
+In many cases, servers can be configured globally using a
+configuration file in the user's home directory or in the project
+directory, which the language server reads.  For example, the
+@command{pylsp} server for Python reads the file
+@file{~/.config/pycodestyle} and the @command{clangd} server reads the
+file @file{.clangd} anywhere in the current project's directory tree.
+If possible, we recommend using those configuration files that are
+independent of Eglot and Emacs; they have the advantage that they will
+work with other LSP clients as well.
+
+If you do need to provide Emacs-specific configuration for a language
+server, we recommend defining the appropriate value in the
+@file{.dir-locals.el} file in the project's directory.  The value of
+this variable should be a property list of the following format:
+
+@lisp
+ (:@var{server} @var{plist}@dots{})
+@end lisp
+
+@noindent
+Here @code{:@var{server}} identifies a particular language server and
+@var{plist} is the corresponding keyword-value property list of one or
+more parameter settings for that server, serialized by Eglot as a JSON
+object.  @var{plist} may be arbitrarity complex, generally containing
+other keywork-value property sublists corresponding to JSON subobjects.
+The JSON values @code{true}, @code{false}, @code{null} and @code{@{@}}
+are represented by the Lisp values @code{t}, @code{:json-false},
+@code{nil}, and @code{eglot-@{@}}, respectively.
+
+@findex eglot-show-workspace-configuration
+When experimenting with workspace settings, you can use the command
+@kbd{M-x eglot-show-workspace-configuration} to inspect and debug the
+JSON value to be sent to the server.  This helper command works even
+before actually connecting to the server.
+
+Here's an example of defining the workspace-configuration settings for
+a project that uses two different language servers, one for Python,
+the other one for Go (presumably, the project is written in a
+combination of these two languages).  The server for Python in this
+case is @command{pylsp}, the server for Go is @command{gopls}.  The
+value of @code{eglot-workspace-configuration} in this case should be:
+
+@lisp
+((python-mode
+  . ((eglot-workspace-configuration
+      . (:pylsp (:plugins (:jedi_completion (:include_params t
+                                             :fuzzy t)
+                           :pylint (:enabled :json-false)))))))
+ (go-mode
+  . ((eglot-workspace-configuration
+      . (:gopls (:usePlaceholders t))))))
+@end lisp
+
+@noindent
+This should go into the @file{.dir-locals.el} file in the project's
+root directory.  It sets up the value of
+@code{eglot-workspace-configuration} separately for each major mode.
+
+Alternatively, the same configuration could be defined as follows:
+
+@lisp
+((nil
+  . ((eglot-workspace-configuration
+      . (:pylsp (:plugins (:jedi_completion (:include_params t
+                                             :fuzzy t)
+                           :pylint (:enabled :json-false)))
+         :gopls (:usePlaceholders t))))))
+@end lisp
+
+This is an equivalent setup which sets the value for all the
+major-modes inside the project; each server will use only the section
+of the parameters intended for that server, and ignore the rest.
+
+As yet another alternative, you can set the value of
+@code{eglot-workspace-configuration} programmatically, via the
+@code{dir-locals-set-class-variables} function, @pxref{Directory Local
+Variables,,, elisp, GNU Emacs Lisp Reference Manual}.
+
+Finally, if one needs to determine the workspace configuration based
+on some dynamic context, @code{eglot-workspace-configuration} can be
+set to a function.  The function is called with the
+@code{eglot-lsp-server} instance of the connected server (if any) and
+with @code{default-directory} set to the root of the project.  The
+function should return a value of the form described above.
+
+Some servers need special hand-holding to operate correctly.  If your
+server has some quirks or non-conformity, it's possible to extend
+Eglot via Elisp to adapt to it, by defining a suitable
+@code{eglot-initialization-options} method via @code{cl-defmethod}
+(@pxref{Generic Functions,,, elisp, GNU Emacs Lisp Reference Manual}).
+
+Here's an example:
+
+@lisp
+(require 'eglot)
+
+(add-to-list 'eglot-server-programs
+             '((c++-mode c-mode) . (eglot-cquery "cquery")))
+
+(defclass eglot-cquery (eglot-lsp-server) ()
+  :documentation "A custom class for cquery's C/C++ langserver.")
+
+(cl-defmethod eglot-initialization-options ((server eglot-cquery))
+  "Passes through required cquery initialization options"
+  (let* ((root (car (project-roots (eglot--project server))))
+         (cache (expand-file-name ".cquery_cached_index/" root)))
+    (list :cacheDirectory (file-name-as-directory cache)
+          :progressReportFrequencyMs -1)))
+@end lisp
+
+@noindent
+See the doc string of @code{eglot-initialization-options} for more
+details.
+@c FIXME: The doc string of eglot-initialization-options should be
+@c enhanced and extended.
+
+@node Troubleshooting Eglot
+@chapter Troubleshooting Eglot
+@cindex troubleshooting Eglot
+
+This section documents commands and variables that can be used to
+troubleshoot Eglot problems.  It also provides guidelines for
+reporting Eglot bugs in a way that facilitates their resolution.
+
+When you encounter problems with Eglot, try first using the commands
+@kbd{M-x eglot-events-server} and @kbd{M-x eglot-stderr-buffer}.  They
+pop up special buffers that can be used to inspect the communications
+between the Eglot and language server.  In many cases, this will
+indicate the problems or at least provide a hint.
+
+A common and easy-to-fix cause of performance problems is the length
+of these two buffers.  If Eglot is operating correctly but slowly,
+customize the variable @code{eglot-events-buffer-size} (@pxref{Eglot
+Variables}) to limit logging, and thus speed things up.
+
+If you need to report an Eglot bug, please keep in mind that, because
+there are so many variables involved, it is generally both very
+@emph{difficult} and @emph{absolutely essential} to reproduce bugs
+exactly as they happened to you, the user.  Therefore, every bug
+report should include:
+
+@enumerate
+@item
+The transcript of events obtained from the buffer popped up by
+@kbd{M-x eglot-events-buffer}.  If the transcript can be narrowed down
+to show the problematic exchange, so much the better.  This is
+invaluable for the investigation and reproduction of the problem.
+
+@item
+If Emacs signaled an error (an error message was seen or heard), make
+sure to repeat the process after toggling @code{debug-on-error} on
+(via @kbd{M-x toggle-debug-on-error}).  This normally produces a
+backtrace of the error that should also be attached to the bug report.
+
+@item
+An explanation of how to obtain, install, and configure the language
+server you used.  If possible, try to replicate the problem with the
+C/C@t{++} or Python servers, as these are very easy to install.
+
+@item
+A description of how to setup the @emph{minimal} project (one or two
+files and their contents) where the problem happens.
+
+@item
+A recipe to replicate the problem with @emph{a clean Emacs run}.  This
+means @kbd{emacs -Q} invocation or a very minimal (no more that 10
+lines) @file{.emacs} initialization file.  @code{eglot-ensure} and
+@code{use-package} calls are generally @emph{not} needed.
+
+@item
+Make sure to double check all the above elements and re-run the
+recipe to see that the problem is reproducible.
+@end enumerate
+
+Please keep in mind that some problems reported against Eglot may
+actually be bugs in the language server or the Emacs feature/package
+that used Eglot to communicate with the language server.
+
+@node GNU Free Documentation License
+@appendix GNU Free Documentation License
+@include doclicense.texi
+
+@node Index
+@unnumbered Index
+@printindex cp
+
+@bye
diff --git a/doc/misc/eshell.texi b/doc/misc/eshell.texi
index 0ee33f2c2a..96873a3f9a 100644
--- a/doc/misc/eshell.texi
+++ b/doc/misc/eshell.texi
@@ -439,11 +439,6 @@ Print the current local time as a human-readable string.  
This command
 is similar to, but slightly different from, the GNU Coreutils
 @command{date} command.
 
-@item define
-@cmindex define
-Define a variable alias.
-@xref{Variable Aliases, , , elisp, The Emacs Lisp Reference Manual}.
-
 @item diff
 @cmindex diff
 Compare files using Emacs's internal @code{diff} (not to be confused
@@ -699,10 +694,18 @@ used for comparing lists of strings.
 This command can be loaded as part of the eshell-xtra module, which is
 disabled by default.
 
+@item set
+@cmindex set
+Set variable values, using the function @code{set} like a command
+(@pxref{Setting Variables,,, elisp, GNU Emacs Lisp Reference Manual}).
+A variable name can be a symbol, in which case it refers to a Lisp
+variable, or a string, referring to an environment variable
+(@pxref{Arguments}).
+
 @item setq
 @cmindex setq
-Set variable values, using the function @code{setq} like a command.
-@xref{Setting Variables,,, elisp, GNU Emacs Lisp Reference Manual}.
+Set variable values, using the function @code{setq} like a command
+(@pxref{Setting Variables,,, elisp, GNU Emacs Lisp Reference Manual}).
 
 @item source
 @cmindex source
@@ -714,9 +717,12 @@ current environment.
 @cmindex su
 @itemx sudo
 @cmindex sudo
-Uses TRAMP's @command{su} or @command{sudo} method @pxref{Inline methods, , , 
tramp}
-to run a command via @command{su} or @command{sudo}.  These commands
-are in the eshell-tramp module, which is disabled by default.
+@itemx doas
+@cmindex doas
+Uses TRAMP's @command{su}, @command{sudo}, or @command{doas} method
+@pxref{Inline methods, , , tramp} to run a command via @command{su},
+@command{sudo}, or @command{doas}.  These commands are in the
+eshell-tramp module, which is disabled by default.
 
 
 @item substitute
@@ -748,7 +754,9 @@ disabled by default.
 
 @item unset
 @cmindex unset
-Unset an environment variable.
+Unset one or more variables.  As with @command{set}, a variable name
+can be a symbol, in which case it refers to a Lisp variable, or a
+string, referring to an environment variable.
 
 @item wait
 @cmindex wait
@@ -795,12 +803,12 @@ command-line switch:
 
 @table @var
 @item short
-This element, if non-nil, should be a character to be used as a short
+This element, if non-@code{nil}, should be a character to be used as a short
 switch, like @code{-@var{short}}.  At least one of this element and
-@var{long} must be non-nil.
+@var{long} must be non-@code{nil}.
 
 @item long
-This element, if non-nil, should be a string to be used as a long
+This element, if non-@code{nil}, should be a string to be used as a long
 switch, like @code{--@var{long}}.
 
 @item value
@@ -886,12 +894,35 @@ For example, you could handle a subset of the options for 
the
 
 @node Variables
 @section Variables
-Since Eshell is just an Emacs @acronym{REPL}@footnote{
+@vindex eshell-prefer-lisp-variables
+Since Eshell is a combination of an Emacs @acronym{REPL}@footnote{
 Short for ``Read-Eval-Print Loop''.
-}
-, it does not have its own scope, and simply stores variables the same
-you would in an Elisp program.  Eshell provides a command version of
-@code{setq} for convenience.
+} and a command shell, it can refer to variables from two different
+sources: ordinary Emacs Lisp variables, as well as environment
+variables.  By default, when using a variable in Eshell, it will first
+look in the list of built-in variables, then in the list of
+environment variables, and finally in the list of Lisp variables.  If
+you would prefer to use Lisp variables over environment variables, you
+can set @code{eshell-prefer-lisp-variables} to @code{t}.
+
+You can set variables in a few different ways.  To set a Lisp
+variable, you can use the command @samp{setq @var{name} @var{value}},
+which works much like its Lisp counterpart (@pxref{Setting Variables,
+, , elisp, The Emacs Lisp Reference Manual}).  To set an environment
+variable, use @samp{export @var{name}=@var{value}}.  You can also use
+@samp{set @var{variable} @var{value}}, which sets a Lisp variable if
+@var{variable} is a symbol, or an environment variable if it's a
+string (@pxref{Arguments}).  Finally, you can temporarily set
+environment variables for a single command with
+@samp{@var{name}=@var{value} @var{command} @dots{}}.  This is
+equivalent to:
+
+@example
+@{
+  export @var{name}=@var{value}
+  @var{command} @dots{}
+@}
+@end example
 
 @subsection Built-in variables
 Eshell knows a few built-in variables:
@@ -914,6 +945,16 @@ When using @code{$-}, you can also access older 
directories in the
 directory ring via subscripting, e.g.@: @samp{$-[1]} refers to the
 working directory @emph{before} the previous one.
 
+@vindex $PATH
+@item $PATH
+This specifies the directories to search for executable programs.  Its
+value is a string, separated by @code{":"} for Unix and GNU systems,
+and @code{";"} for MS systems.  This variable is connection-aware, so
+whenever you change the current directory to a different host
+(@pxref{Remote Files, , , emacs, The GNU Emacs Manual}),
+the value will automatically update to reflect the search path on that
+host.
+
 @vindex $_
 @item $_
 This refers to the last argument of the last command.  With a
@@ -1138,7 +1179,7 @@ a number if possible.
 
 @item one or both (non-@code{nil}) lists
 Concatenate ``adjacent'' elements of each value (possibly converting
-back to a number as above).  For example, @samp{$list("a" "b")c}
+back to a number as above).  For example, @samp{$(list "a" "b")c}
 returns @samp{("a" "bc")}.
 
 @item anything else
diff --git a/doc/misc/eudc.texi b/doc/misc/eudc.texi
index 0037ba78d3..7293f48f41 100644
--- a/doc/misc/eudc.texi
+++ b/doc/misc/eudc.texi
@@ -85,6 +85,10 @@ LDAP, Lightweight Directory Access Protocol
 BBDB, Big Brother's Insidious Database
 @item
 macOS Contacts
+@item
+@code{ecomplete}, Emacs's electrical completion
+@item
+@code{mailabbrev}, Emacs's abbrev-expansion of mail aliases
 @end itemize
 
 The main features of the EUDC interface are:
@@ -110,6 +114,8 @@ Interface to BBDB to let you insert server records into 
your own BBDB database
 * LDAP::                        What is LDAP ?
 * BBDB::                        What is BBDB ?
 * macOS Contacts::              What is macOS Contacts ?
+* ecomplete::                   What is @code{ecomplete} ?
+* mailabbrev::                  What is @code{mailabbrev}?
 @end menu
 
 
@@ -173,14 +179,73 @@ Address Book; the EUDC macOS Contacts back end also works 
on those
 older versions.
 
 
+@node ecomplete
+@section @code{ecomplete}
+
+@code{ecomplete} is Emacs's ``electric completion'', and it is part of
+Emacs.  It stores all information in an @file{ecompleterc} file, whose
+location, and name can be configured via the variable
+@code{ecomplete-database-file} (which see).  The format of the file
+is:
+
+@display
+((TYPE_1 ITEM_1 ITEM_2 ...)
+ (TYPE_2 ITEM_N+1 ITEM_N+2 ...)
+ ...)
+@end display
+
+That is, it is an alist map where the key is the type of match (so
+that you can have one list of things for ``mail'', and one for, say,
+``mastodon'').  In each of these sections you then have a list where
+each item is of the form:
+
+@display
+(KEY TIMES-USED LAST-TIME-USED STRING)
+@end display
+
+When performing a query, the result will be all items where the search
+term matches all, or part of STRING.
+
+When EUDC performs queries with @code{ecomplete}, the name of each
+attribute making up the query is used as the type in which the lookup
+is performed.  The mapping from EUDC attribute names to
+@code{ecomplete} type names is performed according to the variable
+@code{eudc-ecomplete-attributes-translation-alist} (which see).
+
+
+@node mailabbrev
+@section @code{mailabbrev}
+
+@code{mailabbrev} is Emacs's ``abbrev-expansion of mail aliases'', and
+it is part of Emacs.  It stores all information in a @file{mailrc}
+file, whose location, and name can be configured via the variable
+@code{mail-personal-alias-file} (which see).  The @file{mailrc} file
+has the same format as the @command{mail} and @command{mailx} commands
+use for their startup configuration file.  @code{mailabbrev} processes
+@samp{alias}, and @samp{source} statements in the @file{mailrc} file.
+@samp{alias} statements can define simple aliases and distribution
+lists, and and can be nested in that the alias expansion can contain
+references to other alias definitions.  Forward references, that is
+references to aliases before they are actually defined, are possible,
+too.
+
+Originally, @code{mailabbrev} was designed to be used with
+@code{abbrev-mode}.  The @code{mailabbrev} EUDC backend does not use
+@code{abbrev-mode}, but queries @code{mailabbrev} for alias entries
+only, and returns these as EUDC results.  All entries where the alias
+name exactly equals either the @code{email}, @code{name}, or
+@code{firstname} attribute value in the EUDC query, will be returned
+as matches.  When a @file{mailrc} alias defines a distribution list,
+that is it expands to more than one email address, the EUDC result
+will contain a single entry, which will contain an email attribute
+only, whose value will be a comma-separated list of RFC 5322 formatted
+recipient specifications.
+
+
 @node Installation
 @chapter Installation
 
-Add the following to your @file{.emacs} init file:
-@lisp
-(require 'eudc)
-@end lisp
-This will install EUDC at startup.
+EUDC is built-in to Emacs, and its main functions are autoloaded.
 
 After installing EUDC you will find (the next time you launch Emacs) a
 new @code{Directory Search} submenu in the @samp{Tools} menu that will
@@ -200,6 +265,8 @@ email composition buffers (@pxref{Inline Query Expansion})
 @menu
 * LDAP Configuration::           EUDC needs external support for LDAP
 * macOS Contacts Configuration:: Enable the macOS Contacts backend
+* ecomplete Configuration::      Enable the ecomplete backend
+* mailabbrev Configuration::     Enable the mailabbrev backend
 @end menu
 
 @node LDAP Configuration
@@ -256,7 +323,7 @@ will return all LDAP entries with surnames that begin with
 @code{Smith}.  In every LDAP query it makes, EUDC implicitly appends
 the wildcard character to the end of the last word, except if the word
 corresponds to an attribute which is a member of
-`eudc-ldap-no-wildcard-attributes'.
+@code{eudc-ldap-no-wildcard-attributes}.
 
 @menu
 * Emacs-only Configuration::    Configure with @file{.emacs}
@@ -406,9 +473,9 @@ level to 5 by appending @code{-d 5} to the command line.
 macOS Contacts support is added by means of @file{eudcb-mab.el}, or
 @file{eudcb-macos-contacts.el} which are part of Emacs.
 
-To enable a macOS Contacts backend, first `require' the respective
-library to load it, and then set the `eudc-server' to localhost in
-your init file:
+To enable a macOS Contacts backend, first @code{require} the
+respective library to load it, and then set the @code{eudc-server} to
+localhost in your init file:
 @lisp
 (require 'eudcb-macos-contacts)
 (eudc-macos-contacts-set-server "localhost")
@@ -433,6 +500,32 @@ command-line utility before upgrading to a new version of 
macOS.
 existing configurations, and may be removed in a future release.
 
 
+@node ecomplete Configuration
+@section @code{ecomplete} Configuration
+
+@code{ecomplete} is Emacs's ``electrical completion'', and is part of
+Emacs.  To use it, you will need to set up a database file
+(@pxref{ecomplete}) first.
+
+It will be autoloaded on demand.
+
+You can also enable multi-server queries as described in
+@pxref{Multi-server Queries}.
+
+
+@node mailabbrev Configuration
+@section @code{mailabbrev} Configuration
+
+@code{mailabbrev} is Emacs's ``abbrev-expansion of mail aliases'', and
+it is part of Emacs.  To use it, you will need to set up a database file
+(@pxref{mailabbrev}) first.
+
+It will be autoloaded on demand.
+
+You can also enable multi-server queries as described in
+@pxref{Multi-server Queries}.
+
+
 @node Usage
 @chapter Usage
 
@@ -916,13 +1009,23 @@ in other places, like for example the body of the 
message.
 @section The Server Hotlist
 
 EUDC lets you maintain a list of frequently used servers so that you
-can easily switch from one to another.  This hotlist appears in the
-@samp{Server} submenu.  You select a server in this list by clicking on
-its name.  You can add the current server to the list with the command
-@kbd{M-x eudc-bookmark-current-server}.  The list is contained in the variable
-@code{eudc-server-hotlist} which is stored in and retrieved from the file
-designated by @code{eudc-options-file}.  EUDC also provides a facility to
-edit the hotlist interactively (@pxref{The Hotlist Edit Buffer}).
+can easily switch from one to another.  Most users should configure
+the hotlist via Customize, and store the configuration in the main
+Emacs initialization file.  Configuring it dynamically can be
+confusing, particularly if the hotlist settings are saved to
+@code{eudc-options-file} automatically.  @code{eudc-options-file} is
+historical and support for it is still maintained, but new EUDC users
+should set @code{eudc-ignore-options-file} to @code{t}.
+
+However, this hotlist also appears in the @samp{Server} submenu.  You
+select a server in this list by clicking on its name.  You can add the
+current server to the list with the command @kbd{M-x
+eudc-bookmark-current-server}.  The list is contained in the variable
+@code{eudc-server-hotlist} which is stored in and retrieved from the
+file designated by @code{eudc-options-file}, or normal Emacs
+initialization if @code{eudc-ignore-options-file} is non-nil.  EUDC
+also provides a facility to edit the hotlist interactively (@pxref{The
+Hotlist Edit Buffer}).
 
 The hotlist is also used to make queries on multiple servers
 successively (@pxref{Multi-server Queries}).  The order in which the
@@ -937,6 +1040,14 @@ Add @var{server} to the hotlist of servers
 Add the current server to the hotlist of servers
 @end deffn
 
+@defvar eudc-ignore-options-file
+If non-nil, then EUDC ignores @code{eudc-options-file} and warns or
+issues an error when an attempt is made to use it.  Most users should
+set this, and keep their EUDC configuration in the main Emacs
+initialization file instead.  The separate eudc-options file has
+created confusion for users in the past.
+@end defvar
+
 @defvar eudc-options-file
 The name of a file where EUDC stores its internal variables (the
 hotlist and the current server).  EUDC will try to load that file upon
diff --git a/doc/misc/flymake.texi b/doc/misc/flymake.texi
index 7406557623..c075f0298a 100644
--- a/doc/misc/flymake.texi
+++ b/doc/misc/flymake.texi
@@ -312,7 +312,7 @@ been reported.
 Which fringe (if any) should show the warning/error bitmaps.
 
 @item flymake-wrap-around
-If non-nil, moving to errors with @code{flymake-goto-next-error} and
+If non-@code{nil}, moving to errors with @code{flymake-goto-next-error} and
 @code{flymake-goto-prev-error} wraps around buffer boundaries.
 @end vtable
 
@@ -615,7 +615,7 @@ delimited by @var{beg} and @var{end}.  @var{type} is a 
diagnostic
 symbol (@pxref{Flymake error types}), and @var{text} is a description
 of the problem detected in this region.  Most commonly @var{locus} is
 the buffer object designating for the current buffer being
-syntax-checked.  However, it may be a string nameing a file relative
+syntax-checked.  However, it may be a string naming a file relative
 to the current working directory.  @xref{Foreign and list-only
 diagnostics}, for when this may be useful.  Depending on the type of
 @var{locus}, @var{beg} and @var{end} are both either buffer positions
@@ -878,7 +878,7 @@ line-idx col-idx err-text-idx)}.  @xref{Parsing the output}.
 @item flymake-proc-diagnostic-type-pred
 A function to classify a diagnostic text as particular type of error.
 Should be a function taking an error text and returning a diagnostic
-symbol (@pxref{Flymake error types}).  If non-nil is returned but
+symbol (@pxref{Flymake error types}).  If non-@code{nil} is returned but
 there is no such symbol in that table, a warning is assumed.  If nil
 is returned, an error is assumed.  Can also be a regular expression
 that should match only warnings.  This variable replaces the old
diff --git a/doc/misc/gnus-faq.texi b/doc/misc/gnus-faq.texi
index 3aad985c5a..7cb5621b69 100644
--- a/doc/misc/gnus-faq.texi
+++ b/doc/misc/gnus-faq.texi
@@ -1330,7 +1330,7 @@ details.
 
 However, what you really want is the Insidious Big Brother
 Database bbdb.  Get it from
-@uref{http://bbdb.sourceforge.net/, bbdb's website}.
+@uref{https://bbdb.sourceforge.net/, bbdb's website}.
 Now place the following in @file{~/.gnus.el}, to activate bbdb for Gnus:
 
 @example
@@ -1782,13 +1782,13 @@ when you're online.
 Let's talk about Unix systems first: For the news part,
 the easiest solution is a small nntp server like
 @uref{https://www.leafnode.org/, Leafnode} or
-@uref{http://patrik.iki.fi/sn/, sn},
+@uref{https://patrik.iki.fi/sn/, sn},
 of course you can also install a full featured news
 server like
 @uref{https://www.isc.org/othersoftware/, inn}.
 Then you want to fetch your Mail, popular choices
 are @uref{https://www.fetchmail.info/, fetchmail}
-and @uref{http://pyropus.ca/software/getmail/, getmail}.
+and @uref{https://pyropus.ca/software/getmail/, getmail}.
 You should tell those to write the mail to your disk and
 Gnus to read it from there.  Last but not least the mail
 sending part: This can be done with every MTA like
diff --git a/doc/misc/gnus.texi b/doc/misc/gnus.texi
index b1331e79bf..c4705928d3 100644
--- a/doc/misc/gnus.texi
+++ b/doc/misc/gnus.texi
@@ -8045,7 +8045,7 @@ to a string containing the default command and options 
(default
 @findex gnus-summary-muttprint
 @vindex gnus-summary-muttprint-program
 Save the current article into muttprint.  That is, print it using the
-external program @uref{http://muttprint.sourceforge.net/,
+external program @uref{https://muttprint.sourceforge.net/,
 Muttprint}.  The program name and options to use is controlled by the
 variable @code{gnus-summary-muttprint-program}.
 (@code{gnus-summary-muttprint}).
@@ -9343,7 +9343,7 @@ Use Gnus rendered based on w3m.
 Use @uref{http://emacs-w3m.namazu.org/, emacs-w3m}.
 
 @item w3m-standalone
-Use @uref{http://w3m.sourceforge.net/, w3m}.
+Use @uref{https://w3m.sourceforge.net/, w3m}.
 
 @item links
 Use @uref{http://links.twibright.com/, Links}.
@@ -10497,7 +10497,7 @@ normally, but it'll make this command work a whole lot 
faster.  Of
 course, it'll make group entry somewhat slow.
 
 @vindex gnus-refer-thread-use-search
-If @code{gnus-refer-thread-use-search} is non-nil then those backends
+If @code{gnus-refer-thread-use-search} is non-@code{nil} then those backends
 that know how to find threads directly will search not just in the
 current group but all groups on the same server.
 
@@ -10515,7 +10515,7 @@ is true and the initial referral starts from a summary 
buffer for a
 non-virtual group this may not be possible.  In this case a new
 summary buffer is created holding a virtual group with the result of
 the thread search.)  If @code{gnus-refer-thread-limit-to-thread} is
-non-nil then the summary buffer will be limited to articles in the
+non-@code{nil} then the summary buffer will be limited to articles in the
 thread.
 
 @item M-^ (Summary)
@@ -13602,7 +13602,7 @@ Here's the method for a public spool:
 If you are behind a firewall and only have access to the @acronym{NNTP}
 server from the firewall machine, you can instruct Gnus to @code{rlogin}
 on the firewall machine and connect with
-@uref{http://netcat.sourceforge.net/, netcat} from there to the
+@uref{https://netcat.sourceforge.net/, netcat} from there to the
 @acronym{NNTP} server.
 Doing this can be rather fiddly, but your virtual server definition
 should probably look something like this:
@@ -21569,7 +21569,7 @@ Search Groups}).
 Search queries can be specified one of two ways: either using the
 syntax of the engine responsible for the group you're searching, or
 using Gnus' generalized search syntax.  Set the option
-@code{gnus-search-use-parsed-queries} to a non-nil value to used the
+@code{gnus-search-use-parsed-queries} to a non-@code{nil} value to used the
 generalized syntax.  The advantage of this syntax is that, if you have
 multiple backends indexed by different engines, you don't need to
 remember which one you're searching---it's also possible to issue the
@@ -21592,11 +21592,10 @@ details on Gnus' query language, see @ref{Search 
Queries}.
 
 In order to search for messages from any given server, that server
 must have a search engine associated with it.  IMAP servers do their
-own searching (theoretically it is possible to use a different engine
-to search an IMAP store, but we don't recommend it), but in all other
-cases the user will have to manually specify an engine to use.  This
-can be done at two different levels: by server type, or on a
-per-server basis.
+own searching, and searching IMAP groups will work with no additional
+configuration, but in all other cases the user will have to manually
+specify an engine to use.  This can be done at two different levels:
+by server type, or on a per-server basis.
 
 @vindex gnus-search-default-engines
 The option @code{gnus-search-default-engines} assigns search engines
@@ -21900,14 +21899,13 @@ be found at
 @uref{http://www.rpcurnow.force9.co.uk/mairix/index.html}
 
 Though mairix might not be as flexible as other search tools like
-swish++ or namazu, which you can use via the @code{nnir} back end, it
-has the prime advantage of being incredibly fast.  On current systems, it
-can easily search through headers and message bodies of thousands and
-thousands of mails in well under a second.  Building the database
-necessary for searching might take a minute or two, but only has to be
-done once fully.  Afterwards, the updates are done incrementally and
-therefore are really fast, too.  Additionally, mairix is very easy to set
-up.
+swish++ or namazu, it has the prime advantage of being incredibly
+fast.  On current systems, it can easily search through headers and
+message bodies of thousands and thousands of mails in well under a
+second.  Building the database necessary for searching might take a
+minute or two, but only has to be done once fully.  Afterwards, the
+updates are done incrementally and therefore are really fast, too.
+Additionally, mairix is very easy to set up.
 
 For maximum speed though, mairix should be used with mails stored in
 @code{Maildir} or @code{MH} format (this includes the @code{nnml} back
@@ -22545,6 +22543,21 @@ to you, using @kbd{G b u} and updating the group will 
usually fix this.
 
 @end itemize
 
+@node nnir
+@section Migrating from nnir
+
+@cindex nnir
+
+Gnus' previous search engine was called nnir, and is obsolete as of
+Emacs version 28.  If you've upgraded Emacs and are now getting
+obsolete-variable warnings about @code{nnir-*} variables, migration is
+fairly straightforward.  In addition to the variables raised by the
+warnings, all previous engine-specific variables can be updated by
+simply replacing the @code{nnir-} prefix with @code{gnus-search-}.
+For instance, @code{nnir-notmuch-program} is now
+@code{gnus-search-notmuch-program}.
+
+
 @iftex
 @iflatex
 @chapter Message
@@ -23794,7 +23807,7 @@ On a GNU/Linux system, the @code{display} program is 
included in the
 ImageMagick package.  For external conversion programs look for packages
 with names like @code{netpbm}, @code{libgr-progs} and @code{compface}.
 On Windows, you may use the packages @code{netpbm} and @code{compface}
-from @url{http://gnuwin32.sourceforge.net}.  You need to add the
+from @url{https://gnuwin32.sourceforge.net}.  You need to add the
 @code{bin} directory to your @code{PATH} environment variable.
 @c In fact only the following DLLs and binaries seem to be required:
 @c compface1.dll uncompface.exe libnetpbm10.dll icontopbm.exe
@@ -26329,7 +26342,7 @@ size, it will reject insertion of new entries.
 @end defvar
 
 @defvar gnus-registry-register-all
-If this option is non-nil, the registry will register all messages, as
+If this option is non-@code{nil}, the registry will register all messages, as
 you see them.  This is important to making split-to-parent and
 Message-ID references work correctly, as the registry needs to know
 where all messages are, but it can slow down group opening and the
@@ -26429,7 +26442,7 @@ have to put a rule like this:
 
 in your fancy split setup.
 
-If @code{gnus-registry-register-all} is non-nil (the default), the
+If @code{gnus-registry-register-all} is non-@code{nil} (the default), the
 registry will perform splitting for all messages.  If it is nil,
 splitting will only happen for children of messages you've explicitly
 registered.
@@ -26508,7 +26521,7 @@ Store @code{value} under @code{key} for message 
@code{id}.
 
 @defun gnus-registry-get-id-key (id key)
 Get the data under @code{key} for message @code{id}.  If the option
-@code{gnus-registry-register-all} is non-nil, this function will also
+@code{gnus-registry-register-all} is non-@code{nil}, this function will also
 create an entry for @code{id} if one doesn't exist.
 @end defun
 
@@ -26633,7 +26646,7 @@ connections after the system resumes.  On systems 
compiled with D-Bus
 support (check the value of @code{(featurep 'dbusbind)}), Gnus can
 register a D-Bus signal to automatically close all server connections
 before the system goes to sleep.  To enable this, set
-@code{gnus-dbus-close-on-sleep} to a non-nil value.
+@code{gnus-dbus-close-on-sleep} to a non-@code{nil} value.
 
 For more information about D-Bus and Emacs, @pxref{Top,,, dbus, D-Bus 
integration in Emacs}.
 
@@ -26911,10 +26924,17 @@ Gnus 5.10 on May 1st 2003 (24 releases).
 
 On the January 4th 2004, No Gnus was begun.
 
+Gnus 5.11 was bundled with GNU Emacs 22.1 in June 2007.
+
+A version of No Gnus was released as Gnus 5.13 with GNU Emacs 23.1 in
+July 2009.
+
 On April 19, 2010 Gnus development was moved to Git.
 
 On the January 31th 2012, Ma Gnus was begun.
 
+Since then, Gnus has only been released together with Emacs.
+
 If you happen upon a version of Gnus that has a prefixed name---``(ding)
 Gnus'', ``September Gnus'', ``Red Gnus'', ``Quassia Gnus'',
 ``Pterodactyl Gnus'', ``Oort Gnus'', ``No Gnus'', ``Ma Gnus''---don't
@@ -29080,8 +29100,9 @@ moving articles to a group that has not turned 
auto-expire on.
 @subsubsection Ma Gnus
 @cindex Ma Gnus
 
-I'm sure there will be lots of text here.  It's really spelled 真
-Gnus.
+It's really spelled 真Gnus.  Ma Gnus was the code name for the
+development version of Gnus started in 2012.  These days, Gnus is only
+released together with Emacs.
 
 New features in Ma Gnus:
 
@@ -29111,6 +29132,8 @@ The new hooks @code{gnus-gcc-pre-body-encode-hook} and
 the message body of the Gcc copy of a sent message.
 @xref{Archived Messages}.
 
+For more recent changes, see the Emacs @file{NEWS} files.
+
 @end itemize
 
 @end itemize
diff --git a/doc/misc/mh-e.texi b/doc/misc/mh-e.texi
index ffb219b7d4..1a80c62edb 100644
--- a/doc/misc/mh-e.texi
+++ b/doc/misc/mh-e.texi
@@ -7540,8 +7540,8 @@ Allowlisted message face
 @cindex spam filters, bogofilter
 
 MH-E depends on @uref{https://spamassassin.apache.org/, SpamAssassin},
-@uref{http://bogofilter.sourceforge.net/, bogofilter}, or
-@uref{http://spamprobe.sourceforge.net/, SpamProbe} to throw the dreck
+@uref{https://bogofilter.sourceforge.net/, bogofilter}, or
+@uref{https://spamprobe.sourceforge.net/, SpamProbe} to throw the dreck
 away. This chapter describes briefly how to configure these programs
 to work well with MH-E and how to use MH-E's interface that provides
 continuing education for these programs.
@@ -7721,7 +7721,7 @@ done by adding the following to your @file{crontab}:
 
 Bogofilter is a Bayesian spam filtering program. Get it from your
 local distribution or from the
-@uref{http://bogofilter.sourceforge.net/, bogofilter web site}.
+@uref{https://bogofilter.sourceforge.net/, bogofilter web site}.
 
 Bogofilter is taught by running:
 
@@ -7791,7 +7791,7 @@ bogofilter.
 @cindex spam filters, SpamProbe
 
 SpamProbe is a Bayesian spam filtering program. Get it from your local
-distribution or from the @uref{http://spamprobe.sourceforge.net,
+distribution or from the @uref{https://spamprobe.sourceforge.net,
 SpamProbe web site}.
 
 To use SpamProbe, add the following recipes to @file{~/.procmailrc}:
@@ -8633,7 +8633,7 @@ via SourceForge (@pxref{Bug Reports}).
 @cindex FAQ
 @cindex MH FAQ
 
-The article @uref{http://www.newt.com/faq/mh.html, @cite{MH Frequently
+The article @uref{https://www.newt.com/faq/mh.html, @cite{MH Frequently
 Asked Questions (FAQ) with Answers}} appears monthly in the newsgroup
 @samp{comp.mail.mh}. While very little is there that deals with MH-E
 specifically, there is an incredible wealth of material about MH
diff --git a/doc/misc/modus-themes.org b/doc/misc/modus-themes.org
index 2680fe9eb5..56ba5fd348 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 2.7.0
-#+macro:                 release-date 2022-10-01
-#+macro:                 development-version 2.8.0-dev
+#+macro:                 stable-version 3.0.0
+#+macro:                 release-date 2022-10-28
+#+macro:                 development-version 3.1.0-dev
 #+macro:                 file @@texinfo:@file{@@$1@@texinfo:}@@
 #+macro:                 space @@texinfo:@: @@
 #+macro:                 kbd @@texinfo:@kbd{@@$1@@texinfo:}@@
@@ -35,6 +35,7 @@ Current development target is {{{development-version}}}.
 + Homepage: https://protesilaos.com/emacs/modus-themes.
 + Git repository: https://git.sr.ht/~protesilaos/modus-themes.
 + Mailing list: https://lists.sr.ht/~protesilaos/modus-themes.
++ Backronym: My Old Display Unexpectedly Sharpened ... themes
 
 #+toc: headlines 8 insert TOC here, with eight headline levels
 
@@ -632,7 +633,7 @@ to user options to take effect 
([[#h:3f3c3728-1b34-437d-9d0c-b110f5b161a9][Enabl
 :end:
 #+vindex: modus-themes-deuteranopia
 
-Brief: When non-nil use red/blue color-coding instead of red/green,
+Brief: When non-~nil~ use red/blue color-coding instead of red/green,
 where appropriate.
 
 Symbol: ~modus-themes-deuteranopia~ (=boolean= type)
@@ -679,7 +680,7 @@ Possible values:
 The default is to use a bold typographic weight only when it is
 required.
 
-With a non-nil value (~t~) display several syntactic constructs in bold
+With a non-~nil~ value (~t~) display several syntactic constructs in bold
 weight.  This concerns keywords and other important aspects of code
 syntax.  It also affects certain mode line indicators and command-line
 prompts.
@@ -709,7 +710,7 @@ Possible values:
 The default is to not use slanted text forms (italics) unless it is
 absolutely necessary.
 
-With a non-nil value (~t~) choose to render more faces in italics.  This
+With a non-~nil~ value (~t~) choose to render more faces in italics.  This
 typically affects documentation strings and code comments.
 
 Advanced users may also want to configure the exact attributes of the
@@ -799,7 +800,7 @@ Possible values:
 1. ~nil~ (default)
 2. ~t~
 
-When set to non-nil (~t~), configure some spacing-sensitive faces like Org
+When set to non-~nil~ (~t~), configure some spacing-sensitive faces like Org
 tables and code blocks to always inherit from the ~fixed-pitch~ face.
 This is to ensure that certain constructs like code blocks and tables
 remain monospaced even when users opt for a mode that remaps typeface
@@ -926,8 +927,8 @@ an empty list).  The list can include any of the following 
symbols:
   the form of =(height . FLOAT)=
 + ~all-buttons~
 
-The default (a nil value or an empty list) is a gray background combined
-with a pseudo three-dimensional effect.
+The default (a ~nil~ value or an empty list) is a gray background
+combined with a pseudo three-dimensional effect.
 
 The ~flat~ property makes the button two dimensional.
 
@@ -1068,7 +1069,7 @@ effect, color, and border visibility:
 + A floating point to set the height of the mode line's text.  It can
   also be a cons cell in the form of ~(height . FLOAT)~.
 
-The default (a nil value or an empty list) is a two-dimensional
+The default (a ~nil~ value or an empty list) is a two-dimensional
 rectangle with a border around it.  The active and the inactive mode
 lines use different shades of grayscale values for the background,
 foreground, border.
@@ -1097,7 +1098,7 @@ of NATNUM pixels at the boundaries of the mode lines.  
The default value
 is 1 and does not need to be specified explicitly.  The padding has no
 effect when the ~moody~ property is also used, because Moody already
 applies its own tweaks.  To ensure that the underline is placed at the
-bottom of the mode line, set ~x-underline-at-descent-line~ to non-nil
+bottom of the mode line, set ~x-underline-at-descent-line~ to non-~nil~
 (this is not needed when the ~borderless~ property is also set).  For
 users on Emacs 29, the ~x-use-underline-position-properties~ variable must
 also be set to nil.
@@ -1159,7 +1160,7 @@ colors (which have been carefully designed to be highly 
accessible).
 
 Furthermore, because Moody expects an underline and overline instead of
 a box style, it is strongly advised to set ~x-underline-at-descent-line~
-to a non-nil value.
+to a non-~nil~ value.
 
 Finally, note that various packages which heavily modify the mode line,
 such as =doom-modeline=, =nano-modeline=, =powerline=, =spaceline= may not look
@@ -1183,7 +1184,7 @@ Possible values:
 + ~t~
 
 By default, all tab interfaces use backgrounds which are shades of gray.
-When this option is set to non-nil, the backgrounds become colorful.
+When this option is set to non-~nil~, the backgrounds become colorful.
 
 This affects the built-in ~tab-bar-mode~ and ~tab-line-mode~, as well as the
 Centaur tabs package.
@@ -1202,8 +1203,9 @@ Symbol: ~modus-themes-completions~ (=alist= type 
properties)
 
 This affects Company, Corfu, Flx, Helm, Icomplete/Fido, Ido, Ivy,
 Orderless, Selectrum, Vertico.  The value is an alist that takes the
-form of a =(key . properties)= combination.  Here is a sample, followed
-by a description of the particularities:
+form of a =(KEY . PROPERTIES)= combination.  =KEY= is a symbol, while
+=PROPERTIES= is a list.  Here is a sample, followed by a description
+of the particularities:
 
 #+begin_src emacs-lisp
 (setq modus-themes-completions
@@ -1213,10 +1215,10 @@ by a description of the particularities:
 #+end_src
 
 The ~matches~ key refers to the highlighted characters that correspond
-to the user's input.  By default (nil or an empty list), they 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:
+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:
 
 - ~background~ to add a background color;
 
@@ -1232,10 +1234,10 @@ appear in:
   variable.  The absence of a weight means that bold will be used.
 
 The ~selection~ key applies to the current line or currently matched
-candidate, depending on the specifics of the User Interface.  By default
-(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):
+candidate, depending on the specifics of the user interface.  When its
+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):
 
 - ~accented~ to make the background colorful instead of gray;
 
@@ -1251,7 +1253,11 @@ accepts is as follows (order is not significant):
   cetera.  Valid symbols are defined in the ~modus-themes-weights~
   variable.  The absence of a weight means that bold will be used.
 
-The ~popup~ key takes the same values as ~selection~.
+The ~popup~ key takes the same values as ~selection~.  The only
+difference is that it applies specifically to user interfaces that
+display an inline popup and thus have slightly different styling
+requirements than the minibuffer.  The two prominent packages are
+=company= and =corfu=.
 
 Apart from specifying each key separately, a fallback list is accepted.
 This is only useful when the desired aesthetic is the same across all
@@ -1276,21 +1282,10 @@ corresponding key is simply ignored (~matches~ does not 
have ~accented~
 and ~text-also~, while ~selection~ and ~popup~ do not have
 ~background~).
 
-A concise expression of those associations can be written as follows,
-where the ~car~ is always the key and the ~cdr~ is the list of
-properties (whatever order they may appear in):
-
-#+begin_src emacs-lisp
-(setq modus-themes-completions
-      '((matches extrabold background intense)
-        (selection semibold accented intense)
-        (popup accented)))
-#+end_src
-
 [[#h:2793a224-2109-4f61-a106-721c57c01375][Configure bold and italic faces]].
 
-Also refer to the Orderless documentation for its intersection with
-Company (if you choose to use those in tandem).
+Also refer to the documentation of the ~orderless~ package for its
+intersection with ~company~ (if you choose to use those in tandem).
 
 ** Option for mail citations
 :properties:
@@ -1312,7 +1307,7 @@ Possible values:
 3. ~faint~
 4. ~monochrome~
 
-By default (a nil value) citations are styled with contrasting hues to
+By default (a ~nil~ value) citations are styled with contrasting hues to
 denote their depth.  Colors are easy to tell apart because they
 complement each other, but they otherwise are not very prominent.
 
@@ -1342,17 +1337,16 @@ Symbol: ~modus-themes-fringes~ (=choice= type)
 
 Possible values:
 
-1. ~nil~ (default)
+1. ~nil~
 2. ~subtle~
 3. ~intense~
 
-The default is to use the same color as that of the main background,
-meaning that the fringes are not obvious though they still occupy the
-space given to them by ~fringe-mode~.
+When the value is nil, do not apply a distinct background color.
 
-Options ~subtle~ and ~intense~ apply a gray background, making the fringes
-visible.  The difference between the two is one of degree, as their
-names imply.
+With a value of ~subtle~ use a gray background color that is
+visible yet close to the main background color.
+
+With ~intense~ use a more pronounced gray background color.
 
 ** Option for language checkers
 :properties:
@@ -1435,16 +1429,16 @@ Brief: Control the style of the current line of 
~hl-line-mode~.
 
 Symbol: ~modus-themes-hl-line~ (=choice= type, list of properties)
 
-Possible values are expressed as a list of properties (default is ~nil~ or
-an empty list).  The list can include any of the following symbols:
+The value is a list of properties, each designated by a symbol.  With
+a ~nil~ value, or an empty list, the style is a subtle gray background
+color.
+
+Possible properties are the following symbols:
 
 + ~accented~
 + ~intense~
 + ~underline~
 
-The default (a ~nil~ value or an empty list) is a subtle gray background
-color.
-
 The property ~accented~ changes the background to a colored variant.
 
 An ~underline~ property draws a line below the highlighted area.  Its
@@ -1471,8 +1465,9 @@ In user configuration files the form may look like this:
 (setq modus-themes-hl-line '(underline accented))
 #+end_src
 
-Set ~x-underline-at-descent-line~ to a non-nil value for better results
-with underlines.
+Set ~x-underline-at-descent-line~ to a non-~nil~ value so that the
+placement of the underline coincides with the lower boundary of the
+colored background.
 
 This style affects several packages that enable ~hl-line-mode~, such as
 =elfeed=, =notmuch=, and =mu4e=.
@@ -1506,7 +1501,7 @@ Similarly, the faces for 
~display-line-numbers-major-tick~ and its
 counterpart ~display-line-numbers-minor-tick~ use appropriate styles that
 involve a bespoke background and foreground combination.
 
-With a non-nil value (~t~), line numbers have no background of their own.
+With a non-~nil~ value (~t~), line numbers have no background of their own.
 Instead they retain the primary background of the theme, blending with
 the rest of the buffer.  Foreground values for all relevant faces are
 updated to accommodate this aesthetic.
@@ -1529,7 +1524,7 @@ Possible value:
 2. ~t~
 
 By default all mouseover effects apply a highlight with a subtle colored
-background.  When non-nil, these have a more pronounced effect.
+background.  When non-~nil~, these have a more pronounced effect.
 
 Note that this affects the generic ~highlight~ which, strictly speaking,
 is not limited to mouse usage.
@@ -1713,11 +1708,11 @@ Option ~desaturated~ follows the same principles as 
with the default
 (~nil~), though it tones down all relevant colors.
 
 Option ~bg-only~ applies a background but does not override the text's
-foreground.  This makes it suitable for a non-nil value passed to
+foreground.  This makes it suitable for a non-~nil~ value passed to
 ~diff-font-lock-syntax~ (note: Magit does not support syntax highlighting
 in diffs---last checked on 2021-12-02).
 
-When the user option ~modus-themes-deuteranopia~ is non-nil, all diffs
+When the user option ~modus-themes-deuteranopia~ is non-~nil~, all diffs
 will use a red/blue color-coding system instead of the standard
 red/green.  Other stylistic changes are made in the interest of
 optimizing for such a use-case.
@@ -1776,8 +1771,8 @@ of the block.  Disable the extension of such backgrounds 
by setting
 ~org-fontify-whole-block-delimiter-line~ to nil.
 
 Code blocks use their major mode's colors 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-src-fontify-natively~ is non-~nil~.  While quote/verse blocks require
+setting ~org-fontify-quote-and-verse-blocks~ to a non-~nil~ value.
 
 [[#h:f44cc6e3-b0f1-4a5e-8a90-9e48fa557b50][Update Org block delimiter 
fontification]].
 
@@ -1894,7 +1889,7 @@ An ~event~ key covers (i) headings with a plain time 
stamp that are
 shown on the agenda, also known as events, (ii) entries imported from
 the diary, and (iii) other items that derive from a symbolic expression
 or sexp (phases of the moon, holidays, etc.).  By default all those look
-the same and have a subtle foreground color (the default is a nil value
+the same and have a subtle foreground color (the default is a ~nil~ value
 or an empty list).  This key accepts a list of properties.  Those are:
 
 - ~accented~ applies an accent value to the event's foreground,
@@ -1925,7 +1920,7 @@ A ~scheduled~ key applies to tasks with a scheduled date. 
 By default (a
 ~nil~ value), those use varying shades of yellow to denote (i) a past or
 current date and (ii) a future date.  Valid values are symbols:
 
-- nil (default);
+- ~nil~ (default);
 - ~uniform~ to make all scheduled dates the same color;
 - ~rainbow~ to use contrasting colors for past, present, future
   scheduled dates.
@@ -1963,7 +1958,7 @@ passed as a symbol.  Those are:
   being too late.  The difference between ready and clear states is
   attenuated by painting both of them using shades of green.  This
   option thus highlights the alert and overdue states.
-- When ~modus-themes-deuteranopia~ is non-nil the exact style of the habit
+- When ~modus-themes-deuteranopia~ is non-~nil~ the exact style of the habit
   graph adapts to the needs of users with red-green color deficiency by
   substituting every instance of green with blue or cyan (depending on
   the specifics).
@@ -2108,7 +2103,7 @@ In user configuration files the form may look like this:
 #+end_src
 
 When defining the styles per heading level, it is possible to pass a
-non-nil value (~t~) instead of a list of properties.  This will retain the
+non-~nil~ value (~t~) instead of a list of properties.  This will retain the
 original aesthetic for that level.  For example:
 
 #+begin_src emacs-lisp
@@ -2153,7 +2148,7 @@ and tab line.
 The default is to use the same font as the rest of Emacs, which usually
 is a monospaced family.
 
-With a non-nil value (~t~) apply a proportionately spaced typeface.  This
+With a non-~nil~ value (~t~) apply a proportionately spaced typeface.  This
 is done by assigning the ~variable-pitch~ face to the relevant items.
 
 [[#h:defcf4fc-8fa8-4c29-b12e-7119582cc929][Font configurations for Org and 
others]].
@@ -2839,7 +2834,7 @@ To reset the changes, we apply this and reload the theme:
 
 Users who wish to leverage such a mechanism can opt to implement it
 on-demand by means of a global minor mode.  The following snippet covers
-both themes and expands to some more assosiations in the palette:
+both themes and expands to some more associations in the palette:
 
 #+begin_src emacs-lisp
 (define-minor-mode my-modus-themes-tinted
@@ -4681,6 +4676,7 @@ have lots of extensions, so the "full support" may not be 
100% true…
 + notmuch
 + num3-mode
 + nxml-mode
++ olivetti
 + orderless
 + org*
 + org-journal
@@ -4753,7 +4749,7 @@ have lots of extensions, so the "full support" may not be 
100% true…
 + tab-bar-groups
 + tab-bar-mode
 + tab-line-mode
-+ table (built-in table.el)
++ table (built-in {{{file(table.el)}}})
 + telega
 + telephone-line
 + terraform-mode
@@ -4768,7 +4764,7 @@ have lots of extensions, so the "full support" may not be 
100% true…
 + tuareg
 + typescript
 + undo-tree
-+ vc (vc-dir.el, vc-hooks.el)
++ vc ({{{file(vc-dir.el)}}}, {{{file(vc-hooks.el)}}})
 + vertico
 + vertico-quick
 + vimish-fold
@@ -4793,8 +4789,7 @@ have lots of extensions, so the "full support" may not be 
100% true…
 + yasnippet
 + ztree
 
-Plus many other miscellaneous faces that are provided by the upstream
-GNU Emacs distribution.
+Plus many other miscellaneous faces that are provided by Emacs.
 
 ** Indirectly covered packages
 :properties:
@@ -5397,7 +5392,7 @@ https://github.com/tumashu/company-posframe/]
 :custom_id: h:98bdf319-1e32-4469-8a01-771200fba65c
 :end:
 
-The built-in IRC client ~erc~ has the ability to colorise any text using
+The built-in IRC client ~erc~ has the ability to colorize any text using
 escape sequences that start with =^C= (inserted with {{{kbd(C-q C-c)}}}) and 
are
 followed by a number for the foreground and background.[fn:: This page
 explains the basics, though it is not specific to Emacs:
@@ -5453,7 +5448,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
+~shr-use-fonts~, which is set to non-~nil~ by default.  To use the standard
 font instead, set that variable to nil.
 
 [[#h:defcf4fc-8fa8-4c29-b12e-7119582cc929][Font configurations for Org and 
others]].
@@ -5543,9 +5538,9 @@ ANSI color number 1 (red) from the already-supported 
array of
 :end:
 
 Hints are drawn by [[https://imagemagick.org/][ImageMagick]], not Emacs, i.e., 
ImageMagick doesn't
-know about the hint face unless you tell ImageMagick about it. By
+know about the hint face unless you tell ImageMagick about it.  By
 default, only the foreground and background color attributes are
-passed. The below snippet adds to those the various font attributes.  As
+passed.  The below snippet adds to those the various font attributes.  As
 it queries various faces, specifically ~pdf-links-read-link~ and the faces
 it inherits, it needs to be added to your initialization file after
 you've customized any faces.
@@ -5816,9 +5811,9 @@ In general, an additional source of light other than that 
of the monitor
 can help reduce eye strain: the eyes are more relaxed when they do not
 have to focus on one point to gather light.
 
-The monitor's display settings must be accounted for. Gamma values, in
+The monitor's display settings must be accounted for.  Gamma values, in
 particular, need to be calibrated to neither amplify nor distort the
-perception of black. Same principle for sharpness, brightness, and
+perception of black.  Same principle for sharpness, brightness, and
 contrast as determined by the hardware, which all have an effect on how
 text is read on the screen.
 
@@ -5883,7 +5878,7 @@ interface virtually unusable.
 
 [[#h:5808be52-361a-4d18-88fd-90129d206f9b][Option for links]].
 
-Again, one must exercise judgement in order to avoid discrimination,
+Again, one must exercise judgment in order to avoid discrimination,
 where "discrimination" refers to:
 
 + The treatment of substantially different magnitudes as if they were of
@@ -6125,22 +6120,22 @@ The Modus themes are a collective effort.  Every bit of 
work matters.
   Yiltiz, Ilja Kocken, Iris Garcia, Ivan Popovych, 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, Kostadin Ninev, Len Trigg, Lennart
-  C. Karssen, Luis Miguel Castañeda, Magne Hov, Manuel Uberti, Mark
-  Bestley, Mark Burton, Mark Simpson, Markus Beppler, Matt Armstrong,
-  Mauro Aranda, Maxime Tréca, Michael Goldenberg, Morgan Smith, Morgan
-  Willcock, Murilo Pereira, Nicky van Foreest, Nicolas De Jaeghere,
-  Paul Poloskov, Pengji Zhang, Pete Kazmier, Peter Wu, Philip
-  Kaludercic, Pierre Téchoueyres, Przemysław Kryger, Robert Hepple,
-  Roman Rudakov, Ryan Phillips, Rytis Paškauskas, Rudolf Adamkovič,
-  Sam Kleinman, Samuel Culpepper, Saša Janiška, Shreyas Ragavan, Simon
-  Pugnet, Tassilo Horn, Thibaut Verron, Thomas Heartman, Togan
-  Muftuoglu, Tony Zorman, Trey Merkley, Tomasz Hołubowicz, Toon Claes,
-  Uri Sharf, Utkarsh Singh, Vincent Foley.  As well as users: Ben,
-  CsBigDataHub1, Emacs Contrib, Eugene, Fourchaux, Fredrik, Moesasji,
-  Nick, Summer Emacs, TheBlob42, Trey, bepolymathe, bit9tream,
-  derek-upham, doolio, fleimgruber, gitrj95, iSeeU, jixiuf, okamsn,
-  pRot0ta1p.
+  Fleming, Kévin Le Gouguec, Kevin Kainan Li, Kostadin Ninev, Len
+  Trigg, Lennart C. Karssen, Luis Miguel Castañeda, Magne Hov, Manuel
+  Uberti, Mark Bestley, Mark Burton, Mark Simpson, Markus Beppler,
+  Matt Armstrong, Matthias Fuchs, Mauro Aranda, Maxime Tréca, Michael
+  Goldenberg, Morgan Smith, Morgan Willcock, Murilo Pereira, Nicky van
+  Foreest, Nicolas De Jaeghere, Pablo Stafforini, Paul Poloskov,
+  Pengji Zhang, Pete Kazmier, Peter Wu, Philip Kaludercic, Pierre
+  Téchoueyres, Przemysław Kryger, Robert Hepple, Roman Rudakov, Ryan
+  Phillips, Rytis Paškauskas, Rudolf Adamkovič, Sam Kleinman, Samuel
+  Culpepper, Saša Janiška, Shreyas Ragavan, Simon Pugnet, Tassilo
+  Horn, Thibaut Verron, Thomas Heartman, Togan Muftuoglu, Tony Zorman,
+  Trey Merkley, Tomasz Hołubowicz, Toon Claes, Uri Sharf, Utkarsh
+  Singh, Vincent Foley.  As well as users: Ben, CsBigDataHub1, Emacs
+  Contrib, Eugene, Fourchaux, Fredrik, Moesasji, Nick, Summer Emacs,
+  TheBlob42, Trey, bepolymathe, bit9tream, derek-upham, doolio,
+  fleimgruber, gitrj95, iSeeU, jixiuf, okamsn, pRot0ta1p.
 
 + Packaging :: Basil L.{{{space()}}} Contovounesios, Eli Zaretskii,
   Glenn Morris, Mauro Aranda, Richard Stallman, Stefan Kangas (core
diff --git a/doc/misc/newsticker.texi b/doc/misc/newsticker.texi
index 92dba55c01..fec571d923 100644
--- a/doc/misc/newsticker.texi
+++ b/doc/misc/newsticker.texi
@@ -172,7 +172,7 @@ is fetched for the very first time.
 @vindex newsticker-obsolete-item-max-age
 @item newsticker-keep-obsolete-items
 Obsolete headlines are removed immediately unless
-@code{newsticker-keep-obsolete-items} is non-nil in which case they
+@code{newsticker-keep-obsolete-items} is non-@code{nil} in which case they
 are kept until @code{newsticker-obsolete-item-max-age} is reached.
 
 @vindex newsticker-automatically-mark-items-as-old
diff --git a/doc/misc/octave-mode.texi b/doc/misc/octave-mode.texi
index 31d64c3d84..d7ea54198a 100644
--- a/doc/misc/octave-mode.texi
+++ b/doc/misc/octave-mode.texi
@@ -204,9 +204,10 @@ The GNU Emacs Manual}).  Currently, function names can be 
indexed.
 
 @cindex ElDoc Mode Support
 @vindex octave-eldoc-message-style
-ElDoc mode (@pxref{Lisp Doc,,, emacs, The GNU Emacs Manual}) is
-supported.  By customizing @code{octave-eldoc-message-style} it can be
-changed from displaying one or multi line hints.
+ElDoc mode (@pxref{Programming Language Doc,,, emacs, The GNU Emacs
+Manual}) is supported.  By customizing
+@code{octave-eldoc-message-style} it can be changed from displaying
+one or multi line hints.
 
 @c @cindex TAGS
 @c @cindex Emacs TAGS files
diff --git a/doc/misc/org.org b/doc/misc/org.org
index 1ce99728c6..ae3ca0b64f 100644
--- a/doc/misc/org.org
+++ b/doc/misc/org.org
@@ -68,8 +68,8 @@ of Org, as well as additional information, frequently asked 
questions
 [[https://orgmode.org]].
 
 #+cindex: print edition
-An earlier version (7.3) of this manual is available as a 
[[http://www.network-theory.co.uk/org/manual/][paperback
-book from Network Theory Ltd.]].
+An earlier version (7.3) of this manual was published as a paperback book by
+Network Theory Ltd. in 2010.
 
 ** Installation
 :PROPERTIES:
@@ -3234,7 +3234,7 @@ options:
 
 | Link Type  | Example                                                  |
 |------------+----------------------------------------------------------|
-| http       | =http://staff.science.uva.nl/c.dominik/=                 |
+| http       | =https://staff.science.uva.nl/c.dominik/=                |
 | https      | =https://orgmode.org/=                                   |
 | doi        | =doi:10.1000/182=                                        |
 | file       | =file:/home/dominik/images/jupiter.jpg=                  |
@@ -3567,7 +3567,7 @@ replacement text.  Here is an example:
       '(("bugzilla"        . "http://10.1.2.9/bugzilla/show_bug.cgi?id=";)
         ("Nu Html Checker" . "https://validator.w3.org/nu/?doc=%h";)
        ("duckduckgo"      . "https://duckduckgo.com/?q=%s";)
-        ("omap"            . 
"http://nominatim.openstreetmap.org/search?q=%s&polygon=1";)
+        ("omap"            . 
"https://nominatim.openstreetmap.org/search?q=%s&polygon=1";)
         ("ads"             . 
"https://ui.adsabs.harvard.edu/search/q=%20author%3A\"%s\"";)))
 #+end_src
 
@@ -3596,7 +3596,7 @@ can define them in the file with
 
 #+cindex: @samp{LINK}, keyword
 #+begin_example
-,#+LINK: bugzilla  http://10.1.2.9/bugzilla/show_bug.cgi?id=
+,#+LINK: bugzilla  https://10.1.2.9/bugzilla/show_bug.cgi?id=
 ,#+LINK: duckduckgo https://duckduckgo.com/?q=%s
 #+end_example
 
@@ -13642,7 +13642,7 @@ not have descriptions, such as these links 
=[[file:img.jpg]]= or
 =[[./img.jpg]]=, as direct image insertions in the final PDF output.  In
 the PDF, they are no longer links but actual images embedded on the
 page.  The LaTeX export back-end uses =\includegraphics= macro to
-insert the image.  But for TikZ (http://sourceforge.net/projects/pgf/)
+insert the image.  But for TikZ (https://sourceforge.net/projects/pgf/)
 images, the back-end uses an ~\input~ macro wrapped within
 a ~tikzpicture~ environment.
 
@@ -13982,7 +13982,7 @@ some text in German...
 #+cindex: Markdown export
 
 The Markdown export back-end, "md", converts an Org file to Markdown
-format, as defined at http://daringfireball.net/projects/markdown/.
+format, as defined at https://daringfireball.net/projects/markdown/.
 
 Since it is built on top of the HTML back-end (see [[*HTML Export]]), it
 converts every Org construct not defined in Markdown syntax, such as
@@ -22053,7 +22053,7 @@ MathJax are processed.  When dvipng, dvisvgm, or 
ImageMagick suite is
 used to create images, any LaTeX environment is handled.
 
 [fn:112] These are respectively available at
-[[http://sourceforge.net/projects/dvipng/]], [[http://dvisvgm.bplaced.net/]]
+[[https://sourceforge.net/projects/dvipng/]], [[http://dvisvgm.bplaced.net/]]
 and from the ImageMagick suite.  Choose the converter by setting the
 variable ~org-preview-latex-default-process~ accordingly.
 
@@ -22123,9 +22123,9 @@ semantic relevance.
 
 [fn:130] Please note that exported formulas are part of an HTML
 document, and that signs such as =<=, =>=, or =&= have special
-meanings.  See 
[[http://docs.mathjax.org/en/latest/tex.html#tex-and-latex-in-html-documents][MathJax
 TeX and LaTeX support]].
+meanings.  See 
[[https://docs.mathjax.org/en/latest/tex.html#tex-and-latex-in-html-documents][MathJax
 TeX and LaTeX support]].
 
-[fn:131] See [[http://docs.mathjax.org/en/latest/tex.html#tex-extensions][TeX 
and LaTeX extensions]] in the [[http://docs.mathjax.org][MathJax manual]] to 
learn
+[fn:131] See [[https://docs.mathjax.org/en/latest/tex.html#tex-extensions][TeX 
and LaTeX extensions]] in the [[https://docs.mathjax.org][MathJax manual]] to 
learn
 about extensions.
 
 [fn:132] If the classes on TODO keywords and tags lead to conflicts,
@@ -22140,14 +22140,14 @@ as latexmk, can select the correct bibliography 
compiler.
 which requires the flag =-shell-escape= to be added to
 ~org-latex-pdf-process~.
 
-[fn:135] See 
[[http://docs.oasis-open.org/office/v1.2/OpenDocument-v1.2.html][Open Document 
Format for Office Applications
+[fn:135] See 
[[https://docs.oasis-open.org/office/v1.2/OpenDocument-v1.2.html][Open Document 
Format for Office Applications
 (OpenDocument) Version 1.2]].
 
 [fn:136] See [[http://www.mathtoweb.com/cgi-bin/mathtoweb_home.pl][MathToWeb]].
 
-[fn:137] See [[http://dlmf.nist.gov/LaTeXML/]].
+[fn:137] See [[https://dlmf.nist.gov/LaTeXML/]].
 
-[fn:138] 
[[http://docs.oasis-open.org/office/v1.2/OpenDocument-v1.2.html][OpenDocument-v1.2
 Specification]]
+[fn:138] 
[[https://docs.oasis-open.org/office/v1.2/OpenDocument-v1.2.html][OpenDocument-v1.2
 Specification]]
 
 [fn:139] See the =<table:table-template>= element of the
 OpenDocument-v1.2 specification.
@@ -22170,7 +22170,7 @@ are not evaluated when they appear in a keyword (see 
[[*Summary of
 In-Buffer Settings]]).
 
 [fn:144] For noweb literate programming details, see
-http://www.cs.tufts.edu/~nr/noweb/.
+https://www.cs.tufts.edu/~nr/noweb/.
 
 [fn:145] For more information, please refer to the commentary section
 in =org-tempo.el=.
diff --git a/doc/misc/rcirc.texi b/doc/misc/rcirc.texi
index 307fe55a63..84933a9ca7 100644
--- a/doc/misc/rcirc.texi
+++ b/doc/misc/rcirc.texi
@@ -259,7 +259,7 @@ Use @kbd{C-c C-@key{SPC}} to switch to these buffers.
 
 @vindex rcirc-track-ignore-server-buffer-flag
 If the user wishes to ignore events in the server buffer, set
-@code{rcirc-track-ignore-server-buffer-flag} to a non-nil value.
+@code{rcirc-track-ignore-server-buffer-flag} to a non-@code{nil} value.
 
 @node Reference
 @chapter Reference
@@ -693,7 +693,7 @@ buffers.
 
 @cindex rcirc-track-abbrevate-flag
 By default the channel names are abbreviated, set
-@code{rcirc-track-abbrevate-flag} to a non-nil value. This might be
+@code{rcirc-track-abbrevate-flag} to a non-@code{nil} value. This might be
 interesting if the IRC activities are not tracked in the mode line,
 but somewhere else.
 
diff --git a/doc/misc/reftex.texi b/doc/misc/reftex.texi
index b30e5aeaa4..e85f096f99 100644
--- a/doc/misc/reftex.texi
+++ b/doc/misc/reftex.texi
@@ -3605,7 +3605,7 @@ menu.  @xref{Key Bindings}.
 
 @deffn Command reftex-toc
 Show the table of contents for the current document.  When called with
-one ore two @kbd{C-u} prefixes, rescan the document first.
+one or two @kbd{C-u} prefixes, rescan the document first.
 @end deffn
 
 @deffn Command reftex-label
diff --git a/doc/misc/remember.texi b/doc/misc/remember.texi
index 9d1fe545d4..80bb6966e2 100644
--- a/doc/misc/remember.texi
+++ b/doc/misc/remember.texi
@@ -424,13 +424,6 @@ The default priority for remembered mail messages.
 @section Saving to an Org Mode file
 @cindex org mode, integration
 
-@ignore
-From org.texi:
-Up to version 6.36 Org used a special setup
-for @file{remember.el}.  @file{org-remember.el} is still part of Org mode for
-backward compatibility with existing setups.  You can find the documentation
-for org-remember at @url{http://orgmode.org/org-remember.pdf}.
-@end ignore
 For instructions on how to integrate Remember with Org Mode,
 consult @ref{Capture, , , org}.
 
diff --git a/doc/misc/sem-user.texi b/doc/misc/sem-user.texi
index 3141ab7c69..36e09b152e 100644
--- a/doc/misc/sem-user.texi
+++ b/doc/misc/sem-user.texi
@@ -621,7 +621,7 @@ Run the Semantic idle work function with debugging turned 
on.
 Semantic Idle Summary mode is a minor mode that displays a short
 summary of the symbol at point, such as its function prototype, in the
 echo area.  Its functionality is similar to what ElDoc mode provides
-for Emacs Lisp (@pxref{Lisp Doc,,,emacs,Emacs manual}).
+for Emacs Lisp (@pxref{Programming Language Doc,,,emacs,Emacs manual}).
 
 @deffn global-semantic-idle-summary-mode &optional arg
 This command toggles Semantic Idle Summary mode in all
diff --git a/doc/misc/semantic.texi b/doc/misc/semantic.texi
index 25ba30d13c..b23e4c36fe 100644
--- a/doc/misc/semantic.texi
+++ b/doc/misc/semantic.texi
@@ -82,7 +82,7 @@ hippie-expand, and several other parts of Emacs.
 
 To send bug reports, or participate in discussions about semantic,
 use the mailing list cedet-semantic@@sourceforge.net via the URL:
-@url{http://lists.sourceforge.net/lists/listinfo/cedet-semantic}
+@url{https://lists.sourceforge.net/lists/listinfo/cedet-semantic}
 
 @ifnottex
 @insertcopying
diff --git a/doc/misc/sieve.texi b/doc/misc/sieve.texi
index df03dd0144..77a393192c 100644
--- a/doc/misc/sieve.texi
+++ b/doc/misc/sieve.texi
@@ -339,7 +339,7 @@ Indicate which script on the server should be active.
 The Emacs Sieve package implements all or parts of a small but
 hopefully growing number of RFCs and drafts documents.  This chapter
 lists the relevant ones.  They can all be fetched from
-@uref{http://quimby.gnus.org/notes/}.
+@uref{https://quimby.gnus.org/notes/}.
 
 @table @dfn
 
diff --git a/doc/misc/tramp.texi b/doc/misc/tramp.texi
index 7de64829c0..99a268367b 100644
--- a/doc/misc/tramp.texi
+++ b/doc/misc/tramp.texi
@@ -3532,9 +3532,14 @@ the @file{~/.authinfo.gpg} authentication file.  The 
user option
 @code{tramp-completion-use-auth-sources} controls, whether such a
 search is performed during completion.
 
+@vindex tramp-completion-use-cache
 Remote hosts previously visited or hosts whose connections are kept
 persistently (@pxref{Connection caching}) will be included in the
-completion lists.
+completion lists.  If you want to suppress this completion because
+there are invalid entries in the persistency file, for example if the
+host configuration changes often, or if you plug your laptop to
+different networks frequently, you can set the user option
+@code{tramp-completion-use-cache} to nil.
 
 After remote host name completion comes completion of file names on
 the remote host.  It works the same as with local host file completion
diff --git a/doc/misc/transient.texi b/doc/misc/transient.texi
index 24c3090ef7..e5e7cccbe8 100644
--- a/doc/misc/transient.texi
+++ b/doc/misc/transient.texi
@@ -945,7 +945,7 @@ that multiple suffix commands can be bound to the same key, 
provided
 they are never active at the same time, see @ref{Predicate Slots}.
 
 Unfortunately both false-positives and false-negatives are possible.
-To deal with the former use non-nil @var{KEEP-OTHER@.}  To deal with the
+To deal with the former use non-@code{nil} @var{KEEP-OTHER@.}  To deal with the
 latter remove the conflicting binding explicitly.
 @end defun
 
@@ -1957,7 +1957,7 @@ probably don't want that.
 @item
 @code{transient-suffix} and @code{transient-non-suffix} play a part when
 determining whether the currently active transient prefix command
-remains active/transient when a suffix or abitrary non-suffix
+remains active/transient when a suffix or arbitrary non-suffix
 command is invoked.  @xref{Transient State}.
 
 @item
diff --git a/doc/misc/url.texi b/doc/misc/url.texi
index 546639b017..420a2b56c5 100644
--- a/doc/misc/url.texi
+++ b/doc/misc/url.texi
@@ -268,7 +268,7 @@ argument is @code{nil}, the allowed characters are those 
specified as
 @dfn{unreserved characters} by RFC 3986 (see the variable
 @code{url-unreserved-chars}).  Otherwise, @var{allowed-chars} should
 be either a list of allowed chars, or a vector whose Nth element is
-non-nil if character N is allowed.
+non-@code{nil} if character N is allowed.
 @end defun
 
 @defun url-unhex-string string &optional allow-newlines
diff --git a/doc/misc/vhdl-mode.texi b/doc/misc/vhdl-mode.texi
index 7d451c71bd..8abf882a29 100644
--- a/doc/misc/vhdl-mode.texi
+++ b/doc/misc/vhdl-mode.texi
@@ -892,7 +892,7 @@ list.  Send email to the maintainer @email{reto@@gnu.org} 
to join
 either of these lists.
 
 The official Emacs VHDL Mode Home Page can be found at
-@uref{http://www.iis.ee.ethz.ch/~zimmi/emacs/vhdl-mode.html}.
+@uref{https://www.iis.ee.ethz.ch/~zimmi/emacs/vhdl-mode.html}.
 
 @node  Sample Init File
 @chapter  Sample Init File
diff --git a/etc/DEBUG b/etc/DEBUG
index f57e6f197b..ef9160a209 100644
--- a/etc/DEBUG
+++ b/etc/DEBUG
@@ -1004,7 +1004,7 @@ incompatible with the --with-dumping=unexec option of 
'configure'.
 
 ** Running Emacs under Valgrind
 
-Valgrind <http://valgrind.org/> is free software that can be useful
+Valgrind <https://valgrind.org/> is free software that can be useful
 when debugging low-level Emacs problems.  Unlike GCC sanitizers,
 Valgrind does not need you to compile Emacs with special debugging
 flags, so it can be helpful in investigating problems that vanish when
diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS
index 988eb1e09c..5cabb9b015 100644
--- a/etc/ERC-NEWS
+++ b/etc/ERC-NEWS
@@ -98,6 +98,11 @@ Although rare, server passwords containing white space are 
now handled
 correctly.
 
 ** Miscellaneous behavioral changes in the library API.
+A number of core macros and other definitions have been moved to a new
+file called erc-common.el.  This was done to further lessen the
+various complications arising from the mutual dependency between 'erc'
+and 'erc-backend'.
+
 The function 'erc-network' always returns non-nil in server and target
 buffers belonging to a successfully established IRC connection, even
 after that connection has been closed.
@@ -109,11 +114,14 @@ network-context identifiers via a new ':id' keyword.  The 
latter
 carries wider significance beyond autojoin and can be used for
 unequivocally identifying a connection in a human-readable way.
 
-The function 'erc-auto-query', unused internally, and basically
-inscrutable when read, has been deprecated with no public replacement.
-This raises a related issue: if you use ERC as a library and need
-something only offered internally, please lobby to have it exported by
-writing to emacs-erc@gnu.org.
+The function 'erc-auto-query' was deemed too difficult to reason
+through and has thus been deprecated with no public replacement; it
+has also been removed from the client code path.
+
+A few internal variables have been introduced that could just as well
+have been made public, possibly as user options.  Likewise for some
+internal functions.  As always, users needing such functionality
+officially exposed are encouraged to write to emacs-erc@gnu.org.
 
 
 * Changes in ERC 5.4.1
diff --git a/etc/NEWS b/etc/NEWS
index 464cb2719f..7cd192b9d3 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -105,29 +105,21 @@ configuration on X is known to have problems, such as 
undesirable
 frame positioning and various issues with keyboard input of sequences
 such as 'C-;' and 'C-S-u'.
 
+---
+** The implementation of overlays has changed.
+Emacs now uses an implementation of overlays that is much more
+efficient than the original one, and should speed up all the
+operations that involve overlays, especially when there are lots of
+them in a buffer.  However, no changes in behavior of overlays should
+be visible on the Lisp or user level, with the exception of better
+performance and the order of overlays returned by functions that don't
+promise any particular order.
+
 ---
 ** The docstrings of preloaded files are not in "etc/DOC" any more.
 Instead, they're fetched as needed from the corresponding ".elc" file,
 as was already the case for all the non-preloaded files.
 
-** Emacs Sessions (Desktop)
-
-+++
-*** New user option to load a locked desktop if locking Emacs is not running.
-The option 'desktop-load-locked-desktop' can now be set to the value
-'check-pid', which means to allow loading a locked ".emacs.desktop"
-file if the Emacs process which locked it is no longer running on the
-local machine.  This allows avoiding questions about locked desktop
-files when the Emacs session which locked it crashes, or was otherwise
-interrupted, and didn't exit gracefully.  See the "(emacs) Saving
-Emacs Sessions" node in the Emacs manual for more details.
-
-** Miscellaneous
-
-+++
-*** User option 'minibuffer-eldef-shorten-default' is now obsolete.
-Customize the user option 'minibuffer-default-prompt-format' instead.
-
 
 * Startup Changes in Emacs 29.1
 
@@ -173,7 +165,7 @@ time.
 +++
 *** New variable 'inhibit-automatic-native-compilation'.
 If set, Emacs will inhibit native compilation (and won't write
-anything to the eln cache automatically).  The variable is initialised
+anything to the eln cache automatically).  The variable is initialized
 from the 'EMACS_INHIBIT_AUTOMATIC_NATIVE_COMPILATION' environment
 variable on Emacs startup.
 
@@ -356,6 +348,11 @@ previous 'C-x ='.
 
 ** Eshell
 
+*** Eshell's PATH is now derived from 'exec-path'.
+For consistency with remote connections, Eshell now uses 'exec-path'
+to determine the execution path on the local system, instead of using
+the PATH environment variable directly.
+
 ---
 *** 'source' and '.' no longer accept the '--help' option.
 This is for compatibility with the shell versions of these commands,
@@ -374,6 +371,11 @@ node in the Eshell manual for more details.
 *** Eshell pipelines now only pipe stdout by default.
 To pipe both stdout and stderr, use the '|&' operator instead of '|'.
 
+*** New eshell built-in command 'doas'.
+The privilege-escalation program 'doas' has been added to the existing
+'su' and 'sudo' commands from the 'eshell-tramp' module.  The external
+command may still be accessed by using '*doas'.
+
 ---
 ** The 'delete-forward-char' command now deletes by grapheme clusters.
 This command is by default bound to the <Delete> function key
@@ -710,6 +712,15 @@ part of the buffer.
 +++
 ** 'count-words' will now report sentence count when used interactively.
 
+** New user option 'set-message-functions'.
+It allows selecting more functions for 'set-message-function'
+in addition to the default function that handles messages
+in the active minibuffer.  The most useful are 'inhibit-message'
+that allows specifying a list of messages to inhibit via
+'inhibit-message-regexps', and 'set-multi-message' that
+accumulates recent messages and displays them stacked
+in the echo area.
+
 ---
 ** New user option 'find-library-include-other-files'.
 If set to nil, commands like 'find-library' will only include library
@@ -815,22 +826,6 @@ This means Emacs built with GNUstep or built on macOS is 
now able to
 display different faces and images inside tooltips when the
 'use-system-tooltips' user option is nil.
 
-** Connection-local variables
-
-+++
-*** Some connection-local variables are now user options.
-The variables 'connection-local-profile-alist' and
-'connection-local-criteria-alist' are now user options, in order to
-make it more convenient to inspect and modify them.
-
-+++
-*** The default connection-local application can be changed temporarily.
-Running 'with-connection-local-variables' defaults to application
-'tramp'.  This can be changed by let-binding
-'connection-local-default-application' to another symbol.  This is
-useful when running code in a buffer where Tramp has already set some
-connection-local variables.
-
 ---
 ** New minor mode 'pixel-scroll-precision-mode'.
 When enabled, and if your mouse supports it, you can scroll the
@@ -1024,6 +1019,12 @@ The apropos commands will now select the apropos window 
if
 If the symbol at point is a keymap, 'describe-keymap' suggests it as
 the default candidate.
 
+---
+*** New command 'help-quick' displays an overview of common commands.
+The command pops up a buffer at the bottom of the screen with a few
+helpful commands for various tasks.  You can toggle the display using
+'C-h q'.
+
 ** Outline Mode
 
 +++
@@ -1041,15 +1042,11 @@ or is itself too long.
 +++
 *** New user option 'outline-minor-mode-use-buttons'.
 If non-nil, Outline Minor Mode will use buttons to hide/show outlines
-in addition to the ellipsis.  The default is nil in editing modes, but
-non-nil in 'help-mode' and its derivatives.
-
-+++
-*** New user option 'outline-minor-mode-use-margins'.
-If non-nil, Outline Minor Mode will use the window margins to
-hide/show outlines in addition to the ellipsis.  The default is
-non-nil in 'special-mode' and its derivatives, and it can be used in
-editing modes.
+in addition to the ellipsis.  The default is nil, but in 'help-mode'
+it has the value 'insert' that inserts the buttons directly to the
+buffer where you can use 'RET' to cycle outline visibility.  When
+the value is 'in-margins', Outline Minor Mode uses the window margins
+to hide/show outlines.
 
 ** Windows
 
@@ -1094,6 +1091,13 @@ the corresponding deleted frame.
 
 ** Tab Bars and Tab Lines
 
+---
+*** New user option 'tab-bar-auto-width' to automatically determine tab width.
+This option is non-nil by default, which resizes tab-bar tabs so that
+their width is evenly distributed across the tab bar.  A companion
+option 'tab-bar-auto-width-max' controls the maximum width of a tab
+before its name on display is truncated.
+
 ---
 *** 'C-x t RET' creates a new tab when the provided tab name doesn't exist.
 
@@ -1362,6 +1366,16 @@ The default input method for the Tamil language 
environment is now
 change the input method's translation rules, customize the user option
 'tamil-translation-rules'.
 
+---
+*** New tamil99 input method for the Tamil language.
+This supports the keyboard layout specifically designed for the Tamil
+language.
+
+---
+*** New input method 'slovak-qwerty'.
+This is a variant of the 'slovak' input method, which corresponds to
+the QWERTY Slovak keyboards.
+
 
 * Changes in Specialized Modes and Packages in Emacs 29.1
 
@@ -1376,6 +1390,11 @@ the ecomplete database.
 *** New user option 'ecomplete-auto-select'.
 If non-nil and there's only one matching option, auto-select that.
 
+---
+*** New user option 'ecomplete-filter-regexp'.
+If non-nil, this user option describes what entries not to add to the
+database stored on disk.
+
 ** Dired
 
 +++
@@ -1472,6 +1491,10 @@ This controls how statements like the following are 
indented:
 It is enabled by default, but requires that the external "shellcheck"
 command is installed.
 
+** CC Mode
+---
+*** C++ Mode now supports most of the new features in the C++20 standard.
+
 ** Cperl Mode
 
 ---
@@ -1551,6 +1574,48 @@ These commands can be useful if the ".elc" files are out 
of date
 If no packages are marked, 'x' will install the package under point if
 it isn't already, and remove it if it is installed.
 
++++
+*** New command 'package-vc-install'
+Packages can now be installed directly from source by cloning from a
+repository.
+
++++
+*** New command 'package-vc-install-from-checkout'
+An existing checkout can now be loaded via package.el, by creating a
+symbolic link from the usual package directory to the checkout.
+
++++
+*** New command 'package-vc-checkout'
+Used to fetch the source of a package by cloning a repository without
+activating the package.
+
++++
+*** New command 'package-vc-prepare-patch'
+This command allows you to send patches to package maintainers, for
+packages checked out using 'package-vc-install'.
+
++++
+*** New command 'package-report-bug'
+This command helps you compose an email for sending bug reports to
+package maintainers.
+
++++
+*** New user option 'package-vc-selected-packages'
+By customizing this user option you can specify specific packages to
+install.
+
+** Emacs Sessions (Desktop)
+
++++
+*** New user option to load a locked desktop if locking Emacs is not running.
+The option 'desktop-load-locked-desktop' can now be set to the value
+'check-pid', which means to allow loading a locked ".emacs.desktop"
+file if the Emacs process which locked it is no longer running on the
+local machine.  This allows avoiding questions about locked desktop
+files when the Emacs session which locked it crashes, or was otherwise
+interrupted, and didn't exit gracefully.  See the "(emacs) Saving
+Emacs Sessions" node in the Emacs manual for more details.
+
 ** Miscellaneous
 
 +++
@@ -1627,7 +1692,7 @@ This mode adds some highlighting, fixes the 'M-q' 
command, and has
 commands for doing maintenance of the Emacs NEWS files.  In addition,
 this mode turns on 'outline-minor-mode', and thus displays
 customizable icons (see 'icon-preference') in the margins.  To
-disable these icons, customize 'outline-minor-mode-use-margins' to a
+disable these icons, set 'outline-minor-mode-use-buttons' to a
 nil value.
 
 ---
@@ -1689,6 +1754,10 @@ but completes on the history items instead of the 
default completion
 table.  'minibuffer-complete-defaults' ('C-x <down>') completes
 on the list of default items.
 
++++
+*** User option 'minibuffer-eldef-shorten-default' is now obsolete.
+Customize the user option 'minibuffer-default-prompt-format' instead.
+
 +++
 *** New user option 'completions-sort'.
 This option controls the sorting of the completion candidates in
@@ -1845,8 +1914,6 @@ with the changes against the last commit, e.g. with 'C-x 
v D'
 want to commit.  Finally, type 'C-x v v' in that diff buffer to commit
 only part of your changes, those whose hunks were left in the buffer.
 
-Currently this feature works only with the Git as 'vc-backend'.
-
 ---
 *** 'C-x v v' on an unregistered file will now use the most specific backend.
 Previously, if you had an SVN-covered "~/" directory, and a Git-covered
@@ -1911,6 +1978,13 @@ It narrows to the current node.
 
 ** EUDC
 
++++
+*** New user option 'eudc-ignore-options-file' that defaults to 'nil'
+The 'eudc-ignore-options-file' user option can be configured to ignore
+the 'eudc-options-file' (typically "~/.emacs.d/eudc-options").  Most
+users should configure this to 't' and put EUDC configuration in the
+main Emacs initialization file (".emacs" or "~/.emacs.d/init.el").
+
 +++
 *** 'eudc-expansion-overwrites-query' to 'eudc-expansion-save-query-as-kill'.
 'eudc-expansion-overwrites-query' is renamed to
@@ -1957,6 +2031,25 @@ The EUDC back-end for the macOS Contacts app now 
provides a wider set
 of attributes to use for queries, and delivers more attributes in
 query results.
 
++++
+*** New back-end for ecomplete
+A new back-end for ecomplete allows information from that database to
+be queried by EUDC, too.  The attributes present in the EUDC query are
+used to select the entry type in the ecomplete database.
+
++++
+*** New back-end for mailabbrev
+A new back-end for mailabbrev allows information from that database to
+be queried by EUDC, too.  The attributes email, name, and firstname
+are supported only.
+
++++
+*** New default for 'eudc-server-hotlist' includes built-in backends
+The 'eudc-server-hotlist' user option now defaults to including
+entries for the new built-in ecomplete and mailabbrev EUDC backends.
+As a result, 'C-u M-x eudc-expand-try-all' will query both of these
+backends for email address completions, by default.
+
 ** EWW/SHR
 
 +++
@@ -2045,6 +2138,18 @@ Formerly it was a pair of numbers '(A B)' that 
represented 65536*A + B,
 to cater to older Emacs implementations that lacked bignums.
 The older form still works but is undocumented.
 
+** Rmail
+
+---
+*** Rmail partial summaries can now be applied one on top of the other.
+You can now narrow the filtering of messages by the summary's criteria
+(recipients, topic, senders, etc.) by making a summary of the already
+summarized messages.  For example, invoking 'rmail-summary-by-senders',
+followed by 'rmail-summary-by-topic' will produce a summary where both
+the senders and the topic are according to your selection.  The new
+user option 'rmail-summary-apply-filters-consecutively' controls
+whether the stacking of the filters is in effect.
+
 ** EIEIO
 
 +++
@@ -2511,6 +2616,12 @@ the user requesting such a connection, and not of the 
user who is the
 target.  This has always been needed, just the password prompt and the
 related 'auth-sources' entry were wrong.
 
++++
+*** New user option 'tramp-completion-use-cache'.
+During user and host name completion in the minibuffer, results from
+Tramp's connection cache are taken into account.  This can be disabled
+by setting the user option 'tramp-completion-use-cache' to nil.
+
 ** Browse URL
 
 ---
@@ -2590,7 +2701,7 @@ otherwise be returned.
 *** Concatenating Eshell expansions now works more similarly to other shells.
 When concatenating an Eshell expansion that returns a list, "adjacent"
 elements of each operand are now concatenated together,
-e.g. '$list("a" "b")c' returns '("a" "bc")'.  See the "(eshell)
+e.g. '$(list "a" "b")c' returns '("a" "bc")'.  See the "(eshell)
 Expansion" node in the Eshell manual for more details.
 
 +++
@@ -2640,6 +2751,14 @@ commands with a warning face as you type.
 *** New user option 'calc-kill-line-numbering'.
 Set it to nil to exclude line numbering from kills and copies.
 
+** Hierarchy
+
++++
+*** Tree Display can delay computation of children.
+'hierarchy-add-tree' and 'hierarchy-add-trees' have an optional
+argument which allows tree-widget display to be activated and computed
+only when the user expands the node.
+
 ** Miscellaneous
 
 ---
@@ -2749,6 +2868,13 @@ name.
 
 * New Modes and Packages in Emacs 29.1
 
++++
+** Eglot: Emacs Client for the Language Server Protocol.
+Emacs now comes with the Eglot package, which enhances various Emacs
+features, such as completion, documentation, error detection, etc.,
+based on data provided by language servers using the Language Server
+Protocol (LSP).
+
 +++
 ** New commands 'image-crop' and 'image-cut.
 These commands allow interactively cropping/cutting the image at
@@ -2835,6 +2961,11 @@ Previously, ';;;###' specs inside a top-level form 
(i.e., something
 like '(when ... ;;;### ...)' would be ignored.  They are now parsed as
 normal.
 
+---
+** Themes have special autoload cookies.
+All built-in themes are scraped for ';;;###theme-autoload' cookies
+that are loaded along with the regular auto-loaded code.
+
 +++
 ** 'buffer-modified-p' has been extended.
 This function was previously documented to return only nil or t.  This
@@ -3148,7 +3279,16 @@ The following generalized variables have been made 
obsolete:
 * Lisp Changes in Emacs 29.1
 
 +++
-** New accessor function 'file-attribute-file-number'.
+** Interpreted closures are "safe for space".
+As was already the case for byte-compiled closures, instead of capturing
+the whole current lexical environment, interpreted closures now only
+capture the part of the environment that they need.
+The previous behavior could occasionally lead to memory leaks or
+to problems where a printed closure would not be 'read'able because
+of an un'read'able value in an unrelated lexical variable.
+
++++
+** New accessor function 'file-attribute-file-identifier'.
 It returns the list of the inode number and device identifier
 retrieved by 'file-attributes'.  This value can be used to identify a
 file uniquely.  The device identifier can be a single number or (for
@@ -3203,6 +3343,33 @@ TIMEOUT is the idle time after which to deactivate the 
transient map.
 The default timeout value can be defined by the new variable
 'set-transient-map-timeout'.
 
+** Connection-local variables
+
++++
+*** Some connection-local variables are now user options.
+The variables 'connection-local-profile-alist' and
+'connection-local-criteria-alist' are now user options, in order to
+make it more convenient to inspect and modify them.
+
++++
+*** New function 'connection-local-update-profile-variables'.
+This function allows to modify the settings of an existing
+connection-local profile.
+
++++
+*** New macro 'with-connection-local-application-variables'.
+This macro works like 'with-connection-local-variables', but it allows
+to use another application but 'tramp'.  This is useful when running
+code in a buffer where Tramp has already set some connection-local
+variables.
+
++++
+*** New macro 'setq-connection-local'.
+This allows dynamically setting variable values for a particular
+connection within the body of 'with-connection-local-{application-}variables'.
+See the "(elisp) Connection Local Variables" node in the Lisp
+Reference manual for more information.
+
 +++
 ** 'plist-get', 'plist-put' and 'plist-member' are no longer limited to 'eq'.
 These function now take an optional comparison predicate argument.
@@ -4165,5 +4332,5 @@ Local variables:
 coding: utf-8
 mode: outline
 mode: emacs-news
-paragraph-separate: "[         ]*$"
+paragraph-separate: "[         ]"
 end:
diff --git a/etc/NEWS.21 b/etc/NEWS.21
index 6c25a76378..a718283191 100644
--- a/etc/NEWS.21
+++ b/etc/NEWS.21
@@ -217,7 +217,7 @@ Default is 'grow-only'.
 ** LessTif support.
 
 Emacs now runs with the LessTif toolkit (see
-<http://lesstif.sourceforge.net>).  You will need version 0.92.26, or later.
+<https://lesstif.sourceforge.net>).  You will need version 0.92.26, or later.
 
 ** LessTif/Motif file selection dialog.
 
diff --git a/etc/NEWS.22 b/etc/NEWS.22
index d7b26dda51..b4ecbe7039 100644
--- a/etc/NEWS.22
+++ b/etc/NEWS.22
@@ -3540,7 +3540,7 @@ read-only on computers that are administered by someone 
else.
 PBM and XBM images are supported out of the box.  Other image formats
 depend on external libraries.  All of these libraries have been ported
 to Windows, and can be found in both source and binary form at
-http://gnuwin32.sourceforge.net/.  Note that libpng also depends on
+https://gnuwin32.sourceforge.net/.  Note that libpng also depends on
 zlib, and tiff depends on the version of jpeg that it was compiled
 against.  For additional information, see nt/INSTALL.
 
diff --git a/etc/NEWS.25 b/etc/NEWS.25
index d1e43e0538..e716f8194f 100644
--- a/etc/NEWS.25
+++ b/etc/NEWS.25
@@ -72,7 +72,7 @@ using large fonts, at the price of a larger memory footprint.
 ** The version number of CC Mode has been changed from 5.33 to
 5.32.99, although the software itself hasn't changed.  This aims to
 reduce confusion with the standalone CC Mode 5.33 (available from
-http://cc-mode.sourceforge.net), which is a more mature version than
+https://cc-mode.sourceforge.net), which is a more mature version than
 the one included in Emacs 25.2.
 
 
diff --git a/etc/NEWS.26 b/etc/NEWS.26
index 50a711a0d1..9a6a799208 100644
--- a/etc/NEWS.26
+++ b/etc/NEWS.26
@@ -1223,7 +1223,7 @@ specialized for editing freedesktop.org desktop entries.
 editing Less files.
 
 ** New package 'auth-source-pass' integrates 'auth-source' with the
-password manager password-store (http://passwordstore.org).
+password manager password-store (https://passwordstore.org).
 
 
 * Incompatible Lisp Changes in Emacs 26.1
diff --git a/etc/NEWS.27 b/etc/NEWS.27
index f67a8c70d4..f4fb4e3121 100644
--- a/etc/NEWS.27
+++ b/etc/NEWS.27
@@ -2763,7 +2763,7 @@ not waiting for a process to be set up.
 This variable determines how many bytes can be read from a sub-process
 in one read operation.  The default, 4096 bytes, was previously a
 hard-coded constant.  Setting it to a larger value might enhance
-throughput of reading from sub-processes that produces vast
+throughput of reading from sub-processes that produce vast
 (megabytes) amounts of data in one go.
 
 ** The new user option 'quit-window-hook' is now run first when
diff --git a/etc/NEWS.28 b/etc/NEWS.28
index 1edf4e85b0..8eab05a763 100644
--- a/etc/NEWS.28
+++ b/etc/NEWS.28
@@ -199,7 +199,7 @@ lacks the terminfo database, you can instruct Emacs to 
support 24-bit
 true color by setting 'COLORTERM=truecolor' in the environment.  This is
 useful on systems such as FreeBSD which ships only with "etc/termcap".
 
-** File names given on the command line are now be pushed onto history.
+** File names given on the command line are now pushed onto history.
 The file names will be pushed onto 'file-name-history', like the names
 of files visited via 'C-x C-f' and other commands.
 
diff --git a/etc/PROBLEMS b/etc/PROBLEMS
index ed2bc1ae05..aaecc41f6e 100644
--- a/etc/PROBLEMS
+++ b/etc/PROBLEMS
@@ -1229,6 +1229,17 @@ you should use an Emacs input method instead.
 
 ** X keyboard problems
 
+*** `x-focus-frame' fails to activate the frame.
+
+Some window managers prevent `x-focus-frame' from activating the given
+frame when Emacs is in the background.
+
+Emacs tries to work around this problem by default, but the workaround
+does not work on all window managers.  You can try different
+workarounds by changing the value of `x-allow-focus-stealing' (see its
+doc string for more details).  The value `imitate-pager' may be
+required on some versions of KWin.
+
 *** You "lose characters" after typing Compose Character key.
 
 This is because the Compose Character key is defined as the keysym
diff --git a/etc/TODO b/etc/TODO
index 6253a0a93a..bf30436270 100644
--- a/etc/TODO
+++ b/etc/TODO
@@ -509,7 +509,7 @@ Also for listing fonts, displaying a font as a sample, etc.
 
 ** Program Enriched mode to read and save in RTF
 Is there actually a decent single definition of RTF?  Maybe see info at
-http://latex2rtf.sourceforge.net/.
+https://latex2rtf.sourceforge.net/.
 
 This task seems to be addressed by
 https://savannah.nongnu.org/projects/emacs-rtf/, which is still in
@@ -1770,7 +1770,7 @@ The MPX code has not been tested under X toolkit or GTK+ 
2.x builds
 and is not expected to work there.
 
 ** Framework for doing animations
-Emacs does animations all over the place, usually "pluse" animations.
+Emacs does animations all over the place, usually "pulse" animations.
 These currently animate by waiting for a small but fixed amount of
 time between each redisplay, which causes screen tearing by not
 synchronizing with the vertical refresh.  Frame synchronization works
diff --git a/etc/images/gud/README b/etc/images/gud/README
index 5edd99e2bf..c56c3fc0ee 100644
--- a/etc/images/gud/README
+++ b/etc/images/gud/README
@@ -13,10 +13,10 @@ License: GNU General Public License version 3 or later (see 
COPYING)
 
 Some icons are derived from Red Hat's Insight Debugger:
 
-<http://sourceware.org/insight/>
+<https://sourceware.org/insight/>
 "Insight is a graphical user interface to GDB, the GNU Debugger"
 
-<http://sourceware.org/insight/aboutus.php>
+<https://sourceware.org/insight/aboutus.php>
 "Insight is being released under the terms of the GNU General Public
 License (GPL)"
 
diff --git a/etc/publicsuffix.txt b/etc/publicsuffix.txt
index 5676a31c3a..025cf47274 100644
--- a/etc/publicsuffix.txt
+++ b/etc/publicsuffix.txt
@@ -7171,7 +7171,7 @@ org.zw
 
 // newGTLDs
 
-// List of new gTLDs imported from 
https://www.icann.org/resources/registries/gtlds/v2/gtlds.json on 
2022-09-15T15:17:34Z
+// List of new gTLDs imported from 
https://www.icann.org/resources/registries/gtlds/v2/gtlds.json on 
2022-10-29T15:16:24Z
 // This list is auto-generated, don't edit it manually.
 // aaa : 2015-02-26 American Automobile Association, Inc.
 aaa
@@ -7341,7 +7341,7 @@ arab
 // aramco : 2014-11-20 Aramco Services Company
 aramco
 
-// archi : 2014-02-06 Afilias Limited
+// archi : 2014-02-06 Identity Digital Limited
 archi
 
 // army : 2014-03-06 Dog Beach, LLC
@@ -7389,7 +7389,7 @@ auto
 // autos : 2014-01-09 XYZ.COM LLC
 autos
 
-// avianca : 2015-01-08 Avianca Holdings S.A.
+// avianca : 2015-01-08 Avianca Inc.
 avianca
 
 // aws : 2015-06-25 AWS Registry LLC
@@ -7485,7 +7485,7 @@ best
 // bestbuy : 2015-07-31 BBY Solutions, Inc.
 bestbuy
 
-// bet : 2015-05-07 Afilias Limited
+// bet : 2015-05-07 Identity Digital Limited
 bet
 
 // bharti : 2014-01-09 Bharti Enterprises (Holding) Private Limited
@@ -7506,10 +7506,10 @@ bing
 // bingo : 2014-12-04 Binky Moon, LLC
 bingo
 
-// bio : 2014-03-06 Afilias Limited
+// bio : 2014-03-06 Identity Digital Limited
 bio
 
-// black : 2014-01-16 Afilias Limited
+// black : 2014-01-16 Identity Digital Limited
 black
 
 // blackfriday : 2014-01-16 Registry Services, LLC
@@ -7524,7 +7524,7 @@ blog
 // bloomberg : 2014-07-17 Bloomberg IP Holdings LLC
 bloomberg
 
-// blue : 2013-11-07 Afilias Limited
+// blue : 2013-11-07 Identity Digital Limited
 blue
 
 // bms : 2014-10-30 Bristol-Myers Squibb Company
@@ -7596,9 +7596,6 @@ brother
 // brussels : 2014-02-06 DNS.be vzw
 brussels
 
-// bugatti : 2015-07-23 Bugatti International SA
-bugatti
-
 // build : 2013-11-07 Plan Bee LLC
 build
 
@@ -7641,9 +7638,6 @@ camera
 // camp : 2013-11-07 Binky Moon, LLC
 camp
 
-// cancerresearch : 2014-05-15 Australian Cancer Research Foundation
-cancerresearch
-
 // canon : 2014-09-12 Canon Inc.
 canon
 
@@ -7782,7 +7776,7 @@ claims
 // cleaning : 2013-12-05 Binky Moon, LLC
 cleaning
 
-// click : 2014-06-05 UNR Corp.
+// click : 2014-06-05 Internet Naming Company LLC
 click
 
 // clinic : 2014-03-20 Binky Moon, LLC
@@ -8436,7 +8430,7 @@ graphics
 // gratis : 2014-03-20 Binky Moon, LLC
 gratis
 
-// green : 2014-05-08 Afilias Limited
+// green : 2014-05-08 Identity Digital Limited
 green
 
 // gripe : 2014-03-06 Binky Moon, LLC
@@ -8751,7 +8745,7 @@ kia
 // kids : 2021-08-13 DotKids Foundation Limited
 kids
 
-// kim : 2013-09-23 Afilias Limited
+// kim : 2013-09-23 Identity Digital Limited
 kim
 
 // kinder : 2014-11-07 Ferrero Trading Lux S.A.
@@ -8856,7 +8850,7 @@ lego
 // lexus : 2015-04-23 TOYOTA MOTOR CORPORATION
 lexus
 
-// lgbt : 2014-05-08 Afilias Limited
+// lgbt : 2014-05-08 Identity Digital Limited
 lgbt
 
 // lidl : 2014-09-18 Schwarz Domains und Services GmbH & Co. KG
@@ -8904,7 +8898,7 @@ live
 // living : 2015-07-30 Lifestyle Domain Holdings, Inc.
 living
 
-// llc : 2017-12-14 Afilias Limited
+// llc : 2017-12-14 Identity Digital Limited
 llc
 
 // llp : 2019-08-26 Intercap Registry Inc.
@@ -8934,7 +8928,7 @@ london
 // lotte : 2014-11-07 Lotte Holdings Co., Ltd.
 lotte
 
-// lotto : 2014-04-10 Afilias Limited
+// lotto : 2014-04-10 Identity Digital Limited
 lotto
 
 // love : 2014-12-22 Merchant Law Group LLP
@@ -9282,7 +9276,7 @@ oracle
 // orange : 2015-03-12 Orange Brand Services Limited
 orange
 
-// organic : 2014-03-27 Afilias Limited
+// organic : 2014-03-27 Identity Digital Limited
 organic
 
 // origins : 2015-10-01 The Estée Lauder Companies Inc.
@@ -9330,7 +9324,7 @@ pay
 // pccw : 2015-05-14 PCCW Enterprises Limited
 pccw
 
-// pet : 2015-05-07 Afilias Limited
+// pet : 2015-05-07 Identity Digital Limited
 pet
 
 // pfizer : 2015-09-11 Pfizer Inc.
@@ -9378,7 +9372,7 @@ pin
 // ping : 2015-06-11 Ping Registry Provider, Inc.
 ping
 
-// pink : 2013-10-01 Afilias Limited
+// pink : 2013-10-01 Identity Digital Limited
 pink
 
 // pioneer : 2015-07-16 Pioneer Corporation
@@ -9408,7 +9402,7 @@ pnc
 // pohl : 2014-06-23 Deutsche Vermögensberatung Aktiengesellschaft DVAG
 pohl
 
-// poker : 2014-07-03 Afilias Limited
+// poker : 2014-07-03 Identity Digital Limited
 poker
 
 // politie : 2015-08-20 Politie Nederland
@@ -9441,7 +9435,7 @@ prof
 // progressive : 2015-07-23 Progressive Casualty Insurance Company
 progressive
 
-// promo : 2014-12-18 Afilias Limited
+// promo : 2014-12-18 Identity Digital Limited
 promo
 
 // properties : 2013-12-05 Binky Moon, LLC
@@ -9495,7 +9489,7 @@ realty
 // recipes : 2013-10-17 Binky Moon, LLC
 recipes
 
-// red : 2013-11-07 Afilias Limited
+// red : 2013-11-07 Identity Digital Limited
 red
 
 // redstone : 2014-10-31 Redstone Haute Couture Co., Ltd.
@@ -9744,7 +9738,7 @@ shell
 // shia : 2014-09-04 Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti.
 shia
 
-// shiksha : 2013-11-14 Afilias Limited
+// shiksha : 2013-11-14 Identity Digital Limited
 shiksha
 
 // shoes : 2013-10-02 Binky Moon, LLC
@@ -9777,7 +9771,7 @@ singles
 // site : 2015-01-15 Radix FZC
 site
 
-// ski : 2015-04-09 Afilias Limited
+// ski : 2015-04-09 Identity Digital Limited
 ski
 
 // skin : 2015-01-15 XYZ.COM LLC
@@ -10218,7 +10212,7 @@ wanggou
 // watch : 2013-11-14 Binky Moon, LLC
 watch
 
-// watches : 2014-12-22 Afilias Limited
+// watches : 2014-12-22 Identity Digital Limited
 watches
 
 // weather : 2015-01-08 International Business Machines Corporation
@@ -10353,7 +10347,7 @@ xin
 // xn--5tzm5g : 2014-12-22 Global Website TLD Asia Limited
 网站
 
-// xn--6frz82g : 2013-09-23 Afilias Limited
+// xn--6frz82g : 2013-09-23 Identity Digital Limited
 移动
 
 // xn--6qq986b3xl : 2013-09-13 Tycoon Treasure Limited
@@ -10660,6 +10654,10 @@ graphox.us
 // Submitted by accesso Team <accessoecommerce@accesso.com>
 *.devcdnaccesso.com
 
+// Acorn Labs : https://acorn.io
+// Submitted by Craig Jellick <domains@acorn.io>
+*.on-acorn.io
+
 // Adobe : https://www.adobe.com/
 // Submitted by Ian Boston <boston@adobe.com> and Lars Trieloff 
<trieloff@adobe.com>
 adobeaemcloud.com
@@ -10704,51 +10702,49 @@ altervista.org
 // Submitted by Cyril <admin@alwaysdata.com>
 alwaysdata.net
 
-// Amazon CloudFront : https://aws.amazon.com/cloudfront/
+// Amazon : https://www.amazon.com/
+// Submitted by AWS Security <psl-maintainers@amazon.com>
+// Subsections of Amazon/subsidiaries will appear until "concludes" tag
+
+// Amazon CloudFront
 // Submitted by Donavan Miller <donavanm@amazon.com>
+// Reference: 54144616-fd49-4435-8535-19c6a601bdb3
 cloudfront.net
 
-// Amazon Elastic Compute Cloud : https://aws.amazon.com/ec2/
+// Amazon EC2
 // Submitted by Luke Wells <psl-maintainers@amazon.com>
+// Reference: 4c38fa71-58ac-4768-99e5-689c1767e537
 *.compute.amazonaws.com
 *.compute-1.amazonaws.com
 *.compute.amazonaws.com.cn
 us-east-1.amazonaws.com
 
-// Amazon Elastic Beanstalk : https://aws.amazon.com/elasticbeanstalk/
-// Submitted by Luke Wells <psl-maintainers@amazon.com>
-cn-north-1.eb.amazonaws.com.cn
-cn-northwest-1.eb.amazonaws.com.cn
-elasticbeanstalk.com
-ap-northeast-1.elasticbeanstalk.com
-ap-northeast-2.elasticbeanstalk.com
-ap-northeast-3.elasticbeanstalk.com
-ap-south-1.elasticbeanstalk.com
-ap-southeast-1.elasticbeanstalk.com
-ap-southeast-2.elasticbeanstalk.com
-ca-central-1.elasticbeanstalk.com
-eu-central-1.elasticbeanstalk.com
-eu-west-1.elasticbeanstalk.com
-eu-west-2.elasticbeanstalk.com
-eu-west-3.elasticbeanstalk.com
-sa-east-1.elasticbeanstalk.com
-us-east-1.elasticbeanstalk.com
-us-east-2.elasticbeanstalk.com
-us-gov-west-1.elasticbeanstalk.com
-us-west-1.elasticbeanstalk.com
-us-west-2.elasticbeanstalk.com
-
-// Amazon Elastic Load Balancing : https://aws.amazon.com/elasticloadbalancing/
-// Submitted by Luke Wells <psl-maintainers@amazon.com>
-*.elb.amazonaws.com
-*.elb.amazonaws.com.cn
-
-// Amazon Global Accelerator : https://aws.amazon.com/global-accelerator/
-// Submitted by Daniel Massaguer <psl-maintainers@amazon.com>
-awsglobalaccelerator.com
-
-// Amazon S3 : https://aws.amazon.com/s3/
+// Amazon S3
 // Submitted by Luke Wells <psl-maintainers@amazon.com>
+// Reference: d068bd97-f0a9-4838-a6d8-954b622ef4ae
+s3.cn-north-1.amazonaws.com.cn
+s3.dualstack.ap-northeast-1.amazonaws.com
+s3.dualstack.ap-northeast-2.amazonaws.com
+s3.ap-northeast-2.amazonaws.com
+s3-website.ap-northeast-2.amazonaws.com
+s3.dualstack.ap-south-1.amazonaws.com
+s3.ap-south-1.amazonaws.com
+s3-website.ap-south-1.amazonaws.com
+s3.dualstack.ap-southeast-1.amazonaws.com
+s3.dualstack.ap-southeast-2.amazonaws.com
+s3.dualstack.ca-central-1.amazonaws.com
+s3.ca-central-1.amazonaws.com
+s3-website.ca-central-1.amazonaws.com
+s3.dualstack.eu-central-1.amazonaws.com
+s3.eu-central-1.amazonaws.com
+s3-website.eu-central-1.amazonaws.com
+s3.dualstack.eu-west-1.amazonaws.com
+s3.dualstack.eu-west-2.amazonaws.com
+s3.eu-west-2.amazonaws.com
+s3-website.eu-west-2.amazonaws.com
+s3.dualstack.eu-west-3.amazonaws.com
+s3.eu-west-3.amazonaws.com
+s3-website.eu-west-3.amazonaws.com
 s3.amazonaws.com
 s3-ap-northeast-1.amazonaws.com
 s3-ap-northeast-2.amazonaws.com
@@ -10763,48 +10759,25 @@ s3-eu-west-3.amazonaws.com
 s3-external-1.amazonaws.com
 s3-fips-us-gov-west-1.amazonaws.com
 s3-sa-east-1.amazonaws.com
-s3-us-gov-west-1.amazonaws.com
 s3-us-east-2.amazonaws.com
+s3-us-gov-west-1.amazonaws.com
 s3-us-west-1.amazonaws.com
 s3-us-west-2.amazonaws.com
-s3.ap-northeast-2.amazonaws.com
-s3.ap-south-1.amazonaws.com
-s3.cn-north-1.amazonaws.com.cn
-s3.ca-central-1.amazonaws.com
-s3.eu-central-1.amazonaws.com
-s3.eu-west-2.amazonaws.com
-s3.eu-west-3.amazonaws.com
-s3.us-east-2.amazonaws.com
-s3.dualstack.ap-northeast-1.amazonaws.com
-s3.dualstack.ap-northeast-2.amazonaws.com
-s3.dualstack.ap-south-1.amazonaws.com
-s3.dualstack.ap-southeast-1.amazonaws.com
-s3.dualstack.ap-southeast-2.amazonaws.com
-s3.dualstack.ca-central-1.amazonaws.com
-s3.dualstack.eu-central-1.amazonaws.com
-s3.dualstack.eu-west-1.amazonaws.com
-s3.dualstack.eu-west-2.amazonaws.com
-s3.dualstack.eu-west-3.amazonaws.com
-s3.dualstack.sa-east-1.amazonaws.com
-s3.dualstack.us-east-1.amazonaws.com
-s3.dualstack.us-east-2.amazonaws.com
-s3-website-us-east-1.amazonaws.com
-s3-website-us-west-1.amazonaws.com
-s3-website-us-west-2.amazonaws.com
 s3-website-ap-northeast-1.amazonaws.com
 s3-website-ap-southeast-1.amazonaws.com
 s3-website-ap-southeast-2.amazonaws.com
 s3-website-eu-west-1.amazonaws.com
 s3-website-sa-east-1.amazonaws.com
-s3-website.ap-northeast-2.amazonaws.com
-s3-website.ap-south-1.amazonaws.com
-s3-website.ca-central-1.amazonaws.com
-s3-website.eu-central-1.amazonaws.com
-s3-website.eu-west-2.amazonaws.com
-s3-website.eu-west-3.amazonaws.com
+s3-website-us-east-1.amazonaws.com
+s3-website-us-west-1.amazonaws.com
+s3-website-us-west-2.amazonaws.com
+s3.dualstack.sa-east-1.amazonaws.com
+s3.dualstack.us-east-1.amazonaws.com
+s3.dualstack.us-east-2.amazonaws.com
+s3.us-east-2.amazonaws.com
 s3-website.us-east-2.amazonaws.com
 
-// AWS Cloud9 : https://aws.amazon.com/cloud9/
+// AWS Cloud9
 // Submitted by: AWS Security <psl-maintainers@amazon.com>
 // Reference: 2b6dfa9a-3a7f-4367-b2e7-0321e77c0d59
 vfs.cloud9.af-south-1.amazonaws.com
@@ -10850,6 +10823,49 @@ webview-assets.cloud9.us-west-1.amazonaws.com
 vfs.cloud9.us-west-2.amazonaws.com
 webview-assets.cloud9.us-west-2.amazonaws.com
 
+// AWS Elastic Beanstalk
+// Submitted by Luke Wells <psl-maintainers@amazon.com>
+// Reference: aa202394-43a0-4857-b245-8db04549137e
+cn-north-1.eb.amazonaws.com.cn
+cn-northwest-1.eb.amazonaws.com.cn
+elasticbeanstalk.com
+ap-northeast-1.elasticbeanstalk.com
+ap-northeast-2.elasticbeanstalk.com
+ap-northeast-3.elasticbeanstalk.com
+ap-south-1.elasticbeanstalk.com
+ap-southeast-1.elasticbeanstalk.com
+ap-southeast-2.elasticbeanstalk.com
+ca-central-1.elasticbeanstalk.com
+eu-central-1.elasticbeanstalk.com
+eu-west-1.elasticbeanstalk.com
+eu-west-2.elasticbeanstalk.com
+eu-west-3.elasticbeanstalk.com
+sa-east-1.elasticbeanstalk.com
+us-east-1.elasticbeanstalk.com
+us-east-2.elasticbeanstalk.com
+us-gov-west-1.elasticbeanstalk.com
+us-west-1.elasticbeanstalk.com
+us-west-2.elasticbeanstalk.com
+
+// (AWS) Elastic Load Balancing
+// Submitted by Luke Wells <psl-maintainers@amazon.com>
+// Reference: 12a3d528-1bac-4433-a359-a395867ffed2
+*.elb.amazonaws.com.cn
+*.elb.amazonaws.com
+
+// AWS Global Accelerator
+// Submitted by Daniel Massaguer <psl-maintainers@amazon.com>
+// Reference: d916759d-a08b-4241-b536-4db887383a6a
+awsglobalaccelerator.com
+
+// eero
+// Submitted by Yue Kang <eero-dynamic-dns@amazon.com>
+// Reference: 264afe70-f62c-4c02-8ab9-b5281ed24461
+eero.online
+eero-stage.online
+
+// concludes Amazon
+
 // Amune : https://amune.org/
 // Submitted by Team Amune <cert@amune.org>
 t3l3p0rt.net
@@ -11745,11 +11761,6 @@ e4.cz
 easypanel.app
 easypanel.host
 
-// eero : https://eero.com/
-// Submitted by Yue Kang <eero-dynamic-dns@amazon.com>
-eero.online
-eero-stage.online
-
 // Elementor : Elementor Ltd.
 // Submitted by Anton Barkan <antonb@elementor.com>
 elementor.cloud
@@ -11963,6 +11974,10 @@ a.ssl.fastly.net
 b.ssl.fastly.net
 global.ssl.fastly.net
 
+// Fastmail : https://www.fastmail.com/
+// Submitted by Marc Bradshaw <marc@fastmailteam.com>
+*.user.fm
+
 // FASTVPS EESTI OU : https://fastvps.ru/
 // Submitted by Likhachev Vasiliy <lihachev@fastvps.ru>
 fastvps-server.com
@@ -12973,25 +12988,6 @@ cust.retrosnub.co.uk
 // Submitted by Paulus Schoutsen <infra@nabucasa.com>
 ui.nabu.casa
 
-// Names.of.London : https://names.of.london/
-// Submitted by James Stevens <registry[at]names.of.london> or 
<publiclist[at]jrcs.net>
-pony.club
-of.fashion
-in.london
-of.london
-from.marketing
-with.marketing
-for.men
-repair.men
-and.mom
-for.mom
-for.one
-under.one
-for.sale
-that.win
-from.work
-to.work
-
 // Net at Work Gmbh : https://www.netatwork.de
 // Submitted by Jan Jaeschke <jan.jaeschke@netatwork.de>
 cloud.nospamproxy.com
@@ -13188,7 +13184,26 @@ omniwe.site
 
 // One.com: https://www.one.com/
 // Submitted by Jacob Bunk Nielsen <jbn@one.com>
+123hjemmeside.dk
+123hjemmeside.no
+123homepage.it
+123kotisivu.fi
+123minsida.se
+123miweb.es
+123paginaweb.pt
+123sait.ru
+123siteweb.fr
+123webseite.at
+123webseite.de
+123website.be
+123website.ch
+123website.lu
+123website.nl
 service.one
+simplesite.com
+simplesite.com.br
+simplesite.gr
+simplesite.pl
 
 // One Fold Media : http://www.onefoldmedia.com/
 // Submitted by Eddie Jones <eddie@onefoldmedia.com>
@@ -13457,7 +13472,9 @@ app.render.com
 onrender.com
 
 // Repl.it : https://repl.it
-// Submitted by Mason Clayton <mason@repl.it>
+// Submitted by Lincoln Bergeson <lincoln@replit.com>
+firewalledreplit.co
+id.firewalledreplit.co
 repl.co
 id.repl.co
 repl.run
diff --git a/etc/srecode/ede-autoconf.srt b/etc/srecode/ede-autoconf.srt
index ecca7afd00..51656eb73c 100644
--- a/etc/srecode/ede-autoconf.srt
+++ b/etc/srecode/ede-autoconf.srt
@@ -38,7 +38,7 @@ template ede-empty :project
 {{comment_prefix}} by EDE when this file is updated.
 {{comment_prefix}}
 {{comment_prefix}} EDE is the Emacs Development Environment.
-{{comment_prefix}} http://cedet.sourceforge.net/ede.shtml
+{{comment_prefix}} https://cedet.sourceforge.net/ede.shtml
 {{comment_prefix}}
 {{comment_prefix}} Process this file with autoconf to produce a configure 
script
 
diff --git a/etc/srecode/ede-make.srt b/etc/srecode/ede-make.srt
index cde1690f54..c01054e042 100644
--- a/etc/srecode/ede-make.srt
+++ b/etc/srecode/ede-make.srt
@@ -34,7 +34,7 @@ template ede-empty :file :project
 #
 # DO NOT MODIFY THIS FILE OR YOUR CHANGES MAY BE LOST.
 # EDE is the Emacs Development Environment.
-# http://cedet.sourceforge.net/ede.shtml
+# https://cedet.sourceforge.net/ede.shtml
 #
 
 ----
@@ -58,7 +58,7 @@ template ede-empty :file
 #
 # DO NOT MODIFY THIS FILE OR YOUR CHANGES MAY BE LOST.
 # EDE is the Emacs Development Environment.
-# http://cedet.sourceforge.net/ede.shtml
+# https://cedet.sourceforge.net/ede.shtml
 
 ARDUINO_DIR  = {{ARDUINO_HOME}}
 
diff --git a/etc/themes/adwaita-theme.el b/etc/themes/adwaita-theme.el
index ba83a0578c..6ad8405559 100644
--- a/etc/themes/adwaita-theme.el
+++ b/etc/themes/adwaita-theme.el
@@ -21,10 +21,13 @@
 
 ;;; Code:
 
+;;;###theme-autoload
 (deftheme adwaita
   "Face colors similar to the default theme of Gnome 3 (Adwaita).
 The colors are chosen to match Adwaita window decorations and the
-default look of the Gnome 3 desktop.")
+default look of the Gnome 3 desktop."
+  :background-mode 'light
+  :kind 'color-scheme)
 
 (let ((class '((class color) (min-colors 89))))
   (custom-theme-set-faces
diff --git a/etc/themes/deeper-blue-theme.el b/etc/themes/deeper-blue-theme.el
index 8f19147f91..48ed9ba061 100644
--- a/etc/themes/deeper-blue-theme.el
+++ b/etc/themes/deeper-blue-theme.el
@@ -21,8 +21,11 @@
 
 ;;; Code:
 
+;;;###theme-autoload
 (deftheme deeper-blue
-  "Face colors using a deep blue background.")
+  "Face colors using a deep blue background."
+  :background-mode 'dark
+  :kind 'color-scheme)
 
 (let ((class '((class color) (min-colors 89))))
   (custom-theme-set-faces
diff --git a/etc/themes/dichromacy-theme.el b/etc/themes/dichromacy-theme.el
index d53c075d92..fe44d520cc 100644
--- a/etc/themes/dichromacy-theme.el
+++ b/etc/themes/dichromacy-theme.el
@@ -21,6 +21,7 @@
 
 ;;; Code:
 
+;;;###theme-autoload
 (deftheme dichromacy
   "Face colors suitable for red/green color-blind users.
 The color palette is from B. Wong, Nature Methods 8, 441 (2011).
@@ -28,7 +29,9 @@ It is intended to provide good variability while being easily
 differentiated by individuals with protanopia or deuteranopia.
 
 Basic, Font Lock, Isearch, Gnus, Message, Flyspell, and
-Ansi-Color faces are included.")
+Ansi-Color faces are included."
+  :background-mode 'light
+  :kind 'color-scheme)
 
 (let ((class '((class color) (min-colors 89)))
       (orange "#e69f00")
diff --git a/etc/themes/leuven-dark-theme.el b/etc/themes/leuven-dark-theme.el
index 0e162c8bab..08978a2668 100644
--- a/etc/themes/leuven-dark-theme.el
+++ b/etc/themes/leuven-dark-theme.el
@@ -5,7 +5,7 @@
 ;; Author: Fabrice Niessen <(concat "fniessen" at-sign "pirilampo.org")>
 ;; Contributor: Thibault Polge <(concat "thibault" at-sign "thb.lt")>
 ;; URL: https://github.com/fniessen/emacs-leuven-dark-theme
-;; Version: 20220202.1126
+;; Version: 20221010.1208
 ;; Keywords: color theme
 
 ;; This file is part of GNU Emacs.
@@ -93,11 +93,15 @@ CONTROL can be a number, nil, or t.  When t, use 
DEFAULT-HEIGHT."
 
 ;;; Theme Faces.
 
+;;;###theme-autoload
 (deftheme leuven-dark
   "Face colors with a light background.
 Basic, Font Lock, Isearch, Gnus, Message, Org mode, Diff, Ediff,
 Flyspell, Semantic, and Ansi-Color faces are included -- and much
-more...")
+more..."
+  :background-mode 'dark
+  :family 'leuven
+  :kind 'color-scheme)
 
 (let ((class '((class color) (min-colors 89)))
 
diff --git a/etc/themes/leuven-theme.el b/etc/themes/leuven-theme.el
index d9a8d5391a..e712a79adf 100644
--- a/etc/themes/leuven-theme.el
+++ b/etc/themes/leuven-theme.el
@@ -4,7 +4,7 @@
 
 ;; Author: Fabrice Niessen <(concat "fniessen" at-sign "pirilampo.org")>
 ;; URL: https://github.com/fniessen/emacs-leuven-theme
-;; Version: 20200513.1928
+;; Version: 20221010.1209
 ;; Keywords: color theme
 
 ;; This file is part of GNU Emacs.
@@ -74,11 +74,15 @@ CONTROL can be a number, nil, or t.  When t, use 
DEFAULT-HEIGHT."
 
 ;;; Theme Faces.
 
+;;;###theme-autoload
 (deftheme leuven
   "Face colors with a light background.
 Basic, Font Lock, Isearch, Gnus, Message, Org mode, Diff, Ediff,
 Flyspell, Semantic, and Ansi-Color faces are included -- and much
-more...")
+more..."
+  :background-mode 'light
+  :kind 'color-scheme
+  :family 'leuven)
 
 (let ((class '((class color) (min-colors 89)))
 
diff --git a/etc/themes/light-blue-theme.el b/etc/themes/light-blue-theme.el
index eeca46210c..808fcbfeb2 100644
--- a/etc/themes/light-blue-theme.el
+++ b/etc/themes/light-blue-theme.el
@@ -26,8 +26,11 @@
 
 ;;; Code:
 
+;;;###theme-autoload
 (deftheme light-blue
-  "Face colors utilizing a light blue background.")
+  "Face colors utilizing a light blue background."
+  :background-mode 'light
+  :kind 'color-scheme)
 
 (make-obsolete 'light-blue nil "29.1")
 
diff --git a/etc/themes/manoj-dark-theme.el b/etc/themes/manoj-dark-theme.el
index af5576386c..f9aaa97c25 100644
--- a/etc/themes/manoj-dark-theme.el
+++ b/etc/themes/manoj-dark-theme.el
@@ -64,10 +64,13 @@
 
 ;;; Code:
 
+;;;###theme-autoload
 (deftheme manoj-dark
   "Very high contrast faces with a black background.
 This theme avoids subtle color variations, while avoiding the
-jarring angry fruit salad look to reduce eye fatigue.")
+jarring angry fruit salad look to reduce eye fatigue."
+  :background-mode 'dark
+  :kind 'color-scheme)
 
 (custom-theme-set-faces
  'manoj-dark
diff --git a/etc/themes/misterioso-theme.el b/etc/themes/misterioso-theme.el
index 55186384ad..3fd6cdb5af 100644
--- a/etc/themes/misterioso-theme.el
+++ b/etc/themes/misterioso-theme.el
@@ -21,8 +21,11 @@
 
 ;;; Code:
 
+;;;###theme-autoload
 (deftheme misterioso
-  "Predominantly blue/cyan faces on a dark cyan background.")
+  "Predominantly blue/cyan faces on a dark cyan background."
+  :background-mode 'dark
+  :kind 'color-scheme)
 
 (let ((class '((class color) (min-colors 89))))
 
diff --git a/etc/themes/modus-operandi-theme.el 
b/etc/themes/modus-operandi-theme.el
index 6e609c0803..6ea92f8559 100644
--- a/etc/themes/modus-operandi-theme.el
+++ b/etc/themes/modus-operandi-theme.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: 2.7.1
+;; Version: 3.0.0
 ;; Package-Requires: ((emacs "27.1"))
 ;; Keywords: faces, theme, accessibility
 
@@ -71,4 +71,7 @@ which corresponds to a minimum contrast in relative luminance 
of
 
   (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-themes.el b/etc/themes/modus-themes.el
index d5e1b0a120..a9d0d53cba 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: 2.7.1
+;; Version: 3.0.0
 ;; Package-Requires: ((emacs "27.1"))
 ;; Keywords: faces, theme, accessibility
 
@@ -88,7 +88,7 @@
   (require 'subr-x))
 
 (defgroup modus-themes ()
-  "Options for `modus-operandi', `modus-vivendi'.
+  "Options for `modus-operandi', `modus-vivendi' themes.
 The Modus themes conform with the WCAG AAA standard for color
 contrast between background and foreground combinations (a
 minimum contrast of 7:1---the highest standard of its kind).  The
@@ -103,13 +103,13 @@ cover the blue-cyan-magenta side of the spectrum."
   :tag "Modus Themes")
 
 (defgroup modus-themes-faces ()
-  "Faces defined by `modus-operandi' and `modus-vivendi'."
+  "Faces defined by `modus-operandi' and `modus-vivendi' themes."
   :group 'modus-themes
   :link '(info-link "(modus-themes) Top")
   :prefix "modus-themes-"
   :tag "Modus Themes Faces")
 
-(defvar modus-themes--version "2.7.0"
+(defvar modus-themes--version "3.0.0"
   "Current version of the Modus themes.
 
 The version either is the last tagged release, such as '1.0.0',
@@ -123,10 +123,7 @@ those would count as part of '1.1.0-dev'.")
 If optional INSERT argument is provided from Lisp or as a prefix
 argument, insert the `modus-themes--version' at point."
   (interactive "P")
-  (if-let ((version modus-themes--version)
-           ((or insert current-prefix-arg)))
-      (insert version)
-    (message version)))
+  (funcall (if insert 'insert 'message) modus-themes--version))
 
 ;;;###autoload
 (defun modus-themes-report-bug ()
@@ -1364,26 +1361,6 @@ The actual styling of the face is done by 
`modus-themes-faces'."
 The actual styling of the face is done by `modus-themes-faces'."
   :group 'modus-themes-faces)
 
-(define-obsolete-face-alias
- 'modus-themes-completion-standard-first-match
- 'modus-themes-completion-selected
- "2.2.0")
-
-(define-obsolete-face-alias
- 'modus-themes-completion-standard-selected
- 'modus-themes-completion-selected
- "2.2.0")
-
-(define-obsolete-face-alias
- 'modus-themes-completion-extra-selected
- 'modus-themes-completion-selected
- "2.2.0")
-
-(define-obsolete-face-alias
- 'modus-themes-completion-key-binding
- 'modus-themes-key-binding
- "2.2.0")
-
 (defface modus-themes-completion-selected nil
   "Face for current selection in completion UIs.
 The actual styling of the face is done by `modus-themes-faces'."
@@ -1934,20 +1911,22 @@ For example:
   :initialize #'custom-initialize-default
   :link '(info-link "(modus-themes) Org agenda"))
 
-(defcustom modus-themes-fringes nil
-  "Define the visibility of fringes.
+(defcustom modus-themes-fringes 'subtle
+  "Control the visibility of fringes.
+
+When the value is nil, do not apply a distinct background color.
 
-Nil means the fringes have no background color.  Option `subtle'
-will apply a grayscale value that is visible yet close to the
-main buffer background color.  Option `intense' will use a more
-pronounced grayscale value."
+With a value of `subtle' use a gray background color that is
+visible yet close to the main background color.
+
+With `intense' use a more pronounced gray background color."
   :group 'modus-themes
-  :package-version '(modus-themes . "1.0.0")
-  :version "28.1"
+  :package-version '(modus-themes . "3.0.0")
+  :version "29.1"
   :type '(choice
-          (const :format "[%v] %t\n" :tag "No visible fringes (default)" nil)
-          (const :format "[%v] %t\n" :tag "Subtle grayscale background" subtle)
-          (const :format "[%v] %t\n" :tag "Intense grayscale background" 
intense))
+          (const :format "[%v] %t\n" :tag "No visible fringes" nil)
+          (const :format "[%v] %t\n" :tag "Subtle gray background" subtle)
+          (const :format "[%v] %t\n" :tag "Intense gray background" intense))
   :set #'modus-themes--set-option
   :initialize #'custom-initialize-default
   :link '(info-link "(modus-themes) Fringes"))
@@ -2212,13 +2191,16 @@ interest of optimizing for such a use-case."
   :initialize #'custom-initialize-default
   :link '(info-link "(modus-themes) Diffs"))
 
-(defcustom modus-themes-completions nil
+(defcustom modus-themes-completions
+  '((selection . (intense))
+    (popup . (intense)))
   "Control the style of completion user interfaces.
 
 This affects Company, Corfu, Flx, Helm, Icomplete/Fido, Ido, Ivy,
-Mct, Orderless, Selectrum, Vertico.  The value is an alist that
-takes the form of a (key . properties) combination.  Here is a
-sample, followed by a description of the particularities:
+Orderless, Selectrum, Vertico.  The value is an alist that takes
+the form of a (KEY . PROPERTIES) combination.  KEY is a symbol,
+while PROPERTIES is a list.  Here is a sample, followed by a
+description of the particularities:
 
     (setq modus-themes-completions
           (quote ((matches . (extrabold background intense))
@@ -2226,10 +2208,11 @@ sample, followed by a description of the 
particularities:
                   (popup . (accented)))))
 
 The `matches' key refers to the highlighted characters that
-correspond to the user's input.  By default (nil or an empty
-list), they 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:
+correspond 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:
 
 - `background' to add a background color;
 
@@ -2246,10 +2229,10 @@ regardless of the order they may appear in:
   that bold will be used.
 
 The `selection' key applies to the current line or currently
-matched candidate, depending on the specifics of the User
-Interface.  By default (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
+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, a bold weight, and the base foreground
+value for the text.  The list of properties it accepts is as
 follows (order is not significant):
 
 - `accented' to make the background colorful instead of gray;
@@ -2268,7 +2251,11 @@ follows (order is not significant):
   variable `modus-themes-weights'.  The absence of a weight means
   that bold will be used.
 
-The `popup' key takes the same values as `selection'.
+The `popup' key takes the same values as `selection'.  The only
+difference is that it applies specifically to user interfaces
+that display an inline popup and thus have slightly different
+styling requirements than the minibuffer.  The two prominent
+packages are `company' and `corfu'.
 
 Apart from specifying each key separately, a fallback list is
 accepted.  This is only useful when the desired aesthetic is the
@@ -2290,22 +2277,14 @@ the corresponding key is simply ignored (`matches' does 
not have
 `accented' and `text-also', while `selection' and `popup' do not
 have `background').
 
-A concise expression of those associations can be written as
-follows, where the `car' is always the key and the `cdr' is the
-list of properties (whatever order they may appear in):
-
-    (setq modus-themes-completions
-          (quote ((matches extrabold background intense)
-                  (selection semibold accented intense)
-                  (popup accented))))
-
 Check the manual for tweaking `bold' and `italic' faces: Info
 node `(modus-themes) Configure bold and italic faces'.
 
-Also refer to the Orderless documentation for its intersection
-with Company (if you choose to use those in tandem)."
+Also refer to the documentation of the `orderless' package for
+its intersection with `company' (if you choose to use those in
+tandem)."
   :group 'modus-themes
-  :package-version '(modus-themes . "2.3.0")
+  :package-version '(modus-themes . "3.0.0")
   :version "29.1"
   :type `(set
           (cons :tag "Matches"
@@ -2420,11 +2399,11 @@ In user configuration files the form may look like this:
   :initialize #'custom-initialize-default
   :link '(info-link "(modus-themes) Command prompts"))
 
-(defcustom modus-themes-hl-line nil
-  "Control the current line highlight of HL-line mode.
+(defcustom modus-themes-hl-line '(intense)
+  "Control the current line highlight of `hl-line-mode'.
 
 The value is a list of properties, each designated by a symbol.
-The default (a nil value or an empty list) is a subtle gray
+With a nil value, or an empty list, the style is a subtle gray
 background color.
 
 The property `accented' changes the background to a colored
@@ -2450,11 +2429,12 @@ In user configuration files the form may look like this:
 
     (setq modus-themes-hl-line (quote (underline accented)))
 
-Set `x-underline-at-descent-line' to a non-nil value for better
-results with underlines."
+Set `x-underline-at-descent-line' to a non-nil value so that the
+placement of the underline coincides with the lower boundary of
+the colored background."
   :group 'modus-themes
-  :package-version '(modus-themes . "1.5.0")
-  :version "28.1"
+  :package-version '(modus-themes . "3.0.0")
+  :version "29.1"
   :type '(set :tag "Properties" :greedy t
               (const :tag "Colored background" accented)
               (const :tag "Underline" underline)
@@ -2522,8 +2502,6 @@ Also check the variables `org-hide-emphasis-markers',
   :initialize #'custom-initialize-default
   :link '(info-link "(modus-themes) Markup"))
 
-(make-obsolete 'modus-themes-intense-markup 'modus-themes-markup "2.1.0")
-
 (defcustom modus-themes-paren-match nil
   "Control the style of matching parentheses or delimiters.
 
@@ -3225,11 +3203,6 @@ an alternative to the default value."
   "Get cdr of KEY in ALIST."
   (cdr (assoc key alist)))
 
-(define-obsolete-variable-alias
-  'modus-themes--heading-weights
-  'modus-themes-weights
-  "2.1.0")
-
 (defconst modus-themes-weights
   '( thin ultralight extralight light semilight regular medium
      semibold bold heavy extrabold ultrabold)
@@ -3582,9 +3555,6 @@ foreground unspecified."
       (list deuteran)
     (list main)))
 
-(make-obsolete 'modus-themes--completion 'modus-themes--completion-line 
"2.3.0")
-(make-obsolete 'modus-themes--completion 'modus-themes--completion-match 
"2.3.0")
-
 (defun modus-themes--completion-line (key bg fg bgintense fgintense &optional 
bgaccent bgaccentintense)
   "Styles for `modus-themes-completions'.
 KEY is the key of a cons cell.  BG and FG are the main colors.
@@ -4309,8 +4279,8 @@ by virtue of calling either of 
`modus-themes-load-operandi' and
                   magenta-subtle-bg magenta-intense))))
     `(modus-themes-completion-match-1
       ((,class ,@(modus-themes--completion-match
-                  'matches bg-special-faint-cold cyan
-                  cyan-subtle-bg cyan-intense))))
+                  'matches bg-special-faint-cold blue
+                  blue-subtle-bg blue-intense))))
     `(modus-themes-completion-match-2
       ((,class ,@(modus-themes--completion-match
                   'matches bg-special-faint-mild green
@@ -4798,9 +4768,7 @@ by virtue of calling either of 
`modus-themes-load-operandi' and
     `(consult-line-number-prefix ((,class :foreground ,fg-unfocused)))
     `(consult-narrow-indicator ((,class :foreground ,magenta-alt)))
     `(consult-preview-cursor ((,class :inherit modus-themes-intense-blue)))
-    `(consult-preview-error ((,class :inherit modus-themes-intense-red)))
     `(consult-preview-insertion ((,class :inherit modus-themes-special-warm)))
-    `(consult-preview-line ((,class :background ,bg-hl-alt-intense)))
 ;;;;; corfu
     `(corfu-current ((,class :inherit modus-themes-completion-selected-popup)))
     `(corfu-bar ((,class :background ,fg-alt)))
@@ -4928,16 +4896,12 @@ by virtue of calling either of 
`modus-themes-load-operandi' and
 ;;;;; diff-hl
     `(diff-hl-change ((,class :inherit modus-themes-fringe-yellow)))
     `(diff-hl-delete ((,class :inherit modus-themes-fringe-red)))
-    `(diff-hl-dired-change ((,class :inherit diff-hl-change)))
-    `(diff-hl-dired-delete ((,class :inherit diff-hl-delete)))
-    `(diff-hl-dired-ignored ((,class :inherit dired-ignored)))
-    `(diff-hl-dired-insert ((,class :inherit diff-hl-insert)))
-    `(diff-hl-dired-unknown ((,class :inherit dired-ignored)))
     `(diff-hl-insert ((,class :inherit modus-themes-grue-background-active)))
     `(diff-hl-reverted-hunk-highlight ((,class :background ,fg-main 
:foreground ,bg-main)))
 ;;;;; diff-mode
     `(diff-added ((,class :inherit modus-themes-diff-added)))
     `(diff-changed ((,class :inherit modus-themes-diff-changed :extend t)))
+    `(diff-changed-unspecified ((,class :inherit diff-changed)))
     `(diff-context ((,class ,@(unless (eq modus-themes-diffs 'bg-only) (list 
:foreground fg-unfocused)))))
     `(diff-error ((,class :inherit modus-themes-intense-red)))
     `(diff-file-header ((,class :inherit (bold diff-header))))
@@ -5741,17 +5705,43 @@ by virtue of calling either of 
`modus-themes-load-operandi' and
     ;; HACK 2022-06-23: The :inverse-video prevents hl-line-mode from
     ;; overriding the background.  Such an override really defeats the
     ;; purpose of setting those highlights.
-    `(hi-aquamarine ((,class :background ,bg-main :foreground ,cyan 
:inverse-video t)))
+    ;;
+    ;; NOTE 2022-10-04: We do not use the ,class here but instead
+    ;; hardcode color values.  We have to do this as the themes lack
+    ;; entries in their palette for such an edge case.  Defining those
+    ;; entries is not appropriate.
+    `(hi-aquamarine ((((class color) (min-colors 88) (background light))
+                      :background "white" :foreground "#227f9f" :inverse-video 
t)
+                     (((class color) (min-colors 88) (background dark))
+                      :background "black" :foreground "#66cbdc" :inverse-video 
t)))
     `(hi-black-b ((,class :inverse-video t)))
     `(hi-black-hb ((,class :background ,bg-main :foreground ,fg-alt 
:inverse-video t)))
-    `(hi-blue ((,class :background ,bg-main :foreground ,blue-alt 
:inverse-video t)))
+    `(hi-blue ((((class color) (min-colors 88) (background light))
+                :background "white" :foreground "#3366dd" :inverse-video t)
+               (((class color) (min-colors 88) (background dark))
+                :background "black" :foreground "#aaccff" :inverse-video t)))
     `(hi-blue-b ((,class :inherit (bold hi-blue))))
-    `(hi-green ((,class :background ,bg-main :foreground ,green :inverse-video 
t)))
+    `(hi-green ((((class color) (min-colors 88) (background light))
+                  :background "white" :foreground "#008a00" :inverse-video t)
+                 (((class color) (min-colors 88) (background dark))
+                  :background "black" :foreground "#66dd66" :inverse-video t)))
     `(hi-green-b ((,class :inherit (bold hi-green))))
-    `(hi-pink ((,class :background ,bg-main :foreground ,magenta 
:inverse-video t)))
-    `(hi-red-b ((,class :inherit bold :background ,bg-main :foreground ,red 
:inverse-video t)))
-    `(hi-salmon ((,class :background ,bg-main :foreground ,red-alt-faint 
:inverse-video t)))
-    `(hi-yellow ((,class :background ,bg-main :foreground ,yellow-alt 
:inverse-video t)))
+    `(hi-pink ((((class color) (min-colors 88) (background light))
+                  :background "white" :foreground "#bd30aa" :inverse-video t)
+                 (((class color) (min-colors 88) (background dark))
+                  :background "black" :foreground "#ff88ee" :inverse-video t)))
+    `(hi-red-b ((((class color) (min-colors 88) (background light))
+                  :background "white" :foreground "#dd0000" :inverse-video t)
+                 (((class color) (min-colors 88) (background dark))
+                  :background "black" :foreground "#f06666" :inverse-video t)))
+    `(hi-salmon ((((class color) (min-colors 88) (background light))
+                  :background "white" :foreground "#bf555a" :inverse-video t)
+                 (((class color) (min-colors 88) (background dark))
+                  :background "black" :foreground "#e08a50" :inverse-video t)))
+    `(hi-yellow ((((class color) (min-colors 88) (background light))
+                  :background "white" :foreground "#af6400" :inverse-video t)
+                 (((class color) (min-colors 88) (background dark))
+                  :background "black" :foreground "#faea00" :inverse-video t)))
     `(highlight ((,class ,@(if modus-themes-intense-mouseovers
                                (list :background blue-intense-bg :foreground 
fg-main)
                              (list :background cyan-subtle-bg :foreground 
fg-main)))))
@@ -5800,6 +5790,8 @@ by virtue of calling either of 
`modus-themes-load-operandi' and
     `(iflipb-other-buffer-face ((,class :inherit shadow)))
 ;;;;; image-dired
     `(image-dired-thumb-flagged ((,class :background ,red-intense-bg)))
+    `(image-dired-thumb-header-file-name ((,class :inherit bold)))
+    `(image-dired-thumb-header-file-size ((,class :foreground ,blue-active)))
     `(image-dired-thumb-mark ((,class :inherit 
modus-themes-grue-background-intense)))
 ;;;;; imenu-list
     `(imenu-list-entry-face-0 ((,class :foreground ,cyan)))
@@ -6485,6 +6477,8 @@ by virtue of calling either of 
`modus-themes-load-operandi' and
     `(nxml-prolog-keyword ((,class :inherit font-lock-keyword-face)))
     `(nxml-ref ((,class :inherit modus-themes-bold :foreground 
,fg-special-mild)))
     `(rng-error ((,class :inherit error)))
+;;;;; olivetti
+    `(olivetti-fringe ((,class :background ,bg-main)))
 ;;;;; orderless
     `(orderless-match-face-0 ((,class :inherit 
modus-themes-completion-match-0)))
     `(orderless-match-face-1 ((,class :inherit 
modus-themes-completion-match-1)))
@@ -7109,17 +7103,13 @@ by virtue of calling either of 
`modus-themes-load-operandi' and
 ;;;;; table (built-in table.el)
     `(table-cell ((,class :background ,blue-nuanced-bg)))
 ;;;;; telega
-    ;; FIXME 2021-03-28: Some aspects of `telega' are not fully
-    ;; supported or have not been tested thoroughly.  Please understand
-    ;; that I do not use that service because it requires a smartphone
-    ;; and I have none.  Help with testing is appreciated.
     `(telega-button ((,class :box t :foreground ,blue)))
     `(telega-button-active ((,class :box ,blue-intense-bg :background 
,blue-intense-bg :foreground ,fg-main)))
     `(telega-button-highlight ((,class :inherit modus-themes-subtle-magenta)))
     `(telega-chat-prompt ((,class :inherit bold)))
-    `(telega-entity-type-code ((,class :inherit modus-themes-fixed-pitch)))
+    `(telega-entity-type-code ((,class :inherit modus-themes-markup-verbatim)))
     `(telega-entity-type-mention ((,class :foreground ,cyan)))
-    `(telega-entity-type-pre ((,class :inherit modus-themes-fixed-pitch)))
+    `(telega-entity-type-pre ((,class :inherit modus-themes-markup-code)))
     `(telega-entity-type-spoiler ((,class :background ,fg-main :foreground 
,fg-main)))
     `(telega-msg-heading ((,class :background ,bg-alt)))
     `(telega-msg-self-title ((,class :inherit bold)))
@@ -7168,7 +7158,7 @@ by virtue of calling either of 
`modus-themes-load-operandi' and
     `(term-color-yellow ((,class :background ,yellow :foreground ,yellow)))
     `(term-underline ((,class :underline t)))
 ;;;;; textsec
-    `(textsec-suspicious ((,class :inherit modus-themes-refine-red)))
+    `(textsec-suspicious (()))
 ;;;;; tomatinho
     `(tomatinho-ok-face ((,class :foreground ,blue-intense)))
     `(tomatinho-pause-face ((,class :foreground ,yellow-intense)))
diff --git a/etc/themes/modus-vivendi-theme.el 
b/etc/themes/modus-vivendi-theme.el
index 0983e26c78..aefef540a1 100644
--- a/etc/themes/modus-vivendi-theme.el
+++ b/etc/themes/modus-vivendi-theme.el
@@ -1,4 +1,4 @@
-;;; modus-vivendi-theme.el --- Elegant, highly legible and customizable light 
theme -*- lexical-binding:t -*-
+;;; modus-vivendi-theme.el --- Elegant, highly legible and customizable dark 
theme -*- lexical-binding:t -*-
 
 ;; Copyright (C) 2019-2022  Free Software Foundation, Inc.
 
@@ -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: 2.7.1
+;; Version: 3.0.0
 ;; Package-Requires: ((emacs "27.1"))
 ;; Keywords: faces, theme, accessibility
 
@@ -71,4 +71,7 @@ which corresponds to a minimum contrast in relative luminance 
of
 
   (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/tango-dark-theme.el b/etc/themes/tango-dark-theme.el
index ef00d2ac49..85995e4e99 100644
--- a/etc/themes/tango-dark-theme.el
+++ b/etc/themes/tango-dark-theme.el
@@ -27,10 +27,15 @@
 
 ;;; Code:
 
+;;;###theme-autoload
 (deftheme tango-dark
   "Face colors using the Tango palette (dark background).
 Basic, Font Lock, Isearch, Gnus, Message, Ediff, Flyspell,
-Semantic, and Ansi-Color faces are included.")
+Semantic, and Ansi-Color faces are included."
+  :background-mode 'dark
+  :kind 'color-scheme
+  :family 'tango)
+
 
 (let ((class '((class color) (min-colors 89)))
       ;; Tango palette colors.
diff --git a/etc/themes/tango-theme.el b/etc/themes/tango-theme.el
index ecbbf03753..2ac1b42294 100644
--- a/etc/themes/tango-theme.el
+++ b/etc/themes/tango-theme.el
@@ -27,10 +27,14 @@
 
 ;;; Code:
 
+;;;###theme-autoload
 (deftheme tango
   "Face colors using the Tango palette (light background).
 Basic, Font Lock, Isearch, Gnus, Message, Ediff, Flyspell,
-Semantic, and Ansi-Color faces are included.")
+Semantic, and Ansi-Color faces are included."
+  :background-mode 'light
+  :kind 'color-scheme
+  :family 'tango)
 
 (let ((class '((class color) (min-colors 89)))
       ;; Tango palette colors.
diff --git a/etc/themes/tsdh-dark-theme.el b/etc/themes/tsdh-dark-theme.el
index a88ad75520..6b1e865e42 100644
--- a/etc/themes/tsdh-dark-theme.el
+++ b/etc/themes/tsdh-dark-theme.el
@@ -19,8 +19,12 @@
 
 ;;; Code:
 
+;;;###theme-autoload
 (deftheme tsdh-dark
-  "A dark theme used and created by Tassilo Horn.")
+  "A dark theme used and created by Tassilo Horn."
+  :background-mode 'dark
+  :kind 'color-scheme
+  :family 'tsdh)
 
 (custom-theme-set-faces
  'tsdh-dark
diff --git a/etc/themes/tsdh-light-theme.el b/etc/themes/tsdh-light-theme.el
index d9d09b702b..ac964d66d6 100644
--- a/etc/themes/tsdh-light-theme.el
+++ b/etc/themes/tsdh-light-theme.el
@@ -19,9 +19,13 @@
 
 ;;; Code:
 
+;;;###theme-autoload
 (deftheme tsdh-light
   "A light Emacs theme.
-Used and created by Tassilo Horn.")
+Used and created by Tassilo Horn."
+  :background-mode 'light
+  :kind 'color-scheme
+  :family 'tsdh)
 
 (custom-theme-set-faces
  'tsdh-light
diff --git a/etc/themes/wheatgrass-theme.el b/etc/themes/wheatgrass-theme.el
index c56c8a2d8a..20e7bbbac2 100644
--- a/etc/themes/wheatgrass-theme.el
+++ b/etc/themes/wheatgrass-theme.el
@@ -19,11 +19,14 @@
 
 ;;; Code:
 
+;;;###theme-autoload
 (deftheme wheatgrass
   "High-contrast green/blue/brown faces on a black background.
 Basic, Font Lock, Isearch, Gnus, and Message faces are included.
 The default face foreground is wheat, with other faces in shades
-of green, brown, and blue.")
+of green, brown, and blue."
+  :background-mode 'dark
+  :kind 'color-scheme)
 
 (let ((class '((class color) (min-colors 89))))
   (custom-theme-set-faces
diff --git a/etc/themes/whiteboard-theme.el b/etc/themes/whiteboard-theme.el
index f21b18b421..2f86234b32 100644
--- a/etc/themes/whiteboard-theme.el
+++ b/etc/themes/whiteboard-theme.el
@@ -21,8 +21,11 @@
 
 ;;; Code:
 
+;;;###theme-autoload
 (deftheme whiteboard
-  "Face colors similar to markers on a whiteboard.")
+  "Face colors similar to markers on a whiteboard."
+  :background-mode 'light
+  :kind 'color-scheme)
 
 (let ((class '((class color) (min-colors 89))))
   (custom-theme-set-faces
diff --git a/etc/themes/wombat-theme.el b/etc/themes/wombat-theme.el
index d9fab8ac78..9bb026ead1 100644
--- a/etc/themes/wombat-theme.el
+++ b/etc/themes/wombat-theme.el
@@ -21,11 +21,14 @@
 
 ;;; Code:
 
+;;;###theme-autoload
 (deftheme wombat
   "Medium-contrast faces with a dark gray background.
 Adapted, with permission, from a Vim color scheme by Lars H. Nielsen.
 Basic, Font Lock, Isearch, Gnus, Message, and Ansi-Color faces
-are included.")
+are included."
+  :background-mode 'dark
+  :kind 'color-scheme)
 
 (let ((class '((class color) (min-colors 89))))
   (custom-theme-set-faces
diff --git a/lib-src/rcs2log b/lib-src/rcs2log
index bc7875cfdd..2a72404d9e 100755
--- a/lib-src/rcs2log
+++ b/lib-src/rcs2log
@@ -209,7 +209,7 @@ month_data='
 if type mktemp >/dev/null 2>&1; then
        logdir=`mktemp -d`
 else
-       logdir=$TMPDIR/rcs2log$$
+       logdir="${TMPDIR-/tmp}/rcs2log$$"
        (umask 077 && mkdir "$logdir")
 fi || exit
 case $logdir in
diff --git a/lisp/ChangeLog.10 b/lisp/ChangeLog.10
index 0b97a64109..6053ffa65a 100644
--- a/lisp/ChangeLog.10
+++ b/lisp/ChangeLog.10
@@ -9490,7 +9490,7 @@
        toolbar/rescan.pbm, toolbar/rescan.xpm, toolbar/show.pbm,
        toolbar/show.xpm, toolbar/widen.pbm, toolbar/widen.xpm:
        Upgraded to mh-e version 6.1.1.  Full ChangeLog available in
-       http://prdownloads.sourceforge.net/mh-e/mh-e-6.1.tgz?download .
+       https://prdownloads.sourceforge.net/mh-e/mh-e-6.1.tgz?download .
        There were no user-visible changes in 6.1.1 from 6.1--only the
        section of the Makefile that installs the files into Emacs was changed.
 
diff --git a/lisp/ChangeLog.14 b/lisp/ChangeLog.14
index c84e44536d..686746abe0 100644
--- a/lisp/ChangeLog.14
+++ b/lisp/ChangeLog.14
@@ -155,7 +155,7 @@
 
        * epa.el (epa-decrypt-region): Detect encoding if
        coding-system-for-read is not specified.
-       <http://sourceforge.jp/ticket/browse.php?group_id=2267&tid=17018>
+       <https://sourceforge.jp/ticket/browse.php?group_id=2267&tid=17018>
        (epa-verify-region): Ditto.
 
 2009-06-04  Stefan Monnier  <monnier@iro.umontreal.ca>
@@ -540,7 +540,7 @@
 
        * epa-file.el (epa-file-decode-and-insert):
        Use string-to-multibyte instead of set-buffer-multibyte.
-       <http://sourceforge.jp/ticket/browse.php?group_id=2267&tid=15259>
+       <https://sourceforge.jp/ticket/browse.php?group_id=2267&tid=15259>
 
 2009-04-18  Yann Hodique  <yann.hodique@gmail.com>  (tiny change)
 
diff --git a/lisp/ChangeLog.15 b/lisp/ChangeLog.15
index 53caf69e1c..9aa4caedfe 100644
--- a/lisp/ChangeLog.15
+++ b/lisp/ChangeLog.15
@@ -11955,7 +11955,7 @@
        (verilog-skip-backward-comments, verilog-skip-forward-comment-p):
        fix bug for /* / comments.
        (verilog-backward-syntactic-ws, verilog-forward-syntactic-ws):
-       Speed up and simplfy as this is never called with a bound.
+       Speed up and simplify as this is never called with a bound.
        (verilog-pretty-declarations): Enhance to line up declarations
        inside a parameter list, suggested by Alan Morgan.
        (verilog-pretty-expr): Tune assignment regular expression match
@@ -15783,7 +15783,7 @@
        * simple.el (with-wrapper-hook): Fix thinko.
 
        * hfy-cmap.el (hfy-rgb-file): Use locate-file.
-       (htmlfontify-load-rgb-file): Remove unnused var `ff'.
+       (htmlfontify-load-rgb-file): Remove unused var `ff'.
        Use with-current-buffer and string-to-number.
        (hfy-fallback-colour-values): Use assoc-string.
        * htmlfontify.el (hfy-face-to-css): Remove unused var `style'.
@@ -22762,7 +22762,7 @@
 
        Automatically handle .xz suffix (XZ-compressed files), too.
        * jka-cmpr-hook.el (jka-compr-compression-info-list): Add xz.
-       XZ is the successor to LZMA: <http://tukaani.org/xz/>
+       XZ is the successor to LZMA: <https://tukaani.org/xz/>
 
 2009-06-22  Dmitry Dzhus  <dima@sphinx.net.ru>
            Nick Roberts  <nickrob@snap.net.nz>
diff --git a/lisp/ChangeLog.16 b/lisp/ChangeLog.16
index f0a50bb4bc..ef59698317 100644
--- a/lisp/ChangeLog.16
+++ b/lisp/ChangeLog.16
@@ -2814,7 +2814,7 @@
 2012-12-12  Jonas Bernoulli  <jonas@bernoul.li>
 
        * emacs-lisp/eieio.el: Prettier object pretty-printing (bug#13115).
-       (eieio-override-prin1): Don't quote kewords and booleans.
+       (eieio-override-prin1): Don't quote keywords and booleans.
        (object-write) <eieio-default-superclass>: Don't put closing parens
        on new line, avoid needless empty lines, align values that are objects
        with the slot keyword (instead of beginning on the same line).
@@ -10765,7 +10765,7 @@
 
        * play/zone.el (zone-hiding-mode-line): Rename from
        zone-hiding-modeline.  All callers changed.
-       (zone): Remove unusued `modeline-hidden-level' property.
+       (zone): Remove unused `modeline-hidden-level' property.
 
        * progmodes/xscheme.el (xscheme-mode-line-initialize): Rename from
        xscheme-modeline-initialize.  All callers changed.
@@ -24104,7 +24104,7 @@
 
        * emacs-lisp/lisp-mode.el (eval-defun-2): Use eval-sexp-add-defvars.
 
-       * htmlfontify.el (hfy-etags-cmd): Remove inoperant eval-and-compile.
+       * htmlfontify.el (hfy-etags-cmd): Remove inoperative eval-and-compile.
        (hfy-e2x-etags-cmd, hfy-etags-cmd-alist-default)
        (hfy-etags-cmd-alist): Don't eval-and-compile any more.
 
diff --git a/lisp/ChangeLog.17 b/lisp/ChangeLog.17
index cebafe18aa..c494f43896 100644
--- a/lisp/ChangeLog.17
+++ b/lisp/ChangeLog.17
@@ -917,7 +917,7 @@
 
        * desktop.el (desktop-buffer-info): Write docstring.
        (desktop-buffer-info): Use `pushnew' instead of `add-to-list' and
-       unquote lamda.
+       unquote lambda.
 
        * emacs-lisp/package.el (package-refresh-contents): Update doc.
 
@@ -14039,7 +14039,7 @@
 
        * epa-file.el (epa-file-write-region): Encode the region according
        to `buffer-file-format'.  Problem reported at:
-       <http://sourceforge.jp/ticket/browse.php?group_id=2267&tid=32917>.
+       <https://sourceforge.jp/ticket/browse.php?group_id=2267&tid=32917>.
 
 2014-01-14  Stefan Monnier  <monnier@iro.umontreal.ca>
 
diff --git a/lisp/ChangeLog.7 b/lisp/ChangeLog.7
index 747a9ffab9..c81968e6ee 100644
--- a/lisp/ChangeLog.7
+++ b/lisp/ChangeLog.7
@@ -3147,20 +3147,20 @@
 
        * international/ccl.el: Change term translate-XXX-map to map-XXX
        throughout the file.  Change terms unify/unification to
-       translate/translation respectively throughtout the file.
+       translate/translation respectively throughout the file.
 
        * international/quail.el (quail-completion): Consecutive call of
        this command scrolls the Quail completion buffer.
 
        * international/mule.el: Change term unification to translation
-       throughtout the file.
+       throughout the file.
        (set-clipboard-coding-system): New function.
 
        * international/mule-conf.el: Change term unification to
-       translation throughtout the file.
+       translation throughout the file.
 
        * international/mule-util.el: Change term unification to
-       translation throughtout the file.
+       translation throughout the file.
 
 1998-05-17  Richard Stallman  <rms@psilocin.ai.mit.edu>
 
diff --git a/lisp/abbrev.el b/lisp/abbrev.el
index a4f0196a78..2ca8e25dac 100644
--- a/lisp/abbrev.el
+++ b/lisp/abbrev.el
@@ -1221,7 +1221,7 @@ SORTFUN is passed to `sort' to change the default 
ordering."
 
 (defface abbrev-table-name
   '((t :inherit font-lock-function-name-face))
-  "Face used for displaying the abbrev table name in `edit-abbrev-mode'."
+  "Face used for displaying the abbrev table name in `edit-abbrevs-mode'."
   :version "29.1")
 
 (defvar edit-abbrevs-mode-font-lock-keywords
diff --git a/lisp/allout-widgets.el b/lisp/allout-widgets.el
index 7a65777d32..4eab2c11c6 100644
--- a/lisp/allout-widgets.el
+++ b/lisp/allout-widgets.el
@@ -2177,7 +2177,7 @@ Operation is inhibited by 
`allout-inhibit-body-modification-handler'."
 ;; ?? Escapes removal (before changes) is not done when edits span multiple
 ;; items, recognizing that item structure is being preserved, including
 ;; escaping of item-prefix-like text within bodies.  See
-;; `allout-before-modification-handler' and
+;; `allout-body-modification-handler' and
 ;; `allout-inhibit-body-modification-handler'.
 ;;
 ;; Adds the overlay to the `allout-unresolved-body-mod-workroster' during
diff --git a/lisp/ansi-color.el b/lisp/ansi-color.el
index 6f1c270c23..5e7015db54 100644
--- a/lisp/ansi-color.el
+++ b/lisp/ansi-color.el
@@ -418,7 +418,7 @@ and it should apply face FACE to the text between BEG and 
END.")
   (setq ansi-color-for-comint-mode 'filter))
 
 ;;;###autoload
-(defun ansi-color-process-output (ignored)
+(defun ansi-color-process-output (_ignored)
   "Maybe translate SGR control sequences of comint output into text properties.
 
 Depending on variable `ansi-color-for-comint-mode' the comint output is
diff --git a/lisp/ansi-osc.el b/lisp/ansi-osc.el
index 34154998cd..a8523fc9dc 100644
--- a/lisp/ansi-osc.el
+++ b/lisp/ansi-osc.el
@@ -125,13 +125,11 @@ and `shell-dirtrack-mode'."
 
 ;; Hyperlink handling (OSC 8)
 
-(defvar ansi-osc-hyperlink-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map "\C-c\r" 'browse-url-button-open)
-    (define-key map [mouse-2] 'browse-url-button-open)
-    (define-key map [follow-link] 'mouse-face)
-    map)
-  "Keymap used by OSC 8 hyperlink buttons.")
+(defvar-keymap ansi-osc-hyperlink-map
+  :doc "Keymap used by OSC 8 hyperlink buttons."
+  "C-c RET"       #'browse-url-button-open
+  "<mouse-2>"     #'browse-url-button-open
+  "<follow-link>" 'mouse-face)
 
 (define-button-type 'ansi-osc-hyperlink
   'keymap ansi-osc-hyperlink-map
@@ -143,7 +141,7 @@ and `shell-dirtrack-mode'."
 
 (defun ansi-osc-hyperlink-handler (_ text)
   "Create a hyperlink from an OSC 8 escape sequence.
-This function is intended to be included as an elemnt of the list
+This function is intended to be included as an element of the list
 that is the value of `ansi-osc-handlers'."
   (when ansi-osc-hyperlink--state
     (let ((start (car ansi-osc-hyperlink--state))
diff --git a/lisp/apropos.el b/lisp/apropos.el
index 624c29cb41..62a37df820 100644
--- a/lisp/apropos.el
+++ b/lisp/apropos.el
@@ -25,8 +25,7 @@
 ;;; Commentary:
 
 ;; The ideas for this package were derived from the C code in
-;; src/keymap.c and elsewhere.  The functions in this file should
-;; always be byte-compiled for speed.
+;; src/keymap.c and elsewhere.
 
 ;; The idea for super-apropos is based on the original implementation
 ;; by Lynn Slater <lrs@esl.com>.
diff --git a/lisp/autoinsert.el b/lisp/autoinsert.el
index 580f6b3ced..354b28da5a 100644
--- a/lisp/autoinsert.el
+++ b/lisp/autoinsert.el
@@ -135,7 +135,7 @@ If this contains a %s, that will be replaced by the 
matching rule."
        (if (eq major-mode (default-value 'major-mode))
            (sh-mode))))
 
-    (ada-mode . ada-header)
+    (ada-mode . ada-skel-initial-string)
 
     (("\\.[1-9]\\'" . "Man page skeleton")
      "Short description: "
@@ -168,7 +168,7 @@ If this contains a %s, that will be replaced by the 
matching rule."
 
     (".dir-locals.el"
      nil
-     ";;; Directory Local Variables\n"
+     ";;; Directory Local Variables         -*- no-byte-compile: t; -*-\n"
      ";;; For more information see (info \"(emacs) Directory Variables\")\n\n"
      "(("
      '(setq v1 (let (modes)
diff --git a/lisp/autorevert.el b/lisp/autorevert.el
index 576659675b..e3d66c04bc 100644
--- a/lisp/autorevert.el
+++ b/lisp/autorevert.el
@@ -297,9 +297,10 @@ You should set this variable through Custom."
 (defcustom auto-revert-notify-exclude-dir-regexp
   (concat
    ;; No mounted file systems.
-   "^" (regexp-opt '("/afs/" "/media/" "/mnt" "/net/" "/tmp_mnt/"))
+   mounted-file-systems
    ;; No remote files.
-   (unless auto-revert-remote-files "\\|^/[^/|:][^/|]+:"))
+   (unless auto-revert-remote-files
+     (rx (| "" (: bol "/" (not (any "/:|")) (1+ (not (any "/|"))) ":")))))
   "Regular expression of directories to be excluded from file notifications."
   :group 'auto-revert
   :type 'regexp
diff --git a/lisp/bindings.el b/lisp/bindings.el
index 2e32128274..c1ad5f7520 100644
--- a/lisp/bindings.el
+++ b/lisp/bindings.el
@@ -1029,6 +1029,14 @@ if `inhibit-field-text-motion' is non-nil."
 (define-key global-map [XF86Back] 'previous-buffer)
 (put 'previous-buffer :advertised-binding [?\C-x left])
 
+(defvar-keymap buffer-navigation-repeat-map
+  :doc "Keymap to repeat `next-buffer' and `previous-buffer'.  Used in 
`repeat-mode'."
+  "<right>" #'next-buffer
+  "<left>"  #'previous-buffer)
+
+(put 'next-buffer 'repeat-map 'buffer-navigation-repeat-map)
+(put 'previous-buffer 'repeat-map 'buffer-navigation-repeat-map)
+
 (let ((map minibuffer-local-map))
   (define-key map "\en"   'next-history-element)
   (define-key map [next]  'next-history-element)
diff --git a/lisp/bs.el b/lisp/bs.el
index aabc2dc558..060bae6fdd 100644
--- a/lisp/bs.el
+++ b/lisp/bs.el
@@ -823,10 +823,14 @@ Leave Buffer Selection Menu."
   "Visit the tags table in the buffer on this line.
 See `visit-tags-table'."
   (interactive)
-  (let ((file (buffer-file-name (bs--current-buffer))))
-    (if file
-       (visit-tags-table file)
-      (error "Specified buffer has no file"))))
+  (let* ((buf (bs--current-buffer))
+         (file (buffer-file-name buf)))
+    (cond
+      ((not file) (error "Specified buffer has no file"))
+      ((and buf (with-current-buffer buf
+                  (etags-verify-tags-table)))
+       (visit-tags-table file))
+      (t (error "Specified buffer is not a tags-table")))))
 
 (defun bs-toggle-current-to-show ()
   "Toggle status of showing flag for buffer in current line."
diff --git a/lisp/calendar/diary-lib.el b/lisp/calendar/diary-lib.el
index 98e91aaa75..0ebfc4bb8d 100644
--- a/lisp/calendar/diary-lib.el
+++ b/lisp/calendar/diary-lib.el
@@ -92,7 +92,7 @@ are holidays."
 This is used by `diary-pull-attrs' to fontify certain diary
 elements.  REGEXP is a regular expression to for, and SUBEXP is
 the numbered sub-expression to extract.  `diary-glob-file-regexp-prefix'
-is pre-pended to REGEXP for file-wide specifiers.  ATTRIBUTE
+is prepended to REGEXP for file-wide specifiers.  ATTRIBUTE
 specifies which face attribute (e.g. `:foreground') to modify, or
 that this is a face (`:face') to apply.  TYPE is the type of
 attribute being applied.  Available TYPES (see `diary-attrtype-convert')
@@ -109,7 +109,7 @@ are: `string', `symbol', `int', `tnil', `stringtnil'."
   :group 'diary)
 
 (defcustom diary-glob-file-regexp-prefix "^#"
-  "Regular expression pre-pended to `diary-face-attrs' for file-wide 
specifiers."
+  "Regular expression prepended to `diary-face-attrs' for file-wide 
specifiers."
   :type 'regexp
   :group 'diary)
 
diff --git a/lisp/cedet/ede.el b/lisp/cedet/ede.el
index e6bfd0b1e8..1118235757 100644
--- a/lisp/cedet/ede.el
+++ b/lisp/cedet/ede.el
@@ -598,7 +598,7 @@ an EDE controlled project."
      "\\.#"
      "~$"
      )
-  "List of file name patters that EDE will never ask about.")
+  "List of file name patterns that EDE will never ask about.")
 
 (defun ede-ignore-file (filename)
   "Should we ignore FILENAME?"
diff --git a/lisp/cedet/ede/locate.el b/lisp/cedet/ede/locate.el
index b9b1194ccc..3d89419364 100644
--- a/lisp/cedet/ede/locate.el
+++ b/lisp/cedet/ede/locate.el
@@ -172,7 +172,7 @@ You cannot create projects for the baseclass."
 (defclass ede-locate-locate (ede-locate-base)
   ()
   "EDE Locator using the locate command.
-Configure the Emacs `locate-program' variable to also
+Configure the Emacs `locate-command' variable to also
 configure the use of EDE locate.")
 
 (cl-defmethod ede-locate-ok-in-project ((_loc (subclass ede-locate-locate))
@@ -315,7 +315,7 @@ that created this EDE locate object."
   ()
   "EDE Locator using Cscope.
 Configure EDE's use of Cscope through the cedet-cscope.el
-file name searching variable `cedet-cscope-file-command'.")
+file name searching variable `cedet-cscope-command'.")
 
 (cl-defmethod initialize-instance ((loc ede-locate-cscope)
                                   &rest _slots)
diff --git a/lisp/cedet/pulse.el b/lisp/cedet/pulse.el
index 0564cf6d04..5b0df013a3 100644
--- a/lisp/cedet/pulse.el
+++ b/lisp/cedet/pulse.el
@@ -47,7 +47,7 @@
 ;; The original pulse code was written for semantic tag highlighting.
 ;; It has been extracted, and adapted for general purpose pulsing.
 ;;
-;; Pulse is a part of CEDET.  http://cedet.sf.net
+;; Pulse is a part of CEDET.  https://cedet.sourceforge.net
 
 (require 'color)
 
diff --git a/lisp/cedet/semantic/symref/grep.el 
b/lisp/cedet/semantic/symref/grep.el
index 27ea80fc32..076775bfec 100644
--- a/lisp/cedet/semantic/symref/grep.el
+++ b/lisp/cedet/semantic/symref/grep.el
@@ -139,6 +139,8 @@ This shell should support pipe redirect syntax."
                             (lambda (s) (concat "\\" s))
                             string nil t))
 
+(defvar semantic-symref-grep--local-dir nil)
+
 (cl-defmethod semantic-symref-perform-search ((tool semantic-symref-tool-grep))
   "Perform a search with Grep."
   ;; Grep doesn't support some types of searches.
@@ -170,11 +172,12 @@ This shell should support pipe redirect syntax."
       (erase-buffer)
       (setq default-directory rootdir)
       (let ((cmd (semantic-symref-grep-use-template
-                  (directory-file-name (file-local-name rootdir))
+                  "."
                   filepattern grepflags greppat)))
         (process-file semantic-symref-grep-shell nil b nil
                       shell-command-switch cmd)))
-    (setq ans (semantic-symref-parse-tool-output tool b))
+    (let ((semantic-symref-grep--local-dir (directory-file-name 
(file-local-name rootdir))))
+      (setq ans (semantic-symref-parse-tool-output tool b)))
     ;; Return the answer
     ans))
 
@@ -190,12 +193,12 @@ Moves cursor to end of the match."
           ((eq (oref tool resulttype) 'line-and-text)
            (when (re-search-forward grep-re nil t)
              (list (string-to-number (match-string line-group))
-                   (match-string file-group)
+                   (concat semantic-symref-grep--local-dir (substring 
(match-string file-group) 1))
                    (buffer-substring-no-properties (point) 
(line-end-position)))))
          (t
           (when (re-search-forward grep-re nil t)
             (cons (string-to-number (match-string line-group))
-                  (match-string file-group))
+                  (concat semantic-symref-grep--local-dir (substring 
(match-string file-group) 1)))
             )))))
 
 (provide 'semantic/symref/grep)
diff --git a/lisp/cedet/semantic/wisent.el b/lisp/cedet/semantic/wisent.el
index 55eeef453e..b13dc99456 100644
--- a/lisp/cedet/semantic/wisent.el
+++ b/lisp/cedet/semantic/wisent.el
@@ -38,7 +38,7 @@
 
 (defvar wisent-lex-lookahead nil
   "Extra lookahead token.
-When non-nil it is directly returned by `wisent-lex-function'.")
+When non-nil it is directly returned by `wisent-lexer-function'.")
 
 (defmacro wisent-lex-eoi ()
   "Return an End-Of-Input lexical token.
diff --git a/lisp/cus-edit.el b/lisp/cus-edit.el
index ee32c9c945..00ee9504c2 100644
--- a/lisp/cus-edit.el
+++ b/lisp/cus-edit.el
@@ -1,7 +1,7 @@
 ;;; cus-edit.el --- tools for customizing Emacs and Lisp packages -*- 
lexical-binding:t -*-
-;;
-;; Copyright (C) 1996-1997, 1999-2022 Free Software Foundation, Inc.
-;;
+
+;; Copyright (C) 1996-2022 Free Software Foundation, Inc.
+
 ;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
 ;; Maintainer: emacs-devel@gnu.org
 ;; Keywords: help, faces
@@ -23,7 +23,7 @@
 ;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
 
 ;;; Commentary:
-;;
+
 ;; This file implements the code to create and edit customize buffers.
 ;;
 ;; See `custom.el'.
@@ -428,32 +428,30 @@ Use group `text' for this instead.  This group is 
deprecated."
 
 ;;; Custom mode keymaps
 
-(defvar custom-mode-map
-  (let ((map (make-keymap)))
-    (set-keymap-parent map widget-keymap)
-    (define-key map [remap self-insert-command] 'Custom-no-edit)
-    (define-key map "\^m" 'Custom-newline)
-    (define-key map " " 'scroll-up-command)
-    (define-key map [?\S-\ ] 'scroll-down-command)
-    (define-key map "\177" 'scroll-down-command)
-    (define-key map "\C-c\C-c" 'Custom-set)
-    (define-key map "\C-x\C-s" 'Custom-save)
-    (define-key map "q" 'Custom-buffer-done)
-    (define-key map "u" 'Custom-goto-parent)
-    (define-key map "n" 'widget-forward)
-    (define-key map "p" 'widget-backward)
-    (define-key map "H" 'custom-toggle-hide-all-widgets)
-    map)
-  "Keymap for `Custom-mode'.")
-
-(defvar custom-mode-link-map
-  (let ((map (make-keymap)))
-    (set-keymap-parent map custom-mode-map)
-    (define-key map [down-mouse-2] nil)
-    (define-key map [down-mouse-1] 'mouse-drag-region)
-    (define-key map [mouse-2] 'widget-move-and-invoke)
-    map)
-  "Local keymap for links in `Custom-mode'.")
+(defvar-keymap custom-mode-map
+  :doc "Keymap for `Custom-mode'."
+  :full t
+  :parent widget-keymap
+  "<remap> <self-insert-command>" #'Custom-no-edit
+  "RET"     #'Custom-newline
+  "SPC"     #'scroll-up-command
+  "S-SPC"   #'scroll-down-command
+  "DEL"     #'scroll-down-command
+  "C-c C-c" #'Custom-set
+  "C-x C-s" #'Custom-save
+  "q"       #'Custom-buffer-done
+  "u"       #'Custom-goto-parent
+  "n"       #'widget-forward
+  "p"       #'widget-backward
+  "H"       #'custom-toggle-hide-all-widgets)
+
+(defvar-keymap custom-mode-link-map
+  :doc "Local keymap for links in `Custom-mode'."
+  :full t
+  :parent custom-mode-map
+  "<down-mouse-2>" nil
+  "<down-mouse-1>" #'mouse-drag-region
+  "<mouse-2>"      #'widget-move-and-invoke)
 
 (defvar custom-field-keymap
   (let ((map (copy-keymap widget-field-keymap)))
@@ -1546,7 +1544,7 @@ If TYPE is `groups', include only groups."
      "*Customize Apropos*")))
 
 ;;;###autoload
-(defun customize-apropos-options (regexp &optional ignored)
+(defun customize-apropos-options (regexp &optional _ignored)
   "Customize all loaded customizable options matching REGEXP."
   (interactive (list (apropos-read-pattern "options")))
   (customize-apropos regexp 'options))
diff --git a/lisp/cus-theme.el b/lisp/cus-theme.el
index 90680ff68f..0260ad4a50 100644
--- a/lisp/cus-theme.el
+++ b/lisp/cus-theme.el
@@ -32,17 +32,15 @@
 (eval-when-compile
   (require 'wid-edit))
 
-(defvar custom-new-theme-mode-map
-  (let ((map (make-keymap)))
-    (set-keymap-parent map (make-composed-keymap widget-keymap
-                                                special-mode-map))
-    (suppress-keymap map)
-    (define-key map "\C-x\C-s" 'custom-theme-write)
-    (define-key map "q" 'Custom-buffer-done)
-    (define-key map "n" 'widget-forward)
-    (define-key map "p" 'widget-backward)
-    map)
-  "Keymap for `custom-new-theme-mode'.")
+(defvar-keymap custom-new-theme-mode-map
+  :doc "Keymap for `custom-new-theme-mode'."
+  :full t
+  :suppress t
+  :parent (make-composed-keymap widget-keymap special-mode-map)
+  "C-x C-s" #'custom-theme-write
+  "q"       #'Custom-buffer-done
+  "n"       #'widget-forward
+  "p"       #'widget-backward)
 
 (define-derived-mode custom-new-theme-mode nil "Custom-Theme"
   "Major mode for editing Custom themes.
@@ -142,7 +140,7 @@ remove them from your saved Custom file.\n\n")
     (widget-create 'push-button
                   :tag " Revert "
                   :help-echo "Revert this buffer to its original state."
-                  :action (lambda (&rest ignored) (revert-buffer)))
+                   :action (lambda (&rest _ignored) (revert-buffer)))
 
     (widget-insert "\n\nTheme name : ")
     (setq custom-theme-name
@@ -534,17 +532,15 @@ It includes all faces in list FACES."
   :type 'boolean
   :group 'custom-buffer)
 
-(defvar custom-theme-choose-mode-map
-  (let ((map (make-keymap)))
-    (set-keymap-parent map (make-composed-keymap widget-keymap
-                                                special-mode-map))
-    (suppress-keymap map)
-    (define-key map "\C-x\C-s" 'custom-theme-save)
-    (define-key map "n" 'widget-forward)
-    (define-key map "p" 'widget-backward)
-    (define-key map "?" 'custom-describe-theme)
-    map)
-  "Keymap for `custom-theme-choose-mode'.")
+(defvar-keymap custom-theme-choose-mode-map
+  :doc "Keymap for `custom-theme-choose-mode'."
+  :full t
+  :suppress t
+  :parent (make-composed-keymap widget-keymap special-mode-map)
+  "C-x C-s" #'custom-theme-save
+  "n"       #'widget-forward
+  "p"       #'widget-backward
+  "?"       #'custom-describe-theme)
 
 (define-derived-mode custom-theme-choose-mode special-mode "Themes"
   "Major mode for selecting Custom themes.
diff --git a/lisp/custom.el b/lisp/custom.el
index 604b1a3ff4..0d3e2e5d0c 100644
--- a/lisp/custom.el
+++ b/lisp/custom.el
@@ -1152,9 +1152,11 @@ list, in which A occurs before B if B was defined with a
 ;;   (provide-theme 'THEME)
 
 
-(defmacro deftheme (theme &optional doc)
+(defmacro deftheme (theme &optional doc &rest properties)
   "Declare THEME to be a Custom theme.
 The optional argument DOC is a doc string describing the theme.
+PROPERTIES are interpreted as a property list that will be stored
+in the `theme-properties' property for THEME.
 
 Any theme `foo' should be defined in a file called `foo-theme.el';
 see `custom-make-theme-feature' for more information."
@@ -1164,18 +1166,25 @@ see `custom-make-theme-feature' for more information."
     ;; It is better not to use backquote in this file,
     ;; because that makes a bootstrapping problem
     ;; if you need to recompile all the Lisp files using interpreted code.
-    (list 'custom-declare-theme (list 'quote theme) (list 'quote feature) 
doc)))
+    (list 'custom-declare-theme (list 'quote theme) (list 'quote feature) doc
+          (cons 'list properties))))
 
-(defun custom-declare-theme (theme feature &optional doc)
+(defun custom-declare-theme (theme feature &optional doc properties)
   "Like `deftheme', but THEME is evaluated as a normal argument.
-FEATURE is the feature this theme provides.  Normally, this is a symbol
-created from THEME by `custom-make-theme-feature'."
+FEATURE is the feature this theme provides.  Normally, this is a
+symbol created from THEME by `custom-make-theme-feature'.  The
+optional argument DOC may contain the documentation for THEME.
+The optional argument PROPERTIES may contain a property list of
+attributes associated with THEME."
   (unless (custom-theme-name-valid-p theme)
     (error "Custom theme cannot be named %S" theme))
   (unless (memq theme custom-known-themes)
     (push theme custom-known-themes))
   (put theme 'theme-feature feature)
-  (when doc (put theme 'theme-documentation doc)))
+  (when doc
+    (put theme 'theme-documentation doc))
+  (when properties
+    (put theme 'theme-properties properties)))
 
 (defun custom-make-theme-feature (theme)
   "Given a symbol THEME, create a new symbol by appending \"-theme\".
@@ -1372,6 +1381,58 @@ Return t if THEME was successfully loaded, nil 
otherwise."
     (enable-theme theme))
   t)
 
+(defun theme-list-variants (theme &rest list)
+  "Return a list of theme variants for THEME.
+By default this will use all known custom themes (see
+`custom-available-themes') to check for variants.  This can be
+restricted if the optional argument LIST containing a list of
+theme symbols to consider."
+  (let* ((properties (get theme 'theme-properties))
+         (family (plist-get properties :family)))
+    (seq-filter
+     (lambda (variant)
+       (and (eq (plist-get (get variant 'theme-properties) :family)
+                family)
+            (not (eq variant theme))))
+     (or list (custom-available-themes)))))
+
+(defun theme-choose-variant (&optional no-confirm no-enable)
+  "Switch from the current theme to one of its variants.
+The current theme will be disabled before variant is enabled.  If
+the current theme has only one variant, switch to that variant
+without prompting, otherwise prompt for the variant to select.
+See `load-theme' for the meaning of NO-CONFIRM and NO-ENABLE."
+  (interactive)
+  (let ((active-color-schemes
+         (seq-filter
+          (lambda (theme)
+            ;; FIXME: As most themes currently do not have a `:kind'
+            ;; tag, it is assumed that a theme is a color scheme by
+            ;; default.  This should be reconsidered in the future.
+            (memq (plist-get (get theme 'theme-properties) :kind)
+                  '(color-scheme nil)))
+          custom-enabled-themes)))
+    (cond
+     ((length= active-color-schemes 0)
+      (user-error "No theme is active, cannot toggle"))
+     ((length> active-color-schemes 1)
+      (user-error "More than one theme active, cannot unambiguously toggle")))
+    (let* ((theme (car active-color-schemes))
+           (family (plist-get (get theme 'theme-properties) :family)))
+      (unless family
+        (error "Theme `%s' does not have any known variants" theme))
+      (let* ((variants (theme-list-variants theme))
+             (choice (cond
+                      ((null variants)
+                       (error "`%s' has no variants" theme))
+                      ((length= variants 1)
+                       (car variants))
+                      ((intern (completing-read "Load custom theme: " 
variants))))))
+        (disable-theme theme)
+        (load-theme choice no-confirm no-enable)))))
+
+(defalias 'toggle-theme #'theme-choose-variant)
+
 (defun custom-theme-load-confirm (hash)
   "Query the user about loading a Custom theme that may not be safe.
 The theme should be in the current buffer.  If the user agrees,
diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el
index 9add96c260..5e1745069f 100644
--- a/lisp/dired-aux.el
+++ b/lisp/dired-aux.el
@@ -1025,8 +1025,9 @@ If PROGRAM exits successfully, display \"MSG...done\" and 
return nil.
 If PROGRAM exits abnormally, save in `dired-log-buffer' the command
 that invoked PROGRAM and the messages it emitted, and return either
 the offending ARGUMENTS or PROGRAM if no ARGUMENTS were provided."
-  (let (err-buffer err (dir default-directory))
-    (message "%s..." msg)
+  (let ((dir default-directory)
+        (reporter (make-progress-reporter msg))
+        err-buffer err)
     (save-excursion
       ;; Get a clean buffer for error output:
       (setq err-buffer (get-buffer-create " *dired-check-process output*"))
@@ -1041,8 +1042,8 @@ the offending ARGUMENTS or PROGRAM if no ARGUMENTS were 
provided."
            (dired-log err-buffer)
            (or arguments program t))
        (kill-buffer err-buffer)
-       (message "%s...done" msg)
-       nil))))
+        (progress-reporter-done reporter)
+        nil))))
 
 (defun dired-shell-command (cmd)
   "Run CMD, and check for output.
@@ -3478,7 +3479,7 @@ Use \\[dired-hide-all] to (un)hide all directories."
       (dired-next-subdir 1 t))))
 
 ;;;###autoload
-(defun dired-hide-all (&optional ignored)
+(defun dired-hide-all (&optional _ignored)
   "Hide all subdirectories, leaving only their header lines.
 If there is already something hidden, make everything visible again.
 Use \\[dired-hide-subdir] to (un)hide a particular subdirectory."
@@ -3718,9 +3719,9 @@ function works."
 ;;;###autoload
 (defun dired-show-file-type (file &optional deref-symlinks)
   "Print the type of FILE, according to the `file' command.
-If you give a prefix to this command, and FILE is a symbolic
-link, then the type of the file linked to by FILE is printed
-instead."
+If you give a prefix argument \\[universal-argument] to this command, and
+FILE is a symbolic link, then the command will print the type
+of the target of the link instead."
   (interactive (list (dired-get-filename t) current-prefix-arg))
   (let (process-file-side-effects)
     (with-temp-buffer
diff --git a/lisp/dired.el b/lisp/dired.el
index 85a7131570..8995c48df7 100644
--- a/lisp/dired.el
+++ b/lisp/dired.el
@@ -1457,9 +1457,9 @@ wildcards, erases the buffer, and builds the subdir-alist 
anew
        (if (eq (car attributes) t)
            (set-visited-file-modtime (file-attribute-modification-time
                                        attributes))))
-      (set-buffer-modified-p nil)
       (when dired-make-directory-clickable
         (dired--make-directory-clickable))
+      (set-buffer-modified-p nil)
       ;; No need to narrow since the whole buffer contains just
       ;; dired-readin's output, nothing else.  The hook can
       ;; successfully use dired functions (e.g. dired-get-filename)
@@ -1911,11 +1911,15 @@ mouse-2: visit this file in other window"
 (defun dired--make-directory-clickable ()
   (save-excursion
     (goto-char (point-min))
-    (while (re-search-forward "^  /" nil t 1)
+    (while (re-search-forward
+            (if (memq system-type '(windows-nt ms-dos))
+                "^  \\([a-zA-Z]:/\\|//\\)"
+              "^  /")
+            nil t 1)
       (let ((bound (line-end-position))
             (segment-start (point))
             (inhibit-read-only t)
-            (dir "/"))
+            (dir (substring (match-string 0) 2)))
         (while (search-forward "/" bound t 1)
           (setq dir (concat dir (buffer-substring segment-start (point))))
           (add-text-properties
@@ -3024,13 +3028,13 @@ See options: `dired-hide-details-hide-symlink-targets' 
and
   ;; The old code used selective-display which only works at
   ;; a line-granularity, so it used start and end positions that where
   ;; approximate ("anywhere on the line is fine").
-  ;; FIXME: This also removes other invisible properties!
   (save-excursion
     (let ((inhibit-read-only t))
       (remove-list-of-text-properties
        (progn (goto-char start) (line-end-position))
        (progn (goto-char end) (line-end-position))
-       '(invisible)))))
+       '(invisible))
+      (dired-insert-set-properties start end))))
 
 ;;; Functions for finding the file name in a dired buffer line
 
diff --git a/lisp/dom.el b/lisp/dom.el
index f8c794a300..01bdef3a07 100644
--- a/lisp/dom.el
+++ b/lisp/dom.el
@@ -30,23 +30,17 @@
 (defsubst dom-tag (node)
   "Return the NODE tag."
   ;; Called on a list of nodes.  Use the first.
-  (if (consp (car node))
-      (caar node)
-    (car node)))
+  (car (if (consp (car node)) (car node) node)))
 
 (defsubst dom-attributes (node)
   "Return the NODE attributes."
   ;; Called on a list of nodes.  Use the first.
-  (if (consp (car node))
-      (cadr (car node))
-    (cadr node)))
+  (cadr (if (consp (car node)) (car node) node)))
 
 (defsubst dom-children (node)
   "Return the NODE children."
   ;; Called on a list of nodes.  Use the first.
-  (if (consp (car node))
-      (cddr (car node))
-    (cddr node)))
+  (cddr (if (consp (car node)) (car node) node)))
 
 (defun dom-non-text-children (node)
   "Return all non-text-node children of NODE."
@@ -62,10 +56,11 @@
 (defun dom-set-attribute (node attribute value)
   "Set ATTRIBUTE in NODE to VALUE."
   (setq node (dom-ensure-node node))
-  (let ((old (assoc attribute (cadr node))))
+  (let* ((attributes (cadr node))
+         (old (assoc attribute attributes)))
     (if old
        (setcdr old value)
-      (setcar (cdr node) (nconc (cadr node) (list (cons attribute value)))))))
+      (setcar (cdr node) (cons (cons attribute value) attributes)))))
 
 (defun dom-remove-attribute (node attribute)
   "Remove ATTRIBUTE from NODE."
@@ -80,7 +75,7 @@ A typical attribute is `href'."
 
 (defun dom-text (node)
   "Return all the text bits in the current node concatenated."
-  (mapconcat 'identity (cl-remove-if-not 'stringp (dom-children node)) " "))
+  (mapconcat #'identity (cl-remove-if-not #'stringp (dom-children node)) " "))
 
 (defun dom-texts (node &optional separator)
   "Return all textual data under NODE concatenated with SEPARATOR in-between."
@@ -195,9 +190,7 @@ ATTRIBUTE would typically be `class', `id' or the like."
 
 (defun dom-node (tag &optional attributes &rest children)
   "Return a DOM node with TAG and ATTRIBUTES."
-  (if children
-      `(,tag ,attributes ,@children)
-    (list tag attributes)))
+  `(,tag ,attributes ,@children))
 
 (defun dom-append-child (node child)
   "Append CHILD to the end of NODE's children."
@@ -215,11 +208,7 @@ If BEFORE is nil, make CHILD NODE's first child."
     (let ((pos (if before
                   (cl-position before children)
                 0)))
-      (if (zerop pos)
-         ;; First child.
-         (setcdr (cdr node) (cons child (cddr node)))
-       (setcdr (nthcdr (1- pos) children)
-               (cons child (nthcdr pos children))))))
+      (push child (nthcdr (+ 2 pos) node))))
   node)
 
 (defun dom-ensure-node (node)
@@ -247,7 +236,7 @@ white-space."
          (insert (format "(%S . %S)" (car elem) (cdr elem)))
          (if (zerop (cl-decf times))
              (insert ")")
-           (insert "\n" (make-string column ? ))))))
+           (insert "\n" (make-string column ?\s))))))
     (let* ((children (if remove-empty
                         (cl-remove-if
                          (lambda (child)
@@ -258,16 +247,16 @@ white-space."
           (times (length children)))
       (if (null children)
          (insert ")")
-       (insert "\n" (make-string (1+ column) ? ))
+       (insert "\n" (make-string (1+ column) ?\s))
        (dolist (child children)
          (if (stringp child)
-             (if (or (not remove-empty)
-                     (not (string-match "\\`[\n\r\t  ]*\\'" child)))
+             (if (not (and remove-empty
+                           (string-match "\\`[\n\r\t  ]*\\'" child)))
                  (insert (format "%S" child)))
            (dom-pp child remove-empty))
          (if (zerop (cl-decf times))
              (insert ")")
-           (insert "\n" (make-string (1+ column) ? ))))))))
+           (insert "\n" (make-string (1+ column) ?\s))))))))
 
 (defun dom-print (dom &optional pretty xml)
   "Print DOM at point as HTML/XML.
@@ -279,18 +268,19 @@ If XML, generate XML instead of HTML."
       (dolist (elem attr)
        ;; In HTML, these are boolean attributes that should not have
        ;; an = value.
-       (if (and (memq (car elem)
-                      '(async autofocus autoplay checked
-                              contenteditable controls default
-                              defer disabled formNoValidate frameborder
-                              hidden ismap itemscope loop
-                              multiple muted nomodule novalidate open
-                              readonly required reversed
-                              scoped selected typemustmatch))
-                (cdr elem)
-                (not xml))
-           (insert (format " %s" (car elem)))
-         (insert (format " %s=%S" (car elem) (cdr elem))))))
+       (insert (if (and (memq (car elem)
+                              '(async autofocus autoplay checked
+                                contenteditable controls default
+                                defer disabled formNoValidate frameborder
+                                hidden ismap itemscope loop
+                                multiple muted nomodule novalidate open
+                                readonly required reversed
+                                scoped selected typemustmatch))
+                        (cdr elem)
+                        (not xml))
+                   (format " %s" (car elem))
+                 (format " %s=\"%s\"" (car elem)
+                         (url-insert-entities-in-string (cdr elem)))))))
     (let* ((children (dom-children dom))
           (non-text nil))
       (if (null children)
@@ -301,7 +291,7 @@ If XML, generate XML instead of HTML."
              (insert child)
            (setq non-text t)
            (when pretty
-              (insert "\n" (make-string (+ column 2) ? )))
+              (insert "\n" (make-string (+ column 2) ?\s)))
            (dom-print child pretty xml)))
        ;; If we inserted non-text child nodes, or a text node that
        ;; ends with a newline, then we indent the end tag.
@@ -310,7 +300,7 @@ If XML, generate XML instead of HTML."
                       non-text))
          (unless (bolp)
             (insert "\n"))
-         (insert (make-string column ? )))
+         (insert (make-string column ?\s)))
         (insert (format "</%s>" (dom-tag dom)))))))
 
 (provide 'dom)
diff --git a/lisp/ecomplete.el b/lisp/ecomplete.el
index 6ff67d46d2..b532ef95e7 100644
--- a/lisp/ecomplete.el
+++ b/lisp/ecomplete.el
@@ -70,9 +70,9 @@
   :type '(symbol :tag "Coding system"))
 
 (defcustom ecomplete-sort-predicate #'ecomplete-decay
-  "Predicate to use when sorting matched.
-The predicate is called with two parameters that represent the
-completion.  Each parameter is a list where the first element is
+  "Predicate to use when sorting matched ecomplete candidates.
+The predicate is called with two arguments that represent the
+completion.  Each argument is a list where the first element is
 the times the completion has been used, the second is the
 timestamp of the most recent usage, and the third item is the
 string that was matched."
@@ -86,6 +86,12 @@ string that was matched."
   :type 'boolean
   :version "29.1")
 
+(defcustom ecomplete-filter-regexp nil
+  "Regular expression of addresses that should not be stored by ecomplete."
+  :type '(choice (const :tag "None" nil)
+                 (regexp :tag "Regexp"))
+  :version "29.1")
+
 ;;; Internal variables.
 
 (defvar ecomplete-database nil)
@@ -104,20 +110,22 @@ string that was matched."
 By default, the longest version of TEXT will be preserved, but if
 FORCE is non-nil, use TEXT exactly as is."
   (unless ecomplete-database (ecomplete-setup))
-  (let ((elems (assq type ecomplete-database))
-       (now (time-convert nil 'integer))
-       entry)
-    (unless elems
-      (push (setq elems (list type)) ecomplete-database))
-    (if (setq entry (assoc key (cdr elems)))
-       (pcase-let ((`(,_key ,count ,_time ,oldtext) entry))
-         (setcdr entry (list (1+ count) now
-                             ;; Preserve the "more complete" text.
-                             (if (or force
-                                      (>= (length text) (length oldtext)))
-                                 text
-                                oldtext))))
-      (nconc elems (list (list key 1 now text))))))
+  (unless (and ecomplete-filter-regexp
+               (string-match-p ecomplete-filter-regexp key))
+    (let ((elems (assq type ecomplete-database))
+          (now (time-convert nil 'integer))
+          entry)
+      (unless elems
+        (push (setq elems (list type)) ecomplete-database))
+      (if (setq entry (assoc key (cdr elems)))
+          (pcase-let ((`(,_key ,count ,_time ,oldtext) entry))
+            (setcdr entry (list (1+ count) now
+                                ;; Preserve the "more complete" text.
+                                (if (or force
+                                        (>= (length text) (length oldtext)))
+                                    text
+                                  oldtext))))
+        (nconc elems (list (list key 1 now text)))))))
 
 (defun ecomplete--remove-item (type key)
   "Remove the element of TYPE and KEY from the ecomplete database."
@@ -182,7 +190,7 @@ FORCE is non-nil, use TEXT exactly as is."
 If CHOOSE, allow the user to choose interactively between the
 matches.
 
-Auto-select when `ecomplete-message-display-abbrev-auto-select' is
+Auto-select when `ecomplete-auto-select' is
 non-nil and there is only a single completion option available."
   (let* ((matches (ecomplete-get-matches type word))
          (match-list (and matches (split-string matches "\n")))
@@ -289,7 +297,7 @@ non-nil and there is only a single completion option 
available."
                      nil t)))
 
 (defun ecomplete-edit ()
-  "Prompt for an item and allow editing it."
+  "Prompt for an ecomplete item and allow editing it."
   (interactive)
   (let* ((type (ecomplete--prompt-type))
          (data (cdr (assq type ecomplete-database)))
@@ -305,7 +313,8 @@ non-nil and there is only a single completion option 
available."
     (ecomplete-save)))
 
 (defun ecomplete-remove ()
-  "Remove entries matching a regexp from the ecomplete database."
+  "Remove from the ecomplete database the entries matching a regexp.
+Prompt for the regexp to match the database entries to be removed."
   (interactive)
   (let* ((type (ecomplete--prompt-type))
          (data (cdr (assq type ecomplete-database)))
diff --git a/lisp/elide-head.el b/lisp/elide-head.el
index 90bf1fe35b..75a3612df9 100644
--- a/lisp/elide-head.el
+++ b/lisp/elide-head.el
@@ -53,8 +53,8 @@
   `(;; GNU GPL
     ("is free software[:;] you can redistribute it" .
      ,(rx (or (seq "If not, see " (? "<")
-                   "http" (? "s") "://www.gnu.org/licenses/"
-                   (? ">") (? " "))
+                   "http" (? "s") "://www.gnu.org/licenses"
+                   (? "/") (? ">") (? " "))
               (seq "Boston, MA " (? " ")
                    "0211" (or "1-1307" "0-1301")
                    (or "  " ", ") "USA")
diff --git a/lisp/emacs-lisp/backtrace.el b/lisp/emacs-lisp/backtrace.el
index 4ffe6f573c..d461698c88 100644
--- a/lisp/emacs-lisp/backtrace.el
+++ b/lisp/emacs-lisp/backtrace.el
@@ -753,7 +753,7 @@ property for use by navigation."
 
 (defun backtrace--line-length-or-nil ()
   "Return `backtrace-line-length' if valid, nil else."
-  ;; mirror the logic in `cl-print-to-string-with-limits'
+  ;; mirror the logic in `cl-print-to-string-with-limit'
   (and (natnump backtrace-line-length)
        (not (zerop backtrace-line-length))
        backtrace-line-length))
diff --git a/lisp/emacs-lisp/bindat.el b/lisp/emacs-lisp/bindat.el
index 0ecac3d52a..82d3c5309f 100644
--- a/lisp/emacs-lisp/bindat.el
+++ b/lisp/emacs-lisp/bindat.el
@@ -163,7 +163,9 @@
   (let ((s (substring bindat-raw bindat-idx (+ bindat-idx len))))
     (setq bindat-idx (+ bindat-idx len))
     (if (stringp s) s
-      (apply #'unibyte-string s))))
+      ;; FIXME: There should be a more efficient way to do this.
+      ;; Should `apply' accept vectors in addition to lists?
+      (apply #'unibyte-string (append s nil)))))
 
 (defun bindat--unpack-strz (&optional len)
   (let ((i 0) s)
@@ -172,7 +174,7 @@
     (setq s (substring bindat-raw bindat-idx (+ bindat-idx i)))
     (setq bindat-idx (+ bindat-idx (or len (1+ i))))
     (if (stringp s) s
-      (apply #'unibyte-string s))))
+      (apply #'unibyte-string (append s nil)))))
 
 (defun bindat--unpack-bits (len)
   (let ((bits nil) (bnum (1- (* 8 len))) j m)
diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el
index 5ef2d7fe82..a7e1df3622 100644
--- a/lisp/emacs-lisp/byte-opt.el
+++ b/lisp/emacs-lisp/byte-opt.el
@@ -178,7 +178,7 @@ Earlier variables shadow later ones with the same name.")
                ;; be displayed when the function's source file will be
                ;; compiled anyway, but more importantly we would otherwise
                ;; emit spurious warnings here because we don't have the full
-               ;; context, such as `declare-functions' placed earlier in the
+               ;; context, such as `declare-function's placed earlier in the
                ;; source file's code or `with-suppressed-warnings' that
                ;; surrounded the `defsubst'.
                (byte-compile-warnings nil))
diff --git a/lisp/emacs-lisp/byte-run.el b/lisp/emacs-lisp/byte-run.el
index 9db84c31b8..a33808ab92 100644
--- a/lisp/emacs-lisp/byte-run.el
+++ b/lisp/emacs-lisp/byte-run.el
@@ -481,6 +481,11 @@ convention was modified."
   (puthash (indirect-function function) signature
            advertised-signature-table))
 
+(defun get-advertised-calling-convention (function)
+  "Get the advertised SIGNATURE of FUNCTION.
+Return t if there isn't any."
+  (gethash function advertised-signature-table t))
+
 (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).
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index 74ba8984f2..c685e5087f 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -129,6 +129,7 @@
 ;; us from emitting warnings when compiling files which use cl-lib without
 ;; requiring it! (bug#30635)
 (eval-when-compile (require 'cl-lib))
+(eval-when-compile (require 'subr-x))
 
 ;; The feature of compiling in a specific target Emacs version
 ;; has been turned off because compile time options are a bad idea.
@@ -1185,27 +1186,22 @@ message buffer `default-directory'."
 (defun byte-compile--first-symbol-with-pos (form)
   "Return the first symbol with position in form, or nil if none.
 Order is by depth-first search."
-  (cond
-   ((symbol-with-pos-p form) form)
-   ((consp form)
-    (or (byte-compile--first-symbol-with-pos (car form))
-        (let ((sym nil))
-          (setq form (cdr form))
-          (while (and (consp form)
-                      (not (setq sym (byte-compile--first-symbol-with-pos
-                                      (car form)))))
-            (setq form (cdr form)))
-          (or sym
-              (and form (byte-compile--first-symbol-with-pos form))))))
-   ((or (vectorp form) (recordp form))
-    (let ((len (length form))
-          (i 0)
-          (sym nil))
-      (while (and (< i len)
-                  (not (setq sym (byte-compile--first-symbol-with-pos
-                                  (aref form i)))))
-        (setq i (1+ i)))
-      sym))))
+  (named-let loop ((form form)
+                   (depth 10))          ;Arbitrary limit.
+    (cond
+     ((<= depth 0) nil)                 ;Avoid cycles (bug#58601).
+     ((symbol-with-pos-p form) form)
+     ((consp form)
+      (or (loop (car form) (1- depth))
+          (loop (cdr form) (1- depth))))
+     ((or (vectorp form) (recordp form))
+      (let ((len (length form))
+            (i 0)
+            (sym nil))
+        (while (and (< i len)
+                    (not (setq sym (loop (aref form i) (1- depth)))))
+          (setq i (1+ i)))
+        sym)))))
 
 (defun byte-compile--warning-source-offset ()
   "Return a source offset from `byte-compile-form-stack' or nil if none."
@@ -1405,11 +1401,11 @@ when printing the error message."
                          (and (not macro-p)
                               (compiled-function-p (symbol-function fn)))))
            (setq fn (symbol-function fn)))
-          (let ((advertised (gethash (if (and (symbolp fn) (fboundp fn))
-                                         ;; Could be a subr.
-                                         (symbol-function fn)
-                                       fn)
-                                     advertised-signature-table t)))
+          (let ((advertised (get-advertised-calling-convention
+                             (if (and (symbolp fn) (fboundp fn))
+                                 ;; Could be a subr.
+                                 (symbol-function fn)
+                               fn))))
             (cond
              ((listp advertised)
               (if macro-p
@@ -1886,6 +1882,9 @@ Files in subdirectories of DIRECTORY are processed also."
   (interactive "DByte force recompile (directory): ")
   (byte-recompile-directory directory nil t))
 
+(defvar byte-compile-ignore-files nil
+  "List of regexps for files to ignore during byte compilation.")
+
 ;;;###autoload
 (defun byte-recompile-directory (directory &optional arg force follow-symlinks)
   "Recompile every `.el' file in DIRECTORY that needs recompilation.
@@ -1942,14 +1941,23 @@ also be compiled."
                      ;; This file is a subdirectory.  Handle them differently.
                      (or (null arg) (eq 0 arg)
                          (y-or-n-p (concat "Check " source "? ")))
-                     (setq directories (nconc directories (list source))))
+                     (setq directories (nconc directories (list source)))
+                      ;; Directory is requested to be ignored
+                      (string-match-p
+                       (regexp-opt byte-compile-ignore-files)
+                       source)
+                      (setq directories (nconc directories (list source))))
                ;; It is an ordinary file.  Decide whether to compile it.
                (if (and (string-match emacs-lisp-file-regexp source)
                        ;; The next 2 tests avoid compiling lock files
                         (file-readable-p source)
                        (not (string-match "\\`\\.#" file))
                         (not (auto-save-file-name-p source))
-                        (not (member source (dir-locals--all-files 
directory))))
+                        (not (member source (dir-locals--all-files directory)))
+                        ;; File is requested to be ignored
+                        (not (string-match-p
+                              (regexp-opt byte-compile-ignore-files)
+                              source)))
                    (progn (cl-incf
                            (pcase (byte-recompile-file source force arg)
                              ('no-byte-compile skip-count)
@@ -2323,9 +2331,15 @@ With argument ARG, insert value in current buffer after 
the form."
        (setq case-fold-search nil))
      (displaying-byte-compile-warnings
       (with-current-buffer inbuffer
-       (and byte-compile-current-file
-            (byte-compile-insert-header byte-compile-current-file
-                                         byte-compile--outbuffer))
+       (when byte-compile-current-file
+         (byte-compile-insert-header byte-compile-current-file
+                                      byte-compile--outbuffer)
+          ;; Instruct native-comp to ignore this file.
+          (when (bound-and-true-p no-native-compile)
+            (with-current-buffer byte-compile--outbuffer
+              (insert
+               "(when (boundp 'comp--no-native-compile)
+  (puthash load-file-name t comp--no-native-compile))\n\n"))))
        (goto-char (point-min))
        ;; Should we always do this?  When calling multiple files, it
        ;; would be useful to delay this warning until all have been
@@ -2563,7 +2577,7 @@ list that represents a doc string reference.
   ;; macroexpand-all.
   ;; (if (memq byte-optimize '(t source))
   ;;     (setq form (byte-optimize-form form for-effect)))
-  (cconv-closure-convert form))
+  (cconv-closure-convert form byte-compile-bound-variables))
 
 ;; byte-hunk-handlers cannot call this!
 (defun byte-compile-toplevel-file-form (top-level-form)
@@ -4661,13 +4675,6 @@ Return the offset in the form (VAR . OFFSET)."
           (byte-compile-form (cadr clause))
         (byte-compile-push-constant nil)))))
 
-(defun byte-compile-not-lexical-var-p (var)
-  (or (not (symbolp var))
-      (special-variable-p var)
-      (memq var byte-compile-bound-variables)
-      (memq var '(nil t))
-      (keywordp var)))
-
 (defun byte-compile-bind (var init-lexenv)
   "Emit byte-codes to bind VAR and update `byte-compile--lexical-environment'.
 INIT-LEXENV should be a lexical-environment alist describing the
@@ -4676,7 +4683,7 @@ Return non-nil if the TOS value was popped."
   ;; The mix of lexical and dynamic bindings mean that we may have to
   ;; juggle things on the stack, to move them to TOS for
   ;; dynamic binding.
-  (if (and lexical-binding (not (byte-compile-not-lexical-var-p var)))
+  (if (not (cconv--not-lexical-var-p var byte-compile-bound-variables))
       ;; VAR is a simple stack-allocated lexical variable.
       (progn (push (assq var init-lexenv)
                    byte-compile--lexical-environment)
diff --git a/lisp/emacs-lisp/cconv.el b/lisp/emacs-lisp/cconv.el
index 23d0f12194..f3431db415 100644
--- a/lisp/emacs-lisp/cconv.el
+++ b/lisp/emacs-lisp/cconv.el
@@ -64,20 +64,12 @@
 ;;
 ;;; Code:
 
-;; PROBLEM cases found during conversion to lexical binding.
-;; We should try and detect and warn about those cases, even
-;; for lexical-binding==nil to help prepare the migration.
-;; - Uses of run-hooks, and friends.
-;; - Cases where we want to apply the same code to different vars depending on
-;;   some test.  These sometimes use a (let ((foo (if bar 'a 'b)))
-;;   ... (symbol-value foo) ... (set foo ...)).
-
 ;; TODO: (not just for cconv but also for the lexbind changes in general)
 ;; - let (e)debug find the value of lexical variables from the stack.
 ;; - make eval-region do the eval-sexp-add-defvars dance.
 ;; - byte-optimize-form should be applied before cconv.
 ;;   OTOH, the warnings emitted by cconv-analyze need to come before optimize
-;;   since afterwards they can because obnoxious (warnings about an "unused
+;;   since afterwards they can become obnoxious (warnings about an "unused
 ;;   variable" should not be emitted when the variable use has simply been
 ;;   optimized away).
 ;; - let macros specify that some let-bindings come from the same source,
@@ -87,33 +79,9 @@
 ;; - canonize code in macro-expand so we don't have to handle (let (var) body)
 ;;   and other oddities.
 ;; - new byte codes for unwind-protect so that closures aren't needed at all.
-;; - a reference to a var that is known statically to always hold a constant
-;;   should be turned into a byte-constant rather than a byte-stack-ref.
-;;   Hmm... right, that's called constant propagation and could be done here,
-;;   but when that constant is a function, we have to be careful to make sure
-;;   the bytecomp only compiles it once.
 ;; - Since we know here when a variable is not mutated, we could pass that
 ;;   info to the byte-compiler, e.g. by using a new `immutable-let'.
 ;; - call known non-escaping functions with `goto' rather than `call'.
-;; - optimize mapc to a dolist loop.
-
-;; (defmacro dlet (binders &rest body)
-;;   ;; Works in both lexical and non-lexical mode.
-;;   (declare (indent 1) (debug let))
-;;   `(progn
-;;      ,@(mapcar (lambda (binder)
-;;                  `(defvar ,(if (consp binder) (car binder) binder)))
-;;                binders)
-;;      (let ,binders ,@body)))
-
-;; (defmacro llet (binders &rest body)
-;;   ;; Only works in lexical-binding mode.
-;;   `(funcall
-;;     (lambda ,(mapcar (lambda (binder) (if (consp binder) (car binder) 
binder))
-;;                 binders)
-;;       ,@body)
-;;     ,@(mapcar (lambda (binder) (if (consp binder) (cadr binder)))
-;;               binders)))
 
 (eval-when-compile (require 'cl-lib))
 
@@ -142,13 +110,19 @@ is less than this number.")
   ;; interactive forms.
   (make-hash-table :test #'eq :weakness 'key))
 
+(defvar cconv--dynbound-variables nil
+  "List of variables known to be dynamically bound.")
+
 ;;;###autoload
-(defun cconv-closure-convert (form)
+(defun cconv-closure-convert (form &optional dynbound-vars)
   "Main entry point for closure conversion.
 FORM is a piece of Elisp code after macroexpansion.
+DYNBOUND-VARS is a list of symbols that should be considered as
+using dynamic scoping.
 
 Returns a form where all lambdas don't have any free variables."
-  (let ((cconv-freevars-alist '())
+  (let ((cconv--dynbound-variables dynbound-vars)
+       (cconv-freevars-alist '())
        (cconv-var-classification '()))
     ;; Analyze form - fill these variables with new information.
     (cconv-analyze-form form '())
@@ -156,8 +130,6 @@ Returns a form where all lambdas don't have any free 
variables."
     (prog1 (cconv-convert form nil nil) ; Env initially empty.
       (cl-assert (null cconv-freevars-alist)))))
 
-(defconst cconv--dummy-var (make-symbol "ignored"))
-
 (defun cconv--set-diff (s1 s2)
   "Return elements of set S1 that are not in set S2."
   (let ((res '()))
@@ -262,9 +234,7 @@ Returns a form where all lambdas don't have any free 
variables."
               ;; it is often non-trivial for the programmer to avoid such
               ;; unused vars.
               (not (intern-soft var))
-              (eq ?_ (aref (symbol-name var) 0))
-             ;; As a special exception, ignore "ignored".
-             (eq var 'ignored))
+              (eq ?_ (aref (symbol-name var) 0)))
        (let ((suggestions (help-uni-confusable-suggestions (symbol-name var))))
          (format "Unused lexical %s `%S'%s"
                  varkind (bare-symbol var)
@@ -342,7 +312,7 @@ EXTEND is a list of variables which might need to be 
accessed even from places
 where they are shadowed, because some part of ENV causes them to be used at
 places where they originally did not directly appear."
   (cl-assert (not (delq nil (mapcar (lambda (mapping)
-                                      (if (eq (cadr mapping) 'apply-partially)
+                                      (if (eq (cadr mapping) #'apply-partially)
                                           (cconv--set-diff (cdr (cddr mapping))
                                                            extend)))
                                     env))))
@@ -634,6 +604,12 @@ places where they originally did not directly appear."
 
 (defvar byte-compile-lexical-variables)
 
+(defun cconv--not-lexical-var-p (var dynbounds)
+  (or (not lexical-binding)
+      (not (symbolp var))
+      (special-variable-p var)
+      (memq var dynbounds)))
+
 (defun cconv--analyze-use (vardata form varkind)
   "Analyze the use of a variable.
 VARDATA should be (BINDER READ MUTATED CAPTURED CALLED).
@@ -677,7 +653,7 @@ FORM is the parent form that binds this var."
          ;; outside of it.
          (envcopy
           (mapcar (lambda (vdata) (list (car vdata) nil nil nil nil)) env))
-         (byte-compile-bound-variables byte-compile-bound-variables)
+         (cconv--dynbound-variables cconv--dynbound-variables)
          (newenv envcopy))
     ;; Push it before recursing, so cconv-freevars-alist contains entries in
     ;; the order they'll be used by closure-convert-rec.
@@ -685,7 +661,7 @@ FORM is the parent form that binds this var."
     (when lexical-binding
       (dolist (arg args)
         (cond
-         ((byte-compile-not-lexical-var-p arg)
+         ((cconv--not-lexical-var-p arg cconv--dynbound-variables)
           (byte-compile-warn-x
            arg
            "Lexical argument shadows the dynamic variable %S"
@@ -715,6 +691,8 @@ FORM is the parent form that binds this var."
           (setf (nth 3 (car env)) t))
         (setq env (cdr env) envcopy (cdr envcopy))))))
 
+(defvar cconv--dynbindings)
+
 (defun cconv-analyze-form (form env)
   "Find mutated variables and variables captured by closure.
 Analyze lambdas if they are suitable for lambda lifting.
@@ -730,7 +708,7 @@ This function does not return anything but instead fills the
      (let ((orig-env env)
            (newvars nil)
            (var nil)
-           (byte-compile-bound-variables byte-compile-bound-variables)
+           (cconv--dynbound-variables cconv--dynbound-variables)
            (value nil))
        (dolist (binder binders)
          (if (not (consp binder))
@@ -743,7 +721,9 @@ This function does not return anything but instead fills the
 
            (cconv-analyze-form value (if (eq letsym 'let*) env orig-env)))
 
-         (unless (or (byte-compile-not-lexical-var-p var) (not 
lexical-binding))
+         (if (cconv--not-lexical-var-p var cconv--dynbound-variables)
+             (when (boundp 'cconv--dynbindings)
+               (push var cconv--dynbindings))
            (cl-pushnew var byte-compile-lexical-variables)
            (let ((varstruct (list var nil nil nil nil)))
              (push (cons binder (cdr varstruct)) newvars)
@@ -797,7 +777,8 @@ This function does not return anything but instead fills the
      (cconv-analyze-form protected-form env)
      (unless lexical-binding
        (setq var nil))
-     (when (and var (symbolp var) (byte-compile-not-lexical-var-p var))
+     (when (and var (symbolp var)
+                (cconv--not-lexical-var-p var cconv--dynbound-variables))
        (byte-compile-warn-x
         var "Lexical variable shadows the dynamic variable %S" var))
      (let* ((varstruct (list var nil nil nil nil)))
@@ -813,9 +794,9 @@ This function does not return anything but instead fills the
      (cconv-analyze-form form env)
      (cconv--analyze-function () body env form))
 
-    (`(defvar ,var) (push var byte-compile-bound-variables))
+    (`(defvar ,var) (push var cconv--dynbound-variables))
     (`(,(or 'defconst 'defvar) ,var ,value . ,_)
-     (push var byte-compile-bound-variables)
+     (push var cconv--dynbound-variables)
      (cconv-analyze-form value env))
 
     (`(,(or 'funcall 'apply) ,fun . ,args)
@@ -847,5 +828,78 @@ This function does not return anything but instead fills 
the
          (setf (nth 1 dv) t))))))
 (define-obsolete-function-alias 'cconv-analyse-form #'cconv-analyze-form 
"25.1")
 
+(defun cconv-fv (form lexvars dynvars)
+  "Return the list of free variables in FORM.
+LEXVARS is the list of statically scoped vars in the context
+and DYNVARS is the list of dynamically scoped vars in the context.
+Returns a pair (LEXV . DYNV) of those vars actually used by FORM."
+  (let* ((fun
+          ;; Wrap FORM into a function because the analysis code we
+          ;; have only computes freevars for functions.
+          ;; In practice FORM is always already of the form
+          ;; #'(lambda ...), so optimize for this case.
+          (if (and (eq 'function (car-safe form))
+                   (eq 'lambda (car-safe (cadr form)))
+                   ;; To get correct results, FUN needs to be a "simple lambda"
+                   ;; without nested forms that aren't part of the body.  :-(
+                   (not (assq 'interactive (cadr form)))
+                   (not (assq ':documentation (cadr form))))
+              form
+            `#'(lambda () ,form)))
+         (analysis-env (mapcar (lambda (v) (list v nil nil nil nil)) lexvars))
+         (cconv--dynbound-variables dynvars)
+         (byte-compile-lexical-variables nil)
+         (cconv--dynbindings nil)
+         (cconv-freevars-alist '())
+        (cconv-var-classification '()))
+    (let* ((body (cddr (cadr fun))))
+      ;; Analyze form - fill these variables with new information.
+      (cconv-analyze-form fun analysis-env)
+      (setq cconv-freevars-alist (nreverse cconv-freevars-alist))
+      (unless (equal (if (eq :documentation (car-safe (car body)))
+                            (cdr body) body)
+                     (caar cconv-freevars-alist))
+        (message "BOOH!\n%S\n%S"
+                 body (caar cconv-freevars-alist)))
+      (cl-assert (equal (if (eq :documentation (car-safe (car body)))
+                            (cdr body) body)
+                        (caar cconv-freevars-alist)))
+      (let ((fvs (nreverse (cdar cconv-freevars-alist)))
+            (dyns (delq nil (mapcar (lambda (var) (car (memq var dynvars)))
+                                    (delete-dups cconv--dynbindings)))))
+        (cons fvs dyns)))))
+
+(defun cconv-make-interpreted-closure (fun env)
+  (cl-assert (eq (car-safe fun) 'lambda))
+  (let ((lexvars (delq nil (mapcar #'car-safe env))))
+    (if (null lexvars)
+        ;; The lexical environment is empty, so there's no need to
+        ;; look for free variables.
+        `(closure ,env . ,(cdr fun))
+      ;; We could try and cache the result of the macroexpansion and
+      ;; `cconv-fv' analysis.  Not sure it's worth the trouble.
+      (let* ((form `#',fun)
+             (expanded-form
+              (let ((lexical-binding t) ;; Tell macros which dialect is in use.
+                   ;; Make the macro aware of any defvar declarations in scope.
+                    (macroexp--dynvars
+                     (if macroexp--dynvars
+                         (append env macroexp--dynvars) env)))
+                (macroexpand-all form macroexpand-all-environment)))
+             ;; Since we macroexpanded the body, we may as well use that.
+             (expanded-fun-cdr
+              (pcase expanded-form
+                (`#'(lambda . ,cdr) cdr)
+                (_ (cdr fun))))
+         
+             (dynvars (delq nil (mapcar (lambda (b) (if (symbolp b) b)) env)))
+             (fvs (cconv-fv expanded-form lexvars dynvars))
+             (newenv (nconc (mapcar (lambda (fv) (assq fv env)) (car fvs))
+                            (cdr fvs))))
+        ;; Never return a nil env, since nil means to use the dynbind
+        ;; dialect of ELisp.
+        `(closure ,(or newenv '(t)) . ,expanded-fun-cdr)))))
+
+
 (provide 'cconv)
 ;;; cconv.el ends here
diff --git a/lisp/emacs-lisp/cl-extra.el b/lisp/emacs-lisp/cl-extra.el
index 7c7f027d77..66b214554e 100644
--- a/lisp/emacs-lisp/cl-extra.el
+++ b/lisp/emacs-lisp/cl-extra.el
@@ -615,12 +615,12 @@ PROPLIST is a list of the sort returned by `symbol-plist'.
                                  ,(funcall setter
                                            `(cl--set-getf ,getter ,k ,val))
                                  ,val)))))))))
-  (let ((val-tail (cdr-safe (plist-member plist tag))))
+  (let ((val-tail (cdr (plist-member plist tag))))
     (if val-tail (car val-tail) def)))
 
 ;;;###autoload
 (defun cl--set-getf (plist tag val)
-  (let ((val-tail (cdr-safe (plist-member plist tag))))
+  (let ((val-tail (cdr (plist-member plist tag))))
     (if val-tail (progn (setcar val-tail val) plist)
       (cl-list* tag val plist))))
 
diff --git a/lisp/emacs-lisp/cl-generic.el b/lisp/emacs-lisp/cl-generic.el
index b3ade3b894..7b6d43e572 100644
--- a/lisp/emacs-lisp/cl-generic.el
+++ b/lisp/emacs-lisp/cl-generic.el
@@ -650,13 +650,17 @@ The set of acceptable TYPEs (also called 
\"specializers\") is defined
                                      (cl--generic-name generic)
                                      qualifiers specializers))
                   current-load-list :test #'equal)
-      (let (;; Prevent `defalias' from recording this as the definition site of
+      (let ((old-adv-cc (get-advertised-calling-convention
+                         (symbol-function sym)))
+            ;; Prevent `defalias' from recording this as the definition site of
             ;; the generic function.
             current-load-list
             ;; BEWARE!  Don't purify this function definition, since that leads
             ;; to memory corruption if the hash-tables it holds are modified
             ;; (the GC doesn't trace those pointers).
             (purify-flag nil))
+        (when (listp old-adv-cc)
+          (set-advertised-calling-convention gfun old-adv-cc nil))
         ;; But do use `defalias', so that it interacts properly with nadvice,
         ;; e.g. for tracing/debug-on-entry.
         (defalias sym gfun)))))
diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el
index beafee1d63..43a2ed9205 100644
--- a/lisp/emacs-lisp/cl-macs.el
+++ b/lisp/emacs-lisp/cl-macs.el
@@ -656,6 +656,8 @@ its argument list allows full Common Lisp conventions."
                   (check `(while ,var
                              (cond
                               ((memq (car ,var) ',(append keys allow))
+                               (unless (cdr ,var)
+                                 (error "Missing argument for %s" (car ,var)))
                                (setq ,var (cdr (cdr ,var))))
                               ((car (cdr (memq (quote ,@allow) ,restarg)))
                                (setq ,var nil))
diff --git a/lisp/emacs-lisp/comp-cstr.el b/lisp/emacs-lisp/comp-cstr.el
index 8cff06a383..1338ae6e13 100644
--- a/lisp/emacs-lisp/comp-cstr.el
+++ b/lisp/emacs-lisp/comp-cstr.el
@@ -96,7 +96,7 @@ Integer values are handled in the `range' slot.")
 `comp-common-supertype'.")
   (subtype-p-mem (make-hash-table :test #'equal) :type hash-table
                  :documentation "Serve memoization for
-`comp-subtype-p-mem'.")
+`comp-cstr-ctxt-subtype-p-mem'.")
   (union-1-mem-no-range (make-hash-table :test #'equal) :type hash-table
                         :documentation "Serve memoization for
 `comp-cstr-union-1'.")
diff --git a/lisp/emacs-lisp/comp.el b/lisp/emacs-lisp/comp.el
index c0e4d9bdeb..3c7b86a612 100644
--- a/lisp/emacs-lisp/comp.el
+++ b/lisp/emacs-lisp/comp.el
@@ -57,7 +57,7 @@
   :safe #'integerp
   :version "28.1")
 
-(defcustom native-comp-debug (if (eq 'windows-nt system-type) 1 0)
+(defcustom native-comp-debug  0
   "Debug level for native compilation, a number between 0 and 3.
 This is intended for debugging the compiler itself.
   0 no debug output.
@@ -67,7 +67,7 @@ This is intended for debugging the compiler itself.
   passes and libgccjit log file."
   :type 'natnum
   :safe #'natnump
-  :version "28.1")
+  :version "29.1")
 
 (defcustom native-comp-verbose 0
   "Compiler verbosity for native compilation, a number between 0 and 3.
@@ -694,6 +694,9 @@ Useful to hook into pass checkers.")
   'native-compiler-error)
 
 
+(defvar comp-no-spawn nil
+  "Non-nil don't spawn native compilation processes.")
+
 ;; Moved early to avoid circularity when comp.el is loaded and
 ;; `macroexpand' needs to be advised (bug#47049).
 ;;;###autoload
@@ -703,12 +706,9 @@ Useful to hook into pass checkers.")
               (memq subr-name native-comp-never-optimize-functions)
               (gethash subr-name comp-installed-trampolines-h))
     (cl-assert (subr-primitive-p (symbol-function subr-name)))
-    (comp--install-trampoline
-     subr-name
-     (or (comp-trampoline-search subr-name)
-         (comp-trampoline-compile subr-name)
-         ;; Should never happen.
-         (cl-assert nil)))))
+    (when-let ((trampoline (or (comp-trampoline-search subr-name)
+                               (comp-trampoline-compile subr-name))))
+        (comp--install-trampoline subr-name trampoline))))
 
 
 (cl-defstruct (comp-vec (:copier nil))
@@ -2068,9 +2068,10 @@ and the annotation emission."
   "Lexically-scoped FUNCTION."
   (let ((args (comp-func-l-args function)))
     (cons (make-comp-mvar :constant (comp-args-base-min args))
-          (make-comp-mvar :constant (if (comp-args-p args)
-                                        (comp-args-max args)
-                                      'many)))))
+          (make-comp-mvar :constant (cond
+                                     ((comp-args-p args) (comp-args-max args))
+                                     ((comp-nargs-rest args) 'many)
+                                     (t (comp-nargs-nonrest args)))))))
 
 (cl-defmethod comp-prepare-args-for-top-level ((function comp-func-d))
   "Dynamically scoped FUNCTION."
@@ -3735,7 +3736,8 @@ Prepare every function for final compilation and drive 
the C back-end."
               (if (zerop
                    (call-process (expand-file-name invocation-name
                                                    invocation-directory)
-                                nil t t "--batch" "-l" temp-file))
+                                nil t t "-no-comp-spawn" "--batch" "-l"
+                                 temp-file))
                   (progn
                     (delete-file temp-file)
                     output)
@@ -3950,6 +3952,7 @@ processes from `comp-async-compilations'"
   "Start compiling files from `comp-files-queue' asynchronously.
 When compilation is finished, run `native-comp-async-all-done-hook' and
 display a message."
+  (cl-assert (null comp-no-spawn))
   (if (or comp-files-queue
           (> (comp-async-runnings) 0))
       (unless (>= (comp-async-runnings) (comp-effective-async-max-jobs))
@@ -3968,8 +3971,8 @@ display a message."
                     (file-newer-than-file-p
                      source-file (comp-el-to-eln-filename source-file))))
          do (let* ((expr `((require 'comp)
-                           (setq comp-async-compilation t)
-                           (setq warning-fill-column most-positive-fixnum)
+                           (setq comp-async-compilation t
+                                 warning-fill-column most-positive-fixnum)
                            ,(let ((set (list 'setq)))
                               (dolist (var '(comp-file-preloaded-p
                                              native-comp-compile-static-data
@@ -4026,7 +4029,8 @@ display a message."
                              :command (list
                                        (expand-file-name invocation-name
                                                          invocation-directory)
-                                       "--batch" "-l" temp-file)
+                                       "-no-comp-spawn" "--batch" "-l"
+                                       temp-file)
                              :sentinel
                              (lambda (process _event)
                                (run-hook-with-args
@@ -4070,72 +4074,73 @@ the deferred compilation mechanism."
               (stringp function-or-file))
     (signal 'native-compiler-error
             (list "Not a function symbol or file" function-or-file)))
-  (catch 'no-native-compile
-    (let* ((print-symbols-bare t)
-           (data function-or-file)
-           (comp-native-compiling t)
-           (byte-native-qualities nil)
-           (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
-                                      :with-late-load with-late-load)))
-      (comp-log "\n\n" 1)
-      (unwind-protect
-          (progn
-            (condition-case err
-                (cl-loop
-                 with report = nil
-                 for t0 = (current-time)
-                 for pass in comp-passes
-                 unless (memq pass comp-disabled-passes)
-                 do
-                 (comp-log (format "(%s) Running pass %s:\n"
-                                   function-or-file pass)
-                           2)
-                 (setf data (funcall pass data))
-                 (push (cons pass (float-time (time-since t0))) report)
-                 (cl-loop for f in (alist-get pass comp-post-pass-hooks)
-                          do (funcall f data))
-                 finally
-                 (when comp-log-time-report
-                   (comp-log (format "Done compiling %s" data) 0)
-                   (cl-loop for (pass . time) in (reverse report)
-                            do (comp-log (format "Pass %s took: %fs."
-                                                 pass time) 0))))
-              (native-compiler-skip)
-              (t
-               (let ((err-val (cdr err)))
-                 ;; If we are doing an async native compilation print the
-                 ;; error in the correct format so is parsable and abort.
-                 (if (and comp-async-compilation
-                          (not (eq (car err) 'native-compiler-error)))
-                     (progn
-                       (message (if err-val
-                                    "%s: Error: %s %s"
-                                  "%s: Error %s")
-                                function-or-file
-                                (get (car err) 'error-message)
-                                (car-safe err-val))
-                       (kill-emacs -1))
-                   ;; Otherwise re-signal it adding the compilation input.
-                  (signal (car err) (if (consp err-val)
-                                        (cons function-or-file err-val)
-                                      (list function-or-file err-val)))))))
-            (if (stringp function-or-file)
-                data
-              ;; So we return the compiled function.
-              (native-elisp-load data)))
-        ;; We may have created a temporary file when we're being
-        ;; called with something other than a file as the argument.
-        ;; Delete it.
-        (when (and (not (stringp function-or-file))
-                   (not output)
-                   comp-ctxt
-                   (comp-ctxt-output comp-ctxt)
-                   (file-exists-p (comp-ctxt-output comp-ctxt)))
-          (message "Deleting %s" (comp-ctxt-output comp-ctxt))
-          (delete-file (comp-ctxt-output comp-ctxt)))))))
+  (when (or (null comp-no-spawn) comp-async-compilation)
+    (catch 'no-native-compile
+      (let* ((print-symbols-bare t)
+             (data function-or-file)
+             (comp-native-compiling t)
+             (byte-native-qualities nil)
+             (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
+                                        :with-late-load with-late-load)))
+        (comp-log "\n\n" 1)
+        (unwind-protect
+            (progn
+              (condition-case err
+                  (cl-loop
+                   with report = nil
+                   for t0 = (current-time)
+                   for pass in comp-passes
+                   unless (memq pass comp-disabled-passes)
+                   do
+                   (comp-log (format "(%s) Running pass %s:\n"
+                                     function-or-file pass)
+                             2)
+                   (setf data (funcall pass data))
+                   (push (cons pass (float-time (time-since t0))) report)
+                   (cl-loop for f in (alist-get pass comp-post-pass-hooks)
+                            do (funcall f data))
+                   finally
+                   (when comp-log-time-report
+                     (comp-log (format "Done compiling %s" data) 0)
+                     (cl-loop for (pass . time) in (reverse report)
+                              do (comp-log (format "Pass %s took: %fs."
+                                                   pass time) 0))))
+                (native-compiler-skip)
+                (t
+                 (let ((err-val (cdr err)))
+                   ;; If we are doing an async native compilation print the
+                   ;; error in the correct format so is parsable and abort.
+                   (if (and comp-async-compilation
+                            (not (eq (car err) 'native-compiler-error)))
+                       (progn
+                         (message (if err-val
+                                      "%s: Error: %s %s"
+                                    "%s: Error %s")
+                                  function-or-file
+                                  (get (car err) 'error-message)
+                                  (car-safe err-val))
+                         (kill-emacs -1))
+                     ;; Otherwise re-signal it adding the compilation input.
+                    (signal (car err) (if (consp err-val)
+                                          (cons function-or-file err-val)
+                                        (list function-or-file err-val)))))))
+              (if (stringp function-or-file)
+                  data
+                ;; So we return the compiled function.
+                (native-elisp-load data)))
+          ;; We may have created a temporary file when we're being
+          ;; called with something other than a file as the argument.
+          ;; Delete it.
+          (when (and (not (stringp function-or-file))
+                     (not output)
+                     comp-ctxt
+                     (comp-ctxt-output comp-ctxt)
+                     (file-exists-p (comp-ctxt-output comp-ctxt)))
+            (message "Deleting %s" (comp-ctxt-output comp-ctxt))
+            (delete-file (comp-ctxt-output comp-ctxt))))))))
 
 (defun native-compile-async-skip-p (file load selector)
   "Return non-nil if FILE's compilation should be skipped.
@@ -4143,6 +4148,7 @@ the deferred compilation mechanism."
 LOAD and SELECTOR work as described in `native--compile-async'."
   ;; Make sure we are not already compiling `file' (bug#40838).
   (or (gethash file comp-async-compilations)
+      (gethash (file-name-with-extension file "elc") comp--no-native-compile)
       (cond
        ((null selector) nil)
        ((functionp selector) (not (funcall selector file)))
@@ -4190,7 +4196,8 @@ bytecode definition was not changed in the meantime)."
     (error "LOAD must be nil, t or 'late"))
   (unless (listp files)
     (setf files (list files)))
-  (let (file-list)
+  (let ((added-something nil)
+        file-list)
     (dolist (file-or-dir files)
       (cond ((file-directory-p file-or-dir)
              (dolist (file (if recursively
@@ -4218,11 +4225,15 @@ bytecode definition was not changed in the meantime)."
               (make-directory out-dir t))
             (if (file-writable-p out-filename)
                 (setf comp-files-queue
-                      (append comp-files-queue `((,file . ,load))))
+                      (append comp-files-queue `((,file . ,load)))
+                      added-something t)
               (display-warning 'comp
                                (format "No write access for %s skipping."
                                        out-filename)))))))
-    (when (zerop (comp-async-runnings))
+    ;; Perhaps nothing passed `native-compile-async-skip-p'?
+    (when (and added-something
+               ;; Don't start if there's one already running.
+               (zerop (comp-async-runnings)))
       (comp-run-async-workers))))
 
 
@@ -4258,14 +4269,13 @@ Search happens in `native-comp-eln-load-path'."
 (defun native-compile (function-or-file &optional output)
   "Compile FUNCTION-OR-FILE into native code.
 This is the synchronous entry-point for the Emacs Lisp native
-compiler.
-FUNCTION-OR-FILE is a function symbol, a form, or the filename of
-an Emacs Lisp source file.
-If OUTPUT is non-nil, use it as the filename for the compiled
-object.
-If FUNCTION-OR-FILE is a filename, return the filename of the
-compiled object.  If FUNCTION-OR-FILE is a function symbol or a
-form, return the compiled function."
+compiler.  FUNCTION-OR-FILE is a function symbol, a form, or the
+filename of an Emacs Lisp source file.  If OUTPUT is non-nil, use
+it as the filename for the compiled object.  If FUNCTION-OR-FILE
+is a filename, if the compilation was successful return the
+filename of the compiled object.  If FUNCTION-OR-FILE is a
+function symbol or a form, if the compilation was successful
+return the compiled function."
   (comp--native-compile function-or-file nil output))
 
 ;;;###autoload
@@ -4352,13 +4362,15 @@ of (commands) to run simultaneously."
     ;; `invocation-directory'.
     (setq dir (expand-file-name dir invocation-directory))
     (when (file-exists-p dir)
-      (dolist (subdir (directory-files dir t))
+      (dolist (subdir (seq-filter
+                       (lambda (f) (not (string-match (rx "/." (? ".") eos) 
f)))
+                       (directory-files dir t)))
         (when (and (file-directory-p subdir)
                    (file-writable-p subdir)
                    (not (equal (file-name-nondirectory
                                 (directory-file-name subdir))
                                comp-native-version-dir)))
-          (message "Deleting %s..." subdir)
+          (message "Deleting `%s'..." subdir)
           ;; We're being overly cautious here -- there shouldn't be
           ;; anything but .eln files in these directories.
           (dolist (eln (directory-files subdir t "\\.eln\\(\\.tmp\\)?\\'"))
diff --git a/lisp/emacs-lisp/crm.el b/lisp/emacs-lisp/crm.el
index 6d4b29b552..8e61797315 100644
--- a/lisp/emacs-lisp/crm.el
+++ b/lisp/emacs-lisp/crm.el
@@ -201,7 +201,7 @@ This function is modeled after 
`minibuffer-complete-and-exit'."
     (if doexit (exit-minibuffer))))
 
 (defun crm--choose-completion-string (choice buffer base-position
-                                             &rest ignored)
+                                             &rest _ignored)
   "Completion string chooser for `completing-read-multiple'.
 This is called from `choose-completion-string-functions'.
 It replaces the string that is currently being completed, without
diff --git a/lisp/emacs-lisp/eldoc.el b/lisp/emacs-lisp/eldoc.el
index 6fd89a690d..e1801c45b7 100644
--- a/lisp/emacs-lisp/eldoc.el
+++ b/lisp/emacs-lisp/eldoc.el
@@ -55,21 +55,24 @@
   :group 'extensions)
 
 (defcustom eldoc-idle-delay 0.50
-  "Number of seconds of idle time to wait before printing.
+  "Number of seconds of idle time to wait before displaying documentation.
 If user input arrives before this interval of time has elapsed after the
-last input, no documentation will be printed.
+last input event, no documentation will be displayed.
 
-If this variable is set to 0, no idle time is required."
+If this variable is set to 0, display the documentation without any delay."
   :type 'number)
 
 (defcustom eldoc-print-after-edit nil
-  "If non-nil, eldoc info is only shown when editing.
+  "If non-nil, eldoc info is only shown after editing commands.
 Changing the value requires toggling `eldoc-mode'."
   :type 'boolean)
 
 (defcustom eldoc-echo-area-display-truncation-message t
   "If non-nil, provide verbose help when a message has been truncated.
-If nil, truncated messages will just have \"...\" appended."
+When this is non-nil, and the documentation string was truncated to
+fit in the echo-area, the documentation will be followed by an
+explanation of how to display the full documentation text.
+If nil, truncated messages will just have \"...\" to indicate truncation."
   :type 'boolean
   :version "28.1")
 
@@ -93,22 +96,24 @@ Note that this variable has no effect, unless
 
 (defcustom eldoc-echo-area-use-multiline-p 'truncate-sym-name-if-fit
   "Allow long ElDoc doc strings to resize echo area display.
-If value is t, never attempt to truncate messages, even if the
-echo area must be resized to fit.
+If the value is t, never attempt to truncate messages, even if the
+echo area must be resized to fit.  In that case, Emacs will resize
+the mini-window up to the limit set by `max-mini-window-height'.
 
 If the value is a positive number, it is used to calculate a
-number of logical lines of documentation that ElDoc is allowed to
-put in the echo area.  If a positive integer, the number is used
-directly, while a float specifies the number of lines as a
-proportion of the echo area frame's height.
+number of screen lines of documentation that ElDoc is allowed to
+put in the echo area.  A positive integer specifies the maximum
+number of lines directly, while a floating-point number specifies
+the number of screen lines as a fraction of the echo area frame's
+height.
 
-If value is the symbol `truncate-sym-name-if-fit', the part of
+If the value is the symbol `truncate-sym-name-if-fit', the part of
 the doc string that represents a symbol's name may be truncated
 if it will enable the rest of the doc string to fit on a single
 line, without resizing the echo area.
 
-If value is nil, a doc string is always truncated to fit in a
-single line of display in the echo area.
+If the value is nil, a doc string is always truncated to fit in a
+single screen line of echo-area display.
 
 Any resizing of the echo area additionally respects
 `max-mini-window-height'."
@@ -121,12 +126,12 @@ Any resizing of the echo area additionally respects
  line" truncate-sym-name-if-fit)))
 
 (defcustom eldoc-echo-area-prefer-doc-buffer nil
-  "Prefer ElDoc's documentation buffer if it is showing in some frame.
+  "Prefer ElDoc's documentation buffer if it is displayed in some window.
 If this variable's value is t, ElDoc will skip showing
 documentation in the echo area if the dedicated documentation
-buffer (given by `eldoc-doc-buffer') is being displayed in some
-window.  If the value is the symbol `maybe', then the echo area
-is only skipped if the documentation doesn't fit there."
+buffer (displayed by `eldoc-doc-buffer') is already displayed in
+some window.  If the value is the symbol `maybe', then the echo area
+is only skipped if the documentation needs to be truncated there."
   :type 'boolean)
 
 (defface eldoc-highlight-function-argument
@@ -287,8 +292,10 @@ reflect the change."
 (put 'eldoc-mode-line-string 'risky-local-variable t)
 
 (defun eldoc-minibuffer-message (format-string &rest args)
-  "Display messages in the mode-line when in the minibuffer.
-Otherwise work like `message'."
+  "Display message specified by FORMAT-STRING and ARGS on the mode-line as 
needed.
+This function displays the message produced by formatting ARGS
+with FORMAT-STRING on the mode line when the current buffer is a minibuffer.
+Otherwise, it displays the message like `message' would."
   (if (minibufferp)
       (progn
        (add-hook 'minibuffer-exit-hook
@@ -632,8 +639,8 @@ If INTERACTIVE is t, also display the buffer."
   (when interactive (eldoc-doc-buffer t)))
 
 (defun eldoc-documentation-default ()
-  "Show first doc string for item at point.
-Default value for `eldoc-documentation-strategy'."
+  "Show the first non-nil documentation string for item at point.
+This is the default value for `eldoc-documentation-strategy'."
   (run-hook-with-args-until-success 'eldoc-documentation-functions
                                     (eldoc--make-callback :patient)))
 
@@ -651,18 +658,18 @@ else wait for all doc strings."
   t)
 
 (defun eldoc-documentation-compose ()
-  "Show multiple doc strings at once after waiting for all.
-Meant as a value for `eldoc-documentation-strategy'."
+  "Show multiple documentation strings together after waiting for all of them.
+This is meant to be used as a value for `eldoc-documentation-strategy'."
   (eldoc--documentation-compose-1 nil))
 
 (defun eldoc-documentation-compose-eagerly ()
-  "Show multiple doc strings at once as soon as possible.
-Meant as a value for `eldoc-documentation-strategy'."
+  "Show multiple documentation strings one by one as soon as possible.
+This is meant to be used as a value for `eldoc-documentation-strategy'."
   (eldoc--documentation-compose-1 t))
 
 (defun eldoc-documentation-enthusiast ()
-  "Show most important doc string produced so far.
-Meant as a value for `eldoc-documentation-strategy'."
+  "Show most important documentation string produced so far.
+This is meant to be used as a value for `eldoc-documentation-strategy'."
   (run-hook-wrapped 'eldoc-documentation-functions
                     (lambda (f)
                       (let* ((callback (eldoc--make-callback :enthusiast))
@@ -692,40 +699,42 @@ Meant as a value for `eldoc-documentation-strategy'."
 (eldoc--documentation-strategy-defcustom eldoc-documentation-strategy
     eldoc-documentation-function
   #'eldoc-documentation-default
-  "How to collect and organize results of `eldoc-documentation-functions'.
-
-This variable controls how `eldoc-documentation-functions', which
-specifies the sources of documentation, is queried and how its
-results are organized before being displayed to the user.  The
-following values are allowed:
-
-- `eldoc-documentation-default': calls functions in the special
-  hook in order until one is found that produces a doc string
-  value.  Display only that value;
-
-- `eldoc-documentation-compose': calls all functions in the
-  special hook and displays all of the resulting doc strings
-  together.  Wait for all strings to be ready, and preserve their
-  relative order as specified by the order of functions in the hook;
-
-- `eldoc-documentation-compose-eagerly': calls all functions in
-  the special hook and displays as many of the resulting doc
-  strings as possible, as soon as possible.  Preserves the
-  relative order of doc strings;
-
-- `eldoc-documentation-enthusiast': calls all functions in the
-  special hook and displays only the most important resulting
-  docstring one at any given time.  A function appearing first in
-  the special hook is considered more important.
-
-This variable can also be set to a function of no args that
-returns something other than a string or nil and allows for some
+  "How to collect and display results of `eldoc-documentation-functions'.
+
+This variable controls how to call the functions in the special hook
+`eldoc-documentation-functions', and how to organize their results
+for display to the user.  The functions in `eldoc-documentation-functions'
+are the source of documentation, and act as back-end for ElDoc.
+
+The following values are supported:
+
+- `eldoc-documentation-default': Call functions in the special
+  hook in order, until one of them returns a non-nil string
+  value.  Display only that string.
+
+- `eldoc-documentation-compose': Call all the functions in the
+  special hook and display all of the resulting strings together,
+  after all of the functions were called, and in the order of the
+  functions in the hook.
+
+- `eldoc-documentation-compose-eagerly': Call all the functions in
+  the special hook, and display each non-nil string as soon as it
+  is returned by a function, before calling the next function.
+
+- `eldoc-documentation-enthusiast': Call all the functions in the
+  special hook, and display only the most important resulting
+  string at any given time.  A function appearing first in
+  the special hook is considered more important than those which
+  appear after it.
+
+This variable can also be set to a function of no arguments that
+returns something other than a string or nil, and allows for some
 or all of the special hook `eldoc-documentation-functions' to be
 run.  In that case, the strategy function should follow that
-other variable's protocol closely and endeavor to display the
-resulting doc strings itself.
+other variable's protocol closely and display the resulting doc
+strings itself.
 
-For backward compatibility to the \"old\" protocol, this variable
+For backward compatibility with the \"old\" protocol, this variable
 can also be set to a function that returns nil or a doc string,
 depending whether or not there is documentation to display at
 all."
diff --git a/lisp/emacs-lisp/gv.el b/lisp/emacs-lisp/gv.el
index a96fa19a3f..11251d7a96 100644
--- a/lisp/emacs-lisp/gv.el
+++ b/lisp/emacs-lisp/gv.el
@@ -445,16 +445,17 @@ The return value is the last VAL in the list.
                             ,v))))))))))
 
 (gv-define-expander plist-get
-  (lambda (do plist prop)
+  (lambda (do plist prop &optional predicate)
     (macroexp-let2 macroexp-copyable-p key prop
       (gv-letplace (getter setter) plist
-        (macroexp-let2 nil p `(cdr (plist-member ,getter ,key))
+        (macroexp-let2 nil p `(cdr (plist-member ,getter ,key ,predicate))
           (funcall do
                    `(car ,p)
                    (lambda (val)
                      `(if ,p
                           (setcar ,p ,val)
-                        ,(funcall setter `(cons ,key (cons ,val 
,getter)))))))))))
+                        ,(funcall setter
+                                  `(cons ,key (cons ,val ,getter)))))))))))
 
 ;;; Some occasionally handy extensions.
 
diff --git a/lisp/emacs-lisp/hierarchy.el b/lisp/emacs-lisp/hierarchy.el
index 6c95d86b47..fb5d518b22 100644
--- a/lisp/emacs-lisp/hierarchy.el
+++ b/lisp/emacs-lisp/hierarchy.el
@@ -71,7 +71,8 @@
                (:conc-name hierarchy--))
   (roots (list)) ; list of the hierarchy roots (no parent)
   (parents (make-hash-table :test 'equal)) ; map an item to its parent
-  (children (make-hash-table :test 'equal)) ; map an item to its childre
+  (children (make-hash-table :test 'equal)) ; map an item to its children
+  (delaying-parents (make-hash-table :test 'equal)) ; map an item to its 
childrenfn
   ;; cache containing the set of all items in the hierarchy
   (seen-items (make-hash-table :test 'equal)))  ; map an item to t
 
@@ -133,7 +134,8 @@ keys are :key and :test."
   "Create a hierarchy and return it."
   (hierarchy--make))
 
-(defun hierarchy-add-tree (hierarchy item parentfn &optional childrenfn 
acceptfn)
+(defun hierarchy-add-tree (hierarchy item parentfn
+                                     &optional childrenfn acceptfn 
delay-children-p)
   "In HIERARCHY, add ITEM.
 
 PARENTFN is either nil or a function defining the child-to-parent
@@ -151,27 +153,39 @@ CHILDRENFN are expected to be coherent with each other.
 
 ACCEPTFN is a function returning non-nil if its parameter (any object)
 should be an item of the hierarchy.  By default, ACCEPTFN returns non-nil
-if its parameter is non-nil."
+if its parameter is non-nil.
+
+DELAY-CHILDREN-P is a predicate determining whether the children that would
+normally be processed by CHILDRENFN should, instead, have their processing be
+delayed and stored to be processed by CHILDRENFN when the child is selected
+during use of the hierarchy."
   (unless (hierarchy-has-item hierarchy item)
     (let ((acceptfn (or acceptfn #'identity)))
       (hierarchy--seen-items-add hierarchy item)
       (let ((parent (and parentfn (funcall parentfn item))))
         (when (funcall acceptfn parent)
           (hierarchy--add-relation hierarchy item parent acceptfn)
-          (hierarchy-add-tree hierarchy parent parentfn childrenfn)))
-      (let ((children (and childrenfn (funcall childrenfn item))))
-        (mapc (lambda (child)
-                (when (funcall acceptfn child)
-                  (hierarchy--add-relation hierarchy child item acceptfn)
-                  (hierarchy-add-tree hierarchy child parentfn childrenfn)))
-              children)))))
-
-(defun hierarchy-add-trees (hierarchy items parentfn &optional childrenfn 
acceptfn)
+          (hierarchy-add-tree hierarchy parent
+                              parentfn (if delay-children-p nil childrenfn))))
+      (if (and childrenfn delay-children-p)
+          (map-put! (hierarchy--delaying-parents hierarchy) item childrenfn)
+        (let ((children (and childrenfn (funcall childrenfn item))))
+          (map-put! (hierarchy--delaying-parents hierarchy) item nil)
+          (mapc (lambda (child)
+                  (when (funcall acceptfn child)
+                    (hierarchy--add-relation hierarchy child item acceptfn)
+                    (hierarchy-add-tree hierarchy child parentfn childrenfn)))
+                children))))))
+
+(defun hierarchy-add-trees (hierarchy items parentfn
+                                      &optional childrenfn acceptfn 
delay-children-p)
   "Call `hierarchy-add-tree' on HIERARCHY and each element of ITEMS.
 
-PARENTFN, CHILDRENFN and ACCEPTFN have the same meaning as in `hierarchy-add'."
+PARENTFN, CHILDRENFN, ACCEPTFN, and DELAY-CHILDREN-P have the same meaning as 
in
+`hierarchy-add'."
   (seq-map (lambda (item)
-             (hierarchy-add-tree hierarchy item parentfn childrenfn acceptfn))
+             (hierarchy-add-tree hierarchy item parentfn
+                                 childrenfn acceptfn delay-children-p))
            items))
 
 (defun hierarchy-add-list (hierarchy list &optional wrap childrenfn)
@@ -541,6 +555,30 @@ nil.  The buffer is returned."
     buffer))
 
 (declare-function widget-convert "wid-edit")
+(defun hierarchy--create-delayed-tree-widget (elem labelfn indent childrenfn)
+  "Return a list of tree-widgets for the children generated.
+
+ELEM is the element of the hierarchy passed from
+`hierarchy-convert-to-tree-widget'; it and the CHILDRENFN are used to generate
+the children of the element dynamically.
+
+LABELFN is the same function passed to `hierarchy-convert-to-tree-widget'.
+
+INDENT is the same function passed to `hierarchy-convert-to-tree-widget'.
+
+CHILDRENFN is the function used to discover the children of ELEM."
+  (lambda (_widget)
+    (mapcar
+     (lambda (item)
+       (widget-convert
+        'tree-widget
+        :tag (hierarchy-labelfn-to-string labelfn item indent)
+        :expander (hierarchy--create-delayed-tree-widget
+                   item
+                   labelfn
+                   (1+ indent)
+                   childrenfn)))
+     (funcall childrenfn elem))))
 (defun hierarchy-convert-to-tree-widget (hierarchy labelfn)
   "Return a tree-widget for HIERARCHY.
 
@@ -550,10 +588,21 @@ node label."
   (require 'wid-edit)
   (require 'tree-widget)
   (hierarchy-map-tree (lambda (item indent children)
-                        (widget-convert
-                         'tree-widget
-                         :tag (hierarchy-labelfn-to-string labelfn item indent)
-                         :args children))
+                        (let ((childrenfn (map-elt
+                                           (hierarchy--delaying-parents 
hierarchy)
+                                           item)))
+                          (apply
+                           #'widget-convert
+                           (list 'tree-widget
+                                 :tag (hierarchy-labelfn-to-string labelfn 
item indent)
+                                 (if childrenfn :expander :args)
+                                 (if childrenfn
+                                     (hierarchy--create-delayed-tree-widget
+                                      item
+                                      labelfn
+                                      (1+ indent)
+                                      childrenfn)
+                                   children)))))
                       hierarchy))
 
 (defun hierarchy-tree-display (hierarchy labelfn &optional buffer)
diff --git a/lisp/emacs-lisp/icons.el b/lisp/emacs-lisp/icons.el
index a08ac7463c..86c4483030 100644
--- a/lisp/emacs-lisp/icons.el
+++ b/lisp/emacs-lisp/icons.el
@@ -196,18 +196,21 @@ present if the icon is represented by an image."
          (image-supported-file-p file)
          (propertize
           " " 'display
-          (if-let ((height (plist-get keywords :height)))
-              (create-image file
-                            nil nil
-                            :height (if (eq height 'line)
+          (let ((props
+                 (append
+                  (if-let ((height (plist-get keywords :height)))
+                      (list :height (if (eq height 'line)
                                         (window-default-line-height)
-                                      height)
-                            :scale 1
-                            :rotation (or (plist-get keywords :rotation) 0)
-                            :ascent (if (plist-member keywords :ascent)
-                                        (plist-get keywords :ascent)
-                                      'center))
-            (create-image file))))))
+                                      height)))
+                  '(:scale 1)
+                  (if-let ((rotation (plist-get keywords :rotation)))
+                      (list :rotation rotation))
+                  (if-let ((margin (plist-get keywords :margin)))
+                      (list :margin margin))
+                  (list :ascent (if (plist-member keywords :ascent)
+                                    (plist-get keywords :ascent)
+                                  'center)))))
+            (apply 'create-image file nil nil props))))))
 
 (cl-defmethod icons--create ((_type (eql 'emoji)) icon _keywords)
   (when-let ((font (and (display-multi-font-p)
diff --git a/lisp/emacs-lisp/loaddefs-gen.el b/lisp/emacs-lisp/loaddefs-gen.el
index 964d23c770..ecc5f7e47b 100644
--- a/lisp/emacs-lisp/loaddefs-gen.el
+++ b/lisp/emacs-lisp/loaddefs-gen.el
@@ -283,6 +283,12 @@ expression, in which case we want to handle forms 
differently."
            ,@(when-let ((safe (plist-get props :safe)))
                `((put ',varname 'safe-local-variable ,safe))))))
 
+     ;; Extract theme properties.
+     ((eq car 'deftheme)
+      (let* ((name (car-safe (cdr-safe form)))
+            (props (nthcdr 3 form)))
+       `(put ',name 'theme-properties (list ,@props))))
+
      ((eq car 'defgroup)
       ;; In Emacs this is normally handled separately by cus-dep.el, but for
       ;; third party packages, it can be convenient to explicitly autoload
@@ -730,7 +736,14 @@ rules for built-in packages and excluded files."
      ;; updated.
      (file-newer-than-file-p
       (expand-file-name "emacs-lisp/loaddefs-gen.el" lisp-directory)
-      output-file))))
+      output-file)))
+  (let ((lisp-mode-autoload-regexp
+         "^;;;###\\(\\(noexist\\)-\\)?\\(theme-autoload\\)"))
+      (loaddefs-generate
+       (expand-file-name "../etc/themes/" lisp-directory)
+       (expand-file-name "theme-loaddefs.el" lisp-directory))))
+
+;;;###autoload (load "theme-loaddefs.el" t)
 
 (provide 'loaddefs-gen)
 
diff --git a/lisp/emacs-lisp/map.el b/lisp/emacs-lisp/map.el
index 8c67d7c7a2..8e3b698d37 100644
--- a/lisp/emacs-lisp/map.el
+++ b/lisp/emacs-lisp/map.el
@@ -5,7 +5,7 @@
 ;; Author: Nicolas Petton <nicolas@petton.fr>
 ;; Maintainer: emacs-devel@gnu.org
 ;; Keywords: extensions, lisp
-;; Version: 3.2.1
+;; Version: 3.3.1
 ;; Package-Requires: ((emacs "26"))
 
 ;; This file is part of GNU Emacs.
@@ -80,48 +80,82 @@ MAP can be an alist, plist, hash-table, or array."
   `(pcase-let ((,(map--make-pcase-patterns keys) ,map))
      ,@body))
 
-(eval-when-compile
-  (defmacro map--dispatch (map-var &rest args)
-    "Evaluate one of the forms specified by ARGS based on the type of MAP-VAR.
-
-The following keyword types are meaningful: `:list',
-`:hash-table' and `:array'.
-
-An error is thrown if MAP-VAR is neither a list, hash-table nor array.
-
-Returns the result of evaluating the form associated with MAP-VAR's type."
-    (declare (debug t) (indent 1))
-    `(cond ((listp ,map-var) ,(plist-get args :list))
-           ((hash-table-p ,map-var) ,(plist-get args :hash-table))
-           ((arrayp ,map-var) ,(plist-get args :array))
-           (t (error "Unsupported map type `%S': %S"
-                     (type-of ,map-var) ,map-var)))))
-
 (define-error 'map-not-inplace "Cannot modify map in-place")
 
 (defsubst map--plist-p (list)
+  "Return non-nil if LIST is the start of a nonempty plist map."
   (and (consp list) (atom (car list))))
 
+(defconst map--plist-has-predicate
+  (condition-case nil
+      (with-no-warnings (plist-get () nil #'eq) t)
+    (wrong-number-of-arguments))
+  "Non-nil means `plist-get' & co. accept a predicate in Emacs 29+.
+Note that support for this predicate in map.el is patchy and
+deprecated.")
+
+(defun map--plist-member-1 (plist prop &optional predicate)
+  "Compatibility shim for the PREDICATE argument of `plist-member'.
+Assumes non-nil PLIST satisfies `map--plist-p'."
+  (if (or (memq predicate '(nil eq)) (null plist))
+      (plist-member plist prop)
+    (let ((tail plist) found)
+      (while (and (not (setq found (funcall predicate (car tail) prop)))
+                  (consp (setq tail (cdr tail)))
+                  (consp (setq tail (cdr tail)))))
+      (and tail (not found)
+           (signal 'wrong-type-argument `(plistp ,plist)))
+      tail)))
+
+(defalias 'map--plist-member
+  (if map--plist-has-predicate #'plist-member #'map--plist-member-1)
+  "Compatibility shim for `plist-member' in Emacs 29+.
+\n(fn PLIST PROP &optional PREDICATE)")
+
+(defun map--plist-put-1 (plist prop val &optional predicate)
+  "Compatibility shim for the PREDICATE argument of `plist-put'.
+Assumes non-nil PLIST satisfies `map--plist-p'."
+  (if (or (memq predicate '(nil eq)) (null plist))
+      (plist-put plist prop val)
+    (let ((tail plist) prev found)
+      (while (and (consp (cdr tail))
+                  (not (setq found (funcall predicate (car tail) prop)))
+                  (consp (setq prev tail tail (cddr tail)))))
+      (cond (found (setcar (cdr tail) val))
+            (tail (signal 'wrong-type-argument `(plistp ,plist)))
+            (prev (setcdr (cdr prev) (cons prop (cons val (cddr prev)))))
+            ((setq plist (cons prop (cons val plist)))))
+      plist)))
+
+(defalias 'map--plist-put
+  (if map--plist-has-predicate #'plist-put #'map--plist-put-1)
+  "Compatibility shim for `plist-put' in Emacs 29+.
+\n(fn PLIST PROP VAL &optional PREDICATE)")
+
 (cl-defgeneric map-elt (map key &optional default testfn)
   "Look up KEY in MAP and return its associated value.
 If KEY is not found, return DEFAULT which defaults to nil.
 
 TESTFN is the function to use for comparing keys.  It is
 deprecated because its default and valid values depend on the MAP
-argument.  Generally, alist keys are compared with `equal', plist
-keys with `eq', and hash-table keys with the hash-table's test
+argument, and it was never consistently supported by the map.el
+API.  Generally, alist keys are compared with `equal', plist keys
+with `eq', and hash-table keys with the hash-table's test
 function.
 
 In the base definition, MAP can be an alist, plist, hash-table,
 or array."
   (declare
+   ;; `testfn' is deprecated.
+   (advertised-calling-convention (map key &optional default) "27.1")
    (gv-expander
     (lambda (do)
       (gv-letplace (mgetter msetter) `(gv-delay-error ,map)
         (macroexp-let2* nil
             ;; Eval them once and for all in the right order.
             ((key key) (default default) (testfn testfn))
-          (funcall do `(map-elt ,mgetter ,key ,default)
+          (funcall do
+                   `(map-elt ,mgetter ,key ,default ,@(and testfn `(,testfn)))
                    (lambda (v)
                      (macroexp-let2 nil v v
                        `(condition-case nil
@@ -132,19 +166,21 @@ or array."
                            ,(funcall msetter
                                      `(map-insert ,mgetter ,key ,v))
                            ;; Always return the value.
-                           ,v)))))))))
-   ;; `testfn' is deprecated.
-   (advertised-calling-convention (map key &optional default) "27.1"))
-  ;; Can't use `cl-defmethod' with `advertised-calling-convention'.
-  (map--dispatch map
-    :list (if (map--plist-p map)
-              (let ((res (plist-member map key)))
-                (if res (cadr res) default))
-            (alist-get key map default nil (or testfn #'equal)))
-    :hash-table (gethash key map default)
-    :array (if (map-contains-key map key)
-               (aref map key)
-             default)))
+                           ,v)))))))))))
+
+(cl-defmethod map-elt ((map list) key &optional default testfn)
+  (if (map--plist-p map)
+      (let ((res (map--plist-member map key testfn)))
+        (if res (cadr res) default))
+    (alist-get key map default nil (or testfn #'equal))))
+
+(cl-defmethod map-elt ((map hash-table) key &optional default _testfn)
+  (gethash key map default))
+
+(cl-defmethod map-elt ((map array) key &optional default _testfn)
+  (if (map-contains-key map key)
+      (aref map key)
+    default))
 
 (defmacro map-put (map key value &optional testfn)
   "Associate KEY with VALUE in MAP and return VALUE.
@@ -154,8 +190,12 @@ When MAP is an alist, test equality with TESTFN if non-nil,
 otherwise use `equal'.
 
 MAP can be an alist, plist, hash-table, or array."
-  (declare (obsolete "use map-put! or (setf (map-elt ...) ...) instead" 
"27.1"))
-  `(setf (map-elt ,map ,key nil ,testfn) ,value))
+  (declare
+   (obsolete "use `map-put!' or `(setf (map-elt ...) ...)' instead." "27.1"))
+  (if testfn
+      `(with-no-warnings
+         (setf (map-elt ,map ,key nil ,testfn) ,value))
+    `(setf (map-elt ,map ,key) ,value)))
 
 (defun map--plist-delete (map key)
   (let ((tail map) last)
@@ -338,15 +378,16 @@ The default implementation delegates to `map-length'."
   "Return non-nil if and only if MAP contains KEY.
 TESTFN is deprecated.  Its default depends on MAP.
 The default implementation delegates to `map-some'."
+  (declare (advertised-calling-convention (map key) "27.1"))
   (unless testfn (setq testfn #'equal))
   (map-some (lambda (k _v) (funcall testfn key k)) map))
 
 (cl-defmethod map-contains-key ((map list) key &optional testfn)
   "Return non-nil if MAP contains KEY.
 If MAP is an alist, TESTFN defaults to `equal'.
-If MAP is a plist, `plist-member' is used instead."
+If MAP is a plist, TESTFN defaults to `eq'."
   (if (map--plist-p map)
-      (plist-member map key)
+      (map--plist-member map key testfn)
     (let ((v '(nil)))
       (not (eq v (alist-get key map v nil (or testfn #'equal)))))))
 
@@ -459,24 +500,30 @@ This operates by modifying MAP in place.
 If it cannot do that, it signals a `map-not-inplace' error.
 To insert an element without modifying MAP, use `map-insert'."
   ;; `testfn' only exists for backward compatibility with `map-put'!
-  (declare (advertised-calling-convention (map key value) "27.1"))
-  ;; Can't use `cl-defmethod' with `advertised-calling-convention'.
-  (map--dispatch
-   map
-   :list
-   (progn
-     (if (map--plist-p map)
-         (plist-put map key value)
-       (let ((oldmap map))
-         (setf (alist-get key map key nil (or testfn #'equal)) value)
-         (unless (eq oldmap map)
-           (signal 'map-not-inplace (list oldmap)))))
-     ;; Always return the value.
-     value)
-   :hash-table (puthash key value map)
-   ;; FIXME: If `key' is too large, should we signal `map-not-inplace'
-   ;; and let `map-insert' grow the array?
-   :array (aset map key value)))
+  (declare (advertised-calling-convention (map key value) "27.1")))
+
+(cl-defmethod map-put! ((map list) key value &optional testfn)
+  (if (map--plist-p map)
+      (map--plist-put map key value testfn)
+    (let ((oldmap map))
+      (setf (alist-get key map key nil (or testfn #'equal)) value)
+      (unless (eq oldmap map)
+        (signal 'map-not-inplace (list oldmap)))))
+  ;; Always return the value.
+  value)
+
+(cl-defmethod map-put! ((map hash-table) key value &optional _testfn)
+  (puthash key value map))
+
+(cl-defmethod map-put! ((map array) key value &optional _testfn)
+  ;; FIXME: If `key' is too large, should we signal `map-not-inplace'
+  ;; and let `map-insert' grow the array?
+  (aset map key value))
+
+;; There shouldn't be old source code referring to `map--put', yet we do
+;; need to keep it for backward compatibility with .elc files where the
+;; expansion of `setf' may call this function.
+(define-obsolete-function-alias 'map--put #'map-put! "27.1")
 
 (cl-defgeneric map-insert (map key value)
   "Return a new map like MAP except that it associates KEY with VALUE.
@@ -493,11 +540,6 @@ The default implementation defaults to `map-copy' and 
`map-put!'."
       (cons key (cons value map))
     (cons (cons key value) map)))
 
-;; There shouldn't be old source code referring to `map--put', yet we do
-;; need to keep it for backward compatibility with .elc files where the
-;; expansion of `setf' may call this function.
-(define-obsolete-function-alias 'map--put #'map-put! "27.1")
-
 (cl-defmethod map-apply (function (map list))
   (if (map--plist-p map)
       (cl-call-next-method)
diff --git a/lisp/emacs-lisp/memory-report.el b/lisp/emacs-lisp/memory-report.el
index 56b1ea6ed4..968a80b59e 100644
--- a/lisp/emacs-lisp/memory-report.el
+++ b/lisp/emacs-lisp/memory-report.el
@@ -262,12 +262,7 @@ by counted more than once."
                    (cl-struct-slot-info struct-type)))))
 
 (defun memory-report--format (bytes)
-  (setq bytes (/ bytes 1024.0))
-  (let ((units '("KiB" "MiB" "GiB" "TiB")))
-    (while (>= bytes 1024)
-      (setq bytes (/ bytes 1024.0))
-      (setq units (cdr units)))
-    (format "%6.1f %s" bytes (car units))))
+  (format "%10s" (file-size-human-readable bytes 'iec " ")))
 
 (defun memory-report--gc-elem (elems type)
   (* (nth 1 (assq type elems))
diff --git a/lisp/emacs-lisp/multisession.el b/lisp/emacs-lisp/multisession.el
index d6f1ab98fa..9d6e8c0d88 100644
--- a/lisp/emacs-lisp/multisession.el
+++ b/lisp/emacs-lisp/multisession.el
@@ -19,7 +19,18 @@
 
 ;;; Commentary:
 
+;; This library provides multisession variables for Emacs Lisp, to
+;; make them persist between sessions.
 ;;
+;; Use `define-multisession-variable' to define a multisession
+;; variable, and `multisession-value' to read its value.  Use
+;; `list-multisession-values' to list multisession variables.
+;;
+;; Users might want to customize `multisession-storage' and
+;; `multisession-directory'.
+;;
+;; See Info node `(elisp) Multisession Variables' for more
+;; information.
 
 ;;; Code:
 
diff --git a/lisp/emacs-lisp/oclosure.el b/lisp/emacs-lisp/oclosure.el
index c77ac151d7..a17fdb7e35 100644
--- a/lisp/emacs-lisp/oclosure.el
+++ b/lisp/emacs-lisp/oclosure.el
@@ -216,7 +216,7 @@ is a list of additional properties among the following:
     function) named COPIER.  It will take an object of type NAME as first
     argument followed by ARGS.  ARGS lists the names of the slots that will
     be updated with the value of the corresponding argument.
-SLOTS is a list if slot descriptions.  Each slot can be a single symbol
+SLOTS is a list of slot descriptions.  Each slot can be a single symbol
 which is the name of the slot, or it can be of the form (SLOT-NAME . SPROPS)
 where SLOT-NAME is then the name of the slot and SPROPS is a property
 list of slot properties.  The currently known properties are the following:
@@ -341,11 +341,11 @@ list of slot properties.  The currently known properties 
are the following:
 
 (defmacro oclosure--lambda (type bindings mutables args &rest body)
   "Low level construction of an OClosure object.
-TYPE should be a form returning an OClosure type (a symbol)
+TYPE should be a form returning an OClosure type (a symbol).
 BINDINGS should list all the slots expected by this type, in the proper order.
 MUTABLE is a list of symbols indicating which of the BINDINGS
 should be mutable.
-No checking is performed,"
+No checking is performed."
   (declare (indent 3) (debug (sexp (&rest (sexp form)) sexp def-body)))
   ;; FIXME: Fundamentally `oclosure-lambda' should be a special form.
   ;; We define it here as a macro which expands to something that
diff --git a/lisp/emacs-lisp/package-vc.el b/lisp/emacs-lisp/package-vc.el
new file mode 100644
index 0000000000..a0b4b03118
--- /dev/null
+++ b/lisp/emacs-lisp/package-vc.el
@@ -0,0 +1,726 @@
+;;; package-vc.el --- Manage packages from VC checkouts     -*- 
lexical-binding: t; -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+
+;; Author: Philip Kaludercic <philipk@posteo.net>
+;; Keywords: tools
+
+;; This program 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.
+
+;; This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; While packages managed by package.el use tarballs for distributing
+;; the source code, this extension allows for packages to be fetched
+;; and updated directly from a version control system.
+;;
+;; To install a package from source use `package-vc-install'.  If you
+;; aren't interested in activating a package, you can use
+;; `package-vc-checkout' instead, which will prompt you for a target
+;; directory.  If you wish to re-use an existing checkout, the command
+;; `package-vc-install-from-checkout' will create a symbolic link and
+;; prepare the package.
+;;
+;; If you make local changes that you wish to share with an upstream
+;; maintainer, the command `package-vc-prepare-patch' can prepare
+;; these as patches to send via Email.
+
+;;; TODO:
+
+;; - Allow maintaining patches that are ported back onto regular
+;;   packages and maintained between versions.
+
+;;; Code:
+
+(eval-when-compile (require 'rx))
+(eval-when-compile (require 'inline))
+(eval-when-compile (require 'map))
+(require 'package)
+(require 'lisp-mnt)
+(require 'vc)
+(require 'seq)
+(require 'xdg)
+
+(defgroup package-vc nil
+  "Manage packages from VC checkouts."
+  :group 'package
+  :link '(custom-manual "(emacs) Package from Source")
+  :prefix "package-vc-"
+  :version "29.1")
+
+(defconst package-vc--elpa-packages-version 1
+  "Version number of the package specification format understood by 
package-vc.")
+
+(defcustom package-vc-heuristic-alist
+  `((,(rx bos "http" (? "s") "://"
+          (or (: (? "www.") "github.com"
+                 "/" (+ (or alnum "-" "." "_"))
+                 "/" (+ (or alnum "-" "." "_")))
+              (: "codeberg.org"
+                 "/" (+ (or alnum "-" "." "_"))
+                 "/" (+ (or alnum "-" "." "_")))
+              (: (? "www.") "gitlab" (+ "." (+ alnum))
+                 "/" (+ (or alnum "-" "." "_"))
+                 "/" (+ (or alnum "-" "." "_")))
+              (: "git.sr.ht"
+                 "/~" (+ (or alnum "-" "." "_"))
+                 "/" (+ (or alnum "-" "." "_")))
+              (: "git." (or "savannah" "sv") "." (? "non") "gnu.org/"
+                 (or "r" "git") "/"
+                 (+ (or alnum "-" "." "_")) (? "/")))
+          (or (? "/") ".git") eos)
+     . Git)
+    (,(rx bos "http" (? "s") "://"
+          (or (: "hg.sr.ht"
+                 "/~" (+ (or alnum "-" "." "_"))
+                 "/" (+ (or alnum "-" "." "_")))
+              (: "hg." (or "savannah" "sv") "." (? "non") "gnu.org/hgweb/"
+                 (+ (or alnum "-" "." "_")) (? "/")))
+          eos)
+     . Hg)
+    (,(rx bos "http" (? "s") "://"
+          (or (: "bzr." (or "savannah" "sv") "." (? "non") "gnu.org/r/"
+                 (+ (or alnum "-" "." "_")) (? "/")))
+          eos)
+     . Bzr))
+  "Heuristic mapping URL regular expressions to VC backends."
+  :type `(alist :key-type (regexp :tag "Regular expression matching URLs")
+                :value-type (choice :tag "VC Backend"
+                                    ,@(mapcar (lambda (b) `(const ,b))
+                                              vc-handled-backends)))
+  :version "29.1")
+
+(defcustom package-vc-repository-store
+  (expand-file-name "emacs/vc-packages" (xdg-data-home))
+  "Directory used by `package-vc--unpack' to store repositories."
+  :type 'directory
+  :version "29.1")
+
+(defcustom package-vc-default-backend 'Git
+  "Default VC backend used when cloning a package repository.
+If no repository type was specified or could be guessed by
+`package-vc-heuristic-alist', this is the default VC backend
+used as fallback.  The value must be a member of
+`vc-handled-backends' and the named backend must implement
+the `clone' function."
+  :type `(choice ,@(mapcar (lambda (b) (list 'const b))
+                           vc-handled-backends))
+  :version "29.1")
+
+(defvar package-vc-selected-packages) ; pacify byte-compiler
+(defun package-vc-ensure-packages ()
+  "Ensure packages specified in `package-vc-selected-packages' are installed."
+  (pcase-dolist (`(,(and (pred symbolp) name) . ,spec)
+                 package-vc-selected-packages)
+    (let ((pkg-desc (cadr (assoc name package-alist #'string=))))
+      (unless (and name (package-installed-p name)
+                   (package-vc-p pkg-desc))
+        (cond
+         ((null spec)
+          (package-vc-install name))
+         ((stringp spec)
+          (package-vc-install name nil spec))
+         ((listp spec)
+          (package-vc--archives-initialize)
+          (package-vc--unpack pkg-desc spec)))))))
+
+;;;###autoload
+(defcustom package-vc-selected-packages '()
+  "List of packages that must be installed.
+Each member of the list is of the form (NAME . SPEC), where NAME
+is a symbol designating the package and SPEC is one of:
+
+- nil, if any package version can be installed;
+- a version string, if that specific revision is to be installed;
+- a property list of the form described in
+  `package-vc-archive-spec-alist', giving a package
+  specification.
+
+This user option differs from `package-selected-packages' in that
+it is meant to be specified manually.  You can also use the
+function `package-vc-selected-packages' to apply the changes."
+  :type '(alist :tag "List of packages you want to be installed"
+                :key-type (symbol :tag "Package")
+                :value-type
+                (choice (const :tag "Any revision" nil)
+                        (string :tag "Specific revision")
+                        (plist :options ((:url string)
+                                         (:branch string)
+                                         (:lisp-dir string)
+                                         (:main-file string)
+                                         (:vc-backend symbol)))))
+  :set (lambda (sym val)
+         (custom-set-default sym val)
+         (package-vc-ensure-packages))
+  :version "29.1")
+
+(defvar package-vc--archive-spec-alist nil
+  "List of package specifications for each archive.
+The list maps each package name, as a string, to a plist.
+Valid keys and the corresponding value types are:
+
+ `:url' (string)
+    The URL of the repository used to fetch the package source.
+
+ `:branch' (string)
+    If given, the name of the branch to checkout after cloning the directory.
+
+ `:lisp-dir' (string)
+    The repository-relative name of the directory to use for loading the Lisp
+    sources.  If not given, the value defaults to the root directory
+    of the repository.
+
+ `:main-file' (string)
+    The main file of the project, relevant to gather package metadata.
+    If not given, the assumed default is the package name with \".el\"
+    appended to it.
+
+ `:vc-backend' (symbol)
+    A symbol of the VC backend to use for cloning the package.  The
+    value ought to be a member of `vc-handled-backends'.  If omitted,
+    `vc-clone' will fall back onto the archive default or on
+    `package-vc-default-backend'.
+
+All other values are ignored.")
+
+(defvar package-vc--archive-data-alist nil
+  "List of package specification metadata for archives.
+Each element of the list has the form (ARCHIVE . PLIST), where
+PLIST keys are one of:
+
+ `:version' (integer)
+   Indicates the version of the file formatting, to be compared
+   with `package-vc--elpa-packages-version'.
+
+ `:vc-backend' (symbol)
+   A symbol of the default VC backend to use if a package specification
+   does not indicate a backend.  The value ought to be a member of
+   `vc-handled-backends'.  If omitted, `vc-clone' will fall back on
+   `package-vc-default-backend'.
+
+All other values are ignored.")
+
+(defun package-vc--desc->spec (pkg-desc &optional name)
+  "Retrieve the package specification for PKG-DESC.
+The optional argument NAME can be used to override the default
+name for PKG-DESC."
+  (alist-get
+   (or name (package-desc-name pkg-desc))
+   (if (package-desc-archive pkg-desc)
+       (alist-get (intern (package-desc-archive pkg-desc))
+                  package-vc--archive-spec-alist)
+     (mapcan #'append (mapcar #'cdr package-vc--archive-spec-alist)))
+   nil nil #'string=))
+
+(define-inline package-vc--query-spec (pkg-desc prop)
+  "Query the property PROP for the package specification of PKG-DESC.
+If no package specification can be determined, the function will
+return nil."
+  (inline-letevals (pkg-desc prop)
+    (inline-quote (plist-get (package-vc--desc->spec ,pkg-desc) ,prop))))
+
+(defun package-vc--read-archive-data (archive)
+  "Update `package-vc--archive-spec-alist' for ARCHIVE.
+This function is meant to be used as a hook for `package--read-archive-hook'."
+  (let ((contents-file (expand-file-name
+                        (format "archives/%s/elpa-packages.eld" archive)
+                        package-user-dir)))
+    (when (file-exists-p contents-file)
+      (with-temp-buffer
+        (let ((coding-system-for-read 'utf-8))
+          (insert-file-contents contents-file)
+          ;; The response from the server is expected to have the form
+          ;;
+          ;;    ((("foo" :url "..." ...) ...)
+          ;;     :version 1
+          ;;     :default-vc Git)
+          (let ((spec (read (current-buffer))))
+            (when (eq package-vc--elpa-packages-version
+                      (plist-get (cdr spec) :version))
+              (setf (alist-get (intern archive) package-vc--archive-spec-alist)
+                    (car spec)))
+            (setf (alist-get (intern archive) package-vc--archive-data-alist)
+                  (cdr spec))
+            (when-let ((default-vc (plist-get (cdr spec) :default-vc))
+                       ((not (memq default-vc vc-handled-backends))))
+              (warn "Archive `%S' expects missing VC backend %S"
+                    archive (plist-get (cdr spec) :default-vc)))))))))
+
+(defun package-vc--download-and-read-archives (&optional async)
+  "Download specifications of all `package-archives' and read them.
+Populate `package-vc--archive-spec-alist' with the result.
+
+If optional argument ASYNC is non-nil, perform the downloads
+asynchronously."
+  (dolist (archive package-archives)
+    (condition-case-unless-debug nil
+        (package--download-one-archive archive "elpa-packages.eld" async)
+      (error (message "Failed to download `%s' archive." (car archive))))))
+
+(add-hook 'package-read-archive-hook     #'package-vc--read-archive-data 20)
+(add-hook 'package-refresh-contents-hook 
#'package-vc--download-and-read-archives 20)
+
+(defun package-vc-commit (pkg)
+  "Return the last commit of a development package PKG."
+  (cl-assert (package-vc-p pkg))
+  ;; FIXME: vc should be extended to allow querying the commit of a
+  ;; directory (as is possible when dealing with git repositories).
+  ;; This should be a fallback option.
+  (cl-loop with dir = (package-desc-dir pkg)
+           for file in (directory-files dir t "\\.el\\'" t)
+           when (vc-working-revision file) return it
+           finally return "unknown"))
+
+(defun package-vc--version (pkg)
+  "Extract the commit of a development package PKG."
+  (cl-assert (package-vc-p pkg))
+  (if-let ((main-file (package-vc--main-file pkg)))
+      (with-temp-buffer
+        (insert-file-contents main-file)
+        (package-strip-rcs-id
+         (or (lm-header "package-version")
+             (lm-header "version"))))
+    "0"))
+
+(defun package-vc--main-file (pkg-desc)
+  "Return the name of the main file for PKG-DESC."
+  (cl-assert (package-vc-p pkg-desc))
+  (let ((pkg-spec (package-vc--desc->spec pkg-desc)))
+    (or (plist-get pkg-spec :main-file)
+        (expand-file-name
+         (format "%s.el" (package-desc-name pkg-desc))
+         (file-name-concat
+          (or (package-desc-dir pkg-desc)
+              (expand-file-name
+               (package-desc-name pkg-desc)
+               package-user-dir))
+          (plist-get pkg-spec :lisp-dir))))))
+
+(defun package-vc--generate-description-file (pkg-desc pkg-file)
+  "Generate a package description file for PKG-DESC and write it to PKG-FILE."
+  (let ((name (package-desc-name pkg-desc)))
+    ;; Infer the subject if missing.
+    (unless (package-desc-summary pkg-desc)
+      (setf (package-desc-summary pkg-desc)
+            (let ((main-file (package-vc--main-file pkg-desc)))
+              (or (package-desc-summary pkg-desc)
+                  (and-let* ((pkg (cadr (assq name package-archive-contents))))
+                    (package-desc-summary pkg))
+                  (and main-file (file-exists-p main-file)
+                       (lm-summary main-file))
+                  package--default-summary))))
+    (let ((print-level nil)
+          (print-quoted t)
+          (print-length nil))
+      (write-region
+       (concat
+        ";;; Generated package description from "
+        (replace-regexp-in-string
+         "-pkg\\.el\\'" ".el"
+         (file-name-nondirectory pkg-file))
+        "  -*- no-byte-compile: t -*-\n"
+        (prin1-to-string
+         (nconc
+          (list 'define-package
+                (symbol-name name)
+                (cons 'vc (package-vc--version pkg-desc))
+                (package-desc-summary pkg-desc)
+                (let ((requires (package-desc-reqs pkg-desc)))
+                  (list 'quote
+                        ;; Turn version lists into string form.
+                        (mapcar
+                         (lambda (elt)
+                           (list (car elt)
+                                 (package-version-join (cadr elt))))
+                         requires))))
+          (package--alist-to-plist-args
+           (package-desc-extras pkg-desc))))
+        "\n")
+       nil pkg-file nil 'silent))))
+
+(declare-function org-export-to-file "ox" (backend file))
+
+(defun package-vc--build-documentation (pkg-desc file)
+  "Build documentation FILE for PKG-DESC.
+FILE can be an Org file, indicated by its \".org\" extension,
+otherwise it's assumed to be an Info file."
+  (let ((pkg-dir (package-desc-dir pkg-desc)))
+    (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-"))
+        (org-export-to-file 'texinfo file)))
+    (call-process "install-info" nil nil nil
+                  file pkg-dir)))
+
+(defun package-vc--unpack-1 (pkg-desc pkg-dir)
+  "Install PKG-DESC that is already checked-out in PKG-DIR."
+  ;; In case the package was installed directly from source, the
+  ;; dependency list wasn't know beforehand, and they might have
+  ;; to be installed explicitly.
+  (let (deps)
+    (dolist (file (directory-files pkg-dir t "\\.el\\'" t))
+      (with-temp-buffer
+        (insert-file-contents file)
+        (when-let* ((require-lines (lm-header-multiline "package-requires")))
+          (thread-last
+            (mapconcat #'identity require-lines " ")
+            package-read-from-string
+            package--prepare-dependencies
+            (nconc deps)
+            (setq deps)))))
+    (dolist (dep deps)
+      (cl-callf version-to-list (cadr dep)))
+    (package-download-transaction
+     (package-compute-transaction nil (delete-dups deps))))
+
+  (let ((default-directory (file-name-as-directory pkg-dir))
+        (name (package-desc-name pkg-desc))
+        (pkg-file (expand-file-name (package--description-file pkg-dir) 
pkg-dir)))
+    ;; Generate autoloads
+    (package-generate-autoloads name pkg-dir)
+
+    ;; Generate package file
+    (package-vc--generate-description-file pkg-desc pkg-file)
+
+    ;; Detect a manual
+    (when-let ((pkg-spec (package-vc--desc->spec pkg-desc))
+               ((executable-find "install-info")))
+      (dolist (doc-file (ensure-list (plist-get pkg-spec :doc)))
+        (package-vc--build-documentation pkg-desc doc-file))))
+
+  ;; Update package-alist.
+  (let ((new-desc (package-load-descriptor pkg-dir)))
+    ;; Activation has to be done before compilation, so that if we're
+    ;; upgrading and macros have changed we load the new definitions
+    ;; before compiling.
+    (when (package-activate-1 new-desc :reload :deps)
+      ;; FIXME: Compilation should be done as a separate, optional, step.
+      ;; E.g. for multi-package installs, we should first install all packages
+      ;; and then compile them.
+      (package--compile new-desc)
+      (when package-native-compile
+        (package--native-compile-async new-desc))
+      ;; After compilation, load again any files loaded by
+      ;; `activate-1', so that we use the byte-compiled definitions.
+      (package--reload-previously-loaded new-desc)))
+
+  ;; Mark package as selected
+  (package--save-selected-packages
+   (cons (package-desc-name pkg-desc)
+         package-selected-packages))
+
+  ;; Confirm that the installation was successful
+  (let ((main-file (package-vc--main-file pkg-desc)))
+    (message "Source package `%s' installed (Version %s, Revision %S)."
+             (package-desc-name pkg-desc)
+             (lm-with-file main-file
+               (package-strip-rcs-id
+                (or (lm-header "package-version")
+                    (lm-header "version"))))
+             (vc-working-revision main-file)))
+  t)
+
+(defun package-vc--guess-backend (url)
+  "Guess the VC backend for URL.
+This function will internally query `package-vc-heuristic-alist'
+and return nil if it cannot reasonably guess."
+  (and url (alist-get url package-vc-heuristic-alist
+                      nil nil #'string-match-p)))
+
+(defun package-vc--clone (pkg-desc pkg-spec dir rev)
+  "Clone the package PKG-DESC whose spec is PKG-SPEC into the directory DIR.
+REV specifies a specific revision to checkout.  This overrides the `:branch'
+attribute in PKG-SPEC."
+  (pcase-let* ((name (package-desc-name pkg-desc))
+               ((map :url :branch) pkg-spec))
+
+    ;; Clone the repository into `repo-dir' if necessary
+    (unless (file-exists-p dir)
+      (make-directory (file-name-directory dir) t)
+      (let ((backend (or (plist-get pkg-spec :vc-backend)
+                         (package-vc--query-spec pkg-desc :vc-backend)
+                         (package-vc--guess-backend url)
+                         (plist-get (alist-get (package-desc-archive pkg-desc)
+                                               package-vc--archive-data-alist
+                                               nil nil #'string=)
+                                    :vc-backend)
+                         package-vc-default-backend)))
+        (unless (vc-clone url backend dir
+                          (or (and (not (eq rev :last-release)) rev) branch))
+          (error "Failed to clone %s from %s" name url))))
+
+    ;; Check out the latest release if requested
+    (when (eq rev :last-release)
+      (if-let ((release-rev (package-vc--release-rev pkg-desc)))
+          (vc-retrieve-tag dir release-rev)
+        (message "No release revision was found, continuing...")))))
+
+(defun package-vc--unpack (pkg-desc pkg-spec &optional rev)
+  "Install the package described by PKG-DESC.
+PKG-SPEC is a package specification, a property list describing
+how to fetch and build the package.  See `package-vc--archive-spec-alist'
+for details.  The optional argument REV specifies a specific revision to
+checkout.  This overrides the `:branch' attribute in PKG-SPEC."
+  (pcase-let* (((map :url :lisp-dir) pkg-spec)
+               (name (package-desc-name pkg-desc))
+               (dirname (package-desc-full-name pkg-desc))
+               (pkg-dir (expand-file-name dirname package-user-dir))
+               (real-dir (if (null lisp-dir)
+                             pkg-dir
+                           (unless (file-exists-p package-vc-repository-store)
+                             (make-directory package-vc-repository-store t))
+                           (file-name-concat
+                            package-vc-repository-store
+                            ;; FIXME: We aren't sure this directory
+                            ;; will be unique, but we can try other
+                            ;; names to avoid an unnecessary error.
+                            (file-name-base url)))))
+    (setf (package-desc-dir pkg-desc) pkg-dir)
+    (when (file-exists-p pkg-dir)
+      (if (yes-or-no-p "Overwrite previous checkout?")
+          (package--delete-directory pkg-dir pkg-desc)
+        (error "There already exists a checkout for %s" name)))
+    (package-vc--clone pkg-desc pkg-spec real-dir rev)
+    (unless (eq pkg-dir real-dir)
+      ;; Link from the right position in `repo-dir' to the package
+      ;; directory in the ELPA store.
+      (make-symbolic-link (file-name-concat real-dir lisp-dir) pkg-dir))
+
+    (package-vc--unpack-1 pkg-desc pkg-dir)))
+
+(defun package-vc--sourced-packages-list ()
+  "Generate a list of packages with VC data."
+  (seq-filter
+   (lambda (pkg)
+     (or (package-vc--desc->spec (cadr pkg))
+         ;; If we have no explicit VC data, we can try a kind of
+         ;; heuristic and use the URL header, that might already be
+         ;; pointing towards a repository, and use that as a backup
+         (and-let* ((extras (package-desc-extras (cadr pkg)))
+                    (url (alist-get :url extras))
+                    ((package-vc--guess-backend url))))))
+   package-archive-contents))
+
+(defun package-vc-update (pkg-desc)
+  "Attempt to update the package PKG-DESC."
+  ;; HACK: To run `package-vc--unpack-1' after checking out the new
+  ;; revision, we insert a hook into `vc-post-command-functions', and
+  ;; remove it right after it ran.  To avoid running the hook multiple
+  ;; times or even for the wrong repository (as `vc-pull' is often
+  ;; asynchronous), we extract the relevant arguments using a pseudo
+  ;; filter for `vc-filter-command-function', executed only for the
+  ;; side effect, and store them in the lexical scope.  When the hook
+  ;; is run, we check if the arguments are the same (`eq') as the ones
+  ;; previously extracted, and only in that case will be call
+  ;; `package-vc--unpack-1'.  Ugh...
+  ;;
+  ;; If there is a better way to do this, it should be done.
+  (letrec ((pkg-dir (package-desc-dir pkg-desc))
+           (empty (make-symbol empty))
+           (args (list empty empty empty))
+           (vc-filter-command-function
+            (lambda (command file-or-list flags)
+              (setf (nth 0 args) command
+                    (nth 1 args) file-or-list
+                    (nth 2 args) flags)
+              (list command file-or-list flags)))
+           (post-upgrade
+            (lambda (command file-or-list flags)
+              (when (and (memq (nth 0 args) (list command empty))
+                         (memq (nth 1 args) (list file-or-list empty))
+                         (memq (nth 2 args) (list flags empty)))
+                (with-demoted-errors "Failed to activate: %S"
+                  (package-vc--unpack-1 pkg-desc pkg-dir))
+                (remove-hook 'vc-post-command-functions post-upgrade)))))
+    (add-hook 'vc-post-command-functions post-upgrade)
+    (with-demoted-errors "Failed to fetch: %S"
+      (vc-pull))))
+
+(defun package-vc--archives-initialize ()
+  "Initialize package.el and fetch package specifications."
+  (package--archives-initialize)
+  (unless package-vc--archive-data-alist
+    (package-vc--download-and-read-archives)))
+
+(defun package-vc--release-rev (pkg-desc)
+  "Return the latest revision that bumps the \"Version\" tag for PKG-DESC.
+If no such revision can be found, return nil."
+  (with-current-buffer (find-file-noselect (package-vc--main-file pkg-desc))
+    (vc-buffer-sync)
+    (save-excursion
+      (goto-char (point-min))
+      (let ((case-fold-search t))
+        (when (cond
+               ((re-search-forward
+                 (concat (lm-get-header-re "package-version") ".*$")
+                 (lm-code-start) t))
+               ((re-search-forward
+                 (concat (lm-get-header-re "version") ".*$")
+                 (lm-code-start) t)))
+          (ignore-error vc-not-supported
+            (vc-call-backend (vc-backend (buffer-file-name))
+                             'last-change
+                             (buffer-file-name)
+                             (line-number-at-pos nil t))))))))
+
+;;;###autoload
+(defun package-vc-install (name-or-url &optional name rev backend)
+  "Fetch a package NAME-OR-URL and set it up for using with Emacs.
+If NAME-OR-URL is a URL, download the package from the repository
+at that URL; the function will try to guess the name of the package
+from the URL.  Otherwise NAME-OR-URL should be a symbol whose name
+is the package name, and the URL for the package will be taken from
+the package's metadata.
+By default, this function installs the last version of the package
+available from its repository, but if REV is given and non-nil, it
+specifies the revision to install.  If REV has the special value
+`:last-release' (interactively, the prefix argument), that stands
+for the last released version of the package.
+When calling from Lisp, optional argument NAME overrides the package
+name as deduced from NAME-OR-URL.
+Optional argument BACKEND specifies the VC backend to use for cloning
+the package's repository; this is only possible if NAME-OR-URL is a URL,
+a string.  If BACKEND is omitted or nil, the function
+uses `package-vc--guess-backend' to guess the backend."
+  (interactive
+   (progn
+     ;; Initialize the package system to get the list of package
+     ;; symbols for completion.
+     (package-vc--archives-initialize)
+     (let* ((packages (package-vc--sourced-packages-list))
+            (input (completing-read
+                    "Fetch package source (name or URL): " packages))
+            (name (file-name-base input)))
+       (list input (intern (string-remove-prefix "emacs-" name))
+             (and current-prefix-arg :last-release)))))
+  (package-vc--archives-initialize)
+  (cond
+   ((and-let* (((stringp name-or-url))
+               (backend (or backend (package-vc--guess-backend name-or-url))))
+      (package-vc--unpack
+       (package-desc-create
+        :name (or name (intern (file-name-base name-or-url)))
+        :kind 'vc)
+       (list :vc-backend backend :url name-or-url)
+       rev)))
+   ((and-let* ((desc (assoc name-or-url package-archive-contents #'string=)))
+      (package-vc--unpack
+       (let ((copy (copy-package-desc (cadr desc))))
+         (setf (package-desc-kind copy) 'vc)
+         copy)
+       (or (package-vc--desc->spec (cadr desc))
+           (and-let* ((extras (package-desc-extras (cadr desc)))
+                      (url (alist-get :url extras))
+                      (backend (package-vc--guess-backend url)))
+             (list :vc-backend backend :url url))
+           (user-error "Package has no VC data"))
+       rev)))
+   ((user-error "Unknown package to fetch: %s" name-or-url))))
+
+;;;###autoload
+(defun package-vc-checkout (pkg-desc directory &optional rev)
+  "Clone the sources for PKG-DESC into DIRECTORY and visit that directory.
+Unlike `package-vc-install', this does not yet set up the package
+for use with Emacs; use `package-vc-link-directory' for setting
+the package up after this function finishes.
+Optional argument REV means to clone a specific version of the
+package; it defaults to the last version available from the
+package's repository.  If REV has the special value
+`:last-release' (interactively, the prefix argument), that stands
+for the last released version of the package."
+  (interactive
+   (progn
+     ;; Initialize the package system to get the list of package
+     ;; symbols for completion.
+     (package-vc--archives-initialize)
+     (let* ((packages (package-vc--sourced-packages-list))
+            (input (completing-read
+                    "Fetch package source (name or URL): " packages)))
+       (list (cadr (assoc input package-archive-contents #'string=))
+             (read-file-name "Clone into new or empty directory: " nil nil t 
nil
+                             (lambda (dir) (or (not (file-exists-p dir))
+                                               (directory-empty-p dir))))
+             (and current-prefix-arg :last-release)))))
+  (package-vc--archives-initialize)
+  (let ((pkg-spec (or (package-vc--desc->spec pkg-desc)
+                      (and-let* ((extras (package-desc-extras pkg-desc))
+                                 (url (alist-get :url extras))
+                                 (backend (package-vc--guess-backend url)))
+                        (list :vc-backend backend :url url))
+                      (user-error "Package has no VC data"))))
+    (package-vc--clone pkg-desc pkg-spec directory rev)
+    (find-file directory)))
+
+;;;###autoload
+(defun package-vc-install-from-checkout (dir name)
+  "Set up the package NAME in DIR by linking it into the ELPA directory.
+Interactively, prompt the user for DIR, which should be a directory
+under version control, typically one created by `package-vc-checkout'.
+If invoked interactively with a prefix argument, prompt the user
+for the NAME of the package to set up.  Otherwise infer the package
+name from the base name of DIR."
+  (interactive (let ((dir (read-directory-name "Directory: ")))
+                 (list dir
+                       (if current-prefix-arg
+                           (read-string "Package name: ")
+                         (file-name-base (directory-file-name dir))))))
+  (unless (vc-responsible-backend dir)
+    (user-error "Directory %S is not under version control" dir))
+  (package-vc--archives-initialize)
+  (let* ((name (or name (file-name-base (directory-file-name dir))))
+         (pkg-dir (expand-file-name name package-user-dir)))
+    (make-symbolic-link dir pkg-dir)
+    (package-vc--unpack-1 (package-desc-create
+                          :name (intern name)
+                          :kind 'vc)
+                         pkg-dir)))
+
+;;;###autoload
+(defun package-vc-refresh (pkg-desc)
+  "Refresh the installation for package given by PKG-DESC.
+Interactively, prompt for the name of the package to refresh."
+  (interactive (package-vc--read-pkg "Refresh package: "))
+  (package-vc--unpack-1 pkg-desc (package-desc-dir pkg-desc)))
+
+(defun package-vc--read-pkg (prompt)
+  "Query for a source package description with PROMPT."
+  (cadr (assoc (completing-read
+                prompt
+                package-alist
+                (lambda (pkg) (package-vc-p (cadr pkg)))
+                t)
+               package-alist
+               #'string=)))
+
+;;;###autoload
+(defun package-vc-prepare-patch (pkg subject revisions)
+  "Send patch for REVISIONS to maintainer of the package PKG using SUBJECT.
+SUBJECT and REVISIONS are passed on to `vc-prepare-patch', which see.
+PKG must be a package description.
+Interactively, prompt for PKG, SUBJECT, and REVISIONS.  However,
+if the current buffer has marked commit log entries, REVISIONS
+are the tags of the marked entries, see `log-view-get-marked'."
+  (interactive
+   (list (package-vc--read-pkg "Package to prepare a patch for: ")
+         (and (not vc-prepare-patches-separately)
+              (read-string "Subject: " "[PATCH] " nil nil t))
+         (or (log-view-get-marked)
+             (vc-read-multiple-revisions "Revisions: "))))
+  (vc-prepare-patch (package-maintainers pkg t)
+                    subject revisions))
+
+(provide 'package-vc)
+;;; package-vc.el ends here
diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el
index d619142d64..a7bcdd214c 100644
--- a/lisp/emacs-lisp/package.el
+++ b/lisp/emacs-lisp/package.el
@@ -146,6 +146,7 @@
 (require 'cl-lib)
 (eval-when-compile (require 'subr-x))
 (eval-when-compile (require 'epg))      ;For setf accessors.
+(eval-when-compile (require 'inline))   ;For `define-inline'
 (require 'seq)
 
 (require 'tabulated-list)
@@ -456,6 +457,11 @@ synchronously."
 
 (defvar package--default-summary "No description available.")
 
+(define-inline package-vc-p (pkg-desc)
+  "Return non-nil if PKG-DESC is a source package."
+  (inline-letevals (pkg-desc)
+    (inline-quote (eq (package-desc-kind ,pkg-desc) 'vc))))
+
 (cl-defstruct (package-desc
                ;; Rename the default constructor from `make-package-desc'.
                (:constructor package-desc-create)
@@ -468,14 +474,18 @@ synchronously."
                  &rest rest-plist
                  &aux
                  (name (intern name-string))
-                 (version (version-to-list version-string))
+                 (version (if (eq (car-safe version-string) 'vc)
+                              (version-to-list (cdr version-string))
+                            (version-to-list version-string)))
                  (reqs (mapcar (lambda (elt)
                                  (list (car elt)
                                        (version-to-list (cadr elt))))
                                (if (eq 'quote (car requirements))
                                    (nth 1 requirements)
                                  requirements)))
-                 (kind (plist-get rest-plist :kind))
+                 (kind (if (eq (car-safe version-string) 'vc)
+                           'vc
+                         (plist-get rest-plist :kind)))
                  (archive (plist-get rest-plist :archive))
                  (extras (let (alist)
                            (while rest-plist
@@ -567,9 +577,11 @@ This is, approximately, the inverse of `version-to-list'.
 (defun package-desc-full-name (pkg-desc)
   "Return full name of package-desc object PKG-DESC.
 This is the name of the package with its version appended."
-  (format "%s-%s"
-          (package-desc-name pkg-desc)
-          (package-version-join (package-desc-version pkg-desc))))
+  (if (package-vc-p pkg-desc)
+      (symbol-name (package-desc-name pkg-desc))
+    (format "%s-%s"
+            (package-desc-name pkg-desc)
+            (package-version-join (package-desc-version pkg-desc)))))
 
 (defun package-desc-suffix (pkg-desc)
   "Return file-name extension of package-desc object PKG-DESC.
@@ -600,6 +612,25 @@ package."
   "Return the priority of the archive of package-desc object PKG-DESC."
   (package-archive-priority (package-desc-archive pkg-desc)))
 
+(defun package--parse-elpaignore (pkg-desc)
+  "Return the of regular expression to match files ignored by PKG-DESC."
+  (let* ((pkg-dir (file-name-as-directory (package-desc-dir pkg-desc)))
+         (ignore (expand-file-name ".elpaignore" pkg-dir))
+         files)
+    (when (file-exists-p ignore)
+      (with-temp-buffer
+        (insert-file-contents ignore)
+        (goto-char (point-min))
+        (while (not (eobp))
+          (push (wildcard-to-regexp
+                 (let ((line (buffer-substring
+                              (line-beginning-position)
+                              (line-end-position))))
+                   (file-name-concat pkg-dir (string-trim-left line "/"))))
+                files)
+          (forward-line)))
+      files)))
+
 (cl-defstruct (package--bi-desc
                (:constructor package-make-builtin (version summary))
                (:type vector))
@@ -648,6 +679,8 @@ loaded and/or activated, customize `package-load-list'.")
 ;; `package-load-all-descriptors', which ultimately populates the
 ;; `package-alist' variable.
 
+(declare-function package-vc-version "package-vc" (pkg))
+
 (defun package-process-define-package (exp)
   "Process define-package expression EXP and push it to `package-alist'.
 EXP should be a form read from a foo-pkg.el file.
@@ -676,6 +709,8 @@ are sorted with the highest version first."
               nil)))
       new-pkg-desc)))
 
+(declare-function package-vc-commit "package-vc" (pkg))
+
 (defun package-load-descriptor (pkg-dir)
   "Load the package description file in directory PKG-DIR.
 Create a new `package-desc' object, add it to `package-alist' and
@@ -706,11 +741,9 @@ description file containing a call to `define-package', 
which
 updates `package-alist'."
   (dolist (dir (cons package-user-dir package-directory-list))
     (when (file-directory-p dir)
-      (dolist (subdir (directory-files dir))
-        (unless (equal subdir "..")
-          (let ((pkg-dir (expand-file-name subdir dir)))
-            (when (file-directory-p pkg-dir)
-              (package-load-descriptor pkg-dir))))))))
+      (dolist (pkg-dir (directory-files dir t "\\`[^.]" t))
+        (when (file-directory-p pkg-dir)
+          (package-load-descriptor pkg-dir))))))
 
 (defun package--alist ()
   "Return `package-alist', after computing it if needed."
@@ -835,8 +868,7 @@ byte-compilation of the new package to fail."
 If DEPS is non-nil, also activate its dependencies (unless they
 are already activated).
 If RELOAD is non-nil, also `load' any files inside the package which
-correspond to previously loaded files (those returned by
-`package--list-loaded-files')."
+correspond to previously loaded files."
   (let* ((name (package-desc-name pkg-desc))
          (pkg-dir (package-desc-dir pkg-desc)))
     (unless pkg-dir
@@ -874,14 +906,22 @@ correspond to previously loaded files (those returned by
 
 (defun package--get-activatable-pkg (pkg-name)
   ;; Is "activatable" a word?
-  (let ((pkg-descs (cdr (assq pkg-name package-alist))))
+  (let ((pkg-descs (sort (cdr (assq pkg-name package-alist))
+                         (lambda (p1 p2)
+                           (let ((v1 (package-desc-version p1))
+                                 (v2 (package-desc-version p2)))
+                             (or
+                              ;; Prefer source packages.
+                              (package-vc-p p1)
+                              (package-vc-p p2)
+                              ;; Prefer builtin packages.
+                              (package-disabled-p p1 v1)
+                              (not (package-disabled-p p2 v2))))))))
     ;; Check if PACKAGE is available in `package-alist'.
     (while
         (when pkg-descs
           (let ((available-version (package-desc-version (car pkg-descs))))
-            (or (package-disabled-p pkg-name available-version)
-                ;; Prefer a builtin package.
-                (package-built-in-p pkg-name available-version))))
+            (package-disabled-p pkg-name available-version)))
       (setq pkg-descs (cdr pkg-descs)))
     (car pkg-descs)))
 
@@ -959,7 +999,7 @@ untar into a directory named DIR; otherwise, signal an 
error."
          ;; indistinguishable from a `tar' or a `single'. Let's make
          ;; things simple by ensuring we're one of them.
          (setf (package-desc-kind pkg-desc)
-               (if (> (length file-list) 1) 'tar 'single))))
+               (if (length> file-list 1) 'tar 'single))))
       ('tar
        (make-directory package-user-dir t)
        (let* ((default-directory (file-name-as-directory package-user-dir)))
@@ -1022,6 +1062,7 @@ untar into a directory named DIR; otherwise, signal an 
error."
         "\n")
        nil pkg-file nil 'silent))))
 
+
 ;;;; Autoload
 (declare-function autoload-rubric "autoload" (file &optional type feature))
 
@@ -1069,11 +1110,13 @@ untar into a directory named DIR; otherwise, signal an 
error."
 
 ;;;; Compilation
 (defvar warning-minimum-level)
+(defvar byte-compile-ignore-files)
 (defun package--compile (pkg-desc)
   "Byte-compile installed package PKG-DESC.
 This assumes that `pkg-desc' has already been activated with
 `package-activate-1'."
-  (let ((warning-minimum-level :error)
+  (let ((byte-compile-ignore-files (package--parse-elpaignore pkg-desc))
+        (warning-minimum-level :error)
         (load-path load-path))
     (byte-recompile-directory (package-desc-dir pkg-desc) 0 t)))
 
@@ -1602,13 +1645,19 @@ This is the value of `package-archive-priorities' last 
time
 by arbitrary functions to decide whether it is necessary to call
 it again.")
 
+(defvar package-read-archive-hook (list #'package-read-archive-contents)
+  "List of functions to call to read the archive contents.
+Each function must take an optional argument, a symbol indicating
+what archive to read in.  The symbol ought to be a key in
+`package-archives'.")
+
 (defun package-read-all-archive-contents ()
   "Read cached archive file for all archives in `package-archives'.
 If successful, set or update `package-archive-contents'."
   (setq package-archive-contents nil)
   (setq package--old-archive-priorities package-archive-priorities)
   (dolist (archive package-archives)
-    (package-read-archive-contents (car archive))))
+    (run-hook-with-args 'package-read-archive-hook (car archive))))
 
 
 ;;;; Package Initialize
@@ -1734,9 +1783,14 @@ Once it's empty, run 
`package--post-download-archives-hook'."
 ARCHIVE should be a cons cell of the form (NAME . LOCATION),
 similar to an entry in `package-alist'.  Save the cached copy to
 \"archives/NAME/FILE\" in `package-user-dir'."
+  ;; The downloaded archive contents will be read as part of
+  ;; `package--update-downloads-in-progress'.
+  (dolist (archive package-archives)
+    (cl-pushnew (cons archive file) package--downloads-in-progress
+                :test #'equal))
   (package--with-response-buffer (cdr archive) :file file
     :async async
-    :error-form (package--update-downloads-in-progress archive)
+    :error-form (package--update-downloads-in-progress (cons archive file))
     (let* ((location (cdr archive))
            (name (car archive))
            (content (buffer-string))
@@ -1749,10 +1803,10 @@ similar to an entry in `package-alist'.  Save the 
cached copy to
             ;; If we don't care about the signature, save the file and
             ;; we're done.
             (progn
-             (cl-assert (not enable-multibyte-characters))
-             (let ((coding-system-for-write 'binary))
-               (write-region content nil local-file nil 'silent))
-             (package--update-downloads-in-progress archive))
+              (cl-assert (not enable-multibyte-characters))
+              (let ((coding-system-for-write 'binary))
+                (write-region content nil local-file nil 'silent))
+              (package--update-downloads-in-progress (cons archive file)))
           ;; If we care, check it (perhaps async) and *then* write the file.
           (package--check-signature
            location file content async
@@ -1765,7 +1819,7 @@ similar to an entry in `package-alist'.  Save the cached 
copy to
              (when good-sigs
                (write-region (mapconcat #'epg-signature-to-string good-sigs 
"\n")
                              nil (concat local-file ".signed") nil 'silent)))
-           (lambda () (package--update-downloads-in-progress archive))))))))
+           (lambda () (package--update-downloads-in-progress (cons archive 
file)))))))))
 
 (defun package--download-and-read-archives (&optional async)
   "Download descriptions of all `package-archives' and read them.
@@ -1773,17 +1827,17 @@ Populate `package-archive-contents' with the result.
 
 If optional argument ASYNC is non-nil, perform the downloads
 asynchronously."
-  ;; The downloaded archive contents will be read as part of
-  ;; `package--update-downloads-in-progress'.
-  (dolist (archive package-archives)
-    (cl-pushnew archive package--downloads-in-progress
-                :test #'equal))
   (dolist (archive package-archives)
     (condition-case-unless-debug nil
         (package--download-one-archive archive "archive-contents" async)
       (error (message "Failed to download `%s' archive."
                (car archive))))))
 
+(defvar package-refresh-contents-hook (list 
#'package--download-and-read-archives)
+  "List of functions to call to refresh the package archive.
+Each function may take an optional argument indicating that the
+operation ought to be executed asynchronously.")
+
 ;;;###autoload
 (defun package-refresh-contents (&optional async)
   "Download descriptions of all configured ELPA packages.
@@ -1802,7 +1856,7 @@ downloads in the background."
       (condition-case-unless-debug error
           (package-import-keyring default-keyring)
         (error (message "Cannot import default keyring: %S" (cdr error))))))
-  (package--download-and-read-archives async))
+  (run-hook-with-args 'package-refresh-contents-hook async))
 
 
 ;;; Dependency Management
@@ -2036,9 +2090,9 @@ if all the in-between dependencies are also in 
PACKAGE-LIST."
   (cdr (assoc (package-desc-archive desc) package-archives)))
 
 (defun package-install-from-archive (pkg-desc)
-  "Download and install a tar package defined by PKG-DESC."
+  "Download and install a package defined by PKG-DESC."
   ;; This won't happen, unless the archive is doing something wrong.
-  (when (eq (package-desc-kind pkg-desc) 'dir)
+  (when (package-vc-p pkg-desc)
     (error "Can't install directory package from archive"))
   (let* ((location (package-archive-base pkg-desc))
          (file (concat (package-desc-full-name pkg-desc)
@@ -2176,17 +2230,22 @@ to install it but still mark it as selected."
           (message  "Package `%s' installed." name))
       (message "`%s' is already installed" name))))
 
+(declare-function package-vc-update "package-vc" (pkg))
+
 ;;;###autoload
 (defun package-update (name)
   "Update package NAME if a newer version exists."
   (interactive
    (list (completing-read
           "Update package: " (package--updateable-packages) nil t)))
-  (let ((package (if (symbolp name)
-                     name
-                   (intern name))))
-    (package-delete (cadr (assq package package-alist)) 'force)
-    (package-install package 'dont-select)))
+  (let* ((package (if (symbolp name)
+                      name
+                    (intern name)))
+         (pkg-desc (cadr (assq package package-alist))))
+    (if (package-vc-p pkg-desc)
+        (package-vc-update pkg-desc)
+      (package-delete pkg-desc 'force)
+      (package-install package 'dont-select))))
 
 (defun package--updateable-packages ()
   ;; Initialize the package system to get the list of package
@@ -2196,12 +2255,13 @@ to install it but still mark it as selected."
    #'car
    (seq-filter
     (lambda (elt)
-      (let ((available
-             (assq (car elt) package-archive-contents)))
-        (and available
-             (version-list-<
-              (package-desc-version (cadr elt))
-              (package-desc-version (cadr available))))))
+      (or (let ((available
+                 (assq (car elt) package-archive-contents)))
+            (and available
+                 (version-list-<
+                  (package-desc-version (cadr elt))
+                  (package-desc-version (cadr available)))))
+          (package-vc-p (cadr (assq (car elt) package-alist)))))
     package-alist)))
 
 ;;;###autoload
@@ -2358,15 +2418,28 @@ installed), maybe you need to 
\\[package-refresh-contents]")
          pkg))
 
 (declare-function comp-el-to-eln-filename "comp.c")
-(defun package--delete-directory (dir)
-  "Delete DIR recursively.
+(defvar package-vc-repository-store)
+(defun package--delete-directory (dir pkg-desc)
+  "Delete PKG-DESC directory DIR recursively.
 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\\'")
      do (comp-clean-up-stale-eln (comp-el-to-eln-filename file))))
-  (delete-directory dir t))
+  (if (and (package-vc-p pkg-desc)
+           (require 'package-vc)   ;load `package-vc-repository-store'
+           (file-in-directory-p dir package-vc-repository-store))
+      (progn
+        (delete-directory
+         (expand-file-name
+          (car (file-name-split
+                (file-relative-name dir package-vc-repository-store)))
+          package-vc-repository-store)
+         t)
+        (delete-file (directory-file-name dir)))
+    (delete-directory dir t)))
+
 
 (defun package-delete (pkg-desc &optional force nosave)
   "Delete package PKG-DESC.
@@ -2420,7 +2493,7 @@ If NOSAVE is non-nil, the package is not removed from
                   (package-desc-name pkg-used-elsewhere-by)))
           (t
            (add-hook 'post-command-hook #'package-menu--post-refresh)
-           (package--delete-directory dir)
+           (package--delete-directory dir pkg-desc)
            ;; Remove NAME-VERSION.signed and NAME-readme.txt files.
            ;;
            ;; NAME-readme.txt files are no longer created, but they
@@ -2631,7 +2704,10 @@ Helper function for `describe-package'."
          (incompatible-reason (package--incompatible-p desc))
          (signed (if desc (package-desc-signed desc)))
          (maintainer (cdr (assoc :maintainer extras)))
-         (authors (cdr (assoc :authors extras))))
+         (authors (cdr (assoc :authors extras)))
+         (news (and-let* ((file (expand-file-name "news" pkg-dir))
+                          ((file-readable-p file)))
+                 file)))
     (when (string= status "avail-obso")
       (setq status "available obsolete"))
     (when incompatible-reason
@@ -2830,6 +2906,14 @@ Helper function for `describe-package'."
               t)
             (insert (or readme-string
                         "This package does not provide a description.")))))
+
+      ;; Insert news if available.
+      (when news
+        (insert "\n" (make-separator-line) "\n"
+                (propertize "* News" 'face 'package-help-section-name)
+                "\n\n")
+        (insert-file-contents news))
+
       ;; Make library descriptions into links.
       (goto-char start-of-description)
       (package--describe-add-library-links)
@@ -2920,6 +3004,7 @@ either a full name or nil, and EMAIL is a valid email 
address."
   "r"     #'revert-buffer
   "~"     #'package-menu-mark-obsolete-for-deletion
   "w"     #'package-browse-url
+  "b"     #'package-report-bug
   "x"     #'package-menu-execute
   "h"     #'package-menu-quick-help
   "H"     #'package-menu-hide-package
@@ -3078,6 +3163,7 @@ of these dependencies, similar to the list returned by
          (signed (or (not package-list-unsigned)
                      (package-desc-signed pkg-desc))))
     (cond
+     ((package-vc-p pkg-desc) "source")
      ((eq dir 'builtin) "built-in")
      ((and lle (null held)) "disabled")
      ((stringp held)
@@ -3166,8 +3252,9 @@ to their archives."
           (if (not installed)
               filtered-by-priority
             (let ((ins-version (package-desc-version installed)))
-              (cl-remove-if (lambda (p) (version-list-= (package-desc-version 
p)
-                                                   ins-version))
+              (cl-remove-if (lambda (p) (or (version-list-= 
(package-desc-version p)
+                                                            ins-version)
+                                            (package-vc-p installed)))
                             filtered-by-priority))))))))
 
 (defcustom package-hidden-regexps nil
@@ -3369,6 +3456,11 @@ Return (PKG-DESC [NAME VERSION STATUS DOC])."
   "Face used on the status and version of installed packages."
   :version "25.1")
 
+(defface package-status-from-source
+  '((t :inherit font-lock-negation-char-face))
+  "Face used on the status and version of installed packages."
+  :version "29.1")
+
 (defface package-status-dependency
   '((t :inherit package-status-installed))
   "Face used on the status and version of dependency packages."
@@ -3406,6 +3498,7 @@ Return (PKG-DESC [NAME VERSION STATUS DOC])."
                  ("held"      'package-status-held)
                  ("disabled"  'package-status-disabled)
                  ("installed" 'package-status-installed)
+                 ("source"    'package-status-from-source)
                  ("dependency" 'package-status-dependency)
                  ("unsigned"  'package-status-unsigned)
                  ("incompat"  'package-status-incompat)
@@ -3417,9 +3510,14 @@ Return (PKG-DESC [NAME VERSION STATUS DOC])."
              follow-link t
              package-desc ,pkg
              action package-menu-describe-package)
-            ,(propertize (package-version-join
-                          (package-desc-version pkg))
-                         'font-lock-face face)
+            ,(propertize
+              (if (package-vc-p pkg)
+                  (progn
+                    (require 'package-vc)
+                    (package-vc-commit pkg))
+                (package-version-join
+                 (package-desc-version pkg)))
+              'font-lock-face face)
             ,(propertize status 'font-lock-face face)
             ,@(if (cdr package-archives)
                   (list (propertize (or (package-desc-archive pkg) "")
@@ -3494,7 +3592,7 @@ If optional arg BUTTON is non-nil, describe its 
associated package."
   (interactive "p" package-menu-mode)
   (package--ensure-package-menu-mode)
   (if (member (package-menu-get-status)
-              '("installed" "dependency" "obsolete" "unsigned"))
+              '("installed" "source" "dependency" "obsolete" "unsigned"))
       (tabulated-list-put-tag "D" t)
     (forward-line)))
 
@@ -3850,6 +3948,8 @@ This is used for `tabulated-list-format' in 
`package-menu-mode'."
           ((string= sB "installed") nil)
           ((string= sA "dependency") t)
           ((string= sB "dependency") nil)
+          ((string= sA "source") t)
+          ((string= sB "source") nil)
           ((string= sA "unsigned") t)
           ((string= sB "unsigned") nil)
           ((string= sA "held") t)
@@ -4143,6 +4243,7 @@ packages."
                                         "held"
                                         "incompat"
                                         "installed"
+                                        "source"
                                         "new"
                                         "unsigned")))
                package-menu-mode)
@@ -4214,22 +4315,22 @@ Unlike other filters, this leaves the marks intact."
       (while (not (eobp))
         (setq mark (char-after))
         (unless (eq mark ?\s)
-         (setq pkg-id (tabulated-list-get-id))
+          (setq pkg-id (tabulated-list-get-id))
           (setq entry (package-menu--print-info-simple pkg-id))
-         (push entry found-entries)
-         ;; remember the mark
-         (push (cons pkg-id mark) marks))
+          (push entry found-entries)
+          ;; remember the mark
+          (push (cons pkg-id mark) marks))
         (forward-line))
       (if found-entries
           (progn
             (setq tabulated-list-entries found-entries)
             (package-menu--display t nil)
-           ;; redo the marks, but we must remember the marks!!
-           (goto-char (point-min))
-           (while (not (eobp))
-             (setq mark (cdr (assq (tabulated-list-get-id) marks)))
-             (tabulated-list-put-tag (char-to-string mark) t)))
-       (user-error "No packages found")))))
+            ;; redo the marks, but we must remember the marks!!
+            (goto-char (point-min))
+            (while (not (eobp))
+              (setq mark (cdr (assq (tabulated-list-get-id) marks)))
+              (tabulated-list-put-tag (char-to-string mark) t)))
+        (user-error "No packages found")))))
 
 (defun package-menu-filter-upgradable ()
   "Filter \"*Packages*\" buffer to show only upgradable packages."
@@ -4411,11 +4512,22 @@ beginning of the line."
             (package-version-join (package-desc-version package-desc))
             (package-desc-summary package-desc))))
 
+(defun package--query-desc (&optional alist)
+  "Query the user for a package or return the package at point.
+The optional argument ALIST must consist of elements with the
+form (PKG-NAME PKG-DESC).  If not specified, it will default to
+`package-alist'."
+  (or (tabulated-list-get-id)
+      (let ((alist (or alist package-alist)))
+        (cadr (assoc (completing-read "Package: " alist nil t)
+                     alist #'string=)))))
+
 (defun package-browse-url (desc &optional secondary)
   "Open the website of the package under point in a browser.
-`browse-url' is used to determine the browser to be used.
-If SECONDARY (interactively, the prefix), use the secondary browser."
-  (interactive (list (tabulated-list-get-id)
+`browse-url' is used to determine the browser to be used.  If
+SECONDARY (interactively, the prefix), use the secondary browser.
+DESC must be a `package-desc' object."
+  (interactive (list (package--query-desc)
                      current-prefix-arg)
                package-menu-mode)
   (unless desc
@@ -4424,9 +4536,47 @@ If SECONDARY (interactively, the prefix), use the 
secondary browser."
     (unless url
       (user-error "No website for %s" (package-desc-name desc)))
     (if secondary
-       (funcall browse-url-secondary-browser-function url)
+        (funcall browse-url-secondary-browser-function url)
       (browse-url url))))
 
+(defun package-maintainers (pkg-desc &optional no-error)
+  "Return an email address for the maintainers of PKG-DESC.
+The email address may contain commas, if there are multiple
+maintainers.  If no maintainers are found, an error will be
+signalled.  If the optional argument NO-ERROR is non-nil no error
+will be signalled in that case."
+  (unless pkg-desc
+    (error "Invalid package description"))
+  (let* ((extras (package-desc-extras pkg-desc))
+         (maint (alist-get :maintainer extras)))
+    (cond
+     ((and (null maint) (null no-error))
+      (user-error "Package has no explicit maintainer"))
+     ((not (null maint))
+      (with-temp-buffer
+        (package--print-email-button maint)
+        (string-trim (substring-no-properties (buffer-string))))))))
+
+(defun package-report-bug (desc)
+  "Prepare a message to send to the maintainers of a package.
+DESC must be a `package-desc' object."
+  (interactive (list (package--query-desc package-alist))
+               package-menu-mode)
+  (let ((maint (package-maintainers desc))
+        (name (symbol-name (package-desc-name desc)))
+        vars)
+    (dolist-with-progress-reporter (group custom-current-group-alist)
+        "Scanning for modified user options..."
+      (dolist (ent (get (cdr group) 'custom-group))
+        (when (and (custom-variable-p (car ent))
+                   (boundp (car ent))
+                   (not (eq (custom--standard-value (car ent))
+                            (default-toplevel-value (car ent))))
+                   (file-in-directory-p (car group) (package-desc-dir desc)))
+          (push (car ent) vars))))
+    (dlet ((reporter-prompt-for-summary-p t))
+      (reporter-submit-bug-report maint name vars))))
+
 ;;;; Introspection
 
 (defun package-get-descriptor (pkg-name)
diff --git a/lisp/emacs-lisp/re-builder.el b/lisp/emacs-lisp/re-builder.el
index 897c35b5b1..0f9b60730f 100644
--- a/lisp/emacs-lisp/re-builder.el
+++ b/lisp/emacs-lisp/re-builder.el
@@ -211,6 +211,7 @@ Except for Lisp syntax this is the same as `reb-regexp'.")
 
 (defvar reb-valid-string ""
   "String in mode line showing validity of RE.")
+(put 'reb-valid-string 'risky-local-variable t)
 
 (defconst reb-buffer "*RE-Builder*"
   "Buffer to use for the RE Builder.")
@@ -308,13 +309,13 @@ Except for Lisp syntax this is the same as `reb-regexp'.")
   "Return t if display is capable of displaying colors."
   (eq 'color (frame-parameter nil 'display-type)))
 
-(defsubst reb-lisp-syntax-p ()
+(defun reb-lisp-syntax-p ()
   "Return non-nil if RE Builder uses `rx' syntax."
   (eq reb-re-syntax 'rx))
 
-(defmacro reb-target-binding (symbol)
+(defun reb-target-value (symbol)
   "Return binding for SYMBOL in the RE Builder target buffer."
-  `(with-current-buffer reb-target-buffer ,symbol))
+  (buffer-local-value symbol reb-target-buffer))
 
 (defun reb-initialize-buffer ()
   "Initialize the current buffer as a RE Builder buffer."
@@ -440,7 +441,7 @@ provided in the Commentary section of this library."
   (interactive)
   (reb-update-regexp)
   (let ((re (with-output-to-string
-             (print (reb-target-binding reb-regexp)))))
+             (print (reb-target-value 'reb-regexp)))))
     (setq re (substring re 1 (1- (length re))))
     (setq re (string-replace "\n" "\\n" re))
     (kill-new re)
@@ -518,12 +519,17 @@ An actual update is only done if the regexp has changed 
or if the
 optional fourth argument FORCE is non-nil."
   (let ((prev-valid reb-valid-string)
        (new-valid
-        (condition-case nil
+        (condition-case err
             (progn
               (when (or (reb-update-regexp) force)
                 (reb-do-update))
               "")
-          (error " *invalid*"))))
+          (error (propertize
+                   (format " %s"
+                           (if (and (consp (cdr err)) (stringp (cadr err)))
+                               (format "%s: %s" (car err) (cadr err))
+                             (car err)))
+                   'face 'font-lock-warning-face)))))
     (setq reb-valid-string new-valid)
     (force-mode-line-update)
 
@@ -554,7 +560,7 @@ optional fourth argument FORCE is non-nil."
         (if reb-subexp-mode
              (format " (subexp %s)" (or reb-subexp-displayed "-"))
           "")
-        (if (not (reb-target-binding case-fold-search))
+        (if (not (reb-target-value 'case-fold-search))
             " Case"
           "")))
   (force-mode-line-update))
@@ -600,7 +606,7 @@ optional fourth argument FORCE is non-nil."
 
 (defun reb-insert-regexp ()
   "Insert current RE."
-  (let ((re (or (reb-target-binding reb-regexp)
+  (let ((re (or (reb-target-value 'reb-regexp)
                (reb-empty-regexp))))
   (cond ((eq reb-re-syntax 'read)
         (print re (current-buffer)))
@@ -608,7 +614,7 @@ optional fourth argument FORCE is non-nil."
         (insert "\n\"" re "\""))
        ;; For the Lisp syntax we need the "source" of the regexp
        ((reb-lisp-syntax-p)
-        (insert (or (reb-target-binding reb-regexp-src)
+        (insert (or (reb-target-value 'reb-regexp-src)
                     (reb-empty-regexp)))))))
 
 (defun reb-cook-regexp (re)
@@ -627,9 +633,8 @@ Return t if the (cooked) expression changed."
        (prog1
            (not (string= oldre re))
          (setq reb-regexp re)
-         ;; Only update the source re for the lisp formats
-         (when (reb-lisp-syntax-p)
-           (setq reb-regexp-src re-src)))))))
+         ;; Update the source re for the Lisp formats.
+         (setq reb-regexp-src re-src))))))
 
 
 ;; And now the real core of the whole thing
@@ -644,7 +649,7 @@ Return t if the (cooked) expression changed."
 (defun reb-update-overlays (&optional subexp)
   "Switch to `reb-target-buffer' and mark all matches of `reb-regexp'.
 If SUBEXP is non-nil mark only the corresponding sub-expressions."
-  (let* ((re (reb-target-binding reb-regexp))
+  (let* ((re (reb-target-value 'reb-regexp))
         (subexps (reb-count-subexps re))
         (matches 0)
         (submatches 0)
diff --git a/lisp/emacs-lisp/smie.el b/lisp/emacs-lisp/smie.el
index 61d52026b3..b86070deef 100644
--- a/lisp/emacs-lisp/smie.el
+++ b/lisp/emacs-lisp/smie.el
@@ -56,7 +56,7 @@
 ;; which includes a kind of tutorial to get started with SMIE:
 ;;
 ;;     SMIE: Weakness is Power!  Auto-indentation with incomplete information
-;;     Stefan Monnier, <Programming> Journal 2020, volumn 5, issue 1.
+;;     Stefan Monnier, <Programming> Journal 2020, volume 5, issue 1.
 ;;     doi: 10.22152/programming-journal.org/2021/5/1
 
 ;; A good background to understand the development (especially the parts
diff --git a/lisp/emacs-lisp/tabulated-list.el 
b/lisp/emacs-lisp/tabulated-list.el
index c01f3fd4fe..206c10a773 100644
--- a/lisp/emacs-lisp/tabulated-list.el
+++ b/lisp/emacs-lisp/tabulated-list.el
@@ -374,7 +374,7 @@ Optional arg POS is a buffer position where to look for a 
fake header;
 defaults to `point-min'."
   (overlays-at (or pos (point-min))))
 
-(defun tabulated-list-revert (&rest ignored)
+(defun tabulated-list-revert (&rest _ignored)
   "The `revert-buffer-function' for `tabulated-list-mode'.
 It runs `tabulated-list-revert-hook', then calls `tabulated-list-print'."
   (interactive)
diff --git a/lisp/emacs-lisp/text-property-search.el 
b/lisp/emacs-lisp/text-property-search.el
index d11980f4f4..d41222bdbf 100644
--- a/lisp/emacs-lisp/text-property-search.el
+++ b/lisp/emacs-lisp/text-property-search.el
@@ -208,8 +208,14 @@ and if a matching region is found, place point at the 
start of the region."
                 (goto-char end)
                 (setq ended t)))))
       ;; End this at the first place the property changes value.
-      (setq end (previous-single-property-change
-                 (point) property nil (point-min)))
+      (setq end
+            (if (and (> (point) (point-min))
+                     (text-property--match-p
+                      value (get-text-property (1- (point)) property)
+                      predicate))
+                (previous-single-property-change (point)
+                                                 property nil (point-min))
+              (point)))
       (goto-char end))
     (make-prop-match :beginning end
                      :end (1+ start)
diff --git a/lisp/erc/ChangeLog.1 b/lisp/erc/ChangeLog.1
index 0ea7ef09aa..89c24758cb 100644
--- a/lisp/erc/ChangeLog.1
+++ b/lisp/erc/ChangeLog.1
@@ -3779,7 +3779,7 @@
        doesn't appear).
 
        * NEWS: Added the information from
-       http://emacswiki.org/cgi-bin/wiki/ErcCvsFeatures and the newer
+       https://emacswiki.org/cgi-bin/wiki/ErcCvsFeatures and the newer
        changes which weren't yet documented on that page.
 
 2005-01-06  Hoan Ton-That  <hoan@ton-that.org>
@@ -8298,7 +8298,7 @@
        it doesn't move point to end-of-buffer in non-ERC buffers.  Fixed
        erc-kill-buffer-function so it doesn't run the erc-kill-server-hook 
hooks if the
        server connection is closed.  Fixed bug 658552, which is described in 
detail at
-       
http://sourceforge.net/tracker/index.php?func=detail&aid=658552&group_id=30118&atid=398125
+       
https://sourceforge.net/tracker/index.php?func=detail&aid=658552&group_id=30118&atid=398125
 
 2002-12-26  Alex Schroeder  <alex@gnu.org>
 
@@ -9372,8 +9372,8 @@
 
 2002-08-14  Mario Lang  <mlang@delysid.org>
 
-       * erc-button.el:
-       Try to be compatible to XEmacs regexp-opt. (Im going to quit this job 
if I find more of those damn differencies
+       * erc-button.el: Try to be compatible to XEmacs regexp-opt.  (I'm
+       going to quit this job if I find more of those damn differences.)
 
        * debian/README.Debian, debian/scripts/install:
        * Added info to README.Debian
diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el
index df9efe4b0c..026b34849a 100644
--- a/lisp/erc/erc-backend.el
+++ b/lisp/erc/erc-backend.el
@@ -99,24 +99,117 @@
 ;;; Code:
 
 (eval-when-compile (require 'cl-lib))
-;; There's a fairly strong mutual dependency between erc.el and erc-backend.el.
-;; Luckily, erc.el does not need erc-backend.el for macroexpansion whereas the
-;; reverse is true:
-(require 'erc)
+(require 'erc-common)
+
+(defvar erc--target)
+(defvar erc-auto-query)
+(defvar erc-channel-list)
+(defvar erc-channel-users)
+(defvar erc-default-nicks)
+(defvar erc-default-recipients)
+(defvar erc-format-nick-function)
+(defvar erc-format-query-as-channel-p)
+(defvar erc-hide-prompt)
+(defvar erc-input-marker)
+(defvar erc-insert-marker)
+(defvar erc-invitation)
+(defvar erc-join-buffer)
+(defvar erc-kill-buffer-on-part)
+(defvar erc-kill-server-buffer-on-quit)
+(defvar erc-log-p)
+(defvar erc-minibuffer-ignored)
+(defvar erc-networks--id)
+(defvar erc-nick)
+(defvar erc-nick-change-attempt-count)
+(defvar erc-prompt-for-channel-key)
+(defvar erc-prompt-hidden)
+(defvar erc-reuse-buffers)
+(defvar erc-verbose-server-ping)
+(defvar erc-whowas-on-nosuchnick)
+
+(declare-function erc--open-target "erc" (target))
+(declare-function erc--target-from-string "erc" (string))
+(declare-function erc-active-buffer "erc" nil)
+(declare-function erc-add-default-channel "erc" (channel))
+(declare-function erc-banlist-update "erc" (proc parsed))
+(declare-function erc-buffer-filter "erc" (predicate &optional proc))
+(declare-function erc-buffer-list-with-nick "erc" (nick proc))
+(declare-function erc-channel-begin-receiving-names "erc" nil)
+(declare-function erc-channel-end-receiving-names "erc" nil)
+(declare-function erc-channel-p "erc" (channel))
+(declare-function erc-channel-receive-names "erc" (names-string))
+(declare-function erc-cmd-JOIN "erc" (channel &optional key))
+(declare-function erc-connection-established "erc" (proc parsed))
+(declare-function erc-current-nick "erc" nil)
+(declare-function erc-current-nick-p "erc" (nick))
+(declare-function erc-current-time "erc" (&optional specified-time))
+(declare-function erc-default-target "erc" nil)
+(declare-function erc-delete-default-channel "erc" (channel &optional buffer))
+(declare-function erc-display-error-notice "erc" (parsed string))
+(declare-function erc-display-server-message "erc" (_proc parsed))
+(declare-function erc-emacs-time-to-erc-time "erc" (&optional specified-time))
+(declare-function erc-format-message "erc" (msg &rest args))
+(declare-function erc-format-privmessage "erc" (nick msg privp msgp))
+(declare-function erc-get-buffer "erc" (target &optional proc))
+(declare-function erc-handle-login "erc" nil)
+(declare-function erc-handle-user-status-change "erc" (type nlh &optional l))
+(declare-function erc-ignored-reply-p "erc" (msg tgt proc))
+(declare-function erc-ignored-user-p "erc" (spec))
+(declare-function erc-is-message-ctcp-and-not-action-p "erc" (message))
+(declare-function erc-is-message-ctcp-p "erc" (message))
+(declare-function erc-log-irc-protocol "erc" (string &optional outbound))
+(declare-function erc-login "erc" nil)
+(declare-function erc-make-notice "erc" (message))
+(declare-function erc-network "erc-networks" nil)
+(declare-function erc-networks--id-given "erc-networks" (arg &rest args))
+(declare-function erc-networks--id-reload "erc-networks" (arg &rest args))
+(declare-function erc-nickname-in-use "erc" (nick reason))
+(declare-function erc-parse-user "erc" (string))
+(declare-function erc-process-away "erc" (proc away-p))
+(declare-function erc-process-ctcp-query "erc" (proc parsed nick login host))
+(declare-function erc-query-buffer-p "erc" (&optional buffer))
+(declare-function erc-remove-channel-member "erc" (channel nick))
+(declare-function erc-remove-channel-users "erc" nil)
+(declare-function erc-remove-user "erc" (nick))
+(declare-function erc-sec-to-time "erc" (ns))
+(declare-function erc-server-buffer "erc" nil)
+(declare-function erc-set-active-buffer "erc" (buffer))
+(declare-function erc-set-current-nick "erc" (nick))
+(declare-function erc-set-modes "erc" (tgt mode-string))
+(declare-function erc-time-diff "erc" (t1 t2))
+(declare-function erc-trim-string "erc" (s))
+(declare-function erc-update-mode-line "erc" (&optional buffer))
+(declare-function erc-update-mode-line-buffer "erc" (buffer))
+(declare-function erc-wash-quit-reason "erc" (reason nick login host))
+
+(declare-function erc-display-message "erc"
+                  (parsed type buffer msg &rest args))
+(declare-function erc-get-buffer-create "erc"
+                  (server port target &optional tgt-info id))
+(declare-function erc-process-ctcp-reply "erc"
+                  (proc parsed nick login host msg))
+(declare-function erc-update-channel-topic "erc"
+                  (channel topic &optional modify))
+(declare-function erc-update-modes "erc"
+                  (tgt mode-string &optional _nick _host _login))
+(declare-function erc-update-user-nick "erc"
+                  (nick &optional new-nick host login full-name info))
+(declare-function erc-open "erc"
+                  (&optional server port nick full-name connect passwd tgt-list
+                             channel process client-certificate user id))
+(declare-function erc-update-channel-member "erc"
+                  (channel nick new-nick
+                           &optional add voice halfop op admin owner host
+                           login full-name info update-message-time))
 
 ;;;; Variables and options
 
+(defvar-local erc-session-password nil
+  "The password used for the current session.")
+
 (defvar erc-server-responses (make-hash-table :test #'equal)
   "Hash table mapping server responses to their handler hooks.")
 
-(cl-defstruct (erc-response (:conc-name erc-response.))
-  (unparsed "" :type string)
-  (sender "" :type string)
-  (command "" :type string)
-  (command-args '() :type list)
-  (contents "" :type string)
-  (tags '() :type list))
-
 ;;; User data
 
 (defvar-local erc-server-current-nick nil
@@ -1662,16 +1755,6 @@ Then display the welcome message."
          (split-string value ",")
        (list value)))))
 
-(defmacro erc--with-memoization (table &rest forms)
-  "Adapter to be migrated to erc-compat."
-  (declare (indent defun))
-  `(cond
-    ((fboundp 'with-memoization)
-     (with-memoization ,table ,@forms)) ; 29.1
-    ((fboundp 'cl--generic-with-memoization)
-     (cl--generic-with-memoization ,table ,@forms))
-    (t ,@forms)))
-
 (defun erc--get-isupport-entry (key &optional single)
   "Return an item for \"ISUPPORT\" token KEY, a symbol.
 When a lookup fails return nil.  Otherwise return a list whose
@@ -1681,7 +1764,7 @@ ambiguous and only useful for tokens supporting a single
 primitive value."
   (if-let* ((table (or erc--isupport-params
                        (erc-with-server-buffer erc--isupport-params)))
-            (value (erc--with-memoization (gethash key table)
+            (value (erc-compat--with-memoization (gethash key table)
                      (when-let ((v (assoc (symbol-name key)
                                           erc-server-parameters)))
                        (if (cdr v)
diff --git a/lisp/erc/erc-capab.el b/lisp/erc/erc-capab.el
index c590b45fd2..8759282a2a 100644
--- a/lisp/erc/erc-capab.el
+++ b/lisp/erc/erc-capab.el
@@ -62,7 +62,7 @@
 ;; You can customize the prefix and the face used to display it,
 ;; `erc-capab-identify-unidentified'.  If the value of
 ;; `erc-capab-identify-prefix' is nil or you disable this module (see
-;; `erc-capab-identify-disable'), no prefix will be inserted, but the
+;; `erc-capab-identify-activated'), no prefix will be inserted, but the
 ;; flag sent by the server will still be stripped.
 
 ;;; Code:
diff --git a/lisp/erc/erc-common.el b/lisp/erc/erc-common.el
new file mode 100644
index 0000000000..d8aac36eab
--- /dev/null
+++ b/lisp/erc/erc-common.el
@@ -0,0 +1,271 @@
+;;; erc-common.el --- Macros and types for ERC  -*- lexical-binding:t -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+;;
+;; Maintainer: Amin Bandali <bandali@gnu.org>, F. Jason Park <jp@neverwas.me>
+;; Keywords: comm, IRC, chat, client, internet
+;;
+;; 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:
+;;; Code:
+
+(eval-when-compile (require 'cl-lib) (require 'subr-x))
+(require 'erc-compat)
+
+(defvar erc--casemapping-rfc1459)
+(defvar erc--casemapping-rfc1459-strict)
+(defvar erc-channel-users)
+(defvar erc-dbuf)
+(defvar erc-log-p)
+(defvar erc-server-users)
+(defvar erc-session-server)
+
+(declare-function erc--get-isupport-entry "erc-backend" (key &optional single))
+(declare-function erc-get-buffer "erc" (target &optional proc))
+(declare-function erc-server-buffer "erc" nil)
+
+(cl-defstruct erc-input
+  string insertp sendp)
+
+(cl-defstruct (erc--input-split (:include erc-input))
+  lines cmdp)
+
+(cl-defstruct (erc-server-user (:type vector) :named)
+  ;; User data
+  nickname host login full-name info
+  ;; Buffers
+  ;;
+  ;; This is an alist of the form (BUFFER . CHANNEL-DATA), where
+  ;; CHANNEL-DATA is either nil or an erc-channel-user struct.
+  (buffers nil))
+
+(cl-defstruct (erc-channel-user (:type vector) :named)
+  voice halfop op admin owner
+  ;; Last message time (in the form of the return value of
+  ;; (current-time)
+  ;;
+  ;; This is useful for ordered name completion.
+  (last-message-time nil))
+
+(cl-defstruct erc--target
+  (string "" :type string :documentation "Received name of target.")
+  (symbol nil :type symbol :documentation "Case-mapped name as symbol."))
+
+;; At some point, it may make sense to add a query type with an
+;; account field, which may help support reassociation across
+;; reconnects and nick changes (likely requires v3 extensions).
+;;
+;; These channel variants should probably take on a `joined' field to
+;; track "joinedness", which `erc-server-JOIN', `erc-server-PART',
+;; etc. should toggle.  Functions like `erc--current-buffer-joined-p'
+;; may find it useful.
+
+(cl-defstruct (erc--target-channel (:include erc--target)))
+(cl-defstruct (erc--target-channel-local (:include erc--target-channel)))
+
+(cl-defstruct (erc-response (:conc-name erc-response.))
+  (unparsed "" :type string)
+  (sender "" :type string)
+  (command "" :type string)
+  (command-args '() :type list)
+  (contents "" :type string)
+  (tags '() :type list))
+
+(defmacro define-erc-module (name alias doc enable-body disable-body
+                                  &optional local-p)
+  "Define a new minor mode using ERC conventions.
+Symbol NAME is the name of the module.
+Symbol ALIAS is the alias to use, or nil.
+DOC is the documentation string to use for the minor mode.
+ENABLE-BODY is a list of expressions used to enable the mode.
+DISABLE-BODY is a list of expressions used to disable the mode.
+If LOCAL-P is non-nil, the mode will be created as a buffer-local
+mode, rather than a global one.
+
+This will define a minor mode called erc-NAME-mode, possibly
+an alias erc-ALIAS-mode, as well as the helper functions
+erc-NAME-enable, and erc-NAME-disable.
+
+Example:
+
+  ;;;###autoload(autoload \\='erc-replace-mode \"erc-replace\")
+  (define-erc-module replace nil
+    \"This mode replaces incoming text according to `erc-replace-alist'.\"
+    ((add-hook \\='erc-insert-modify-hook
+               #\\='erc-replace-insert))
+    ((remove-hook \\='erc-insert-modify-hook
+                  #\\='erc-replace-insert)))"
+  (declare (doc-string 3) (indent defun))
+  (let* ((sn (symbol-name name))
+         (mode (intern (format "erc-%s-mode" (downcase sn))))
+         (group (intern (format "erc-%s" (downcase sn))))
+         (enable (intern (format "erc-%s-enable" (downcase sn))))
+         (disable (intern (format "erc-%s-disable" (downcase sn)))))
+    `(progn
+       (define-minor-mode
+         ,mode
+         ,(format "Toggle ERC %S mode.
+With a prefix argument ARG, enable %s if ARG is positive,
+and disable it otherwise.  If called from Lisp, enable the mode
+if ARG is omitted or nil.
+%s" name name doc)
+         ;; FIXME: We don't know if this group exists, so this `:group' may
+         ;; actually just silence a valid warning about the fact that the var
+         ;; is not associated with any group.
+         :global ,(not local-p) :group (quote ,group)
+         (if ,mode
+             (,enable)
+           (,disable)))
+       (defun ,enable ()
+         ,(format "Enable ERC %S mode."
+                  name)
+         (interactive)
+         (add-to-list 'erc-modules (quote ,name))
+         (setq ,mode t)
+         ,@enable-body)
+       (defun ,disable ()
+         ,(format "Disable ERC %S mode."
+                  name)
+         (interactive)
+         (setq erc-modules (delq (quote ,name) erc-modules))
+         (setq ,mode nil)
+         ,@disable-body)
+       ,(when (and alias (not (eq name alias)))
+          `(defalias
+             ',(intern
+                (format "erc-%s-mode"
+                        (downcase (symbol-name alias))))
+             #',mode))
+       ;; For find-function and find-variable.
+       (put ',mode    'definition-name ',name)
+       (put ',enable  'definition-name ',name)
+       (put ',disable 'definition-name ',name))))
+
+(defmacro erc-with-buffer (spec &rest body)
+  "Execute BODY in the buffer associated with SPEC.
+
+SPEC should have the form
+
+ (TARGET [PROCESS])
+
+If TARGET is a buffer, use it.  Otherwise, use the buffer
+matching TARGET in the process specified by PROCESS.
+
+If PROCESS is nil, use the current `erc-server-process'.
+See `erc-get-buffer' for details.
+
+See also `with-current-buffer'.
+
+\(fn (TARGET [PROCESS]) BODY...)"
+  (declare (indent 1) (debug ((form &optional form) body)))
+  (let ((buf (make-symbol "buf"))
+        (proc (make-symbol "proc"))
+        (target (make-symbol "target"))
+        (process (make-symbol "process")))
+    `(let* ((,target ,(car spec))
+            (,process ,(cadr spec))
+            (,buf (if (bufferp ,target)
+                      ,target
+                    (let ((,proc (or ,process
+                                     (and (processp erc-server-process)
+                                          erc-server-process))))
+                      (if (and ,target ,proc)
+                          (erc-get-buffer ,target ,proc))))))
+       (when (buffer-live-p ,buf)
+         (with-current-buffer ,buf
+           ,@body)))))
+
+(defmacro erc-with-server-buffer (&rest body)
+  "Execute BODY in the current ERC server buffer.
+If no server buffer exists, return nil."
+  (declare (indent 0) (debug (body)))
+  (let ((buffer (make-symbol "buffer")))
+    `(let ((,buffer (erc-server-buffer)))
+       (when (buffer-live-p ,buffer)
+         (with-current-buffer ,buffer
+           ,@body)))))
+
+(defmacro erc-with-all-buffers-of-server (process pred &rest forms)
+  "Execute FORMS in all buffers which have same process as this server.
+FORMS will be evaluated in all buffers having the process PROCESS and
+where PRED matches or in all buffers of the server process if PRED is
+nil."
+  (declare (indent 2) (debug (form form body)))
+  (macroexp-let2 nil pred pred
+    `(erc-buffer-filter (lambda ()
+                          (when (or (not ,pred) (funcall ,pred))
+                            ,@forms))
+                        ,process)))
+
+(defun erc-log-aux (string)
+  "Do the debug logging of STRING."
+  (let ((cb (current-buffer))
+        (point 1)
+        (was-eob nil)
+        (session-buffer (erc-server-buffer)))
+    (if session-buffer
+        (progn
+          (set-buffer session-buffer)
+          (if (not (and erc-dbuf (bufferp erc-dbuf) (buffer-live-p erc-dbuf)))
+              (progn
+                (setq erc-dbuf (get-buffer-create
+                                (concat "*ERC-DEBUG: "
+                                        erc-session-server "*")))))
+          (set-buffer erc-dbuf)
+          (setq point (point))
+          (setq was-eob (eobp))
+          (goto-char (point-max))
+          (insert (concat "** " string "\n"))
+          (if was-eob (goto-char (point-max))
+            (goto-char point))
+          (set-buffer cb))
+      (message "ERC: ** %s" string))))
+
+(define-inline erc-log (string)
+  "Logs STRING if logging is on (see `erc-log-p')."
+  (inline-quote
+   (when erc-log-p
+     (erc-log-aux ,string))))
+
+(defun erc-downcase (string)
+  "Return a downcased copy of STRING with properties.
+Use the CASEMAPPING ISUPPORT parameter to determine the style."
+  (let* ((mapping (erc--get-isupport-entry 'CASEMAPPING 'single))
+         (inhibit-read-only t))
+    (if (equal mapping "ascii")
+        (downcase string)
+      (with-temp-buffer
+        (insert string)
+        (translate-region (point-min) (point-max)
+                          (if (equal mapping "rfc1459-strict")
+                              erc--casemapping-rfc1459-strict
+                            erc--casemapping-rfc1459))
+        (buffer-string)))))
+
+(define-inline erc-get-channel-user (nick)
+  "Find NICK in the current buffer's `erc-channel-users' hash table."
+  (inline-quote (gethash (erc-downcase ,nick) erc-channel-users)))
+
+(define-inline erc-get-server-user (nick)
+  "Find NICK in the current server's `erc-server-users' hash table."
+  (inline-letevals (nick)
+    (inline-quote (erc-with-server-buffer
+                    (gethash (erc-downcase ,nick) erc-server-users)))))
+
+(provide 'erc-common)
+
+;;; erc-common.el ends here
diff --git a/lisp/erc/erc-compat.el b/lisp/erc/erc-compat.el
index 8a00e711ac..03bd8f1352 100644
--- a/lisp/erc/erc-compat.el
+++ b/lisp/erc/erc-compat.el
@@ -156,6 +156,18 @@ If START or END is negative, it counts from the end."
                 (setq i (1+ i) start (1+ start)))
               res))))))
 
+
+;;;; Misc 29.1
+
+(defmacro erc-compat--with-memoization (table &rest forms)
+  (declare (indent defun))
+  (cond
+   ((fboundp 'with-memoization)
+    `(with-memoization ,table ,@forms)) ; 29.1
+   ((fboundp 'cl--generic-with-memoization)
+    `(cl--generic-with-memoization ,table ,@forms))
+   (t `(progn ,@forms))))
+
 (provide 'erc-compat)
 
 ;;; erc-compat.el ends here
diff --git a/lisp/erc/erc-dcc.el b/lisp/erc/erc-dcc.el
index 90a10766c4..ebeab921fb 100644
--- a/lisp/erc/erc-dcc.el
+++ b/lisp/erc/erc-dcc.el
@@ -411,8 +411,11 @@ where FOO is one of CLOSE, GET, SEND, LIST, CHAT, etc."
   "Provide completion for the /DCC command."
   (pcomplete-here (append '("chat" "close" "get" "list")
                           (when (fboundp 'make-network-process) '("send"))))
+  (when (equal "get" (downcase (pcomplete-arg 1)))
+    (pcomplete-opt "ts")
+    (pcomplete-opt (if (equal "-s" (pcomplete-arg 'first 2)) "t" "s")))
   (pcomplete-here
-   (pcase (intern (downcase (pcomplete-arg 1)))
+   (pcase (intern (downcase (pcomplete-arg 'first 1)))
      ('chat (mapcar (lambda (elt) (plist-get elt :nick))
                     (cl-remove-if-not
                      (lambda (elt)
@@ -428,7 +431,7 @@ where FOO is one of CLOSE, GET, SEND, LIST, CHAT, etc."
                     erc-dcc-list)))
      ('send (pcomplete-erc-all-nicks))))
   (pcomplete-here
-   (pcase (intern (downcase (pcomplete-arg 2)))
+   (pcase (intern (downcase (pcomplete-arg 'first 1)))
      ('get (mapcar (lambda (elt) (plist-get elt :file))
                    (cl-remove-if-not
                     (lambda (elt)
diff --git a/lisp/erc/erc-goodies.el b/lisp/erc/erc-goodies.el
index 8fef23945d..59b5f01f23 100644
--- a/lisp/erc/erc-goodies.el
+++ b/lisp/erc/erc-goodies.el
@@ -29,10 +29,23 @@
 
 ;;; Code:
 
-(require 'erc)
-
 ;;; Imenu support
 
+(require 'erc-common)
+
+(defvar erc-controls-highlight-regexp)
+(defvar erc-controls-remove-regexp)
+(defvar erc-input-marker)
+(defvar erc-insert-marker)
+(defvar erc-server-process)
+(defvar erc-modules)
+(defvar erc-log-p)
+
+(declare-function erc-buffer-list "erc" (&optional predicate proc))
+(declare-function erc-error "erc" (&rest args))
+(declare-function erc-extract-command-from-line "erc" (line))
+(declare-function erc-beg-of-input-line "erc" nil)
+
 (defun erc-imenu-setup ()
   "Setup Imenu support in an ERC buffer."
   (setq-local imenu-create-index-function #'erc-create-imenu-index))
diff --git a/lisp/erc/erc-networks.el b/lisp/erc/erc-networks.el
index 2c8f8fb72b..dba6ead073 100644
--- a/lisp/erc/erc-networks.el
+++ b/lisp/erc/erc-networks.el
@@ -39,8 +39,32 @@
 
 ;;; Code:
 
-(require 'erc)
 (eval-when-compile (require 'cl-lib))
+(require 'erc-common)
+
+(defvar erc--target)
+(defvar erc-insert-marker)
+(defvar erc-kill-buffer-hook)
+(defvar erc-kill-server-hook)
+(defvar erc-modules)
+(defvar erc-rename-buffers)
+(defvar erc-reuse-buffers)
+(defvar erc-server-announced-name)
+(defvar erc-server-connected)
+(defvar erc-server-parameters)
+(defvar erc-server-process)
+(defvar erc-session-server)
+
+(declare-function erc--default-target "erc" nil)
+(declare-function erc--get-isupport-entry "erc-backend" (key &optional single))
+(declare-function erc-buffer-filter "erc" (predicate &optional proc))
+(declare-function erc-current-nick "erc" nil)
+(declare-function erc-display-error-notice "erc" (parsed string))
+(declare-function erc-error "erc" (&rest args))
+(declare-function erc-get-buffer "erc" (target &optional proc))
+(declare-function erc-server-buffer "erc" nil)
+(declare-function erc-server-process-alive "erc-backend" (&optional buffer))
+(declare-function erc-set-active-buffer "erc" (buffer))
 
 ;; Variables
 
@@ -813,7 +837,7 @@ This may have originated from an `:id' arg to entry-point 
commands
   (erc-networks--id-symbol nid))
 
 (cl-generic-define-context-rewriter erc-obsolete-var (var spec)
-  `((with-suppressed-warnings ((obsolete ,var)) ,var) ,spec))
+  `((with-suppressed-warnings ((obsolete ,var) (free-vars ,var)) ,var) ,spec))
 
 ;; As a catch-all, derive the symbol from the unquoted printed repr.
 (cl-defgeneric erc-networks--id-create (id)
@@ -1256,7 +1280,7 @@ Signal an error when the network cannot be determined."
       ;; but aren't being proxied through to a real network.  The
       ;; service may send a 422 but no NETWORK param (or *any* 005s).
       (let ((m (concat "Failed to determine network. Please set entry for "
-                       erc-server-announced-name " in `erc-network-alist'.")))
+                       erc-server-announced-name " in `erc-networks-alist'.")))
         (erc-display-error-notice parsed m)
         (erc-error "Failed to determine network"))) ; beep
     (setq erc-network name))
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index db39e341b2..6b14cf87e2 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -60,6 +60,9 @@
 
 (load "erc-loaddefs" 'noerror 'nomessage)
 
+(require 'erc-networks)
+(require 'erc-goodies)
+(require 'erc-backend)
 (require 'cl-lib)
 (require 'format-spec)
 (require 'pp)
@@ -69,8 +72,6 @@
 (require 'iso8601)
 (eval-when-compile (require 'subr-x))
 
-(require 'erc-compat)
-
 (defconst erc-version "5.4.1"
   "This version of ERC.")
 
@@ -132,29 +133,12 @@
   "Running scripts at startup and with /LOAD."
   :group 'erc)
 
-;; Defined in erc-backend
-(defvar erc--server-last-reconnect-count)
-(defvar erc--server-reconnecting)
-(defvar erc-channel-members-changed-hook)
-(defvar erc-network)
-(defvar erc-networks--id)
-(defvar erc-server-367-functions)
-(defvar erc-server-announced-name)
-(defvar erc-server-connect-function)
-(defvar erc-server-connected)
-(defvar erc-server-current-nick)
-(defvar erc-server-lag)
-(defvar erc-server-last-sent-time)
-(defvar erc-server-process)
-(defvar erc-server-quitting)
-(defvar erc-server-reconnect-count)
-(defvar erc-server-reconnecting)
-(defvar erc-session-client-certificate)
-(defvar erc-session-connector)
-(defvar erc-session-port)
-(defvar erc-session-server)
-(defvar erc-session-user-full-name)
-(defvar erc-session-username)
+;; Forward declarations
+(defvar erc-message-parsed)
+
+(defvar tabbar--local-hlf)
+(defvar motif-version-string)
+(defvar gtk-version-string)
 
 ;; tunable connection and authentication parameters
 
@@ -349,9 +333,6 @@ A typical value would be \((\"#emacs\" \"QUIT\" \"JOIN\")
   :group 'erc-ignore
   :type 'erc-message-type)
 
-(defvar-local erc-session-password nil
-  "The password used for the current session.")
-
 (defcustom erc-disconnected-hook nil
   "Run this hook with arguments (NICK IP REASON) when disconnected.
 This happens before automatic reconnection.  Note, that
@@ -436,69 +417,14 @@ It associates nicknames with `erc-server-user' struct 
instances.")
    '((?\[ . ?\{) (?\] . ?\}) (?\\ . ?\|))
    (mapcar (lambda (c) (cons c (+ c 32))) "ABCDEFGHIJKLMNOPQRSTUVWXYZ")))
 
-(defun erc-downcase (string)
-  "Return a downcased copy of STRING with properties.
-Use the CASEMAPPING ISUPPORT parameter to determine the style."
-  (let* ((mapping (erc--get-isupport-entry 'CASEMAPPING 'single))
-         (inhibit-read-only t))
-    (if (equal mapping "ascii")
-        (downcase string)
-      (with-temp-buffer
-        (insert string)
-        (translate-region (point-min) (point-max)
-                          (if (equal mapping "rfc1459-strict")
-                              erc--casemapping-rfc1459-strict
-                            erc--casemapping-rfc1459))
-        (buffer-string)))))
-
-(defmacro erc-with-server-buffer (&rest body)
-  "Execute BODY in the current ERC server buffer.
-If no server buffer exists, return nil."
-  (declare (indent 0) (debug (body)))
-  (let ((buffer (make-symbol "buffer")))
-    `(let ((,buffer (erc-server-buffer)))
-       (when (buffer-live-p ,buffer)
-         (with-current-buffer ,buffer
-           ,@body)))))
-
-(cl-defstruct (erc-server-user (:type vector) :named)
-  ;; User data
-  nickname host login full-name info
-  ;; Buffers
-  ;;
-  ;; This is an alist of the form (BUFFER . CHANNEL-DATA), where
-  ;; CHANNEL-DATA is either nil or an erc-channel-user struct.
-  (buffers nil)
-  )
-
-(cl-defstruct (erc-channel-user (:type vector) :named)
-  voice halfop op admin owner
-  ;; Last message time (in the form of the return value of
-  ;; (current-time)
-  ;;
-  ;; This is useful for ordered name completion.
-  (last-message-time nil))
-
-(define-inline erc-get-channel-user (nick)
-  "Find NICK in the current buffer's `erc-channel-users' hash table."
-  (inline-quote (gethash (erc-downcase ,nick) erc-channel-users)))
-
-(define-inline erc-get-server-user (nick)
-  "Find NICK in the current server's `erc-server-users' hash table."
-  (inline-letevals (nick)
-    (inline-quote (erc-with-server-buffer
-                   (gethash (erc-downcase ,nick) erc-server-users)))))
-
-(define-inline erc-add-server-user (nick user)
+(defun erc-add-server-user (nick user)
   "This function is for internal use only.
 
 Adds USER with nickname NICK to the `erc-server-users' hash table."
-  (inline-letevals (nick user)
-    (inline-quote
-     (erc-with-server-buffer
-       (puthash (erc-downcase ,nick) ,user erc-server-users)))))
+  (erc-with-server-buffer
+    (puthash (erc-downcase nick) user erc-server-users)))
 
-(define-inline erc-remove-server-user (nick)
+(defun erc-remove-server-user (nick)
   "This function is for internal use only.
 
 Removes the user with nickname NICK from the `erc-server-users'
@@ -506,10 +432,8 @@ hash table.  This user is not removed from the
 `erc-channel-users' lists of other buffers.
 
 See also: `erc-remove-user'."
-  (inline-letevals (nick)
-    (inline-quote
-     (erc-with-server-buffer
-       (remhash (erc-downcase ,nick) erc-server-users)))))
+  (erc-with-server-buffer
+    (remhash (erc-downcase nick) erc-server-users)))
 
 (defun erc-change-user-nickname (user new-nick)
   "This function is for internal use only.
@@ -580,55 +504,45 @@ Removes all users in the current channel.  This is called 
by
              erc-channel-users)
     (clrhash erc-channel-users)))
 
-(define-inline erc-channel-user-owner-p (nick)
+(defun erc-channel-user-owner-p (nick)
   "Return non-nil if NICK is an owner of the current channel."
-  (inline-letevals (nick)
-    (inline-quote
-     (and ,nick
-         (hash-table-p erc-channel-users)
-         (let ((cdata (erc-get-channel-user ,nick)))
-           (and cdata (cdr cdata)
-                (erc-channel-user-owner (cdr cdata))))))))
-
-(define-inline erc-channel-user-admin-p (nick)
+  (and nick
+       (hash-table-p erc-channel-users)
+       (let ((cdata (erc-get-channel-user nick)))
+         (and cdata (cdr cdata)
+              (erc-channel-user-owner (cdr cdata))))))
+
+(defun erc-channel-user-admin-p (nick)
   "Return non-nil if NICK is an admin in the current channel."
-  (inline-letevals (nick)
-    (inline-quote
-     (and ,nick
+  (and nick
        (hash-table-p erc-channel-users)
-       (let ((cdata (erc-get-channel-user ,nick)))
+       (let ((cdata (erc-get-channel-user nick)))
          (and cdata (cdr cdata)
-              (erc-channel-user-admin (cdr cdata))))))))
+              (erc-channel-user-admin (cdr cdata))))))
 
-(define-inline erc-channel-user-op-p (nick)
+(defun erc-channel-user-op-p (nick)
   "Return non-nil if NICK is an operator in the current channel."
-  (inline-letevals (nick)
-    (inline-quote
-     (and ,nick
+  (and nick
        (hash-table-p erc-channel-users)
-       (let ((cdata (erc-get-channel-user ,nick)))
+       (let ((cdata (erc-get-channel-user nick)))
          (and cdata (cdr cdata)
-              (erc-channel-user-op (cdr cdata))))))))
+              (erc-channel-user-op (cdr cdata))))))
 
-(define-inline erc-channel-user-halfop-p (nick)
+(defun erc-channel-user-halfop-p (nick)
   "Return non-nil if NICK is a half-operator in the current channel."
-  (inline-letevals (nick)
-    (inline-quote
-     (and ,nick
+  (and nick
        (hash-table-p erc-channel-users)
-       (let ((cdata (erc-get-channel-user ,nick)))
+       (let ((cdata (erc-get-channel-user nick)))
          (and cdata (cdr cdata)
-              (erc-channel-user-halfop (cdr cdata))))))))
+              (erc-channel-user-halfop (cdr cdata))))))
 
-(define-inline erc-channel-user-voice-p (nick)
+(defun erc-channel-user-voice-p (nick)
   "Return non-nil if NICK has voice in the current channel."
-  (inline-letevals (nick)
-    (inline-quote
-     (and ,nick
+  (and nick
        (hash-table-p erc-channel-users)
-       (let ((cdata (erc-get-channel-user ,nick)))
+       (let ((cdata (erc-get-channel-user nick)))
          (and cdata (cdr cdata)
-              (erc-channel-user-voice (cdr cdata))))))))
+              (erc-channel-user-voice (cdr cdata))))))
 
 (defun erc-get-channel-user-list ()
   "Return a list of users in the current channel.
@@ -1377,96 +1291,6 @@ See also `erc-show-my-nick'."
 
 (defvar-local erc-dbuf nil)
 
-(defmacro define-erc-module (name alias doc enable-body disable-body
-                                  &optional local-p)
-  "Define a new minor mode using ERC conventions.
-Symbol NAME is the name of the module.
-Symbol ALIAS is the alias to use, or nil.
-DOC is the documentation string to use for the minor mode.
-ENABLE-BODY is a list of expressions used to enable the mode.
-DISABLE-BODY is a list of expressions used to disable the mode.
-If LOCAL-P is non-nil, the mode will be created as a buffer-local
-mode, rather than a global one.
-
-This will define a minor mode called erc-NAME-mode, possibly
-an alias erc-ALIAS-mode, as well as the helper functions
-erc-NAME-enable, and erc-NAME-disable.
-
-Example:
-
-  ;;;###autoload(autoload \\='erc-replace-mode \"erc-replace\")
-  (define-erc-module replace nil
-    \"This mode replaces incoming text according to `erc-replace-alist'.\"
-    ((add-hook \\='erc-insert-modify-hook
-               #\\='erc-replace-insert))
-    ((remove-hook \\='erc-insert-modify-hook
-                  #\\='erc-replace-insert)))"
-  (declare (doc-string 3) (indent defun))
-  (let* ((sn (symbol-name name))
-         (mode (intern (format "erc-%s-mode" (downcase sn))))
-         (group (intern (format "erc-%s" (downcase sn))))
-         (enable (intern (format "erc-%s-enable" (downcase sn))))
-         (disable (intern (format "erc-%s-disable" (downcase sn)))))
-    `(progn
-       (define-minor-mode
-        ,mode
-        ,(format "Toggle ERC %S mode.
-With a prefix argument ARG, enable %s if ARG is positive,
-and disable it otherwise.  If called from Lisp, enable the mode
-if ARG is omitted or nil.
-%s" name name doc)
-        ;; FIXME: We don't know if this group exists, so this `:group' may
-        ;; actually just silence a valid warning about the fact that the var
-        ;; is not associated with any group.
-        :global ,(not local-p) :group (quote ,group)
-        (if ,mode
-            (,enable)
-          (,disable)))
-       (defun ,enable ()
-         ,(format "Enable ERC %S mode."
-                  name)
-         (interactive)
-         (add-to-list 'erc-modules (quote ,name))
-         (setq ,mode t)
-         ,@enable-body)
-       (defun ,disable ()
-         ,(format "Disable ERC %S mode."
-                  name)
-         (interactive)
-         (setq erc-modules (delq (quote ,name) erc-modules))
-         (setq ,mode nil)
-         ,@disable-body)
-       ,(when (and alias (not (eq name alias)))
-          `(defalias
-             ',(intern
-                (format "erc-%s-mode"
-                        (downcase (symbol-name alias))))
-             #',mode))
-       ;; For find-function and find-variable.
-       (put ',mode    'definition-name ',name)
-       (put ',enable  'definition-name ',name)
-       (put ',disable 'definition-name ',name))))
-
-;; The rationale for favoring inheritance here (nicer dispatch) is
-;; kinda flimsy since there aren't yet any actual methods.
-
-(cl-defstruct erc--target
-  (string "" :type string :documentation "Received name of target.")
-  (symbol nil :type symbol :documentation "Case-mapped name as symbol."))
-
-;; These should probably take on a `joined' field to track joinedness,
-;; which should be toggled by `erc-server-JOIN', `erc-server-PART',
-;; etc.  Functions like `erc--current-buffer-joined-p' (bug#48598) may
-;; find it useful.
-
-(cl-defstruct (erc--target-channel (:include erc--target)))
-
-(cl-defstruct (erc--target-channel-local (:include erc--target-channel)))
-
-;; At some point, it may make sense to add a query type with an
-;; account field, which may help support reassociation across
-;; reconnects and nick changes (likely requires v3 extensions).
-
 (defun erc--target-from-string (string)
   "Construct an `erc--target' variant from STRING."
   (funcall (if (erc-channel-p string)
@@ -1516,12 +1340,6 @@ capabilities."
     (add-hook hook fun nil t)
     fun))
 
-(define-inline erc-log (string)
-  "Logs STRING if logging is on (see `erc-log-p')."
-  (inline-quote
-   (when erc-log-p
-     (erc-log-aux ,string))))
-
 (defun erc-server-buffer ()
   "Return the server buffer for the current buffer's process.
 The buffer-local variable `erc-server-process' is used to find
@@ -1577,29 +1395,7 @@ If BUFFER is nil, the current buffer is used."
                    (if erc-online-p "" "not "))
         erc-online-p))))
 
-(defun erc-log-aux (string)
-  "Do the debug logging of STRING."
-  (let ((cb (current-buffer))
-        (point 1)
-        (was-eob nil)
-        (session-buffer (erc-server-buffer)))
-    (if session-buffer
-        (progn
-          (set-buffer session-buffer)
-          (if (not (and erc-dbuf (bufferp erc-dbuf) (buffer-live-p erc-dbuf)))
-              (progn
-                (setq erc-dbuf (get-buffer-create
-                                (concat "*ERC-DEBUG: "
-                                        erc-session-server "*")))))
-          (set-buffer erc-dbuf)
-          (setq point (point))
-          (setq was-eob (eobp))
-          (goto-char (point-max))
-          (insert (concat "** " string "\n"))
-          (if was-eob (goto-char (point-max))
-            (goto-char point))
-          (set-buffer cb))
-      (message "ERC: ** %s" string))))
+
 
 ;; Last active buffer, to print server messages in the right place
 
@@ -1841,40 +1637,6 @@ All strings are compared according to IRC protocol case 
rules, see
           (throw 'result list)
         (setq list (cdr list))))))
 
-(defmacro erc-with-buffer (spec &rest body)
-  "Execute BODY in the buffer associated with SPEC.
-
-SPEC should have the form
-
- (TARGET [PROCESS])
-
-If TARGET is a buffer, use it.  Otherwise, use the buffer
-matching TARGET in the process specified by PROCESS.
-
-If PROCESS is nil, use the current `erc-server-process'.
-See `erc-get-buffer' for details.
-
-See also `with-current-buffer'.
-
-\(fn (TARGET [PROCESS]) BODY...)"
-  (declare (indent 1) (debug ((form &optional form) body)))
-  (let ((buf (make-symbol "buf"))
-        (proc (make-symbol "proc"))
-        (target (make-symbol "target"))
-        (process (make-symbol "process")))
-    `(let* ((,target ,(car spec))
-            (,process ,(cadr spec))
-            (,buf (if (bufferp ,target)
-                      ,target
-                    (let ((,proc (or ,process
-                                     (and (processp erc-server-process)
-                                          erc-server-process))))
-                      (if (and ,target ,proc)
-                          (erc-get-buffer ,target ,proc))))))
-       (when (buffer-live-p ,buf)
-         (with-current-buffer ,buf
-           ,@body)))))
-
 (defun erc-get-buffer (target &optional proc)
   "Return the buffer matching TARGET in the process PROC.
 If PROC is not supplied, all processes are searched."
@@ -1921,18 +1683,6 @@ needs to match PROC."
     (setq predicate (lambda () t)))
   (erc-buffer-filter predicate proc))
 
-(defmacro erc-with-all-buffers-of-server (process pred &rest forms)
-  "Execute FORMS in all buffers which have same process as this server.
-FORMS will be evaluated in all buffers having the process PROCESS and
-where PRED matches or in all buffers of the server process if PRED is
-nil."
-  (declare (indent 1) (debug (form form body)))
-  (macroexp-let2 nil pred pred
-    `(erc-buffer-filter (lambda ()
-                          (when (or (not ,pred) (funcall ,pred))
-                            ,@forms))
-                        ,process)))
-
 (define-obsolete-function-alias 'erc-iswitchb #'erc-switch-to-buffer "25.1")
 (defun erc--switch-to-buffer (&optional arg)
   (read-buffer "Switch to ERC buffer: "
@@ -2877,8 +2627,6 @@ every `erc-lurker-cleanup-interval' updates to
 consumption of lurker state during long Emacs sessions and/or ERC
 sessions with large numbers of incoming PRIVMSGs.")
 
-(defvar erc-message-parsed)
-
 (defun erc-lurker-update-status (_message)
   "Update `erc-lurker-state' if necessary.
 
@@ -4071,7 +3819,7 @@ the message given by REASON."
         (delete-process process))
       (erc-server-reconnect)
       (with-suppressed-warnings ((obsolete erc-server-reconnecting)
-                                 ((obsolete erc-reuse-buffers)))
+                                 (obsolete erc-reuse-buffers))
         (if erc-reuse-buffers
             (progn (cl-assert (not erc--server-reconnecting))
                    (cl-assert (not erc-server-reconnecting)))
@@ -4090,9 +3838,6 @@ the message given by REASON."
   t)
 (put 'erc-cmd-SERVER 'process-not-needed t)
 
-(defvar motif-version-string)
-(defvar gtk-version-string)
-
 (defun erc-cmd-SV ()
   "Say the current ERC and Emacs version into channel."
   (erc-send-message (format "I'm using ERC %s with GNU Emacs %s (%s%s)%s."
@@ -5349,6 +5094,12 @@ Example: (operator) o => @, (voiced) v => +."
           (setq i (1+ i)))
         alist))))
 
+(defcustom erc-channel-members-changed-hook nil
+  "This hook is called every time the variable `channel-members' changes.
+The buffer where the change happened is current while this hook is called."
+  :group 'erc-hooks
+  :type 'hook)
+
 (defun erc-channel-receive-names (names-string)
   "This function is for internal use only.
 
@@ -5392,13 +5143,6 @@ channel."
              name name t voice halfop op admin owner)))))
     (run-hooks 'erc-channel-members-changed-hook)))
 
-
-(defcustom erc-channel-members-changed-hook nil
-  "This hook is called every time the variable `channel-members' changes.
-The buffer where the change happened is current while this hook is called."
-  :group 'erc-hooks
-  :type 'hook)
-
 (defun erc-update-user-nick (nick &optional new-nick
                                   host login full-name info)
   "Update the stored user information for the user with nickname NICK.
@@ -6008,12 +5752,6 @@ When the returned value is a string, pass it to 
`erc-error'.")
 (defvar erc-command-regexp "^/\\([A-Za-z']+\\)\\(\\s-+.*\\|\\s-*\\)$"
   "Regular expression used for matching commands in ERC.")
 
-(cl-defstruct erc-input
-  string insertp sendp)
-
-(cl-defstruct (erc--input-split (:include erc-input))
-  lines cmdp)
-
 (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
@@ -6957,9 +6695,6 @@ shortened server name instead."
           (t ""))))
 
 ;; erc-goodies is required at end of this file.
-(declare-function erc-controls-strip "erc-goodies" (str))
-
-(defvar tabbar--local-hlf)
 
 ;; FIXME when 29.1 is cut and `format-spec' is added to ELPA Compat,
 ;; remove the function invocations from the spec form below.
@@ -7448,12 +7183,4 @@ Otherwise, connect to HOST:PORT as USER and /join 
CHANNEL."
 
 (provide 'erc)
 
-(require 'erc-backend)
-
-;; Deprecated. We might eventually stop requiring the goodies automatically.
-;; IMPORTANT: This require must appear _after_ the above (provide 'erc) to
-;; avoid a recursive require error when byte-compiling the entire package.
-(require 'erc-goodies)
-(require 'erc-networks)
-
 ;;; erc.el ends here
diff --git a/lisp/eshell/em-tramp.el b/lisp/eshell/em-tramp.el
index aebbc36e71..499deaa7fc 100644
--- a/lisp/eshell/em-tramp.el
+++ b/lisp/eshell/em-tramp.el
@@ -1,4 +1,4 @@
-;;; em-tramp.el --- Eshell features that require TRAMP  -*- lexical-binding:t 
-*-
+;;; em-tramp.el --- Eshell features that require Tramp  -*- lexical-binding:t 
-*-
 
 ;; Copyright (C) 1999-2022 Free Software Foundation, Inc.
 
@@ -21,7 +21,7 @@
 
 ;;; Commentary:
 
-;; Eshell features that require TRAMP.
+;; Eshell features that require Tramp.
 
 ;;; Code:
 
@@ -38,29 +38,30 @@
 ;;;###autoload
 (progn
  (defgroup eshell-tramp nil
-   "This module defines commands that use TRAMP in a way that is
+   "This module defines commands that use Tramp in a way that is
   not transparent to the user.  So far, this includes only the
-  built-in su and sudo commands, which are not compatible with
-  the full, external su and sudo commands, and require the user
-  to understand how to use the TRAMP sudo method."
-   :tag "TRAMP Eshell features"
+  built-in su, sudo and doas commands, which are not compatible
+  with the full, external su, sudo, and doas commands, and
+  require the user to understand how to use the Tramp sudo
+  method."
+   :tag "Tramp Eshell features"
    :group 'eshell-module))
 
 (defun eshell-tramp-initialize ()   ;Called from `eshell-mode' via intern-soft!
-  "Initialize the TRAMP-using commands code."
+  "Initialize the Tramp-using commands code."
   (when (eshell-using-module 'eshell-cmpl)
     (add-hook 'pcomplete-try-first-hook
              'eshell-complete-host-reference nil t))
   (setq-local eshell-complex-commands
-              (append '("su" "sudo")
+              (append '("su" "sudo" "doas")
                       eshell-complex-commands)))
 
 (autoload 'eshell-parse-command "esh-cmd")
 
 (defun eshell/su (&rest args)
-  "Alias \"su\" to call TRAMP.
+  "Alias \"su\" to call Tramp.
 
-Uses the system su through TRAMP's su method."
+Uses the system su through Tramp's su method."
   (eshell-eval-using-options
    "su" args
    '((?h "help" nil nil "show this usage screen")
@@ -91,42 +92,67 @@ Become another USER during a login session.")
 
 (put 'eshell/su 'eshell-no-numeric-conversions t)
 
+(defun eshell--method-wrap-directory (directory method &optional user)
+  "Return DIRECTORY as accessed by a Tramp METHOD for USER."
+  (let ((user (or user "root"))
+        (dir (file-local-name (expand-file-name directory)))
+        (prefix (file-remote-p directory))
+        (host (or (file-remote-p directory 'host)
+                 tramp-default-host))
+        (rmethod (file-remote-p directory 'method))
+        (ruser (file-remote-p directory 'user)))
+    (if (and prefix (or (not (string-equal rmethod method))
+                     (not (string-equal ruser user))))
+        (format "%s|%s:%s@%s:%s"
+                (substring prefix 0 -1) method user host dir)
+      (format "/%s:%s@%s:%s" method user host dir))))
+
 (defun eshell/sudo (&rest args)
   "Alias \"sudo\" to call Tramp.
 
-Uses the system sudo through TRAMP's sudo method."
+Uses the system sudo through Tramp's sudo method."
   (eshell-eval-using-options
    "sudo" args
    '((?h "help" nil nil "show this usage screen")
      (?u "user" t user "execute a command as another USER")
+     (?s "shell" nil shell "start a shell instead of executing COMMAND")
      :show-usage
      :parse-leading-options-only
-     :usage "[(-u | --user) USER] COMMAND
+     :usage "[(-u | --user) USER] (-s | --shell) | COMMAND
 Execute a COMMAND as the superuser or another USER.")
-   (throw 'eshell-external
-          (let* ((user (or user "root"))
-                 (host (or (file-remote-p default-directory 'host)
-                           tramp-default-host))
-                 (dir (file-local-name (expand-file-name default-directory)))
-                 (prefix (file-remote-p default-directory))
-                 (default-directory
-                   (if (and prefix
-                            (or
-                             (not
-                              (string-equal
-                               "sudo"
-                               (file-remote-p default-directory 'method)))
-                             (not
-                              (string-equal
-                               user
-                               (file-remote-p default-directory 'user)))))
-                       (format "%s|sudo:%s@%s:%s"
-                               (substring prefix 0 -1) user host dir)
-                     (format "/sudo:%s@%s:%s" user host dir))))
-            (eshell-named-command (car args) (cdr args))))))
+   (let ((dir (eshell--method-wrap-directory default-directory "sudo" user)))
+     (if shell
+         (throw 'eshell-replace-command
+                (eshell-parse-command "cd" (list dir)))
+       (throw 'eshell-external
+              (let ((default-directory dir))
+                (eshell-named-command (car args) (cdr args))))))))
 
 (put 'eshell/sudo 'eshell-no-numeric-conversions t)
 
+(defun eshell/doas (&rest args)
+  "Call Tramp's doas method with ARGS.
+
+Uses the system doas through Tramp's doas method."
+  (eshell-eval-using-options
+   "doas" args
+   '((?h "help" nil nil "show this usage screen")
+     (?u "user" t user "execute a command as another USER")
+     (?s "shell" nil shell "start a shell instead of executing COMMAND")
+     :show-usage
+     :parse-leading-options-only
+     :usage "[(-u | --user) USER] (-s | --shell) | COMMAND
+Execute a COMMAND as the superuser or another USER.")
+   (let ((dir (eshell--method-wrap-directory default-directory "doas" user)))
+     (if shell
+         (throw 'eshell-replace-command
+                (eshell-parse-command "cd" (list dir)))
+       (throw 'eshell-external
+              (let ((default-directory dir))
+                (eshell-named-command (car args) (cdr args))))))))
+
+(put 'eshell/doas 'eshell-no-numeric-conversions t)
+
 (provide 'em-tramp)
 
 ;; Local Variables:
diff --git a/lisp/eshell/em-unix.el b/lisp/eshell/em-unix.el
index 378b0ceeea..4b5e4dd53e 100644
--- a/lisp/eshell/em-unix.el
+++ b/lisp/eshell/em-unix.el
@@ -374,8 +374,8 @@ Remove the DIRECTORY(ies), if they are empty.")
             (file-attribute-inode-number attr)
             (file-attribute-device-number attr-target)
             (file-attribute-device-number attr)
-            (equal (file-attribute-file-number attr-target)
-                   (file-attribute-file-number attr)))
+            (equal (file-attribute-file-identifier attr-target)
+                   (file-attribute-file-identifier attr)))
        (eshell-error (format-message "%s: `%s' and `%s' are the same file\n"
                                      command (car files) target)))
        (t
diff --git a/lisp/eshell/esh-cmd.el b/lisp/eshell/esh-cmd.el
index 3f3a1616ee..4a41bbe8fa 100644
--- a/lisp/eshell/esh-cmd.el
+++ b/lisp/eshell/esh-cmd.el
@@ -261,9 +261,9 @@ the command."
 (defcustom eshell-subcommand-bindings
   '((eshell-in-subcommand-p t)
     (eshell-in-pipeline-p nil)
-    (default-directory default-directory)
-    (process-environment (eshell-copy-environment)))
+    (default-directory default-directory))
   "A list of `let' bindings for subcommand environments."
+  :version "29.1"                     ; removed `process-environment'
   :type 'sexp
   :risky t)
 
@@ -1274,8 +1274,9 @@ be finished later after the completion of an asynchronous 
subprocess."
                         name)
                   (eshell-search-path name)))))
       (if (not program)
-         (eshell-error (format "which: no %s in (%s)\n"
-                               name (getenv "PATH")))
+          (eshell-error (format "which: no %s in (%s)\n"
+                                name (string-join (eshell-get-path t)
+                                                  (path-separator))))
        (eshell-printn program)))))
 
 (put 'eshell/which 'eshell-no-numeric-conversions t)
diff --git a/lisp/eshell/esh-ext.el b/lisp/eshell/esh-ext.el
index 98902fc6f2..d513d750d9 100644
--- a/lisp/eshell/esh-ext.el
+++ b/lisp/eshell/esh-ext.el
@@ -77,7 +77,7 @@ but Eshell will be able to understand
     (let ((list (eshell-get-path))
          suffixes n1 n2 file)
       (while list
-       (setq n1 (concat (car list) name))
+       (setq n1 (file-name-concat (car list) name))
        (setq suffixes eshell-binary-suffixes)
        (while suffixes
          (setq n2 (concat n1 (car suffixes)))
@@ -239,17 +239,16 @@ causing the user to wonder if anything's really going 
on..."
      (?h "help" nil nil  "display this usage message")
      :usage "[-b] PATH
 Adds the given PATH to $PATH.")
-   (if args
-       (progn
-        (setq eshell-path-env (getenv "PATH")
-              args (mapconcat #'identity args path-separator)
-              eshell-path-env
-              (if prepend
-                  (concat args path-separator eshell-path-env)
-                (concat eshell-path-env path-separator args)))
-        (setenv "PATH" eshell-path-env))
-     (dolist (dir (parse-colon-path (getenv "PATH")))
-       (eshell-printn dir)))))
+   (let ((path (eshell-get-path t)))
+     (if args
+         (progn
+           (setq path (if prepend
+                          (append args path)
+                        (append path args)))
+           (eshell-set-path path)
+           (string-join path (path-separator)))
+       (dolist (dir path)
+         (eshell-printn dir))))))
 
 (put 'eshell/addpath 'eshell-no-numeric-conversions t)
 (put 'eshell/addpath 'eshell-filename-arguments t)
diff --git a/lisp/eshell/esh-proc.el b/lisp/eshell/esh-proc.el
index 7e005a0fc1..bb928fc5fb 100644
--- a/lisp/eshell/esh-proc.el
+++ b/lisp/eshell/esh-proc.el
@@ -277,7 +277,23 @@ Used only on systems which do not support async 
subprocesses.")
              eshell-delete-exited-processes
            delete-exited-processes))
         (process-environment (eshell-environment-variables))
+         (coding-system-for-read coding-system-for-read)
+         (coding-system-for-write coding-system-for-write)
         proc stderr-proc decoding encoding changed)
+    ;; 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
+    ;; programs will always interpret any non-ASCII input as encoded
+    ;; in the system codepage.
+    (when (eq system-type 'windows-nt)
+      (or coding-system-for-read        ; Honor manual decoding settings
+          (setq coding-system-for-read
+                (coding-system-change-eol-conversion locale-coding-system
+                                                     'dos)))
+      (or coding-system-for-write       ; Honor manual encoding settings
+          (setq coding-system-for-write
+                (coding-system-change-eol-conversion locale-coding-system
+                                                     'unix))))
     (cond
      ((fboundp 'make-process)
       (unless (equal (car (aref eshell-current-handles eshell-output-handle))
@@ -325,7 +341,7 @@ Used only on systems which do not support async 
subprocesses.")
            (setq decoding (coding-system-change-eol-conversion decoding 'dos)
                  changed t))
        ;; Even if `make-process' left the coding system for encoding
-       ;; data sent from the process undecided, we had better use the
+       ;; data sent to the process undecided, we had better use the
        ;; same one as what we use for decoding.  But, we should
        ;; suppress EOL conversion.
        (if (and decoding (not encoding))
diff --git a/lisp/eshell/esh-util.el b/lisp/eshell/esh-util.el
index 9258ca5e40..0ec11e8a0b 100644
--- a/lisp/eshell/esh-util.el
+++ b/lisp/eshell/esh-util.el
@@ -249,17 +249,60 @@ trailing newlines removed.  Otherwise, this behaves as 
follows:
 It might be different from \(getenv \"PATH\"), when
 `default-directory' points to a remote host.")
 
-(defun eshell-get-path ()
+(make-obsolete-variable 'eshell-path-env 'eshell-get-path "29.1")
+
+(defvar-local eshell-path-env-list nil)
+
+(connection-local-set-profile-variables
+ 'eshell-connection-default-profile
+ '((eshell-path-env-list . nil)))
+
+(connection-local-set-profiles
+ '(:application eshell)
+ 'eshell-connection-default-profile)
+
+(defun eshell-get-path (&optional literal-p)
   "Return $PATH as a list.
-Add the current directory on MS-Windows."
-  (eshell-parse-colon-path
-   (if (eshell-under-windows-p)
-       (concat "." path-separator eshell-path-env)
-     eshell-path-env)))
+If LITERAL-P is nil, return each directory of the path as a full,
+possibly-remote file name; on MS-Windows, add the current
+directory as the first directory in the path as well.
+
+If LITERAL-P is non-nil, return the local part of each directory,
+as the $PATH was actually specified."
+  (with-connection-local-application-variables 'eshell
+    (let ((remote (file-remote-p default-directory))
+          (path
+           (or eshell-path-env-list
+               ;; If not already cached, get the path from
+               ;; `exec-path', removing the last element, which is
+               ;; `exec-directory'.
+               (setq-connection-local eshell-path-env-list
+                                      (butlast (exec-path))))))
+      (when (and (not literal-p)
+                 (not remote)
+                 (eshell-under-windows-p))
+        (push "." path))
+      (if (and remote (not literal-p))
+          (mapcar (lambda (x) (file-name-concat remote x)) path)
+        path))))
+
+(defun eshell-set-path (path)
+  "Set the Eshell $PATH to PATH.
+PATH can be either a list of directories or a string of
+directories separated by `path-separator'."
+  (with-connection-local-application-variables 'eshell
+    (setq-connection-local
+     eshell-path-env-list
+     (if (listp path)
+        path
+       ;; Don't use `parse-colon-path' here, since we don't want
+       ;; the additional translations it does on each element.
+       (split-string path (path-separator))))))
 
 (defun eshell-parse-colon-path (path-env)
   "Split string with `parse-colon-path'.
 Prepend remote identification of `default-directory', if any."
+  (declare (obsolete nil "29.1"))
   (let ((remote (file-remote-p default-directory)))
     (if remote
        (mapcar
@@ -307,16 +350,13 @@ Prepend remote identification of `default-directory', if 
any."
   "Convert OBJECT into a string value."
   (cond
    ((stringp object) object)
-   ((and (listp object)
-        (not (eq object nil)))
-    (let ((string (pp-to-string object)))
-      (substring string 0 (1- (length string)))))
    ((numberp object)
     (number-to-string object))
+   ((and (eq object t)
+        (not eshell-stringify-t))
+    nil)
    (t
-    (unless (and (eq object t)
-                (not eshell-stringify-t))
-      (pp-to-string object)))))
+    (string-trim-right (pp-to-string object)))))
 
 (defsubst eshell-stringify-list (args)
   "Convert each element of ARGS into a string value."
@@ -412,7 +452,7 @@ list."
   ;; runs while point is in the minibuffer and the users attempt
   ;; to use completion.  Don't ask me.
   (condition-case nil
-      (sit-for 0 0)
+      (sit-for 0)
     (error nil)))
 
 (defun eshell-read-passwd-file (file)
diff --git a/lisp/eshell/esh-var.el b/lisp/eshell/esh-var.el
index 36e59cd5a4..57ea42f493 100644
--- a/lisp/eshell/esh-var.el
+++ b/lisp/eshell/esh-var.el
@@ -113,7 +113,7 @@
 (require 'pcomplete)
 (require 'ring)
 
-(defconst eshell-inside-emacs (format "%s,eshell" emacs-version)
+(defvar-local eshell-inside-emacs (format "%s,eshell" emacs-version)
   "Value for the `INSIDE_EMACS' environment variable.")
 
 (defgroup eshell-var nil
@@ -156,14 +156,21 @@ if they are quoted with a backslash."
     ("LINES" ,(lambda () (window-body-height nil 'remap)) t t)
     ("INSIDE_EMACS" eshell-inside-emacs t)
 
-    ;; for eshell-cmd.el
+    ;; for esh-ext.el
+    ("PATH" (,(lambda () (string-join (eshell-get-path t) (path-separator)))
+             . ,(lambda (_ value)
+                  (eshell-set-path value)
+                  value))
+     t t)
+
+    ;; for esh-cmd.el
     ("_" ,(lambda (indices quoted)
            (if (not indices)
                (car (last eshell-last-arguments))
              (eshell-apply-indices eshell-last-arguments
                                    indices quoted))))
-    ("?" eshell-last-command-status)
-    ("$" eshell-last-command-result)
+    ("?" (eshell-last-command-status . nil))
+    ("$" (eshell-last-command-result . nil))
 
     ;; for em-alias.el and em-script.el
     ("0" eshell-command-name)
@@ -176,7 +183,7 @@ if they are quoted with a backslash."
     ("7" ,(lambda () (nth 6 eshell-command-arguments)) nil t)
     ("8" ,(lambda () (nth 7 eshell-command-arguments)) nil t)
     ("9" ,(lambda () (nth 8 eshell-command-arguments)) nil t)
-    ("*" eshell-command-arguments))
+    ("*" (eshell-command-arguments . nil)))
   "This list provides aliasing for variable references.
 Each member is of the following form:
 
@@ -186,6 +193,11 @@ NAME defines the name of the variable, VALUE is a Lisp 
value used to
 compute the string value that will be returned when the variable is
 accessed via the syntax `$NAME'.
 
+If VALUE is a cons (GET . SET), then variable references to NAME
+will use GET to get the value, and SET to set it.  GET and SET
+can be one of the forms described below.  If SET is nil, the
+variable is read-only.
+
 If VALUE is a function, its behavior depends on the value of
 SIMPLE-FUNCTION.  If SIMPLE-FUNCTION is nil, call VALUE with two
 arguments: the list of the indices that were used in the reference,
@@ -193,23 +205,30 @@ and either t or nil depending on whether or not the 
variable was
 quoted with double quotes.  For example, if `NAME' were aliased
 to a function, a reference of `$NAME[10][20]' would result in that
 function being called with the arguments `((\"10\") (\"20\"))' and
-nil.
-If SIMPLE-FUNCTION is non-nil, call the function with no arguments
-and then pass its return value to `eshell-apply-indices'.
+nil.  If SIMPLE-FUNCTION is non-nil, call the function with no
+arguments and then pass its return value to `eshell-apply-indices'.
+
+When VALUE is a function, it's read-only by default.  To make it
+writeable, use the (GET . SET) form described above.  If SET is a
+function, it takes two arguments: a list of indices (currently
+always nil, but reserved for future enhancement), and the new
+value to set.
 
-If VALUE is a string, return the value for the variable with that
-name in the current environment.  If no variable with that name exists
-in the environment, but if a symbol with that same name exists and has
-a value bound to it, return that symbol's value instead.  You can
-prefer symbol values over environment values by setting the value
-of `eshell-prefer-lisp-variables' to t.
+If VALUE is a string, get/set the value for the variable with
+that name in the current environment.  When getting the value, if
+no variable with that name exists in the environment, but if a
+symbol with that same name exists and has a value bound to it,
+return that symbol's value instead.  You can prefer symbol values
+over environment values by setting the value of
+`eshell-prefer-lisp-variables' to t.
 
-If VALUE is a symbol, return the value bound to it.
+If VALUE is a symbol, get/set the value bound to it.
 
 If VALUE has any other type, signal an error.
 
 Additionally, if COPY-TO-ENVIRONMENT is non-nil, the alias should be
 copied (a.k.a. \"exported\") to the environment of created subprocesses."
+  :version "29.1"
   :type '(repeat (list string sexp
                       (choice (const :tag "Copy to environment" t)
                                (const :tag "Use only in Eshell" nil))
@@ -234,6 +253,12 @@ copied (a.k.a. \"exported\") to the environment of created 
subprocesses."
   ;; changing a variable will affect all of Emacs.
   (unless eshell-modify-global-environment
     (setq-local process-environment (eshell-copy-environment)))
+  (setq-local eshell-subcommand-bindings
+              (append
+               '((process-environment (eshell-copy-environment))
+                 (eshell-variable-aliases-list eshell-variable-aliases-list)
+                 (eshell-path-env-list eshell-path-env-list))
+               eshell-subcommand-bindings))
 
   (setq-local eshell-special-chars-inside-quoting
        (append eshell-special-chars-inside-quoting '(?$)))
@@ -282,9 +307,9 @@ copied (a.k.a. \"exported\") to the environment of created 
subprocesses."
             (while (string-match setvar command)
               (nconc
                l (list
-                  (list 'setenv (match-string 1 command)
-                        (match-string 2 command)
-                        (= (length (match-string 2 command)) 0))))
+                   (list 'eshell-set-variable
+                         (match-string 1 command)
+                         (match-string 2 command))))
               (setq command (eshell-stringify (car args))
                     args (cdr args)))
             (cdr l))
@@ -302,6 +327,11 @@ This function is explicit for adding to 
`eshell-parse-argument-hook'."
 
 (defun eshell/define (var-alias definition)
   "Define a VAR-ALIAS using DEFINITION."
+  ;; FIXME: This function doesn't work (it produces variable aliases
+  ;; in a form not recognized by other parts of the code), and likely
+  ;; hasn't worked since before its introduction into Emacs.  It
+  ;; should either be removed or fixed up.
+  (declare (obsolete nil "29.1"))
   (if (not definition)
       (setq eshell-variable-aliases-list
            (delq (assoc var-alias eshell-variable-aliases-list)
@@ -323,12 +353,11 @@ This function is explicit for adding to 
`eshell-parse-argument-hook'."
 
 (defun eshell/export (&rest sets)
   "This alias allows the `export' command to act as bash users expect."
-  (while sets
-    (if (and (stringp (car sets))
-            (string-match "^\\([^=]+\\)=\\(.*\\)" (car sets)))
-       (setenv (match-string 1 (car sets))
-               (match-string 2 (car sets))))
-    (setq sets (cdr sets))))
+  (dolist (set sets)
+    (when (and (stringp set)
+               (string-match "^\\([^=]+\\)=\\(.*\\)" set))
+      (eshell-set-variable (match-string 1 set)
+                           (match-string 2 set)))))
 
 (defun pcomplete/eshell-mode/export ()
   "Completion function for Eshell's `export'."
@@ -338,16 +367,28 @@ This function is explicit for adding to 
`eshell-parse-argument-hook'."
            (eshell-envvar-names)))))
 
 (defun eshell/unset (&rest args)
-  "Unset an environment variable."
-  (while args
-    (if (stringp (car args))
-       (setenv (car args) nil t))
-    (setq args (cdr args))))
+  "Unset one or more variables.
+This is equivalent to calling `eshell/set' for all of ARGS with
+the values of nil for each."
+  (dolist (arg args)
+    (eshell-set-variable arg nil)))
 
 (defun pcomplete/eshell-mode/unset ()
   "Completion function for Eshell's `unset'."
   (while (pcomplete-here (eshell-envvar-names))))
 
+(defun eshell/set (&rest args)
+  "Allow command-ish use of `set'."
+  (let (last-value)
+    (while args
+      (setq last-value (eshell-set-variable (car args) (cadr args))
+            args (cddr args)))
+    last-value))
+
+(defun pcomplete/eshell-mode/set ()
+  "Completion function for Eshell's `set'."
+  (while (pcomplete-here (eshell-envvar-names))))
+
 (defun eshell/setq (&rest args)
   "Allow command-ish use of `setq'."
   (let (last-value)
@@ -561,18 +602,21 @@ INDICES is a list of index-lists (see 
`eshell-parse-indices').
 If QUOTED is non-nil, this was invoked inside double-quotes."
   (if-let ((alias (assoc name eshell-variable-aliases-list)))
       (let ((target (nth 1 alias)))
+        (when (and (not (functionp target))
+                   (consp target))
+          (setq target (car target)))
         (cond
          ((functionp target)
           (if (nth 3 alias)
               (eshell-apply-indices (funcall target) indices quoted)
-            (condition-case nil
-               (funcall target indices quoted)
-              (wrong-number-of-arguments
-               (display-warning
-                :warning (concat "Function for `eshell-variable-aliases-list' "
-                                 "entry should accept two arguments: INDICES "
-                                 "and QUOTED.'"))
-               (funcall target indices)))))
+            (let ((max-arity (cdr (func-arity target))))
+              (if (or (eq max-arity 'many) (>= max-arity 2))
+                  (funcall target indices quoted)
+                (display-warning
+                 :warning (concat "Function for `eshell-variable-aliases-list' 
"
+                                  "entry should accept two arguments: INDICES "
+                                  "and QUOTED.'"))
+                (funcall target indices)))))
          ((symbolp target)
           (eshell-apply-indices (symbol-value target) indices quoted))
          (t
@@ -589,6 +633,44 @@ If QUOTED is non-nil, this was invoked inside 
double-quotes."
         (getenv name)))
      indices quoted)))
 
+(defun eshell-set-variable (name value)
+  "Set the variable named NAME to VALUE.
+NAME can be a string (in which case it refers to an environment
+variable or variable alias) or a symbol (in which case it refers
+to a Lisp variable)."
+  (if-let ((alias (assoc name eshell-variable-aliases-list)))
+      (let ((target (nth 1 alias)))
+        (cond
+         ((functionp target)
+          (setq target nil))
+         ((consp target)
+          (setq target (cdr target))))
+        (cond
+         ((functionp target)
+          (funcall target nil value))
+         ((null target)
+          (unless eshell-in-subcommand-p
+            (error "Variable `%s' is not settable" (eshell-stringify name)))
+          (push `(,name ,(lambda () value) t t)
+                eshell-variable-aliases-list)
+          value)
+         ;; Since getting a variable alias with a string target and
+         ;; `eshell-prefer-lisp-variables' non-nil gets the
+         ;; corresponding Lisp variable, make sure setting does the
+         ;; same.
+         ((and eshell-prefer-lisp-variables
+               (stringp target))
+          (eshell-set-variable (intern target) value))
+         (t
+          (eshell-set-variable target value))))
+    (cond
+     ((stringp name)
+      (setenv name value))
+     ((symbolp name)
+      (set name value))
+     (t
+      (error "Unknown variable `%s'" (eshell-stringify name))))))
+
 (defun eshell-apply-indices (value indices &optional quoted)
   "Apply to VALUE all of the given INDICES, returning the sub-result.
 The format of INDICES is:
diff --git a/lisp/face-remap.el b/lisp/face-remap.el
index 432385587b..f1530285fb 100644
--- a/lisp/face-remap.el
+++ b/lisp/face-remap.el
@@ -367,7 +367,7 @@ See `text-scale-increase' for more details."
 ;;;###autoload
 (defun text-scale-adjust (inc)
   "Adjust the font size in the current buffer by INC steps.
-INC may be passed as a numeric prefix argument.
+Interactively, INC is the prefix numeric argument, and defaults to 1.
 
 The actual adjustment made depends on the final component of the
 keybinding used to invoke the command, with all modifiers removed:
@@ -377,13 +377,14 @@ keybinding used to invoke the command, with all modifiers 
removed:
    \\`0'      Reset the font size to the global default
 
 After adjusting, continue to read input events and further adjust
-the font size as long as the input event read
-\(with all modifiers removed) is one of the above characters.
+the font size as long as the input event (with all modifiers removed)
+is one of the above characters.
 
-Each step scales the height of the default face by the variable
-`text-scale-mode-step' (a negative number of steps decreases the
-height by the same amount).  As a special case, an argument of 0
-will remove any scaling currently active.
+Each step scales the height of the default face by the factor that
+is the value of `text-scale-mode-step' (a negative number of steps
+decreases the height by that factor).  As a special case, an argument
+of 0 will remove any scaling currently active, thus resetting the
+font size to the original value.
 
 This command is a special-purpose wrapper around the
 `text-scale-increase' command which makes repetition convenient
@@ -467,19 +468,22 @@ the `cdr' has the maximum font size, in units of 1/10 pt."
 ;;;###autoload (define-key ctl-x-map [(control meta ?0)] 
'global-text-scale-adjust)
 ;;;###autoload
 (defun global-text-scale-adjust (increment)
-  "Globally adjust the font size by INCREMENT.
+  "Change (a.k.a. \"adjust\") the font size of all faces by INCREMENT.
 
-Interactively, INCREMENT may be passed as a numeric prefix argument.
+Interactively, INCREMENT is the prefix numeric argument, and defaults
+to 1.  Positive values of INCREMENT increase the font size, negative
+values decrease it.
 
-The adjustment made depends on the final component of the key binding
-used to invoke the command, with all modifiers removed:
+When you invoke this command, it performs the initial change of the
+font size, and after that allows further changes by typing one of the
+following keys immediately after invoking the command:
 
    \\`+', \\`='   Globally increase the height of the default face
    \\`-'      Globally decrease the height of the default face
    \\`0'      Globally reset the height of the default face
 
-After adjusting, further adjust the font size as long as the key,
-with all modifiers removed, is one of the above characters.
+(The change of the font size produced by these keys depends on the
+final component of the key sequence, with all modifiers removed.)
 
 Buffer-local face adjustments have higher priority than global
 face adjustments.
diff --git a/lisp/files-x.el b/lisp/files-x.el
index da1e44e250..7199db3e44 100644
--- a/lisp/files-x.el
+++ b/lisp/files-x.el
@@ -489,7 +489,9 @@ from the MODE alist ignoring the input argument VALUE."
                               dir-locals-directory-cache))
 
       ;; Insert modified alist of directory-local variables.
-      (insert ";;; Directory Local Variables\n")
+      ;; When changing this, also update the ".dir-locals.el" file for
+      ;; Emacs itself, as well as the template in autoinsert.el.
+      (insert ";;; Directory Local Variables            -*- no-byte-compile: t 
-*-\n")
       (insert ";;; For more information see (info \"(emacs) Directory 
Variables\")\n\n")
       (princ (dir-locals-to-string
               (sort variables
@@ -618,16 +620,25 @@ PROFILES is a list of connection profiles (symbols)."
   :group 'tramp
   :version "29.1")
 
+(defvar connection-local-criteria nil
+  "The current connection-local criteria, or nil.
+This is set while executing the body of
+`with-connection-local-variables'.")
+
+(defvar connection-local-profile-name-for-setq nil
+  "The current connection-local profile name, or nil.
+This is the name of the profile to use when setting variables via
+`setq-connection-local'.  Its value is derived from
+`connection-local-criteria' and is set while executing the body
+of `with-connection-local-variables'.")
+
 (defsubst connection-local-normalize-criteria (criteria)
   "Normalize plist CRITERIA according to properties.
 Return a reordered plist."
-  (apply
-   #'append
-   (mapcar
-    (lambda (property)
-      (when (and (plist-member criteria property) (plist-get criteria 
property))
-        (list property (plist-get criteria property))))
-    '(:application :protocol :user :machine))))
+  (mapcan (lambda (property)
+            (let ((value (plist-get criteria property)))
+              (and value (list property value))))
+          '(:application :protocol :user :machine)))
 
 (defsubst connection-local-get-profiles (criteria)
   "Return the connection profiles list for CRITERIA.
@@ -694,6 +705,23 @@ in order."
   (customize-set-variable
    'connection-local-profile-alist connection-local-profile-alist))
 
+;;;###autoload
+(defun connection-local-update-profile-variables (profile variables)
+  "Update the variable settings for PROFILE in-place.
+VARIABLES is a list that declares connection-local variables for
+the connection profile.  An element in VARIABLES is an alist
+whose elements are of the form (VAR . VALUE).
+
+Unlike `connection-local-set-profile-variables' (which see), this
+function preserves the values of any existing variable
+definitions that aren't listed in VARIABLES."
+  (when-let ((existing-variables
+              (nreverse (connection-local-get-profile-variables profile))))
+    (dolist (var variables)
+      (setf (alist-get (car var) existing-variables) (cdr var)))
+    (setq variables (nreverse existing-variables)))
+  (connection-local-set-profile-variables profile variables))
+
 (defun hack-connection-local-variables (criteria)
   "Read connection-local variables according to CRITERIA.
 Store the connection-local variables in buffer local
@@ -736,6 +764,15 @@ If APPLICATION is nil, 
`connection-local-default-application' is used."
       :user        ,(file-remote-p default-directory 'user)
       :machine     ,(file-remote-p default-directory 'host))))
 
+(defun connection-local-profile-name-for-criteria (criteria)
+  "Get a connection-local profile name based on CRITERIA."
+  (when criteria
+    (let (print-level print-length)
+      (intern (concat
+               "autogenerated-connection-local-profile/"
+               (prin1-to-string
+                (connection-local-normalize-criteria criteria)))))))
+
 ;;;###autoload
 (defmacro with-connection-local-variables (&rest body)
   "Apply connection-local variables according to `default-directory'.
@@ -743,16 +780,28 @@ Execute BODY, and unwind connection-local variables."
   (declare (debug t))
   `(with-connection-local-variables-1 (lambda () ,@body)))
 
+;;;###autoload
+(defmacro with-connection-local-application-variables (application &rest body)
+  "Apply connection-local variables for APPLICATION in `default-directory'.
+Execute BODY, and unwind connection-local variables."
+  (declare (debug t) (indent 1))
+  `(let ((connection-local-default-application ,application))
+     (with-connection-local-variables-1 (lambda () ,@body))))
+
 ;;;###autoload
 (defun with-connection-local-variables-1 (body-fun)
   "Apply connection-local variables according to `default-directory'.
 Call BODY-FUN with no args, and then unwind connection-local variables."
   (if (file-remote-p default-directory)
-      (let ((enable-connection-local-variables t)
-            (old-buffer-local-variables (buffer-local-variables))
-           connection-local-variables-alist)
-       (hack-connection-local-variables-apply
-        (connection-local-criteria-for-default-directory))
+      (let* ((enable-connection-local-variables t)
+             (connection-local-criteria
+              (connection-local-criteria-for-default-directory))
+             (connection-local-profile-name-for-setq
+              (connection-local-profile-name-for-criteria
+               connection-local-criteria))
+             (old-buffer-local-variables (buffer-local-variables))
+            connection-local-variables-alist)
+       (hack-connection-local-variables-apply connection-local-criteria)
        (unwind-protect
             (funcall body-fun)
          ;; Cleanup.
@@ -764,6 +813,49 @@ Call BODY-FUN with no args, and then unwind 
connection-local variables."
     ;; No connection-local variables to apply.
     (funcall body-fun)))
 
+;;;###autoload
+(defmacro setq-connection-local (&rest pairs)
+  "Set each VARIABLE connection-locally to VALUE.
+
+When `connection-local-profile-name-for-setq' is set, assign each
+variable's value on that connection profile, and set that profile
+for `connection-local-criteria'.  You can use this in combination
+with `with-connection-local-variables', as in
+
+  (with-connection-local-variables
+    (setq-connection-local VARIABLE VALUE))
+
+If there's no connection-local profile to use, just set the
+variables normally, as with `setq'.
+
+The variables are literal symbols and should not be quoted.  The
+second VALUE is not computed until after the first VARIABLE is
+set, and so on; each VALUE can use the new value of variables set
+earlier in the `setq-connection-local'.  The return value of the
+`setq-connection-local' form is the value of the last VALUE.
+
+\(fn [VARIABLE VALUE]...)"
+  (declare (debug setq))
+  (unless (zerop (mod (length pairs) 2))
+    (error "PAIRS must have an even number of variable/value members"))
+  (let ((set-expr nil)
+        (profile-vars nil))
+    (while pairs
+      (unless (symbolp (car pairs))
+        (error "Attempting to set a non-symbol: %s" (car pairs)))
+      (push `(set ',(car pairs) ,(cadr pairs)) set-expr)
+      (push `(cons ',(car pairs) ,(car pairs)) profile-vars)
+      (setq pairs (cddr pairs)))
+    `(prog1
+         ,(macroexp-progn (nreverse set-expr))
+       (when connection-local-profile-name-for-setq
+         (connection-local-update-profile-variables
+          connection-local-profile-name-for-setq
+          (list ,@(nreverse profile-vars)))
+         (connection-local-set-profiles
+          connection-local-criteria
+          connection-local-profile-name-for-setq)))))
+
 ;;;###autoload
 (defun path-separator ()
   "The connection-local value of `path-separator'."
diff --git a/lisp/files.el b/lisp/files.el
index dd10f7399c..a282532258 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -2164,7 +2164,7 @@ If there is no such live buffer, return nil."
             (setq list (cdr list)))
           found)
         (let* ((attributes (file-attributes truename))
-               (number (file-attribute-file-number attributes))
+               (number (file-attribute-file-identifier attributes))
                (list (buffer-list)) found)
           (and buffer-file-numbers-unique
                (car-safe number)       ;Make sure the inode is not just nil.
@@ -2367,7 +2367,7 @@ the various files."
       (let* ((buf (get-file-buffer filename))
             (truename (abbreviate-file-name (file-truename filename)))
             (attributes (file-attributes truename))
-            (number (file-attribute-file-number attributes))
+            (number (file-attribute-file-identifier attributes))
             ;; Find any buffer for a file that has same truename.
             (other (and (not buf)
                          (find-buffer-visiting
@@ -3863,7 +3863,7 @@ If these settings come from directory-local variables, 
then
 DIR-NAME is the name of the associated directory.  Otherwise it is nil."
   ;; Find those variables that we may want to save to
   ;; `safe-local-variable-values'.
-  (let (all-vars risky-vars unsafe-vars ignored)
+  (let (all-vars risky-vars unsafe-vars)
     (dolist (elt variables)
       (let ((var (car elt))
            (val (cdr elt)))
@@ -4745,7 +4745,7 @@ the old visited file has been renamed to the new name 
FILENAME."
              (setq buffer-file-name truename))))
     (setq buffer-file-number
          (if filename
-             (file-attribute-file-number (file-attributes buffer-file-name))
+             (file-attribute-file-identifier (file-attributes 
buffer-file-name))
            nil))
     ;; write-file-functions is normally used for things like ftp-find-file
     ;; that visit things that are not local files as if they were files.
@@ -5734,7 +5734,8 @@ Before and after saving the buffer, this function runs
                  (setq save-buffer-coding-system last-coding-system-used)
                (setq buffer-file-coding-system last-coding-system-used))
              (setq buffer-file-number
-                   (file-attribute-file-number (file-attributes 
buffer-file-name)))
+                   (file-attribute-file-identifier
+                     (file-attributes buffer-file-name)))
              (if setmodes
                  (condition-case ()
                      (progn
@@ -8662,7 +8663,7 @@ It is a nonnegative integer."
 It is an integer or a cons cell of integers."
   (nth 11 attributes))
 
-(defsubst file-attribute-file-number (attributes)
+(defsubst file-attribute-file-identifier (attributes)
   "The inode and device numbers in ATTRIBUTES returned by `file-attributes'.
 The value is a list of the form (INODENUM DEVICE), where DEVICE could be
 either a single number or a cons cell of two numbers.
diff --git a/lisp/follow.el b/lisp/follow.el
index adf1c1b762..c26949985e 100644
--- a/lisp/follow.el
+++ b/lisp/follow.el
@@ -1301,7 +1301,7 @@ non-first windows in Follow mode."
   "The buffer current at the last call to `follow-adjust-window' or nil.
 `follow-mode' is not necessarily enabled in this buffer.")
 
-;; This function is added to `pre-display-function' and is thus called
+;; This function is added to `pre-redisplay-function' and is thus called
 ;; before each redisplay operation.  It supersedes (2018-09) the
 ;; former use of the post command hook, and now does the right thing
 ;; when a program calls `redisplay' or `sit-for'.
diff --git a/lisp/gnus/ChangeLog.1 b/lisp/gnus/ChangeLog.1
index 1949f62609..002b7bcfff 100644
--- a/lisp/gnus/ChangeLog.1
+++ b/lisp/gnus/ChangeLog.1
@@ -910,7 +910,7 @@
 1998-07-11  Mike McEwan  <mike@lotusland.demon.co.uk>
 
        * gnus-agent.el (gnus-agent-fetch-headers): Note last fetched
-       headers per sesion to aid expiry in `headers only' groups.
+       headers per session to aid expiry in `headers only' groups.
 
        * gnus-agent.el (gnus-agent-expire): Update group info to add
        expired articles to list of read articles and prevent
diff --git a/lisp/gnus/ChangeLog.3 b/lisp/gnus/ChangeLog.3
index c33c76f68d..8087021a7c 100644
--- a/lisp/gnus/ChangeLog.3
+++ b/lisp/gnus/ChangeLog.3
@@ -4902,7 +4902,7 @@
 2011-07-31  Marcus Harnisch  <marcus.harnisch@gmx.net>  (tiny change)
 
        * gnus-art.el (gnus-article-stop-animations): Use `elt' instead of
-       `aref' for XEmacs compatibiltiy.
+       `aref' for XEmacs compatibility.
 
 2011-07-31  Lars Magne Ingebrigtsen  <larsi@gnus.org>
 
@@ -23276,7 +23276,7 @@
        Signal a specific `search-failed' rather than a generic `error'.
 
        * gnus-salt.el (gnus-pick-mouse-pick-region): Switch 1 => point-min.
-       (gnus-generate-vertical-tree): Usue `bobp' rather than compare to 1.
+       (gnus-generate-vertical-tree): Use `bobp' rather than compare to 1.
        (gnus-highlight-selected-tree): Use point-min rather than 1 and 2.
 
 2004-09-10  Simon Josefsson  <jas@extundo.com>
diff --git a/lisp/gnus/gnus-art.el b/lisp/gnus/gnus-art.el
index 3bea1a4c1d..814d21823d 100644
--- a/lisp/gnus/gnus-art.el
+++ b/lisp/gnus/gnus-art.el
@@ -26,7 +26,6 @@
 
 (eval-when-compile (require 'cl-lib))
 (defvar tool-bar-map)
-(defvar w3m-minor-mode-map)
 
 (require 'gnus)
 (require 'gnus-sum)
diff --git a/lisp/gnus/gnus-bookmark.el b/lisp/gnus/gnus-bookmark.el
index 18732218c9..29d963984b 100644
--- a/lisp/gnus/gnus-bookmark.el
+++ b/lisp/gnus/gnus-bookmark.el
@@ -65,7 +65,7 @@
 ;; http://thread.gmane.org/v9fxx9fkm4.fsf@marauder.physik.uni-ulm.de
 
 ;; FIXME: Check if `gnus-bookmark.el' should use
-;; `bookmark-make-cell-function'.
+;; `bookmark-make-record-function'.
 ;; Cf. http://article.gmane.org/gmane.emacs.gnus.general/66076
 
 (defgroup gnus-bookmark nil
diff --git a/lisp/gnus/gnus-cus.el b/lisp/gnus/gnus-cus.el
index 32c475239e..5a0cf77a32 100644
--- a/lisp/gnus/gnus-cus.el
+++ b/lisp/gnus/gnus-cus.el
@@ -41,10 +41,7 @@ The following commands are available:\\<widget-keymap>
 \\[widget-forward]             Move to next button or editable field.
 \\[widget-backward]            Move to previous button or editable field.
 \\[widget-button-click]        Activate button under the mouse pointer.
-\\[widget-button-press]                Activate button under point.
-
-Entry to this mode calls the value of `gnus-custom-mode-hook'
-if that value is non-nil."
+\\[widget-button-press]                Activate button under point."
   (use-local-map widget-keymap)
   ;; Emacs stuff:
   (when (and (facep 'custom-button-face)
diff --git a/lisp/gnus/gnus-start.el b/lisp/gnus/gnus-start.el
index 8d9e50059f..4963fd083f 100644
--- a/lisp/gnus/gnus-start.el
+++ b/lisp/gnus/gnus-start.el
@@ -1810,7 +1810,7 @@ where unread is an integer count of calculated unread
 messages (or nil), and info is a regular gnus info entry.
 
 The info element is shared with the same element of
-`gnus-newrc-alist', so as to conserve space."
+`gnus-newsrc-alist', so as to conserve space."
   (let ((alist gnus-newsrc-alist)
        (ohashtb gnus-newsrc-hashtb)
        info method gname rest methods)
diff --git a/lisp/gnus/message.el b/lisp/gnus/message.el
index 67ec0531fa..3bbd68bdcd 100644
--- a/lisp/gnus/message.el
+++ b/lisp/gnus/message.el
@@ -4361,10 +4361,10 @@ arguments.  If METHOD is nil in this case, the return 
value of
 the function will be inserted instead.
 If the buffer already has a\"X-Message-SMTP-Method\" header,
 it is left unchanged."
-  :type '(alist :key-type '(choice
-                            (string :tag "From Address")
-                            (function :tag "Predicate"))
-                :value-type 'string)
+  :type '(alist :key-type (choice
+                           (string :tag "From Address")
+                           (function :tag "Predicate"))
+                :value-type string)
   :version "29.1"
   :group 'message-sending)
 
@@ -4385,7 +4385,7 @@ it is left unchanged."
                      (setq method (or (cdr server) res))
                      (throw 'exit nil))))
                 ((and (stringp (car server))
-                      (string= (car server) from))
+                      (string-equal-ignore-case (car server) from))
                  (setq method (cdr server))
                  (throw 'exit nil)))))
       (when method
@@ -5192,10 +5192,7 @@ command evaluates `message-send-mail-hook' just before 
sending a message."
 (defun message-canlock-generate ()
   "Return a string that is non-trivial to guess.
 Do not use this for anything important, it is cryptographically weak."
-  (sha1 (concat (message-unique-id)
-                (format "%x%x%x" (random) (random) (random))
-                (prin1-to-string (recent-keys))
-                (prin1-to-string (garbage-collect)))))
+  (secure-hash 'sha1 'iv-auto 128))
 
 (defvar canlock-password)
 (defvar canlock-password-for-verify)
@@ -7037,6 +7034,7 @@ is a function used to switch to and display the mail 
buffer."
           ;; Firefox sends us In-Reply-To headers that are Message-IDs
           ;; without <> around them.  Fix that.
           (when (and (eq (car h) 'In-Reply-To)
+                     (stringp (cdr h))
                      ;; Looks like a Message-ID.
                      (string-match-p "\\`[^ @]+@[^ @]+\\'" (cdr h))
                      (not (string-match-p "\\`<.*>\\'" (cdr h))))
diff --git a/lisp/gnus/mm-bodies.el b/lisp/gnus/mm-bodies.el
index 9045966df5..44ce1c9485 100644
--- a/lisp/gnus/mm-bodies.el
+++ b/lisp/gnus/mm-bodies.el
@@ -189,24 +189,8 @@ If TYPE is `text/plain' CRLF->LF translation may occur."
            (quoted-printable-decode-region (point-min) (point-max))
            t)
           ((eq encoding 'base64)
-           (base64-decode-region
-            (point-min)
-            (save-excursion
-               ;; Some mailers insert whitespace junk at the end which
-              ;; base64-decode-region dislikes.
-              (goto-char (point-min))
-              (while (re-search-forward "^[\t ]*\r?\n" nil t)
-                (delete-region (match-beginning 0) (match-end 0)))
-              ;; Also ignore junk which could have been added by
-              ;; mailing list software by finding the final line with
-              ;; base64 text.
-              (goto-char (point-max))
-               (beginning-of-line)
-               (while (and (not (mm-base64-line-p))
-                           (not (bobp)))
-                 (forward-line -1))
-               (forward-line 1)
-              (point))))
+           ;; MIME says to ignore any non-base64 junk
+           (base64-decode-region (point-min) (point-max) nil t))
           ((memq encoding '(nil 7bit 8bit binary))
            ;; Do nothing.
            t)
diff --git a/lisp/gnus/mm-uu.el b/lisp/gnus/mm-uu.el
index 8646998deb..8d31470634 100644
--- a/lisp/gnus/mm-uu.el
+++ b/lisp/gnus/mm-uu.el
@@ -194,7 +194,7 @@ This can be either \"inline\" or \"attachment\".")
      nil)
     (verbatim-marks
      ;; slrn-style verbatim marks, see
-     ;; 
http://slrn.sourceforge.net/docs/slrn-manual-6.html#process_verbatim_marks
+     ;; 
https://slrn.sourceforge.net/docs/slrn-manual-6.html#process_verbatim_marks
      "^#v\\+"
      "^#v\\-$"
      ,(lambda () (mm-uu-verbatim-marks-extract 0 0))
diff --git a/lisp/gnus/nndoc.el b/lisp/gnus/nndoc.el
index cdff7c9acc..378ada6247 100644
--- a/lisp/gnus/nndoc.el
+++ b/lisp/gnus/nndoc.el
@@ -23,7 +23,7 @@
 
 ;;; Commentary:
 
-;; For Outlook mail boxes format, see http://mbx2mbox.sourceforge.net/
+;; For Outlook mail boxes format, see https://mbx2mbox.sourceforge.net/
 
 ;;; Code:
 
diff --git a/lisp/gnus/nnimap.el b/lisp/gnus/nnimap.el
index 73cd183a02..8392eb601f 100644
--- a/lisp/gnus/nnimap.el
+++ b/lisp/gnus/nnimap.el
@@ -238,7 +238,7 @@ during splitting, which may be slow."
       (with-current-buffer (nnimap-buffer)
        (erase-buffer)
         ;; If we have a lot of ranges, split them up to avoid
-        ;; generating too-long lines.  (The limit is 8192 octects,
+        ;; generating too-long lines.  (The limit is 8192 octets,
         ;; and this should guarantee that it's (much) shorter than
         ;; that.)  We don't stream the requests, since the server
         ;; may respond to the requests out-of-order:
diff --git a/lisp/gnus/smime.el b/lisp/gnus/smime.el
index 7bb116d0c5..409befc242 100644
--- a/lisp/gnus/smime.el
+++ b/lisp/gnus/smime.el
@@ -152,7 +152,7 @@ certificate."
 (defcustom smime-CA-file (car (gnutls-trustfiles))
   "File containing certificates for CAs you trust.
 The file should contain certificates in PEM format.  By default,
-this is initialized from the `gnutls-trusfiles' variable."
+this is initialized from the `gnutls-trustfiles' variable."
   :version "29.1"
   :type '(choice (const :tag "none" nil)
                 file))
diff --git a/lisp/help-fns.el b/lisp/help-fns.el
index eef895ae88..e29f763dab 100644
--- a/lisp/help-fns.el
+++ b/lisp/help-fns.el
@@ -669,7 +669,7 @@ the C sources, too."
   "Insert usage at point and return docstring.  With highlighting."
   (if (keymapp function)
       doc                       ; If definition is a keymap, skip arglist note.
-    (let* ((advertised (gethash real-def advertised-signature-table t))
+    (let* ((advertised (get-advertised-calling-convention real-def))
            (arglist (if (listp advertised)
                         advertised (help-function-arglist real-def)))
            (usage (help-split-fundoc doc function)))
diff --git a/lisp/help-macro.el b/lisp/help-macro.el
index 91c2a80400..cf024afe25 100644
--- a/lisp/help-macro.el
+++ b/lisp/help-macro.el
@@ -147,16 +147,16 @@ and then returns."
                  (while (or (memq char (append help-event-list
                                                (cons help-char '( ?? ?\C-v ?\s 
?\177 ?\M-v ?\S-\s
                                                                   deletechar 
backspace vertical-scroll-bar
-                                                                  next prior 
up down))))
+                                                                  home end 
next prior up down))))
                             (eq (car-safe char) 'switch-frame)
                             (equal key "\M-v"))
                    (condition-case nil
                        (cond
                         ((eq (car-safe char) 'switch-frame)
                          (handle-switch-frame char))
-                        ((memq char '(?\C-v ?\s next))
+                        ((memq char '(?\C-v ?\s next end))
                          (scroll-up))
-                        ((or (memq char '(?\177 ?\M-v ?\S-\s deletechar 
backspace prior))
+                        ((or (memq char '(?\177 ?\M-v ?\S-\s deletechar 
backspace prior home))
                              (equal key "\M-v"))
                          (scroll-down))
                         ((memq char '(down))
@@ -210,7 +210,11 @@ and then returns."
                            (unless (eq new-frame (selected-frame))
                              (iconify-frame new-frame))
                            (setq new-frame nil)))
-                     (ding)))))
+                     (unless (equal (key-description key) "C-g")
+                       (message (substitute-command-keys
+                                (format "No help command is bound to `\\`%s''"
+                                        (key-description key))))
+                       (ding))))))
            (when config
              (set-window-configuration config))
            (when new-frame
diff --git a/lisp/help.el b/lisp/help.el
index b4b9120da3..b25a8ce299 100644
--- a/lisp/help.el
+++ b/lisp/help.el
@@ -55,66 +55,68 @@
 This variable is bound to t during the preparation of a *Help*
 buffer.")
 
-(defvar help-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map (char-to-string help-char) 'help-for-help)
-    (define-key map [help] 'help-for-help)
-    (define-key map [f1] 'help-for-help)
-    (define-key map "." 'display-local-help)
-    (define-key map "?" 'help-for-help)
-
-    (define-key map "\C-a" 'about-emacs)
-    (define-key map "\C-c" 'describe-copying)
-    (define-key map "\C-d" 'view-emacs-debugging)
-    (define-key map "\C-e" 'view-external-packages)
-    (define-key map "\C-f" 'view-emacs-FAQ)
-    (define-key map "\C-m" 'view-order-manuals)
-    (define-key map "\C-n" 'view-emacs-news)
-    (define-key map "\C-o" 'describe-distribution)
-    (define-key map "\C-p" 'view-emacs-problems)
-    (define-key map "\C-s" 'search-forward-help-for-help)
-    (define-key map "\C-t" 'view-emacs-todo)
-    (define-key map "\C-w" 'describe-no-warranty)
-
-    ;; This does not fit the pattern, but it is natural given the C-\ command.
-    (define-key map "\C-\\" 'describe-input-method)
-
-    (define-key map "C" 'describe-coding-system)
-    (define-key map "F" 'Info-goto-emacs-command-node)
-    (define-key map "I" 'describe-input-method)
-    (define-key map "K" 'Info-goto-emacs-key-command-node)
-    (define-key map "L" 'describe-language-environment)
-    (define-key map "S" 'info-lookup-symbol)
-
-    (define-key map "a" 'apropos-command)
-    (define-key map "b" 'describe-bindings)
-    (define-key map "c" 'describe-key-briefly)
-    (define-key map "d" 'apropos-documentation)
-    (define-key map "e" 'view-echo-area-messages)
-    (define-key map "f" 'describe-function)
-    (define-key map "g" 'describe-gnu-project)
-    (define-key map "h" 'view-hello-file)
-
-    (define-key map "i" 'info)
-    (define-key map "4i" 'info-other-window)
-
-    (define-key map "k" 'describe-key)
-    (define-key map "l" 'view-lossage)
-    (define-key map "m" 'describe-mode)
-    (define-key map "o" 'describe-symbol)
-    (define-key map "n" 'view-emacs-news)
-    (define-key map "p" 'finder-by-keyword)
-    (define-key map "P" 'describe-package)
-    (define-key map "r" 'info-emacs-manual)
-    (define-key map "R" 'info-display-manual)
-    (define-key map "s" 'describe-syntax)
-    (define-key map "t" 'help-with-tutorial)
-    (define-key map "v" 'describe-variable)
-    (define-key map "w" 'where-is)
-    (define-key map "x" 'describe-command)
-    (define-key map "q" 'help-quit)
-    map)
-  "Keymap for characters following the Help key.")
+(defun help-key ()
+  "Return `help-char' in a format suitable for the `keymap-set' KEY argument."
+  (key-description (char-to-string help-char)))
+
+(defvar-keymap help-map
+  :doc "Keymap for characters following the Help key."
+  (help-key) #'help-for-help
+  "<help>" #'help-for-help
+  "<f1>" #'help-for-help
+  "."    #'display-local-help
+  "?"    #'help-for-help
+
+  "C-a"  #'about-emacs
+  "C-c"  #'describe-copying
+  "C-d"  #'view-emacs-debugging
+  "C-e"  #'view-external-packages
+  "C-f"  #'view-emacs-FAQ
+  "RET"  #'view-order-manuals
+  "C-n"  #'view-emacs-news
+  "C-o"  #'describe-distribution
+  "C-p"  #'view-emacs-problems
+  "C-s"  #'search-forward-help-for-help
+  "C-t"  #'view-emacs-todo
+  "C-w"  #'describe-no-warranty
+
+  ;; This does not fit the pattern, but it is natural given the C-\ command.
+  "C-\\" #'describe-input-method
+
+  "C"    #'describe-coding-system
+  "F"    #'Info-goto-emacs-command-node
+  "I"    #'describe-input-method
+  "K"    #'Info-goto-emacs-key-command-node
+  "L"    #'describe-language-environment
+  "S"    #'info-lookup-symbol
+
+  "a"    #'apropos-command
+  "b"    #'describe-bindings
+  "c"    #'describe-key-briefly
+  "d"    #'apropos-documentation
+  "e"    #'view-echo-area-messages
+  "f"    #'describe-function
+  "g"    #'describe-gnu-project
+  "h"    #'view-hello-file
+
+  "i"    #'info
+  "4 i"  #'info-other-window
+
+  "k"    #'describe-key
+  "l"    #'view-lossage
+  "m"    #'describe-mode
+  "o"    #'describe-symbol
+  "n"    #'view-emacs-news
+  "p"    #'finder-by-keyword
+  "P"    #'describe-package
+  "r"    #'info-emacs-manual
+  "R"    #'info-display-manual
+  "s"    #'describe-syntax
+  "t"    #'help-with-tutorial
+  "v"    #'describe-variable
+  "w"    #'where-is
+  "x"    #'describe-command
+  "q"    #'help-quit-or-quick)
 
 (define-key global-map (char-to-string help-char) 'help-command)
 (define-key global-map [help] 'help-command)
@@ -125,11 +127,146 @@ buffer.")
 (defvar help-button-cache nil)
 
 
+
+(defvar help-quick-sections
+  '(("File"
+     (save-buffers-kill-terminal . "exit")
+     (find-file . "find")
+     (write-file . "write")
+     (save-buffer . "save")
+     (save-some-buffers . "all"))
+    ("Buffer"
+     (kill-buffer . "kill")
+     (list-buffers . "list")
+     (switch-to-buffer . "switch")
+     (goto-line . "goto line")
+     (read-only-mode . "read only"))
+    ("Window"
+     (delete-window . "only other")
+     (delete-other-windows . "only this")
+     (split-window-below . "split vert.")
+     (split-window-right . "split horiz.")
+     (other-window . "other window"))
+    ("Mark & Kill"
+     (set-mark-command . "mark")
+     (kill-line . "kill line")
+     (kill-ring-save . "kill region")
+     (yank . "yank")
+     (exchange-point-and-mark . "swap"))
+    ("Projects"
+     (project-switch-project . "switch")
+     (project-find-file . "find file")
+     (project-find-regexp . "search")
+     (project-query-replace-regexp . "search & replace")
+     (project-compile . "compile"))
+    ("Misc."
+     (undo . "undo")
+     (isearch-forward . "search")
+     (isearch-backward . "reverse search")
+     (query-replace . "search & replace")
+     (fill-paragraph . "reformat"))))
+
+(declare-function prop-match-value "text-property-search" (match))
+
+;; Inspired by a mg fork (https://github.com/troglobit/mg)
+(defun help-quick ()
+  "Display a quick-help buffer."
+  (interactive)
+  (with-current-buffer (get-buffer-create "*Quick Help*")
+    (let ((inhibit-read-only t) (padding 2) blocks)
+
+      ;; Go through every section and prepare a text-rectangle to be
+      ;; inserted later.
+      (dolist (section help-quick-sections)
+        (let ((max-key-len 0) (max-cmd-len 0) keys)
+          (dolist (ent (reverse (cdr section)))
+            (catch 'skip
+              (let* ((bind (where-is-internal (car ent) nil t))
+                     (key (if bind
+                              (propertize
+                               (key-description bind)
+                               'face 'help-key-binding)
+                            (throw 'skip nil))))
+                (setq max-cmd-len (max (length (cdr ent)) max-cmd-len)
+                      max-key-len (max (length key) max-key-len))
+                (push (list key (cdr ent) (car ent)) keys))))
+          (when keys
+            (let ((fmt (format "%%-%ds %%-%ds%s" max-key-len max-cmd-len
+                               (make-string padding ?\s)))
+                  (width (+ max-key-len 1 max-cmd-len padding)))
+              (push `(,width
+                      ,(propertize
+                        (concat
+                         (car section)
+                         (make-string (- width (length (car section))) ?\s))
+                        'face 'bold)
+                      ,@(mapcar (lambda (ent)
+                                  (format fmt
+                                          (propertize
+                                           (car ent)
+                                           'quick-help-cmd
+                                           (caddr ent))
+                                          (cadr ent)))
+                                keys))
+                    blocks)))))
+
+      ;; Insert each rectangle in order until they don't fit into the
+      ;; frame any more, in which case the next sections are inserted
+      ;; in a new "line".
+      (erase-buffer)
+      (dolist (block (nreverse blocks))
+        (when (> (+ (car block) (current-column)) (frame-width))
+          (goto-char (point-max))
+          (newline 2))
+        (save-excursion
+          (insert-rectangle (cdr block)))
+        (end-of-line))
+      (delete-trailing-whitespace)
+
+      (save-excursion
+        (goto-char (point-min))
+        (while-let ((match (text-property-search-forward 'quick-help-cmd)))
+          (make-text-button (prop-match-beginning match)
+                            (prop-match-end match)
+                            'mouse-face 'highlight
+                            'button t
+                            'keymap button-map
+                            'action #'describe-symbol
+                            'button-data (prop-match-value match)))))
+
+    (help-mode)
+
+    ;; Display the buffer at the bottom of the frame...
+    (with-selected-window (display-buffer-at-bottom (current-buffer) '())
+      ;; ... mark it as dedicated to prevent focus from being stolen
+      (set-window-dedicated-p (selected-window) t)
+      ;; ... and shrink it immediately.
+      (fit-window-to-buffer))
+    (message
+     (substitute-command-keys "Toggle the quick help buffer using 
\\[help-quit-or-quick]."))))
+
+(defalias 'cheat-sheet #'help-quick)
+
 (defun help-quit ()
   "Just exit from the Help command's command loop."
   (interactive)
   nil)
 
+(defun help-quit-or-quick ()
+  "Call `help-quit' or  `help-quick' depending on the context."
+  (interactive)
+  (cond
+   (help-buffer-under-preparation
+    ;; FIXME: There should be a better way to detect if we are in the
+    ;;        help command loop.
+    (help-quit))
+   ((and-let* ((window (get-buffer-window "*Quick Help*")))
+      (quit-window t window)
+      ;; Clear the message we may have gotten from `C-h' and then
+      ;; waiting before hitting `q'.
+      (message "")))
+   ((help-quick))))
+
 (defvar help-return-method nil
   "What to do to \"exit\" the help buffer.
 This is a list
@@ -279,6 +416,7 @@ Do not call this in the scope of `with-help-window'."
        ("describe-package" "Describe a specific Emacs package")
        ""
        ("help-with-tutorial" "Start the Emacs tutorial")
+       ("help-quick-or-quit" "Display the quick help buffer.")
        ("view-echo-area-messages"
         "Show recent messages (from echo area)")
        ("view-lossage" ,(format "Show last %d input keystrokes (lossage)"
@@ -608,7 +746,8 @@ or a buffer name."
           (setq-local outline-heading-end-regexp ":\n")
           (setq-local outline-level (lambda () 1))
           (setq-local outline-minor-mode-cycle t
-                      outline-minor-mode-highlight t)
+                      outline-minor-mode-highlight t
+                      outline-minor-mode-use-buttons 'insert)
           (outline-minor-mode 1)
           (save-excursion
             (goto-char (point-min))
diff --git a/lisp/icomplete.el b/lisp/icomplete.el
index 19afdaa278..ef710d582d 100644
--- a/lisp/icomplete.el
+++ b/lisp/icomplete.el
@@ -139,7 +139,7 @@ See `icomplete-delay-completions-threshold'."
 
 (defvar icomplete-in-buffer nil
   "If non-nil, also use Icomplete when completing in non-mini buffers.
-This affects commands like `complete-in-region', but not commands
+This affects commands like `completion-in-region', but not commands
 that use their own completions setup.")
 
 (defcustom icomplete-minibuffer-setup-hook nil
@@ -228,7 +228,7 @@ the default otherwise."
 ;; Apropos `icomplete-scroll', we implement "scrolling icomplete"
 ;; within classic icomplete, which is "rotating", by contrast.
 ;;
-;; The two variables supporing this are
+;; The two variables supporting this are
 ;; `icomplete--scrolled-completions' and `icomplete--scrolled-past'.
 ;; They come into play when:
 ;;
diff --git a/lisp/ido.el b/lisp/ido.el
index 1d0082da97..77e4dd447d 100644
--- a/lisp/ido.el
+++ b/lisp/ido.el
@@ -3915,7 +3915,7 @@ If `ido-change-word-sub' cannot be found in WORD, return 
nil."
   "Return dotted pair (RES . 1)."
   (cons res 1))
 
-(defun ido-choose-completion-string (choice &rest ignored)
+(defun ido-choose-completion-string (choice &rest _ignored)
   (when (ido-active)
     ;; Insert the completion into the buffer where completion was requested.
     (and ido-completion-buffer
diff --git a/lisp/image/image-dired-external.el 
b/lisp/image/image-dired-external.el
index 026a84560d..b1f8a6c228 100644
--- a/lisp/image/image-dired-external.el
+++ b/lisp/image/image-dired-external.el
@@ -216,7 +216,7 @@ Each item has the form (ORIGINAL-FILE TARGET-FILE).")
   "Maximum number of concurrent jobs permitted for generating images.
 Increase at own risk.  If you want to experiment with this,
 consider setting `image-dired-debug' to a non-nil value to see
-the time spent on generating thumbnails.  Run `image-clear-cache'
+the time spent on generating thumbnails.  Run `clear-image-cache'
 and remove the cached thumbnail files between each trial run.")
 
 (defun image-dired-pngnq-thumb (spec)
diff --git a/lisp/info-look.el b/lisp/info-look.el
index ce0a08dcbe..2eec6f49f5 100644
--- a/lisp/info-look.el
+++ b/lisp/info-look.el
@@ -1051,6 +1051,7 @@ Return nil if there is nothing appropriate in the buffer 
near point."
    ("eieio" "Function Index")
    ("gnutls" "(emacs-gnutls)Variable Index" "(emacs-gnutls)Function Index")
    ("mm" "(emacs-mime)Index")
+   ("eglot" "Index")
    ("epa" "Variable Index" "Function Index")
    ("ert" "Index")
    ("eshell" "Function and Variable Index")
diff --git a/lisp/info.el b/lisp/info.el
index fabba2734a..02dde50dd6 100644
--- a/lisp/info.el
+++ b/lisp/info.el
@@ -1883,6 +1883,9 @@ See `completing-read' for a description of arguments and 
usage."
    (t (complete-with-action
        code Info-read-node-completion-table string predicate))))
 
+(defvar Info-minibuf-history nil
+  "History for `Info-read-node-name'.")
+
 ;; Arrange to highlight the proper letters in the completion list buffer.
 (defun Info-read-node-name (prompt &optional default)
   "Read an Info node name with completion, prompting with PROMPT.
@@ -2481,7 +2484,6 @@ Table of contents is created from the tree structure of 
menus."
            (sections '(("Top" "Top")))
            nodes subfiles)
       (while (or main-file subfiles)
-        ;; (or main-file (message "Searching subfile %s..." (car subfiles)))
         (erase-buffer)
         (info-insert-file-contents (or main-file (car subfiles)))
         (goto-char (point-min))
@@ -2540,7 +2542,6 @@ Table of contents is created from the tree structure of 
menus."
               (setq subfiles (nreverse subfiles)
                     main-file nil))
           (setq subfiles (cdr subfiles))))
-      (message "")
       (nreverse nodes))))
 
 (defun Info-toc-nodes (filename)
diff --git a/lisp/international/emoji.el b/lisp/international/emoji.el
index 4f4d4f4832..3d065b778e 100644
--- a/lisp/international/emoji.el
+++ b/lisp/international/emoji.el
@@ -552,8 +552,7 @@ the name is not known."
             (apply (or class 'transient-prefix) :command name
                    (cons :variable-pitch (cons t slots))))
        (put name 'transient--layout
-            (cl-mapcan (lambda (s) (transient--parse-child name s))
-                       suffixes)))
+            (transient-parse-suffixes name suffixes)))
     name))
 
 (defun emoji--recent-transient (end-function)
diff --git a/lisp/international/mule-cmds.el b/lisp/international/mule-cmds.el
index 48e5c9aa1f..61a26b504c 100644
--- a/lisp/international/mule-cmds.el
+++ b/lisp/international/mule-cmds.el
@@ -1208,6 +1208,16 @@ Arguments are the same as `set-language-info'."
                          (list 'const lang))
                        (sort (mapcar 'car language-info-alist) 'string<))))))
 
+(defun set-language-info-setup-keymap (lang-env alist describe-map setup-map)
+  "Setup menu items for LANG-ENV.
+See `set-language-info-alist' for details of other arguments."
+  (let ((doc (assq 'documentation alist)))
+    (when doc
+      (define-key-after describe-map (vector (intern lang-env))
+       (cons lang-env 'describe-specified-language-support))))
+  (define-key-after setup-map (vector (intern lang-env))
+    (cons lang-env 'setup-specified-language-environment)))
+
 (defun set-language-info-alist (lang-env alist &optional parents)
   "Store ALIST as the definition of language environment LANG-ENV.
 ALIST is an alist of KEY and INFO values.  See the documentation of
@@ -1222,51 +1232,44 @@ in the European submenu in each of those two menus."
         (setq lang-env (symbol-name lang-env)))
        ((stringp lang-env)
         (setq lang-env (purecopy lang-env))))
-  (let ((describe-map describe-language-environment-map)
-       (setup-map setup-language-environment-map))
-    (if parents
-       (let ((l parents)
-             map parent-symbol parent prompt)
-         (while l
-           (if (symbolp (setq parent-symbol (car l)))
-               (setq parent (symbol-name parent))
-             (setq parent parent-symbol parent-symbol (intern parent)))
-           (setq map (lookup-key describe-map (vector parent-symbol)))
-           ;; This prompt string is for define-prefix-command, so
-           ;; that the map it creates will be suitable for a menu.
-           (or map (setq prompt (format "%s Environment" parent)))
-           (if (not map)
-               (progn
-                 (setq map (intern (format "describe-%s-environment-map"
-                                           (downcase parent))))
-                 (define-prefix-command map nil prompt)
-                 (define-key-after describe-map (vector parent-symbol)
-                   (cons parent map))))
-           (setq describe-map (symbol-value map))
-           (setq map (lookup-key setup-map (vector parent-symbol)))
-           (if (not map)
-               (progn
-                 (setq map (intern (format "setup-%s-environment-map"
-                                           (downcase parent))))
-                 (define-prefix-command map nil prompt)
-                 (define-key-after setup-map (vector parent-symbol)
-                   (cons parent map))))
-           (setq setup-map (symbol-value map))
-           (setq l (cdr l)))))
-
-    ;; Set up menu items for this language env.
-    (let ((doc (assq 'documentation alist)))
-      (when doc
-       (define-key-after describe-map (vector (intern lang-env))
-         (cons lang-env 'describe-specified-language-support))))
-    (define-key-after setup-map (vector (intern lang-env))
-      (cons lang-env 'setup-specified-language-environment))
-
-    (dolist (elt alist)
-      (set-language-info-internal lang-env (car elt) (cdr elt)))
-
-    (if (equal lang-env current-language-environment)
-       (set-language-environment lang-env))))
+  (if parents
+      (while parents
+       (let (describe-map setup-map parent-symbol parent prompt)
+         (if (symbolp (setq parent-symbol (car parents)))
+             (setq parent (symbol-name parent))
+           (setq parent parent-symbol parent-symbol (intern parent)))
+         (setq describe-map (lookup-key describe-language-environment-map
+                                         (vector parent-symbol)))
+         ;; This prompt string is for define-prefix-command, so
+         ;; that the map it creates will be suitable for a menu.
+         (or describe-map (setq prompt (format "%s Environment" parent)))
+         (unless describe-map
+           (setq describe-map (intern (format "describe-%s-environment-map"
+                                              (downcase parent))))
+           (define-prefix-command describe-map nil prompt)
+           (define-key-after
+              describe-language-environment-map
+              (vector parent-symbol) (cons parent describe-map)))
+         (setq setup-map (lookup-key setup-language-environment-map
+                                      (vector parent-symbol)))
+         (unless setup-map
+           (setq setup-map (intern (format "setup-%s-environment-map"
+                                            (downcase parent))))
+           (define-prefix-command setup-map nil prompt)
+           (define-key-after
+              setup-language-environment-map
+              (vector parent-symbol) (cons parent setup-map)))
+         (setq parents (cdr parents))
+          (set-language-info-setup-keymap
+           lang-env alist
+           (symbol-value describe-map) (symbol-value setup-map))))
+    (set-language-info-setup-keymap
+     lang-env alist
+     describe-language-environment-map setup-language-environment-map))
+  (dolist (elt alist)
+    (set-language-info-internal lang-env (car elt) (cdr elt)))
+  (if (equal lang-env current-language-environment)
+      (set-language-environment lang-env)))
 
 (defun read-language-name (key prompt &optional default)
   "Read a language environment name which has information for KEY.
diff --git a/lisp/international/mule-diag.el b/lisp/international/mule-diag.el
index cdf2e527e2..6e49c9cb21 100644
--- a/lisp/international/mule-diag.el
+++ b/lisp/international/mule-diag.el
@@ -812,7 +812,7 @@ but still contains full information about each coding 
system."
 
 (declare-function font-info "font.c" (name &optional frame))
 
-(defun describe-font-internal (font-info &optional ignored)
+(defun describe-font-internal (font-info &optional _ignored)
   "Print information about a font in FONT-INFO.
 The IGNORED argument is ignored."
   (print-list "name (opened by):" (aref font-info 0))
diff --git a/lisp/international/textsec.el b/lisp/international/textsec.el
index 82eba1b5d5..f8ff89c314 100644
--- a/lisp/international/textsec.el
+++ b/lisp/international/textsec.el
@@ -156,7 +156,7 @@ Levels are (in decreasing order of restrictiveness) 
`ascii-only',
                              tibetan)))
     ;; The string is covered by Latin and any one other Recommended
     ;; script, except Cyrillic, Greek.
-    'moderately-retrictive)
+    'moderately-restrictive)
    ;; Fixme `minimally-restrictive' -- needs well-formedness criteria
    ;; and Identifier Profile.
    (t
diff --git a/lisp/jka-compr.el b/lisp/jka-compr.el
index 8db78ebcda..420d83ab1f 100644
--- a/lisp/jka-compr.el
+++ b/lisp/jka-compr.el
@@ -598,7 +598,7 @@ There should be no more than seven characters after the 
final `/'."
 
 
 ;; Support for loading compressed files.
-(defun jka-compr-load (file &optional noerror nomessage _nosuffix)
+(defun jka-compr-load (file &optional noerror nomessage _nosuffix _must-suffix)
   "Documented as original."
 
   (let* ((local-copy (jka-compr-file-local-copy file))
diff --git a/lisp/language/ind-util.el b/lisp/language/ind-util.el
index 27facaa858..e2a21820f4 100644
--- a/lisp/language/ind-util.el
+++ b/lisp/language/ind-util.el
@@ -31,7 +31,7 @@
 
 ;;; Transliteration
 
-;; The followings provide the various transliteration schemes (such as
+;; The following provides the various transliteration schemes (such as
 ;; ITRANS, kyoto-harvard, and Aiba) of Indian scripts.  They are also
 ;; used in quail/indian.el for typing Indian script in Emacs.
 
@@ -638,7 +638,7 @@
 
 ;;; IS 13194 utilities
 
-;; The followings provide conversion between IS 13194 (ISCII) and UCS.
+;; The following provides conversion between IS 13194 (ISCII) and UCS.
 
 (dlet
     ;;Unicode vs IS13194  ;; only Devanagari is supported now.
diff --git a/lisp/language/misc-lang.el b/lisp/language/misc-lang.el
index c34017d9b3..230db3b100 100644
--- a/lisp/language/misc-lang.el
+++ b/lisp/language/misc-lang.el
@@ -253,7 +253,7 @@ in this language environment."))
                (documentation . "\
 Language environment for Gāndhārī, Sanskrit, and other languages
 using the Kharoṣṭhī script."))
- '("Misc"))
+ '("Indian"))
 
 (let ((consonant     "[\U00010A00\U00010A10-\U00010A35]")
       (vowel         "[\U00010A01-\U00010A06]")
diff --git a/lisp/ldefs-boot.el b/lisp/ldefs-boot.el
index c9502fbb21..c754e72354 100644
--- a/lisp/ldefs-boot.el
+++ b/lisp/ldefs-boot.el
@@ -3925,18 +3925,6 @@ See the documentation of `define-ccl-program' for the 
detail of CCL program.
 (register-definition-prefixes "ccl" '("ccl-"))
 
 
-;;; Generated autoloads from emacs-lisp/cconv.el
-
-(autoload 'cconv-closure-convert "cconv" "\
-Main entry point for closure conversion.
-FORM is a piece of Elisp code after macroexpansion.
-
-Returns a form where all lambdas don't have any free variables.
-
-(fn FORM)")
-(register-definition-prefixes "cconv" '("cconv-"))
-
-
 ;;; Generated autoloads from cdl.el
 
 (register-definition-prefixes "cdl" '("cdl-"))
@@ -4702,14 +4690,13 @@ Search happens in `native-comp-eln-load-path'.
 (autoload 'native-compile "comp" "\
 Compile FUNCTION-OR-FILE into native code.
 This is the synchronous entry-point for the Emacs Lisp native
-compiler.
-FUNCTION-OR-FILE is a function symbol, a form, or the filename of
-an Emacs Lisp source file.
-If OUTPUT is non-nil, use it as the filename for the compiled
-object.
-If FUNCTION-OR-FILE is a filename, return the filename of the
-compiled object.  If FUNCTION-OR-FILE is a function symbol or a
-form, return the compiled function.
+compiler.  FUNCTION-OR-FILE is a function symbol, a form, or the
+filename of an Emacs Lisp source file.  If OUTPUT is non-nil, use
+it as the filename for the compiled object.  If FUNCTION-OR-FILE
+is a filename, if the compilation was successful return the
+filename of the compiled object.  If FUNCTION-OR-FILE is a
+function symbol or a form, if the compilation was successful
+return the compiled function.
 
 (fn FUNCTION-OR-FILE &optional OUTPUT)")
 (autoload 'batch-native-compile "comp" "\
@@ -8981,6 +8968,55 @@ Turn on EDT Emulation." t)
 (register-definition-prefixes "edt-vt100" '("edt-set-term-width-"))
 
 
+;;; Generated autoloads from progmodes/eglot.el
+
+(push (purecopy '(eglot 1 9)) package--builtin-versions)
+(autoload 'eglot "eglot" "\
+Start LSP server in support of PROJECT's buffers under MANAGED-MAJOR-MODE.
+
+This starts a Language Server Protocol (LSP) server suitable for the
+buffers of PROJECT whose `major-mode' is MANAGED-MAJOR-MODE.
+CLASS is the class of the LSP server to start and CONTACT specifies
+how to connect to the server.
+
+Interactively, the command attempts to guess MANAGED-MAJOR-MODE
+from the current buffer's `major-mode', CLASS and CONTACT from
+`eglot-server-programs' looked up by the major mode, and PROJECT from
+`project-find-functions'.  The search for active projects in this
+context binds `eglot-lsp-context' (which see).
+
+If it can't guess, it prompts the user for the mode and the server.
+With a single \\[universal-argument] prefix arg, it always prompts for COMMAND.
+With two \\[universal-argument], it also always prompts for MANAGED-MAJOR-MODE.
+
+The LSP server of CLASS is started (or contacted) via CONTACT.
+If this operation is successful, current *and future* file
+buffers of MANAGED-MAJOR-MODE inside PROJECT become \"managed\"
+by the LSP server, meaning the information about their contents is
+exchanged periodically with the server to provide enhanced
+code-analysis via `xref-find-definitions', `flymake-mode',
+`eldoc-mode', and `completion-at-point', among others.
+
+PROJECT is a project object as returned by `project-current'.
+
+CLASS is a subclass of `eglot-lsp-server'.
+
+CONTACT specifies how to contact the server.  It is a
+keyword-value plist used to initialize CLASS or a plain list as
+described in `eglot-server-programs', which see.
+
+LANGUAGE-ID is the language ID string to send to the server for
+MANAGED-MAJOR-MODE, which matters to a minority of servers.
+
+INTERACTIVE is t if called interactively.
+
+(fn MANAGED-MAJOR-MODE PROJECT CLASS CONTACT LANGUAGE-ID &optional 
INTERACTIVE)" t)
+(autoload 'eglot-ensure "eglot" "\
+Start Eglot session for current buffer if there isn't one.")
+(put 'eglot-workspace-configuration 'safe-local-variable 'listp)
+(register-definition-prefixes "eglot" '("eglot-"))
+
+
 ;;; Generated autoloads from ehelp.el
 
 (autoload 'with-electric-help "ehelp" "\
@@ -11719,6 +11755,17 @@ variables are set in the server's process buffer 
according to the
 VARIABLES list of the connection profile.  The list is processed
 in order.
 
+(fn PROFILE VARIABLES)")
+(autoload 'connection-local-update-profile-variables "files-x" "\
+Update the variable settings for PROFILE in-place.
+VARIABLES is a list that declares connection-local variables for
+the connection profile.  An element in VARIABLES is an alist
+whose elements are of the form (VAR . VALUE).
+
+Unlike `connection-local-set-profile-variables' (which see), this
+function preserves the values of any existing variable
+definitions that aren't listed in VARIABLES.
+
 (fn PROFILE VARIABLES)")
 (autoload 'hack-connection-local-variables-apply "files-x" "\
 Apply connection-local variables identified by CRITERIA.
@@ -11731,11 +11778,38 @@ Apply connection-local variables according to 
`default-directory'.
 Execute BODY, and unwind connection-local variables.
 
 (fn &rest BODY)" nil t)
+(autoload 'with-connection-local-application-variables "files-x" "\
+Apply connection-local variables for APPLICATION in `default-directory'.
+Execute BODY, and unwind connection-local variables.
+
+(fn APPLICATION &rest BODY)" nil t)
+(function-put 'with-connection-local-application-variables 
'lisp-indent-function 1)
 (autoload 'with-connection-local-variables-1 "files-x" "\
 Apply connection-local variables according to `default-directory'.
 Call BODY-FUN with no args, and then unwind connection-local variables.
 
 (fn BODY-FUN)")
+(autoload 'setq-connection-local "files-x" "\
+Set each VARIABLE connection-locally to VALUE.
+
+When `connection-local-profile-name-for-setq' is set, assign each
+variable's value on that connection profile, and set that profile
+for `connection-local-criteria'.  You can use this in combination
+with `with-connection-local-variables', as in
+
+  (with-connection-local-variables
+    (setq-connection-local VARIABLE VALUE))
+
+If there's no connection-local profile to use, just set the
+variables normally, as with `setq'.
+
+The variables are literal symbols and should not be quoted.  The
+second VALUE is not computed until after the first VARIABLE is
+set, and so on; each VALUE can use the new value of variables set
+earlier in the `setq-connection-local'.  The return value of the
+`setq-connection-local' form is the value of the last VALUE.
+
+(fn [VARIABLE VALUE]...)" nil t)
 (autoload 'path-separator "files-x" "\
 The connection-local value of `path-separator'.")
 (autoload 'null-device "files-x" "\
@@ -14367,7 +14441,12 @@ Run gdb passing it COMMAND-LINE as arguments.
 If COMMAND-LINE names a program FILE to debug, gdb will run in
 a buffer named *gud-FILE*, and the directory containing FILE
 becomes the initial working directory and source-file directory
-for your debugger.
+for your debugger.  If you don't want `default-directory' to
+change to the directory of FILE, specify FILE without leading
+directories, in which case FILE should reside either in the
+directory of the buffer from which this command is invoked, or
+it can be found by searching PATH.
+
 If COMMAND-LINE requests that gdb attaches to a process PID, gdb
 will run in *gud-PID*, otherwise it will run in *gud*; in these
 cases the initial working directory is the `default-directory' of
@@ -15397,7 +15476,7 @@ it is disabled.
 
 ;;; Generated autoloads from progmodes/hideshow.el
 
-(defvar hs-special-modes-alist (mapcar 'purecopy '((c-mode "{" "}" "/[*/]" nil 
nil) (c++-mode "{" "}" "/[*/]" nil nil) (bibtex-mode ("@\\S(*\\(\\s(\\)" 1)) 
(java-mode "{" "}" "/[*/]" nil nil) (js-mode "{" "}" "/[*/]" nil) (mhtml-mode 
"{\\|<[^/>]*?" "}\\|</[^/>]*[^/]>" "<!--" mhtml-forward nil))) "\
+(defvar hs-special-modes-alist (mapcar #'purecopy '((c-mode "{" "}" "/[*/]" 
nil nil) (c++-mode "{" "}" "/[*/]" nil nil) (bibtex-mode ("@\\S(*\\(\\s(\\)" 
1)) (java-mode "{" "}" "/[*/]" nil nil) (js-mode "{" "}" "/[*/]" nil) 
(mhtml-mode "{\\|<[^/>]*?" "}\\|</[^/>]*[^/]>" "<!--" mhtml-forward nil))) "\
 Alist for initializing the hideshow variables for different modes.
 Each element has the form
   (MODE START END COMMENT-START FORWARD-SEXP-FUNC ADJUST-BEG-FUNC
@@ -17379,7 +17458,7 @@ Convert old Emacs Devanagari characters to UCS.
 
 ;;; Generated autoloads from leim/quail/indian.el
 
-(register-definition-prefixes "quail/indian" '("indian-mlm-mozhi-u" 
"inscript-" "quail-" "tamil-"))
+(register-definition-prefixes "quail/indian" '("indian-mlm-mozhi-u" 
"inscript-" "quail-" "tamil"))
 
 
 ;;; Generated autoloads from progmodes/inf-lisp.el
@@ -18726,6 +18805,7 @@ This scans for ;;;###autoload forms and related things.
 The first element on the command line should be the (main)
 loaddefs.el output file, and the rest are the directories to
 use.")
+ (load "theme-loaddefs.el" t)
 (register-definition-prefixes "loaddefs-gen" '("autoload-" 
"generated-autoload-" "loaddefs-generate--" "no-update-autoloads"))
 
 
@@ -18847,6 +18927,8 @@ done.  Otherwise, this function will use the current 
buffer.
 Major mode for browsing CVS log output.
 
 (fn)" t)
+(autoload 'log-view-get-marked "log-view" "\
+Return the list of tags for the marked log entries.")
 (register-definition-prefixes "log-view" '("log-view-"))
 
 
@@ -19530,7 +19612,7 @@ Populate MENU with commands that open a man page at 
point.
 
 ;;; Generated autoloads from emacs-lisp/map.el
 
-(push (purecopy '(map 3 2 1)) package--builtin-versions)
+(push (purecopy '(map 3 3 1)) package--builtin-versions)
 (register-definition-prefixes "map" '("map-"))
 
 
@@ -24534,7 +24616,7 @@ Open profile FILENAME.
 
 ;;; Generated autoloads from progmodes/project.el
 
-(push (purecopy '(project 0 8 1)) package--builtin-versions)
+(push (purecopy '(project 0 8 2)) package--builtin-versions)
 (autoload 'project-current "project" "\
 Return the project instance in DIRECTORY, defaulting to `default-directory'.
 
@@ -25926,6 +26008,9 @@ The mode's hook is called both when the mode is enabled 
and when
 it is disabled.
 
 (fn &optional ARG)" t)
+(autoload 'repeat-exit "repeat" "\
+Exit the repeating sequence.
+This function can be used to force exit of repetition while it's active." t)
 (register-definition-prefixes "repeat" '("describe-repeat-maps" "repeat-"))
 
 
@@ -26474,7 +26559,7 @@ Emacs will list the message in the summary.
 (fn REGEXP)" t)
 (autoload 'rmail-summary-by-topic "rmailsum" "\
 Display a summary of all messages with the given SUBJECT.
-Normally checks just the Subject field of headers; but with prefix
+Normally checks just the Subject field of headers; but when prefix
 argument WHOLE-MESSAGE is non-nil, looks in the whole message.
 SUBJECT is a regular expression.
 
@@ -29436,6 +29521,8 @@ PROMPT will be inserted at the start of the buffer, but 
won't be
 included in the resulting string.  If PROMPT is nil, no help text
 will be inserted.
 
+Also see `read-string-from-buffer'.
+
 (fn PROMPT STRING SUCCESS-CALLBACK &key ABORT-CALLBACK)")
 (autoload 'read-string-from-buffer "string-edit" "\
 Switch to a new buffer to edit STRING in a recursive edit.
@@ -29445,6 +29532,8 @@ PROMPT will be inserted at the start of the buffer, but 
won't be
 included in the resulting string.  If nil, no prompt will be
 inserted in the buffer.
 
+Also see `string-edit'.
+
 (fn PROMPT STRING)")
 (register-definition-prefixes "string-edit" '("string-edit-"))
 
@@ -32000,14 +32089,14 @@ Add archive file name handler to 
`file-name-handler-alist'." (when (and tramp-ar
 (register-definition-prefixes "tramp-compat" '("tramp-"))
 
 
-;;; Generated autoloads from net/tramp-crypt.el
+;;; Generated autoloads from net/tramp-container.el
 
-(register-definition-prefixes "tramp-crypt" '("tramp-crypt-"))
+(register-definition-prefixes "tramp-container" '("tramp-"))
 
 
-;;; Generated autoloads from net/tramp-docker.el
+;;; Generated autoloads from net/tramp-crypt.el
 
-(register-definition-prefixes "tramp-docker" '("tramp-docker-"))
+(register-definition-prefixes "tramp-crypt" '("tramp-crypt-"))
 
 
 ;;; Generated autoloads from net/tramp-ftp.el
@@ -32740,6 +32829,10 @@ if it had been inserted from a file named URL.
 
 
 (fn URL &optional VISIT BEG END REPLACE)")
+(autoload 'url-insert-file-contents-literally "url-handlers" "\
+Insert the data retrieved from URL literally in the current buffer.
+
+(fn URL)")
 (register-definition-prefixes "url-handlers" '("url-"))
 
 
@@ -32992,7 +33085,7 @@ Like `message', but do nothing if `url-show-status' is 
nil.
 
 
 (fn X Y)")
-(defalias 'url-basepath 'url-file-directory)
+(defalias 'url-basepath #'url-file-directory)
 (autoload 'url-file-directory "url-util" "\
 Return the directory part of FILE, for a URL.
 
@@ -33440,11 +33533,13 @@ Show the change log for BRANCH root in a window.
 (autoload 'vc-log-incoming "vc" "\
 Show log of changes that will be received with pull from REMOTE-LOCATION.
 When called interactively with a prefix argument, prompt for REMOTE-LOCATION.
+In some version control systems REMOTE-LOCATION can be a remote branch name.
 
 (fn &optional REMOTE-LOCATION)" t)
 (autoload 'vc-log-outgoing "vc" "\
 Show log of changes that will be sent with a push operation to REMOTE-LOCATION.
 When called interactively with a prefix argument, prompt for REMOTE-LOCATION.
+In some version control systems REMOTE-LOCATION can be a remote branch name.
 
 (fn &optional REMOTE-LOCATION)" t)
 (autoload 'vc-log-search "vc" "\
@@ -33571,6 +33666,18 @@ log entries should be gathered.
 Request editing the next VC shell command before execution.
 This is a prefix command.  It affects only a VC command executed
 immediately after this one." t)
+(autoload 'vc-prepare-patch "vc" "\
+Compose an Email sending patches for REVISIONS to ADDRESSEE.
+If `vc-prepare-patches-separately' is nil, SUBJECT will be used
+as the default subject for the message (and it will be prompted
+for when called interactively).  Otherwise a separate message
+will be composed for each revision, with SUBJECT derived from the
+invidividual commits.
+
+When invoked interactively in a Log View buffer with marked
+revisions, those revisions will be used.
+
+(fn ADDRESSEE SUBJECT REVISIONS)" t)
 (register-definition-prefixes "vc" '("vc-" "with-vc-properties"))
 
 
@@ -34565,10 +34672,6 @@ Convert Vietnamese characters of the current buffer to 
`VIQR' mnemonics." t)
 
 ;;; Generated autoloads from view.el
 
-(defvar view-remove-frame-by-deleting t "\
-Determine how View mode removes a frame no longer needed.
-If nil, make an icon of the frame.  If non-nil, delete the frame.")
-(custom-autoload 'view-remove-frame-by-deleting "view" t)
 (defvar-local view-mode nil "\
 Non-nil if View mode is enabled.
 Don't change this variable directly, you must change it by one of the
@@ -36122,7 +36225,13 @@ Extract file name from an yenc header.")
 ;;; Generated autoloads from play/zone.el
 
 (autoload 'zone "zone" "\
-Zone out, completely." t)
+Zone out, completely.
+With a prefix argument the user is prompted for a program to run.
+When called from Lisp the optional argument PGM can be used to
+run a specific program.  The program must be a member of
+`zone-programs'.
+
+(fn &optional PGM)" t)
 (register-definition-prefixes "zone" '("zone-"))
 
 ;;; End of scraped data
diff --git a/lisp/leim/quail/indian.el b/lisp/leim/quail/indian.el
index 048e16e8d8..deef00b4c2 100644
--- a/lisp/leim/quail/indian.el
+++ b/lisp/leim/quail/indian.el
@@ -30,6 +30,8 @@
 
 ;;; Code:
 
+(require 'pcase)
+(require 'seq)
 (require 'quail)
 (require 'ind-util)
 
@@ -369,7 +371,7 @@ Full key sequences are listed below:")
 ;;; Tamil phonetic input method
 ;;;
 
-;; Define the input method straightaway.
+;; Define the input method straight away.
 (quail-define-package "tamil-phonetic" "Tamil" "ழ" t
  "Customisable Tamil phonetic input method.
 To change the translation rules of the input method, customize
@@ -699,6 +701,165 @@ is."
  "tamil-inscript-digits" "Tamil" "TmlISD"
  "Tamil keyboard Inscript with Tamil digits support.")
 
+;; Tamil99 input method
+;;
+;; Tamil99 is a keyboard layout and input method that is specifically
+;; designed for the Tamil language.  Vowels and vowel modifiers are
+;; input with your left hand, and consonants are input with your right
+;; hand. See https://en.wikipedia.org/wiki/Tamil_99
+;;
+;; தமிழ்99 உள்ளீட்டு முறை
+;;
+;; தமிழ்99 தமிழுக்கென்றே உருவாக்கப்பட்ட விசைப்பலகை அமைப்பும் உள்ளீட்டு முறையும்
+;; ஆகும். உயிர்களை இடக்கையுடனும் மெய்களை வலக்கையுடனும் தட்டச்சிடும்படி
+;; அமைக்கப்பட்டது. 
https://ta.wikipedia.org/wiki/%E0%AE%A4%E0%AE%AE%E0%AE%BF%E0%AE%B4%E0%AF%8D_99
+;; காண்க.
+
+(quail-define-package
+ "tamil99" "Tamil" "தமிழ்99"
+ t "Tamil99 input method"
+ nil t t t t nil nil nil nil nil t)
+
+(defconst tamil99-vowels
+  '(("q" "ஆ")
+    ("w" "ஈ")
+    ("e" "ஊ")
+    ("r" "ஐ")
+    ("t" "ஏ")
+    ("a" "அ")
+    ("s" "இ")
+    ("d" "உ")
+    ("g" "எ")
+    ("z" "ஔ")
+    ("x" "ஓ")
+    ("c" "ஒ"))
+  "Mapping for vowels.")
+
+(defconst tamil99-vowel-modifiers
+  '(("q" "ா")
+    ("w" "ீ")
+    ("e" "ூ")
+    ("r" "ை")
+    ("t" "ே")
+    ("a" "")
+    ("s" "ி")
+    ("d" "ு")
+    ("g" "ெ")
+    ("z" "ௌ")
+    ("x" "ோ")
+    ("c" "ொ")
+    ("f" "்"))
+  "Mapping for vowel modifiers.")
+
+(defconst tamil99-hard-consonants
+  '(("h" "க")
+    ("[" "ச")
+    ("o" "ட")
+    ("l" "த")
+    ("j" "ப")
+    ("u" "ற"))
+  "Mapping for hard consonants (வல்லினம்).")
+
+(defconst tamil99-soft-consonants
+  '(("b" "ங")
+    ("]" "ஞ")
+    ("p" "ண")
+    (";" "ந")
+    ("k" "ம")
+    ("i" "ன"))
+  "Mapping for soft consonants (மெல்லினம்).")
+
+(defconst tamil99-medium-consonants
+  '(("'" "ய")
+    ("m" "ர")
+    ("n" "ல")
+    ("v" "வ")
+    ("/" "ழ")
+    ("y" "ள"))
+  "Mapping for medium consonants (இடையினம்).")
+
+(defconst tamil99-grantham-consonants
+  '(("Q" "ஸ")
+    ("W" "ஷ")
+    ("E" "ஜ")
+    ("R" "ஹ"))
+  "Mapping for grantham consonants (கிரந்தம்).")
+
+(defconst tamil99-consonants
+  (append tamil99-hard-consonants
+          tamil99-soft-consonants
+          tamil99-medium-consonants
+          tamil99-grantham-consonants)
+  "Mapping for all consonants.")
+
+(defconst tamil99-other
+  `(("T" ,(vector "க்ஷ"))
+    ("Y" ,(vector "ஶஂரீ"))
+    ("O" "[")
+    ("P" "]")
+    ("A" "௹")
+    ("S" "௺")
+    ("D" "௸")
+    ("F" "ஃ")
+    ("K" "\"")
+    ("L" ":")
+    (":" ";")
+    ("\"" "'")
+    ("Z" "௳")
+    ("X" "௴")
+    ("C" "௵")
+    ("V" "௶")
+    ("B" "௷")
+    ("M" "/"))
+  "Mapping for miscellaneous characters.")
+
+;; உயிர்
+;; vowel
+(mapc (pcase-lambda (`(,vowel-key ,vowel))
+        (quail-defrule vowel-key vowel))
+      tamil99-vowels)
+
+(mapc (pcase-lambda (`(,consonant-key ,consonant))
+        ;; அகர உயிர்மெய்
+        ;; consonant symbol (consonant combined with the first vowel அ)
+        (quail-defrule consonant-key consonant)
+        ;; மெய்யொற்று பின் அகர உயிர்மெய்
+        ;; pulli on double consonant
+        (quail-defrule (concat consonant-key consonant-key)
+                       (vector (concat consonant "்" consonant)))
+        (mapc (pcase-lambda (`(,vowel-key ,vowel-modifier))
+                ;; உயிர்மெய்
+                ;; vowelised consonant
+                (quail-defrule (concat consonant-key vowel-key)
+                               (vector (concat consonant vowel-modifier)))
+                ;; மெய்யொற்று பின் பிற உயிர்மெய்
+                ;; vowelised consonant after double consonant
+                (quail-defrule (concat consonant-key consonant-key vowel-key)
+                               (vector (concat consonant "்" consonant 
vowel-modifier))))
+              tamil99-vowel-modifiers))
+      tamil99-consonants)
+
+(seq-mapn (pcase-lambda (`(,soft-consonant-key ,soft-consonant)
+                         `(,hard-consonant-key ,hard-consonant))
+            ;; மெல்லினம் பின் வல்லினம்
+            ;; hard consonant after soft consonant
+            (quail-defrule (concat soft-consonant-key hard-consonant-key)
+                           (vector (concat soft-consonant "்" hard-consonant)))
+            (mapc (pcase-lambda (`(,vowel-key ,vowel-modifier))
+                    ;; மெல்லின ஒற்றொட்டிய வல்லினம் பின் உயிர்மெய்
+                    ;; vowelised consonant after soft-hard consonant pair
+                    (quail-defrule (concat soft-consonant-key 
hard-consonant-key vowel-key)
+                                   (vector (concat soft-consonant "்" 
hard-consonant vowel-modifier))))
+                  tamil99-vowel-modifiers))
+          tamil99-soft-consonants
+          tamil99-hard-consonants)
+
+;; பிற வரியுருக்கள்
+;; other characters
+(mapc (pcase-lambda (`(,key ,translation))
+        (quail-defrule key translation))
+      tamil99-other)
+
 ;; Probhat Input Method
 (quail-define-package
  "bengali-probhat" "Bengali" "BngPB" t
diff --git a/lisp/leim/quail/misc-lang.el b/lisp/leim/quail/misc-lang.el
index 73287ee784..e9e11ac679 100644
--- a/lisp/leim/quail/misc-lang.el
+++ b/lisp/leim/quail/misc-lang.el
@@ -1526,7 +1526,7 @@
 
 (quail-define-package
  "gothic" "Gothic" "𐌰" nil
- "Input methid for the ancient Gothic script."
+ "Input method for the ancient Gothic script."
  nil t t t t nil nil nil nil nil t)
 
 (quail-define-rules
diff --git a/lisp/leim/quail/slovak.el b/lisp/leim/quail/slovak.el
index acde11d02a..8ddd92d5b4 100644
--- a/lisp/leim/quail/slovak.el
+++ b/lisp/leim/quail/slovak.el
@@ -3,7 +3,8 @@
 ;; Copyright (C) 1998, 2001-2022 Free Software Foundation, Inc.
 
 ;; Authors: Tibor Šimko <tibor.simko@fmph.uniba.sk>
-;;     Milan Zamazal <pdm@zamazal.org>
+;;          Milan Zamazal <pdm@zamazal.org>
+;;          Rudolf Adamkovič <salutis@me.com>
 ;; Maintainer: Pavel Janík <Pavel@Janik.cz>
 ;; Keywords: i18n, multilingual, input method, Slovak
 
@@ -25,8 +26,9 @@
 ;;; Commentary:
 
 ;; This file defines the following Slovak keyboards:
-;; - standard Slovak keyboard
+;; - standard Slovak keyboards, QWERTZ and QWERTY variants
 ;; - three Slovak keyboards for programmers
+;; LocalWords: QWERTZ
 
 ;;; Code:
 
@@ -35,7 +37,7 @@
 
 (quail-define-package
  "slovak" "Slovak" "SK" t
- "Standard Slovak keyboard."
+ "Standard Slovak QWERTZ keyboard."
  nil t nil nil t nil nil nil nil nil t)
 
 (quail-define-rules
@@ -154,6 +156,123 @@
  ("+0" ?\)))
 
 
+(quail-define-package
+ "slovak-qwerty" "Slovak" "SK" t
+ "Standard Slovak QWERTY keyboard."
+ nil t nil nil t nil nil nil nil nil t)
+
+(quail-define-rules
+ ("1" ?+)
+ ("2" ?ľ)
+ ("3" ?š)
+ ("4" ?č)
+ ("5" ?ť)
+ ("6" ?ž)
+ ("7" ?ý)
+ ("8" ?á)
+ ("9" ?í)
+ ("0" ?é)
+ ("!" ?1)
+ ("@" ?2)
+ ("#" ?3)
+ ("$" ?4)
+ ("%" ?5)
+ ("^" ?6)
+ ("&" ?7)
+ ("*" ?8)
+ ("(" ?9)
+ (")" ?0)
+ ("-" ?=)
+ ("_" ?%)
+ ("=" ?')
+ ("[" ?ú)
+ ("{" ?/)
+ ("]" ?ä)
+ ("}" ?\()
+ ("\\" ?ň)
+ ("|" ?\))
+ (";" ?ô)
+ (":" ?\")
+ ("'" ?§)
+ ("\"" ?!)
+ ("<" ??)
+ (">" ?:)
+ ("/" ?-)
+ ("?" ?_)
+ ("`" ?\;)
+ ("~" ?^)
+ ("=a" ?á)
+ ("+a" ?ä)
+ ("+=a" ?ä)
+ ("+c" ?č)
+ ("+d" ?ď)
+ ("=e" ?é)
+ ("+e" ?ě)
+ ("=i" ?í)
+ ("=l" ?ĺ)
+ ("+l" ?ľ)
+ ("+n" ?ň)
+ ("=o" ?ó)
+ ("+o" ?ô)
+ ("~o" ?ô)
+ ("+=o" ?ö)
+ ("=r" ?ŕ)
+ ("+r" ?ř)
+ ("=s" ?ß)
+ ("+s" ?š)
+ ("+t" ?ť)
+ ("=u" ?ú)
+ ("+u" ?ů)
+ ("+=u" ?ü)
+ ("=y" ?ý)
+ ("+z" ?ž)
+ ("=A" ?Á)
+ ("+A" ?Ä)
+ ("+=A" ?Ä)
+ ("+C" ?Č)
+ ("+D" ?Ď)
+ ("=E" ?É)
+ ("+E" ?Ě)
+ ("=I" ?Í)
+ ("=L" ?Ĺ)
+ ("+L" ?Ľ)
+ ("+N" ?Ň)
+ ("=O" ?Ó)
+ ("+O" ?Ô)
+ ("~O" ?Ô)
+ ("+=O" ?Ö)
+ ("=R" ?Ŕ)
+ ("+R" ?Ř)
+ ("=S" ?ß)
+ ("+S" ?Š)
+ ("+T" ?Ť)
+ ("=U" ?Ú)
+ ("+U" ?Ů)
+ ("+=U" ?Ü)
+ ("=Y" ?Ý)
+ ("+Z" ?Ž)
+ ("=q" ?`)
+ ("=2" ?@)
+ ("=3" ?#)
+ ("=4" ?$)
+ ("=5" ?%)
+ ("=6" ?^)
+ ("=7" ?&)
+ ("=8" ?*)
+ ("=9" ?\()
+ ("=0" ?\))
+ ("+1" ?!)
+ ("+2" ?@)
+ ("+3" ?#)
+ ("+4" ?$)
+ ("+5" ?%)
+ ("+6" ?^)
+ ("+7" ?&)
+ ("+8" ?*)
+ ("+9" ?\()
+ ("+0" ?\)))
+
+
 (quail-define-package
  "slovak-prog-1" "Slovak" "SK" t
  "Slovak (non-standard) keyboard for programmers #1.
diff --git a/lisp/loadup.el b/lisp/loadup.el
index c01c827a75..2a9aff4c1f 100644
--- a/lisp/loadup.el
+++ b/lisp/loadup.el
@@ -366,6 +366,11 @@
 (load "emacs-lisp/shorthands")
 
 (load "emacs-lisp/eldoc")
+(load "emacs-lisp/cconv")
+(when (and (compiled-function-p (symbol-function 'cconv-fv))
+           (compiled-function-p (symbol-function 'macroexpand-all)))
+  (setq internal-make-interpreted-closure-function
+        #'cconv-make-interpreted-closure))
 (load "cus-start") ;Late to reduce customize-rogue (needs loaddefs.el anyway)
 (if (not (eq system-type 'ms-dos))
     (load "tooltip"))
@@ -501,7 +506,10 @@ lost after dumping")))
                                          bin-dest-dir)
                      ;; Relative filename from the built uninstalled binary.
                      (file-relative-name file invocation-directory)))))
-              comp-loaded-comp-units-h))))
+              comp-loaded-comp-units-h)))
+  ;; Set up the mechanism to allow inhibiting native-comp via
+  ;; file-local variables.
+  (defvar comp--no-native-compile (make-hash-table :test #'equal)))
 
 (when (hash-table-p purify-flag)
   (let ((strings 0)
diff --git a/lisp/mail/feedmail.el b/lisp/mail/feedmail.el
index 2ae916e3ac..eb6a071bf4 100644
--- a/lisp/mail/feedmail.el
+++ b/lisp/mail/feedmail.el
@@ -131,17 +131,13 @@
 ;; feedmail-send-it.  Hers's the best way to use the stuff in this
 ;; file:
 ;;
-;; Save this file as feedmail.el somewhere on your elisp loadpath;
-;; byte-compile it.  Put the following lines in your init file:
+;; Put the following lines in your init file:
 ;;
 ;;     (setq send-mail-function 'feedmail-send-it)
-;;     (autoload 'feedmail-send-it "feedmail")
 ;;
 ;; If you plan to use the queue stuff, also use this:
 ;;
 ;;     (setq feedmail-enable-queue t)
-;;     (autoload 'feedmail-run-the-queue "feedmail")
-;;     (autoload 'feedmail-run-the-queue-no-prompts "feedmail")
 ;;     (setq auto-mode-alist (cons '("\\.fqm$" . mail-mode) auto-mode-alist))
 ;;
 ;; though VM users might find it more comfortable to use this instead of
@@ -174,11 +170,6 @@
 ;; like to add the suffix ".fqm" to the list of non-saved things via the 
variable
 ;; desktop-files-not-to-save.
 ;;
-;; If you are planning to call feedmail-queue-reminder from your .emacs or
-;; something similar, you might need this:
-;;
-;;     (autoload 'feedmail-queue-reminder "feedmail")
-;;
 ;; If you ever use rmail-resend and queue messages, you should do this:
 ;;
 ;;     (setq feedmail-queue-alternative-mail-header-separator "")
diff --git a/lisp/mail/mail-hist.el b/lisp/mail/mail-hist.el
index a13f9de174..9fb7b36e98 100644
--- a/lisp/mail/mail-hist.el
+++ b/lisp/mail/mail-hist.el
@@ -1,6 +1,6 @@
 ;;; mail-hist.el --- headers and message body history for outgoing mail  -*- 
lexical-binding: t; -*-
 
-;; Copyright (C) 1994, 2001-2022 Free Software Foundation, Inc.
+;; Copyright (C) 1994-2022 Free Software Foundation, Inc.
 
 ;; Author: Karl Fogel <kfogel@red-bean.com>
 ;; Created: March, 1994
@@ -24,21 +24,15 @@
 
 ;;; Commentary:
 
-;; Thanks to Jim Blandy for mentioning ring.el.  It saved a lot of
-;; time.
-;;
-;; To use this package, put it in a directory in your load-path, and
-;; put this in your init file:
+;; To use this package, add this to your init file:
 ;;
-;; (load "mail-hist" nil t)
+;;     (require 'mail-hist)
 ;;
-;; Or you could do it with autoloads and hooks in your .emacs:
+;; Or you could do it with hooks in your .emacs:
 ;;
-;; (add-hook 'mail-mode-hook 'mail-hist-define-keys)
-;; (add-hook 'mail-send-hook 'mail-hist-put-headers-into-history)
-;; (add-hook 'vm-mail-mode-hook 'mail-hist-define-keys) ;or rmail, etc
-;; (autoload 'mail-hist-define-keys "mail-hist")
-;; (autoload 'mail-hist-put-headers-into-history "mail-hist")
+;;     (add-hook 'mail-mode-hook 'mail-hist-define-keys)
+;;     (add-hook 'mail-send-hook 'mail-hist-put-headers-into-history)
+;;     (add-hook 'vm-mail-mode-hook 'mail-hist-define-keys) ;or rmail, etc
 ;;
 ;; Once it's installed, use M-p and M-n from mail headers to recover
 ;; previous/next contents in the history for that header, or, in the
@@ -51,6 +45,9 @@
 ;; point, so that you can mix the histories of different messages
 ;; easily.  This might be confusing at times, but there should be no
 ;; problems that undo can't handle.
+;;
+;; Thanks to Jim Blandy for mentioning ring.el.  It saved a lot of
+;; time.
 
 ;;; Code:
 (require 'ring)
diff --git a/lisp/mail/rmail.el b/lisp/mail/rmail.el
index f095d5e9c0..e3372a6ff4 100644
--- a/lisp/mail/rmail.el
+++ b/lisp/mail/rmail.el
@@ -1751,6 +1751,7 @@ not be a new one).  It returns non-nil if it got any new 
messages."
            (spam-filter-p (and (featurep 'rmail-spam-filter)
                                rmail-use-spam-filter))
            (blurb "")
+            (mod-p (buffer-modified-p))
            result success suffix)
        (narrow-to-region (point) (point))
        ;; Read in the contents of the inbox files, renaming them as
@@ -1766,10 +1767,11 @@ not be a new one).  It returns non-nil if it got any 
new messages."
                  (rmail-insert-inbox-text files nil)
                (setq delete-files (rmail-insert-inbox-text files t))))
          ;; If there was no new mail, or we aborted before actually
-         ;; trying to get any, mark buffer unmodified.  Otherwise the
-         ;; buffer is correctly marked modified and the file locked
-         ;; until we save out the new mail.
-         (if (= (point-min) (point-max))
+         ;; trying to get any, mark buffer unmodified, unless it was
+         ;; modified originally.  Otherwise the buffer is correctly
+         ;; marked modified and the file locked until we save out the
+         ;; new mail.
+         (if (and (null mod-p) (= (point-min) (point-max)))
              (set-buffer-modified-p nil)))
        ;; Scan the new text and convert each message to
        ;; Rmail/mbox format.
diff --git a/lisp/mail/rmailsum.el b/lisp/mail/rmailsum.el
index b959f45250..0144a34e5e 100644
--- a/lisp/mail/rmailsum.el
+++ b/lisp/mail/rmailsum.el
@@ -50,6 +50,23 @@ Setting this option to nil might speed up the generation of 
summaries."
   :type 'boolean
   :group 'rmail-summary)
 
+(defcustom rmail-summary-apply-filters-consecutively nil
+  "If non-nil, Rmail summary commands apply filtering on top existing 
filtering.
+When this variable is non-nil, `rmail-summary-by-*' commands work on the
+current summary, and so their filtering can be stacked one on top of another.
+This allows gradual narrowing of the selection of the messages."
+  :type 'boolean
+  :version "29.1"
+  :group 'rmail-summary)
+
+(defvar rmail-summary-currently-displayed-msgs nil
+  "String made of `y' and `n'.
+The character at position i tells wether message i is shown in the
+summary or not.  First character is ignored.
+Used when applying `rmail-summary-by-*' commands consecutively.  Filled
+by `rmail-summary-fill-displayed-messages'.")
+(put 'rmail-summary-currently-displayed-msgs 'permanent-local t)
+
 (defvar rmail-summary-font-lock-keywords
   '(("^ *[0-9]+D.*" . font-lock-string-face)                   ; Deleted.
     ("^ *[0-9]+-.*" . font-lock-type-face)                     ; Unread.
@@ -267,6 +284,34 @@ Setting this option to nil might speed up the generation 
of summaries."
 (defun rmail-update-summary (&rest _)
   (apply (car rmail-summary-redo) (cdr rmail-summary-redo)))
 
+(defun rmail-summary-fill-displayed-messages ()
+  "Fill the rmail-summary-currently-displayed-msgs string."
+  (with-current-buffer rmail-buffer
+    (with-current-buffer rmail-summary-buffer
+      (setq rmail-summary-currently-displayed-msgs
+           (make-string (1+ rmail-total-messages) ?n))
+      (goto-char (point-min))
+      (while (not (eobp))
+       (aset rmail-summary-currently-displayed-msgs
+             (string-to-number (thing-at-point 'line))
+             ?y)
+       (forward-line 1)))))
+
+(defun rmail-summary-negate ()
+  "Toggle display of messages that match the summary and those which do not."
+  (interactive)
+  (rmail-summary-fill-displayed-messages)
+  (rmail-new-summary "Negate"
+                    '(rmail-summary-by-regexp ".*")
+                    (lambda (msg)
+                      (if
+                          (= (aref rmail-summary-currently-displayed-msgs msg)
+                             ?n)
+                          (progn
+                            (aset rmail-summary-currently-displayed-msgs msg 
?y) t)
+                        (progn
+                          (aset rmail-summary-currently-displayed-msgs msg ?n) 
nil)))))
+
 ;;;###autoload
 (defun rmail-summary ()
   "Display a summary of all messages, one line per message."
@@ -282,9 +327,16 @@ LABELS should be a string containing the desired labels, 
separated by commas."
       (setq labels (or rmail-last-multi-labels
                       (error "No label specified"))))
   (setq rmail-last-multi-labels labels)
+  (if rmail-summary-apply-filters-consecutively
+      (rmail-summary-fill-displayed-messages))
   (rmail-new-summary (concat "labels " labels)
                     (list 'rmail-summary-by-labels labels)
-                    'rmail-message-labels-p
+                    (if rmail-summary-apply-filters-consecutively
+                        (lambda (msg l)
+                          (and (= (aref rmail-summary-currently-displayed-msgs 
msg)
+                                  ?y)
+                               (rmail-message-labels-p msg l)))
+                      'rmail-message-labels-p)
                     (concat " \\("
                             (mail-comma-list-regexp labels)
                             "\\)\\(,\\|\\'\\)")))
@@ -297,10 +349,18 @@ but if PRIMARY-ONLY is non-nil (prefix arg given),
  only look in the To and From fields.
 RECIPIENTS is a regular expression."
   (interactive "sRecipients to summarize by: \nP")
+  (if rmail-summary-apply-filters-consecutively
+      (rmail-summary-fill-displayed-messages))
   (rmail-new-summary
    (concat "recipients " recipients)
    (list 'rmail-summary-by-recipients recipients primary-only)
-   'rmail-message-recipients-p recipients primary-only))
+   (if rmail-summary-apply-filters-consecutively
+       (lambda (msg r &optional po)
+        (and (= (aref rmail-summary-currently-displayed-msgs msg)
+                ?y)
+             (rmail-message-recipients-p msg r po)))
+     'rmail-message-recipients-p)
+   recipients primary-only))
 
 (defun rmail-message-recipients-p (msg recipients &optional primary-only)
   (rmail-apply-in-message msg 'rmail-message-recipients-p-1
@@ -328,9 +388,16 @@ Emacs will list the message in the summary."
       (setq regexp (or rmail-last-regexp
                         (error "No regexp specified"))))
   (setq rmail-last-regexp regexp)
+  (if rmail-summary-apply-filters-consecutively
+      (rmail-summary-fill-displayed-messages))
   (rmail-new-summary (concat "regexp " regexp)
                     (list 'rmail-summary-by-regexp regexp)
-                    'rmail-message-regexp-p
+                    (if rmail-summary-apply-filters-consecutively
+                        (lambda (msg r)
+                          (and (= (aref rmail-summary-currently-displayed-msgs 
msg)
+                                  ?y)
+                               (rmail-message-regexp-p msg r)))
+                      'rmail-message-regexp-p)
                      regexp))
 
 (defun rmail-message-regexp-p (msg regexp)
@@ -365,7 +432,7 @@ Emacs will list the message in the summary."
 ;;;###autoload
 (defun rmail-summary-by-topic (subject &optional whole-message)
   "Display a summary of all messages with the given SUBJECT.
-Normally checks just the Subject field of headers; but with prefix
+Normally checks just the Subject field of headers; but when prefix
 argument WHOLE-MESSAGE is non-nil, looks in the whole message.
 SUBJECT is a regular expression."
   (interactive
@@ -376,10 +443,18 @@ SUBJECT is a regular expression."
                          (if subject ", default current subject" "")
                          "): ")))
      (list (read-string prompt nil nil subject) current-prefix-arg)))
+  (if rmail-summary-apply-filters-consecutively
+      (rmail-summary-fill-displayed-messages))
   (rmail-new-summary
    (concat "about " subject)
    (list 'rmail-summary-by-topic subject whole-message)
-   'rmail-message-subject-p subject whole-message))
+   (if rmail-summary-apply-filters-consecutively
+       (lambda (msg s &optional wm)
+        (and (= (aref rmail-summary-currently-displayed-msgs msg)
+                ?y)
+             (rmail-message-subject-p msg s wm)))
+     'rmail-message-subject-p)
+   subject whole-message))
 
 (defun rmail-message-subject-p (msg subject &optional whole-message)
   (if whole-message
@@ -402,9 +477,18 @@ sender of the current message."
                          (if sender ", default this message's sender" "")
                          "): ")))
      (list (read-string prompt nil nil sender))))
+  (if rmail-summary-apply-filters-consecutively
+      (rmail-summary-fill-displayed-messages))
   (rmail-new-summary
    (concat "senders " senders)
-   (list 'rmail-summary-by-senders senders) 'rmail-message-senders-p senders))
+   (list 'rmail-summary-by-senders senders)
+   (if rmail-summary-apply-filters-consecutively
+       (lambda (msg s)
+        (and (= (aref rmail-summary-currently-displayed-msgs msg)
+                ?y)
+             (rmail-message-senders-p msg s)))
+     'rmail-message-senders-p)
+   senders))
 
 (defun rmail-message-senders-p (msg senders)
   (string-match senders (or (rmail-get-header "From" msg) "")))
diff --git a/lisp/mail/sendmail.el b/lisp/mail/sendmail.el
index 387792eb31..3f75faa077 100644
--- a/lisp/mail/sendmail.el
+++ b/lisp/mail/sendmail.el
@@ -550,10 +550,10 @@ This also saves the value of `send-mail-function' via 
Customize."
   #'mail-send-and-exit)
 
 ;;;###autoload
-(defun sendmail-user-agent-compose (&optional to subject other-headers
-                                   continue switch-function yank-action
-                                   send-actions return-action
-                                   &rest ignored)
+(defun sendmail-user-agent-compose ( &optional to subject other-headers
+                                     continue switch-function yank-action
+                                     send-actions return-action
+                                     &rest _ignored )
   (if switch-function
       (funcall switch-function "*mail*"))
   (let ((cc (cdr (assoc-string "cc" other-headers t)))
diff --git a/lisp/man.el b/lisp/man.el
index 7ba7bee417..6c50f017e3 100644
--- a/lisp/man.el
+++ b/lisp/man.el
@@ -451,50 +451,45 @@ Otherwise, the value is whatever the function
     table)
   "Syntax table used in Man mode buffers.")
 
-(defvar Man-mode-map
-  (let ((map (make-sparse-keymap)))
-    (suppress-keymap map)
-    (set-keymap-parent map
-      (make-composed-keymap button-buffer-map special-mode-map))
-
-    (define-key map "n"    'Man-next-section)
-    (define-key map "p"    'Man-previous-section)
-    (define-key map "\en"  'Man-next-manpage)
-    (define-key map "\ep"  'Man-previous-manpage)
-    (define-key map "."    'beginning-of-buffer)
-    (define-key map "r"    'Man-follow-manual-reference)
-    (define-key map "g"    'Man-goto-section)
-    (define-key map "s"    'Man-goto-see-also-section)
-    (define-key map "k"    'Man-kill)
-    (define-key map "u"    'Man-update-manpage)
-    (define-key map "m"    'man)
-    ;; Not all the man references get buttons currently.  The text in the
-    ;; manual page can contain references to other man pages
-    (define-key map "\r"   'man-follow)
-
-    (easy-menu-define nil map
-      "`Man-mode' menu."
-      '("Man"
-        ["Next Section" Man-next-section t]
-        ["Previous Section" Man-previous-section t]
-        ["Go To Section..." Man-goto-section t]
-        ["Go To \"SEE ALSO\" Section" Man-goto-see-also-section
-         :active (cl-member Man-see-also-regexp Man--sections
-                            :test #'string-match-p)]
-        ["Follow Reference..." Man-follow-manual-reference
-         :active Man--refpages
-         :help "Go to a manpage referred to in the \"SEE ALSO\" section"]
-        "--"
-        ["Next Manpage" Man-next-manpage
-         :active (> (length Man-page-list) 1)]
-        ["Previous Manpage" Man-previous-manpage
-         :active (> (length Man-page-list) 1)]
-        "--"
-        ["Man..." man t]
-        ["Kill Buffer" Man-kill t]
-        ["Quit" quit-window t]))
-    map)
-  "Keymap for Man mode.")
+(defvar-keymap Man-mode-map
+  :doc "Keymap for Man mode."
+  :suppress t
+  :parent (make-composed-keymap button-buffer-map special-mode-map)
+  "n"   #'Man-next-section
+  "p"   #'Man-previous-section
+  "M-n" #'Man-next-manpage
+  "M-p" #'Man-previous-manpage
+  "."   #'beginning-of-buffer
+  "r"   #'Man-follow-manual-reference
+  "g"   #'Man-goto-section
+  "s"   #'Man-goto-see-also-section
+  "k"   #'Man-kill
+  "u"   #'Man-update-manpage
+  "m"   #'man
+  ;; Not all the man references get buttons currently.  The text in the
+  ;; manual page can contain references to other man pages
+  "RET" #'man-follow
+
+  :menu
+  '("Man"
+    ["Next Section" Man-next-section t]
+    ["Previous Section" Man-previous-section t]
+    ["Go To Section..." Man-goto-section t]
+    ["Go To \"SEE ALSO\" Section" Man-goto-see-also-section
+     :active (cl-member Man-see-also-regexp Man--sections
+                        :test #'string-match-p)]
+    ["Follow Reference..." Man-follow-manual-reference
+     :active Man--refpages
+     :help "Go to a manpage referred to in the \"SEE ALSO\" section"]
+    "--"
+    ["Next Manpage" Man-next-manpage
+     :active (> (length Man-page-list) 1)]
+    ["Previous Manpage" Man-previous-manpage
+     :active (> (length Man-page-list) 1)]
+    "--"
+    ["Man..." man t]
+    ["Kill Buffer" Man-kill t]
+    ["Quit" quit-window t]))
 
 ;; buttons
 (define-button-type 'Man-abstract-xref-man-page
diff --git a/lisp/menu-bar.el b/lisp/menu-bar.el
index c2c18320b1..849e0f7723 100644
--- a/lisp/menu-bar.el
+++ b/lisp/menu-bar.el
@@ -527,12 +527,12 @@
       `(menu-item "Paste" yank
                   :enable (funcall
                            ',(lambda ()
-                               (and (or
+                               (and (not buffer-read-only)
+                                    (or
                                      (gui-backend-selection-exists-p 
'CLIPBOARD)
                                      (if (featurep 'ns) ; like paste-from-menu
                                          (cdr yank-menu)
-                                       kill-ring))
-                                    (not buffer-read-only))))
+                                       kill-ring)))))
                   :help "Paste (yank) text most recently cut/copied"
                   :keys ,(lambda ()
                            (if cua-mode
@@ -1847,6 +1847,10 @@ mail status in mode line"))
                   :help "Toggle automatic parsing in source code buffers 
(Semantic mode)"
                   :button (:toggle . (bound-and-true-p semantic-mode))))
 
+    (bindings--define-key menu [eglot]
+      '(menu-item "Language Server Support (Eglot)" eglot
+                  :help "Start language server suitable for this buffer's 
major-mode"))
+
     (bindings--define-key menu [ede]
       '(menu-item "Project Support (EDE)"
                   global-ede-mode
diff --git a/lisp/mh-e/ChangeLog.1 b/lisp/mh-e/ChangeLog.1
index 00e52df2bb..c7f5586140 100644
--- a/lisp/mh-e/ChangeLog.1
+++ b/lisp/mh-e/ChangeLog.1
@@ -8064,7 +8064,7 @@
 
        * mh-e.el (mh-last-msg): Add call to mh-recenter.
 
-2002-10-26  Peter S Galbraith  <psg@debia.org>
+2002-10-26  Peter S Galbraith  <psg@debian.org>
 
        * mh-comp.el (mh-search-addr-regexp, mh-re-search-to-cc): Remove
        `mh-re-search-to-cc' in favor of more generalized new function
diff --git a/lisp/mh-e/mh-junk.el b/lisp/mh-e/mh-junk.el
index be1b7642eb..38a8216dc0 100644
--- a/lisp/mh-e/mh-junk.el
+++ b/lisp/mh-e/mh-junk.el
@@ -402,7 +402,7 @@ information can be used so that you can replace multiple
 
 Bogofilter is a Bayesian spam filtering program. Get it from your
 local distribution or from the bogofilter web site at URL
-`http://bogofilter.sourceforge.net/'.
+`https://bogofilter.sourceforge.io/'.
 
 Bogofilter is taught by running:
 
@@ -487,7 +487,7 @@ See `mh-bogofilter-blocklist' for more information."
 
 SpamProbe is a Bayesian spam filtering program. Get it from your
 local distribution or from the SpamProbe web site at URL
-`http://spamprobe.sourceforge.net'.
+`https://spamprobe.sourceforge.net'.
 
 To use SpamProbe, add the following recipes to \".procmailrc\":
 
diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index 9f26e4f7f9..6bb0fa3ae9 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -850,7 +850,88 @@ via `set-message-function'."
         ;; was handled specially by this function.
         t))))
 
-(setq set-message-function 'set-minibuffer-message)
+(setq set-message-function 'set-message-functions)
+
+(defcustom set-message-functions '(set-minibuffer-message)
+  "List of functions to handle display of echo-area messages.
+Each function is called with one argument that is the text of a message.
+If a function returns nil, a previous message string is given to the
+next function in the list, and if the last function returns nil, the
+last message string is displayed in the echo area.
+If a function returns a string, the returned string is given to the
+next function in the list, and if the last function returns a string,
+it's displayed in the echo area.
+If a function returns any other non-nil value, no more functions are
+called from the list, and no message will be displayed in the echo area."
+  :type '(choice (const :tag "No special message handling" nil)
+                 (repeat
+                  (choice (function-item :tag "Inhibit some messages"
+                                         inhibit-message)
+                          (function-item :tag "Accumulate messages"
+                                         set-multi-message)
+                          (function-item :tag "Handle minibuffer"
+                                         set-minibuffer-message)
+                          (function :tag "Custom function"))))
+  :version "29.1")
+
+(defun set-message-functions (message)
+  (run-hook-wrapped 'set-message-functions
+                    (lambda (fun)
+                      (when (stringp message)
+                        (let ((ret (funcall fun message)))
+                          (when ret (setq message ret))))
+                      nil))
+  message)
+
+(defcustom inhibit-message-regexps nil
+  "List of regexps that inhibit messages by the function `inhibit-message'."
+  :type '(repeat regexp)
+  :version "29.1")
+
+(defun inhibit-message (message)
+  "Don't display MESSAGE when it matches the regexp `inhibit-message-regexps'.
+This function is intended to be added to `set-message-functions'."
+  (or (and (consp inhibit-message-regexps)
+           (string-match-p (mapconcat #'identity inhibit-message-regexps "\\|")
+                           message))
+      message))
+
+(defcustom multi-message-timeout 2
+  "Number of seconds between messages before clearing the accumulated list."
+  :type 'number
+  :version "29.1")
+
+(defcustom multi-message-max 8
+  "Max size of the list of accumulated messages."
+  :type 'number
+  :version "29.1")
+
+(defvar multi-message-separator "\n")
+
+(defvar multi-message-list nil)
+
+(defun set-multi-message (message)
+  "Return recent messages as one string to display in the echo area.
+Note that this feature works best only when `resize-mini-windows'
+is at its default value `grow-only'."
+  (let ((last-message (car multi-message-list)))
+    (unless (and last-message (equal message (aref last-message 1)))
+      (when last-message
+        (cond
+         ((> (float-time) (+ (aref last-message 0) multi-message-timeout))
+          (setq multi-message-list nil))
+         ((or
+           ;; `message-log-max' was nil, potential clutter.
+           (aref last-message 2)
+           ;; Remove old message that is substring of the new message
+           (string-prefix-p (aref last-message 1) message))
+          (setq multi-message-list (cdr multi-message-list)))))
+      (push (vector (float-time) message (not message-log-max)) 
multi-message-list)
+      (when (> (length multi-message-list) multi-message-max)
+        (setf (nthcdr multi-message-max multi-message-list) nil)))
+    (mapconcat (lambda (m) (aref m 1))
+               (reverse multi-message-list)
+               multi-message-separator)))
 
 (defun clear-minibuffer-message ()
   "Clear minibuffer message.
@@ -972,10 +1053,18 @@ ALL-COMPLETIONS is the function that lists the 
completions (it should
 follow the calling convention of `completion-all-completions'),
 and DOC describes the way this style of completion works.")
 
+(defun completion--update-styles-options (widget)
+  "Function to keep updated the options in `completion-category-overrides'."
+  (let ((lst (mapcar (lambda (x)
+                       (list 'const (car x)))
+                    completion-styles-alist)))
+    (widget-put widget :args (mapcar #'widget-convert lst))
+    widget))
+
 (defconst completion--styles-type
   `(repeat :tag "insert a new menu to add more styles"
-           (choice ,@(mapcar (lambda (x) (list 'const (car x)))
-                             completion-styles-alist))))
+           (choice :convert-widget completion--update-styles-options)))
+
 (defconst completion--cycling-threshold-type
   '(choice (const :tag "No cycling" nil)
            (const :tag "Always cycle" t)
@@ -1237,9 +1326,9 @@ pair of a group title string and a list of group 
candidate strings."
   :version "28.1")
 
 (defface completions-group-separator
-  '((t :inherit shadow :strike-through t))
+  '((t :inherit shadow :underline t))
   "Face used for the separator lines between the candidate groups."
-  :version "28.1")
+  :version "29.1")
 
 (defun completion--cycle-threshold (metadata)
   (let* ((cat (completion-metadata-get metadata 'category))
diff --git a/lisp/net/ange-ftp.el b/lisp/net/ange-ftp.el
index 6ffa65a2dd..d6d0fb9a25 100644
--- a/lisp/net/ange-ftp.el
+++ b/lisp/net/ange-ftp.el
@@ -4242,7 +4242,7 @@ directory, so that Emacs will know its current contents."
          ((eq identification 'localname) localname)
          (t (ange-ftp-replace-name-component file ""))))))
 
-(defun ange-ftp-load (file &optional noerror nomessage nosuffix)
+(defun ange-ftp-load (file &optional noerror nomessage nosuffix must-suffix)
   (if (ange-ftp-ftp-name file)
       (let ((tryfiles (if nosuffix
                          (list file)
@@ -4264,7 +4264,7 @@ directory, so that Emacs will know its current contents."
          (or noerror
              (signal 'file-error (list "Cannot open load file" file)))
          nil))
-    (ange-ftp-real-load file noerror nomessage nosuffix)))
+    (ange-ftp-real-load file noerror nomessage nosuffix must-suffix)))
 
 ;; Calculate default-unhandled-directory for a given ange-ftp buffer.
 (defun ange-ftp-unhandled-file-name-directory (_filename)
diff --git a/lisp/net/dbus.el b/lisp/net/dbus.el
index 6c978c5a5f..9f0ad7b83c 100644
--- a/lisp/net/dbus.el
+++ b/lisp/net/dbus.el
@@ -37,6 +37,7 @@
 (declare-function dbus-message-internal "dbusbind.c")
 (declare-function dbus--init-bus "dbusbind.c")
 (declare-function libxml-parse-xml-region "xml.c")
+(defvar dbus-debug)
 (defvar dbus-message-type-invalid)
 (defvar dbus-message-type-method-call)
 (defvar dbus-message-type-method-return)
diff --git a/lisp/net/dictionary.el b/lisp/net/dictionary.el
index 4c52382c67..315f7e5f52 100644
--- a/lisp/net/dictionary.el
+++ b/lisp/net/dictionary.el
@@ -341,7 +341,8 @@ is utf-8"
   "p"     #'backward-button
   "SPC"   #'scroll-up-command
   "S-SPC" #'scroll-down-command
-  "M-SPC" #'scroll-down-command)
+  "M-SPC" #'scroll-down-command
+  "DEL"   #'scroll-down-command)
 
 (defvar dictionary-connection
   nil
@@ -1150,9 +1151,7 @@ It presents the selection or word at point as default 
input and
 allows editing it."
   (interactive
    (list (let ((default (dictionary-search-default)))
-           (read-string (if default
-                            (format "Search word (%s): " default)
-                          "Search word: ")
+           (read-string (format-prompt "Search word" default)
                         nil 'dictionary-word-history default))
         (if current-prefix-arg
             (read-string (if dictionary-default-dictionary
@@ -1173,7 +1172,10 @@ allows editing it."
 (defun dictionary-lookup-definition ()
   "Unconditionally lookup the word at point."
   (interactive)
-  (dictionary-new-search (cons (current-word) dictionary-default-dictionary)))
+  (let ((word (current-word)))
+    (unless word
+      (error "No word at point"))
+    (dictionary-new-search (cons word dictionary-default-dictionary))))
 
 (defun dictionary-previous ()
   "Go to the previous location in the current buffer."
diff --git a/lisp/net/eudc-vars.el b/lisp/net/eudc-vars.el
index dea17f3424..b44989d906 100644
--- a/lisp/net/eudc-vars.el
+++ b/lisp/net/eudc-vars.el
@@ -38,6 +38,9 @@
 
 (defcustom eudc-server nil
   "The name or IP address of the directory server.
+This variable is deprecated as of Emacs 29.1.  Please add an
+entry to `eudc-server-hotlist' instead of setting `eudc-server'.
+
 A port number may be specified by appending a colon and a
 number to the name of the server.  Use `localhost' if the directory
 server resides on your computer (BBDB backend).
@@ -48,9 +51,10 @@ instead."
 
 ;; Known protocols (used in completion)
 ;; Not to be mistaken with `eudc-supported-protocols'
-(defvar eudc-known-protocols '(bbdb ldap))
+(defvar eudc-known-protocols '(bbdb ldap ecomplete mailabbrev))
 
-(defcustom eudc-server-hotlist nil
+(defcustom eudc-server-hotlist '(("localhost" . ecomplete)
+                                 ("localhost" . mailabbrev))
   "Directory servers to query.
 This is an alist of the form (SERVER . PROTOCOL).  SERVER is the
 host name or URI of the server, PROTOCOL is a symbol representing
@@ -343,9 +347,15 @@ arguments that should be passed to the program."
                        :inline t
                        (string :tag "Argument")))))
 
+(defcustom eudc-ignore-options-file nil
+  "Ignore configuration in `eudc-options-file', if non-nil."
+  :type  'boolean
+  :version "29.1")
+
 (defcustom eudc-options-file
   (locate-user-emacs-file "eudc-options" ".eudc-options")
-  "A file where the `servers' hotlist is stored."
+  "A file where the `servers' hotlist is stored.
+See `eudc-ignore-options-file'."
   :type '(file :Tag "File Name:")
   :version "25.1")
 
diff --git a/lisp/net/eudc.el b/lisp/net/eudc.el
index 40cb25fca2..8319c048e2 100644
--- a/lisp/net/eudc.el
+++ b/lisp/net/eudc.el
@@ -106,44 +106,39 @@
    ;; Split the string just in case.
    (version<= "3" (car (split-string bbdb-version)))))
 
+(defun eudc--plist-member (plist prop &optional predicate)
+  "Like `plist-member', but signal on invalid PLIST."
+  (or (plistp plist)
+      (signal 'wrong-type-argument `(plistp ,plist)))
+  (plist-member plist prop predicate))
+
 (defun eudc-plist-member (plist prop)
-  "Return t if PROP has a value specified in PLIST."
-  (if (not (= 0 (% (length plist) 2)))
-      (error "Malformed plist"))
-  (catch 'found
-    (while plist
-      (if (eq prop (car plist))
-         (throw 'found t))
-      (setq plist (cdr (cdr plist))))
-    nil))
+  "Return t if PROP has a value specified in PLIST.
+Signal an error if PLIST is not a valid property list."
+  (and (eudc--plist-member plist prop) t))
 
-;; Emacs's plist-get lacks third parameter
+;; Emacs's `plist-get' lacks a default parameter, and CL-Lib's
+;; `cl-getf' doesn't accept a predicate or signal an error.
 (defun eudc-plist-get (plist prop &optional default)
-  "Extract a value from a property list.
-PLIST is a property list, which is a list of the form
-\(PROP1 VALUE1 PROP2 VALUE2...).  This function returns the value
-corresponding to the given PROP, or DEFAULT if PROP is not
-one of the properties on the list."
-  (if (eudc-plist-member plist prop)
-      (plist-get plist prop)
-    default))
+  "Extract the value of PROP in property list PLIST.
+PLIST is a list of the form (PROP1 VALUE1 PROP2 VALUE2...).
+This function returns the first value corresponding to the given
+PROP, or DEFAULT if PROP is not one of the properties in the
+list.  The comparison with PROP is done using `eq'.  If PLIST is
+not a valid property list, this function signals an error."
+  (let ((tail (eudc--plist-member plist prop)))
+    (if tail (cadr tail) default)))
 
 (defun eudc-lax-plist-get (plist prop &optional default)
-  "Extract a value from a lax property list.
-
-PLIST is a lax property list, which is a list of the form (PROP1
-VALUE1 PROP2 VALUE2...), where comparisons between properties are done
-using `equal' instead of `eq'.  This function returns the value
-corresponding to PROP, or DEFAULT if PROP is not one of the
-properties on the list."
-  (if (not (= 0 (% (length plist) 2)))
-      (error "Malformed plist"))
-  (catch 'found
-    (while plist
-      (if (equal prop (car plist))
-         (throw 'found (car (cdr plist))))
-      (setq plist (cdr (cdr plist))))
-    default))
+  "Extract the value of PROP from lax property list PLIST.
+PLIST is a list of the form (PROP1 VALUE1 PROP2 VALUE2...), where
+comparisons between properties are done using `equal' instead of
+`eq'.  This function returns the first value corresponding to
+PROP, or DEFAULT if PROP is not one of the properties in the
+list.  If PLIST is not a valid property list, this function
+signals an error."
+  (let ((tail (eudc--plist-member plist prop #'equal)))
+    (if tail (cadr tail) default)))
 
 (defun eudc-replace-in-string (str regexp newtext)
   "Replace all matches in STR for REGEXP with NEWTEXT.
@@ -731,7 +726,8 @@ server for future sessions."
   (if (called-interactively-p 'interactive)
       (message "Current directory server is now %s (%s)" eudc-server 
eudc-protocol))
   (if (null no-save)
-      (eudc-save-options)))
+      (when (not eudc-ignore-options-file)
+       (eudc-save-options))))
 
 ;;;###autoload
 (defun eudc-get-email (name &optional error)
@@ -1112,7 +1108,11 @@ queries the server for the existing fields and displays 
a corresponding form."
       (error "%s:%s is already in the hotlist" protocol server)
     (setq eudc-server-hotlist (cons (cons server protocol) 
eudc-server-hotlist))
     (eudc-install-menu)
-    (eudc-save-options)))
+    (if eudc-ignore-options-file
+       (warn "Not saving bookmark due to `eudc-ignore-options-file'\
+ customization. Instead, customize `eudc-server-hotlist' to include %s:%s"
+             protocol server)
+      (eudc-save-options))))
 
 (defun eudc-bookmark-current-server ()
   "Add current server to the EUDC `servers' hotlist."
@@ -1122,6 +1122,9 @@ queries the server for the existing fields and displays a 
corresponding form."
 (defun eudc-save-options ()
   "Save options to `eudc-options-file'."
   (interactive)
+  (when eudc-ignore-options-file
+    (error "EUDC is configured to ignore the deprecated options file;\
+ see `eudc-ignore-options-file'"))
   (with-current-buffer (find-file-noselect eudc-options-file t)
     (goto-char (point-min))
     ;; delete the previous setq
@@ -1283,11 +1286,13 @@ queries the server for the existing fields and displays 
a corresponding form."
 ;;{{{ Load time initializations
 
 ;; Load the options file
-(if (and (not noninteractive)
-        (and (locate-library eudc-options-file)
-             (progn (message "") t))   ; Remove mode line message
-        (not (featurep 'eudc-options-file)))
-    (load eudc-options-file))
+(let ((library-file-path (locate-library eudc-options-file)))
+  (if (and (not noninteractive)
+          (and library-file-path
+               (progn (message "") t))   ; Remove mode line message
+          (not (featurep 'eudc-options-file))
+          (not eudc-ignore-options-file))
+      (load eudc-options-file)))
 
 ;; Install the full menu
 (unless (featurep 'infodock)
diff --git a/lisp/net/eudcb-ecomplete.el b/lisp/net/eudcb-ecomplete.el
new file mode 100644
index 0000000000..55011d29f6
--- /dev/null
+++ b/lisp/net/eudcb-ecomplete.el
@@ -0,0 +1,108 @@
+;;; eudcb-ecomplete.el --- EUDC - ecomplete backend -*- lexical-binding: t -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+;;
+;; Author: Alexander Adolf
+;;
+;; 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.
+;;
+;;    For example, if your `ecomplete-database-file' (typically
+;;    ~/.emacs.d/ecompleterc) contains:
+;;
+;;    ((mail ("larsi@gnus.org" 38154 1516109510 "Lars <larsi@ecomplete.org>")))
+;;
+;;    Then:
+;;
+;;    C-x m lars C-u M-x eudc-expand-try-all RET
+;;
+;;    should expand the email address into the To: field of the new
+;;    message.
+
+;;; Code:
+
+(require 'eudc)
+(require 'ecomplete)
+(require 'mail-parse)
+
+(defvar eudc-ecomplete-attributes-translation-alist
+  '((email     . mail))
+  "See `eudc-protocol-attributes-translation-alist'.
+The back-end-specific attribute names are used as the \"type\" of
+entry when searching, and they must hence match the types you use
+in your ecompleterc database file.")
+
+;; hook ourselves into the EUDC framework
+(eudc-protocol-set 'eudc-query-function
+                  'eudc-ecomplete-query-internal
+                  'ecomplete)
+(eudc-protocol-set 'eudc-list-attributes-function
+                  nil
+                  'ecomplete)
+(eudc-protocol-set 'eudc-protocol-attributes-translation-alist
+                  'eudc-ecomplete-attributes-translation-alist
+                  'ecomplete)
+(eudc-protocol-set 'eudc-protocol-has-default-query-attributes
+                  nil
+                  'ecomplete)
+
+;;;###autoload
+(defun eudc-ecomplete-query-internal (query &optional _return-attrs)
+  "Query `ecomplete' with QUERY.
+QUERY is a list of cons cells (ATTR . VALUE).  Since `ecomplete'
+does not provide attributes in the usual sense, the
+back-end-specific attribute names in
+`eudc-ecomplete-attributes-translation-alist' are used as the
+KEY (that is, the \"type\" of match) when looking for matches in
+`ecomplete-database'.
+
+RETURN-ATTRS is ignored." ; FIXME: why is this being ignored?
+  (ecomplete-setup)
+  (let ((email-attr (car (eudc-translate-attribute-list '(email))))
+        result)
+    (dolist (term query)
+      (let* ((attr (car term))
+             (value (cdr term))
+             (matches (ecomplete-get-matches attr value)))
+        (when matches
+          (dolist (match (split-string (string-trim (substring-no-properties
+                                                     matches))
+                                       "[\n\r]"))
+            ;; Try to decompose the email address.
+            (let* ((decoded (mail-header-parse-address match t))
+                   (name (cdr decoded))
+                   (email (car decoded)))
+              (if (and decoded (eq attr email-attr))
+                  ;; The email could be decomposed, push individual
+                  ;; fields.
+                  (push `((,attr . ,email)
+                          ,@(when name (list (cons 'name name))))
+                        result)
+                ;; Otherwise just forward the value as-is.
+                (push (list (cons attr match)) result)))))))
+    result))
+
+(eudc-register-protocol 'ecomplete)
+
+(provide 'eudcb-ecomplete)
+;;; eudcb-ecomplete.el ends here
diff --git a/lisp/net/eudcb-mailabbrev.el b/lisp/net/eudcb-mailabbrev.el
new file mode 100644
index 0000000000..64b50af09b
--- /dev/null
+++ b/lisp/net/eudcb-mailabbrev.el
@@ -0,0 +1,127 @@
+;;; eudcb-mailabbrev.el --- EUDC - mailabbrev backend -*- lexical-binding: t 
-*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+;;
+;; Author: Alexander Adolf
+;;
+;; 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.
+;;
+;;    For example, if your `mail-personal-alias-file' (typically
+;;    ~/.mailrc) contains:
+;;
+;;    alias lars "Lars <larsi@mail-abbrev.com>"
+;;
+;;    Then:
+;;
+;;    C-x m lars C-u M-x eudc-expand-try-all RET
+;;
+;;    will expand the correct email address into the To: field of the
+;;    new message.
+
+;;; Code:
+
+(require 'eudc)
+(require 'mailabbrev)
+(require 'mail-parse)
+
+;; hook ourselves into the EUDC framework
+(eudc-protocol-set 'eudc-query-function
+                  'eudc-mailabbrev-query-internal
+                  'mailabbrev)
+(eudc-protocol-set 'eudc-list-attributes-function
+                  nil
+                  'mailabbrev)
+(eudc-protocol-set 'eudc-protocol-attributes-translation-alist
+                  nil
+                  'mailabbrev)
+(eudc-protocol-set 'eudc-protocol-has-default-query-attributes
+                  nil
+                  'mailabbrev)
+;;;###autoload
+(defun eudc-mailabbrev-query-internal (query &optional _return-attrs)
+  "Query `mailabbrev' with QUERY.
+QUERY is a list of cons cells (ATTR . VALUE).  Since `mailabbrev'
+does not provide attributes in the usual sense, only the email,
+name, and firstname attributes in the QUERY are considered, and
+their values are matched against the alias names in the mailrc
+file.  When a mailrc alias is a distribution list, that is it
+expands to more that one email address, the individual recipient
+specifications are formatted using `eudc-rfc5322-make-address',
+and returned as a comma-separated list in the email address
+attribute.
+
+RETURN-ATTRS is a list of attributes to return, defaulting to
+`eudc-default-return-attributes'."
+  (mail-abbrevs-setup)
+  (let (result)
+    (dolist (term query)
+      (let* ((attr (car term))
+             (value (cdr term))
+             (raw-matches (symbol-value (intern-soft value mail-abbrevs))))
+        (when (and raw-matches
+                   (memq attr '(email firstname name)))
+          (let* ((matches (split-string raw-matches ", "))
+                 (num-matches (length matches)))
+            (if (> num-matches 1)
+                ;; multiple matches: distribution list
+                (let ((distr-str (string)))
+                  (dolist (recipient matches)
+                    ;; try to decompose email construct
+                    (let* ((decoded (mail-header-parse-address recipient t))
+                           (name (cdr decoded))
+                           (email (car decoded)))
+                      (if decoded
+                          ;; decoding worked, push rfc5322 rendered address
+                          (setq distr-str
+                                (copy-sequence
+                                 (concat distr-str ", "
+                                         (eudc-rfc5322-make-address email
+                                                                    nil
+                                                                    name))))
+                        ;; else, just forward the value as-is
+                        (setq distr-str
+                              (copy-sequence
+                               (concat distr-str ", " recipient))))))
+                  ;; push result, removing the leading ", "
+                  (push (list (cons 'email (substring distr-str 2 -1)))
+                        result))
+              ;; simple case: single match
+              (let* ((match (car matches))
+                     (decoded (mail-header-parse-address match t))
+                     (name (cdr decoded))
+                     (email (car decoded)))
+                (if decoded
+                    ;; decoding worked, push individual fields
+                    (push `((email . ,email)
+                            ,@(when name (list (cons 'name name))))
+                          result)
+                  ;; else, just forward the value as-is
+                  (push (list (cons 'email match)) result))))))))
+    result))
+
+(eudc-register-protocol 'mailabbrev)
+
+(provide 'eudcb-mailabbrev)
+
+;;; eudcb-mailabbrev.el ends here
diff --git a/lisp/net/eww.el b/lisp/net/eww.el
index 414de931c4..3799ef96e8 100644
--- a/lisp/net/eww.el
+++ b/lisp/net/eww.el
@@ -1596,7 +1596,8 @@ See URL 
`https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Input'.")
                       (list :eww-form eww-form
                             :value value
                             :type "textarea"
-                            :name (dom-attr dom 'name)))))
+                            :name (dom-attr dom 'name)))
+    (put-text-property start (1+ start) 'shr-tab-stop t)))
 
 (defun eww-tag-input (dom)
   (let ((type (downcase (or (dom-attr dom 'type) "text")))
@@ -1660,7 +1661,8 @@ See URL 
`https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Input'.")
       (add-face-text-property start (point) 'eww-form-select)
       (put-text-property start (point) 'keymap eww-select-map)
       (unless (= start (point))
-       (put-text-property start (1+ start) 'help-echo "select field"))
+       (put-text-property start (1+ start) 'help-echo "select field")
+       (put-text-property start (1+ start) 'shr-tab-stop t))
       (shr-ensure-paragraph))))
 
 (defun eww-select-display (select)
diff --git a/lisp/net/ldap.el b/lisp/net/ldap.el
index 062ff05d69..de553468b1 100644
--- a/lisp/net/ldap.el
+++ b/lisp/net/ldap.el
@@ -156,7 +156,7 @@ Valid properties include:
   "The name of the ldapsearch command line program."
   :type '(string :tag "`ldapsearch' Program"))
 
-(defcustom ldap-ldapsearch-args '("-LLL" "-tt")
+(defcustom ldap-ldapsearch-args nil
   "A list of additional arguments to pass to `ldapsearch'."
   :type '(repeat :tag "`ldapsearch' Arguments"
                 (string :tag "Argument")))
@@ -609,7 +609,8 @@ an alist of attribute/value pairs."
        (sizelimit (plist-get search-plist 'sizelimit))
        (withdn (plist-get search-plist 'withdn))
        (numres 0)
-       arglist dn name value record result)
+        (arglist (list "-LLL" "-tt"))
+       dn name value record result)
     (if (or (null filter)
            (equal "" filter))
        (error "No search filter"))
@@ -715,14 +716,14 @@ an alist of attribute/value pairs."
                      (eq (string-match "/\\(.:.*\\)$" value) 0))
                 (setq value (match-string 1 value)))
            ;; Do not try to open non-existent files
-           (if (equal value "")
-               (setq value " ")
-             (with-current-buffer bufval
+            (if (match-string 3)
+              (with-current-buffer bufval
                (erase-buffer)
                (set-buffer-multibyte nil)
                (insert-file-contents-literally value)
                (delete-file value)
-               (setq value (buffer-string))))
+               (setq value (buffer-string)))
+              (setq value " "))
            (setq record (cons (list name value)
                               record))
            (forward-line 1))
diff --git a/lisp/net/network-stream.el b/lisp/net/network-stream.el
index 38a5e14c94..2b7e539392 100644
--- a/lisp/net/network-stream.el
+++ b/lisp/net/network-stream.el
@@ -173,6 +173,9 @@ a greeting from the server.
 :nowait, if non-nil, says the connection should be made
 asynchronously, if possible.
 
+:noquery - when exiting Emacs and the network process is running,
+don't query the user if it's non-nil.
+
 :shell-command is a `format-spec' string that can be used if
 :type is `shell'.  It has two specs, %s for host and %p for port
 number.  Example: \"ssh gateway nc %s %p\".
@@ -195,6 +198,7 @@ gnutls-boot (as returned by `gnutls-boot-parameters')."
        (make-network-process :name name :buffer buffer
                              :host (puny-encode-domain host) :service service
                              :nowait (plist-get parameters :nowait)
+                             :noquery (plist-get parameters :noquery)
                               :tls-parameters
                               (plist-get parameters :tls-parameters)
                               :coding (plist-get parameters :coding))
diff --git a/lisp/net/newst-backend.el b/lisp/net/newst-backend.el
index f65ef522f2..af196ccecf 100644
--- a/lisp/net/newst-backend.el
+++ b/lisp/net/newst-backend.el
@@ -40,7 +40,6 @@
 
 ;; Silence warnings
 (defvar newsticker-groups)
-(defvar w3m-minor-mode-map)
 
 (defvar newsticker--retrieval-timer-list nil
   "List of timers for news retrieval.
diff --git a/lisp/net/rcirc.el b/lisp/net/rcirc.el
index fa481ce528..370f388b3e 100644
--- a/lisp/net/rcirc.el
+++ b/lisp/net/rcirc.el
@@ -1049,7 +1049,7 @@ Each element has the form (TYPE HANDLE), where TYPE is a 
string
 and HANDLE is either the symbol `immediate' or `deferred'.
 Messages in an immediate batch are handled just like regular
 messages, while deferred messages are stored in
-`rcirc-batch-messages'.")
+`rcirc-batched-messages'.")
 
 (defvar-local rcirc-batch-attributes nil
   "Alist mapping batch IDs to parameters.")
@@ -2066,7 +2066,8 @@ connection."
           (set-marker-insertion-type rcirc-prompt-end-marker t)
 
           ;; run markup functions
-          (cl-assert (bolp))
+          (unless (bolp)
+            (newline))
           (save-excursion
             (save-restriction
               (narrow-to-region (point) (point))
@@ -2176,9 +2177,11 @@ connection."
 
 (defun rcirc-generate-log-filename (process target)
   "Return filename for log file based on PROCESS and TARGET."
-  (if target
-      (rcirc-generate-new-buffer-name process target)
-    (process-name process)))
+  (concat
+   (if target
+       (rcirc-generate-new-buffer-name process target)
+     (process-name process))
+   ".log"))
 
 (defcustom rcirc-log-filename-function 'rcirc-generate-log-filename
   "A function to generate the filename used by rcirc's logging facility.
@@ -3018,11 +3021,7 @@ for nick completion."
   :version "29.1")
 
 (defface rcirc-bridged-nick
-  '((((class color) (min-colors 88) (background light)) :background 
"SlateGray1")
-    (((class color) (min-colors 88) (background dark))  :background 
"DarkSlateGray4")
-    (((class color) (min-colors 16) (background light)) :background 
"LightBlue")
-    (((class color) (min-colors 16) (background dark))  :background 
"DarkSlateGray")
-    (t :background "blue"))
+  '((t :inherit highlight))
   "Face used for pseudo-nick ."
   :version "29.1")
 
diff --git a/lisp/net/sieve-manage.el b/lisp/net/sieve-manage.el
index 381e1fcd4f..b2caa62e51 100644
--- a/lisp/net/sieve-manage.el
+++ b/lisp/net/sieve-manage.el
@@ -385,7 +385,7 @@ Return the buffer associated with the connection."
 (defun sieve-manage-open (server &optional port stream auth buffer)
   "Open a network connection to a managesieve SERVER (string).
 Optional argument PORT is port number (integer) on remote server.
-Optional argument STREAM is any of `sieve-manage-streams' (a symbol).
+Optional argument STREAM is any of `sieve-manage-stream' (a symbol).
 Optional argument AUTH indicates authenticator to use, see
 `sieve-manage-authenticators' for available authenticators.
 If nil, chooses the best stream the server is capable of.
diff --git a/lisp/net/tramp-cache.el b/lisp/net/tramp-cache.el
index 4d7d35a4de..912ea5f8bb 100644
--- a/lisp/net/tramp-cache.el
+++ b/lisp/net/tramp-cache.el
@@ -601,19 +601,30 @@ PROPERTIES is a list of file properties (strings)."
            (remove-hook 'kill-emacs-hook
                         #'tramp-dump-connection-properties)))
 
+;;;###tramp-autoload
+(defcustom tramp-completion-use-cache t
+  "Whether to use the Tramp cache for completion of user and host names.
+Set it to nil if there are invalid entries in the cache, for
+example if the host configuration changes often, or if you plug
+your laptop to different networks frequently."
+  :group 'tramp
+  :version "29.1"
+  :type 'boolean)
+
 ;;;###tramp-autoload
 (defun tramp-parse-connection-properties (method)
   "Return a list of (user host) tuples allowed to access for METHOD.
 This function is added always in `tramp-get-completion-function'
 for all methods.  Resulting data are derived from connection history."
-  (mapcar
-   (lambda (key)
-     (and (tramp-file-name-p key)
-         (string-equal method (tramp-file-name-method key))
-         (not (tramp-file-name-localname key))
-         (list (tramp-file-name-user key)
-               (tramp-file-name-host key))))
-   (hash-table-keys tramp-cache-data)))
+  (and tramp-completion-use-cache
+       (mapcar
+       (lambda (key)
+         (and (tramp-file-name-p key)
+              (string-equal method (tramp-file-name-method key))
+              (not (tramp-file-name-localname key))
+              (list (tramp-file-name-user key)
+                    (tramp-file-name-host key))))
+       (hash-table-keys tramp-cache-data))))
 
 ;; When "emacs -Q" has been called, both variables are nil.  We do not
 ;; load the persistency file then, in order to have a clean test environment.
diff --git a/lisp/net/tramp-container.el b/lisp/net/tramp-container.el
index e104babed2..328625b776 100644
--- a/lisp/net/tramp-container.el
+++ b/lisp/net/tramp-container.el
@@ -101,7 +101,8 @@
 
 This function is used by `tramp-set-completion-function', please
 see its function help for a description of the format."
-  (when-let ((raw-list (shell-command-to-string
+  (when-let ((default-directory tramp-compat-temporary-file-directory)
+            (raw-list (shell-command-to-string
                        (concat tramp-docker-program
                                " ps --format '{{.ID}}\t{{.Names}}'")))
              (lines (split-string raw-list "\n" 'omit))
@@ -121,7 +122,8 @@ see its function help for a description of the format."
 
 This function is used by `tramp-set-completion-function', please
 see its function help for a description of the format."
-  (when-let ((raw-list (shell-command-to-string
+  (when-let ((default-directory tramp-compat-temporary-file-directory)
+            (raw-list (shell-command-to-string
                        (concat tramp-kubernetes-program
                                 " get pods --no-headers "
                                 "-o custom-columns=NAME:.metadata.name")))
@@ -130,6 +132,24 @@ see its function help for a description of the format."
               (list nil name))
             names)))
 
+(defun tramp-kubernetes--current-context-data (vec)
+  "Return Kubernetes current context data as JSON string."
+  (with-temp-buffer
+    (when (zerop
+          (tramp-call-process
+           vec tramp-kubernetes-program nil t nil
+           "config" "current-context"))
+      (goto-char (point-min))
+      (let ((current-context (buffer-substring (point) (line-end-position))))
+       (erase-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-autoload
 (defvar tramp-default-remote-shell) ;; Silence byte compiler.
 
@@ -165,6 +185,7 @@ see its function help for a description of the format."
                                    ("-it")
                                    ("--")
                                   ("%l")))
+               (tramp-config-check tramp-kubernetes--current-context-data)
                 (tramp-remote-shell ,tramp-default-remote-shell)
                 (tramp-remote-shell-login ("-l"))
                 (tramp-remote-shell-args ("-i" "-c"))))
diff --git a/lisp/net/tramp-integration.el b/lisp/net/tramp-integration.el
index 35c0636b1c..78107e1a03 100644
--- a/lisp/net/tramp-integration.el
+++ b/lisp/net/tramp-integration.el
@@ -125,6 +125,8 @@ been set up by `rfn-eshadow-setup-minibuffer'."
 
 ;; eshell.el keeps the path in `eshell-path-env'.  We must change it
 ;; when `default-directory' points to another host.
+;; This is fixed in Eshell with Emacs 29.1.
+
 (defun tramp-eshell-directory-change ()
   "Set `eshell-path-env' to $PATH of the host related to `default-directory'."
   ;; Remove last element of `(exec-path)', which is `exec-directory'.
@@ -136,16 +138,17 @@ been set up by `rfn-eshadow-setup-minibuffer'."
           (getenv "PATH"))))
 
 (with-eval-after-load 'esh-util
-  (add-hook 'eshell-mode-hook
-           #'tramp-eshell-directory-change)
-  (add-hook 'eshell-directory-change-hook
-           #'tramp-eshell-directory-change)
-  (add-hook 'tramp-integration-unload-hook
-           (lambda ()
-             (remove-hook 'eshell-mode-hook
-                          #'tramp-eshell-directory-change)
-             (remove-hook 'eshell-directory-change-hook
-                          #'tramp-eshell-directory-change))))
+  (unless (boundp 'eshell-path-env-list)
+    (add-hook 'eshell-mode-hook
+             #'tramp-eshell-directory-change)
+    (add-hook 'eshell-directory-change-hook
+             #'tramp-eshell-directory-change)
+    (add-hook 'tramp-integration-unload-hook
+             (lambda ()
+               (remove-hook 'eshell-mode-hook
+                            #'tramp-eshell-directory-change)
+               (remove-hook 'eshell-directory-change-hook
+                            #'tramp-eshell-directory-change)))))
 
 ;;; Integration of recentf.el:
 
diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el
index 3240f5352a..cfecd32aba 100644
--- a/lisp/net/tramp-sh.el
+++ b/lisp/net/tramp-sh.el
@@ -789,8 +789,8 @@ use strict;
 use warnings;
 use POSIX qw(getgroups);
 
-my ($user, $passwd, $uid, $gid) = getpwuid $< ;
-my $group = getgrgid $gid ;
+my ( $uid, $user ) = ( $>, scalar getpwuid $> );
+my ( $gid, $group ) = ( $), scalar getgrgid $) );
 my @groups = map { $_ . \"(\" . getgrgid ($_) . \")\" } getgroups ();
 
 printf \"uid=%%d(%%s) gid=%%d(%%s) groups=%%s\\n\",
@@ -2827,11 +2827,14 @@ the result will be a local, non-Tramp, file name."
   ;; Handle empty NAME.
   (when (zerop (length name)) (setq name "."))
   ;; On MS Windows, some special file names are not returned properly
-  ;; by `file-name-absolute-p'.
-  (if (and (eq system-type 'windows-nt)
-          (string-match-p
-           (tramp-compat-rx bol (| (: alpha ":") (: (literal null-device) 
eol)))
-           name))
+  ;; by `file-name-absolute-p'.  If `tramp-syntax' is `simplified',
+  ;; there could be the falso positive "/:".
+  (if (or (and (eq system-type 'windows-nt)
+              (string-match-p
+               (tramp-compat-rx bol (| (: alpha ":") (: (literal null-device) 
eol)))
+               name))
+         (and (not (tramp-tramp-file-p name))
+              (not (tramp-tramp-file-p dir))))
       (tramp-run-real-handler #'expand-file-name (list name dir))
     ;; Unless NAME is absolute, concat DIR and NAME.
     (unless (file-name-absolute-p name)
@@ -4469,7 +4472,8 @@ process to set up.  VEC specifies the connection."
   ;; Check whether the output of "uname -sr" has been changed.  If
   ;; yes, this is a strong indication that we must expire all
   ;; connection properties.  We start again with
-  ;; `tramp-maybe-open-connection', it will be caught there.
+  ;; `tramp-maybe-open-connection', it will be caught there.  The same
+  ;; check will be applied with the function kept in `tramp-config-check'.
   (tramp-message vec 5 "Checking system information")
   (let* ((old-uname (tramp-get-connection-property vec "uname"))
         (uname
@@ -4478,8 +4482,23 @@ process to set up.  VEC specifies the connection."
              old-uname
            (tramp-set-connection-property
             vec "uname"
-            (tramp-send-command-and-read vec "echo \\\"`uname -sr`\\\"")))))
-    (when (and (stringp old-uname) (not (string-equal old-uname uname)))
+            (tramp-send-command-and-read vec "echo \\\"`uname -sr`\\\""))))
+        (config-check-function
+         (tramp-get-method-parameter vec 'tramp-config-check))
+        (old-config-check
+         (and config-check-function
+              (tramp-get-connection-property vec "config-check-data")))
+        (config-check
+         (and config-check-function
+              ;; If we are in `make-process', we don't need to recompute.
+              (if (and old-config-check
+                       (tramp-get-connection-property vec "process-name"))
+                  old-config-check
+                (tramp-set-connection-property
+                 vec "config-check-data"
+                 (tramp-compat-funcall config-check-function vec))))))
+    (when (and (stringp old-uname) (stringp uname)
+              (not (string-equal old-uname uname)))
       (tramp-message
        vec 3
        "Connection reset, because remote host changed from `%s' to `%s'"
@@ -4487,6 +4506,15 @@ process to set up.  VEC specifies the connection."
       ;; We want to keep the password.
       (tramp-cleanup-connection vec t t)
       (throw 'uname-changed (tramp-maybe-open-connection vec)))
+    (when (and (stringp old-config-check) (stringp config-check)
+              (not (string-equal old-config-check config-check)))
+      (tramp-message
+       vec 3
+       "Connection reset, because remote configuration changed from `%s' to 
`%s'"
+       old-config-check config-check)
+      ;; We want to keep the password.
+      (tramp-cleanup-connection vec t t)
+      (throw 'uname-changed (tramp-maybe-open-connection vec)))
 
     ;; Try to set up the coding system correctly.
     ;; CCC this can't be the right way to do it.  Hm.
diff --git a/lisp/net/tramp-sudoedit.el b/lisp/net/tramp-sudoedit.el
index dc87c590b3..bc8739c4d6 100644
--- a/lisp/net/tramp-sudoedit.el
+++ b/lisp/net/tramp-sudoedit.el
@@ -369,33 +369,36 @@ the result will be a local, non-Tramp, file name."
   ;; Unless NAME is absolute, concat DIR and NAME.
   (unless (file-name-absolute-p name)
     (setq name (tramp-compat-file-name-concat dir name)))
-  (with-parsed-tramp-file-name name nil
-    ;; Tilde expansion if necessary.  We cannot accept "~/", because
-    ;; under sudo "~/" is expanded to the local user home directory
-    ;; but to the root home directory.
-    (when (zerop (length localname))
-      (setq localname "~"))
-    (unless (file-name-absolute-p localname)
-      (setq localname (format "~%s/%s" user localname)))
-    (when (string-match
-          (tramp-compat-rx bos "~" (group (* (not "/"))) (group (* nonl)) eos)
-          localname)
-      (let ((uname (match-string 1 localname))
-           (fname (match-string 2 localname))
-           hname)
-       (when (zerop (length uname))
-         (setq uname user))
-       (when (setq hname (tramp-get-home-directory v uname))
-         (setq localname (concat hname fname)))))
-    ;; Do not keep "/..".
-    (when (string-match-p (rx bos "/" (** 1 2 ".") eos) localname)
-      (setq localname "/"))
-    ;; Do normal `expand-file-name' (this does "~user/", "/./" and "/../").
-    (tramp-make-tramp-file-name
-     v (if (string-prefix-p "~" localname)
-          localname
-        (tramp-run-real-handler
-         #'expand-file-name (list localname))))))
+  ;; If NAME is not a Tramp file, run the real handler.
+  (if (not (tramp-tramp-file-p name))
+      (tramp-run-real-handler #'expand-file-name (list name))
+    (with-parsed-tramp-file-name name nil
+      ;; Tilde expansion if necessary.  We cannot accept "~/", because
+      ;; under sudo "~/" is expanded to the local user home directory
+      ;; but to the root home directory.
+      (when (zerop (length localname))
+       (setq localname "~"))
+      (unless (file-name-absolute-p localname)
+       (setq localname (format "~%s/%s" user localname)))
+      (when (string-match
+            (tramp-compat-rx bos "~" (group (* (not "/"))) (group (* nonl)) 
eos)
+            localname)
+       (let ((uname (match-string 1 localname))
+             (fname (match-string 2 localname))
+             hname)
+         (when (zerop (length uname))
+           (setq uname user))
+         (when (setq hname (tramp-get-home-directory v uname))
+           (setq localname (concat hname fname)))))
+      ;; Do not keep "/..".
+      (when (string-match-p (rx bos "/" (** 1 2 ".") eos) localname)
+       (setq localname "/"))
+      ;; Do normal `expand-file-name' (this does "~user/", "/./" and "/../").
+      (tramp-make-tramp-file-name
+       v (if (string-prefix-p "~" localname)
+            localname
+          (tramp-run-real-handler
+           #'expand-file-name (list localname)))))))
 
 (defun tramp-sudoedit-remote-acl-p (vec)
   "Check, whether ACL is enabled on the remote host."
diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el
index 4ff57e5d56..b08bc63e8a 100644
--- a/lisp/net/tramp.el
+++ b/lisp/net/tramp.el
@@ -281,6 +281,13 @@ pair of the form (KEY VALUE).  The following KEYs are 
defined:
     Until now, just \"ssh\"-based, \"sshfs\"-based and
     \"adb\"-based methods do.
 
+  * `tramp-config-check'
+    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 connection property \"config-check-data\".
+
   * `tramp-copy-program'
     This specifies the name of the program to use for remotely copying
     the file; this might be the absolute filename of scp or the name of
@@ -1526,7 +1533,8 @@ same connection.  Make a copy in order to avoid side 
effects."
 
 ;; Comparison of file names is performed by `tramp-equal-remote'.
 (defun tramp-file-name-equal-p (vec1 vec2)
-  "Check, whether VEC1 and VEC2 denote the same `tramp-file-name'."
+  "Check, whether VEC1 and VEC2 denote the same `tramp-file-name'.
+LOCALNAME and HOP do not count."
   (and (tramp-file-name-p vec1) (tramp-file-name-p vec2)
        (equal (tramp-file-name-unify vec1)
              (tramp-file-name-unify vec2))))
@@ -3989,6 +3997,17 @@ Let-bind it when necessary.")
   (cond
    ((not (file-exists-p file1)) nil)
    ((not (file-exists-p file2)) t)
+   ;; Tramp reads and writes timestamps on second level.  So we round
+   ;; the timestamps to seconds w/o fractions.
+   ;; `time-convert' has been introduced with Emacs 27.1.
+   ((fboundp 'time-convert)
+    (time-less-p
+     (tramp-compat-funcall
+      'time-convert
+      (file-attribute-modification-time (file-attributes file2)) 'integer)
+     (tramp-compat-funcall
+      'time-convert
+      (file-attribute-modification-time (file-attributes file1)) 'integer)))
    (t (time-less-p
        (file-attribute-modification-time (file-attributes file2))
        (file-attribute-modification-time (file-attributes file1))))))
@@ -4565,14 +4584,9 @@ Do not set it manually, it is used buffer-local in 
`tramp-get-lock-pid'.")
             (setq file (concat file ".elc")))
            ((file-exists-p (concat file ".el"))
             (setq file (concat file ".el")))))
-    (when must-suffix
-      ;; The first condition is always true for absolute file names.
-      ;; Included for safety's sake.
-      (unless (or (file-name-directory file)
-                 (string-match-p (rx ".el" (? "c") eos) file))
-       (tramp-error
-        v 'file-error
-        "File `%s' does not include a `.el' or `.elc' suffix" file)))
+    (when (and must-suffix (not (string-match-p (rx ".el" (? "c") eos) file)))
+      (tramp-error
+       v 'file-error "File `%s' does not include a `.el' or `.elc' suffix" 
file))
     (unless (or noerror (file-exists-p file))
       (tramp-error v 'file-missing file))
     (if (not (file-exists-p file))
diff --git a/lisp/nxml/rng-cmpct.el b/lisp/nxml/rng-cmpct.el
index 453c2b736d..85db33b9a9 100644
--- a/lisp/nxml/rng-cmpct.el
+++ b/lisp/nxml/rng-cmpct.el
@@ -1,6 +1,6 @@
 ;;; rng-cmpct.el --- parsing of RELAX NG Compact Syntax schemas  -*- 
lexical-binding:t -*-
 
-;; Copyright (C) 2003, 2007-2022 Free Software Foundation, Inc.
+;; Copyright (C) 2003-2022 Free Software Foundation, Inc.
 
 ;; Author: James Clark
 ;; Keywords: wp, hypermedia, languages, XML, RelaxNG
@@ -82,19 +82,17 @@ Return a pattern."
   (concat "\\`\\(" (regexp-opt rng-c-keywords) "\\)\\'")
   "Regular expression to match a keyword in the compact syntax.")
 
-(defvar rng-c-syntax-table nil
+(defvar rng-c-syntax-table
+  (let ((st (make-syntax-table)))
+    (modify-syntax-entry ?#  "<" st)
+    (modify-syntax-entry ?\n ">" st)
+    (modify-syntax-entry ?-  "w" st)
+    (modify-syntax-entry ?.  "w" st)
+    (modify-syntax-entry ?_  "w" st)
+    (modify-syntax-entry ?:  "_" st)
+    st)
   "Syntax table for parsing the compact syntax.")
 
-(if rng-c-syntax-table
-    ()
-  (setq rng-c-syntax-table (make-syntax-table))
-  (modify-syntax-entry ?# "<" rng-c-syntax-table)
-  (modify-syntax-entry ?\n ">" rng-c-syntax-table)
-  (modify-syntax-entry ?- "w" rng-c-syntax-table)
-  (modify-syntax-entry ?. "w" rng-c-syntax-table)
-  (modify-syntax-entry ?_ "w" rng-c-syntax-table)
-  (modify-syntax-entry ?: "_" rng-c-syntax-table))
-
 (defconst rng-c-literal-1-re
   "'\\(''\\([^']\\|'[^']\\|''[^']\\)*''\\|[^'\n]*\\)'"
   "Regular expression to match a single-quoted literal.")
diff --git a/lisp/nxml/rng-uri.el b/lisp/nxml/rng-uri.el
index 77fed8c32d..59e696e2cc 100644
--- a/lisp/nxml/rng-uri.el
+++ b/lisp/nxml/rng-uri.el
@@ -68,7 +68,7 @@ Signal an error if URI is not a valid file URL."
 
 ;; pattern is either nil or match or replace
 (defun rng-uri-file-name-1 (uri pattern)
-  (unless (string-match "\\`\\(?:[^%]\\|%[[:xdigit:]]{2}\\)*\\'" uri)
+  (unless (string-match "\\`\\(?:[^%]\\|%[[:xdigit:]]\\{2\\}\\)*\\'" uri)
     (rng-uri-error "Bad escapes in URI `%s'" uri))
   (setq uri (rng-uri-unescape-multibyte uri))
   (let* ((components
@@ -312,7 +312,7 @@ Both FULL and BASE must be absolute URIs."
 (defun rng-uri-unescape-unibyte (str)
   (replace-regexp-in-string "%[0-7][[:xdigit:]]"
                            (lambda (h)
-                             (string-to-number (substring h 1) 16))
+                             (string (string-to-number (substring h 1) 16)))
                            str
                            t
                            t))
@@ -325,8 +325,8 @@ Both FULL and BASE must be absolute URIs."
                                (regexp-quote
                                 (if (= (length match) 1)
                                     match
-                                  (string-to-number (substring match 1)
-                                                    16)))))
+                                  (string (string-to-number (substring match 1)
+                                                            16))))))
                            str
                            t
                            t))
diff --git a/lisp/org/ChangeLog.1 b/lisp/org/ChangeLog.1
index 836e1430df..4e1c44d2bc 100644
--- a/lisp/org/ChangeLog.1
+++ b/lisp/org/ChangeLog.1
@@ -881,7 +881,7 @@
        (org-table-find-dataline, org-table-move-row)
        (org-table-insert-hline, org-table-kill-row):
        Use `org-move-to-column' with the IGNORE-INVISIBLE arg set to `t', so
-       that abbreviated rows don't interfer with setting the cursor back
+       that abbreviated rows don't interfere with setting the cursor back
        at the correct position.
 
        * org.el (org-agenda-prepare-buffers): Use `save-excursion'
@@ -4724,7 +4724,7 @@
 2013-11-12  Michael Brand  <michael.ch.brand@gmail.com>
 
        * org-table.el (org-table-eval-formula): Align the arrow pointing
-       to the error in a Calc formula to the other fomula debugger logs.
+       to the error in a Calc formula to the other formula debugger logs.
 
        * org.el (org-link-escape-chars-browser): Add char double quote.
        (org-open-at-point): Use the constant
@@ -30650,7 +30650,7 @@
        and scheduling search.
 
        * org-exp.el (org-html-handle-time-stamps): No longer check for
-       the `org-export-with-timestamps' option, because the preprocesser
+       the `org-export-with-timestamps' option, because the preprocessor
        has taken care of this already.
 
        * org.el (org-entry-properties): Catch the case when this is
@@ -32411,7 +32411,7 @@
 
        * org-exp.el (org-print-icalendar-entries): Move the call to
        `org-diary-to-ical-string' out of the loop, and kill the buffer
-       afterwords.
+       afterwards.
 
        * org-remember.el (org-remember-visit-immediately):
        Position cursor after moving to the note.
diff --git a/lisp/org/ob-matlab.el b/lisp/org/ob-matlab.el
index 4ee090e4ac..f50da0ea43 100644
--- a/lisp/org/ob-matlab.el
+++ b/lisp/org/ob-matlab.el
@@ -32,7 +32,7 @@
 
 ;; matlab.el required for interactive emacs sessions and matlab-mode
 ;; major mode for source code editing buffer
-;; http://matlab-emacs.sourceforge.net/
+;; https://matlab-emacs.sourceforge.net/
 
 ;;; Code:
 (require 'ob)
diff --git a/lisp/org/ob-plantuml.el b/lisp/org/ob-plantuml.el
index ced00fbdda..7a495e8e7f 100644
--- a/lisp/org/ob-plantuml.el
+++ b/lisp/org/ob-plantuml.el
@@ -30,7 +30,7 @@
 
 ;;; Requirements:
 
-;; plantuml     | http://plantuml.sourceforge.net/
+;; plantuml     | https://plantuml.com/
 ;; plantuml.jar | `org-plantuml-jar-path' should point to the jar file (when 
exec mode is `jar')
 
 ;;; Code:
diff --git a/lisp/org/org-agenda.el b/lisp/org/org-agenda.el
index 35f19cf03b..e43950f13a 100644
--- a/lisp/org/org-agenda.el
+++ b/lisp/org/org-agenda.el
@@ -5306,7 +5306,7 @@ of what a project is and how to check if it stuck, 
customize the variable
   "Hook run when the fancy diary buffer is cleaned up.")
 
 (defun org-agenda-cleanup-fancy-diary ()
-  "Remove unwanted stuff in buffer created by `fancy-diary-display'.
+  "Remove unwanted stuff in buffer created by `diary-fancy-display'.
 This gets rid of the date, the underline under the date, and the
 dummy entry installed by Org mode to ensure non-empty diary for
 each date.  It also removes lines that contain only whitespace."
diff --git a/lisp/org/org-ctags.el b/lisp/org/org-ctags.el
index 6fc97ca399..b1ee32ab33 100644
--- a/lisp/org/org-ctags.el
+++ b/lisp/org/org-ctags.el
@@ -45,9 +45,7 @@
 ;; Installation
 ;; ============
 ;;
-;; Install org mode
-;; Ensure org-ctags.el is somewhere in your emacs load path.
-;; Download and install Exuberant ctags -- "http://ctags.sourceforge.net/";
+;; Download and install Exuberant ctags -- "https://ctags.sourceforge.net/";
 ;; Edit your .emacs file (see next section) and load emacs.
 
 ;; To put in your init file (.emacs):
diff --git a/lisp/org/org-protocol.el b/lisp/org/org-protocol.el
index 7c4de03bc2..137a11f3d9 100644
--- a/lisp/org/org-protocol.el
+++ b/lisp/org/org-protocol.el
@@ -42,7 +42,6 @@
 ;;
 ;;   1.) Add this to your init file (.emacs probably):
 ;;
-;;       (add-to-list 'load-path "/path/to/org-protocol/")
 ;;       (require 'org-protocol)
 ;;
 ;;   3.) Ensure emacs-server is up and running.
@@ -66,7 +65,7 @@
 ;;
 ;;
 ;; As of March 2009 Firefox users follow the steps documented on
-;; http://kb.mozillazine.org/Register_protocol, Opera setup is described here:
+;; https://kb.mozillazine.org/Register_protocol, Opera setup is described here:
 ;; http://www.opera.com/support/kb/view/535/
 ;;
 ;;
diff --git a/lisp/org/ox-ascii.el b/lisp/org/ox-ascii.el
index 76a1a71fab..1452f36c11 100644
--- a/lisp/org/ox-ascii.el
+++ b/lisp/org/ox-ascii.el
@@ -456,7 +456,7 @@ Optional argument JUSTIFY can specify any type of 
justification
 among `left', `center', `right' or `full'.  A nil value is
 equivalent to `left'.  For a justification that doesn't also fill
 string, see `org-ascii--justify-lines' and
-`org-ascii--justify-block'.
+`org-ascii--justify-element'.
 
 Return nil if S isn't a string."
   (when (stringp s)
diff --git a/lisp/outline.el b/lisp/outline.el
index b87d3ac5e7..a646f71db8 100644
--- a/lisp/outline.el
+++ b/lisp/outline.el
@@ -281,37 +281,30 @@ This option is only in effect when 
`outline-minor-mode-cycle' is non-nil."
   [outline-1 outline-2 outline-3 outline-4
    outline-5 outline-6 outline-7 outline-8])
 
-(defcustom outline-minor-mode-use-buttons '(derived-mode . help-mode)
+(defcustom outline-minor-mode-use-buttons nil
   "Whether to display clickable buttons on the headings.
-The value should be a `buffer-match-p' condition.
-
 These buttons can be used to hide and show the body under the heading.
-Note that this feature is not meant to be used in editing
-buffers (yet) -- that will be amended in a future version."
-  :type 'buffer-predicate
-  :safe #'booleanp
+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.
+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."
+  :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))
+  :safe #'symbolp
   :version "29.1")
 
-(defvar-local outline--use-buttons nil
-  "Non-nil when buffer displays clickable buttons on the headings.")
+(defvar-local outline--button-icons nil
+  "A list of pre-computed button icons.")
 
 (defvar-local outline--use-rtl nil
   "Non-nil when direction of clickable buttons is right-to-left.")
 
-(defcustom outline-minor-mode-use-margins '(and (derived-mode . special-mode)
-                                                (not (derived-mode . 
help-mode)))
-  "Whether to display clickable buttons in the margins.
-The value should be a `buffer-match-p' condition.
-
-These buttons can be used to hide and show the body under the heading.
-Note that this feature is meant to be used in editing buffers."
-  :type 'buffer-predicate
-  :safe #'booleanp
-  :version "29.1")
-
-(defvar-local outline--use-margins nil
-  "Non-nil when buffer displays clickable buttons in the margins.")
-
 (define-icon outline-open nil
   '((image "outline-open.svg" "outline-open.pbm" :height (0.8 . em))
     (emoji "🔽")
@@ -339,17 +332,26 @@ Note that this feature is meant to be used in editing 
buffers."
   :version "29.1")
 
 (define-icon outline-open-in-margins outline-open
-  '((image "outline-open.svg" "outline-open.pbm" :height 10))
+  '((image "outline-open.svg" "outline-open.pbm" :height 10)
+    (emoji "🔽")
+    (symbol "▼")
+    (text "v"))
   "Icon used for buttons for opened sections in margins."
   :version "29.1")
 
 (define-icon outline-close-in-margins outline-close
-  '((image "outline-open.svg" "outline-open.pbm" :height 10 :rotation -90))
+  '((image "outline-open.svg" "outline-open.pbm" :height 10 :rotation -90)
+    (emoji "▶️")
+    (symbol "▶")
+    (text ">"))
   "Icon used for buttons for closed sections in margins."
   :version "29.1")
 
 (define-icon outline-close-rtl-in-margins outline-close-rtl
-  '((image "outline-open.svg" "outline-open.pbm" :height 10 :rotation 90))
+  '((image "outline-open.svg" "outline-open.pbm" :height 10 :rotation 90)
+    (emoji "◀️")
+    (symbol "◀")
+    (text "<"))
   "Right-to-left icon used for closed sections in margins."
   :version "29.1")
 
@@ -475,7 +477,7 @@ outline font-lock faces to those of major mode."
     (let ((regexp (concat "^\\(?:" outline-regexp "\\).*$")))
       (while (re-search-forward regexp nil t)
         (let ((overlay (make-overlay (match-beginning 0) (match-end 0))))
-          (overlay-put overlay 'outline-overlay t)
+          (overlay-put overlay 'outline-highlight t)
           ;; FIXME: Is it possible to override all underlying face attributes?
           (when (or (memq outline-minor-mode-highlight '(append override))
                     (and (eq outline-minor-mode-highlight t)
@@ -499,24 +501,22 @@ See the command `outline-mode' for more information on 
this mode."
             (key-description outline-minor-mode-prefix) 
outline-mode-prefix-map)
   (if outline-minor-mode
       (progn
-        (cond
-         ((buffer-match-p outline-minor-mode-use-margins (current-buffer))
-          (setq-local outline--use-margins t))
-         ((buffer-match-p outline-minor-mode-use-buttons (current-buffer))
-          (setq-local outline--use-buttons t)))
-        (when (and (or outline--use-buttons outline--use-margins)
-                   (eq (current-bidi-paragraph-direction) 'right-to-left))
-          (setq-local outline--use-rtl t))
-        (when outline--use-margins
-          (if outline--use-rtl
-              (setq-local right-margin-width (1+ right-margin-width))
-            (setq-local left-margin-width (1+ left-margin-width)))
-          (setq-local fringes-outside-margins t)
-          ;; Force display of margins
-          (set-window-buffer nil (window-buffer)))
-        (when (or outline--use-buttons outline--use-margins)
+        (when outline-minor-mode-use-buttons
           (add-hook 'after-change-functions
-                    #'outline--fix-buttons-after-change nil t))
+                    #'outline--fix-buttons-after-change nil t)
+          (when (eq (current-bidi-paragraph-direction) 'right-to-left)
+            (setq-local outline--use-rtl t))
+          (setq-local outline--button-icons (outline--create-button-icons))
+          (when (and (eq outline-minor-mode-use-buttons 'in-margins)
+                     (> 1 (if outline--use-rtl right-margin-width
+                            left-margin-width)))
+            (if outline--use-rtl
+                (setq-local right-margin-width (1+ right-margin-width))
+              (setq-local left-margin-width (1+ left-margin-width)))
+            (setq-local fringes-outside-margins t)
+            ;; Force display of margins
+            (when (eq (current-buffer) (window-buffer))
+              (set-window-buffer nil (window-buffer)))))
         (when outline-minor-mode-highlight
           (if (and global-font-lock-mode (font-lock-specified-p major-mode))
               (progn
@@ -541,17 +541,19 @@ See the command `outline-mode' for more information on 
this mode."
       (if font-lock-fontified
           (font-lock-remove-keywords nil outline-font-lock-keywords))
       (font-lock-flush)
-      (remove-overlays nil nil 'outline-overlay t))
-    (when outline--use-buttons
-      (remove-overlays nil nil 'outline-button t))
-    (when outline--use-margins
-      (remove-overlays nil nil 'outline-margin t)
-      (if outline--use-rtl
-          (setq-local right-margin-width (1- right-margin-width))
-        (setq-local left-margin-width (1- left-margin-width)))
-      (setq-local fringes-outside-margins nil)
-      ;; Force removal of margins
-      (set-window-buffer nil (window-buffer)))))
+      (remove-overlays nil nil 'outline-highlight t))
+    (when outline-minor-mode-use-buttons
+      (remove-overlays nil nil 'outline-button t)
+      (when (and (eq outline-minor-mode-use-buttons 'in-margins)
+                 (< 0 (if outline--use-rtl right-margin-width
+                        left-margin-width)))
+        (if outline--use-rtl
+            (setq-local right-margin-width (1- right-margin-width))
+          (setq-local left-margin-width (1- left-margin-width)))
+        (setq-local fringes-outside-margins nil)
+        ;; Force removal of margins
+        (when (eq (current-buffer) (window-buffer))
+          (set-window-buffer nil (window-buffer)))))))
 
 (defvar-local outline-heading-alist ()
   "Alist associating a heading for every possible level.
@@ -1643,114 +1645,98 @@ With a prefix argument, show headings up to that 
LEVEL."
 
 ;;; Button/margin indicators
 
-(defun outline--make-button-overlay (type)
-  (let ((o (seq-find (lambda (o)
-                       (overlay-get o 'outline-button))
-                     (overlays-at (point)))))
-    (unless o
-      (setq o (make-overlay (point) (1+ (point))))
-      (overlay-put o 'evaporate t)
-      (overlay-put o 'follow-link 'mouse-face)
-      (overlay-put o 'mouse-face 'highlight)
-      (overlay-put o 'outline-button t))
-    (let ((icon (icon-elements (if (eq type 'close)
-                                   (if outline--use-rtl
-                                       'outline-close-rtl
-                                     'outline-close)
-                                 'outline-open)))
-          (inhibit-read-only t))
-      ;; In editing buffers we use overlays only, but in other buffers
-      ;; we use a mix of text properties, text and overlays to make
-      ;; movement commands work more logically.
-      (when (derived-mode-p 'special-mode)
-        (put-text-property (point) (1+ (point)) 'face (plist-get icon 'face)))
-      (if-let ((image (plist-get icon 'image)))
-          (overlay-put o 'display image)
-        (overlay-put o 'display (concat (plist-get icon 'string)
-                                        (string (char-after (point)))))
-        (overlay-put o 'face (plist-get icon 'face))))
-    o))
-
-(defun outline--make-margin-overlay (type)
-  (let ((o (seq-find (lambda (o)
-                       (overlay-get o 'outline-margin))
-                     (overlays-at (point)))))
-    (unless o
-      (setq o (make-overlay (point) (1+ (point))))
-      (overlay-put o 'evaporate t)
-      (overlay-put o 'outline-margin t))
-    (let ((icon (icon-elements (if (eq type 'close)
-                                   (if outline--use-rtl
-                                       'outline-close-rtl-in-margins
-                                     'outline-close-in-margins)
-                                 'outline-open-in-margins))))
-      (overlay-put
-       o 'before-string
-       (propertize " " 'display
-                   `((margin ,(if outline--use-rtl
-                                  'right-margin 'left-margin))
-                     ,(or (plist-get icon 'image)
-                          (plist-get icon 'string))))))
-    o))
-
-(defun outline--insert-open-button (&optional use-margins)
-  (with-silent-modifications
-    (save-excursion
-      (beginning-of-line)
-      (if use-margins
-          (outline--make-margin-overlay 'open)
-        (when (derived-mode-p 'special-mode)
-          (let ((inhibit-read-only t))
-            (insert "  ")
-            (beginning-of-line)))
-        (let ((o (outline--make-button-overlay 'open)))
-          (overlay-put o 'help-echo "Click to hide")
-          (overlay-put o 'keymap
-                       (define-keymap
-                         "RET" #'outline-hide-subtree
-                         "<mouse-2>" #'outline-hide-subtree)))))))
-
-(defun outline--insert-close-button (&optional use-margins)
+(defun outline--create-button-icons ()
+  (pcase outline-minor-mode-use-buttons
+    ('in-margins
+     (mapcar
+      (lambda (icon-name)
+        (let* ((icon (icon-elements icon-name))
+               (face   (plist-get icon 'face))
+               (string (plist-get icon 'string))
+               (image  (plist-get icon 'image))
+               (display `((margin ,(if outline--use-rtl
+                                       'right-margin 'left-margin))
+                          ,(or image (if face (propertize
+                                               string 'face face)
+                                       string))))
+               (space (propertize " " 'display display)))
+          (if (and image face) (propertize space 'face face) space)))
+      (list 'outline-open-in-margins
+            (if outline--use-rtl
+                'outline-close-rtl-in-margins
+              'outline-close-in-margins))))
+    ('insert
+     (mapcar
+      (lambda (icon-name)
+        (icon-elements icon-name))
+      (list 'outline-open
+            (if outline--use-rtl 'outline-close-rtl 'outline-close))))
+    (_
+     (mapcar
+      (lambda (icon-name)
+        (propertize (icon-string icon-name)
+                    'mouse-face 'default
+                    'follow-link 'mouse-face
+                    'keymap (define-keymap "<mouse-2>" #'outline-cycle)))
+      (list 'outline-open
+            (if outline--use-rtl 'outline-close-rtl 'outline-close))))))
+
+(defun outline--insert-button (type)
   (with-silent-modifications
     (save-excursion
       (beginning-of-line)
-      (if use-margins
-          (outline--make-margin-overlay 'close)
-        (when (derived-mode-p 'special-mode)
-          (let ((inhibit-read-only t))
-            (insert "  ")
-            (beginning-of-line)))
-        (let ((o (outline--make-button-overlay 'close)))
-          (overlay-put o 'help-echo "Click to show")
-          (overlay-put o 'keymap
-                       (define-keymap
-                         "RET" #'outline-show-subtree
-                         "<mouse-2>" #'outline-show-subtree)))))))
+      (let ((icon (nth (if (eq type 'close) 1 0) outline--button-icons))
+            (o (seq-find (lambda (o) (overlay-get o 'outline-button))
+                         (overlays-at (point)))))
+        (unless o
+          (when (eq outline-minor-mode-use-buttons 'insert)
+            (let ((inhibit-read-only t))
+              (insert "  ")
+              (beginning-of-line)))
+          (setq o (make-overlay (point) (1+ (point))))
+          (overlay-put o 'outline-button t)
+          (overlay-put o 'evaporate t))
+        (pcase outline-minor-mode-use-buttons
+          ('insert
+           (overlay-put o 'display (or (plist-get icon 'image)
+                                       (plist-get icon 'string)))
+           (overlay-put o 'face (plist-get icon 'face))
+           (overlay-put o 'follow-link 'mouse-face)
+           (overlay-put o 'mouse-face 'highlight)
+           (overlay-put o 'keymap (define-keymap
+                                    "RET" #'outline-cycle
+                                    "<mouse-2>" #'outline-cycle))
+           (overlay-put o 'help-echo (if (eq type 'close)
+                                         "Click to show"
+                                       "Click to hide")))
+          ('in-margins
+           (overlay-put o 'before-string icon)
+           (overlay-put o 'keymap (define-keymap "RET" #'outline-cycle)))
+          (_
+           (overlay-put o 'before-string icon)
+           (overlay-put o 'keymap (define-keymap "RET" #'outline-cycle))))))))
 
 (defun outline--fix-up-all-buttons (&optional from to)
-  (when (or outline--use-buttons outline--use-margins)
+  (when outline-minor-mode-use-buttons
     (when from
       (save-excursion
         (goto-char from)
         (setq from (line-beginning-position))))
     (outline-map-region
      (lambda ()
-       (if (save-excursion
-             (outline-end-of-heading)
-             (seq-some (lambda (o) (eq (overlay-get o 'invisible) 'outline))
-                       (overlays-at (point))))
-           (outline--insert-close-button outline--use-margins)
-         (outline--insert-open-button outline--use-margins)))
+       (let ((close-p (save-excursion
+                        (outline-end-of-heading)
+                        (seq-some (lambda (o) (eq (overlay-get o 'invisible)
+                                                  'outline))
+                                  (overlays-at (point))))))
+         (outline--insert-button (if close-p 'close 'open))))
      (or from (point-min)) (or to (point-max)))))
 
 (defun outline--fix-buttons-after-change (beg end _len)
   ;; Handle whole lines
   (save-excursion (goto-char beg) (setq beg (pos-bol)))
   (save-excursion (goto-char end) (setq end (pos-eol)))
-  (when outline--use-buttons
-    (remove-overlays beg end 'outline-button t))
-  (when outline--use-margins
-    (remove-overlays beg end 'outline-margin t))
+  (remove-overlays beg end 'outline-button t)
   (outline--fix-up-all-buttons beg end))
 
 
diff --git a/lisp/play/zone.el b/lisp/play/zone.el
index b0ce0194cf..a470f23dfe 100644
--- a/lisp/play/zone.el
+++ b/lisp/play/zone.el
@@ -103,9 +103,24 @@ If the element is a function or a list of a function and a 
number,
                  program))))
 
 ;;;###autoload
-(defun zone ()
-  "Zone out, completely."
-  (interactive)
+(defun zone (&optional pgm)
+  "Zone out, completely.
+With a prefix argument the user is prompted for a program to run.
+When called from Lisp the optional argument PGM can be used to
+run a specific program.  The program must be a member of
+`zone-programs'."
+  (interactive
+   (and current-prefix-arg
+        (let ((choice (completing-read
+                       "Program: "
+                       (mapcar
+                        (lambda (prog)
+                          (substring (symbol-name prog) 9))
+                        zone-programs)
+                       nil t)))
+          (list (intern (concat "zone-pgm-" choice))))))
+  (unless pgm
+    (setq pgm (aref zone-programs (random (length zone-programs)))))
   (save-window-excursion
     (let ((f (selected-frame))
           (outbuf (get-buffer-create "*zone*"))
@@ -124,9 +139,8 @@ If the element is a function or a list of a function and a 
number,
       (untabify (point-min) (point-max))
       (set-window-start (selected-window) (point-min))
       (set-window-point (selected-window) wp)
-      (sit-for 0 500)
-      (let ((pgm (elt zone-programs (random (length zone-programs))))
-            (ct (and f (frame-parameter f 'cursor-type)))
+      (sit-for 0.500)
+      (let ((ct (and f (frame-parameter f 'cursor-type)))
             (show-trailing-whitespace nil)
             restore)
         (when ct
@@ -235,7 +249,7 @@ If the element is a function or a list of a function and a 
number,
     (while (not (input-pending-p))
       (funcall (elt ops (random (length ops))))
       (goto-char (point-min))
-      (sit-for 0 10))))
+      (sit-for 0.01))))
 
 
 ;;;; whacking chars
@@ -248,7 +262,7 @@ If the element is a function or a list of a function and a 
number,
           (aset tbl i (+ 48 (random (- 123 48))))
           (setq i (1+ i)))
         (translate-region (point-min) (point-max) tbl)
-        (sit-for 0 2)))))
+        (sit-for 0.002)))))
 
 (put 'zone-pgm-whack-chars 'wc-tbl
      (let ((tbl (make-string 128 ?x))
@@ -276,7 +290,7 @@ If the element is a function or a list of a function and a 
number,
                   (delete-char 1)
                   (insert " ")))
             (forward-char 1))))
-      (sit-for 0 2))))
+      (sit-for 0.002))))
 
 (defun zone-pgm-dissolve ()
   (zone-remove-text)
@@ -298,7 +312,7 @@ If the element is a function or a list of a function and a 
number,
                 (insert " ")))
           (forward-char 1)))
       (setq i (1+ i))
-      (sit-for 0 2)))
+      (sit-for 0.002)))
   (zone-pgm-jitter))
 
 (defun zone-pgm-explode ()
@@ -333,7 +347,7 @@ If the element is a function or a list of a function and a 
number,
                 (upcase i)))
         (setq i (+ i (1+ (random 5)))))
       (translate-region (point-min) (point-max) tbl)
-      (sit-for 0 2))))
+      (sit-for 0.002))))
 
 (defun zone-pgm-putz-with-case ()
   (goto-char (point-min))
@@ -345,7 +359,7 @@ If the element is a function or a list of a function and a 
number,
                    'downcase-region) (1- np) np)
         (setq np (+ np (1+ (random 5))))))
     (goto-char (point-min))
-    (sit-for 0 2)))
+    (sit-for 0.002)))
 
 
 ;;;; rotating
diff --git a/lisp/printing.el b/lisp/printing.el
index 0654dcda3d..767648df4d 100644
--- a/lisp/printing.el
+++ b/lisp/printing.el
@@ -944,7 +944,7 @@
 ;;                   
`https://www.gnu.org/software/ghostscript/ghostscript.html'
 ;;    gsprint        `https://www.cs.wisc.edu/~ghost/gsview/gsprint.htm'.
 ;;    enscript       `https://people.ssh.fi/mtr/genscript/'
-;;    psnup          `http://gnuwin32.sourceforge.net/packages/psutils.htm'
+;;    psnup          `https://gnuwin32.sourceforge.net/packages/psutils.htm'
 ;;    redmon         `http://www.ghostgum.com.au/software/redmon.htm'
 ;;
 ;;
@@ -1752,7 +1752,7 @@ Useful links:
   `https://linux.die.net/man/1/lp'
 
 * GNU utilities for w32 (cp.exe)
-  `http://unxutils.sourceforge.net/'"
+  `https://unxutils.sourceforge.net/'"
   :type '(repeat
          (list
           :tag "PostScript Printer"
@@ -2382,7 +2382,7 @@ Useful links:
   `http://gershwin.ens.fr/vdaniel/Doc-Locale/Outils-Gnu-Linux/PsUtils/'
 
 * psnup (PsUtils for Windows)
-  `http://gnuwin32.sourceforge.net/packages/psutils.htm'
+  `https://gnuwin32.sourceforge.net/packages/psutils.htm'
 
 * psnup documentation (GNU or Unix - or type `man psnup')
   `https://linux.die.net/man/1/psnup'
diff --git a/lisp/profiler.el b/lisp/profiler.el
index 8670e5786a..e66b1ff42a 100644
--- a/lisp/profiler.el
+++ b/lisp/profiler.el
@@ -534,72 +534,71 @@ RET: expand or collapse"))
 
 ;;; Report mode
 
-(defvar profiler-report-mode-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map "n"            'profiler-report-next-entry)
-    (define-key map "p"            'profiler-report-previous-entry)
-    ;; I find it annoying more than helpful to not be able to navigate
-    ;; normally with the cursor keys.  --Stef
-    ;; (define-key map [down]  'profiler-report-next-entry)
-    ;; (define-key map [up]    'profiler-report-previous-entry)
-    (define-key map "\r"    'profiler-report-toggle-entry)
-    (define-key map "\t"    'profiler-report-toggle-entry)
-    (define-key map "i"     'profiler-report-toggle-entry)
-    (define-key map "f"     'profiler-report-find-entry)
-    (define-key map "j"     'profiler-report-find-entry)
-    (define-key map [follow-link] 'mouse-face)
-    (define-key map [mouse-2] 'profiler-report-find-entry)
-    (define-key map "d"            'profiler-report-describe-entry)
-    (define-key map "C"            'profiler-report-render-calltree)
-    (define-key map "B"            'profiler-report-render-reversed-calltree)
-    (define-key map "A"            'profiler-report-ascending-sort)
-    (define-key map "D"            'profiler-report-descending-sort)
-    (define-key map "="            'profiler-report-compare-profile)
-    (define-key map (kbd "C-x C-w") 'profiler-report-write-profile)
-    (easy-menu-define  profiler-report-menu map "Menu for Profiler Report 
mode."
-      '("Profiler"
-        ["Next Entry" profiler-report-next-entry :active t
-         :help "Move to next entry"]
-        ["Previous Entry" profiler-report-previous-entry :active t
-         :help "Move to previous entry"]
-        "--"
-        ["Toggle Entry" profiler-report-toggle-entry
-         :active (profiler-report-calltree-at-point)
-         :help "Expand or collapse the current entry"]
-        ["Find Entry" profiler-report-find-entry
-         ;; FIXME should deactivate if not on a known function.
-         :active (profiler-report-calltree-at-point)
-         :help "Find the definition of the current entry"]
-        ["Describe Entry" profiler-report-describe-entry
-         :active (profiler-report-calltree-at-point)
-         :help "Show the documentation of the current entry"]
-        "--"
-        ["Show Calltree" profiler-report-render-calltree
-         :active profiler-report-reversed
-         :help "Show calltree view"]
-        ["Show Reversed Calltree" profiler-report-render-reversed-calltree
-         :active (not profiler-report-reversed)
-         :help "Show reversed calltree view"]
-        ["Sort Ascending" profiler-report-ascending-sort
-         :active (not (eq profiler-report-order 'ascending))
-         :help "Sort calltree view in ascending order"]
-        ["Sort Descending" profiler-report-descending-sort
-         :active (not (eq profiler-report-order 'descending))
-         :help "Sort calltree view in descending order"]
-        "--"
-        ["Compare Profile..." profiler-report-compare-profile :active t
-         :help "Compare current profile with another"]
-        ["Write Profile..." profiler-report-write-profile :active t
-         :help "Write current profile to a file"]
-        "--"
-        ["Start Profiler" profiler-start :active (not (profiler-running-p))
-         :help "Start profiling"]
-        ["Stop Profiler" profiler-stop :active (profiler-running-p)
-         :help "Stop profiling"]
-        ["New Report" profiler-report :active (profiler-running-p)
-         :help "Make a new report"]))
-      map)
-  "Keymap for `profiler-report-mode'.")
+(defvar-keymap profiler-report-mode-map
+  :doc "Keymap for `profiler-report-mode'."
+  "n"       #'profiler-report-next-entry
+  "p"       #'profiler-report-previous-entry
+  ;; I find it annoying more than helpful to not be able to navigate
+  ;; normally with the cursor keys.  --Stef
+  ;; "<down>" #'profiler-report-next-entry
+  ;; "<up>"   #'profiler-report-previous-entry
+  "RET"     #'profiler-report-toggle-entry
+  "TAB"     #'profiler-report-toggle-entry
+  "i"       #'profiler-report-toggle-entry
+  "f"       #'profiler-report-find-entry
+  "j"       #'profiler-report-find-entry
+  "d"       #'profiler-report-describe-entry
+  "C"       #'profiler-report-render-calltree
+  "B"       #'profiler-report-render-reversed-calltree
+  "A"       #'profiler-report-ascending-sort
+  "D"       #'profiler-report-descending-sort
+  "="       #'profiler-report-compare-profile
+  "C-x C-w" #'profiler-report-write-profile
+  "<follow-link>" 'mouse-face
+  "<mouse-2>"     #'profiler-report-find-entry
+
+  :menu
+  '("Profiler"
+    ["Next Entry" profiler-report-next-entry :active t
+     :help "Move to next entry"]
+    ["Previous Entry" profiler-report-previous-entry :active t
+     :help "Move to previous entry"]
+    "--"
+    ["Toggle Entry" profiler-report-toggle-entry
+     :active (profiler-report-calltree-at-point)
+     :help "Expand or collapse the current entry"]
+    ["Find Entry" profiler-report-find-entry
+     ;; FIXME should deactivate if not on a known function.
+     :active (profiler-report-calltree-at-point)
+     :help "Find the definition of the current entry"]
+    ["Describe Entry" profiler-report-describe-entry
+     :active (profiler-report-calltree-at-point)
+     :help "Show the documentation of the current entry"]
+    "--"
+    ["Show Calltree" profiler-report-render-calltree
+     :active profiler-report-reversed
+     :help "Show calltree view"]
+    ["Show Reversed Calltree" profiler-report-render-reversed-calltree
+     :active (not profiler-report-reversed)
+     :help "Show reversed calltree view"]
+    ["Sort Ascending" profiler-report-ascending-sort
+     :active (not (eq profiler-report-order 'ascending))
+     :help "Sort calltree view in ascending order"]
+    ["Sort Descending" profiler-report-descending-sort
+     :active (not (eq profiler-report-order 'descending))
+     :help "Sort calltree view in descending order"]
+    "--"
+    ["Compare Profile..." profiler-report-compare-profile :active t
+     :help "Compare current profile with another"]
+    ["Write Profile..." profiler-report-write-profile :active t
+     :help "Write current profile to a file"]
+    "--"
+    ["Start Profiler" profiler-start :active (not (profiler-running-p))
+     :help "Start profiling"]
+    ["Stop Profiler" profiler-stop :active (profiler-running-p)
+     :help "Stop profiling"]
+    ["New Report" profiler-report :active (profiler-running-p)
+     :help "Make a new report"]))
 
 (defun profiler-report-make-buffer-name (profile)
   (format "*%s-Profiler-Report %s*"
diff --git a/lisp/progmodes/antlr-mode.el b/lisp/progmodes/antlr-mode.el
index 733deebdf5..1aee1107e6 100644
--- a/lisp/progmodes/antlr-mode.el
+++ b/lisp/progmodes/antlr-mode.el
@@ -5,7 +5,7 @@
 ;; Author: Christoph Wedler <Christoph.Wedler@sap.com>
 ;; Keywords: languages, ANTLR, code generator
 ;; Version: 2.2c
-;; URL: http://antlr-mode.sourceforge.net/
+;; URL: https://antlr-mode.sourceforge.net/
 
 ;; This file is part of GNU Emacs.
 
@@ -29,7 +29,7 @@
 ;; supported options and various other things like running ANTLR from within
 ;; Emacs.
 
-;; For details, check <http://antlr-mode.sourceforge.net/> or, if you prefer
+;; For details, check <https://antlr-mode.sourceforge.net/> or, if you prefer
 ;; the manual style, follow all commands mentioned in the documentation of
 ;; `antlr-mode'.  ANTLR is a LL(k)-based recognition tool which generates
 ;; lexers, parsers and tree transformers in Java, C++ or Sather and can be
@@ -106,12 +106,12 @@
   "Major mode for ANTLR grammar files."
   :group 'languages
   :link '(emacs-commentary-link "antlr-mode.el")
-  :link '(url-link "http://antlr-mode.sourceforge.net/";)
+  :link '(url-link "https://antlr-mode.sourceforge.net/";)
   :prefix "antlr-")
 
 (defconst antlr-version "2.2c"
   "ANTLR major mode version number.
-Check <http://antlr-mode.sourceforge.net/> for the newest.")
+Check <https://antlr-mode.sourceforge.net/> for the newest.")
 
 
 ;;;===========================================================================
diff --git a/lisp/progmodes/cc-bytecomp.el b/lisp/progmodes/cc-bytecomp.el
index 4b8154dafe..735d829769 100644
--- a/lisp/progmodes/cc-bytecomp.el
+++ b/lisp/progmodes/cc-bytecomp.el
@@ -287,7 +287,7 @@ perhaps a `cc-bytecomp-restore-environment' is forgotten 
somewhere"))
                    (cons cc-file cc-bytecomp-loaded-files))
              (cc-bytecomp-debug-msg
               "cc-bytecomp-load: Loading %S" cc-file)
-             ;; native-comp may async compile also intalled el.gz
+             ;; native-comp may async compile also installed el.gz
              ;; files therefore we may have to load here other el.gz.
              (load cc-part nil t)
              (cc-bytecomp-debug-msg
diff --git a/lisp/progmodes/cc-defs.el b/lisp/progmodes/cc-defs.el
index 4f1a08cfa0..b13f6a5914 100644
--- a/lisp/progmodes/cc-defs.el
+++ b/lisp/progmodes/cc-defs.el
@@ -60,7 +60,6 @@
 (cc-bytecomp-defun region-active-p)    ; XEmacs
 (cc-bytecomp-defvar mark-active)       ; Emacs
 (cc-bytecomp-defvar deactivate-mark)   ; Emacs
-(cc-bytecomp-defvar inhibit-point-motion-hooks) ; Emacs
 (cc-bytecomp-defvar parse-sexp-lookup-properties) ; Emacs
 (cc-bytecomp-defvar text-property-default-nonsticky) ; Emacs 21
 (cc-bytecomp-defun string-to-syntax)   ; Emacs 21
@@ -87,7 +86,7 @@
 
 ;;; Variables also used at compile time.
 
-(defconst c-version "5.35.1"
+(defconst c-version "5.35.2"
   "CC Mode version number.")
 
 (defconst c-version-sym (intern c-version))
diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el
index 0ac96219a1..9139699b5b 100644
--- a/lisp/progmodes/cc-engine.el
+++ b/lisp/progmodes/cc-engine.el
@@ -6052,7 +6052,7 @@ comment at the start of cc-engine.el for more info."
 ;; the like.
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; The approximate interval at which we cache the value of the brace stack.
-(defconst c-bs-interval 5000)
+(defconst c-bs-interval 2000)
 ;; The list of cached values of the brace stack.  Each value in the list is a
 ;; cons of the position it is valid for and the value of the stack as
 ;; described above.
@@ -6158,9 +6158,10 @@ comment at the start of cc-engine.el for more info."
            (setq s (cdr s))))
         ((c-keyword-member kwd-sym 'c-flat-decl-block-kwds)
          (push 0 s))))
-      ;; The failing `c-syntactic-re-search-forward' may have left us in the
-      ;; middle of a token, which might be a significant token.  Fix this!
-      (c-beginning-of-current-token)
+      (when (> prev-match-pos 1)      ; Has the search matched at least once?
+       ;; The failing `c-syntactic-re-search-forward' may have left us in the
+       ;; middle of a token, which might be a significant token.  Fix this!
+       (c-beginning-of-current-token))
       (cons (point)
            (cons bound-<> s)))))
 
@@ -6693,8 +6694,7 @@ comment at the start of cc-engine.el for more info."
          ;; syntactic ws.
          (when (and cfd-match-pos (< cfd-match-pos syntactic-pos))
            (goto-char syntactic-pos)
-           (c-forward-syntactic-ws
-            (min (+ (point) 2000) (point-max)))
+           (c-forward-syntactic-ws cfd-limit)
            (and cfd-continue-pos
                 (< cfd-continue-pos (point))
                 (setq cfd-token-pos (point))))
@@ -6735,8 +6735,7 @@ comment at the start of cc-engine.el for more info."
                        ;; can't be nested, and that's already been done in
                        ;; `c-find-decl-prefix-search'.
                        (when (> cfd-continue-pos cfd-token-pos)
-                         (c-forward-syntactic-ws
-                          (min (+ (point) 2000) (point-max)))
+                         (c-forward-syntactic-ws cfd-limit)
                          (setq cfd-token-pos (point)))
 
                        ;; Continue if the following token fails the
@@ -7357,7 +7356,7 @@ multi-line strings (but not C++, for example)."
 (defun c-ml-string-opener-intersects-region (&optional start finish)
   ;; If any part of the region [START FINISH] is inside an ml-string opener,
   ;; return a dotted list of the start, end and double-quote position of that
-  ;; opener.  That list wlll not include any "context characters" before or
+  ;; opener.  That list will not include any "context characters" before or
   ;; after the opener.  If an opener is found, the match-data will indicate
   ;; it, with (match-string 1) being the entire delimiter, and (match-string
   ;; 2) the "main" double-quote.  Otherwise, the match-data is undefined.
@@ -8196,7 +8195,8 @@ multi-line strings (but not C++, for example)."
 ;; treat possible types (i.e. those that it normally returns 'maybe or
 ;; 'found for) as actual types (and always return 'found for them).
 ;; This means that it records them in `c-record-type-identifiers' if
-;; that is set, and that it adds them to `c-found-types'.
+;; that is set, and that if its value is t (not 'just-one), it adds
+;; them to `c-found-types'.
 (defvar c-promote-possible-types nil)
 
 ;; Dynamically bound variable that instructs `c-forward-<>-arglist' to
@@ -8356,6 +8356,23 @@ multi-line strings (but not C++, for example)."
          (goto-char here))))
   t)
 
+(defun c-forward-over-colon-type-list ()
+  ;; If we're at a sequence of characters which can extend from, e.g.,
+  ;; a class name up to a colon introducing an inheritance list,
+  ;; move forward over them, including the colon, and return non-nil.
+  ;; Otherwise return nil, leaving point unmoved.
+  (let ((here (point)) pos)
+    (while (and (re-search-forward c-sub-colon-type-list-re nil t)
+               (not (eq (char-after) ?:))
+               (c-major-mode-is 'c++-mode)
+               (setq pos (c-looking-at-c++-attribute)))
+      (goto-char pos))
+    (if (eq (char-after) ?:)
+       (progn (forward-char)
+              t)
+      (goto-char here)
+      nil)))
+
 (defun c-forward-keyword-clause (match)
   ;; Submatch MATCH in the current match data is assumed to surround a
   ;; token.  If it's a keyword, move over it and any immediately
@@ -8463,12 +8480,11 @@ multi-line strings (but not C++, for example)."
          (and c-record-type-identifiers
               (progn
                 ;; If a keyword matched both one of the types above and
-                ;; this one, we match `c-colon-type-list-re' after the
+                ;; this one, we move forward to the colon following the
                 ;; clause matched above.
                 (goto-char safe-pos)
-                (looking-at c-colon-type-list-re))
+                (c-forward-over-colon-type-list))
               (progn
-                (goto-char (match-end 0))
                 (c-forward-syntactic-ws)
                 (c-forward-keyword-prefixed-id type))
               ;; There's a type after the `c-colon-type-list-re' match
@@ -8921,8 +8937,16 @@ multi-line strings (but not C++, for example)."
                        ;; Got some other operator.
                        (setq c-last-identifier-range
                              (cons (point) (match-end 0)))
+                       (if (and (eq (char-after) ?\")
+                                (eq (char-after (1+ (point))) ?\"))
+                           ;; operator"" has an (?)optional tag after it.
+                           (progn
+                             (goto-char (match-end 0))
+                             (c-forward-syntactic-ws lim+)
+                             (when (c-on-identifier)
+                               (c-forward-token-2 1 nil lim+)))
                        (goto-char (match-end 0))
-                       (c-forward-syntactic-ws lim+)
+                       (c-forward-syntactic-ws lim+))
                        (setq pos (point)
                              res 'operator)))
 
@@ -9031,7 +9055,8 @@ multi-line strings (but not C++, for example)."
     (c-forward-<>-arglist t)
     (c-forward-syntactic-ws))
 
-  (let ((start (point)) pos res name-res id-start id-end id-range)
+  (let ((start (point)) pos res name-res id-start id-end id-range
+       post-prefix-pos)
 
     ;; Skip leading type modifiers.  If any are found we know it's a
     ;; prefix of a type.
@@ -9043,6 +9068,7 @@ multi-line strings (but not C++, for example)."
        (c-forward-syntactic-ws)
        (or (eq res 'no-id)
            (setq res 'prefix))))
+    (setq post-prefix-pos (point))
 
     (cond
      ((looking-at c-typeof-key) ; e.g. C++'s "decltype".
@@ -9075,9 +9101,14 @@ multi-line strings (but not C++, for example)."
       (setq name-res (c-forward-name))
       (setq res (not (null name-res)))
       (when (eq name-res t)
-       ;; In many languages the name can be used without the
-       ;; prefix, so we add it to `c-found-types'.
-       (c-add-type pos (point))
+       ;; With some keywords the name can be used without the prefix, so we
+       ;; add the name to `c-found-types' when this is the case.
+       (when (save-excursion
+               (goto-char post-prefix-pos)
+               (looking-at c-self-contained-typename-key))
+         (c-add-type pos (save-excursion
+                           (c-backward-syntactic-ws)
+                           (point))))
        (when (and c-record-type-identifiers
                   c-last-identifier-range)
          (c-record-type-id c-last-identifier-range)))
@@ -9162,7 +9193,11 @@ multi-line strings (but not C++, for example)."
             (goto-char id-end)
             (if (or res c-promote-possible-types)
                 (progn
-                  (c-add-type id-start id-end)
+                  (when (not (eq c-promote-possible-types 'just-one))
+                    (c-add-type id-start (save-excursion
+                                           (goto-char id-end)
+                                           (c-backward-syntactic-ws)
+                                           (point))))
                   (when (and c-record-type-identifiers id-range)
                     (c-record-type-id id-range))
                   (unless res
@@ -9676,7 +9711,7 @@ point unchanged and return nil."
   ;; (e.g. "," or ";" or "}").
   (let ((here (point))
        id-start id-end brackets-after-id paren-depth decorated
-       got-init arglist)
+       got-init arglist double-double-quote)
     (or limit (setq limit (point-max)))
     (if        (and
         (< (point) limit)
@@ -9705,6 +9740,10 @@ point unchanged and return nil."
                 (setq id-start (point))
                 (if (looking-at c-overloadable-operators-regexp)
                     (progn
+                      (when (and (c-major-mode-is 'c++-mode)
+                                 (eq (char-after) ?\")
+                                 (eq (char-after (1+ (point))) ?\"))
+                        (setq double-double-quote t))
                       (goto-char (match-end 0))
                       (c-forward-syntactic-ws limit)
                       (setq got-identifier t)
@@ -9756,6 +9795,13 @@ point unchanged and return nil."
             t)
            (t nil)))
 
+        (progn
+          (c-forward-syntactic-ws limit)
+          (when (and double-double-quote       ; C++'s operator"" _tag
+                     (c-on-identifier))
+            (c-forward-token-2 1 nil limit))
+          t)
+
         ;; Skip out of the parens surrounding the identifier.  If closing
         ;; parens are missing, this form returns nil.
         (or (= paren-depth 0)
@@ -9986,7 +10032,8 @@ This function might do hidden buffer changes."
        ;; This identifier is bound only in the inner let.
        '(setq start id-start))))
 
-(defun c-forward-decl-or-cast-1 (preceding-token-end context last-cast-end)
+(defun c-forward-decl-or-cast-1 (preceding-token-end context last-cast-end
+                                                    &optional inside-macro)
   ;; Move forward over a declaration or a cast if at the start of one.
   ;; The point is assumed to be at the start of some token.  Nil is
   ;; returned if no declaration or cast is recognized, and the point
@@ -10075,6 +10122,10 @@ This function might do hidden buffer changes."
   ;; matched.  In that case it's used to discover chains of casts like
   ;; "(a) (b) c".
   ;;
+  ;; INSIDE-MACRO is t when we definitely know we're inside a macro, nil
+  ;; otherwise.  We use it to disambiguate things like "(a) (b);", which is
+  ;; likely a function call in a macro, but a cast outside of one.
+  ;;
   ;; This function records identifier ranges on
   ;; `c-record-type-identifiers' and `c-record-ref-identifiers' if
   ;; `c-record-type-identifiers' is non-nil.
@@ -10157,7 +10208,11 @@ This function might do hidden buffer changes."
        (save-rec-ref-ids c-record-ref-identifiers)
        ;; Set when we parse a declaration which might also be an expression,
        ;; such as "a *b".  See CASE 16 and CASE 17.
-       maybe-expression)
+       maybe-expression
+       ;; Set for the type when `c-forward-type' returned `maybe', and we
+       ;; want to fontify it as a type, but aren't confident enough to enter
+       ;; it into `c-found-types'.
+       unsafe-maybe)
 
     (save-excursion
       (goto-char preceding-token-end)
@@ -10718,12 +10773,28 @@ This function might do hidden buffer changes."
                        ((eq at-decl-or-cast t)
                         (throw 'at-decl-or-cast t))
                        ((and c-has-bitfields
-                             (eq at-decl-or-cast 'ids)) ; bitfield.
+                             ;; Check for a bitfield.
+                             (eq at-decl-or-cast 'ids)
+                             (save-excursion
+                               (forward-char) ; Over the :
+                               (c-forward-syntactic-ws)
+                               (and (looking-at "[[:alnum:]]")
+                                    (progn (c-forward-token-2)
+                                           (c-forward-syntactic-ws)
+                                           (memq (char-after) '(?\; ?,))))))
                         (setq backup-if-not-cast t)
                         (throw 'at-decl-or-cast t)))
 
-                    (setq backup-if-not-cast t)
-                    (throw 'at-decl-or-cast t)))
+                    ;; If we're in declaration or template delimiters, or one
+                    ;; of a certain set of characters follows, we've got a
+                    ;; type and variable.
+                    (if (or (memq context '(decl <>))
+                            (memq (char-after) '(?\; ?, ?= ?\( ?{ ?:)))
+                        (progn
+                          (setq backup-if-not-cast t)
+                          (throw 'at-decl-or-cast t))
+                      ;; We're probably just typing a statement.
+                      (throw 'at-decl-or-cast nil))))
 
                 ;; CASE 4
                 (when (and got-suffix
@@ -10839,8 +10910,13 @@ This function might do hidden buffer changes."
 
         ;; CASE 10
         (when at-decl-or-cast
-          ;; By now we've located the type in the declaration that we know
-          ;; we're in.
+          ;; By now we've located the type in the declaration that we think
+          ;; we're in.  Do we have enough evidence to promote the putative
+          ;; type to a found type?  The user may be halfway through typing
+          ;; a statement beginning with an identifier.
+          (when (and (eq at-type 'maybe)
+                     (not (eq context 'top)))
+            (setq unsafe-maybe t))
           (throw 'at-decl-or-cast t))
 
         ;; CASE 11
@@ -11003,6 +11079,11 @@ This function might do hidden buffer changes."
             ;; `got-parens' or `got-suffix' is set it's "a()", "a[]", "a()[]",
             ;; or similar, which we accept only if the context rules out
             ;; expressions.
+            ;;
+            ;; If we've got at-type 'maybe, we cannot confidently promote the
+            ;; possible type to a found type.
+            (when (and (eq at-type 'maybe))
+              (setq unsafe-maybe t))
             (throw 'at-decl-or-cast t)))
 
         ;; If we had a complete symbol table here (which rules out
@@ -11046,11 +11127,17 @@ This function might do hidden buffer changes."
                   ;; Check if the expression begins with a prefix keyword.
                   (match-beginning 2)
                   (if (match-beginning 1)
-                      ;; Expression begins with an ambiguous operator.  Treat
-                      ;; it as a cast if it's a type decl or if we've
-                      ;; recognized the type somewhere else.
-                      (or at-decl-or-cast
-                          (memq at-type '(t known found)))
+                      ;; Expression begins with an ambiguous operator.
+                      (cond
+                       ((match-beginning c-per-&*+--match)
+                        (memq at-type '(t known found)))
+                       ((match-beginning c-per-++---match)
+                        t)
+                       ((match-beginning c-per-\(-match)
+                        (or
+                         (memq at-type '(t known found))
+                         (not inside-macro)))
+                       (t nil))
                     ;; Unless it's a keyword, it's the beginning of a primary
                     ;; expression.
                     (not (looking-at c-keywords-regexp)))))
@@ -11076,15 +11163,33 @@ This function might do hidden buffer changes."
                     ;; surrounding parens).
                     (looking-at c-simple-stmt-key)
                   (and
-                   ;; Check that it isn't a close paren (block close is ok,
-                   ;; though).
-                   (not (memq (char-before) '(?\) ?\])))
+                   ;; Check that it isn't a close paren (block close , or a
+                   ;; macro arglist is ok, though).
+                   (or
+                    (not (memq (char-before) '(?\) ?\])))
+                    ;; Have we moved back to a macro arglist?
+                    (and c-opt-cpp-prefix
+                         (eq (char-before) ?\))
+                         (save-excursion
+                           (and
+                            (c-go-list-backward)
+                            (let (pos)
+                              (c-backward-syntactic-ws)
+                              (and (setq pos (c-on-identifier))
+                                   (goto-char pos)))
+                            (zerop (c-backward-token-2 2))
+                            (looking-at c-opt-cpp-macro-define-start)))))
+
                    ;; Check that it isn't a nonsymbol identifier.
                    (not (c-on-identifier)))))))))
 
       ;; Handle the cast.
-      (when (and c-record-type-identifiers at-type (not (eq at-type t)))
-       (let ((c-promote-possible-types t))
+      (when (and c-record-type-identifiers
+                at-type
+                (not (eq at-type t)))
+       (let ((c-promote-possible-types (if (eq at-type 'maybe)
+                                           'just-one
+                                         t)))
          (goto-char type-start)
          (c-forward-type)))
 
@@ -11119,7 +11224,8 @@ This function might do hidden buffer changes."
                 ;; fontification just because it's "a known type that can't
                 ;; be a name or other expression".  2013-09-18.
                 )
-       (let ((c-promote-possible-types t))
+       (let ((c-promote-possible-types
+              (if unsafe-maybe 'just-one t)))
          (save-excursion
            (goto-char type-start)
            (c-forward-type))))
diff --git a/lisp/progmodes/cc-fonts.el b/lisp/progmodes/cc-fonts.el
index 2e71285cb3..9444828a0e 100644
--- a/lisp/progmodes/cc-fonts.el
+++ b/lisp/progmodes/cc-fonts.el
@@ -1,4 +1,4 @@
-;;; cc-fonts.el --- font lock support for CC Mode -*- lexical-binding: t -*-
+;; cc-fonts.el --- font lock support for CC Mode -*- lexical-binding: t -*-
 
 ;; Copyright (C) 2002-2022 Free Software Foundation, Inc.
 
@@ -115,6 +115,7 @@
         ;; used for preprocessor directives.
         'font-lock-builtin-face)
        ((and (c-face-name-p 'font-lock-reference-face)
+             (boundp 'font-lock-reference-face)
              (eq font-lock-reference-face 'font-lock-reference-face))
         'font-lock-reference-face)
        (t 'font-lock-constant-face)))
@@ -128,6 +129,7 @@
         ;; suite.)
         'font-lock-label-face)
        ((and (c-face-name-p 'font-lock-constant-face)
+             (boundp 'font-lock-constant-face)
              (eq font-lock-constant-face 'font-lock-constant-face))
         ;; Test both if font-lock-constant-face exists and that it's
         ;; not an alias for something else.  This is important since
@@ -138,20 +140,24 @@
 
 (defconst c-constant-face-name
   (if (and (c-face-name-p 'font-lock-constant-face)
+          (boundp 'font-lock-constant-face)
           (eq font-lock-constant-face 'font-lock-constant-face))
       ;; This doesn't exist in some earlier versions of XEmacs 21.
       'font-lock-constant-face
     c-label-face-name))
 
 (defconst c-reference-face-name
-  (with-no-warnings
-   (if (and (c-face-name-p 'font-lock-reference-face)
-           (eq font-lock-reference-face 'font-lock-reference-face))
-       ;; This is considered obsolete in Emacs, but it still maps well
-       ;; to this use.  (Another reason to do this is to get unique
-       ;; faces for the test suite.)
-       'font-lock-reference-face
-     c-label-face-name)))
+  (cond
+   ((and (c-face-name-p 'font-lock-reference-face)
+          (boundp 'font-lock-reference-face)
+          (eq font-lock-reference-face 'font-lock-reference-face))
+    ;; This is considered obsolete in Emacs, but it still maps well
+    ;; to this use.  (Another reason to do this is to get unique
+    ;; faces for the test suite.)
+    'font-lock-reference-face)
+   ((c-face-name-p 'font-lock-constant-face)
+    'font-lock-constant-face)
+   (t c-label-face-name)))
 
 ;; This should not mapped to a face that also is used to fontify things
 ;; that aren't comments or string literals.
@@ -586,7 +592,8 @@ stuff.  Used on level 1 and higher."
                        (c-lang-const c-opt-cpp-macro-define)
                        (c-lang-const c-nonempty-syntactic-ws)
                        "\\(" (c-lang-const ; 1 + ncle + nsws
-                              c-symbol-key) "\\)"
+                              c-symbol-key)
+                       "\\)"
                        (concat "\\("   ; 2 + ncle + nsws + c-sym-key
                                ;; Macro with arguments - a "function".
                                "\\((\\)" ; 3 + ncle + nsws + c-sym-key
@@ -1141,12 +1148,28 @@ casts and declarations are fontified.  Used on level 2 
and higher."
               (while (and (< (point) id-end)
                           (re-search-forward c-opt-identifier-prefix-key 
id-end t))
                 (c-forward-syntactic-ws limit))))
-          (when (not (get-text-property (point) 'face))
+          ;; Only apply the face when the text doesn't have one yet.
+          ;; Exception: The "" in C++'s operator"" will already wrongly have
+          ;; string face.
+          (when (memq (get-text-property (point) 'face)
+                      '(nil font-lock-string-face))
             (c-put-font-lock-face (point) id-end
                                   (cond
                                    ((not (memq types '(nil t))) types)
                                    (is-function 'font-lock-function-name-face)
-                                   (t 'font-lock-variable-name-face))))))
+                                   (t 'font-lock-variable-name-face))))
+          ;; Fontify any _tag in C++'s operator"" _tag.
+          (when (and
+                 (c-major-mode-is 'c++-mode)
+                 (equal (buffer-substring-no-properties id-start id-end)
+                        "\"\""))
+            (goto-char id-end)
+            (c-forward-syntactic-ws limit)
+            (when (c-on-identifier)
+              (c-put-font-lock-face
+               (point)
+               (progn (c-forward-over-token) (point))
+               font-lock-function-name-face)))))
        (and template-class
            (eq init-char ?=)           ; C++ "<class X = Y>"?
            (progn
@@ -1181,135 +1204,159 @@ casts and declarations are fontified.  Used on level 
2 and higher."
   ;; arguments lists (i.e. lists enclosed by <...>) is more strict about what
   ;; characters it allows within the list.
   (let ((type (and (> match-pos (point-min))
-                  (c-get-char-property (1- match-pos) 'c-type))))
-    (cond ((not (memq (char-before match-pos) '(?\( ?, ?\[ ?< ?{)))
-          (cons (and toplev 'top) nil))
-         ;; A control flow expression or a decltype
-         ((and (eq (char-before match-pos) ?\()
-               (save-excursion
-                 (goto-char match-pos)
-                 (backward-char)
-                 (c-backward-token-2)
-                 (cond
-                  ((looking-at c-paren-stmt-key)
-                   ;; Allow comma separated <> arglists in for statements.
-                   (cons nil nil))
-                  ((or (looking-at c-block-stmt-2-key)
-                       (looking-at c-block-stmt-1-2-key)
-                       (looking-at c-typeof-key))
-                   (cons nil t))
-                  (t nil)))))
-         ;; Near BOB.
-         ((<= match-pos (point-min))
-          (cons 'arglist t))
-         ;; Got a cached hit in a declaration arglist.
-         ((eq type 'c-decl-arg-start)
-          (cons 'decl nil))
-         ;; We're inside (probably) a brace list.
-         ((eq type 'c-not-decl)
-          (cons 'not-decl nil))
-         ;; Inside a C++11 lambda function arglist.
-         ((and (c-major-mode-is 'c++-mode)
-               (eq (char-before match-pos) ?\()
-               (save-excursion
-                 (goto-char match-pos)
-                 (c-backward-token-2)
-                 (and
-                  (c-safe (goto-char (scan-sexps (point) -1)))
-                  (c-looking-at-c++-lambda-capture-list))))
-          (c-put-char-property (1- match-pos) 'c-type
-                               'c-decl-arg-start)
-          (cons 'decl nil))
-         ;; We're inside a brace list.
-         ((and (eq (char-before match-pos) ?{)
-               (c-inside-bracelist-p (1- match-pos)
-                                     (cdr (c-parse-state))
-                                     nil))
-          (c-put-char-property (1- match-pos) 'c-type
-                               'c-not-decl)
-          (cons 'not-decl nil))
-         ;; We're inside an "ordinary" open brace.
-         ((eq (char-before match-pos) ?{)
-          (cons (and toplev 'top) nil))
-         ;; Inside an angle bracket arglist.
-         ((or (eq type 'c-<>-arg-sep)
-              (eq (char-before match-pos) ?<))
-          (cons '<> nil))
-         ;; Got a cached hit in some other type of arglist.
-         (type
-          (cons 'arglist t))
-         ;; We're at a C++ uniform initialization.
-         ((and (c-major-mode-is 'c++-mode)
-               (eq (char-before match-pos) ?\()
-               (save-excursion
-                 (goto-char match-pos)
-                 (and
-                  (zerop (c-backward-token-2 2))
-                  (looking-at c-identifier-start)
-                  (c-got-face-at (point)
-                                 '(font-lock-variable-name-face)))))
-          (cons 'not-decl nil))
-         ((and not-front-decl
+                  (c-get-char-property (1- match-pos) 'c-type)))
+       id-pos)
+    (cond
+     ;; Are we just after something like "(foo((bar))" ?
+     ((and (eq (char-before match-pos) ?\))
+          (c-go-list-backward match-pos)
+          (progn
+            (c-backward-syntactic-ws)
+            (and (setq id-pos (c-on-identifier))
+                 (goto-char id-pos)
+                 (progn
+                   (c-backward-syntactic-ws)
+                   (eq (char-before) ?\()))))
+      (c-get-fontification-context (point) not-front-decl toplev))
+     ((not (memq (char-before match-pos) '(?\( ?, ?\[ ?< ?{)))
+      (cons (and toplev 'top) nil))
+     ;; A control flow expression or a decltype
+     ((and (eq (char-before match-pos) ?\()
+          (save-excursion
+            (goto-char match-pos)
+            (backward-char)
+            (c-backward-token-2)
+            (cond
+             ((looking-at c-paren-stmt-key)
+              ;; Allow comma separated <> arglists in for statements.
+              (cons nil nil))
+             ((or (looking-at c-block-stmt-2-key)
+                  (looking-at c-block-stmt-1-2-key)
+                  (looking-at c-typeof-key))
+              (cons nil t))
+             (t nil)))))
+     ;; Near BOB.
+     ((<= match-pos (point-min))
+      (cons 'arglist t))
+     ;; Got a cached hit in a declaration arglist.
+     ((eq type 'c-decl-arg-start)
+      (cons 'decl nil))
+     ;; We're inside (probably) a brace list.
+     ((eq type 'c-not-decl)
+      (cons 'not-decl nil))
+     ;; Inside a C++11 lambda function arglist.
+     ((and (c-major-mode-is 'c++-mode)
+          (eq (char-before match-pos) ?\()
+          (save-excursion
+            (goto-char match-pos)
+            (c-backward-token-2)
+            (and
+             (c-safe (goto-char (scan-sexps (point) -1)))
+             (c-looking-at-c++-lambda-capture-list))))
+      (c-put-char-property (1- match-pos) 'c-type
+                          'c-decl-arg-start)
+      (cons 'decl nil))
+     ;; We're inside a brace list.
+     ((and (eq (char-before match-pos) ?{)
+          (c-inside-bracelist-p (1- match-pos)
+                                (cdr (c-parse-state))
+                                nil))
+      (c-put-char-property (1- match-pos) 'c-type
+                          'c-not-decl)
+      (cons 'not-decl nil))
+     ;; We're inside an "ordinary" open brace.
+     ((eq (char-before match-pos) ?{)
+      (cons (and toplev 'top) nil))
+     ;; Inside an angle bracket arglist.
+     ((or (eq type 'c-<>-arg-sep)
+         (eq (char-before match-pos) ?<))
+      (cons '<> nil))
+     ;; Got a cached hit in some other type of arglist.
+     (type
+      (cons 'arglist t))
+     ;; We're at a C++ uniform initialization.
+     ((and (c-major-mode-is 'c++-mode)
+          (eq (char-before match-pos) ?\()
+          (save-excursion
+            (goto-char match-pos)
+            (and
+             (zerop (c-backward-token-2 2))
+             (looking-at c-identifier-start)
+             (c-got-face-at (point)
+                            '(font-lock-variable-name-face)))))
+      (cons 'not-decl nil))
+     ((and not-front-decl
           ;; The point is within the range of a previously
           ;; encountered type decl expression, so the arglist
           ;; is probably one that contains declarations.
           ;; However, if `c-recognize-paren-inits' is set it
           ;; might also be an initializer arglist.
-               (or (not c-recognize-paren-inits)
-                   (save-excursion
-                     (goto-char match-pos)
-                     (not (c-back-over-member-initializers)))))
-          ;; The result of this check is cached with a char
-          ;; property on the match token, so that we can look
-          ;; it up again when refontifying single lines in a
-          ;; multiline declaration.
-          (c-put-char-property (1- match-pos)
-                               'c-type 'c-decl-arg-start)
-          (cons 'decl nil))
-         ;; Got (an) open paren(s) preceded by an arith operator.
-         ((and (eq (char-before match-pos) ?\()
-               (save-excursion
-                 (goto-char match-pos)
-                 (while
-                     (and (zerop (c-backward-token-2))
-                          (eq (char-after) ?\()))
-                 (looking-at c-arithmetic-op-regexp)))
-          (cons nil nil))
-         ;; In a C++ member initialization list.
-         ((and (eq (char-before match-pos) ?,)
-               (c-major-mode-is 'c++-mode)
-               (save-excursion
-                 (goto-char match-pos)
-                 (c-back-over-member-initializers)))
-          (c-put-char-property (1- match-pos) 'c-type 'c-not-decl)
-          (cons 'not-decl nil))
-         ;; At start of a declaration inside a declaration paren.
-         ((save-excursion
+          (or (not c-recognize-paren-inits)
+              (save-excursion
+                (goto-char match-pos)
+                (not (c-back-over-member-initializers)))))
+      ;; The result of this check is cached with a char
+      ;; property on the match token, so that we can look
+      ;; it up again when refontifying single lines in a
+      ;; multiline declaration.
+      (c-put-char-property (1- match-pos)
+                          'c-type 'c-decl-arg-start)
+      (cons 'decl nil))
+     ;; Got (an) open paren(s) preceded by an arith operator.
+     ((and (eq (char-before match-pos) ?\()
+          (save-excursion
             (goto-char match-pos)
-            (and (memq (char-before match-pos) '(?\( ?\,))
-                 (c-go-up-list-backward match-pos
-                                         ; c-determine-limit is too slow, here.
-                                        (max (- (point) 2000) (point-min)))
-                 (eq (char-after) ?\()
-                 (let ((type (c-get-char-property (point) 'c-type)))
-                   (or (memq type '(c-decl-arg-start c-decl-type-start))
-                       (and
-                        (progn (c-backward-syntactic-ws) t)
-                        (or
-                         (and
-                          (c-back-over-compound-identifier)
-                          (progn
-                            (c-backward-syntactic-ws)
-                            (or (bobp)
-                                (progn
-                                  (setq type (c-get-char-property (1- (point))
-                                                                  'c-type))
-                                  (memq type '(c-decl-arg-start
-                                               c-decl-type-start))))))
-                         (and (zerop (c-backward-token-2))
-                              (looking-at c-fun-name-substitute-key))))))))
-          (cons 'decl nil))
-         (t (cons 'arglist t)))))
+            (while
+                (and (zerop (c-backward-token-2))
+                     (eq (char-after) ?\()))
+            (looking-at c-arithmetic-op-regexp)))
+      (cons nil nil))
+     ;; In a C++ member initialization list.
+     ((and (eq (char-before match-pos) ?,)
+          (c-major-mode-is 'c++-mode)
+          (save-excursion
+            (goto-char match-pos)
+            (c-back-over-member-initializers)))
+      (c-put-char-property (1- match-pos) 'c-type 'c-not-decl)
+      (cons 'not-decl nil))
+     ;; At start of a declaration inside a declaration paren.
+     ((save-excursion
+       (goto-char match-pos)
+       (and (memq (char-before match-pos) '(?\( ?\,))
+            (c-go-up-list-backward match-pos
+                                       ; c-determine-limit is too slow, here.
+                                   (max (- (point) 2000) (point-min)))
+            (eq (char-after) ?\()
+            (let ((type (c-get-char-property (point) 'c-type)))
+              (or (memq type '(c-decl-arg-start c-decl-type-start))
+                  (progn
+                    (c-backward-syntactic-ws)
+                    (cond
+                     ((and toplev
+                           (eq (char-before) ?\)))
+                      (save-excursion
+                        (and (c-go-list-backward nil (max (- (point) 2000)
+                                                          (point-min)))
+                             (eq (char-after) ?\()
+                             (progn (c-backward-syntactic-ws)
+                                    (c-back-over-compound-identifier)))))
+                     ((save-excursion
+                        (and
+                         (c-back-over-compound-identifier)
+                         (progn
+                           (c-backward-syntactic-ws)
+                           (or (bobp)
+                               (progn
+                                 (setq type (c-get-char-property (1- (point))
+                                                                 'c-type))
+                                 (memq type '(c-decl-arg-start
+                                              c-decl-type-start))))))))
+                     ((and (zerop (c-backward-token-2))
+                           (looking-at c-fun-name-substitute-key)))))))))
+      ;; Cache the result of this test for next time around.
+      (c-put-char-property (1- match-pos) 'c-type 'c-decl-arg-start)
+      (cons 'decl nil))
+     (t (cons 'arglist t)))))
 
 (defun c-font-lock-single-decl (limit decl-or-cast match-pos context toplev)
   ;; Try to fontify a single declaration, together with all its declarators.
@@ -1549,7 +1596,7 @@ casts and declarations are fontified.  Used on level 2 
and higher."
                       nil)
                   (setq decl-or-cast
                         (c-forward-decl-or-cast-1
-                         match-pos context last-cast-end))
+                         match-pos context last-cast-end inside-macro))
 
                   ;; Ensure that c-<>-arg-sep c-type properties are in place 
on the
                   ;; commas separating the arguments inside template/generic 
<..>s.
diff --git a/lisp/progmodes/cc-langs.el b/lisp/progmodes/cc-langs.el
index cd23483a58..291af038b7 100644
--- a/lisp/progmodes/cc-langs.el
+++ b/lisp/progmodes/cc-langs.el
@@ -93,7 +93,7 @@
 ;; definitions (i.e. this file and/or cc-fonts.el) if necessary.
 ;;
 ;; A small example of a derived mode is available at
-;; <http://cc-mode.sourceforge.net/derived-mode-ex.el>.  It also
+;; <https://cc-mode.sourceforge.net/derived-mode-ex.el>.  It also
 ;; contains some useful hints for derived mode developers.
 
 ;;; Using language variables:
@@ -1449,8 +1449,7 @@ form\".  See also `c-op-identifier-prefix'."
         "??'=" "xor_eq" "&=" "and_eq" "|=" "??!=" "or_eq"
         "<<" ">>" ">>=" "<<=" "==" "!=" "not_eq" "<=>" "<=" ">="
         "&&" "and" "||" "??!??!" "or" "++" "--" "," "->*" "->"
-        "()" "[]" "<::>" "??(??)")
-  ;; These work like identifiers in Pike.
+        "()" "[]" "\"\"" "<::>" "??(??)")
   pike '("`+" "`-" "`&" "`|" "`^" "`<<" "`>>" "`*" "`/" "`%" "`~"
         "`==" "`<" "`>" "`!" "`[]" "`[]=" "`->" "`->=" "`()" "``+"
         "``-" "``&" "``|" "``^" "``<<" "``>>" "``*" "``/" "``%"
@@ -2295,11 +2294,22 @@ declaration with a type as a default value.  This is 
used only in
 C++ Mode, e.g. \"<typename X = Y>\"."
   t    nil
   c++  '("class" "typename"))
-
 (c-lang-defconst c-template-typename-key
   t (c-make-keywords-re t (c-lang-const c-template-typename-kwds)))
 (c-lang-defvar c-template-typename-key (c-lang-const c-template-typename-key))
 
+(c-lang-defconst c-self-contained-typename-kwds
+  "Keywords where the following name is a type name which can be
+used in declarations without the keyword."
+  t    nil
+  c++  '("typename"))
+
+(c-lang-defconst c-self-contained-typename-key
+  ;; Adorned regexp matching `c-self-contained-typename-key'.
+  t (c-make-keywords-re t (c-lang-const c-self-contained-typename-kwds)))
+(c-lang-defvar c-self-contained-typename-key
+              (c-lang-const c-self-contained-typename-key))
+
 (c-lang-defconst c-type-prefix-kwds
   "Keywords where the following name - if any - is a type name, and
 where the keyword together with the symbol works as a type in
@@ -2309,7 +2319,7 @@ Note that an alternative if the second part doesn't hold 
is
 `c-type-list-kwds'.  Keywords on this list are typically also present
 on one of the `*-decl-kwds' lists."
   t    nil
-  c    '("struct" "union" "enum")
+  (c objc) '("struct" "union" "enum")
   c++  (append '("class" "typename")
               (c-lang-const c-type-prefix-kwds c)))
 
@@ -2936,6 +2946,15 @@ regexp if `c-colon-type-list-kwds' isn't nil."
          "[^][{}();,/#=:]*:")))
 (c-lang-defvar c-colon-type-list-re (c-lang-const c-colon-type-list-re))
 
+(c-lang-defconst c-sub-colon-type-list-re
+  "Regexp matching buffer content that may come between a keyword in
+`c-colon-type-list-kwds' and a putative colon, or nil if there are no
+such keywords.  Exception: it does not match any C++ attributes."
+  t (if (c-lang-const c-colon-type-list-re)
+       (substring (c-lang-const c-colon-type-list-re) 0 -1)))
+(c-lang-defvar c-sub-colon-type-list-re
+  (c-lang-const c-sub-colon-type-list-re))
+
 (c-lang-defconst c-paren-nontype-kwds
   "Keywords that may be followed by a parenthesis expression that doesn't
 contain type identifiers."
@@ -3470,76 +3489,179 @@ Note that Java specific rules are currently applied to 
tell this from
 (c-lang-defvar c-regular-keywords-regexp
   (c-lang-const c-regular-keywords-regexp))
 
-(c-lang-defconst c-primary-expr-regexp
-  ;; Regexp matching the start of any primary expression, i.e. any
-  ;; literal, symbol, prefix operator, and '('.  It doesn't need to
-  ;; exclude keywords; they are excluded afterwards unless the second
-  ;; submatch matches. If the first but not the second submatch
-  ;; matches then it is an ambiguous primary expression; it could also
-  ;; be a match of e.g. an infix operator. (The case with ambiguous
-  ;; keyword operators isn't handled.)
-
+(c-lang-defconst c-primary-expr-regexp-details
+  ;; A list of c-primary-expr-regexp and three numbers identifying particular
+  ;; matches in it.
   t (let* ((prefix-ops
+           ;All prefix ops
            (c-filter-ops (c-lang-const c-operators)
                          '(prefix)
                          (lambda (op)
                            ;; Filter out the special case prefix
                            ;; operators that are close parens.
                            (not (string-match "\\s)" op)))))
-
-          (nonkeyword-prefix-ops
-           (c-filter-ops prefix-ops
-                         t
-                         "\\`\\(\\s.\\|\\s(\\|\\s)\\)+\\'"))
+          (postfix-ops
+           ;; All postfix ops.
+           (c-filter-ops (c-lang-const c-operators)
+                         '(postfix)
+                         (lambda (op) (not (string-match "\\s)" op)))))
 
           (in-or-postfix-ops
+           ;; All ops which are postfix, etc.
            (c-filter-ops (c-lang-const c-operators)
                          '(postfix
                            postfix-if-paren
                            left-assoc
                            right-assoc
                            right-assoc-sequence)
-                         t)))
+                         t))
 
-      (concat
-       "\\("
-       ;; Take out all symbol class operators from `prefix-ops' and make the
-       ;; first submatch from them together with `c-primary-expr-kwds'.
-       (c-make-keywords-re t
-        (append (c-lang-const c-primary-expr-kwds)
-                (c--set-difference prefix-ops nonkeyword-prefix-ops
-                                   :test 'string-equal)))
-
-       "\\|"
-       ;; Match all ambiguous operators.
-       (c-make-keywords-re nil
-        (c--intersection nonkeyword-prefix-ops in-or-postfix-ops
-                         :test 'string-equal))
-       "\\)"
-
-       "\\|"
-       ;; Now match all other symbols.
-       (c-lang-const c-symbol-start)
-
-       "\\|"
-       ;; The chars that can start integer and floating point
-       ;; constants.
-       "\\.?[0-9]"
-
-       "\\|"
-       ;; The unambiguous operators from `prefix-ops'.
-       (c-make-keywords-re nil
-        (c--set-difference nonkeyword-prefix-ops in-or-postfix-ops
-                           :test 'string-equal))
-
-       "\\|"
-       ;; Match string and character literals.
-       "\\s\""
-       (if (memq 'gen-string-delim c-emacs-features)
-          "\\|\\s|"
-        ""))))
+          (nonkeyword-prefix-ops
+           ;; All prefix ops apart from those which are keywords.
+           (c-filter-ops prefix-ops
+                         t
+                         "\\`\\(\\s.\\|\\s(\\|\\s)\\)+\\'"))
+          (nonkeyword-postfix-ops
+           ;; All postfix ops apart from those which are keywords.
+           (c-filter-ops postfix-ops
+                         t
+                         "\\`\\(\\s.\\|\\s(\\|\\s)\\)+\\'"))
+
+          (cast-ops
+           ;; All prefix ops which have syntax open-paren.
+           (c-filter-ops prefix-ops
+                         t
+                         "\\`\\s(\\'"))
+
+          (ambiguous-pre/postfix-ops
+           ;; All non-keyword ops which are both prefix and postfix, apart
+           ;; from (.
+           (c--set-difference (c--intersection nonkeyword-prefix-ops
+                                               nonkeyword-postfix-ops
+                                               :test 'string-equal)
+                              cast-ops :test 'string-equal))
+          (unambiguous-prefix-ops
+           ;; All non-keyword ops which are prefix ops and not any other type
+           ;; of op.
+           (c--set-difference nonkeyword-prefix-ops
+                              in-or-postfix-ops
+                              :test 'string-equal))
+          (ambiguous-prefix-ops
+           ;; All non-keyword ops which are prefix ops and also some other
+           ;; type of op.
+           (c--intersection nonkeyword-prefix-ops
+                            in-or-postfix-ops
+                            :test 'string-equal)) ; This has everything we
+                                                  ; need, plus (, ++, --.
+
+          (ambiguous-prefix-non-postfix-ops
+           ;; All non-keyword prefix ops which are also other types of ops
+           ;; apart from postfix ops.
+           (c--set-difference (c--set-difference ambiguous-prefix-ops
+                                                 ambiguous-pre/postfix-ops
+                                                 :test 'string-equal)
+                              cast-ops :test 'string-equal))
+
+          (primary-expression-keywords-string
+           ;; Take out all symbol class operators from `prefix-ops' and make
+           ;; the first submatch from them together with
+           ;; `c-primary-expr-kwds'.
+           (c-make-keywords-re t
+             (append (c-lang-const c-primary-expr-kwds)
+                     (c--set-difference prefix-ops nonkeyword-prefix-ops
+                                        :test 'string-equal))))
+          (primary-expression-keywords-string-depth
+           (regexp-opt-depth primary-expression-keywords-string))
+
+          (ambiguous-pre/postfix-string
+           (c-make-keywords-re nil ambiguous-pre/postfix-ops))
+          (ambiguous-pre/postfix-string-depth
+           (regexp-opt-depth ambiguous-pre/postfix-string))
+
+          (ambiguous-prefix-non-postfix-string
+           (c-make-keywords-re nil ambiguous-prefix-non-postfix-ops))
+          (ambiguous-prefix-non-postfix-string-depth
+           (regexp-opt-depth ambiguous-prefix-non-postfix-string))
+
+          (per-++---match (+ 2 primary-expression-keywords-string-depth))
+          (per-&*+--match (+ 1 per-++---match
+                             ambiguous-pre/postfix-string-depth))
+          (per-\(-match (+ 1 per-&*+--match
+                           ambiguous-prefix-non-postfix-string-depth)))
+
+      (list
+       (concat
+       "\\("                           ; 1
+       primary-expression-keywords-string
+       "\\|"
+       ;; Match all ambiguous operators.
+       "\\("                   ; 2 + primary-expression-keywords-string-depth
+       ambiguous-pre/postfix-string
+       "\\)\\|\\("             ; 3 + primary-expression-keywords-string-depth
+                               ;   + ambiguous-pre/postfix-string-depth
+       ambiguous-prefix-non-postfix-string
+       "\\)\\|"
+       "\\((\\)"              ; 4 + primary-expression-keywords-string-depth
+                              ;   + ambiguous-pre/postfix-string-depth
+                              ;   + ambiguous-prefix-non-postfix-string-depth
+       "\\)"
+
+       "\\|"
+       ;; Now match all other symbols.
+       (c-lang-const c-symbol-start)
+
+       "\\|"
+       ;; The chars that can start integer and floating point
+       ;; constants.
+       "\\.?[0-9]"
+
+       "\\|"
+       ;; The unambiguous operators from `prefix-ops'.
+       (c-make-keywords-re nil
+         ;; (c--set-difference nonkeyword-prefix-ops in-or-postfix-ops
+         ;;                :test 'string-equal)
+         unambiguous-prefix-ops
+         )
+
+       "\\|"
+       ;; Match string and character literals.
+       "\\s\""
+       (if (memq 'gen-string-delim c-emacs-features)
+           "\\|\\s|"
+         ""))
+       per-++---match
+       per-&*+--match
+       per-\(-match)))
+
+(c-lang-defconst c-primary-expr-regexp
+  ;; Regexp matching the start of any primary expression, i.e. any
+  ;; literal, symbol, prefix operator, and '('.  It doesn't need to
+  ;; exclude keywords; they are excluded afterwards unless the second
+  ;; submatch matches. If the first but not the second submatch
+  ;; matches then it is an ambiguous primary expression; it could also
+  ;; be a match of e.g. an infix operator. (The case with ambiguous
+  ;; keyword operators isn't handled.)
+  t (car (c-lang-const c-primary-expr-regexp-details)))
 (c-lang-defvar c-primary-expr-regexp (c-lang-const c-primary-expr-regexp))
 
+(c-lang-defconst c-per-++---match
+  ;; Match number for group in `c-primary-expr-regexp' which matches (in C)
+  ;; the ++ and -- operators, and any similar ones in other languages.
+  t (cadr (c-lang-const c-primary-expr-regexp-details)))
+(c-lang-defvar c-per-++---match (c-lang-const c-per-++---match))
+
+(c-lang-defconst c-per-&*+--match
+  ;; Match number for group in `c-primary-expr-regexp' which matches (in C)
+  ;; the &, *, +, and - operators, and any similar ones in other languages.
+  t (car (cddr (c-lang-const c-primary-expr-regexp-details))))
+(c-lang-defvar c-per-&*+--match (c-lang-const c-per-&*+--match))
+
+(c-lang-defconst c-per-\(-match
+  ;; Match number for group in `c-primary-expr-regexp' which matches (in C)
+  ;; the ( operator, and any similar ones in other languages.
+  t (cadr (cddr (c-lang-const c-primary-expr-regexp-details))))
+(c-lang-defvar c-per-\(-match (c-lang-const c-per-\(-match))
+
 
 ;;; Additional constants for parser-level constructs.
 
diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el
index 2003b09ded..fb5ef69413 100644
--- a/lisp/progmodes/cc-mode.el
+++ b/lisp/progmodes/cc-mode.el
@@ -66,12 +66,12 @@
 ;; You can get the latest version of CC Mode, including PostScript
 ;; documentation and separate individual files from:
 ;;
-;;     http://cc-mode.sourceforge.net/
+;;     https://cc-mode.sourceforge.net/
 ;;
 ;; You can join a moderated CC Mode announcement-only mailing list by
 ;; visiting
 ;;
-;;    http://lists.sourceforge.net/mailman/listinfo/cc-mode-announce
+;;    https://lists.sourceforge.net/mailman/listinfo/cc-mode-announce
 
 ;; Externally maintained major modes which use CC-mode's engine include:
 ;; - cuda-mode
@@ -172,7 +172,7 @@
 ;; `c-font-lock-init' too to set up CC Mode's font lock support.
 ;;
 ;; See cc-langs.el for further info.  A small example of a derived mode
-;; is also available at <http://cc-mode.sourceforge.net/
+;; is also available at <https://cc-mode.sourceforge.net/
 ;; derived-mode-ex.el>.
 
 (defun c-leave-cc-mode-mode ()
@@ -2080,13 +2080,14 @@ with // and /*, not more generic line and block 
comments."
 (defun c-update-new-id (end)
   ;; Note the bounds of any identifier that END is in or just after, in
   ;; `c-new-id-start' and `c-new-id-end'.  Otherwise set these variables to
-  ;; nil.
+  ;; nil.  Set `c-new-id-is-type' unconditionally to nil.
   (save-excursion
     (goto-char end)
     (let ((id-beg (c-on-identifier)))
       (setq c-new-id-start id-beg
            c-new-id-end (and id-beg
-                             (progn (c-end-of-current-token) (point)))))))
+                             (progn (c-end-of-current-token) (point)))
+           c-new-id-is-type nil))))
 
 (defun c-post-command ()
   ;; If point was inside of a new identifier and no longer is, record that
@@ -2389,6 +2390,8 @@ with // and /*, not more generic line and block comments."
          ;; Go to a less nested declaration each time round this loop.
          (and
           (setq old-pos (point))
+          ;; The following form tries to move to the end of the previous
+          ;; declaration without moving outside of an enclosing {.
           (let (pseudo)
             (while
                 (and
@@ -2403,7 +2406,9 @@ with // and /*, not more generic line and block comments."
                           (setq pseudo (c-cheap-inside-bracelist-p 
(c-parse-state)))))))
               (goto-char pseudo))
             t)
-          (>= (point) bod-lim)
+          (or (> (point) bod-lim)
+              (eq bod-lim (point-min)))
+          ;; Move forward to the start of the next declaration.
           (progn (c-forward-syntactic-ws)
                  ;; Have we got stuck in a comment at EOB?
                  (not (and (eobp)
@@ -2500,7 +2505,7 @@ with // and /*, not more generic line and block comments."
                     (not (eobp)))
              (progn
                (c-forward-over-token)
-               ;; Cope with having POS withing a syntactically invalid
+               ;; Cope with having POS within a syntactically invalid
                ;; (...), by moving backward out of the parens and trying
                ;; again.
                (when (and (eq (char-before) ?\))
diff --git a/lisp/progmodes/compile.el b/lisp/progmodes/compile.el
index 6473b50778..18c996e899 100644
--- a/lisp/progmodes/compile.el
+++ b/lisp/progmodes/compile.el
@@ -175,7 +175,7 @@ and a string describing how the process finished.")
 (defvar compilation-num-warnings-found 0)
 (defvar compilation-num-infos-found 0)
 
-(defconst compilation-mode-line-errors
+(defvar compilation-mode-line-errors
   '(" [" (:propertize (:eval (int-to-string compilation-num-errors-found))
                       face compilation-error
                       help-echo "Number of errors so far")
diff --git a/lisp/progmodes/cperl-mode.el b/lisp/progmodes/cperl-mode.el
index 20a73e238e..b36896ae7c 100644
--- a/lisp/progmodes/cperl-mode.el
+++ b/lisp/progmodes/cperl-mode.el
@@ -1429,10 +1429,40 @@ the last)."
   (rx (sequence line-start (0+ blank) (eval cperl--imenu-entries-rx)))
   "The regular expression used for `outline-minor-mode'.")
 
-(defvar cperl-mode-syntax-table nil
+(defvar cperl-mode-syntax-table
+  (let ((st (make-syntax-table)))
+    (modify-syntax-entry ?\\ "\\" st)
+    (modify-syntax-entry ?/  "."  st)
+    (modify-syntax-entry ?*  "."  st)
+    (modify-syntax-entry ?+  "."  st)
+    (modify-syntax-entry ?-  "."  st)
+    (modify-syntax-entry ?=  "."  st)
+    (modify-syntax-entry ?%  "."  st)
+    (modify-syntax-entry ?<  "."  st)
+    (modify-syntax-entry ?>  "."  st)
+    (modify-syntax-entry ?&  "."  st)
+    (modify-syntax-entry ?$  "\\" st)
+    (modify-syntax-entry ?\n ">"  st)
+    (modify-syntax-entry ?#  "<"  st)
+    (modify-syntax-entry ?'  "\"" st)
+    (modify-syntax-entry ?`  "\"" st)
+    (if cperl-under-as-char
+        (modify-syntax-entry ?_ "w" st))
+    (modify-syntax-entry ?:  "_"  st)
+    (modify-syntax-entry ?|  "."  st)
+    st)
   "Syntax table in use in CPerl mode buffers.")
 
-(defvar cperl-string-syntax-table nil
+(defvar cperl-string-syntax-table
+  (let ((st (copy-syntax-table cperl-mode-syntax-table)))
+    (modify-syntax-entry ?$  "." st)
+    (modify-syntax-entry ?\{ "." st)
+    (modify-syntax-entry ?\} "." st)
+    (modify-syntax-entry ?\" "." st)
+    (modify-syntax-entry ?'  "." st)
+    (modify-syntax-entry ?`  "." st)
+    (modify-syntax-entry ?#  "." st) ; (?# comment )
+    st)
   "Syntax table in use in CPerl mode string-like chunks.")
 
 (defsubst cperl-1- (p)
@@ -1441,38 +1471,6 @@ the last)."
 (defsubst cperl-1+ (p)
   (min (point-max) (1+ p)))
 
-(if cperl-mode-syntax-table
-    ()
-  (setq cperl-mode-syntax-table (make-syntax-table))
-  (modify-syntax-entry ?\\ "\\" cperl-mode-syntax-table)
-  (modify-syntax-entry ?/ "." cperl-mode-syntax-table)
-  (modify-syntax-entry ?* "." cperl-mode-syntax-table)
-  (modify-syntax-entry ?+ "." cperl-mode-syntax-table)
-  (modify-syntax-entry ?- "." cperl-mode-syntax-table)
-  (modify-syntax-entry ?= "." cperl-mode-syntax-table)
-  (modify-syntax-entry ?% "." cperl-mode-syntax-table)
-  (modify-syntax-entry ?< "." cperl-mode-syntax-table)
-  (modify-syntax-entry ?> "." cperl-mode-syntax-table)
-  (modify-syntax-entry ?& "." cperl-mode-syntax-table)
-  (modify-syntax-entry ?$ "\\" cperl-mode-syntax-table)
-  (modify-syntax-entry ?\n ">" cperl-mode-syntax-table)
-  (modify-syntax-entry ?# "<" cperl-mode-syntax-table)
-  (modify-syntax-entry ?' "\"" cperl-mode-syntax-table)
-  (modify-syntax-entry ?` "\"" cperl-mode-syntax-table)
-  (if cperl-under-as-char
-      (modify-syntax-entry ?_ "w" cperl-mode-syntax-table))
-  (modify-syntax-entry ?: "_" cperl-mode-syntax-table)
-  (modify-syntax-entry ?| "." cperl-mode-syntax-table)
-  (setq cperl-string-syntax-table (copy-syntax-table cperl-mode-syntax-table))
-  (modify-syntax-entry ?$ "." cperl-string-syntax-table)
-  (modify-syntax-entry ?\{ "." cperl-string-syntax-table)
-  (modify-syntax-entry ?\} "." cperl-string-syntax-table)
-  (modify-syntax-entry ?\" "." cperl-string-syntax-table)
-  (modify-syntax-entry ?' "." cperl-string-syntax-table)
-  (modify-syntax-entry ?` "." cperl-string-syntax-table)
-  (modify-syntax-entry ?# "." cperl-string-syntax-table)) ; (?# comment )
-
-
 
 (defvar cperl-faces-init nil)
 ;; Fix for msb.el
@@ -8323,7 +8321,7 @@ the appropriate statement modifier."
                                  'cperl-short-docs
                                  'variable-documentation))))
         (Man-switches "")
-        (manual-program (if is-func "perldoc -f" "perldoc")))
+         (manual-program (concat "perldoc -i" (if is-func " -f"))))
     (Man-getpage-in-background word)))
 
 ;;;###autoload
diff --git a/lisp/progmodes/cpp.el b/lisp/progmodes/cpp.el
index f4584b6311..43e430d40c 100644
--- a/lisp/progmodes/cpp.el
+++ b/lisp/progmodes/cpp.el
@@ -1,6 +1,6 @@
 ;;; cpp.el --- highlight or hide text according to cpp conditionals -*- 
lexical-binding: t -*-
 
-;; Copyright (C) 1994-1995, 2001-2022 Free Software Foundation, Inc.
+;; Copyright (C) 1994-2022 Free Software Foundation, Inc.
 
 ;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
 ;; Keywords: c, faces, tools
@@ -98,8 +98,8 @@ Each entry is a list with the following elements:
                                (const :tag "Both branches writable" both)))))
 
 (defcustom cpp-message-min-time-interval 1.0
-  "Minimum time interval in seconds for `cpp-progress-message' messages.
-If nil, `cpp-progress-message' prints no progress messages."
+  "Minimum time interval in seconds for `cpp-highlight-buffer' progress 
messages.
+If nil, `cpp-highlight-buffer' prints no progress messages."
   :type '(choice (const :tag "Disable progress messages" nil)
                  float)
   :version "26.1")
@@ -218,14 +218,15 @@ A prefix arg suppresses display of that buffer."
   (cpp-parse-reset)
   (if (null cpp-edit-list)
       (cpp-edit-load))
-  (let (cpp-state-stack)
+  (let ((reporter
+         (and cpp-message-min-time-interval
+              (make-progress-reporter "Parsing..." (point-min) (point-max)
+                                      nil nil cpp-message-min-time-interval)))
+        cpp-state-stack)
     (save-excursion
       (goto-char (point-min))
-      (cpp-progress-message "Parsing...")
       (while (re-search-forward cpp-parse-regexp nil t)
-       (cpp-progress-message "Parsing...%d%%"
-                             (floor (* 100.0 (- (point) (point-min)))
-                                    (buffer-size)))
+        (when reporter (progress-reporter-update reporter (point)))
        (let ((match (buffer-substring (match-beginning 0) (match-end 0))))
          (cond ((or (string-equal match "'")
                     (string-equal match "\""))
@@ -268,7 +269,7 @@ A prefix arg suppresses display of that buffer."
                          (cpp-parse-close from to))
                         (t
                          (cpp-parse-error "Parser error"))))))))
-      (cpp-progress-message "Parsing...done"))
+      (when reporter (progress-reporter-done reporter)))
     (if cpp-state-stack
       (save-excursion
        (goto-char (nth 3 (car cpp-state-stack)))
@@ -410,47 +411,45 @@ A prefix arg suppresses display of that buffer."
 
 ;;; Edit Buffer:
 
-(defvar cpp-edit-mode-map
-  (let ((map (make-keymap)))
-    (suppress-keymap map)
-    (define-key map [ down-mouse-2 ] 'cpp-push-button)
-    (define-key map [ mouse-2 ] 'ignore)
-    (define-key map " " 'scroll-up-command)
-    (define-key map [?\S-\ ] 'scroll-down-command)
-    (define-key map "\C-?" 'scroll-down-command)
-    (define-key map [ delete ] 'scroll-down)
-    (define-key map "\C-c\C-c" 'cpp-edit-apply)
-    (define-key map "a" 'cpp-edit-apply)
-    (define-key map "A" 'cpp-edit-apply)
-    (define-key map "r" 'cpp-edit-reset)
-    (define-key map "R" 'cpp-edit-reset)
-    (define-key map "s" 'cpp-edit-save)
-    (define-key map "S" 'cpp-edit-save)
-    (define-key map "l" 'cpp-edit-load)
-    (define-key map "L" 'cpp-edit-load)
-    (define-key map "h" 'cpp-edit-home)
-    (define-key map "H" 'cpp-edit-home)
-    (define-key map "b" 'cpp-edit-background)
-    (define-key map "B" 'cpp-edit-background)
-    (define-key map "k" 'cpp-edit-known)
-    (define-key map "K" 'cpp-edit-known)
-    (define-key map "u" 'cpp-edit-unknown)
-    (define-key map "u" 'cpp-edit-unknown)
-    (define-key map "t" 'cpp-edit-true)
-    (define-key map "T" 'cpp-edit-true)
-    (define-key map "f" 'cpp-edit-false)
-    (define-key map "F" 'cpp-edit-false)
-    (define-key map "w" 'cpp-edit-write)
-    (define-key map "W" 'cpp-edit-write)
-    (define-key map "X" 'cpp-edit-toggle-known)
-    (define-key map "x" 'cpp-edit-toggle-known)
-    (define-key map "Y" 'cpp-edit-toggle-unknown)
-    (define-key map "y" 'cpp-edit-toggle-unknown)
-    (define-key map "q" 'bury-buffer)
-    (define-key map "Q" 'bury-buffer)
-    map)
-  "Keymap for `cpp-edit-mode'.")
-
+(defvar-keymap cpp-edit-mode-map
+  :doc "Keymap for `cpp-edit-mode'."
+  :full t
+  :suppress t
+  "<down-mouse-2>" #'cpp-push-button
+  "<mouse-2>"      #'ignore
+  "SPC"      #'scroll-up-command
+  "S-SPC"    #'scroll-down-command
+  "DEL"      #'scroll-down-command
+  "<delete>" #'scroll-down
+  "C-c C-c"  #'cpp-edit-apply
+  "a"        #'cpp-edit-apply
+  "A"        #'cpp-edit-apply
+  "r"        #'cpp-edit-reset
+  "R"        #'cpp-edit-reset
+  "s"        #'cpp-edit-save
+  "S"        #'cpp-edit-save
+  "l"        #'cpp-edit-load
+  "L"        #'cpp-edit-load
+  "h"        #'cpp-edit-home
+  "H"        #'cpp-edit-home
+  "b"        #'cpp-edit-background
+  "B"        #'cpp-edit-background
+  "k"        #'cpp-edit-known
+  "K"        #'cpp-edit-known
+  "u"        #'cpp-edit-unknown
+  "U"        #'cpp-edit-unknown
+  "t"        #'cpp-edit-true
+  "T"        #'cpp-edit-true
+  "f"        #'cpp-edit-false
+  "F"        #'cpp-edit-false
+  "w"        #'cpp-edit-write
+  "W"        #'cpp-edit-write
+  "X"        #'cpp-edit-toggle-known
+  "x"        #'cpp-edit-toggle-known
+  "Y"        #'cpp-edit-toggle-unknown
+  "y"        #'cpp-edit-toggle-unknown
+  "q"        #'bury-buffer
+  "Q"        #'bury-buffer)
 
 
 (defvar-local cpp-edit-symbols nil
@@ -816,6 +815,7 @@ Type must be one of the types defined in 
`cpp-face-type-list'."
 
 ;;; Utilities:
 
+(make-obsolete-variable 'cpp-progress-time nil "29.1")
 (defvar cpp-progress-time 0
   "Last time `cpp-progress-message' issued a progress message.")
 
@@ -825,6 +825,7 @@ Type must be one of the types defined in 
`cpp-face-type-list'."
 Print messages at most once every `cpp-message-min-time-interval' seconds.
 If that option is nil, don't prints messages.
 ARGS are the same as for `message'."
+  (declare (obsolete make-progress-reporter "29.1"))
   (when cpp-message-min-time-interval
     (let ((time (current-time)))
       (unless (time-less-p cpp-message-min-time-interval
diff --git a/lisp/progmodes/dcl-mode.el b/lisp/progmodes/dcl-mode.el
index 8f79cdaaab..f1d7f236b9 100644
--- a/lisp/progmodes/dcl-mode.el
+++ b/lisp/progmodes/dcl-mode.el
@@ -1,6 +1,6 @@
 ;;; dcl-mode.el --- major mode for editing DCL command files  -*- 
lexical-binding: t; -*-
 
-;; Copyright (C) 1997, 2001-2022 Free Software Foundation, Inc.
+;; Copyright (C) 1997-2022 Free Software Foundation, Inc.
 
 ;; Author: Odd Gripenstam <gripenstamol@decus.se>
 ;; Maintainer: emacs-devel@gnu.org
@@ -258,38 +258,34 @@ See `imenu-generic-expression' for details."
 ;;; *** Global variables ****************************************************
 
 
-(defvar dcl-mode-syntax-table nil
+(defvar dcl-mode-syntax-table
+  (let ((st (make-syntax-table)))
+    (modify-syntax-entry ?!  "<" st) ; comment start
+    (modify-syntax-entry ?\n ">" st) ; comment end
+    (modify-syntax-entry ?< "(>" st) ; < and ...
+    (modify-syntax-entry ?> ")<" st) ; > is a matching pair
+    (modify-syntax-entry ?\\ "_" st) ; not an escape
+    st)
   "Syntax table used in DCL-buffers.")
-(unless dcl-mode-syntax-table
-  (setq dcl-mode-syntax-table (make-syntax-table))
-  (modify-syntax-entry ?!  "<" dcl-mode-syntax-table) ; comment start
-  (modify-syntax-entry ?\n ">" dcl-mode-syntax-table) ; comment end
-  (modify-syntax-entry ?< "(>" dcl-mode-syntax-table) ; < and ...
-  (modify-syntax-entry ?> ")<" dcl-mode-syntax-table) ; > is a matching pair
-  (modify-syntax-entry ?\\ "_" dcl-mode-syntax-table) ; not an escape
-)
-
-
-(defvar dcl-mode-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map "\e\n"     #'dcl-split-line)
-    (define-key map "\e\t"     #'tempo-complete-tag)
-    (define-key map "\e^"      #'dcl-delete-indentation)
-    (define-key map "\em"      #'dcl-back-to-indentation)
-    (define-key map "\ee"        #'dcl-forward-command)
-    (define-key map "\ea"        #'dcl-backward-command)
-    (define-key map "\e\C-q"   #'dcl-indent-command)
-    (define-key map "\t"         #'dcl-tab)
-    (define-key map ":"          #'dcl-electric-character)
-    (define-key map "F"          #'dcl-electric-character)
-    (define-key map "f"          #'dcl-electric-character)
-    (define-key map "E"          #'dcl-electric-character)
-    (define-key map "e"          #'dcl-electric-character)
-    (define-key map "\C-c\C-o"         #'dcl-set-option)
-    (define-key map "\C-c\C-f"         #'tempo-forward-mark)
-    (define-key map "\C-c\C-b"         #'tempo-backward-mark)
-    map)
-  "Keymap used in DCL-mode buffers.")
+
+(defvar-keymap dcl-mode-map
+  :doc "Keymap used in DCL-mode buffers."
+  "M-RET"   #'dcl-split-line
+  "M-TAB"   #'tempo-complete-tag
+  "M-^"     #'dcl-delete-indentation
+  "M-m"     #'dcl-back-to-indentation
+  "M-e"     #'dcl-forward-command
+  "M-a"     #'dcl-backward-command
+  "C-M-q"   #'dcl-indent-command
+  "TAB"     #'dcl-tab
+  ":"       #'dcl-electric-character
+  "F"       #'dcl-electric-character
+  "f"       #'dcl-electric-character
+  "E"       #'dcl-electric-character
+  "e"       #'dcl-electric-character
+  "C-c C-o" #'dcl-set-option
+  "C-c C-f" #'tempo-forward-mark
+  "C-c C-b" #'tempo-backward-mark)
 
 (easy-menu-define dcl-mode-menu dcl-mode-map
   "Menu for DCL-mode buffers."
diff --git a/lisp/progmodes/ebnf2ps.el b/lisp/progmodes/ebnf2ps.el
index 6e42da2d54..1894826fe4 100644
--- a/lisp/progmodes/ebnf2ps.el
+++ b/lisp/progmodes/ebnf2ps.el
@@ -166,8 +166,6 @@
 ;;
 ;; Where setup-ebnf2ps.el should be a file containing:
 ;;
-;;    ;; set load-path if ebnf2ps isn't installed in your Emacs environment
-;;    (setq load-path (append (list "/dir/of/ebnf2ps") load-path))
 ;;    (require 'ebnf2ps)
 ;;    ;; insert here your ebnf2ps settings
 ;;    (setq ebnf-terminal-shape 'bevel)
diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
new file mode 100644
index 0000000000..12808e80c4
--- /dev/null
+++ b/lisp/progmodes/eglot.el
@@ -0,0 +1,3477 @@
+;;; eglot.el --- The Emacs Client for LSP servers  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2018-2022 Free Software Foundation, Inc.
+
+;; Version: 1.9
+;; Author: João Távora <joaotavora@gmail.com>
+;; Maintainer: João Távora <joaotavora@gmail.com>
+;; URL: https://github.com/joaotavora/eglot
+;; Keywords: convenience, languages
+;; Package-Requires: ((emacs "26.3") (jsonrpc "1.0.14") (flymake "1.2.1") 
(project "0.3.0") (xref "1.0.1") (eldoc "1.11.0") (seq "2.23"))
+
+;; This is is a GNU ELPA :core package.  Avoid adding functionality
+;; that is not available in the version of Emacs recorded above or any
+;; of the package dependencies.
+
+;; 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:
+
+;; Eglot ("Emacs Polyglot") is an Emacs LSP client that stays out of
+;; your way.
+;;
+;; Typing M-x eglot in some source file is often enough to get you
+;; started, if the language server you're looking to use is installed
+;; in your system.  Please refer to the manual, available from
+;; https://joaotavora.github.io/eglot/ or from M-x info for more usage
+;; instructions.
+;;
+;; If you wish to contribute changes to Eglot, please do read the user
+;; manual first.  Additionally, take the following in consideration:
+
+;; * Eglot's main job is to hook up the information that language
+;;   servers offer via LSP to Emacs's UI facilities: Xref for
+;;   definition-chasing, Flymake for diagnostics, Eldoc for at-point
+;;   documentation, etc.  Eglot's job is generally *not* to provide
+;;   such a UI itself, though a small number of simple
+;;   counter-examples do exist, for example in the `eglot-rename'
+;;   command.  When a new UI is evidently needed, consider adding a
+;;   new package to Emacs, or extending an existing one.
+;;
+;; * Eglot was designed to function with just the UI facilities found
+;;   in the latest Emacs core, as long as those facilities are also
+;;   available as GNU ELPA :core packages.  Historically, a number of
+;;   :core packages were added or reworked in Emacs to make this
+;;   possible.  This principle should be upheld when adding new LSP
+;;   features or tweaking existing ones.  Design any new facilities in
+;;   a way that they could work in the absence of LSP or using some
+;;   different protocol, then make sure Eglot can link up LSP
+;;   information to it.
+
+;; * There are few Eglot configuration variables.  This principle
+;;   should also be upheld.  If Eglot had these variables, it could be
+;;   duplicating configuration found elsewhere, bloating itself up,
+;;   and making it generally hard to integrate with the ever growing
+;;   set of LSP features and Emacs packages.  For instance, this is
+;;   why one finds a single variable
+;;   `eglot-ignored-server-capabilities' instead of a number of
+;;   capability-specific flags, or why customizing the display of
+;;   LSP-provided documentation is done via ElDoc's variables, not
+;;   Eglot's.
+;;
+;; * Linking up LSP information to other libraries is generally done
+;;   in the `eglot--managed-mode' minor mode function, by
+;;   buffer-locally setting the other library's variables to
+;;   Eglot-specific versions.  When deciding what to set the variable
+;;   to, the general idea is to choose a good default for beginners
+;;   that doesn't clash with Emacs's defaults.  The settings are only
+;;   in place during Eglot's LSP-enriched tenure over a project.  Even
+;;   so, some of those decisions will invariably aggravate a minority
+;;   of Emacs power users, but these users can use `eglot-stay-out-of'
+;;   and `eglot-managed-mode-hook' to adjust things to their
+;;   preferences.
+;;
+;; * On occasion, to enable new features, Eglot can have soft
+;;   dependencies on popular libraries that are not in Emacs core.
+;;   "Soft" means that the dependency doesn't impair any other use of
+;;   Eglot beyond that feature.  Such is the case of the snippet
+;;   functionality, via the Yasnippet package, Markdown formatting of
+;;   at-point documentation via the markdown-mode package, and nicer
+;;   looking completions when the Company package is used.
+
+;;; Code:
+
+(require 'imenu)
+(require 'cl-lib)
+(require 'project)
+(require 'url-parse)
+(require 'url-util)
+(require 'pcase)
+(require 'compile) ; for some faces
+(require 'warnings)
+(require 'flymake)
+(require 'xref)
+(eval-when-compile
+  (require 'subr-x))
+(require 'jsonrpc)
+(require 'filenotify)
+(require 'ert)
+(require 'array)
+
+;; ElDoc is preloaded in Emacs, so `require'-ing won't guarantee we are
+;; using the latest version from GNU Elpa when we load eglot.el.  Use an
+;; heuristic to see if we need to `load' it in Emacs < 28.
+(if (and (< emacs-major-version 28)
+         (not (boundp 'eldoc-documentation-strategy)))
+    (load "eldoc")
+  (require 'eldoc))
+
+;; Similar issue as above for Emacs 26.3 and seq.el.
+(if (< emacs-major-version 27)
+    (load "seq")
+  (require 'seq))
+
+;; forward-declare, but don't require (Emacs 28 doesn't seem to care)
+(defvar markdown-fontify-code-blocks-natively)
+(defvar company-backends)
+(defvar company-tooltip-align-annotations)
+
+
+
+;;; User tweakable stuff
+(defgroup eglot nil
+  "Interaction with Language Server Protocol servers."
+  :prefix "eglot-"
+  :group 'applications)
+
+(defun eglot-alternatives (alternatives)
+  "Compute server-choosing function for `eglot-server-programs'.
+Each element of ALTERNATIVES is a string PROGRAM or a list of
+strings (PROGRAM ARGS...) where program names an LSP server
+program to start with ARGS.  Returns a function of one argument.
+When invoked, that function will return a list (ABSPATH ARGS),
+where ABSPATH is the absolute path of the PROGRAM that was
+chosen (interactively or automatically)."
+  (lambda (&optional interactive)
+    ;; JT@2021-06-13: This function is way more complicated than it
+    ;; could be because it accounts for the fact that
+    ;; `eglot--executable-find' may take much longer to execute on
+    ;; remote files.
+    (let* ((listified (cl-loop for a in alternatives
+                               collect (if (listp a) a (list a))))
+           (err (lambda ()
+                  (error "None of '%s' are valid executables"
+                         (mapconcat #'car listified ", ")))))
+      (cond (interactive
+             (let* ((augmented (mapcar (lambda (a)
+                                         (let ((found (eglot--executable-find
+                                                       (car a) t)))
+                                           (and found
+                                                (cons (car a) (cons found (cdr 
a))))))
+                                       listified))
+                    (available (remove nil augmented)))
+               (cond ((cdr available)
+                      (cdr (assoc
+                            (completing-read
+                             "[eglot] More than one server executable 
available:"
+                             (mapcar #'car available)
+                             nil t nil nil (car (car available)))
+                            available #'equal)))
+                     ((cdr (car available)))
+                     (t
+                      ;; Don't error when used interactively, let the
+                      ;; Eglot prompt the user for alternative (github#719)
+                      nil))))
+            (t
+             (cl-loop for (p . args) in listified
+                      for probe = (eglot--executable-find p t)
+                      when probe return (cons probe args)
+                      finally (funcall err)))))))
+
+(defvar eglot-server-programs `((rust-mode . ,(eglot-alternatives 
'("rust-analyzer" "rls")))
+                                (cmake-mode . ("cmake-language-server"))
+                                (vimrc-mode . ("vim-language-server" 
"--stdio"))
+                                (python-mode
+                                 . ,(eglot-alternatives
+                                     '("pylsp" "pyls" ("pyright-langserver" 
"--stdio") "jedi-language-server")))
+                                ((js-json-mode json-mode) . 
,(eglot-alternatives '(("vscode-json-language-server" "--stdio") 
("json-languageserver" "--stdio"))))
+                                ((js-mode ts-mode typescript-mode)
+                                 . ("typescript-language-server" "--stdio"))
+                                (sh-mode . ("bash-language-server" "start"))
+                                ((php-mode phps-mode)
+                                 . ("php" "vendor/felixfbecker/\
+language-server/bin/php-language-server.php"))
+                                ((c++-mode c-mode) . ,(eglot-alternatives
+                                                       '("clangd" "ccls")))
+                                (((caml-mode :language-id "ocaml")
+                                  (tuareg-mode :language-id "ocaml") 
reason-mode)
+                                 . ("ocamllsp"))
+                                (ruby-mode
+                                 . ("solargraph" "socket" "--port" :autoport))
+                                (haskell-mode
+                                 . ("haskell-language-server-wrapper" "--lsp"))
+                                (elm-mode . ("elm-language-server"))
+                                (mint-mode . ("mint" "ls"))
+                                (kotlin-mode . ("kotlin-language-server"))
+                                ((go-mode go-dot-mod-mode go-dot-work-mode) . 
("gopls"))
+                                ((R-mode ess-r-mode) . ("R" "--slave" "-e"
+                                                        
"languageserver::run()"))
+                                (java-mode . ("jdtls"))
+                                (dart-mode . ("dart" "language-server"
+                                              "--client-id" 
"emacs.eglot-dart"))
+                                (elixir-mode . ("language_server.sh"))
+                                (ada-mode . ("ada_language_server"))
+                                (scala-mode . ("metals-emacs"))
+                                (racket-mode . ("racket" "-l" 
"racket-langserver"))
+                                ((tex-mode context-mode texinfo-mode 
bibtex-mode)
+                                 . ,(eglot-alternatives '("digestif" 
"texlab")))
+                                (erlang-mode . ("erlang_ls" "--transport" 
"stdio"))
+                                (yaml-mode . ("yaml-language-server" 
"--stdio"))
+                                (nix-mode . ,(eglot-alternatives '("nil" 
"rnix-lsp")))
+                                (gdscript-mode . ("localhost" 6008))
+                                ((fortran-mode f90-mode) . ("fortls"))
+                                (futhark-mode . ("futhark" "lsp"))
+                                (lua-mode . ,(eglot-alternatives
+                                              '("lua-language-server" 
"lua-lsp")))
+                                (zig-mode . ("zls"))
+                                (css-mode . ,(eglot-alternatives 
'(("vscode-css-language-server" "--stdio") ("css-languageserver" "--stdio"))))
+                                (html-mode . ,(eglot-alternatives 
'(("vscode-html-language-server" "--stdio") ("html-languageserver" "--stdio"))))
+                                (dockerfile-mode . ("docker-langserver" 
"--stdio"))
+                                ((clojure-mode clojurescript-mode 
clojurec-mode)
+                                 . ("clojure-lsp"))
+                                (csharp-mode . ("omnisharp" "-lsp"))
+                                (purescript-mode . 
("purescript-language-server" "--stdio"))
+                                ((perl-mode cperl-mode) . ("perl" 
"-MPerl::LanguageServer" "-e" "Perl::LanguageServer::run"))
+                                (markdown-mode . ("marksman" "server")))
+  "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
+language server.  The associated CONTACT specifies how to connect
+to a server for those buffers.
+
+MAJOR-MODE can be:
+
+* In the most common case, a symbol such as `c-mode';
+
+* A list (MAJOR-MODE-SYMBOL :LANGUAGE-ID ID) where
+  MAJOR-MODE-SYMBOL is the aforementioned symbol and ID is a
+  string identifying the language to the server;
+
+* A list combining the previous two alternatives, meaning
+  multiple major modes will be associated with a single server
+  program.  This association is such that the same resulting
+  server process will manage buffers of different major modes.
+
+CONTACT can be:
+
+* In the most common case, a list of strings (PROGRAM [ARGS...]).
+  PROGRAM is called with ARGS and is expected to serve LSP requests
+  over the standard input/output channels.
+
+* A list (PROGRAM [ARGS...] :initializationOptions OPTIONS),
+  whereupon PROGRAM is called with ARGS as in the first option,
+  and the LSP \"initializationOptions\" JSON object is
+  constructed from OPTIONS.  If OPTIONS is a unary function, it
+  is called with the server instance and should return a JSON
+  object.
+
+* A list (HOST PORT [TCP-ARGS...]) where HOST is a string and
+  PORT is a positive integer for connecting to a server via TCP.
+  Remaining ARGS are passed to `open-network-stream' for
+  upgrading the connection with encryption or other capabilities.
+
+* A list (PROGRAM [ARGS...] :autoport [MOREARGS...]), whereupon a
+  combination of previous options is used.  First, an attempt is
+  made to find an available server port, then PROGRAM is launched
+  with ARGS; the `:autoport' keyword substituted for that number;
+  and MOREARGS.  Eglot then attempts to establish a TCP
+  connection to that port number on the localhost.
+
+* A cons (CLASS-NAME . INITARGS) where CLASS-NAME is a symbol
+  designating a subclass of `eglot-lsp-server', for representing
+  experimental LSP servers.  INITARGS is a keyword-value plist
+  used to initialize the object of CLASS-NAME, or a plain list
+  interpreted as the previous descriptions of CONTACT.  In the
+  latter case that plain list is used to produce a plist with a
+  suitable :PROCESS initarg to CLASS-NAME.  The class
+  `eglot-lsp-server' descends from `jsonrpc-process-connection',
+  which you should see for the semantics of the mandatory
+  :PROCESS argument.
+
+* A function of a single argument producing any of the above
+  values for CONTACT.  The argument's value is non-nil if the
+  connection was requested interactively (e.g. from the `eglot'
+  command), and nil if it wasn't (e.g. from `eglot-ensure').  If
+  the call is interactive, the function can ask the user for
+  hints on finding the required programs, etc.  Otherwise, it
+  should not ask the user for any input, and return nil or signal
+  an error if it can't produce a valid CONTACT.  The helper
+  function `eglot-alternatives' (which see) can be used to
+  produce a function that offers more than one server for a given
+  MAJOR-MODE.")
+
+(defface eglot-highlight-symbol-face
+  '((t (:inherit bold)))
+  "Face used to highlight the symbol at point.")
+
+(defface eglot-mode-line
+  '((t (:inherit font-lock-constant-face :weight bold)))
+  "Face for package-name in Eglot's mode line.")
+
+(defface eglot-diagnostic-tag-unnecessary-face
+  '((t (:inherit shadow)))
+  "Face used to render unused or unnecessary code.")
+
+(defface eglot-diagnostic-tag-deprecated-face
+  '((t . (:inherit shadow :strike-through t)))
+  "Face used to render deprecated or obsolete code.")
+
+(defcustom eglot-autoreconnect 3
+  "Control ability to reconnect automatically to the LSP server.
+If t, always reconnect automatically (not recommended).  If nil,
+never reconnect automatically after unexpected server shutdowns,
+crashes or network failures.  A positive integer number says to
+only autoreconnect if the previous successful connection attempt
+lasted more than that many seconds."
+  :type '(choice (const :tag "Reconnect automatically" t)
+                 (const :tag "Never reconnect" nil)
+                 (integer :tag "Number of seconds")))
+
+(defcustom eglot-connect-timeout 30
+  "Number of seconds before timing out LSP connection attempts.
+If nil, never time out."
+  :type '(choice (number :tag "Number of seconds")
+                 (const  :tag "Never time out" nil)))
+
+(defcustom eglot-sync-connect 3
+  "Control blocking of LSP connection attempts.
+If t, block for `eglot-connect-timeout' seconds.  A positive
+integer number means block for that many seconds, and then wait
+for the connection in the background.  nil has the same meaning
+as 0, i.e. don't block at all."
+  :type '(choice (const :tag "Block for `eglot-connect-timeout' seconds" t)
+                 (const :tag "Never block" nil)
+                 (integer :tag "Number of seconds to block")))
+
+(defcustom eglot-autoshutdown nil
+  "If non-nil, shut down server after killing last managed buffer."
+  :type 'boolean)
+
+(defcustom eglot-send-changes-idle-time 0.5
+  "Don't tell server of changes before Emacs's been idle for this many 
seconds."
+  :type 'number)
+
+(defcustom eglot-events-buffer-size 2000000
+  "Control the size of the Eglot events buffer.
+If a number, don't let the buffer grow larger than that many
+characters.  If 0, don't use an event's buffer at all.  If nil,
+let the buffer grow forever.
+
+For changes on this variable to take effect on a connection
+already started, you need to restart the connection.  That can be
+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-extend-to-xref nil
+  "If non-nil, activate Eglot in cross-referenced non-project files."
+  :type 'boolean)
+
+(defcustom eglot-menu-string "eglot"
+  "String displayed in mode line when Eglot is active."
+  :type 'string)
+
+(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.")
+
+;; Customizable via `completion-category-overrides'.
+(when (assoc 'flex completion-styles-alist)
+  (add-to-list 'completion-category-defaults '(eglot (styles flex basic))))
+
+
+;;; Constants
+;;;
+(defconst eglot--symbol-kind-names
+  `((1 . "File") (2 . "Module")
+    (3 . "Namespace") (4 . "Package") (5 . "Class")
+    (6 . "Method") (7 . "Property") (8 . "Field")
+    (9 . "Constructor") (10 . "Enum") (11 . "Interface")
+    (12 . "Function") (13 . "Variable") (14 . "Constant")
+    (15 . "String") (16 . "Number") (17 . "Boolean")
+    (18 . "Array") (19 . "Object") (20 . "Key")
+    (21 . "Null") (22 . "EnumMember") (23 . "Struct")
+    (24 . "Event") (25 . "Operator") (26 . "TypeParameter")))
+
+(defconst eglot--kind-names
+  `((1 . "Text") (2 . "Method") (3 . "Function") (4 . "Constructor")
+    (5 . "Field") (6 . "Variable") (7 . "Class") (8 . "Interface")
+    (9 . "Module") (10 . "Property") (11 . "Unit") (12 . "Value")
+    (13 . "Enum") (14 . "Keyword") (15 . "Snippet") (16 . "Color")
+    (17 . "File") (18 . "Reference") (19 . "Folder") (20 . "EnumMember")
+    (21 . "Constant") (22 . "Struct") (23 . "Event") (24 . "Operator")
+    (25 . "TypeParameter")))
+
+(defconst eglot--tag-faces
+  `((1 . eglot-diagnostic-tag-unnecessary-face)
+    (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)
+  "Like Emacs 27's `executable-find', ignore REMOTE on Emacs 26."
+  (if (>= emacs-major-version 27) (executable-find command remote)
+    (executable-find command)))
+
+
+;;; Message verification helpers
+;;;
+(eval-and-compile
+  (defvar eglot--lsp-interface-alist
+    `(
+      (CodeAction (:title) (:kind :diagnostics :edit :command :isPreferred))
+      (ConfigurationItem () (:scopeUri :section))
+      (Command ((:title . string) (:command . string)) (:arguments))
+      (CompletionItem (:label)
+                      (:kind :detail :documentation :deprecated :preselect
+                             :sortText :filterText :insertText 
:insertTextFormat
+                             :textEdit :additionalTextEdits :commitCharacters
+                             :command :data :tags))
+      (Diagnostic (:range :message) (:severity :code :source 
:relatedInformation :codeDescription :tags))
+      (DocumentHighlight (:range) (:kind))
+      (FileSystemWatcher (:globPattern) (:kind))
+      (Hover (:contents) (:range))
+      (InitializeResult (:capabilities) (:serverInfo))
+      (Location (:uri :range))
+      (LocationLink (:targetUri :targetRange :targetSelectionRange) 
(:originSelectionRange))
+      (LogMessageParams (:type :message))
+      (MarkupContent (:kind :value))
+      (ParameterInformation (:label) (:documentation))
+      (Position (:line :character))
+      (Range (:start :end))
+      (Registration (:id :method) (:registerOptions))
+      (ResponseError (:code :message) (:data))
+      (ShowMessageParams (:type :message))
+      (ShowMessageRequestParams (:type :message) (:actions))
+      (SignatureHelp (:signatures) (:activeSignature :activeParameter))
+      (SignatureInformation (:label) (:documentation :parameters 
:activeParameter))
+      (SymbolInformation (:name :kind :location)
+                         (:deprecated :containerName))
+      (DocumentSymbol (:name :range :selectionRange :kind)
+                      ;; `:containerName' isn't really allowed , but
+                      ;; it simplifies the impl of `eglot-imenu'.
+                      (:detail :deprecated :children :containerName))
+      (TextDocumentEdit (:textDocument :edits) ())
+      (TextEdit (:range :newText))
+      (VersionedTextDocumentIdentifier (:uri :version) ())
+      (WorkspaceEdit () (:changes :documentChanges))
+      (WorkspaceSymbol (:name :kind) (:containerName :location :data)))
+    "Alist (INTERFACE-NAME . INTERFACE) of known external LSP interfaces.
+
+INTERFACE-NAME is a symbol designated by the spec as
+\"interface\".  INTERFACE is a list (REQUIRED OPTIONAL) where
+REQUIRED and OPTIONAL are lists of KEYWORD designating field
+names that must be, or may be, respectively, present in a message
+adhering to that interface.  KEY can be a keyword or a cons (SYM
+TYPE), where type is used by `cl-typep' to check types at
+runtime.
+
+Here's what an element of this alist might look like:
+
+    (Command ((:title . string) (:command . string)) (:arguments))"))
+
+(eval-and-compile
+  (defvar eglot-strict-mode
+    '(;; Uncomment next lines for fun and debugging
+      ;; disallow-non-standard-keys
+      ;; enforce-required-keys
+      ;; enforce-optional-keys
+      )
+    "How strictly to check LSP interfaces at compile- and run-time.
+
+Value is a list of symbols (if the list is empty, no checks are
+performed).
+
+If the symbol `disallow-non-standard-keys' is present, an error
+is raised if any extraneous fields are sent by the server.  At
+compile-time, a warning is raised if a destructuring spec
+includes such a field.
+
+If the symbol `enforce-required-keys' is present, an error is
+raised if any required fields are missing from the message sent
+from the server.  At compile-time, a warning is raised if a
+destructuring spec doesn't use such a field.
+
+If the symbol `enforce-optional-keys' is present, nothing special
+happens at run-time.  At compile-time, a warning is raised if a
+destructuring spec doesn't use all optional fields.
+
+If the symbol `disallow-unknown-methods' is present, Eglot warns
+on unknown notifications and errors on unknown requests."))
+
+(cl-defun eglot--check-object (interface-name
+                               object
+                               &optional
+                               (enforce-required t)
+                               (disallow-non-standard t)
+                               (check-types t))
+  "Check that OBJECT conforms to INTERFACE.  Error otherwise."
+  (cl-destructuring-bind
+      (&key types required-keys optional-keys &allow-other-keys)
+      (eglot--interface interface-name)
+    (when-let ((missing (and enforce-required
+                             (cl-set-difference required-keys
+                                                (eglot--plist-keys object)))))
+      (eglot--error "A `%s' must have %s" interface-name missing))
+    (when-let ((excess (and disallow-non-standard
+                            (cl-set-difference
+                             (eglot--plist-keys object)
+                             (append required-keys optional-keys)))))
+      (eglot--error "A `%s' mustn't have %s" interface-name excess))
+    (when check-types
+      (cl-loop
+       for (k v) on object by #'cddr
+       for type = (or (cdr (assoc k types)) t) ;; FIXME: enforce nil type?
+       unless (cl-typep v type)
+       do (eglot--error "A `%s' must have a %s as %s, but has %s"
+                        interface-name )))
+    t))
+
+(eval-and-compile
+  (defun eglot--keywordize-vars (vars)
+    (mapcar (lambda (var) (intern (format ":%s" var))) vars))
+
+  (defun eglot--ensure-type (k) (if (consp k) k (cons k t)))
+
+  (defun eglot--interface (interface-name)
+    (let* ((interface (assoc interface-name eglot--lsp-interface-alist))
+           (required (mapcar #'eglot--ensure-type (car (cdr interface))))
+           (optional (mapcar #'eglot--ensure-type (cadr (cdr interface)))))
+      (list :types (append required optional)
+            :required-keys (mapcar #'car required)
+            :optional-keys (mapcar #'car optional))))
+
+  (defun eglot--check-dspec (interface-name dspec)
+    "Check destructuring spec DSPEC against INTERFACE-NAME."
+    (cl-destructuring-bind (&key required-keys optional-keys &allow-other-keys)
+        (eglot--interface interface-name)
+      (cond ((or required-keys optional-keys)
+             (let ((too-many
+                    (and
+                     (memq 'disallow-non-standard-keys eglot-strict-mode)
+                     (cl-set-difference
+                      (eglot--keywordize-vars dspec)
+                      (append required-keys optional-keys))))
+                   (ignored-required
+                    (and
+                     (memq 'enforce-required-keys eglot-strict-mode)
+                     (cl-set-difference
+                      required-keys (eglot--keywordize-vars dspec))))
+                   (missing-out
+                    (and
+                     (memq 'enforce-optional-keys eglot-strict-mode)
+                     (cl-set-difference
+                      optional-keys (eglot--keywordize-vars dspec)))))
+               (when too-many (byte-compile-warn
+                               "Destructuring for %s has extraneous %s"
+                               interface-name too-many))
+               (when ignored-required (byte-compile-warn
+                                       "Destructuring for %s ignores required 
%s"
+                                       interface-name ignored-required))
+               (when missing-out (byte-compile-warn
+                                  "Destructuring for %s is missing out on %s"
+                                  interface-name missing-out))))
+            (t
+             (byte-compile-warn "Unknown LSP interface %s" interface-name))))))
+
+(cl-defmacro eglot--dbind (vars object &body body)
+  "Destructure OBJECT, binding VARS in BODY.
+VARS is ([(INTERFACE)] SYMS...)
+Honor `eglot-strict-mode'."
+  (declare (indent 2) (debug (sexp sexp &rest form)))
+  (let ((interface-name (if (consp (car vars))
+                            (car (pop vars))))
+        (object-once (make-symbol "object-once"))
+        (fn-once (make-symbol "fn-once")))
+    (cond (interface-name
+           (eglot--check-dspec interface-name vars)
+           `(let ((,object-once ,object))
+              (cl-destructuring-bind (&key ,@vars &allow-other-keys) 
,object-once
+                (eglot--check-object ',interface-name ,object-once
+                                     (memq 'enforce-required-keys 
eglot-strict-mode)
+                                     (memq 'disallow-non-standard-keys 
eglot-strict-mode)
+                                     (memq 'check-types eglot-strict-mode))
+                ,@body)))
+          (t
+           `(let ((,object-once ,object)
+                  (,fn-once (lambda (,@vars) ,@body)))
+              (if (memq 'disallow-non-standard-keys eglot-strict-mode)
+                  (cl-destructuring-bind (&key ,@vars) ,object-once
+                    (funcall ,fn-once ,@vars))
+                (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'."
+  (declare (indent 1) (debug (sexp &rest form)))
+  (let ((e (cl-gensym "jsonrpc-lambda-elem")))
+    `(lambda (,e) (eglot--dbind ,cl-lambda-list ,e ,@body))))
+
+(cl-defmacro eglot--dcase (obj &rest clauses)
+  "Like `pcase', but for the LSP object OBJ.
+CLAUSES is a list (DESTRUCTURE FORMS...) where DESTRUCTURE is
+treated as in `eglot--dbind'."
+  (declare (indent 1) (debug (sexp &rest (sexp &rest form))))
+  (let ((obj-once (make-symbol "obj-once")))
+    `(let ((,obj-once ,obj))
+       (cond
+        ,@(cl-loop
+           for (vars . body) in clauses
+           for vars-as-keywords = (eglot--keywordize-vars vars)
+           for interface-name = (if (consp (car vars))
+                                    (car (pop vars)))
+           for condition =
+           (cond (interface-name
+                  (eglot--check-dspec interface-name vars)
+                  ;; In this mode, in runtime, we assume
+                  ;; `eglot-strict-mode' is partially on, otherwise we
+                  ;; can't disambiguate between certain types.
+                  `(ignore-errors
+                     (eglot--check-object
+                      ',interface-name ,obj-once
+                      t
+                      (memq 'disallow-non-standard-keys eglot-strict-mode)
+                      t)))
+                 (t
+                  ;; In this interface-less mode we don't check
+                  ;; `eglot-strict-mode' at all: just check that the object
+                  ;; has all the keys the user wants to destructure.
+                  `(null (cl-set-difference
+                          ',vars-as-keywords
+                          (eglot--plist-keys ,obj-once)))))
+           collect `(,condition
+                     (cl-destructuring-bind (&key ,@vars &allow-other-keys)
+                         ,obj-once
+                       ,@body)))
+        (t
+         (eglot--error "%S didn't match any of %S"
+                       ,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)))
+    `(let ((,b ,buf)) (if (buffer-live-p ,b) (with-current-buffer ,b 
,@body)))))
+
+(cl-defmacro eglot--when-buffer-window (buf &body body)
+  "Check BUF showing somewhere, then do BODY in it." (declare (indent 1) 
(debug t))
+  (let ((b (cl-gensym)))
+    `(let ((,b ,buf))
+       ;;notice the exception when testing with `ert'
+       (when (or (get-buffer-window ,b) (ert-running-test))
+         (with-current-buffer ,b ,@body)))))
+
+(cl-defmacro eglot--widening (&rest body)
+  "Save excursion and restriction.  Widen.  Then run BODY." (declare (debug t))
+  `(save-excursion (save-restriction (widen) ,@body)))
+
+(cl-defgeneric eglot-handle-request (server method &rest params)
+  "Handle SERVER's METHOD request with PARAMS.")
+
+(cl-defgeneric eglot-handle-notification (server method &rest params)
+  "Handle SERVER's METHOD notification with PARAMS.")
+
+(cl-defgeneric eglot-execute-command (server command arguments)
+  "Ask SERVER to execute COMMAND with ARGUMENTS.")
+
+(cl-defgeneric eglot-initialization-options (server)
+  "JSON object to send under `initializationOptions'."
+  (:method (s)
+   (let ((probe (plist-get (eglot--saved-initargs s) :initializationOptions)))
+     (cond ((functionp probe) (funcall probe s))
+           (probe)
+           (t eglot--{})))))
+
+(cl-defgeneric eglot-register-capability (server method id &rest params)
+  "Ask SERVER to register capability METHOD marked with ID."
+  (:method
+   (_s method _id &rest _params)
+   (eglot--warn "Server tried to register unsupported capability `%s'"
+                method)))
+
+(cl-defgeneric eglot-unregister-capability (server method id &rest params)
+  "Ask SERVER to register capability METHOD marked with ID."
+  (:method
+   (_s method _id &rest _params)
+   (eglot--warn "Server tried to unregister unsupported capability `%s'"
+                method)))
+
+(cl-defgeneric eglot-client-capabilities (server)
+  "What the Eglot LSP client supports for SERVER."
+  (:method (s)
+           (list
+            :workspace (list
+                        :applyEdit t
+                        :executeCommand `(:dynamicRegistration :json-false)
+                        :workspaceEdit `(:documentChanges t)
+                        :didChangeWatchedFiles
+                        `(:dynamicRegistration
+                          ,(if (eglot--trampish-p s) :json-false t))
+                        :symbol `(:dynamicRegistration :json-false)
+                        :configuration t
+                        :workspaceFolders t)
+            :textDocument
+            (list
+             :synchronization (list
+                               :dynamicRegistration :json-false
+                               :willSave t :willSaveWaitUntil t :didSave t)
+             :completion      (list :dynamicRegistration :json-false
+                                    :completionItem
+                                    `(:snippetSupport
+                                      ,(if (eglot--snippet-expansion-fn)
+                                           t
+                                         :json-false)
+                                      :deprecatedSupport t
+                                      :tagSupport (:valueSet [1]))
+                                    :contextSupport t)
+             :hover              (list :dynamicRegistration :json-false
+                                       :contentFormat
+                                       (if (fboundp 'gfm-view-mode)
+                                           ["markdown" "plaintext"]
+                                         ["plaintext"]))
+             :signatureHelp      (list :dynamicRegistration :json-false
+                                       :signatureInformation
+                                       `(:parameterInformation
+                                         (:labelOffsetSupport t)
+                                         :activeParameterSupport t))
+             :references         `(:dynamicRegistration :json-false)
+             :definition         (list :dynamicRegistration :json-false
+                                       :linkSupport t)
+             :declaration        (list :dynamicRegistration :json-false
+                                       :linkSupport t)
+             :implementation     (list :dynamicRegistration :json-false
+                                       :linkSupport t)
+             :typeDefinition     (list :dynamicRegistration :json-false
+                                       :linkSupport t)
+             :documentSymbol     (list
+                                  :dynamicRegistration :json-false
+                                  :hierarchicalDocumentSymbolSupport t
+                                  :symbolKind `(:valueSet
+                                                [,@(mapcar
+                                                    #'car 
eglot--symbol-kind-names)]))
+             :documentHighlight  `(:dynamicRegistration :json-false)
+             :codeAction         (list
+                                  :dynamicRegistration :json-false
+                                  :codeActionLiteralSupport
+                                  '(:codeActionKind
+                                    (:valueSet
+                                     ["quickfix"
+                                      "refactor" "refactor.extract"
+                                      "refactor.inline" "refactor.rewrite"
+                                      "source" "source.organizeImports"]))
+                                  :isPreferredSupport t)
+             :formatting         `(:dynamicRegistration :json-false)
+             :rangeFormatting    `(:dynamicRegistration :json-false)
+             :rename             `(:dynamicRegistration :json-false)
+             :publishDiagnostics (list :relatedInformation :json-false
+                                       ;; TODO: We can support 
:codeDescription after
+                                       ;; adding an appropriate UI to
+                                       ;; Flymake.
+                                       :codeDescriptionSupport :json-false
+                                       :tagSupport
+                                       `(:valueSet
+                                         [,@(mapcar
+                                             #'car eglot--tag-faces)])))
+            :experimental eglot--{})))
+
+(cl-defgeneric eglot-workspace-folders (server)
+  "Return workspaceFolders for SERVER."
+  (let ((project (eglot--project server)))
+    (vconcat
+     (mapcar (lambda (dir)
+               (list :uri (eglot--path-to-uri dir)
+                     :name (abbreviate-file-name dir)))
+             `(,(project-root project) ,@(project-external-roots project))))))
+
+(defclass eglot-lsp-server (jsonrpc-process-connection)
+  ((project-nickname
+    :documentation "Short nickname for the associated project."
+    :accessor eglot--project-nickname
+    :reader eglot-project-nickname)
+   (major-modes
+    :documentation "Major modes server is responsible for in a given project."
+    :accessor eglot--major-modes)
+   (language-id
+    :documentation "Language ID string for the mode."
+    :accessor eglot--language-id)
+   (capabilities
+    :documentation "JSON object containing server capabilities."
+    :accessor eglot--capabilities)
+   (server-info
+    :documentation "JSON object containing server info."
+    :accessor eglot--server-info)
+   (shutdown-requested
+    :documentation "Flag set when server is shutting down."
+    :accessor eglot--shutdown-requested)
+   (project
+    :documentation "Project associated with server."
+    :accessor eglot--project)
+   (spinner
+    :documentation "List (ID DOING-WHAT DONE-P) representing server progress."
+    :initform `(nil nil t) :accessor eglot--spinner)
+   (inhibit-autoreconnect
+    :initform t
+    :documentation "Generalized boolean inhibiting auto-reconnection if true."
+    :accessor eglot--inhibit-autoreconnect)
+   (file-watches
+    :documentation "Map ID to list of WATCHES for `didChangeWatchedFiles'."
+    :initform (make-hash-table :test #'equal) :accessor eglot--file-watches)
+   (managed-buffers
+    :documentation "List of buffers managed by server."
+    :accessor eglot--managed-buffers)
+   (saved-initargs
+    :documentation "Saved initargs for reconnection purposes."
+    :accessor eglot--saved-initargs)
+   (inferior-process
+    :documentation "Server subprocess started automatically."
+    :accessor eglot--inferior-process))
+  :documentation
+  "Represents a server. Wraps a process for LSP communication.")
+
+(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.")
+
+(defun eglot-shutdown (server &optional _interactive timeout preserve-buffers)
+  "Politely ask SERVER to quit.
+Interactively, read SERVER from the minibuffer unless there is
+only one and it's managing the current buffer.
+
+Forcefully quit it if it doesn't respond within TIMEOUT seconds.
+TIMEOUT defaults to 1.5 seconds.  Don't leave this function with
+the server still running.
+
+If PRESERVE-BUFFERS is non-nil (interactively, when called with a
+prefix argument), do not kill events and output buffers of
+SERVER."
+  (interactive (list (eglot--read-server "Shutdown which server"
+                                         (eglot-current-server))
+                     t nil current-prefix-arg))
+  (eglot--message "Asking %s politely to terminate" (jsonrpc-name server))
+  (unwind-protect
+      (progn
+        (setf (eglot--shutdown-requested server) t)
+        (jsonrpc-request server :shutdown nil :timeout (or timeout 1.5))
+        (jsonrpc-notify server :exit nil))
+    ;; Now ask jsonrpc.el to shut down the server.
+    (jsonrpc-shutdown server (not preserve-buffers))
+    (unless preserve-buffers (kill-buffer (jsonrpc-events-buffer server)))))
+
+(defun eglot-shutdown-all (&optional preserve-buffers)
+  "Politely ask all language servers to quit, in order.
+PRESERVE-BUFFERS as in `eglot-shutdown', which see."
+  (interactive (list current-prefix-arg))
+  (cl-loop for ss being the hash-values of eglot--servers-by-project
+           do (with-demoted-errors "[eglot] shutdown all: %s"
+                (cl-loop for s in ss do (eglot-shutdown s nil nil 
preserve-buffers)))))
+
+(defun eglot--on-shutdown (server)
+  "Called by jsonrpc.el when SERVER is already dead."
+  ;; Turn off `eglot--managed-mode' where appropriate.
+  (dolist (buffer (eglot--managed-buffers server))
+    (let (;; Avoid duplicate shutdowns (github#389)
+          (eglot-autoshutdown nil))
+      (eglot--when-live-buffer buffer (eglot--managed-mode-off))))
+  ;; Kill any expensive watches
+  (maphash (lambda (_id watches)
+             (mapcar #'file-notify-rm-watch watches))
+           (eglot--file-watches server))
+  ;; Kill any autostarted inferior processes
+  (when-let (proc (eglot--inferior-process server))
+    (delete-process proc))
+  ;; Sever the project/server relationship for `server'
+  (setf (gethash (eglot--project server) eglot--servers-by-project)
+        (delq server
+              (gethash (eglot--project server) eglot--servers-by-project)))
+  (cond ((eglot--shutdown-requested server)
+         t)
+        ((not (eglot--inhibit-autoreconnect server))
+         (eglot--warn "Reconnecting after unexpected server exit.")
+         (eglot-reconnect server))
+        ((timerp (eglot--inhibit-autoreconnect server))
+         (eglot--warn "Not auto-reconnecting, last one didn't last long."))))
+
+(defun eglot--all-major-modes ()
+  "Return all known major modes."
+  (let ((retval))
+    (mapatoms (lambda (sym)
+                (when (plist-member (symbol-plist sym) 'derived-mode-parent)
+                  (push sym retval))))
+    retval))
+
+(defvar eglot--command-history nil
+  "History of CONTACT arguments to `eglot'.")
+
+(defun eglot--lookup-mode (mode)
+  "Lookup `eglot-server-programs' for MODE.
+Return (MANAGED-MODES LANGUAGE-ID CONTACT-PROXY).
+
+MANAGED-MODES is a list with MODE as its first elements.
+Subsequent elements are other major modes also potentially
+managed by the server that is to manage MODE.
+
+If not specified in `eglot-server-programs' (which see),
+LANGUAGE-ID is determined from MODE's name.
+
+CONTACT-PROXY is the value of the corresponding
+`eglot-server-programs' entry."
+  (cl-loop
+   for (modes . contact) in eglot-server-programs
+   for mode-symbols = (cons mode
+                            (delete mode
+                                    (mapcar #'car
+                                            (mapcar #'eglot--ensure-list
+                                                    (eglot--ensure-list 
modes)))))
+   thereis (cl-some
+            (lambda (spec)
+              (cl-destructuring-bind (probe &key language-id &allow-other-keys)
+                  (eglot--ensure-list spec)
+                (and (provided-mode-derived-p mode probe)
+                     (list
+                      mode-symbols
+                      (or language-id
+                          (or (get mode 'eglot-language-id)
+                              (get spec 'eglot-language-id)
+                              (string-remove-suffix "-mode" (symbol-name 
mode))))
+                      contact))))
+            (if (or (symbolp modes) (keywordp (cadr modes)))
+                (list modes) modes))))
+
+(defun eglot--guess-contact (&optional interactive)
+  "Helper for `eglot'.
+Return (MANAGED-MODE PROJECT CLASS CONTACT LANG-ID).  If INTERACTIVE is
+non-nil, maybe prompt user, else error as soon as something can't
+be guessed."
+  (let* ((guessed-mode (if buffer-file-name major-mode))
+         (main-mode
+          (cond
+           ((and interactive
+                 (or (>= (prefix-numeric-value current-prefix-arg) 16)
+                     (not guessed-mode)))
+            (intern
+             (completing-read
+              "[eglot] Start a server to manage buffers of what major mode? "
+              (mapcar #'symbol-name (eglot--all-major-modes)) nil t
+              (symbol-name guessed-mode) nil (symbol-name guessed-mode) nil)))
+           ((not guessed-mode)
+            (eglot--error "Can't guess mode to manage for `%s'" 
(current-buffer)))
+           (t guessed-mode)))
+         (triplet (eglot--lookup-mode main-mode))
+         (managed-modes (car triplet))
+         (language-id (or (cadr triplet)
+                          (string-remove-suffix "-mode" (symbol-name 
guessed-mode))))
+         (guess (caddr triplet))
+         (guess (if (functionp guess)
+                    (funcall guess interactive)
+                  guess))
+         (class (or (and (consp guess) (symbolp (car guess))
+                         (prog1 (unless current-prefix-arg (car guess))
+                           (setq guess (cdr guess))))
+                    'eglot-lsp-server))
+         (program (and (listp guess)
+                       (stringp (car guess))
+                       ;; A second element might be the port of a (host, port)
+                       ;; pair, but in that case it is not a string.
+                       (or (null (cdr guess)) (stringp (cadr guess)))
+                       (car guess)))
+         (base-prompt
+          (and interactive
+               "Enter program to execute (or <host>:<port>): "))
+         (full-program-invocation
+          (and program
+               (cl-every #'stringp guess)
+               (combine-and-quote-strings guess)))
+         (prompt
+          (and base-prompt
+               (cond (current-prefix-arg base-prompt)
+                     ((null guess)
+                      (format "[eglot] Sorry, couldn't guess for `%s'!\n%s"
+                              main-mode base-prompt))
+                     ((and program
+                           (not (file-name-absolute-p program))
+                           (not (eglot--executable-find program t)))
+                      (if full-program-invocation
+                          (concat (format "[eglot] I guess you want to run 
`%s'"
+                                          full-program-invocation)
+                                  (format ", but I can't find `%s' in PATH!"
+                                          program)
+                                  "\n" base-prompt)
+                        (eglot--error
+                         (concat "`%s' not found in PATH, but can't form"
+                                 " an interactive prompt for to fix %s!")
+                         program guess))))))
+         (contact
+          (or (and prompt
+                   (split-string-and-unquote
+                    (read-shell-command
+                     prompt
+                     full-program-invocation
+                     'eglot-command-history)))
+              guess)))
+    (list managed-modes (eglot--current-project) class contact language-id)))
+
+(defvar eglot-lsp-context)
+(put 'eglot-lsp-context 'variable-documentation
+     "Dynamically non-nil when searching for projects in LSP context.")
+
+(defvar eglot--servers-by-xrefed-file
+  (make-hash-table :test 'equal :weakness 'value))
+
+(defun eglot--current-project ()
+  "Return a project object for Eglot's LSP purposes.
+This relies on `project-current' and thus on
+`project-find-functions'.  Functions in the latter
+variable (which see) can query the value `eglot-lsp-context' to
+decide whether a given directory is a project containing a
+suitable root directory for a given LSP server's purposes."
+  (let ((eglot-lsp-context t))
+    (or (project-current) `(transient . ,default-directory))))
+
+;;;###autoload
+(defun eglot (managed-major-mode project class contact language-id
+                                 &optional interactive)
+  "Start LSP server in support of PROJECT's buffers under MANAGED-MAJOR-MODE.
+
+This starts a Language Server Protocol (LSP) server suitable for the
+buffers of PROJECT whose `major-mode' is MANAGED-MAJOR-MODE.
+CLASS is the class of the LSP server to start and CONTACT specifies
+how to connect to the server.
+
+Interactively, the command attempts to guess MANAGED-MAJOR-MODE
+from the current buffer's `major-mode', CLASS and CONTACT from
+`eglot-server-programs' looked up by the major mode, and PROJECT from
+`project-find-functions'.  The search for active projects in this
+context binds `eglot-lsp-context' (which see).
+
+If it can't guess, it prompts the user for the mode and the server.
+With a single \\[universal-argument] prefix arg, it always prompts for COMMAND.
+With two \\[universal-argument], it also always prompts for MANAGED-MAJOR-MODE.
+
+The LSP server of CLASS is started (or contacted) via CONTACT.
+If this operation is successful, current *and future* file
+buffers of MANAGED-MAJOR-MODE inside PROJECT become \"managed\"
+by the LSP server, meaning the information about their contents is
+exchanged periodically with the server to provide enhanced
+code-analysis via `xref-find-definitions', `flymake-mode',
+`eldoc-mode', and `completion-at-point', among others.
+
+PROJECT is a project object as returned by `project-current'.
+
+CLASS is a subclass of `eglot-lsp-server'.
+
+CONTACT specifies how to contact the server.  It is a
+keyword-value plist used to initialize CLASS or a plain list as
+described in `eglot-server-programs', which see.
+
+LANGUAGE-ID is the language ID string to send to the server for
+MANAGED-MAJOR-MODE, which matters to a minority of servers.
+
+INTERACTIVE is t if called interactively."
+  (interactive (append (eglot--guess-contact t) '(t)))
+  (setq managed-major-mode (eglot--ensure-list managed-major-mode))
+  (let* ((current-server (eglot-current-server))
+         (live-p (and current-server (jsonrpc-running-p current-server))))
+    (if (and live-p
+             interactive
+             (y-or-n-p "[eglot] Live process found, reconnect instead? "))
+        (eglot-reconnect current-server interactive)
+      (when live-p (ignore-errors (eglot-shutdown current-server)))
+      (eglot--connect managed-major-mode project class contact language-id))))
+
+(defun eglot-reconnect (server &optional interactive)
+  "Reconnect to SERVER.
+INTERACTIVE is t if called interactively."
+  (interactive (list (eglot--current-server-or-lose) t))
+  (when (jsonrpc-running-p server)
+    (ignore-errors (eglot-shutdown server interactive nil 'preserve-buffers)))
+  (eglot--connect (eglot--major-modes server)
+                  (eglot--project server)
+                  (eieio-object-class-name server)
+                  (eglot--saved-initargs server)
+                  (eglot--language-id server))
+  (eglot--message "Reconnected!"))
+
+(defvar eglot--managed-mode) ; forward decl
+
+;;;###autoload
+(defun eglot-ensure ()
+  "Start Eglot session for current buffer if there isn't one."
+  (let ((buffer (current-buffer)))
+    (cl-labels
+        ((maybe-connect
+          ()
+          (remove-hook 'post-command-hook #'maybe-connect nil)
+          (eglot--when-live-buffer buffer
+            (unless eglot--managed-mode
+              (apply #'eglot--connect (eglot--guess-contact))))))
+      (when buffer-file-name
+        (add-hook 'post-command-hook #'maybe-connect 'append nil)))))
+
+(defun eglot-events-buffer (server)
+  "Display events buffer for SERVER.
+Use current server's or first available Eglot events buffer."
+  (interactive (list (eglot-current-server)))
+  (let ((buffer (if server (jsonrpc-events-buffer server)
+                  (cl-find "\\*EGLOT.*events\\*"
+                           (buffer-list)
+                           :key #'buffer-name :test #'string-match))))
+    (if buffer (display-buffer buffer)
+      (eglot--error "Can't find an Eglot events buffer!"))))
+
+(defun eglot-stderr-buffer (server)
+  "Display stderr buffer for SERVER."
+  (interactive (list (eglot--current-server-or-lose)))
+  (display-buffer (jsonrpc-stderr-buffer server)))
+
+(defun eglot-forget-pending-continuations (server)
+  "Forget pending requests for SERVER."
+  (interactive (list (eglot--current-server-or-lose)))
+  (jsonrpc-forget-pending-continuations server))
+
+(defvar eglot-connect-hook
+  '(eglot-signal-didChangeConfiguration)
+  "Hook run after connecting in `eglot--connect'.")
+
+(defvar eglot-server-initialized-hook
+  '()
+  "Hook run after a `eglot-lsp-server' instance is created.
+
+That is before a connection was established.  Use
+`eglot-connect-hook' to hook into when a connection was
+successfully established and the server on the other side has
+received the initializing configuration.
+
+Each function is passed the server as an argument")
+
+(defun eglot--cmd (contact)
+  "Helper for `eglot--connect'."
+  (if (file-remote-p default-directory)
+      ;; TODO: this seems like a bug, although it’s everywhere. For
+      ;; some reason, for remote connections only, over a pipe, we
+      ;; need to turn off line buffering on the tty.
+      ;;
+      ;; Not only does this seem like there should be a better way,
+      ;; but it almost certainly doesn’t work on non-unix systems.
+      (list "sh" "-c"
+            (string-join (cons "stty raw > /dev/null;"
+                               (mapcar #'shell-quote-argument contact))
+             " "))
+    contact))
+
+(defvar-local eglot--cached-server nil
+  "A cached reference to the current Eglot server.")
+
+(defun eglot--connect (managed-modes project class contact language-id)
+  "Connect to MANAGED-MODES, LANGUAGE-ID, PROJECT, CLASS and CONTACT.
+This docstring appeases checkdoc, that's all."
+  (let* ((default-directory (project-root project))
+         (nickname (file-name-base (directory-file-name default-directory)))
+         (readable-name (format "EGLOT (%s/%s)" nickname managed-modes))
+         autostart-inferior-process
+         server-info
+         (contact (if (functionp contact) (funcall contact) contact))
+         (initargs
+          (cond ((keywordp (car contact)) contact)
+                ((integerp (cadr contact))
+                 (setq server-info (list (format "%s:%s" (car contact)
+                                                 (cadr contact))))
+                 `(:process ,(lambda ()
+                               (apply #'open-network-stream
+                                      readable-name nil
+                                      (car contact) (cadr contact)
+                                      (cddr contact)))))
+                ((and (stringp (car contact)) (memq :autoport contact))
+                 (setq server-info (list "<inferior process>"))
+                 `(:process ,(lambda ()
+                               (pcase-let ((`(,connection . ,inferior)
+                                            (eglot--inferior-bootstrap
+                                             readable-name
+                                             contact
+                                             '(:noquery t))))
+                                 (setq autostart-inferior-process inferior)
+                                 connection))))
+                ((stringp (car contact))
+                 (let* ((probe (cl-position-if #'keywordp contact))
+                        (more-initargs (and probe (cl-subseq contact probe)))
+                        (contact (cl-subseq contact 0 probe)))
+                   `(:process
+                     ,(lambda ()
+                        (let ((default-directory default-directory))
+                          (make-process
+                           :name readable-name
+                           :command (setq server-info (eglot--cmd contact))
+                           :connection-type 'pipe
+                           :coding 'utf-8-emacs-unix
+                           :noquery t
+                           :stderr (get-buffer-create
+                                    (format "*%s stderr*" readable-name))
+                           :file-handler t)))
+                     ,@more-initargs)))))
+         (spread (lambda (fn) (lambda (server method params)
+                                (let ((eglot--cached-server server))
+                                 (apply fn server method (append params 
nil))))))
+         (server
+          (apply
+           #'make-instance class
+           :name readable-name
+           :events-buffer-scrollback-size eglot-events-buffer-size
+           :notification-dispatcher (funcall spread 
#'eglot-handle-notification)
+           :request-dispatcher (funcall spread #'eglot-handle-request)
+           :on-shutdown #'eglot--on-shutdown
+           initargs))
+         (cancelled nil)
+         (tag (make-symbol "connected-catch-tag")))
+    (when server-info
+      (jsonrpc--debug server "Running language server: %s"
+                      (string-join server-info " ")))
+    (setf (eglot--saved-initargs server) initargs)
+    (setf (eglot--project server) project)
+    (setf (eglot--project-nickname server) nickname)
+    (setf (eglot--major-modes server) (eglot--ensure-list managed-modes))
+    (setf (eglot--language-id server) language-id)
+    (setf (eglot--inferior-process server) autostart-inferior-process)
+    (run-hook-with-args 'eglot-server-initialized-hook server)
+    ;; Now start the handshake.  To honor `eglot-sync-connect'
+    ;; maybe-sync-maybe-async semantics we use `jsonrpc-async-request'
+    ;; and mimic most of `jsonrpc-request'.
+    (unwind-protect
+        (condition-case _quit
+            (let ((retval
+                   (catch tag
+                     (jsonrpc-async-request
+                      server
+                      :initialize
+                      (list :processId
+                            (unless (or eglot-withhold-process-id
+                                        (file-remote-p default-directory)
+                                        (eq (jsonrpc-process-type server)
+                                            'network))
+                              (emacs-pid))
+                            ;; Maybe turn trampy `/ssh:foo@bar:/path/to/baz.py'
+                            ;; 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)
+                            :initializationOptions 
(eglot-initialization-options
+                                                    server)
+                            :capabilities (eglot-client-capabilities server)
+                            :workspaceFolders (eglot-workspace-folders server))
+                      :success-fn
+                      (eglot--lambda ((InitializeResult) capabilities 
serverInfo)
+                        (unless cancelled
+                          (push server
+                                (gethash project eglot--servers-by-project))
+                          (setf (eglot--capabilities server) capabilities)
+                          (setf (eglot--server-info server) serverInfo)
+                          (jsonrpc-notify server :initialized eglot--{})
+                          (dolist (buffer (buffer-list))
+                            (with-current-buffer buffer
+                              ;; No need to pass SERVER as an argument: it has
+                              ;; been registered in 
`eglot--servers-by-project',
+                              ;; so that it can be found (and cached) from
+                              ;; `eglot--maybe-activate-editing-mode' in any
+                              ;; managed buffer.
+                              (eglot--maybe-activate-editing-mode)))
+                          (setf (eglot--inhibit-autoreconnect server)
+                                (cond
+                                 ((booleanp eglot-autoreconnect)
+                                  (not eglot-autoreconnect))
+                                 ((cl-plusp eglot-autoreconnect)
+                                  (run-with-timer
+                                   eglot-autoreconnect nil
+                                   (lambda ()
+                                     (setf (eglot--inhibit-autoreconnect 
server)
+                                           (null eglot-autoreconnect)))))))
+                          (let ((default-directory (project-root project))
+                                (major-mode (car managed-modes)))
+                            (hack-dir-local-variables-non-file-buffer)
+                            (run-hook-with-args 'eglot-connect-hook server))
+                          (eglot--message
+                           "Connected! Server `%s' now managing `%s' buffers \
+in project `%s'."
+                           (or (plist-get serverInfo :name)
+                               (jsonrpc-name server))
+                           managed-modes
+                           (eglot-project-nickname server))
+                          (when tag (throw tag t))))
+                      :timeout eglot-connect-timeout
+                      :error-fn (eglot--lambda ((ResponseError) code message)
+                                  (unless cancelled
+                                    (jsonrpc-shutdown server)
+                                    (let ((msg (format "%s: %s" code message)))
+                                      (if tag (throw tag `(error . ,msg))
+                                        (eglot--error msg)))))
+                      :timeout-fn (lambda ()
+                                    (unless cancelled
+                                      (jsonrpc-shutdown server)
+                                      (let ((msg (format "Timed out after %s 
seconds"
+                                                         
eglot-connect-timeout)))
+                                        (if tag (throw tag `(error . ,msg))
+                                          (eglot--error msg))))))
+                     (cond ((numberp eglot-sync-connect)
+                            (accept-process-output nil eglot-sync-connect))
+                           (eglot-sync-connect
+                            (while t (accept-process-output
+                                      nil eglot-connect-timeout)))))))
+              (pcase retval
+                (`(error . ,msg) (eglot--error msg))
+                (`nil (eglot--message "Waiting in background for server `%s'"
+                                      (jsonrpc-name server))
+                      nil)
+                (_ server)))
+          (quit (jsonrpc-shutdown server) (setq cancelled 'quit)))
+      (setq tag nil))))
+
+(defun eglot--inferior-bootstrap (name contact &optional connect-args)
+  "Use CONTACT to start a server, then connect to it.
+Return a cons of two process objects (CONNECTION . INFERIOR).
+Name both based on NAME.
+CONNECT-ARGS are passed as additional arguments to
+`open-network-stream'."
+  (let* ((port-probe (make-network-process :name "eglot-port-probe-dummy"
+                                           :server t
+                                           :host "localhost"
+                                           :service 0))
+         (port-number (unwind-protect
+                          (process-contact port-probe :service)
+                        (delete-process port-probe)))
+         inferior connection)
+    (unwind-protect
+        (progn
+          (setq inferior
+                (make-process
+                 :name (format "autostart-inferior-%s" name)
+                 :stderr (format "*%s stderr*" name)
+                 :noquery t
+                 :command (cl-subst
+                           (format "%s" port-number) :autoport contact)))
+          (setq connection
+                (cl-loop
+                 repeat 10 for i from 1
+                 do (accept-process-output nil 0.5)
+                 while (process-live-p inferior)
+                 do (eglot--message
+                     "Trying to connect to localhost and port %s (attempt %s)"
+                     port-number i)
+                 thereis (ignore-errors
+                           (apply #'open-network-stream
+                                  (format "autoconnect-%s" name)
+                                  nil
+                                  "localhost" port-number connect-args))))
+          (cons connection inferior))
+      (cond ((and (process-live-p connection)
+                  (process-live-p inferior))
+             (eglot--message "Done, connected to %s!" port-number))
+            (t
+             (when inferior (delete-process inferior))
+             (when connection (delete-process connection))
+             (eglot--error "Could not start and connect to server%s"
+                           (if inferior
+                               (format " started with %s"
+                                       (process-command inferior))
+                             "!")))))))
+
+
+;;; Helpers (move these to API?)
+;;;
+(defun eglot--error (format &rest args)
+  "Error out with FORMAT with ARGS."
+  (error "[eglot] %s" (apply #'format format args)))
+
+(defun eglot--message (format &rest args)
+  "Message out with FORMAT with ARGS."
+  (message "[eglot] %s" (apply #'format format args)))
+
+(defun eglot--warn (format &rest args)
+  "Warning message with FORMAT and ARGS."
+  (apply #'eglot--message (concat "(warning) " format) args)
+  (let ((warning-minimum-level :error))
+    (display-warning 'eglot (apply #'format format args) :warning)))
+
+(defun eglot-current-column () (- (point) (line-beginning-position)))
+
+(defvar eglot-current-column-function #'eglot-lsp-abiding-column
+  "Function to calculate the current column.
+
+This is the inverse operation of
+`eglot-move-to-column-function' (which see).  It is a function of
+no arguments returning a column number.  For buffers managed by
+fully LSP-compliant servers, this should be set to
+`eglot-lsp-abiding-column' (the default), and
+`eglot-current-column' for all others.")
+
+(defun eglot-lsp-abiding-column (&optional lbp)
+  "Calculate current COLUMN as defined by the LSP spec.
+LBP defaults to `line-beginning-position'."
+  (/ (- (length (encode-coding-region (or lbp (line-beginning-position))
+                                      ;; Fix github#860
+                                      (min (point) (point-max)) 'utf-16 t))
+        2)
+     2))
+
+(defun eglot--pos-to-lsp-position (&optional pos)
+  "Convert point POS to LSP position."
+  (eglot--widening
+   ;; LSP line is zero-origin; emacs is one-origin.
+   (list :line (1- (line-number-at-pos pos t))
+         :character (progn (when pos (goto-char pos))
+                           (funcall eglot-current-column-function)))))
+
+(defvar eglot-move-to-column-function #'eglot-move-to-lsp-abiding-column
+  "Function to move to a column reported by the LSP server.
+
+According to the standard, LSP column/character offsets are based
+on a count of UTF-16 code units, not actual visual columns.  So
+when LSP says position 3 of a line containing just \"aXbc\",
+where X is a multi-byte character, it actually means `b', not
+`c'. However, many servers don't follow the spec this closely.
+
+For buffers managed by fully LSP-compliant servers, this should
+be set to `eglot-move-to-lsp-abiding-column' (the default), and
+`eglot-move-to-column' for all others.")
+
+(defun eglot-move-to-column (column)
+  "Move to COLUMN without closely following the LSP spec."
+  ;; We cannot use `move-to-column' here, because it moves to *visual*
+  ;; columns, which can be different from LSP columns in case of
+  ;; `whitespace-mode', `prettify-symbols-mode', etc.  (github#296,
+  ;; github#297)
+  (goto-char (min (+ (line-beginning-position) column)
+                  (line-end-position))))
+
+(defun eglot-move-to-lsp-abiding-column (column)
+  "Move to COLUMN abiding by the LSP spec."
+  (save-restriction
+    (cl-loop
+     with lbp = (line-beginning-position)
+     initially
+     (narrow-to-region lbp (line-end-position))
+     (move-to-column column)
+     for diff = (- column
+                   (eglot-lsp-abiding-column lbp))
+     until (zerop diff)
+     do (condition-case eob-err
+            (forward-char (/ (if (> diff 0) (1+ diff) (1- diff)) 2))
+          (end-of-buffer (cl-return eob-err))))))
+
+(defun eglot--lsp-position-to-point (pos-plist &optional marker)
+  "Convert LSP position POS-PLIST to Emacs point.
+If optional MARKER, return a marker instead"
+  (save-excursion
+    (save-restriction
+      (widen)
+      (goto-char (point-min))
+      (forward-line (min most-positive-fixnum
+                         (plist-get pos-plist :line)))
+      (unless (eobp) ;; if line was excessive leave point at eob
+        (let ((tab-width 1)
+              (col (plist-get pos-plist :character)))
+          (unless (wholenump col)
+            (eglot--warn
+             "Caution: LSP server sent invalid character position %s. Using 0 
instead."
+             col)
+            (setq col 0))
+          (funcall eglot-move-to-column-function col)))
+      (if marker (copy-marker (point-marker)) (point)))))
+
+(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 (url-type (url-generic-parse-url 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.
+        truepath
+      (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--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)))
+                               (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."
+  (and (boundp 'yas-minor-mode)
+       (symbol-value 'yas-minor-mode)
+       'yas-expand-snippet))
+
+(defun eglot--format-markup (markup)
+  "Format MARKUP according to LSP's spec."
+  (pcase-let ((`(,string ,mode)
+               (if (stringp markup) (list markup 'gfm-view-mode)
+                 (list (plist-get markup :value)
+                       (pcase (plist-get markup :kind)
+                         ("markdown" 'gfm-view-mode)
+                         ("plaintext" 'text-mode)
+                         (_ major-mode))))))
+    (with-temp-buffer
+      (setq-local markdown-fontify-code-blocks-natively t)
+      (insert string)
+      (let ((inhibit-message t)
+           (message-log-max nil))
+        (ignore-errors (delay-mode-hooks (funcall mode))))
+      (font-lock-ensure)
+      (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" :implementationProvider)
+          (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)))
+
+(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--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
+and just return it.  PROMPT shouldn't end with a question mark."
+  (let ((servers (cl-loop for servers
+                          being hash-values of eglot--servers-by-project
+                          append servers))
+        (name (lambda (srv)
+                (format "%s %s" (eglot-project-nickname srv)
+                        (eglot--major-modes srv)))))
+    (cond ((null servers)
+           (eglot--error "No servers!"))
+          ((or (cdr servers) (not dont-if-just-the-one))
+           (let* ((default (when-let ((current (eglot-current-server)))
+                             (funcall name current)))
+                  (read (completing-read
+                         (if default
+                             (format "%s (default %s)? " prompt default)
+                           (concat prompt "? "))
+                         (mapcar name servers)
+                         nil t
+                         nil nil
+                         default)))
+             (cl-find read servers :key name :test #'equal)))
+          (t (car servers)))))
+
+(defun eglot--trampish-p (server)
+  "Tell if SERVER's project root is `file-remote-p'."
+  (file-remote-p (project-root (eglot--project server))))
+
+(defun eglot--plist-keys (plist) "Get keys of a plist."
+  (cl-loop for (k _v) on plist by #'cddr collect k))
+
+(defun eglot--ensure-list (x) (if (listp x) x (list x)))
+(when (fboundp 'ensure-list)            ; Emacs 28 or later
+  (define-obsolete-function-alias 'eglot--ensure-list #'ensure-list "29.1"))
+
+
+;;; Minor modes
+;;;
+(defvar eglot-mode-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map [remap display-local-help] #'eldoc-doc-buffer)
+    map))
+
+(defvar-local eglot--current-flymake-report-fn nil
+  "Current flymake report function for this buffer.")
+
+(defvar-local eglot--saved-bindings nil
+  "Bindings saved by `eglot--setq-saving'.")
+
+(defvar eglot-stay-out-of '()
+  "List of Emacs things that Eglot should try to stay of.
+Each element is a string, a symbol, or a regexp which is matched
+against a variable's name.  Examples include the string
+\"company\" or the symbol `xref'.
+
+Before Eglot starts \"managing\" a particular buffer, it
+opinionatedly sets some peripheral Emacs facilities, such as
+Flymake, Xref and Company.  These overriding settings help ensure
+consistent Eglot behavior and only stay in place until
+\"managing\" stops (usually via `eglot-shutdown'), whereupon the
+previous settings are restored.
+
+However, if you wish for Eglot to stay out of a particular Emacs
+facility that you'd like to keep control of add an element to
+this list and Eglot will refrain from setting it.
+
+For example, to keep your Company customization, add the symbol
+`company' to this variable.")
+
+(defun eglot--stay-out-of-p (symbol)
+  "Tell if Eglot should stay of of SYMBOL."
+  (cl-find (symbol-name symbol) eglot-stay-out-of
+           :test (lambda (s thing)
+                   (let ((re (if (symbolp thing) (symbol-name thing) thing)))
+                     (string-match re s)))))
+
+(defmacro eglot--setq-saving (symbol binding)
+  `(unless (or (not (boundp ',symbol)) (eglot--stay-out-of-p ',symbol))
+     (push (cons ',symbol (symbol-value ',symbol)) eglot--saved-bindings)
+     (setq-local ,symbol ,binding)))
+
+(defun eglot-managed-p ()
+  "Tell if current buffer is managed by Eglot."
+  eglot--managed-mode)
+
+(defvar eglot-managed-mode-hook nil
+  "A hook run by Eglot after it started/stopped managing a buffer.
+Use `eglot-managed-p' to determine if current buffer is managed.")
+
+(define-minor-mode eglot--managed-mode
+  "Mode for source buffers managed by some Eglot project."
+  :init-value nil :lighter nil :keymap eglot-mode-map
+  (cond
+   (eglot--managed-mode
+    (add-hook 'after-change-functions 'eglot--after-change nil t)
+    (add-hook 'before-change-functions 'eglot--before-change nil t)
+    (add-hook 'kill-buffer-hook #'eglot--managed-mode-off nil t)
+    ;; Prepend "didClose" to the hook after the "nonoff", so it will run first
+    (add-hook 'kill-buffer-hook 'eglot--signal-textDocument/didClose nil t)
+    (add-hook 'before-revert-hook 'eglot--signal-textDocument/didClose nil t)
+    (add-hook 'after-revert-hook 'eglot--after-revert-hook nil t)
+    (add-hook 'before-save-hook 'eglot--signal-textDocument/willSave nil t)
+    (add-hook 'after-save-hook 'eglot--signal-textDocument/didSave nil t)
+    (unless (eglot--stay-out-of-p 'xref)
+      (add-hook 'xref-backend-functions 'eglot-xref-backend nil t))
+    (add-hook 'completion-at-point-functions #'eglot-completion-at-point nil t)
+    (add-hook 'change-major-mode-hook #'eglot--managed-mode-off nil t)
+    (add-hook 'post-self-insert-hook 'eglot--post-self-insert-hook nil t)
+    (add-hook 'pre-command-hook 'eglot--pre-command-hook nil t)
+    (eglot--setq-saving eldoc-documentation-functions
+                        '(eglot-signature-eldoc-function
+                          eglot-hover-eldoc-function))
+    (eglot--setq-saving eldoc-documentation-strategy
+                        #'eldoc-documentation-enthusiast)
+    (eglot--setq-saving xref-prompt-for-identifier nil)
+    (eglot--setq-saving flymake-diagnostic-functions '(eglot-flymake-backend))
+    (eglot--setq-saving company-backends '(company-capf))
+    (eglot--setq-saving company-tooltip-align-annotations t)
+    (unless (eglot--stay-out-of-p 'imenu)
+      (add-function :before-until (local 'imenu-create-index-function)
+                    #'eglot-imenu))
+    (unless (eglot--stay-out-of-p 'flymake) (flymake-mode 1))
+    (unless (eglot--stay-out-of-p 'eldoc) (eldoc-mode 1))
+    (cl-pushnew (current-buffer) (eglot--managed-buffers 
(eglot-current-server))))
+   (t
+    (remove-hook 'after-change-functions 'eglot--after-change t)
+    (remove-hook 'before-change-functions 'eglot--before-change t)
+    (remove-hook 'kill-buffer-hook #'eglot--managed-mode-off t)
+    (remove-hook 'kill-buffer-hook 'eglot--signal-textDocument/didClose t)
+    (remove-hook 'before-revert-hook 'eglot--signal-textDocument/didClose t)
+    (remove-hook 'after-revert-hook 'eglot--after-revert-hook t)
+    (remove-hook 'before-save-hook 'eglot--signal-textDocument/willSave t)
+    (remove-hook 'after-save-hook 'eglot--signal-textDocument/didSave t)
+    (remove-hook 'xref-backend-functions 'eglot-xref-backend t)
+    (remove-hook 'completion-at-point-functions #'eglot-completion-at-point t)
+    (remove-hook 'change-major-mode-hook #'eglot--managed-mode-off t)
+    (remove-hook 'post-self-insert-hook 'eglot--post-self-insert-hook t)
+    (remove-hook 'pre-command-hook 'eglot--pre-command-hook t)
+    (cl-loop for (var . saved-binding) in eglot--saved-bindings
+             do (set (make-local-variable var) saved-binding))
+    (remove-function (local 'imenu-create-index-function) #'eglot-imenu)
+    (when eglot--current-flymake-report-fn
+      (eglot--report-to-flymake nil)
+      (setq eglot--current-flymake-report-fn nil))
+    (let ((server eglot--cached-server))
+      (setq eglot--cached-server nil)
+      (when server
+        (setf (eglot--managed-buffers server)
+              (delq (current-buffer) (eglot--managed-buffers server)))
+        (when (and eglot-autoshutdown
+                   (null (eglot--managed-buffers server)))
+          (eglot-shutdown server))))))
+  ;; Note: the public hook runs before the internal eglot--managed-mode-hook.
+  (run-hooks 'eglot-managed-mode-hook))
+
+(defun eglot--managed-mode-off ()
+  "Turn off `eglot--managed-mode' unconditionally."
+  (eglot--managed-mode -1))
+
+(defun eglot-current-server ()
+  "Return logical Eglot server for current buffer, nil if none."
+  (setq eglot--cached-server
+        (or eglot--cached-server
+            (cl-find major-mode
+                     (gethash (eglot--current-project) 
eglot--servers-by-project)
+                     :key #'eglot--major-modes
+                     :test #'memq)
+            (and eglot-extend-to-xref
+                 buffer-file-name
+                 (gethash (expand-file-name buffer-file-name)
+                          eglot--servers-by-xrefed-file)))))
+
+(defun eglot--current-server-or-lose ()
+  "Return current logical Eglot server connection or error."
+  (or (eglot-current-server)
+      (jsonrpc-error "No current JSON-RPC connection")))
+
+(defvar-local eglot--diagnostics nil
+  "Flymake diagnostics for this buffer.")
+
+(defvar revert-buffer-preserve-modes)
+(defun eglot--after-revert-hook ()
+  "Eglot's `after-revert-hook'."
+  (when revert-buffer-preserve-modes (eglot--signal-textDocument/didOpen)))
+
+(defun eglot--maybe-activate-editing-mode ()
+  "Maybe activate `eglot--managed-mode'.
+
+If it is activated, also signal textDocument/didOpen."
+  (unless eglot--managed-mode
+    ;; Called when `revert-buffer-in-progress-p' is t but
+    ;; `revert-buffer-preserve-modes' is nil.
+    (when (and buffer-file-name (eglot-current-server))
+      (setq eglot--diagnostics nil)
+      (eglot--managed-mode)
+      (eglot--signal-textDocument/didOpen))))
+
+(add-hook 'find-file-hook 'eglot--maybe-activate-editing-mode)
+(add-hook 'after-change-major-mode-hook 'eglot--maybe-activate-editing-mode)
+
+(defun eglot-clear-status (server)
+  "Clear the last JSONRPC error for SERVER."
+  (interactive (list (eglot--current-server-or-lose)))
+  (setf (jsonrpc-last-error server) nil))
+
+
+;;; Mode-line, menu and other sugar
+;;;
+(defvar eglot--mode-line-format `(:eval (eglot--mode-line-format)))
+
+(put 'eglot--mode-line-format 'risky-local-variable t)
+
+(defun eglot--mouse-call (what)
+  "Make an interactive lambda for calling WHAT from mode-line."
+  (lambda (event)
+    (interactive "e")
+    (let ((start (event-start event))) (with-selected-window (posn-window 
start)
+                                         (save-excursion
+                                           (goto-char (or (posn-point start)
+                                                          (point)))
+                                           (call-interactively what)
+                                           (force-mode-line-update t))))))
+
+(defun eglot-manual () "Open documentation."
+  (declare (obsolete info "29.1"))
+  (interactive) (info "(eglot)"))
+
+(easy-menu-define eglot-menu nil "Eglot"
+  `("Eglot"
+    ;; Commands for getting information and customization.
+    ["Customize Eglot" (lambda () (interactive) (customize-group "eglot"))]
+    "--"
+    ;; xref like commands.
+    ["Find definitions" xref-find-definitions
+     :help "Find definitions of identifier at point"
+     :active (eglot--server-capable :definitionProvider)]
+    ["Find references" xref-find-references
+     :help "Find references to identifier at point"
+     :active (eglot--server-capable :referencesProvider)]
+    ["Find symbols in workspace (apropos)" xref-find-apropos
+     :help "Find symbols matching a query"
+     :active (eglot--server-capable :workspaceSymbolProvider)]
+    ["Find declaration" eglot-find-declaration
+     :help "Find declaration for identifier at point"
+     :active (eglot--server-capable :declarationProvider)]
+    ["Find implementation" eglot-find-implementation
+     :help "Find implementation for identifier at point"
+     :active (eglot--server-capable :implementationProvider)]
+    ["Find type definition" eglot-find-typeDefinition
+     :help "Find type definition for identifier at point"
+     :active (eglot--server-capable :typeDefinitionProvider)]
+    "--"
+    ;; LSP-related commands (mostly Eglot's own commands).
+    ["Rename symbol" eglot-rename
+     :active (eglot--server-capable :renameProvider)]
+    ["Format buffer" eglot-format-buffer
+     :active (eglot--server-capable :documentFormattingProvider)]
+    ["Format active region" eglot-format
+     :active (and (region-active-p)
+                  (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)]
+    ["Organize imports" eglot-code-action-organize-imports
+     :visible (eglot--server-capable :codeActionProvider)]
+    ["Extract" eglot-code-action-extract
+     :visible (eglot--server-capable :codeActionProvider)]
+    ["Inline" eglot-code-action-inline
+     :visible (eglot--server-capable :codeActionProvider)]
+    ["Rewrite" eglot-code-action-rewrite
+     :visible (eglot--server-capable :codeActionProvider)]
+    ["Quickfix" eglot-code-action-quickfix
+     :visible (eglot--server-capable :codeActionProvider)]))
+
+(easy-menu-define eglot-server-menu nil "Monitor server communication"
+  '("Debugging the server communication"
+    ["Reconnect to server" eglot-reconnect]
+    ["Quit server" eglot-shutdown]
+    "--"
+    ["LSP events buffer" eglot-events-buffer]
+    ["Server stderr buffer" eglot-stderr-buffer]
+    ["Customize event buffer size"
+     (lambda ()
+       (interactive)
+       (customize-variable 'eglot-events-buffer-size))]))
+
+(defun eglot--mode-line-props (thing face defs &optional prepend)
+  "Helper for function `eglot--mode-line-format'.
+Uses THING, FACE, DEFS and PREPEND."
+  (cl-loop with map = (make-sparse-keymap)
+           for (elem . rest) on defs
+           for (key def help) = elem
+           do (define-key map `[mode-line ,key] (eglot--mouse-call def))
+           concat (format "%s: %s" key help) into blurb
+           when rest concat "\n" into blurb
+           finally (return `(:propertize ,thing
+                                         face ,face
+                                         keymap ,map help-echo ,(concat 
prepend blurb)
+                                         mouse-face mode-line-highlight))))
+
+(defun eglot--mode-line-format ()
+  "Compose the Eglot's mode-line."
+  (pcase-let* ((server (eglot-current-server))
+               (nick (and server (eglot-project-nickname server)))
+               (pending (and server (hash-table-count
+                                     (jsonrpc--request-continuations server))))
+               (`(,_id ,doing ,done-p ,_detail) (and server (eglot--spinner 
server)))
+               (last-error (and server (jsonrpc-last-error server))))
+    (append
+     `(,(propertize
+         eglot-menu-string
+         'face 'eglot-mode-line
+         'mouse-face 'mode-line-highlight
+         'help-echo "Eglot: Emacs LSP client\nmouse-1: Display minor mode menu"
+         'keymap (let ((map (make-sparse-keymap)))
+                   (define-key map [mode-line down-mouse-1] eglot-menu)
+                   map)))
+     (when nick
+       `(":"
+         ,(propertize
+           nick
+           'face 'eglot-mode-line
+           'mouse-face 'mode-line-highlight
+           'help-echo (format "Project '%s'\nmouse-1: LSP server control menu" 
nick)
+           'keymap (let ((map (make-sparse-keymap)))
+                     (define-key map [mode-line down-mouse-1] 
eglot-server-menu)
+                     map))
+       ,@(when last-error
+             `("/" ,(eglot--mode-line-props
+                     "error" 'compilation-mode-line-fail
+                     '((mouse-3 eglot-clear-status  "Clear this status"))
+                     (format "An error occurred: %s\n" (plist-get last-error
+                                                                 :message)))))
+         ,@(when (and doing (not done-p))
+             `("/" ,(eglot--mode-line-props doing
+                                            'compilation-mode-line-run '())))
+         ,@(when (cl-plusp pending)
+             `("/" ,(eglot--mode-line-props
+                     (format "%d" pending) 'warning
+                     '((mouse-3 eglot-forget-pending-continuations
+                                "Forget pending continuations"))
+                     "Number of outgoing, \
+still unanswered LSP requests to the server\n"))))))))
+
+(add-to-list 'mode-line-misc-info
+             `(eglot--managed-mode (" [" eglot--mode-line-format "] ")))
+
+
+;;; Flymake customization
+;;;
+(put 'eglot-note 'flymake-category 'flymake-note)
+(put 'eglot-warning 'flymake-category 'flymake-warning)
+(put 'eglot-error 'flymake-category 'flymake-error)
+
+(defalias 'eglot--make-diag 'flymake-make-diagnostic)
+(defalias 'eglot--diag-data 'flymake-diagnostic-data)
+
+(cl-loop for i from 1
+         for type in '(eglot-note eglot-warning eglot-error )
+         do (put type 'flymake-overlay-control
+                 `((mouse-face . highlight)
+                   (priority . ,(+ 50 i))
+                   (keymap . ,(let ((map (make-sparse-keymap)))
+                                (define-key map [mouse-1]
+                                  (eglot--mouse-call 'eglot-code-actions))
+                                map)))))
+
+
+;;; Protocol implementation (Requests, notifications, etc)
+;;;
+(cl-defmethod eglot-handle-notification
+  (_server method &key &allow-other-keys)
+  "Handle unknown notification."
+  (unless (or (string-prefix-p "$" (format "%s" method))
+              (not (memq 'disallow-unknown-methods eglot-strict-mode)))
+    (eglot--warn "Server sent unknown notification method `%s'" method)))
+
+(cl-defmethod eglot-handle-request
+  (_server method &key &allow-other-keys)
+  "Handle unknown request."
+  (when (memq 'disallow-unknown-methods eglot-strict-mode)
+    (jsonrpc-error "Unknown request method `%s'" method)))
+
+(cl-defmethod eglot-execute-command
+  (server command arguments)
+  "Execute COMMAND on SERVER with `:workspace/executeCommand'.
+COMMAND is a symbol naming the command."
+  (jsonrpc-request server :workspace/executeCommand
+                   `(:command ,(format "%s" command) :arguments ,arguments)))
+
+(cl-defmethod eglot-handle-notification
+  (_server (_method (eql window/showMessage)) &key type message)
+  "Handle notification window/showMessage."
+  (eglot--message (propertize "Server reports (type=%s): %s"
+                              'face (if (<= type 1) 'error))
+                  type message))
+
+(cl-defmethod eglot-handle-request
+  (_server (_method (eql window/showMessageRequest)) &key type message actions)
+  "Handle server request window/showMessageRequest."
+  (let* ((actions (append actions nil)) ;; gh#627
+         (label (completing-read
+                 (concat
+                  (format (propertize "[eglot] Server reports (type=%s): %s"
+                                      'face (if (<= type 1) 'error))
+                          type message)
+                  "\nChoose an option: ")
+                 (or (mapcar (lambda (obj) (plist-get obj :title)) actions)
+                     '("OK"))
+                 nil t (plist-get (elt actions 0) :title))))
+    (if label `(:title ,label) :null)))
+
+(cl-defmethod eglot-handle-notification
+  (_server (_method (eql window/logMessage)) &key _type _message)
+  "Handle notification window/logMessage.") ;; noop, use events buffer
+
+(cl-defmethod eglot-handle-notification
+  (_server (_method (eql telemetry/event)) &rest _any)
+  "Handle notification telemetry/event.") ;; noop, use events buffer
+
+(cl-defmethod eglot-handle-notification
+  (_server (_method (eql textDocument/publishDiagnostics)) &key uri diagnostics
+           &allow-other-keys) ; FIXME: doesn't respect `eglot-strict-mode'
+  "Handle notification publishDiagnostics."
+  (cl-flet ((eglot--diag-type (sev)
+              (cond ((null sev) 'eglot-error)
+                    ((<= sev 1) 'eglot-error)
+                    ((= sev 2)  'eglot-warning)
+                    (t          'eglot-note)))
+            (mess (source code message)
+              (concat source (and code (format " [%s]" code)) ": " message)))
+    (if-let ((buffer (find-buffer-visiting (eglot--uri-to-path uri))))
+        (with-current-buffer buffer
+          (cl-loop
+           for diag-spec across diagnostics
+           collect (eglot--dbind ((Diagnostic) range code message severity 
source tags)
+                       diag-spec
+                     (setq message (mess source code message))
+                     (pcase-let
+                         ((`(,beg . ,end) (eglot--range-region range)))
+                       ;; Fallback to `flymake-diag-region' if server
+                       ;; botched the range
+                       (when (= beg end)
+                         (if-let* ((st (plist-get range :start))
+                                   (diag-region
+                                    (flymake-diag-region
+                                     (current-buffer) (1+ (plist-get st :line))
+                                     (plist-get st :character))))
+                             (setq beg (car diag-region) end (cdr diag-region))
+                           (eglot--widening
+                            (goto-char (point-min))
+                            (setq beg
+                                  (line-beginning-position
+                                   (1+ (plist-get (plist-get range :start) 
:line))))
+                            (setq end
+                                  (line-end-position
+                                   (1+ (plist-get (plist-get range :end) 
:line)))))))
+                       (eglot--make-diag
+                        (current-buffer) beg end
+                        (eglot--diag-type severity)
+                        message `((eglot-lsp-diag . ,diag-spec))
+                        (when-let ((faces
+                                    (cl-loop for tag across tags
+                                             when (alist-get tag 
eglot--tag-faces)
+                                             collect it)))
+                          `((face . ,faces))))))
+           into diags
+           finally (cond ((and
+                           ;; only add to current report if Flymake
+                           ;; starts on idle-timer (github#958)
+                           (not (null flymake-no-changes-timeout))
+                           eglot--current-flymake-report-fn)
+                          (eglot--report-to-flymake diags))
+                         (t
+                          (setq eglot--diagnostics diags)))))
+      (cl-loop
+       with path = (expand-file-name (eglot--uri-to-path uri))
+       for diag-spec across diagnostics
+       collect (eglot--dbind ((Diagnostic) code range message severity source) 
diag-spec
+                 (setq message (mess source code message))
+                 (let* ((start (plist-get range :start))
+                        (line (1+ (plist-get start :line)))
+                        (char (1+ (plist-get start :character))))
+                   (eglot--make-diag
+                    path (cons line char) nil (eglot--diag-type severity) 
message)))
+       into diags
+       finally
+       (setq flymake-list-only-diagnostics
+             (assoc-delete-all path flymake-list-only-diagnostics #'string=))
+       (push (cons path diags) flymake-list-only-diagnostics)))))
+
+(cl-defun eglot--register-unregister (server things how)
+  "Helper for `registerCapability'.
+THINGS are either registrations or unregisterations (sic)."
+  (cl-loop
+   for thing in (cl-coerce things 'list)
+   do (eglot--dbind ((Registration) id method registerOptions) thing
+        (apply (cl-ecase how
+                 (register 'eglot-register-capability)
+                 (unregister 'eglot-unregister-capability))
+               server (intern method) id registerOptions))))
+
+(cl-defmethod eglot-handle-request
+  (server (_method (eql client/registerCapability)) &key registrations)
+  "Handle server request client/registerCapability."
+  (eglot--register-unregister server registrations 'register))
+
+(cl-defmethod eglot-handle-request
+  (server (_method (eql client/unregisterCapability))
+          &key unregisterations) ;; XXX: "unregisterations" (sic)
+  "Handle server request client/unregisterCapability."
+  (eglot--register-unregister server unregisterations 'unregister))
+
+(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)
+  `(:applied t))
+
+(cl-defmethod eglot-handle-request
+  (server (_method (eql workspace/workspaceFolders)))
+  "Handle server request workspace/workspaceFolders."
+  (eglot-workspace-folders server))
+
+(defun eglot--TextDocumentIdentifier ()
+  "Compute TextDocumentIdentifier object for current buffer."
+  `(:uri ,(eglot--path-to-uri (or buffer-file-name
+                                  (ignore-errors
+                                    (buffer-file-name
+                                     (buffer-base-buffer)))))))
+
+(defvar-local eglot--versioned-identifier 0)
+
+(defun eglot--VersionedTextDocumentIdentifier ()
+  "Compute VersionedTextDocumentIdentifier object for current buffer."
+  (append (eglot--TextDocumentIdentifier)
+          `(:version ,eglot--versioned-identifier)))
+
+(defun eglot--TextDocumentItem ()
+  "Compute TextDocumentItem object for current buffer."
+  (append
+   (eglot--VersionedTextDocumentIdentifier)
+   (list :languageId
+        (eglot--language-id (eglot--current-server-or-lose))
+         :text
+         (eglot--widening
+          (buffer-substring-no-properties (point-min) (point-max))))))
+
+(defun eglot--TextDocumentPositionParams ()
+  "Compute TextDocumentPositionParams."
+  (list :textDocument (eglot--TextDocumentIdentifier)
+        :position (eglot--pos-to-lsp-position)))
+
+(defvar-local eglot--last-inserted-char nil
+  "If non-nil, value of the last inserted character in buffer.")
+
+(defun eglot--post-self-insert-hook ()
+  "Set `eglot--last-inserted-char', maybe call on-type-formatting."
+  (setq eglot--last-inserted-char last-input-event)
+  (let ((ot-provider (eglot--server-capable 
:documentOnTypeFormattingProvider)))
+    (when (and ot-provider
+               (ignore-errors ; github#906, some LS's send empty strings
+                 (or (eq last-input-event
+                         (seq-first (plist-get ot-provider 
:firstTriggerCharacter)))
+                     (cl-find last-input-event
+                              (plist-get ot-provider :moreTriggerCharacter)
+                              :key #'seq-first))))
+      (eglot-format (point) nil last-input-event))))
+
+(defvar eglot--workspace-symbols-cache (make-hash-table :test #'equal)
+  "Cache of `workspace/Symbol' results  used by `xref-find-definitions'.")
+
+(defun eglot--pre-command-hook ()
+  "Reset some temporary variables."
+  (clrhash eglot--workspace-symbols-cache)
+  (setq eglot--last-inserted-char nil))
+
+(defun eglot--CompletionParams ()
+  (append
+   (eglot--TextDocumentPositionParams)
+   `(:context
+     ,(if-let (trigger (and (characterp eglot--last-inserted-char)
+                            (cl-find eglot--last-inserted-char
+                                     (eglot--server-capable :completionProvider
+                                                            :triggerCharacters)
+                                     :key (lambda (str) (aref str 0))
+                                     :test #'char-equal)))
+          `(:triggerKind 2 :triggerCharacter ,trigger) `(:triggerKind 1)))))
+
+(defvar-local eglot--recent-changes nil
+  "Recent buffer changes as collected by `eglot--before-change'.")
+
+(cl-defmethod jsonrpc-connection-ready-p ((_server eglot-lsp-server) _what)
+  "Tell if SERVER is ready for WHAT in current buffer."
+  (and (cl-call-next-method) (not eglot--recent-changes)))
+
+(defvar-local eglot--change-idle-timer nil "Idle timer for didChange signals.")
+
+(defun eglot--before-change (beg end)
+  "Hook onto `before-change-functions' with BEG and END."
+  (when (listp eglot--recent-changes)
+    ;; Records BEG and END, crucially convert them into LSP
+    ;; (line/char) positions before that information is lost (because
+    ;; the after-change thingy doesn't know if newlines were
+    ;; deleted/added).  Also record markers of BEG and END
+    ;; (github#259)
+    (push `(,(eglot--pos-to-lsp-position beg)
+            ,(eglot--pos-to-lsp-position end)
+            (,beg . ,(copy-marker beg nil))
+            (,end . ,(copy-marker end t)))
+          eglot--recent-changes)))
+
+(defun eglot--after-change (beg end pre-change-length)
+  "Hook onto `after-change-functions'.
+Records BEG, END and PRE-CHANGE-LENGTH locally."
+  (cl-incf eglot--versioned-identifier)
+  (pcase (and (listp eglot--recent-changes)
+              (car eglot--recent-changes))
+    (`(,lsp-beg ,lsp-end
+                (,b-beg . ,b-beg-marker)
+                (,b-end . ,b-end-marker))
+     ;; github#259 and github#367: With `capitalize-word' or somesuch,
+     ;; `before-change-functions' always records the whole word's
+     ;; `b-beg' and `b-end'.  Similarly, when coalescing two lines
+     ;; into one, `fill-paragraph' they mark the end of the first line
+     ;; up to the end of the second line.  In both situations, args
+     ;; received here contradict that information: `beg' and `end'
+     ;; will differ by 1 and will likely only encompass the letter
+     ;; that was capitalized or, in the sentence-joining situation,
+     ;; the replacement of the newline with a space.  That's we keep
+     ;; markers _and_ positions so we're able to detect and correct
+     ;; this.  We ignore `beg', `len' and `pre-change-len' and send
+     ;; "fuller" information about the region from the markers.  I've
+     ;; also experimented with doing this unconditionally but it seems
+     ;; to break when newlines are added.
+     (if (and (= b-end b-end-marker) (= b-beg b-beg-marker)
+              (or (/= beg b-beg) (/= end b-end)))
+         (setcar eglot--recent-changes
+                 `(,lsp-beg ,lsp-end ,(- b-end-marker b-beg-marker)
+                            ,(buffer-substring-no-properties b-beg-marker
+                                                             b-end-marker)))
+       (setcar eglot--recent-changes
+               `(,lsp-beg ,lsp-end ,pre-change-length
+                          ,(buffer-substring-no-properties beg end)))))
+    (_ (setf eglot--recent-changes :emacs-messup)))
+  (when eglot--change-idle-timer (cancel-timer eglot--change-idle-timer))
+  (let ((buf (current-buffer)))
+    (setq eglot--change-idle-timer
+          (run-with-idle-timer
+           eglot-send-changes-idle-time
+           nil (lambda () (eglot--when-live-buffer buf
+                            (when eglot--managed-mode
+                              (eglot--signal-textDocument/didChange)
+                              (setq eglot--change-idle-timer nil))))))))
+
+;; HACK! Launching a deferred sync request with outstanding changes is a
+;; bad idea, since that might lead to the request never having a
+;; chance to run, because `jsonrpc-connection-ready-p'.
+(advice-add #'jsonrpc-request :before
+            (cl-function (lambda (_proc _method _params &key
+                                        deferred &allow-other-keys)
+                           (when (and eglot--managed-mode deferred)
+                             (eglot--signal-textDocument/didChange))))
+            '((name . eglot--signal-textDocument/didChange)))
+
+(defvar-local eglot-workspace-configuration ()
+  "Configure LSP servers specifically for a given project.
+
+This variable's value should be a plist (SECTION VALUE ...).
+SECTION is a keyword naming a parameter section relevant to a
+particular server.  VALUE is a plist or a primitive type
+converted to JSON also understood by that server.
+
+Instead of a plist, an alist ((SECTION . VALUE) ...) can be used
+instead, but this variant is less reliable and not recommended.
+
+This variable should be set as a directory-local variable.  See
+See info node `(emacs)Directory Variables' for various ways to to
+that.
+
+Here's an example value that establishes two sections relevant to
+the Pylsp and Gopls LSP servers:
+
+  (:pylsp (:plugins (:jedi_completion (:include_params t
+                                       :fuzzy t)
+                     :pylint (:enabled :json-false)))
+   :gopls (:usePlaceholders t))
+
+The value of this variable can also be a unary function of a
+single argument, which will be a connected `eglot-lsp-server'
+instance.  The function runs with `default-directory' set to the
+root of the current project.  It should return an object of the
+format described above.")
+
+;;;###autoload
+(put 'eglot-workspace-configuration 'safe-local-variable 'listp)
+
+(defun eglot-show-workspace-configuration (&optional server)
+  "Dump `eglot-workspace-configuration' as JSON for debugging."
+  (interactive (list (and (eglot-current-server)
+                          (eglot--read-server "Server configuration"
+                                              (eglot-current-server)))))
+  (let ((conf (eglot--workspace-configuration-plist server)))
+    (with-current-buffer (get-buffer-create "*EGLOT workspace configuration*")
+      (erase-buffer)
+      (insert (jsonrpc--json-encode conf))
+      (with-no-warnings
+        (require 'json)
+        (when (require 'json-mode nil t) (json-mode))
+        (json-pretty-print-buffer))
+      (pop-to-buffer (current-buffer)))))
+
+(defun eglot--workspace-configuration (server)
+  (if (functionp eglot-workspace-configuration)
+      (funcall eglot-workspace-configuration server)
+    eglot-workspace-configuration))
+
+(defun eglot--workspace-configuration-plist (server)
+  "Returns `eglot-workspace-configuration' suitable for serialization."
+  (let ((val (eglot--workspace-configuration server)))
+    (or (and (consp (car val))
+             (cl-loop for (section . v) in val
+                      collect (if (keywordp section) section
+                                (intern (format ":%s" section)))
+                      collect v))
+        val)))
+
+(defun eglot-signal-didChangeConfiguration (server)
+  "Send a `:workspace/didChangeConfiguration' signal to SERVER.
+When called interactively, use the currently active server"
+  (interactive (list (eglot--current-server-or-lose)))
+  (jsonrpc-notify
+   server :workspace/didChangeConfiguration
+   (list
+    :settings
+    (or (eglot--workspace-configuration-plist server)
+        eglot--{}))))
+
+(cl-defmethod eglot-handle-request
+  (server (_method (eql workspace/configuration)) &key items)
+  "Handle server request workspace/configuration."
+  (apply #'vector
+         (mapcar
+          (eglot--lambda ((ConfigurationItem) scopeUri section)
+            (with-temp-buffer
+              (let* ((uri-path (eglot--uri-to-path scopeUri))
+                     (default-directory
+                      (if (and uri-path
+                               (not (string-empty-p uri-path))
+                               (file-directory-p uri-path))
+                          (file-name-as-directory uri-path)
+                        (project-root (eglot--project server)))))
+                (setq-local major-mode (car (eglot--major-modes server)))
+                (hack-dir-local-variables-non-file-buffer)
+                (cl-loop for (wsection o)
+                         on (eglot--workspace-configuration-plist server)
+                         by #'cddr
+                         when (string=
+                               (if (keywordp wsection)
+                                   (substring (symbol-name wsection) 1)
+                                 wsection)
+                               section)
+                         return o))))
+          items)))
+
+(defun eglot--signal-textDocument/didChange ()
+  "Send textDocument/didChange to server."
+  (when eglot--recent-changes
+    (let* ((server (eglot--current-server-or-lose))
+           (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)
+                            (eq :emacs-messup eglot--recent-changes))))
+      (jsonrpc-notify
+       server :textDocument/didChange
+       (list
+        :textDocument (eglot--VersionedTextDocumentIdentifier)
+        :contentChanges
+        (if full-sync-p
+            (vector `(:text ,(eglot--widening
+                              (buffer-substring-no-properties (point-min)
+                                                              (point-max)))))
+          (cl-loop for (beg end len text) in (reverse eglot--recent-changes)
+                   ;; github#259: `capitalize-word' and commands based
+                   ;; on `casify_region' will cause multiple duplicate
+                   ;; empty entries in `eglot--before-change' calls
+                   ;; without an `eglot--after-change' reciprocal.
+                   ;; Weed them out here.
+                   when (numberp len)
+                   vconcat `[,(list :range `(:start ,beg :end ,end)
+                                    :rangeLength len :text text)]))))
+      (setq eglot--recent-changes nil)
+      (setf (eglot--spinner server) (list nil :textDocument/didChange t))
+      (jsonrpc--call-deferred server))))
+
+(defun eglot--signal-textDocument/didOpen ()
+  "Send textDocument/didOpen to server."
+  (setq eglot--recent-changes nil eglot--versioned-identifier 0)
+  (jsonrpc-notify
+   (eglot--current-server-or-lose)
+   :textDocument/didOpen `(:textDocument ,(eglot--TextDocumentItem))))
+
+(defun eglot--signal-textDocument/didClose ()
+  "Send textDocument/didClose to server."
+  (with-demoted-errors
+      "[eglot] error sending textDocument/didClose: %s"
+    (jsonrpc-notify
+     (eglot--current-server-or-lose)
+     :textDocument/didClose `(:textDocument 
,(eglot--TextDocumentIdentifier)))))
+
+(defun eglot--signal-textDocument/willSave ()
+  "Send textDocument/willSave to server."
+  (let ((server (eglot--current-server-or-lose))
+        (params `(:reason 1 :textDocument ,(eglot--TextDocumentIdentifier))))
+    (when (eglot--server-capable :textDocumentSync :willSave)
+      (jsonrpc-notify server :textDocument/willSave params))
+    (when (eglot--server-capable :textDocumentSync :willSaveWaitUntil)
+      (ignore-errors
+        (eglot--apply-text-edits
+         (jsonrpc-request server :textDocument/willSaveWaitUntil params
+                          :timeout 0.5))))))
+
+(defun eglot--signal-textDocument/didSave ()
+  "Send textDocument/didSave to server."
+  (eglot--signal-textDocument/didChange)
+  (jsonrpc-notify
+   (eglot--current-server-or-lose)
+   :textDocument/didSave
+   (list
+    ;; TODO: Handle TextDocumentSaveRegistrationOptions to control this.
+    :text (buffer-substring-no-properties (point-min) (point-max))
+    :textDocument (eglot--TextDocumentIdentifier))))
+
+(defun eglot-flymake-backend (report-fn &rest _more)
+  "A Flymake backend for Eglot.
+Calls REPORT-FN (or arranges for it to be called) when the server
+publishes diagnostics.  Between calls to this function, REPORT-FN
+may be called multiple times (respecting the protocol of
+`flymake-backend-functions')."
+  (cond (eglot--managed-mode
+         (setq eglot--current-flymake-report-fn report-fn)
+         (eglot--report-to-flymake eglot--diagnostics))
+        (t
+         (funcall report-fn nil))))
+
+(defun eglot--report-to-flymake (diags)
+  "Internal helper for `eglot-flymake-backend'."
+  (save-restriction
+    (widen)
+    (funcall eglot--current-flymake-report-fn diags
+             ;; If the buffer hasn't changed since last
+             ;; call to the report function, flymake won't
+             ;; delete old diagnostics.  Using :region
+             ;; keyword forces flymake to delete
+             ;; them (github#159).
+             :region (cons (point-min) (point-max))))
+  (setq eglot--diagnostics diags))
+
+(defun eglot-xref-backend () "Eglot xref backend." 'eglot)
+
+(defvar eglot--temp-location-buffers (make-hash-table :test #'equal)
+  "Helper variable for `eglot--collecting-xrefs'.")
+
+(defvar eglot-xref-lessp-function #'ignore
+  "Compare two `xref-item' objects for sorting.")
+
+(cl-defmacro eglot--collecting-xrefs ((collector) &rest body)
+  "Sort and handle xrefs collected with COLLECTOR in BODY."
+  (declare (indent 1) (debug (sexp &rest form)))
+  (let ((collected (cl-gensym "collected")))
+    `(unwind-protect
+         (let (,collected)
+           (cl-flet ((,collector (xref) (push xref ,collected)))
+             ,@body)
+           (setq ,collected (nreverse ,collected))
+           (sort ,collected eglot-xref-lessp-function))
+       (maphash (lambda (_uri buf) (kill-buffer buf)) 
eglot--temp-location-buffers)
+       (clrhash eglot--temp-location-buffers))))
+
+(defun eglot--xref-make-match (name uri range)
+  "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))
+       (visiting (or (find-buffer-visiting file)
+                     (gethash uri eglot--temp-location-buffers)))
+       (collect (lambda ()
+                  (eglot--widening
+                   (pcase-let* ((`(,beg . ,end) (eglot--range-region range))
+                                (bol (progn (goto-char beg) 
(line-beginning-position)))
+                                (substring (buffer-substring bol 
(line-end-position)))
+                                (hi-beg (- beg bol))
+                                (hi-end (- (min (line-end-position) end) bol)))
+                     (add-face-text-property hi-beg hi-end 'xref-match
+                                             t substring)
+                     (list substring (line-number-at-pos (point) t)
+                           (eglot-current-column) (- end beg))))))
+       (`(,summary ,line ,column ,length)
+        (cond
+         (visiting (with-current-buffer visiting (funcall collect)))
+         ((file-readable-p file) (with-current-buffer
+                                     (puthash uri (generate-new-buffer " 
*temp*")
+                                              eglot--temp-location-buffers)
+                                   (insert-file-contents file)
+                                   (funcall collect)))
+         (t ;; fall back to the "dumb strategy"
+          (let* ((start (cl-getf range :start))
+                 (line (1+ (cl-getf start :line)))
+                 (start-pos (cl-getf start :character))
+                 (end-pos (cl-getf (cl-getf range :end) :character)))
+            (list name line start-pos (- end-pos start-pos)))))))
+    (setf (gethash (expand-file-name file) eglot--servers-by-xrefed-file)
+          (eglot--current-server-or-lose))
+    (xref-make-match summary (xref-make-file-location file line column) 
length)))
+
+(defun eglot--workspace-symbols (pat &optional buffer)
+  "Ask for :workspace/symbol on PAT, return list of formatted strings.
+If BUFFER, switch to it before."
+  (with-current-buffer (or buffer (current-buffer))
+    (unless (eglot--server-capable :workspaceSymbolProvider)
+      (eglot--error "This LSP server isn't a :workspaceSymbolProvider"))
+    (mapcar
+     (lambda (wss)
+       (eglot--dbind ((WorkspaceSymbol) name containerName kind) wss
+         (propertize
+          (format "%s%s %s"
+                  (if (zerop (length containerName)) ""
+                    (concat (propertize containerName 'face 'shadow) " "))
+                  name
+                  (propertize (alist-get kind eglot--symbol-kind-names 
"Unknown")
+                              'face 'shadow))
+          'eglot--lsp-workspaceSymbol wss)))
+     (jsonrpc-request (eglot--current-server-or-lose) :workspace/symbol
+                      `(:query ,pat)))))
+
+(cl-defmethod xref-backend-identifier-completion-table ((_backend (eql eglot)))
+  "Yet another tricky connection between LSP and Elisp completion semantics."
+  (let ((buf (current-buffer)) (cache eglot--workspace-symbols-cache))
+    (cl-labels ((refresh (pat) (eglot--workspace-symbols pat buf))
+                (lookup-1 (pat) ;; check cache, else refresh
+                  (let ((probe (gethash pat cache :missing)))
+                    (if (eq probe :missing) (puthash pat (refresh pat) cache)
+                      probe)))
+                (lookup (pat)
+                  (let ((res (lookup-1 pat))
+                        (def (and (string= pat "") (gethash :default cache))))
+                    (append def res nil)))
+                (score (c)
+                  (cl-getf (get-text-property
+                            0 'eglot--lsp-workspaceSymbol c)
+                           :score 0)))
+      (lambda (string _pred action)
+        (pcase action
+          (`metadata `(metadata
+                       (cycle-sort-function
+                        . ,(lambda (completions)
+                             (cl-sort completions #'> :key #'score)))
+                       (category . eglot-indirection-joy)))
+          (`(eglot--lsp-tryc . ,point) `(eglot--lsp-tryc . (,string . ,point)))
+          (`(eglot--lsp-allc . ,_point) `(eglot--lsp-allc . ,(lookup string)))
+          (_ nil))))))
+
+(defun eglot--recover-workspace-symbol-meta (string)
+  "Search `eglot--workspace-symbols-cache' for rich entry of STRING."
+  (catch 'found
+    (maphash (lambda (_k v)
+               (while (consp v)
+                 ;; Like mess? Ask minibuffer.el about improper lists.
+                 (when (equal (car v) string) (throw 'found (car v)))
+                 (setq v (cdr v))))
+             eglot--workspace-symbols-cache)))
+
+(add-to-list 'completion-category-overrides
+             '(eglot-indirection-joy (styles . (eglot--lsp-backend-style))))
+
+(cl-defmethod xref-backend-identifier-at-point ((_backend (eql eglot)))
+  (let ((attempt
+         (and (xref--prompt-p this-command)
+              (puthash :default
+                       (ignore-errors
+                         (eglot--workspace-symbols (symbol-name 
(symbol-at-point))))
+                       eglot--workspace-symbols-cache))))
+    (if attempt (car attempt) "LSP identifier at point")))
+
+(defvar eglot--lsp-xref-refs nil
+  "`xref' objects for overriding `xref-backend-references''s.")
+
+(cl-defun eglot--lsp-xrefs-for-method (method &key extra-params capability)
+  "Make `xref''s for METHOD, EXTRA-PARAMS, check CAPABILITY."
+  (unless (eglot--server-capable
+           (or capability
+               (intern
+                (format ":%sProvider"
+                        (cadr (split-string (symbol-name method)
+                                            "/"))))))
+    (eglot--error "Sorry, this server doesn't do %s" method))
+  (let ((response
+         (jsonrpc-request
+          (eglot--current-server-or-lose)
+          method (append (eglot--TextDocumentPositionParams) extra-params))))
+    (eglot--collecting-xrefs (collect)
+      (mapc
+       (lambda (loc-or-loc-link)
+         (let ((sym-name (symbol-name (symbol-at-point))))
+           (eglot--dcase loc-or-loc-link
+             (((LocationLink) targetUri targetSelectionRange)
+              (collect (eglot--xref-make-match sym-name
+                                               targetUri 
targetSelectionRange)))
+             (((Location) uri range)
+              (collect (eglot--xref-make-match sym-name
+                                               uri range))))))
+       (if (vectorp response) response (and response (list response)))))))
+
+(cl-defun eglot--lsp-xref-helper (method &key extra-params capability )
+  "Helper for `eglot-find-declaration' & friends."
+  (let ((eglot--lsp-xref-refs (eglot--lsp-xrefs-for-method
+                               method
+                               :extra-params extra-params
+                               :capability capability)))
+    (if eglot--lsp-xref-refs
+        (xref-find-references "LSP identifier at point.")
+      (eglot--message "%s returned no references" method))))
+
+(defun eglot-find-declaration ()
+  "Find declaration for SYM, the identifier at point."
+  (interactive)
+  (eglot--lsp-xref-helper :textDocument/declaration))
+
+(defun eglot-find-implementation ()
+  "Find implementation for SYM, the identifier at point."
+  (interactive)
+  (eglot--lsp-xref-helper :textDocument/implementation))
+
+(defun eglot-find-typeDefinition ()
+  "Find type definition for SYM, the identifier at point."
+  (interactive)
+  (eglot--lsp-xref-helper :textDocument/typeDefinition))
+
+(cl-defmethod xref-backend-definitions ((_backend (eql eglot)) id)
+  (let ((probe (eglot--recover-workspace-symbol-meta id)))
+    (if probe
+        (eglot--dbind ((WorkspaceSymbol) name location)
+            (get-text-property 0 'eglot--lsp-workspaceSymbol probe)
+          (eglot--dbind ((Location) uri range) location
+            (list (eglot--xref-make-match name uri range))))
+        (eglot--lsp-xrefs-for-method :textDocument/definition))))
+
+(cl-defmethod xref-backend-references ((_backend (eql eglot)) _identifier)
+  (or
+   eglot--lsp-xref-refs
+   (eglot--lsp-xrefs-for-method
+    :textDocument/references :extra-params `(:context (:includeDeclaration 
t)))))
+
+(cl-defmethod xref-backend-apropos ((_backend (eql eglot)) pattern)
+  (when (eglot--server-capable :workspaceSymbolProvider)
+    (eglot--collecting-xrefs (collect)
+      (mapc
+       (eglot--lambda ((SymbolInformation) name location)
+         (eglot--dbind ((Location) uri range) location
+           (collect (eglot--xref-make-match name uri range))))
+       (jsonrpc-request (eglot--current-server-or-lose)
+                        :workspace/symbol
+                        `(:query ,pattern))))))
+
+(defun eglot-format-buffer ()
+  "Format contents of current buffer."
+  (interactive)
+  (eglot-format nil nil))
+
+(defun eglot-format (&optional beg end on-type-format)
+  "Format region BEG END.
+If either BEG or END is nil, format entire buffer.
+Interactively, format active region, or entire buffer if region
+is not active.
+
+If non-nil, ON-TYPE-FORMAT is a character just inserted at BEG
+for which LSP on-type-formatting should be requested."
+  (interactive (and (region-active-p) (list (region-beginning) (region-end))))
+  (pcase-let ((`(,method ,cap ,args)
+               (cond
+                ((and beg on-type-format)
+                 `(:textDocument/onTypeFormatting
+                   :documentOnTypeFormattingProvider
+                   ,`(:position ,(eglot--pos-to-lsp-position beg)
+                      :ch ,(string on-type-format))))
+                ((and beg end)
+                 `(:textDocument/rangeFormatting
+                   :documentRangeFormattingProvider
+                   (:range ,(list :start (eglot--pos-to-lsp-position beg)
+                                  :end (eglot--pos-to-lsp-position end)))))
+                (t
+                 '(:textDocument/formatting :documentFormattingProvider 
nil)))))
+    (unless (eglot--server-capable cap)
+      (eglot--error "Server can't format!"))
+    (eglot--apply-text-edits
+     (jsonrpc-request
+      (eglot--current-server-or-lose)
+      method
+      (cl-list*
+       :textDocument (eglot--TextDocumentIdentifier)
+       :options (list :tabSize tab-width
+                      :insertSpaces (if indent-tabs-mode :json-false t)
+                      :insertFinalNewline (if require-final-newline t 
:json-false)
+                      :trimFinalNewlines (if delete-trailing-lines t 
:json-false))
+       args)
+      :deferred method))))
+
+(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))
+    (let* ((server (eglot--current-server-or-lose))
+           (sort-completions
+            (lambda (completions)
+              (cl-sort completions
+                       #'string-lessp
+                       :key (lambda (c)
+                              (or (plist-get
+                                   (get-text-property 0 'eglot--lsp-item c)
+                                   :sortText)
+                                  "")))))
+           (metadata `(metadata (category . eglot)
+                                (display-sort-function . ,sort-completions)))
+           resp items (cached-proxies :none)
+           (proxies
+            (lambda ()
+              (if (listp cached-proxies) cached-proxies
+                (setq resp
+                      (jsonrpc-request server
+                                       :textDocument/completion
+                                       (eglot--CompletionParams)
+                                       :deferred :textDocument/completion
+                                       :cancel-on-input t))
+                (setq items (append
+                             (if (vectorp resp) resp (plist-get resp :items))
+                             nil))
+                (setq cached-proxies
+                      (mapcar
+                       (jsonrpc-lambda
+                           (&rest item &key label insertText insertTextFormat
+                                  &allow-other-keys)
+                         (let ((proxy
+                                (cond ((and (eql insertTextFormat 2)
+                                            (eglot--snippet-expansion-fn))
+                                       (string-trim-left label))
+                                      ((and insertText
+                                            (not (string-empty-p insertText)))
+                                       insertText)
+                                      (t
+                                       (string-trim-left label)))))
+                           (unless (zerop (length proxy))
+                             (put-text-property 0 1 'eglot--lsp-item item 
proxy))
+                           proxy))
+                       items)))))
+           (resolved (make-hash-table))
+           (resolve-maybe
+            ;; Maybe completion/resolve JSON object `lsp-comp' into
+            ;; another JSON object, if at all possible.  Otherwise,
+            ;; just return lsp-comp.
+            (lambda (lsp-comp)
+              (or (gethash lsp-comp resolved)
+                  (setf (gethash lsp-comp resolved)
+                        (if (and (eglot--server-capable :completionProvider
+                                                        :resolveProvider)
+                                 (plist-get lsp-comp :data))
+                            (jsonrpc-request server :completionItem/resolve
+                                             lsp-comp :cancel-on-input t)
+                          lsp-comp)))))
+           (bounds (bounds-of-thing-at-point 'symbol)))
+      (list
+       (or (car bounds) (point))
+       (or (cdr bounds) (point))
+       (lambda (probe pred action)
+         (cond
+          ((eq action 'metadata) metadata)               ; metadata
+          ((eq action 'lambda)                           ; test-completion
+           (test-completion probe (funcall proxies)))
+          ((eq (car-safe action) 'boundaries) nil)       ; boundaries
+          ((null action)                                 ; try-completion
+           (try-completion probe (funcall proxies)))
+          ((eq action t)                                 ; all-completions
+           (all-completions
+            ""
+            (funcall proxies)
+            (lambda (proxy)
+              (let* ((item (get-text-property 0 'eglot--lsp-item proxy))
+                     (filterText (plist-get item :filterText)))
+                (and (or (null pred) (funcall pred proxy))
+                     (string-prefix-p
+                      probe (or filterText proxy) 
completion-ignore-case))))))))
+       :annotation-function
+       (lambda (proxy)
+         (eglot--dbind ((CompletionItem) detail kind)
+             (get-text-property 0 'eglot--lsp-item proxy)
+           (let* ((detail (and (stringp detail)
+                               (not (string= detail ""))
+                               detail))
+                  (annotation
+                   (or detail
+                       (cdr (assoc kind eglot--kind-names)))))
+             (when annotation
+               (concat " "
+                       (propertize annotation
+                                   'face 'font-lock-function-name-face))))))
+       :company-kind
+       ;; Associate each lsp-item with a lsp-kind symbol.
+       (lambda (proxy)
+         (when-let* ((lsp-item (get-text-property 0 'eglot--lsp-item proxy))
+                     (kind (alist-get (plist-get lsp-item :kind)
+                                      eglot--kind-names)))
+           (intern (downcase kind))))
+       :company-deprecated
+       (lambda (proxy)
+         (when-let ((lsp-item (get-text-property 0 'eglot--lsp-item proxy)))
+           (or (seq-contains-p (plist-get lsp-item :tags)
+                               1)
+               (eq t (plist-get lsp-item :deprecated)))))
+       :company-docsig
+       ;; FIXME: autoImportText is specific to the pyright language server
+       (lambda (proxy)
+         (when-let* ((lsp-comp (get-text-property 0 'eglot--lsp-item proxy))
+                     (data (plist-get (funcall resolve-maybe lsp-comp) :data))
+                     (import-text (plist-get data :autoImportText)))
+           import-text))
+       :company-doc-buffer
+       (lambda (proxy)
+         (let* ((documentation
+                 (let ((lsp-comp (get-text-property 0 'eglot--lsp-item proxy)))
+                   (plist-get (funcall resolve-maybe lsp-comp) 
:documentation)))
+                (formatted (and documentation
+                                (eglot--format-markup documentation))))
+           (when formatted
+             (with-current-buffer (get-buffer-create " *eglot doc*")
+               (erase-buffer)
+               (insert formatted)
+               (current-buffer)))))
+       :company-require-match 'never
+       :company-prefix-length
+       (save-excursion
+         (when (car bounds) (goto-char (car bounds)))
+         (when (listp completion-capability)
+           (looking-back
+            (regexp-opt
+             (cl-coerce (cl-getf completion-capability :triggerCharacters) 
'list))
+            (line-beginning-position))))
+       :exit-function
+       (lambda (proxy status)
+         (when (memq status '(finished exact))
+           ;; To assist in using this whole `completion-at-point'
+           ;; function inside `completion-in-region', ensure the exit
+           ;; function runs in the buffer where the completion was
+           ;; triggered from.  This should probably be in Emacs itself.
+           ;; (github#505)
+           (with-current-buffer (if (minibufferp)
+                                    (window-buffer 
(minibuffer-selected-window))
+                                  (current-buffer))
+             (eglot--dbind ((CompletionItem) insertTextFormat
+                            insertText textEdit additionalTextEdits label)
+                 (funcall
+                  resolve-maybe
+                  (or (get-text-property 0 'eglot--lsp-item proxy)
+                      ;; When selecting from the *Completions*
+                      ;; buffer, `proxy' won't have any properties.
+                      ;; A lookup should fix that (github#148)
+                      (get-text-property
+                       0 'eglot--lsp-item
+                       (cl-find proxy (funcall proxies) :test #'string=))))
+               (let ((snippet-fn (and (eql insertTextFormat 2)
+                                      (eglot--snippet-expansion-fn))))
+                 (cond (textEdit
+                        ;; Undo (yes, undo) the newly inserted completion.
+                        ;; If before completion the buffer was "foo.b" and
+                        ;; now is "foo.bar", `proxy' will be "bar".  We
+                        ;; want to delete only "ar" (`proxy' minus the
+                        ;; symbol whose bounds we've calculated before)
+                        ;; (github#160).
+                        (delete-region (+ (- (point) (length proxy))
+                                          (if bounds
+                                              (- (cdr bounds) (car bounds))
+                                            0))
+                                       (point))
+                        (eglot--dbind ((TextEdit) range newText) textEdit
+                          (pcase-let ((`(,beg . ,end)
+                                       (eglot--range-region range)))
+                            (delete-region beg end)
+                            (goto-char beg)
+                            (funcall (or snippet-fn #'insert) newText))))
+                       (snippet-fn
+                        ;; A snippet should be inserted, but using plain
+                        ;; `insertText'.  This requires us to delete the
+                        ;; whole completion, since `insertText' is the full
+                        ;; completion's text.
+                        (delete-region (- (point) (length proxy)) (point))
+                        (funcall snippet-fn (or insertText label))))
+                 (when (cl-plusp (length additionalTextEdits))
+                   (eglot--apply-text-edits additionalTextEdits)))
+               (eglot--signal-textDocument/didChange)
+               (eldoc)))))))))
+
+(defun eglot--hover-info (contents &optional _range)
+  (mapconcat #'eglot--format-markup
+             (if (vectorp contents) contents (list contents)) "\n"))
+
+(defun eglot--sig-info (sigs active-sig sig-help-active-param)
+  (cl-loop
+   for (sig . moresigs) on (append sigs nil) for i from 0
+   concat
+   (eglot--dbind ((SignatureInformation) label documentation parameters 
activeParameter) sig
+     (with-temp-buffer
+       (save-excursion (insert label))
+       (let ((active-param (or activeParameter sig-help-active-param))
+             params-start params-end)
+         ;; Ad-hoc attempt to parse label as <name>(<params>)
+         (when (looking-at "\\([^(]*\\)(\\([^)]+\\))")
+           (setq params-start (match-beginning 2) params-end (match-end 2))
+           (add-face-text-property (match-beginning 1) (match-end 1)
+                                   'font-lock-function-name-face))
+         (when (eql i active-sig)
+           ;; Decide whether to add one-line-summary to signature line
+           (when (and (stringp documentation)
+                      (string-match "[[:space:]]*\\([^.\r\n]+[.]?\\)"
+                                    documentation))
+             (setq documentation (match-string 1 documentation))
+             (unless (string-prefix-p (string-trim documentation) label)
+               (goto-char (point-max))
+               (insert ": " (eglot--format-markup documentation))))
+           ;; Decide what to do with the active parameter...
+           (when (and (eql i active-sig) active-param
+                      (< -1 active-param (length parameters)))
+             (eglot--dbind ((ParameterInformation) label documentation)
+                 (aref parameters active-param)
+               ;; ...perhaps highlight it in the formals list
+               (when params-start
+                 (goto-char params-start)
+                 (pcase-let
+                     ((`(,beg ,end)
+                       (if (stringp label)
+                           (let ((case-fold-search nil))
+                             (and (re-search-forward
+                                   (concat "\\<" (regexp-quote label) "\\>")
+                                   params-end t)
+                                  (list (match-beginning 0) (match-end 0))))
+                         (mapcar #'1+ (append label nil)))))
+                   (if (and beg end)
+                       (add-face-text-property
+                        beg end
+                        'eldoc-highlight-function-argument))))
+               ;; ...and/or maybe add its doc on a line by its own.
+               (when documentation
+                 (goto-char (point-max))
+                 (insert "\n"
+                         (propertize
+                          (if (stringp label)
+                              label
+                            (apply #'buffer-substring (mapcar #'1+ label)))
+                          'face 'eldoc-highlight-function-argument)
+                         ": " (eglot--format-markup documentation))))))
+         (buffer-string))))
+   when moresigs concat "\n"))
+
+(defun eglot-signature-eldoc-function (cb)
+  "A member of `eldoc-documentation-functions', for signatures."
+  (when (eglot--server-capable :signatureHelpProvider)
+    (let ((buf (current-buffer)))
+      (jsonrpc-async-request
+       (eglot--current-server-or-lose)
+       :textDocument/signatureHelp (eglot--TextDocumentPositionParams)
+       :success-fn
+       (eglot--lambda ((SignatureHelp)
+                       signatures activeSignature activeParameter)
+         (eglot--when-buffer-window buf
+           (funcall cb
+                    (unless (seq-empty-p signatures)
+                      (eglot--sig-info signatures
+                                       activeSignature
+                                       activeParameter)))))
+       :deferred :textDocument/signatureHelp))
+    t))
+
+(defun eglot-hover-eldoc-function (cb)
+  "A member of `eldoc-documentation-functions', for hover."
+  (when (eglot--server-capable :hoverProvider)
+    (let ((buf (current-buffer)))
+      (jsonrpc-async-request
+       (eglot--current-server-or-lose)
+       :textDocument/hover (eglot--TextDocumentPositionParams)
+       :success-fn (eglot--lambda ((Hover) contents range)
+                     (eglot--when-buffer-window buf
+                       (let ((info (unless (seq-empty-p contents)
+                                     (eglot--hover-info contents range))))
+                         (funcall cb info :buffer t))))
+       :deferred :textDocument/hover))
+    (eglot--highlight-piggyback cb)
+    t))
+
+(defvar eglot--highlights nil "Overlays for textDocument/documentHighlight.")
+
+(defun eglot--highlight-piggyback (_cb)
+  "Request and handle `:textDocument/documentHighlight'."
+  ;; 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)
+      (jsonrpc-async-request
+       (eglot--current-server-or-lose)
+       :textDocument/documentHighlight (eglot--TextDocumentPositionParams)
+       :success-fn
+       (lambda (highlights)
+         (mapc #'delete-overlay eglot--highlights)
+         (setq eglot--highlights
+               (eglot--when-buffer-window buf
+                 (mapcar
+                  (eglot--lambda ((DocumentHighlight) range)
+                    (pcase-let ((`(,beg . ,end)
+                                 (eglot--range-region range)))
+                      (let ((ov (make-overlay beg end)))
+                        (overlay-put ov 'face 'eglot-highlight-symbol-face)
+                        (overlay-put ov 'modification-hooks
+                                     `(,(lambda (o &rest _) (delete-overlay 
o))))
+                        ov)))
+                  highlights))))
+       :deferred :textDocument/documentHighlight)
+      nil)))
+
+(defun eglot-imenu ()
+  "Eglot's `imenu-create-index-function'.
+Returns a list as described in docstring of `imenu--index-alist'."
+  (cl-labels
+      ((unfurl (obj)
+         (eglot--dcase obj
+           (((SymbolInformation)) (list obj))
+           (((DocumentSymbol) name children)
+            (cons obj
+                  (mapcar
+                   (lambda (c)
+                     (plist-put
+                      c :containerName
+                      (let ((existing (plist-get c :containerName)))
+                        (if existing (format "%s::%s" name existing)
+                          name))))
+                   (mapcan #'unfurl children)))))))
+    (mapcar
+     (pcase-lambda (`(,kind . ,objs))
+       (cons
+        (alist-get kind eglot--symbol-kind-names "Unknown")
+        (mapcan (pcase-lambda (`(,container . ,objs))
+                  (let ((elems (mapcar
+                                (lambda (obj)
+                                  (cons (plist-get obj :name)
+                                        (car (eglot--range-region
+                                              (eglot--dcase obj
+                                                (((SymbolInformation) location)
+                                                 (plist-get location :range))
+                                                (((DocumentSymbol) 
selectionRange)
+                                                 selectionRange))))))
+                                objs)))
+                    (if container (list (cons container elems)) elems)))
+                (seq-group-by
+                 (lambda (e) (plist-get e :containerName)) objs))))
+     (seq-group-by
+      (lambda (obj) (plist-get obj :kind))
+      (mapcan #'unfurl
+              (jsonrpc-request (eglot--current-server-or-lose)
+                               :textDocument/documentSymbol
+                               `(:textDocument
+                                 ,(eglot--TextDocumentIdentifier))
+                               :cancel-on-input non-essential))))))
+
+(defun eglot--apply-text-edits (edits &optional version)
+  "Apply EDITS for current buffer if at VERSION, or if it's nil."
+  (unless (or (not version) (equal version eglot--versioned-identifier))
+    (jsonrpc-error "Edits on `%s' require version %d, you have %d"
+                   (current-buffer) version eglot--versioned-identifier))
+  (atomic-change-group
+    (let* ((change-group (prepare-change-group))
+           (howmany (length edits))
+           (reporter (make-progress-reporter
+                      (format "[eglot] applying %s edits to `%s'..."
+                              howmany (current-buffer))
+                      0 howmany))
+           (done 0))
+      (mapc (pcase-lambda (`(,newText ,beg . ,end))
+              (let ((source (current-buffer)))
+                (with-temp-buffer
+                  (insert newText)
+                  (let ((temp (current-buffer)))
+                    (with-current-buffer source
+                      (save-excursion
+                        (save-restriction
+                          (narrow-to-region beg end)
+                          (replace-buffer-contents temp)))
+                      (progress-reporter-update reporter (cl-incf done)))))))
+            (mapcar (eglot--lambda ((TextEdit) range newText)
+                      (cons newText (eglot--range-region range 'markers)))
+                    (reverse edits)))
+      (undo-amalgamate-change-group change-group)
+      (progress-reporter-done reporter))))
+
+(defun eglot--apply-workspace-edit (wedit &optional confirm)
+  "Apply the workspace edit WEDIT.  If CONFIRM, ask user first."
+  (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)))
+                   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!")))))
+
+(defun eglot-rename (newname)
+  "Rename the current symbol to NEWNAME."
+  (interactive
+   (list (read-from-minibuffer
+          (format "Rename `%s' to: " (or (thing-at-point 'symbol t)
+                                         "unknown symbol"))
+          nil nil nil nil
+          (symbol-name (symbol-at-point)))))
+  (unless (eglot--server-capable :renameProvider)
+    (eglot--error "Server can't rename!"))
+  (eglot--apply-workspace-edit
+   (jsonrpc-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)))))
+
+(defun eglot-code-actions (beg &optional end action-kind interactive)
+  "Find LSP code actions of type ACTION-KIND between BEG and END.
+Interactively, offer to execute them.
+If ACTION-KIND is nil, consider all kinds of actions.
+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)
+     ,(and current-prefix-arg
+           (completing-read "[eglot] Action kind: "
+                            '("quickfix" "refactor.extract" "refactor.inline"
+                              "refactor.rewrite" "source.organizeImports")))
+     t))
+  (unless (or (not interactive)
+              (eglot--server-capable :codeActionProvider))
+    (eglot--error "Server can't execute code actions!"))
+  (let* ((server (eglot--current-server-or-lose))
+         (actions
+          (jsonrpc-request
+           server
+           :textDocument/codeAction
+           (list :textDocument (eglot--TextDocumentIdentifier)
+                 :range (list :start (eglot--pos-to-lsp-position beg)
+                              :end (eglot--pos-to-lsp-position end))
+                 :context
+                 `(:diagnostics
+                   [,@(cl-loop for diag in (flymake-diagnostics beg end)
+                               when (cdr (assoc 'eglot-lsp-diag
+                                                (eglot--diag-data diag)))
+                               collect it)]
+                   ,@(when action-kind `(:only [,action-kind]))))
+           :deferred t))
+         ;; Redo filtering, in case the `:only' didn't go through.
+         (actions (cl-loop for a across actions
+                           when (or (not action-kind)
+                                    (equal action-kind (plist-get a :kind)))
+                           collect a)))
+    (if interactive
+        (eglot--read-execute-code-action actions server action-kind)
+      actions)))
+
+(defun eglot--read-execute-code-action (actions server &optional action-kind)
+  "Helper for interactive calls to `eglot-code-actions'"
+  (let* ((menu-items
+          (or (cl-loop for a in actions
+                       collect (cons (plist-get a :title) a))
+              (apply #'eglot--error
+                     (if action-kind `("No \"%s\" code actions here" 
,action-kind)
+                       `("No code actions here")))))
+         (preferred-action (cl-find-if
+                            (lambda (menu-item)
+                              (plist-get (cdr menu-item) :isPreferred))
+                            menu-items))
+         (default-action (car (or preferred-action (car menu-items))))
+         (chosen (if (and action-kind (null (cadr menu-items)))
+                     (cdr (car menu-items))
+                   (if (listp last-nonmenu-event)
+                       (x-popup-menu last-nonmenu-event `("Eglot code actions:"
+                                                          ("dummy" 
,@menu-items)))
+                     (cdr (assoc (completing-read
+                                  (format "[eglot] Pick an action (default 
%s): "
+                                          default-action)
+                                  menu-items nil t nil nil default-action)
+                                 menu-items))))))
+    (eglot--dcase chosen
+      (((Command) command arguments)
+       (eglot-execute-command server (intern command) arguments))
+      (((CodeAction) edit command)
+       (when edit (eglot--apply-workspace-edit edit))
+       (when command
+         (eglot--dbind ((Command) command arguments) command
+           (eglot-execute-command server (intern command) arguments)))))))
+
+(defmacro eglot--code-action (name 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))
+     (eglot-code-actions beg end ,kind)))
+
+(eglot--code-action eglot-code-action-organize-imports 
"source.organizeImports")
+(eglot--code-action eglot-code-action-extract "refactor.extract")
+(eglot--code-action eglot-code-action-inline "refactor.inline")
+(eglot--code-action eglot-code-action-rewrite "refactor.rewrite")
+(eglot--code-action eglot-code-action-quickfix "quickfix")
+
+
+;;; Dynamic registration
+;;;
+(cl-defmethod eglot-register-capability
+  (server (method (eql workspace/didChangeWatchedFiles)) id &key watchers)
+  "Handle dynamic registration of workspace/didChangeWatchedFiles."
+  (eglot-unregister-capability server method id)
+  (let* (success
+         (globs (mapcar
+                 (eglot--lambda ((FileSystemWatcher) globPattern kind)
+                   (cons (eglot--glob-compile globPattern t t)
+                         ;; the default "7" means bitwise OR of
+                         ;; WatchKind.Create (1), WatchKind.Change
+                         ;; (2), WatchKind.Delete (4)
+                         (or kind 7)))
+                 watchers))
+         (dirs-to-watch
+          (delete-dups (mapcar #'file-name-directory
+                               (project-files
+                                (eglot--project server))))))
+    (cl-labels
+        ((handle-event
+          (event)
+          (pcase-let* ((`(,desc ,action ,file ,file1) event)
+                       (action-type (cl-case action
+                                      (created 1) (changed 2) (deleted 3)))
+                       (action-bit (when action-type
+                                     (ash 1 (1- action-type)))))
+            (cond
+             ((and (memq action '(created changed deleted))
+                   (cl-loop for (glob . kind-bitmask) in globs
+                            thereis (and (> (logand kind-bitmask action-bit) 0)
+                                         (funcall glob file))))
+              (jsonrpc-notify
+               server :workspace/didChangeWatchedFiles
+               `(:changes ,(vector `(:uri ,(eglot--path-to-uri file)
+                                          :type ,action-type)))))
+             ((eq action 'renamed)
+              (handle-event `(,desc 'deleted ,file))
+              (handle-event `(,desc 'created ,file1)))))))
+      (unwind-protect
+          (progn
+            (dolist (dir dirs-to-watch)
+              (push (file-notify-add-watch dir '(change) #'handle-event)
+                    (gethash id (eglot--file-watches server))))
+            (setq
+             success
+             `(:message ,(format "OK, watching %s directories in %s watchers"
+                                 (length dirs-to-watch) (length watchers)))))
+        (unless success
+          (eglot-unregister-capability server method id))))))
+
+(cl-defmethod eglot-unregister-capability
+  (server (_method (eql workspace/didChangeWatchedFiles)) id)
+  "Handle dynamic unregistration of workspace/didChangeWatchedFiles."
+  (mapc #'file-notify-rm-watch (gethash id (eglot--file-watches server)))
+  (remhash id (eglot--file-watches server))
+  (list t "OK"))
+
+
+;;; Glob heroics
+;;;
+(defun eglot--glob-parse (glob)
+  "Compute list of (STATE-SYM EMITTER-FN PATTERN)."
+  (with-temp-buffer
+    (save-excursion (insert glob))
+    (cl-loop
+     with grammar = '((:**      "\\*\\*/?"              eglot--glob-emit-**)
+                      (:*       "\\*"                   eglot--glob-emit-*)
+                      (:?       "\\?"                   eglot--glob-emit-?)
+                      (:{}      "{[^][*{}]+}"           eglot--glob-emit-{})
+                      (:range   "\\[\\^?[^][/,*{}]+\\]" eglot--glob-emit-range)
+                      (:literal "[^][,*?{}]+"           eglot--glob-emit-self))
+     until (eobp)
+     collect (cl-loop
+              for (_token regexp emitter) in grammar
+              thereis (and (re-search-forward (concat "\\=" regexp) nil t)
+                           (list (cl-gensym "state-") emitter (match-string 
0)))
+              finally (error "Glob '%s' invalid at %s" (buffer-string) 
(point))))))
+
+(defun eglot--glob-compile (glob &optional byte-compile noerror)
+  "Convert GLOB into Elisp function.  Maybe BYTE-COMPILE it.
+If NOERROR, return predicate, else erroring function."
+  (let* ((states (eglot--glob-parse glob))
+         (body `(with-current-buffer (get-buffer-create " 
*eglot-glob-matcher*")
+                  (erase-buffer)
+                  (save-excursion (insert string))
+                  (cl-labels ,(cl-loop for (this that) on states
+                                       for (self emit text) = this
+                                       for next = (or (car that) 'eobp)
+                                       collect (funcall emit text self next))
+                    (or (,(caar states))
+                        (error "Glob done but more unmatched text: '%s'"
+                               (buffer-substring (point) (point-max)))))))
+         (form `(lambda (string) ,(if noerror `(ignore-errors ,body) body))))
+    (if byte-compile (byte-compile form) form)))
+
+(defun eglot--glob-emit-self (text self next)
+  `(,self () (re-search-forward ,(concat "\\=" (regexp-quote text))) (,next)))
+
+(defun eglot--glob-emit-** (_ self next)
+  `(,self () (or (ignore-errors (save-excursion (,next)))
+                 (and (re-search-forward "\\=/?[^/]+/?") (,self)))))
+
+(defun eglot--glob-emit-* (_ self next)
+  `(,self () (re-search-forward "\\=[^/]")
+          (or (ignore-errors (save-excursion (,next))) (,self))))
+
+(defun eglot--glob-emit-? (_ self next)
+  `(,self () (re-search-forward "\\=[^/]") (,next)))
+
+(defun eglot--glob-emit-{} (arg self next)
+  (let ((alternatives (split-string (substring arg 1 (1- (length arg))) ",")))
+    `(,self ()
+            (or (re-search-forward ,(concat "\\=" (regexp-opt alternatives)) 
nil t)
+                (error "Failed matching any of %s" ',alternatives))
+            (,next))))
+
+(defun eglot--glob-emit-range (arg self next)
+  (when (eq ?! (aref arg 1)) (aset arg 1 ?^))
+  `(,self () (re-search-forward ,(concat "\\=" arg)) (,next)))
+
+
+;;; List connections mode
+
+(define-derived-mode eglot-list-connections-mode  tabulated-list-mode
+  "" "Eglot mode for listing server connections
+\\{eglot-list-connections-mode-map}"
+  (setq-local tabulated-list-format
+              `[("Language server" 16) ("Project name" 16) ("Modes handled" 
16)])
+  (tabulated-list-init-header))
+
+(defun eglot-list-connections ()
+  "List currently active Eglot connections."
+  (interactive)
+  (with-current-buffer
+      (get-buffer-create "*EGLOT connections*")
+    (let ((inhibit-read-only t))
+      (erase-buffer)
+      (eglot-list-connections-mode)
+      (setq-local tabulated-list-entries
+                  (mapcar
+                   (lambda (server)
+                     (list server
+                           `[,(or (plist-get (eglot--server-info server) :name)
+                                  (jsonrpc-name server))
+                             ,(eglot-project-nickname server)
+                             ,(mapconcat #'symbol-name
+                                         (eglot--major-modes server)
+                                         ", ")]))
+                   (cl-reduce #'append
+                              (hash-table-values eglot--servers-by-project))))
+      (revert-buffer)
+      (pop-to-buffer (current-buffer)))))
+
+
+;;; Hacks
+;;;
+;; FIXME: Although desktop.el compatibility is Emacs bug#56407, the
+;; optimal solution agreed to there is a bit more work than what I
+;; have time to right now.  See
+;; e.g. https://debbugs.gnu.org/cgi/bugreport.cgi?bug=bug%2356407#68.
+;; For now, just use `with-eval-after-load'
+(with-eval-after-load 'desktop
+  (add-to-list 'desktop-minor-mode-handlers '(eglot--managed-mode . ignore)))
+
+
+;;; Obsolete
+;;;
+
+(make-obsolete-variable 'eglot--managed-mode-hook
+                        'eglot-managed-mode-hook "1.6")
+(provide 'eglot)
+
+
+;;; Backend completion
+
+;; Written by Stefan Monnier circa 2016.  Something to move to
+;; minibuffer.el "ASAP" (with all the `eglot--lsp-' replaced by
+;; something else. The very same code already in SLY and stable for a
+;; long time.
+
+;; This "completion style" delegates all the work to the "programmable
+;; completion" table which is then free to implement its own
+;; completion style.  Typically this is used to take advantage of some
+;; external tool which already has its own completion system and
+;; doesn't give you efficient access to the prefix completion needed
+;; by other completion styles.  The table should recognize the symbols
+;; 'eglot--lsp-tryc and 'eglot--lsp-allc as ACTION, reply with
+;; (eglot--lsp-tryc COMP...) or (eglot--lsp-allc . (STRING . POINT)),
+;; accordingly.  tryc/allc names made akward/recognizable on purpose.
+
+(add-to-list 'completion-styles-alist
+             '(eglot--lsp-backend-style
+               eglot--lsp-backend-style-try-completion
+               eglot--lsp-backend-style-all-completions
+               "Ad-hoc completion style provided by the completion table."))
+
+(defun eglot--lsp-backend-style-call (op string table pred point)
+  (when (functionp table)
+    (let ((res (funcall table string pred (cons op point))))
+      (when (eq op (car-safe res))
+        (cdr res)))))
+
+(defun eglot--lsp-backend-style-try-completion (string table pred point)
+  (eglot--lsp-backend-style-call 'eglot--lsp-tryc string table pred point))
+
+(defun eglot--lsp-backend-style-all-completions (string table pred point)
+  (eglot--lsp-backend-style-call 'eglot--lsp-allc string table pred point))
+
+
+;; Local Variables:
+;; bug-reference-bug-regexp: "\\(github#\\([0-9]+\\)\\)"
+;; bug-reference-url-format: "https://github.com/joaotavora/eglot/issues/%s";
+;; checkdoc-force-docstrings-flag: nil
+;; End:
+
+;;; eglot.el ends here
diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el
index 7e7ea6aeb9..537b9484bd 100644
--- a/lisp/progmodes/elisp-mode.el
+++ b/lisp/progmodes/elisp-mode.el
@@ -1826,8 +1826,8 @@ or elsewhere, return a 1-line docstring."
                (eq 'function (aref elisp--eldoc-last-data 2)))
           (aref elisp--eldoc-last-data 1))
          (t
-          (let* ((advertised (gethash (indirect-function sym)
-                                       advertised-signature-table t))
+          (let* ((advertised (get-advertised-calling-convention
+                               (indirect-function sym)))
                   doc
                  (args
                   (cond
diff --git a/lisp/progmodes/etags.el b/lisp/progmodes/etags.el
index 85c5992998..cbdb0994cb 100644
--- a/lisp/progmodes/etags.el
+++ b/lisp/progmodes/etags.el
@@ -1,7 +1,6 @@
 ;;; etags.el --- etags facility for Emacs  -*- lexical-binding: t -*-
 
-;; Copyright (C) 1985-1986, 1988-1989, 1992-1996, 1998, 2000-2022 Free
-;; Software Foundation, Inc.
+;; Copyright (C) 1985-2022 Free Software Foundation, Inc.
 
 ;; Author: Roland McGrath <roland@gnu.org>
 ;; Maintainer: emacs-devel@gnu.org
@@ -2006,16 +2005,15 @@ see the doc of that variable if you want to add names 
to the list."
   (set-buffer-modified-p nil)
   (select-tags-table-mode))
 
-(defvar select-tags-table-mode-map ; Doc string?
-  (let ((map (make-sparse-keymap)))
-    (set-keymap-parent map button-buffer-map)
-    (define-key map "t" 'push-button)
-    (define-key map " " 'next-line)
-    (define-key map "\^?" 'previous-line)
-    (define-key map "n" 'next-line)
-    (define-key map "p" 'previous-line)
-    (define-key map "q" 'select-tags-table-quit)
-    map))
+(defvar-keymap select-tags-table-mode-map
+  :doc "Keymap for `select-tags-table-mode'."
+  :parent button-buffer-map
+  "t"   #'push-button
+  "SPC" #'next-line
+  "DEL" #'previous-line
+  "n"   #'next-line
+  "p"   #'previous-line
+  "q"   #'select-tags-table-quit)
 
 (define-derived-mode select-tags-table-mode special-mode "Select Tags Table"
   "Major mode for choosing a current tags table among those already loaded."
diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el
index 5bbbfa822f..294cf47087 100644
--- a/lisp/progmodes/flymake.el
+++ b/lisp/progmodes/flymake.el
@@ -1360,8 +1360,9 @@ default) no filter is applied."
     flymake-mode-line-warning-counter
     flymake-mode-line-note-counter "]")
   "Mode-line construct for formatting Flymake diagnostic counters.
-This is a suitable place for placing the `flymake-error-counter',
-`flymake-warning-counter' and `flymake-note-counter' constructs.
+This is a suitable place for placing the `flymake-mode-line-error-counter',
+`flymake-mode-line-warning-counter' and `flymake-mode-line-note-counter'
+constructs.
 Separating each of these with space is not necessary."
   :type '(repeat (choice string symbol)))
 
diff --git a/lisp/progmodes/fortran.el b/lisp/progmodes/fortran.el
index 58d7a2026e..9a68c0e6fd 100644
--- a/lisp/progmodes/fortran.el
+++ b/lisp/progmodes/fortran.el
@@ -1,7 +1,6 @@
 ;;; fortran.el --- Fortran mode for GNU Emacs -*- lexical-binding: t -*-
 
-;; Copyright (C) 1986, 1993-1995, 1997-2022 Free Software Foundation,
-;; Inc.
+;; Copyright (C) 1986-2022 Free Software Foundation, Inc.
 
 ;; Author: Michael D. Prange <prange@erl.mit.edu>
 ;; Maintainer: emacs-devel@gnu.org
@@ -624,34 +623,32 @@ Used in the Fortran entry in `hs-special-modes-alist'.")
     st)
   "Syntax table used to parse Fortran expressions for printing in GUD.")
 
-(defvar fortran-mode-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map ";"        'fortran-abbrev-start)
-    (define-key map "\C-c;"    'fortran-comment-region)
-    ;; The default comment-dwim does at least as much as this.
-;;;    (define-key map "\M-;"     'fortran-indent-comment)
-    (define-key map "\M-\n"    'fortran-split-line)
-    (define-key map "\M-\C-n"  'fortran-end-of-block)
-    (define-key map "\M-\C-p"  'fortran-beginning-of-block)
-    (define-key map "\M-\C-q"  'fortran-indent-subprogram)
-    (define-key map "\C-c\C-w" 'fortran-window-create-momentarily)
-    (define-key map "\C-c\C-r" 'fortran-column-ruler)
-    (define-key map "\C-c\C-p" 'fortran-previous-statement)
-    (define-key map "\C-c\C-n" 'fortran-next-statement)
-    (define-key map "\C-c\C-d" 'fortran-join-line) ; like f90
-    (define-key map "\M-^"     'fortran-join-line) ; subvert delete-indentation
-    (define-key map "0" 'fortran-electric-line-number)
-    (define-key map "1" 'fortran-electric-line-number)
-    (define-key map "2" 'fortran-electric-line-number)
-    (define-key map "3" 'fortran-electric-line-number)
-    (define-key map "4" 'fortran-electric-line-number)
-    (define-key map "5" 'fortran-electric-line-number)
-    (define-key map "6" 'fortran-electric-line-number)
-    (define-key map "7" 'fortran-electric-line-number)
-    (define-key map "8" 'fortran-electric-line-number)
-    (define-key map "9" 'fortran-electric-line-number)
-    map)
-  "Keymap used in Fortran mode.")
+(defvar-keymap fortran-mode-map
+  :doc "Keymap used in Fortran mode."
+  ";"        #'fortran-abbrev-start
+  "C-c ;"    #'fortran-comment-region
+  ;; The default comment-dwim does at least as much as this.
+  ;; "M-;"   #'fortran-indent-comment
+  "C-M-j"    #'fortran-split-line
+  "C-M-n"    #'fortran-end-of-block
+  "C-M-p"    #'fortran-beginning-of-block
+  "C-M-q"    #'fortran-indent-subprogram
+  "C-c C-w"  #'fortran-window-create-momentarily
+  "C-c C-r"  #'fortran-column-ruler
+  "C-c C-p"  #'fortran-previous-statement
+  "C-c C-n"  #'fortran-next-statement
+  "C-c C-d"  #'fortran-join-line        ; like f90
+  "M-^"      #'fortran-join-line        ; subvert delete-indentation
+  "0"        #'fortran-electric-line-number
+  "1"        #'fortran-electric-line-number
+  "2"        #'fortran-electric-line-number
+  "3"        #'fortran-electric-line-number
+  "4"        #'fortran-electric-line-number
+  "5"        #'fortran-electric-line-number
+  "6"        #'fortran-electric-line-number
+  "7"        #'fortran-electric-line-number
+  "8"        #'fortran-electric-line-number
+  "9"        #'fortran-electric-line-number)
 
 
 (define-abbrev-table 'fortran-mode-abbrev-table
@@ -960,7 +957,7 @@ With non-nil ARG, uncomments the region."
     (set-marker save-point nil)))
 
 ;; uncomment-region calls this with 3 args.
-(defun fortran-uncomment-region (start end &optional ignored)
+(defun fortran-uncomment-region (start end &optional _ignored)
   "Uncomment every line in the region."
   (fortran-comment-region start end t))
 
diff --git a/lisp/progmodes/gdb-mi.el b/lisp/progmodes/gdb-mi.el
index 0de3d213a4..dff677e785 100644
--- a/lisp/progmodes/gdb-mi.el
+++ b/lisp/progmodes/gdb-mi.el
@@ -4359,7 +4359,7 @@ member."
   "Mapping of local variable names to a string with their value.")
 
 (defun gdb-locals-values-handler-custom ()
-  "Store the values of local variables in `gdb-locals-value-map'."
+  "Store the values of local variables in `gdb-locals-values-table'."
   (let ((locals-list (bindat-get-field (gdb-mi--partial-output) 'variables)))
     (dolist (local locals-list)
       (let ((name (bindat-get-field local 'name))
diff --git a/lisp/progmodes/gud.el b/lisp/progmodes/gud.el
index 281762fb0a..9b7d7a0535 100644
--- a/lisp/progmodes/gud.el
+++ b/lisp/progmodes/gud.el
@@ -752,7 +752,12 @@ It should return a list of completion strings.")
 If COMMAND-LINE names a program FILE to debug, gdb will run in
 a buffer named *gud-FILE*, and the directory containing FILE
 becomes the initial working directory and source-file directory
-for your debugger.
+for your debugger.  If you don't want `default-directory' to
+change to the directory of FILE, specify FILE without leading
+directories, in which case FILE should reside either in the
+directory of the buffer from which this command is invoked, or
+it can be found by searching PATH.
+
 If COMMAND-LINE requests that gdb attaches to a process PID, gdb
 will run in *gud-PID*, otherwise it will run in *gud*; in these
 cases the initial working directory is the `default-directory' of
diff --git a/lisp/progmodes/hideshow.el b/lisp/progmodes/hideshow.el
index 6de079f05a..00507a3c1a 100644
--- a/lisp/progmodes/hideshow.el
+++ b/lisp/progmodes/hideshow.el
@@ -31,14 +31,14 @@
 ;; are available, implementing block hiding and showing.  They (and their
 ;; keybindings) are:
 ;;
-;;   hs-hide-block                      C-c @ C-h
-;;   hs-show-block                      C-c @ C-s
-;;   hs-hide-all                        C-c @ C-M-h
-;;   hs-show-all                        C-c @ C-M-s
-;;   hs-hide-level                      C-c @ C-l
-;;   hs-toggle-hiding                   C-c @ C-c
-;;   hs-toggle-hiding                   [(shift mouse-2)]
-;;   hs-hide-initial-comment-block
+;;   `hs-hide-block'                      C-c @ C-h
+;;   `hs-show-block'                      C-c @ C-s
+;;   `hs-hide-all'                        C-c @ C-M-h
+;;   `hs-show-all'                        C-c @ C-M-s
+;;   `hs-hide-level'                      C-c @ C-l
+;;   `hs-toggle-hiding'                   C-c @ C-c
+;;   `hs-toggle-hiding'                   S-<mouse-2>
+;;   `hs-hide-initial-comment-block'
 ;;
 ;; Blocks are defined per mode.  In c-mode, c++-mode and java-mode, they
 ;; are simply text between curly braces, while in Lisp-ish modes parens
@@ -50,16 +50,14 @@
 
 ;; * Suggested usage
 ;;
-;; First make sure hideshow.el is in a directory in your `load-path'.
-;; You can optionally byte-compile it using `M-x byte-compile-file'.
-;; Then, add the following to your init file:
+;; Add the following to your init file:
 ;;
-;; (load-library "hideshow")
-;; (add-hook 'X-mode-hook #'hs-minor-mode)           ; other modes similarly
+;;     (require 'hideshow)
+;;     (add-hook 'X-mode-hook #'hs-minor-mode)       ; other modes similarly
 ;;
 ;; where X = {emacs-lisp,c,c++,perl,...}.  You can also manually toggle
 ;; hideshow minor mode by typing `M-x hs-minor-mode'.  After hideshow is
-;; activated or deactivated, `hs-minor-mode-hook' is run w/ `run-hooks'.
+;; activated or deactivated, `hs-minor-mode-hook' is run with `run-hooks'.
 ;;
 ;; Additionally, Joseph Eydelnant writes:
 ;;   I enjoy your package hideshow.el Version 5.24 2001/02/13
@@ -67,14 +65,14 @@
 ;;   toggle hide/show all with a single key.
 ;;   Here are a few lines of code that lets me do just that.
 ;;
-;;   (defvar my-hs-hide nil "Current state of hideshow for toggling all.")
-;;   ;;;###autoload
-;;   (defun my-toggle-hideshow-all () "Toggle hideshow all."
-;;     (interactive)
-;;     (setq my-hs-hide (not my-hs-hide))
-;;     (if my-hs-hide
-;;         (hs-hide-all)
-;;       (hs-show-all)))
+;;     (defvar my-hs-hide nil "Current state of hideshow for toggling all.")
+;;     ;;;###autoload
+;;     (defun my-toggle-hideshow-all () "Toggle hideshow all."
+;;       (interactive)
+;;       (setq my-hs-hide (not my-hs-hide))
+;;       (if my-hs-hide
+;;           (hs-hide-all)
+;;         (hs-show-all)))
 ;;
 ;; [Your hideshow hacks here!]
 
@@ -82,12 +80,12 @@
 ;;
 ;; You can use `M-x customize-variable' on the following variables:
 ;;
-;; - hs-hide-comments-when-hiding-all -- self-explanatory!
-;; - hs-hide-all-non-comment-function -- if non-nil, when doing a
-;;                                       `hs-hide-all', this function
-;;                                       is called w/ no arguments
-;; - hs-isearch-open                  -- what kind of hidden blocks to
-;;                                       open when doing isearch
+;; - `hs-hide-comments-when-hiding-all' -- self-explanatory!
+;; - `hs-hide-all-non-comment-function' -- if non-nil, when doing a
+;;                                         `hs-hide-all', this function
+;;                                         is called with no arguments
+;; - `hs-isearch-open'                  -- what kind of hidden blocks to
+;;                                         open when doing isearch
 ;;
 ;; Some languages (e.g., Java) are deeply nested, so the normal behavior
 ;; of `hs-hide-all' (hiding all but top-level blocks) results in very
@@ -96,21 +94,21 @@
 ;; what is more useful.  For example, the following code shows the next
 ;; nested level in addition to the top-level:
 ;;
-;;   (defun ttn-hs-hide-level-1 ()
-;;     (when (hs-looking-at-block-start-p)
-;;       (hs-hide-level 1))
-;;     (forward-sexp 1))
-;;   (setq hs-hide-all-non-comment-function 'ttn-hs-hide-level-1)
+;;     (defun ttn-hs-hide-level-1 ()
+;;       (when (hs-looking-at-block-start-p)
+;;         (hs-hide-level 1))
+;;       (forward-sexp 1))
+;;     (setq hs-hide-all-non-comment-function 'ttn-hs-hide-level-1)
 ;;
-;; Hideshow works w/ incremental search (isearch) by setting the variable
+;; Hideshow works with incremental search (isearch) by setting the variable
 ;; `hs-headline', which is the line of text at the beginning of a hidden
 ;; block that contains a match for the search.  You can have this show up
 ;; in the mode line by modifying the variable `mode-line-format'.  For
 ;; example, the following code prepends this info to the mode line:
 ;;
-;;   (unless (memq 'hs-headline mode-line-format)
-;;     (setq mode-line-format
-;;           (append '("-" hs-headline) mode-line-format)))
+;;     (unless (memq 'hs-headline mode-line-format)
+;;       (setq mode-line-format
+;;             (append '("-" hs-headline) mode-line-format)))
 ;;
 ;; See documentation for `mode-line-format' for more info.
 ;;
@@ -121,8 +119,8 @@
 ;;
 ;; One of `hs-hide-hook' or `hs-show-hook' is run for the toggling
 ;; commands when the result of the toggle is to hide or show blocks,
-;; respectively.  All hooks are run w/ `run-hooks'.  See docs for each
-;; variable or hook for more info.
+;; respectively.  All hooks are run with `run-hooks'.  See the
+;; documentation for each variable or hook for more information.
 ;;
 ;; Normally, hideshow tries to determine appropriate values for block
 ;; and comment definitions by examining the buffer's major mode.  If
@@ -348,22 +346,20 @@ info node `(elisp)Overlays'."
   "Non-nil if using hideshow mode as a minor mode of some other mode.
 Use the command `hs-minor-mode' to toggle or set this variable.")
 
-(defvar hs-minor-mode-map
-  (let ((map (make-sparse-keymap)))
-    ;; These bindings roughly imitate those used by Outline mode.
-    (define-key map "\C-c@\C-h"              #'hs-hide-block)
-    (define-key map "\C-c@\C-s"              #'hs-show-block)
-    (define-key map "\C-c@\C-\M-h"    #'hs-hide-all)
-    (define-key map "\C-c@\C-\M-s"    #'hs-show-all)
-    (define-key map "\C-c@\C-l"              #'hs-hide-level)
-    (define-key map "\C-c@\C-c"              #'hs-toggle-hiding)
-    (define-key map "\C-c@\C-a"       #'hs-show-all)
-    (define-key map "\C-c@\C-t"       #'hs-hide-all)
-    (define-key map "\C-c@\C-d"       #'hs-hide-block)
-    (define-key map "\C-c@\C-e"       #'hs-toggle-hiding)
-    (define-key map [(shift mouse-2)] #'hs-toggle-hiding)
-    map)
-  "Keymap for hideshow minor mode.")
+(defvar-keymap hs-minor-mode-map
+  :doc "Keymap for hideshow minor mode."
+  ;; These bindings roughly imitate those used by Outline mode.
+  "C-c @ C-h"   #'hs-hide-block
+  "C-c @ C-s"   #'hs-show-block
+  "C-c @ C-M-h" #'hs-hide-all
+  "C-c @ C-M-s" #'hs-show-all
+  "C-c @ C-l"   #'hs-hide-level
+  "C-c @ C-c"   #'hs-toggle-hiding
+  "C-c @ C-a"   #'hs-show-all
+  "C-c @ C-t"   #'hs-hide-all
+  "C-c @ C-d"   #'hs-hide-block
+  "C-c @ C-e"   #'hs-toggle-hiding
+  "S-<mouse-2>" #'hs-toggle-hiding)
 
 (easy-menu-define hs-minor-mode-menu hs-minor-mode-map
   "Menu used when hideshow minor mode is active."
diff --git a/lisp/progmodes/make-mode.el b/lisp/progmodes/make-mode.el
index 5f26521299..4cee361df3 100644
--- a/lisp/progmodes/make-mode.el
+++ b/lisp/progmodes/make-mode.el
@@ -1,6 +1,6 @@
 ;;; make-mode.el --- makefile editing commands for Emacs -*- lexical-binding:t 
-*-
 
-;; Copyright (C) 1992, 1994, 1999-2022 Free Software Foundation, Inc.
+;; Copyright (C) 1992-2022 Free Software Foundation, Inc.
 
 ;; Author: Thomas Neumann <tom@smart.bo.open.de>
 ;;     Eric S. Raymond <esr@snark.thyrsus.com>
@@ -37,7 +37,7 @@
 ;;
 ;; The command C-c C-f adds certain filenames in the current directory
 ;; as targets.  You can filter out filenames by setting the variable
-;; makefile-ignored-files-in-pickup-regex.
+;; `makefile-ignored-files-in-pickup-regex'.
 ;;
 ;; The command C-c C-u grinds for a bit, then pops up a report buffer
 ;; showing which target names are up-to-date with respect to their
@@ -49,11 +49,14 @@
 ;; all marked items back in the Makefile with C-c TAB.
 ;;
 ;; The command C-c TAB in the makefile buffer inserts a GNU make builtin.
-;; You will be prompted for the builtin's args.
+;; You will be prompted for the builtin's arguments.
 ;;
 ;; There are numerous other customization variables.
 
-;;
+
+
+;;; Code:
+
 ;; To Do:
 ;;
 ;; * Add missing doc strings, improve terse doc strings.
@@ -65,7 +68,7 @@
 ;;   indentation and slashification done automatically.  Hard.
 ;; * Consider removing browser mode.  It seems useless.
 ;; * ":" should notice when a new target is made and add it to the
-;;   list (or at least set makefile-need-target-pickup).
+;;   list (or at least set `makefile-need-target-pickup').
 ;; * Make browser into a major mode.
 ;; * Clean up macro insertion stuff.  It is a mess.
 ;; * Browser entry and exit is weird.  Normalize.
@@ -78,15 +81,7 @@
 ;; * Update texinfo manual.
 ;; * Update files.el.
 
-
-
-;;; Code:
-
-;; Sadly we need this for a macro.
-(eval-when-compile
-  (require 'imenu)
-  (require 'dabbrev)
-  (require 'add-log))
+(require 'subr-x) ; `string-limit'
 
 ;;; ------------------------------------------------------------
 ;;; Configurable stuff
@@ -541,7 +536,7 @@ This should identify a `make' command that can handle the 
`-q' option."
   'makefile-query-one-target-method-function "29.1")
 
 (defcustom makefile-query-one-target-method-function
-  'makefile-query-by-make-minus-q
+  #'makefile-query-by-make-minus-q
   "Function to call to determine whether a make target is up to date.
 The function must satisfy this calling convention:
 
@@ -566,34 +561,31 @@ The function must satisfy this calling convention:
 (define-abbrev-table 'makefile-mode-abbrev-table ()
   "Abbrev table in use in Makefile buffers.")
 
-(defvar makefile-mode-map
-  (let ((map (make-sparse-keymap)))
-    ;; set up the keymap
-    (define-key map "\C-c:" 'makefile-insert-target-ref)
-    (if makefile-electric-keys
-       (progn
-         (define-key map "$" 'makefile-insert-macro-ref)
-         (define-key map ":" 'makefile-electric-colon)
-         (define-key map "=" 'makefile-electric-equal)
-         (define-key map "." 'makefile-electric-dot)))
-    (define-key map "\C-c\C-f" 'makefile-pickup-filenames-as-targets)
-    (define-key map "\C-c\C-b" 'makefile-switch-to-browser)
-    (define-key map "\C-c\C-c" 'comment-region)
-    (define-key map "\C-c\C-p" 'makefile-pickup-everything)
-    (define-key map "\C-c\C-u" 'makefile-create-up-to-date-overview)
-    (define-key map "\C-c\C-i" 'makefile-insert-gmake-function)
-    (define-key map "\C-c\C-\\" 'makefile-backslash-region)
-    (define-key map "\C-c\C-m\C-a" 'makefile-automake-mode)
-    (define-key map "\C-c\C-m\C-b" 'makefile-bsdmake-mode)
-    (define-key map "\C-c\C-m\C-g" 'makefile-gmake-mode)
-    (define-key map "\C-c\C-m\C-i" 'makefile-imake-mode)
-    (define-key map "\C-c\C-m\C-m" 'makefile-mode)
-    (define-key map "\C-c\C-m\C-p" 'makefile-makepp-mode)
-    (define-key map "\M-p"     'makefile-previous-dependency)
-    (define-key map "\M-n"     'makefile-next-dependency)
-    (define-key map "\e\t"     'completion-at-point)
-    map)
-  "The keymap that is used in Makefile mode.")
+(defvar-keymap makefile-mode-map
+  :doc "The keymap that is used in Makefile mode."
+  "C-c :"       #'makefile-insert-target-ref
+  "C-c C-f"     #'makefile-pickup-filenames-as-targets
+  "C-c C-b"     #'makefile-switch-to-browser
+  "C-c C-c"     #'comment-region
+  "C-c C-p"     #'makefile-pickup-everything
+  "C-c C-u"     #'makefile-create-up-to-date-overview
+  "C-c TAB"     #'makefile-insert-gmake-function
+  "C-c C-\\"    #'makefile-backslash-region
+  "C-c RET C-a" #'makefile-automake-mode
+  "C-c RET C-b" #'makefile-bsdmake-mode
+  "C-c RET C-g" #'makefile-gmake-mode
+  "C-c RET TAB" #'makefile-imake-mode
+  "C-c RET RET" #'makefile-mode
+  "C-c RET C-p" #'makefile-makepp-mode
+  "M-p"         #'makefile-previous-dependency
+  "M-n"         #'makefile-next-dependency
+  "C-M-i"       #'completion-at-point)
+
+(when makefile-electric-keys
+  (define-key makefile-mode-map "$" #'makefile-insert-macro-ref)
+  (define-key makefile-mode-map ":" #'makefile-electric-colon)
+  (define-key makefile-mode-map "=" #'makefile-electric-equal)
+  (define-key makefile-mode-map "." #'makefile-electric-dot))
 
 (easy-menu-define makefile-mode-menu makefile-mode-map
   "Menu for Makefile mode."
@@ -649,22 +641,20 @@ The function must satisfy this calling convention:
       :selected (eq major-mode 'makefile-makepp-mode)])))
 
 
-(defvar makefile-browser-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map "n"    'makefile-browser-next-line)
-    (define-key map "\C-n" 'makefile-browser-next-line)
-    (define-key map "p"    'makefile-browser-previous-line)
-    (define-key map "\C-p" 'makefile-browser-previous-line)
-    (define-key map " "    'makefile-browser-toggle)
-    (define-key map "i"    'makefile-browser-insert-selection)
-    (define-key map "I"    'makefile-browser-insert-selection-and-quit)
-    (define-key map "\C-c\C-m" 'makefile-browser-insert-continuation)
-    (define-key map "q"    'makefile-browser-quit)
-    ;; disable horizontal movement
-    (define-key map "\C-b" 'undefined)
-    (define-key map "\C-f" 'undefined)
-    map)
-  "The keymap that is used in the macro- and target browser.")
+(defvar-keymap makefile-browser-map
+  :doc "The keymap that is used in the macro- and target browser."
+  "n"       #'makefile-browser-next-line
+  "C-n"     #'makefile-browser-next-line
+  "p"       #'makefile-browser-previous-line
+  "C-p"     #'makefile-browser-previous-line
+  "SPC"     #'makefile-browser-toggle
+  "i"       #'makefile-browser-insert-selection
+  "I"       #'makefile-browser-insert-selection-and-quit
+  "C-c RET" #'makefile-browser-insert-continuation
+  "q"       #'makefile-browser-quit
+  ;; disable horizontal movement
+  "C-b"     #'undefined
+  "C-f"     #'undefined)
 
 
 (defvar makefile-mode-syntax-table
@@ -1001,15 +991,15 @@ Anywhere else just self-inserts."
     (self-insert-command arg)))
 
 (defun makefile-insert-macro (macro-name)
-  "Prepare definition of a new macro."
+  "Prepare definition of a new macro named MACRO-NAME.
+Interactively, prompt for the name of the macro."
   (interactive "sMacro Name: ")
   (makefile-pickup-macros)
-  (if (not (zerop (length macro-name)))
-      (progn
-       (beginning-of-line)
-       (insert macro-name makefile-macro-assign)
-       (setq makefile-need-macro-pickup t)
-       (makefile-remember-macro macro-name))))
+  (unless (zerop (length macro-name))
+    (beginning-of-line)
+    (insert macro-name makefile-macro-assign)
+    (setq makefile-need-macro-pickup t)
+    (makefile-remember-macro macro-name)))
 
 (defun makefile-insert-macro-ref (macro-name)
   "Complete on a list of known macros, then insert complete ref at point."
@@ -1023,24 +1013,23 @@ Anywhere else just self-inserts."
 (defun makefile-insert-target (target-name)
   "Prepare definition of a new target (dependency line)."
   (interactive "sTarget: ")
-  (if (not (zerop (length target-name)))
-      (progn
-       (beginning-of-line)
-       (insert target-name makefile-target-colon)
-       (makefile-forward-after-target-colon)
-       (end-of-line)
-       (setq makefile-need-target-pickup t)
-       (makefile-remember-target target-name))))
+  (unless (zerop (length target-name))
+    (beginning-of-line)
+    (insert target-name makefile-target-colon)
+    (makefile-forward-after-target-colon)
+    (end-of-line)
+    (setq makefile-need-target-pickup t)
+    (makefile-remember-target target-name)))
 
 (defun makefile-insert-target-ref (target-name)
   "Complete on a list of known targets, then insert TARGET-NAME at point."
   (interactive
    (list
     (progn
-     (makefile-pickup-targets)
-     (completing-read "Refer to target: " makefile-target-table nil nil nil))))
-   (if (not (zerop (length target-name)))
-       (insert target-name " ")))
+      (makefile-pickup-targets)
+      (completing-read "Refer to target: " makefile-target-table nil nil 
nil))))
+  (unless (zerop (length target-name))
+    (insert target-name " ")))
 
 (defun makefile-electric-colon (arg)
   "Prompt for name of new target.
@@ -1081,7 +1070,7 @@ Anywhere else just self-inserts."
                 (skip-chars-forward " \t")
                 (not (or (eolp) (eq (char-after) ?:)))))
        (forward-line)))
-    (message "Read targets OK.")))
+    (message "Read targets OK")))
 
 (defun makefile-pickup-macros ()
   "Notice names of all macro definitions in Makefile."
@@ -1101,11 +1090,11 @@ Anywhere else just self-inserts."
              (message "Picked up macro \"%s\" from line %d"
                       macro-name (line-number-at-pos))))
        (forward-line)))
-    (message "Read macros OK.")))
+    (message "Read macros OK")))
 
 (defun makefile-pickup-everything (arg)
   "Notice names of all macros and targets in Makefile.
-Prefix arg means force pickups to be redone."
+Prefix argument ARG means force pickups to be redone."
   (interactive "P")
   (if arg
       (setq makefile-need-target-pickup t
@@ -1173,8 +1162,8 @@ and adds all qualifying names to the list of known 
targets."
 
 (defun makefile-backslash-region (from to delete-flag)
   "Insert, align, or delete end-of-line backslashes on the lines in the region.
-With no argument, inserts backslashes and aligns existing backslashes.
-With an argument, deletes the backslashes.
+With no argument, insert backslashes and align existing backslashes.
+With an argument, delete the backslashes.
 
 This function does not modify the last line of the region if the region ends
 right at the start of the following line; it does not modify blank lines
@@ -1189,9 +1178,9 @@ definition and conveniently use this command."
       (when (and makefile-backslash-align (not delete-flag))
         (while (< (point) to)
           (end-of-line)
-          (if (= (preceding-char) ?\\)
-              (progn (forward-char -1)
-                     (skip-chars-backward " \t")))
+          (when (= (preceding-char) ?\\)
+            (forward-char -1)
+            (skip-chars-backward " \t"))
           (setq column (max column (1+ (current-column))))
          (forward-line 1))
         ;; Adjust upward to a tab column, if that doesn't push
@@ -1358,18 +1347,16 @@ Fill comments, backslashed lines, and variable 
definitions specially."
 (defun makefile-browser-next-line ()
   "Move the browser selection cursor to the next line."
   (interactive)
-  (if (not (makefile-last-line-p))
-      (progn
-       (forward-line 1)
-       (forward-char makefile-browser-cursor-column))))
+  (unless (makefile-last-line-p)
+    (forward-line 1)
+    (forward-char makefile-browser-cursor-column)))
 
 (defun makefile-browser-previous-line ()
   "Move the browser selection cursor to the previous line."
   (interactive)
-  (if (not (makefile-first-line-p))
-      (progn
-       (forward-line -1)
-       (forward-char makefile-browser-cursor-column))))
+  (unless (makefile-first-line-p)
+    (forward-line -1)
+    (forward-char makefile-browser-cursor-column)))
 
 ;;;
 ;;; Quitting the browser (returns to client buffer)
@@ -1469,14 +1456,17 @@ Insertion takes place at point."
   (if (zerop (+ (length targets) (length macros)))
       (progn
        (beep)
-       (message "No macros or targets to browse! Consider running 
`makefile-pickup-everything'"))
+        (message
+         (substitute-command-keys
+          (concat "No macros or targets to browse!  "
+                  "Consider running \\[makefile-pickup-everything]"))))
     (let ((browser-buffer (get-buffer-create makefile-browser-buffer-name)))
-       (pop-to-buffer browser-buffer)
-       (makefile-browser-fill targets macros)
-       (shrink-window-if-larger-than-buffer)
-       (setq-local makefile-browser-selection-vector
-                   (make-vector (+ (length targets) (length macros)) nil))
-       (makefile-browser-start-interaction))))
+      (pop-to-buffer browser-buffer)
+      (makefile-browser-fill targets macros)
+      (shrink-window-if-larger-than-buffer)
+      (setq-local makefile-browser-selection-vector
+                  (make-vector (+ (length targets) (length macros)) nil))
+      (makefile-browser-start-interaction))))
 
 (defun makefile-switch-to-browser ()
   (interactive)
@@ -1504,60 +1494,44 @@ dependency in the makefile."
       ;; writing the current contents of the makefile buffer.
       ;;
       (let ((saved-target-table makefile-target-table)
-           (this-buffer (current-buffer))
-           (makefile-up-to-date-buffer
-            (get-buffer-create makefile-up-to-date-buffer-name))
-           (filename (makefile-save-temporary))
-           ;;
-           ;; Forget the target table because it may contain picked-up 
filenames
-           ;; that are not really targets in the current makefile.
-           ;; We don't want to query these, so get a new target-table with 
just the
-           ;; targets that can be found in the makefile buffer.
-           ;; The 'old' target table will be restored later.
-           ;;
-           (real-targets (progn
-                           (makefile-pickup-targets)
-                           makefile-target-table))
-           (prereqs makefile-has-prereqs)
-           )
-
-       (set-buffer makefile-up-to-date-buffer)
-       (setq buffer-read-only nil)
-       (erase-buffer)
-       (makefile-query-targets filename real-targets prereqs)
-       (if (zerop (buffer-size))               ; if it did not get us anything
-           (progn
-             (kill-buffer (current-buffer))
-             (message "No overview created!")))
-       (set-buffer this-buffer)
-       (setq makefile-target-table saved-target-table)
-       (if (get-buffer makefile-up-to-date-buffer-name)
-           (progn
-             (pop-to-buffer (get-buffer makefile-up-to-date-buffer-name))
-             (shrink-window-if-larger-than-buffer)
-             (sort-lines nil (point-min) (point-max))
-             (setq buffer-read-only t))))))
+            (this-buffer (current-buffer))
+            (makefile-up-to-date-buffer
+             (get-buffer-create makefile-up-to-date-buffer-name))
+            (filename (makefile-save-temporary))
+            ;;
+            ;; Forget the target table because it may contain picked-up 
filenames
+            ;; that are not really targets in the current makefile.
+            ;; We don't want to query these, so get a new target-table with 
just the
+            ;; targets that can be found in the makefile buffer.
+            ;; The 'old' target table will be restored later.
+            ;;
+            (real-targets (progn
+                            (makefile-pickup-targets)
+                            makefile-target-table))
+            (prereqs makefile-has-prereqs))
+        (unwind-protect
+            (progn
+              (set-buffer makefile-up-to-date-buffer)
+              (setq buffer-read-only nil)
+              (erase-buffer)
+              (makefile-query-targets filename real-targets prereqs)
+              (when (zerop (buffer-size))     ; if it did not get us anything
+                (kill-buffer (current-buffer))
+                (message "No overview created!"))
+              (set-buffer this-buffer)
+              (setq makefile-target-table saved-target-table)
+              (when (get-buffer makefile-up-to-date-buffer-name)
+                (pop-to-buffer (get-buffer makefile-up-to-date-buffer-name))
+                (shrink-window-if-larger-than-buffer)
+                (sort-lines nil (point-min) (point-max))
+                (setq buffer-read-only t)))
+          (ignore-errors (delete-file filename))))))
 
 (defun makefile-save-temporary ()
   "Create a temporary file from the current makefile buffer."
-  (let ((filename (makefile-generate-temporary-filename)))
+  (let ((filename (make-temp-name "mktmp.")))
     (write-region (point-min) (point-max) filename nil 0)
-    filename))                         ; return the filename
-
-(defun makefile-generate-temporary-filename ()
-  "Create a filename suitable for use in `makefile-save-temporary'.
-Be careful to allow brain-dead file systems (DOS, SYSV ...) to cope
-with the generated name!"
-  (let ((my-name (user-login-name))
-       (my-uid (int-to-string (user-uid))))
-    (concat "mktmp"
-         (if (> (length my-name) 3)
-             (substring my-name 0 3)
-           my-name)
-         "."
-         (if (> (length my-uid) 3)
-             (substring my-uid 0 3)
-           my-uid))))
+    filename))
 
 (defun makefile-query-targets (filename target-table prereq-list)
   "Fill the up-to-date overview buffer.
@@ -1728,14 +1702,13 @@ matched in a rule action."
 
 (defun makefile-remember-target (target-name &optional has-prereqs)
   "Remember a given target if it is not already remembered for this buffer."
-  (if (not (zerop (length target-name)))
-      (progn
-      (if (not (assoc target-name makefile-target-table))
-         (setq makefile-target-table
-               (cons (list target-name) makefile-target-table)))
-      (if has-prereqs
-         (setq makefile-has-prereqs
-               (cons target-name makefile-has-prereqs))))))
+  (unless (zerop (length target-name))
+    (if (not (assoc target-name makefile-target-table))
+        (setq makefile-target-table
+              (cons (list target-name) makefile-target-table)))
+    (if has-prereqs
+        (setq makefile-has-prereqs
+              (cons target-name makefile-has-prereqs)))))
 
 (defun makefile-remember-macro (macro-name)
   "Remember a given macro if it is not already remembered for this buffer."
@@ -1824,6 +1797,13 @@ If it isn't in one, return nil."
        (setq found (replace-regexp-in-string "\\`[ \t]+" "" found)))
       found)))
 
+(defun makefile-generate-temporary-filename ()
+  "Create a filename suitable for use in `makefile-save-temporary'."
+  (declare (obsolete make-temp-name "29.1"))
+  (format "mktmp%s.%s"
+          (string-limit (user-login-name) 3)
+          (string-limit (int-to-string (user-uid)) 3)))
+
 (provide 'make-mode)
 
 ;;; make-mode.el ends here
diff --git a/lisp/progmodes/mixal-mode.el b/lisp/progmodes/mixal-mode.el
index 9d1ceaa55a..358b347f6e 100644
--- a/lisp/progmodes/mixal-mode.el
+++ b/lisp/progmodes/mixal-mode.el
@@ -33,9 +33,6 @@
 ;; GNU MDK from `https://savannah.gnu.org/projects/mdk/' and
 ;; `https://ftp.gnu.org/pub/gnu/mdk'.
 ;;
-;; To use this mode, place the following in your init file:
-;; `(load-file "/PATH-TO-FILE/mixal-mode.el")'.
-;;
 ;; When you load a file with the extension .mixal the mode will be started
 ;; automatically.  If you want to start the mode manually, use `M-x 
mixal-mode'.
 ;; Font locking will work, the behavior of tabs is the same as Emacs's
diff --git a/lisp/progmodes/modula2.el b/lisp/progmodes/modula2.el
index e668570ba1..09cb848fd5 100644
--- a/lisp/progmodes/modula2.el
+++ b/lisp/progmodes/modula2.el
@@ -65,39 +65,36 @@
   "Column for aligning the end of a comment, in Modula-2."
   :type 'integer)
 
-;;; Added by TEP
-(defvar m2-mode-map
-  (let ((map (make-sparse-keymap)))
-    ;; FIXME: Many of those bindings are contrary to coding conventions.
-    (define-key map "\C-cb" #'m2-begin)
-    (define-key map "\C-cc" #'m2-case)
-    (define-key map "\C-cd" #'m2-definition)
-    (define-key map "\C-ce" #'m2-else)
-    (define-key map "\C-cf" #'m2-for)
-    (define-key map "\C-ch" #'m2-header)
-    (define-key map "\C-ci" #'m2-if)
-    (define-key map "\C-cm" #'m2-module)
-    (define-key map "\C-cl" #'m2-loop)
-    (define-key map "\C-co" #'m2-or)
-    (define-key map "\C-cp" #'m2-procedure)
-    (define-key map "\C-c\C-w" #'m2-with)
-    (define-key map "\C-cr" #'m2-record)
-    (define-key map "\C-cs" #'m2-stdio)
-    (define-key map "\C-ct" #'m2-type)
-    (define-key map "\C-cu" #'m2-until)
-    (define-key map "\C-cv" #'m2-var)
-    (define-key map "\C-cw" #'m2-while)
-    (define-key map "\C-cx" #'m2-export)
-    (define-key map "\C-cy" #'m2-import)
-    (define-key map "\C-c{" #'m2-begin-comment)
-    (define-key map "\C-c}" #'m2-end-comment)
-    (define-key map "\C-c\C-z" #'suspend-emacs)
-    (define-key map "\C-c\C-v" #'m2-visit)
-    (define-key map "\C-c\C-t" #'m2-toggle)
-    (define-key map "\C-c\C-l" #'m2-link)
-    (define-key map "\C-c\C-c" #'m2-compile)
-    map)
-  "Keymap used in Modula-2 mode.")
+(defvar-keymap m2-mode-map
+  :doc "Keymap used in Modula-2 mode."
+  ;; FIXME: Many of those bindings are contrary to coding conventions.
+  "C-c b"   #'m2-begin
+  "C-c c"   #'m2-case
+  "C-c d"   #'m2-definition
+  "C-c e"   #'m2-else
+  "C-c f"   #'m2-for
+  "C-c h"   #'m2-header
+  "C-c i"   #'m2-if
+  "C-c m"   #'m2-module
+  "C-c l"   #'m2-loop
+  "C-c o"   #'m2-or
+  "C-c p"   #'m2-procedure
+  "C-c C-w" #'m2-with
+  "C-c r"   #'m2-record
+  "C-c s"   #'m2-stdio
+  "C-c t"   #'m2-type
+  "C-c u"   #'m2-until
+  "C-c v"   #'m2-var
+  "C-c w"   #'m2-while
+  "C-c x"   #'m2-export
+  "C-c y"   #'m2-import
+  "C-c {"   #'m2-begin-comment
+  "C-c }"   #'m2-end-comment
+  "C-c C-z" #'suspend-emacs
+  "C-c C-v" #'m2-visit
+  "C-c C-t" #'m2-toggle
+  "C-c C-l" #'m2-link
+  "C-c C-c" #'m2-compile)
 
 (defcustom m2-indent 5
   "This variable gives the indentation in Modula-2 mode."
diff --git a/lisp/progmodes/octave.el b/lisp/progmodes/octave.el
index 18b9899169..bce5bc3ba7 100644
--- a/lisp/progmodes/octave.el
+++ b/lisp/progmodes/octave.el
@@ -1,6 +1,6 @@
 ;;; octave.el --- editing octave source files under emacs  -*- 
lexical-binding: t; -*-
 
-;; Copyright (C) 1997, 2001-2022 Free Software Foundation, Inc.
+;; Copyright (C) 1997-2022 Free Software Foundation, Inc.
 
 ;; Author: Kurt Hornik <Kurt.Hornik@wu-wien.ac.at>
 ;;        John Eaton <jwe@octave.org>
@@ -65,43 +65,39 @@ The string `function' and its name are given by the first 
and third
 parenthetical grouping.")
 
 
-(defvar octave-mode-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map "\M-."     'octave-find-definition)
-    (define-key map "\M-\C-j"  'octave-indent-new-comment-line)
-    (define-key map "\C-c\C-p" 'octave-previous-code-line)
-    (define-key map "\C-c\C-n" 'octave-next-code-line)
-    (define-key map "\C-c\C-a" 'octave-beginning-of-line)
-    (define-key map "\C-c\C-e" 'octave-end-of-line)
-    (define-key map [remap down-list] 'smie-down-list)
-    (define-key map "\C-c\M-\C-h" 'octave-mark-block)
-    (define-key map "\C-c]" 'smie-close-block)
-    (define-key map "\C-c/" 'smie-close-block)
-    (define-key map "\C-c;" 'octave-update-function-file-comment)
-    (define-key map "\C-hd" 'octave-help)
-    (define-key map "\C-ha" 'octave-lookfor)
-    (define-key map "\C-c\C-l" 'octave-source-file)
-    (define-key map "\C-c\C-f" 'octave-insert-defun)
-    (define-key map "\C-c\C-il" 'octave-send-line)
-    (define-key map "\C-c\C-ib" 'octave-send-block)
-    (define-key map "\C-c\C-if" 'octave-send-defun)
-    (define-key map "\C-c\C-ir" 'octave-send-region)
-    (define-key map "\C-c\C-ia" 'octave-send-buffer)
-    (define-key map "\C-c\C-is" 'octave-show-process-buffer)
-    (define-key map "\C-c\C-iq" 'octave-hide-process-buffer)
-    (define-key map "\C-c\C-ik" 'octave-kill-process)
-    (define-key map "\C-c\C-i\C-l" 'octave-send-line)
-    (define-key map "\C-c\C-i\C-b" 'octave-send-block)
-    (define-key map "\C-c\C-i\C-f" 'octave-send-defun)
-    (define-key map "\C-c\C-i\C-r" 'octave-send-region)
-    (define-key map "\C-c\C-i\C-a" 'octave-send-buffer)
-    (define-key map "\C-c\C-i\C-s" 'octave-show-process-buffer)
-    (define-key map "\C-c\C-i\C-q" 'octave-hide-process-buffer)
-    (define-key map "\C-c\C-i\C-k" 'octave-kill-process)
-    map)
-  "Keymap used in Octave mode.")
-
-
+(defvar-keymap octave-mode-map
+  :doc "Keymap used in Octave mode."
+  "M-."         #'octave-find-definition
+  "C-M-j"       #'octave-indent-new-comment-line
+  "C-c C-p"     #'octave-previous-code-line
+  "C-c C-n"     #'octave-next-code-line
+  "C-c C-a"     #'octave-beginning-of-line
+  "C-c C-e"     #'octave-end-of-line
+  "<remap> <down-list>" #'smie-down-list
+  "C-c C-M-h"   #'octave-mark-block
+  "C-c ]"       #'smie-close-block
+  "C-c /"       #'smie-close-block
+  "C-c ;"       #'octave-update-function-file-comment
+  "C-h d"       #'octave-help
+  "C-h a"       #'octave-lookfor
+  "C-c C-l"     #'octave-source-file
+  "C-c C-f"     #'octave-insert-defun
+  "C-c C-i l"   #'octave-send-line
+  "C-c C-i b"   #'octave-send-block
+  "C-c C-i f"   #'octave-send-defun
+  "C-c C-i r"   #'octave-send-region
+  "C-c C-i a"   #'octave-send-buffer
+  "C-c C-i s"   #'octave-show-process-buffer
+  "C-c C-i q"   #'octave-hide-process-buffer
+  "C-c C-i k"   #'octave-kill-process
+  "C-c C-i C-l" #'octave-send-line
+  "C-c C-i C-b" #'octave-send-block
+  "C-c C-i C-f" #'octave-send-defun
+  "C-c C-i C-r" #'octave-send-region
+  "C-c C-i C-a" #'octave-send-buffer
+  "C-c C-i C-s" #'octave-show-process-buffer
+  "C-c C-i C-q" #'octave-hide-process-buffer
+  "C-c C-i C-k" #'octave-kill-process)
 
 (easy-menu-define octave-mode-menu octave-mode-map
   "Menu for Octave mode."
diff --git a/lisp/progmodes/perl-mode.el b/lisp/progmodes/perl-mode.el
index 7b7a2cdf01..4dd0fd67a6 100644
--- a/lisp/progmodes/perl-mode.el
+++ b/lisp/progmodes/perl-mode.el
@@ -27,8 +27,8 @@
 
 ;;; Commentary:
 
-;; To enter perl-mode automatically, add (autoload 'perl-mode "perl-mode")
-;; to your init file and change the first line of your perl script to:
+;; To enter `perl-mode' automatically, change the first line of your
+;; perl script to:
 ;; #!/usr/bin/perl --   # -*-Perl-*-
 ;; With arguments to perl:
 ;; #!/usr/bin/perl -P-  # -*-Perl-*-
@@ -215,11 +215,16 @@
 (eval-and-compile
   (defconst perl--syntax-exp-intro-keywords
     '("split" "if" "unless" "until" "while" "print" "printf"
-      "grep" "map" "not" "or" "and" "for" "foreach" "return"))
+      "grep" "map" "not" "or" "and" "for" "foreach" "return" "die"
+      "warn" "eval"))
 
   (defconst perl--syntax-exp-intro-regexp
     (concat "\\(?:\\(?:^\\|[^$@&%[:word:]]\\)"
             (regexp-opt perl--syntax-exp-intro-keywords)
+            ;; A HERE document as an argument to printf?
+            ;; when printing to a filehandle.
+            "\\|printf?[ \t]*\\$?[_[:alpha:]][_[:alnum:]]*"
+            "\\|=>"
             "\\|[?:.,;|&*=!~({[]"
             "\\|[^-+][-+]"    ;Bug#42168: `+' is intro but `++' isn't!
             "\\|\\(^\\)\\)[ \t\n]*")))
@@ -335,7 +340,7 @@
         "<<\\(~\\)?[ 
\t]*\\('[^'\n]*'\\|\"[^\"\n]*\"\\|\\\\[[:alpha:]][[:alnum:]]*\\)"
         ;; The <<EOF case which needs perl--syntax-exp-intro-regexp, to
         ;; disambiguate with the left-bitshift operator.
-        "\\|" perl--syntax-exp-intro-regexp "<<\\(?2:\\sw+\\)\\)"
+        "\\|" perl--syntax-exp-intro-regexp "<<\\(?1:~\\)?\\(?2:\\sw+\\)\\)"
         ".*\\(\n\\)")
        (4 (let* ((eol (match-beginning 4))
                  (st (get-text-property eol 'syntax-table))
diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el
index ac278edd40..ed26872ae7 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-2022 Free Software Foundation, Inc.
-;; Version: 0.8.2
+;; Version: 0.8.3
 ;; Package-Requires: ((emacs "26.1") (xref "1.4.0"))
 
 ;; This is a GNU ELPA :core package.  Avoid using functionality that
@@ -296,7 +296,6 @@ to find the list of ignores for each directory."
 (defun project--files-in-directory (dir ignores &optional files)
   (require 'find-dired)
   (require 'xref)
-  (defvar find-name-arg)
   (let* ((default-directory dir)
          ;; Make sure ~/ etc. in local directory name is
          ;; expanded and not left for the shell command
@@ -308,11 +307,11 @@ to find the list of ignores for each directory."
                           (xref--find-ignores-arguments ignores "./")
                           (if files
                               (concat (shell-quote-argument "(")
-                                      " " find-name-arg " "
+                                      " -name "
                                       (mapconcat
                                        #'shell-quote-argument
                                        (split-string files)
-                                       (concat " -o " find-name-arg " "))
+                                       (concat " -o -name "))
                                       " "
                                       (shell-quote-argument ")"))
                             "")))
@@ -353,7 +352,10 @@ Also quote LOCAL-FILES if `default-directory' is quoted."
               local-files))))
 
 (cl-defgeneric project-buffers (project)
-  "Return the list of all live buffers that belong to PROJECT."
+  "Return the list of all live buffers that belong to PROJECT.
+
+The default implementation matches each buffer to PROJECT root using
+the buffer's value of `default-directory'."
   (let ((root (expand-file-name (file-name-as-directory (project-root 
project))))
         bufs)
     (dolist (buf (buffer-list))
@@ -1222,11 +1224,14 @@ displayed."
 
 (defcustom project-kill-buffer-conditions
   '(buffer-file-name    ; All file-visiting buffers are included.
-    ;; Most of the temp buffers in the background:
-    (major-mode . fundamental-mode)
+    ;; Most of temp and logging buffers (aside from hidden ones):
+    (and
+     (major-mode . fundamental-mode)
+     "\\`[^ ]")
     ;; non-text buffer such as xref, occur, vc, log, ...
     (and (derived-mode . special-mode)
-         (not (major-mode . help-mode)))
+         (not (major-mode . help-mode))
+         (not (derived-mode . gnus-mode)))
     (derived-mode . compilation-mode)
     (derived-mode . dired-mode)
     (derived-mode . diff-mode)
@@ -1277,21 +1282,6 @@ Used by `project-kill-buffers'."
   :package-version '(project . "0.8.2")
   :safe #'booleanp)
 
-(defun project--buffer-list (pr)
-  "Return the list of all buffers in project PR."
-  (let ((conn (file-remote-p (project-root pr)))
-        bufs)
-    (dolist (buf (buffer-list))
-      ;; For now we go with the assumption that a project must reside
-      ;; entirely on one host.  We might relax that in the future.
-      (when (and (equal conn
-                        (file-remote-p (buffer-local-value 'default-directory 
buf)))
-                 (equal pr
-                        (with-current-buffer buf
-                          (project-current))))
-        (push buf bufs)))
-    (nreverse bufs)))
-
 (defun project--buffer-check (buf conditions)
   "Check if buffer BUF matches any element of the list CONDITIONS.
 See `project-kill-buffer-conditions' or
@@ -1667,9 +1657,10 @@ to directory DIR."
   (let ((command (if (symbolp project-switch-commands)
                      project-switch-commands
                    (project--switch-project-command))))
-    (let ((default-directory dir)
-          (project-current-inhibit-prompt t))
-      (call-interactively command))))
+    (with-temp-buffer
+      (let ((default-directory dir)
+            (project-current-inhibit-prompt t))
+        (call-interactively command)))))
 
 (provide 'project)
 ;;; project.el ends here
diff --git a/lisp/progmodes/prolog.el b/lisp/progmodes/prolog.el
index f8edc2b1f7..6e2c2513b0 100644
--- a/lisp/progmodes/prolog.el
+++ b/lisp/progmodes/prolog.el
@@ -1311,7 +1311,7 @@ With prefix argument ARG, restart the Prolog process if 
running before."
     (prolog-mode-variables)
     ))
 
-(defun prolog-inferior-guess-flavor (&optional ignored)
+(defun prolog-inferior-guess-flavor (&optional _ignored)
   (setq-local prolog-system
               (when (or (numberp prolog-system) (markerp prolog-system))
                 (save-excursion
diff --git a/lisp/progmodes/ps-mode.el b/lisp/progmodes/ps-mode.el
index 89482d86ce..6355b17e4a 100644
--- a/lisp/progmodes/ps-mode.el
+++ b/lisp/progmodes/ps-mode.el
@@ -278,24 +278,22 @@ If nil, use `temporary-file-directory'."
 
 ;; Variables.
 
-(defvar ps-mode-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map "\C-c\C-v" #'ps-run-boundingbox)
-    (define-key map "\C-c\C-u" #'ps-mode-uncomment-region)
-    (define-key map "\C-c\C-t" #'ps-mode-epsf-rich)
-    (define-key map "\C-c\C-s" #'ps-run-start)
-    (define-key map "\C-c\C-r" #'ps-run-region)
-    (define-key map "\C-c\C-q" #'ps-run-quit)
-    (define-key map "\C-c\C-p" #'ps-mode-print-buffer)
-    (define-key map "\C-c\C-o" #'ps-mode-comment-out-region)
-    (define-key map "\C-c\C-k" #'ps-run-kill)
-    (define-key map "\C-c\C-j" #'ps-mode-other-newline)
-    (define-key map "\C-c\C-l" #'ps-run-clear)
-    (define-key map "\C-c\C-b" #'ps-run-buffer)
-    ;; FIXME: Add `indent' to backward-delete-char-untabify-method instead?
-    (define-key map "\177" #'ps-mode-backward-delete-char)
-    map)
-  "Local keymap to use in PostScript mode.")
+(defvar-keymap ps-mode-map
+  :doc "Local keymap to use in PostScript mode."
+  "C-c C-v" #'ps-run-boundingbox
+  "C-c C-u" #'ps-mode-uncomment-region
+  "C-c C-t" #'ps-mode-epsf-rich
+  "C-c C-s" #'ps-run-start
+  "C-c C-r" #'ps-run-region
+  "C-c C-q" #'ps-run-quit
+  "C-c C-p" #'ps-mode-print-buffer
+  "C-c C-o" #'ps-mode-comment-out-region
+  "C-c C-k" #'ps-run-kill
+  "C-c C-j" #'ps-mode-other-newline
+  "C-c C-l" #'ps-run-clear
+  "C-c C-b" #'ps-run-buffer
+  ;; FIXME: Add `indent' to backward-delete-char-untabify-method instead?
+  "DEL"     #'ps-mode-backward-delete-char)
 
 (defvar ps-mode-syntax-table
   (let ((st (make-syntax-table)))
@@ -332,15 +330,13 @@ If nil, use `temporary-file-directory'."
     st)
   "Syntax table used while in PostScript mode.")
 
-(defvar ps-run-mode-map
-  (let ((map (make-sparse-keymap)))
-    (set-keymap-parent map comint-mode-map)
-    (define-key map "\C-c\C-q" #'ps-run-quit)
-    (define-key map "\C-c\C-k" #'ps-run-kill)
-    (define-key map "\C-c\C-e" #'ps-run-goto-error)
-    (define-key map [mouse-2] #'ps-run-mouse-goto-error)
-    map)
-  "Local keymap to use in PostScript run mode.")
+(defvar-keymap ps-run-mode-map
+  :doc "Local keymap to use in PostScript run mode."
+  :parent comint-mode-map
+  "C-c C-q"   #'ps-run-quit
+  "C-c C-k"   #'ps-run-kill
+  "C-c C-e"   #'ps-run-goto-error
+  "<mouse-2>" #'ps-run-mouse-goto-error)
 
 (defvar ps-mode-tmp-file nil
   "Name of temporary file, set by `ps-run'.")
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index 0de76b0bde..a734e06149 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -4080,15 +4080,18 @@ using that one instead of current buffer's process."
                  (buffer-substring-no-properties line-start (point)))
             (buffer-substring-no-properties line-start (point))))
          (start
-          (save-excursion
-            (if (not (re-search-backward
-                      (python-rx
-                       (or whitespace open-paren close-paren string-delimiter 
simple-operator))
-                      line-start
-                      t 1))
-                line-start
-              (forward-char (length (match-string-no-properties 0)))
-              (point))))
+          (if (< (point) line-start)
+              (point)
+            (save-excursion
+              (if (not (re-search-backward
+                        (python-rx
+                         (or whitespace open-paren close-paren
+                             string-delimiter simple-operator))
+                        line-start
+                        t 1))
+                  line-start
+                (forward-char (length (match-string-no-properties 0)))
+                (point)))))
          (end (point))
          (prompt-boundaries
           (with-current-buffer (process-buffer process)
@@ -4102,7 +4105,10 @@ using that one instead of current buffer's process."
           (with-current-buffer (process-buffer process)
             (cond ((or (null prompt)
                        (and is-shell-buffer
-                            (< (point) (cdr prompt-boundaries))))
+                            (< (point) (cdr prompt-boundaries)))
+                       (and (not is-shell-buffer)
+                            (string-match-p
+                             python-shell-prompt-pdb-regexp prompt)))
                    #'ignore)
                   ((or (not python-shell-completion-native-enable)
                        ;; Even if native completion is enabled, for
@@ -4369,7 +4375,9 @@ For this to work as best as possible you should call
 `python-shell-send-buffer' from time to time so context in
 inferior Python process is updated properly."
   (let ((process (python-shell-get-process)))
-    (when process
+    (when (and process
+               (python-shell-with-shell-buffer
+                 (python-util-comint-end-of-output-p)))
       (python-shell-completion-at-point process))))
 
 (define-obsolete-function-alias
@@ -4794,6 +4802,8 @@ def __FFAP_get_module_path(objstr):
 (defun python-ffap-module-path (module)
   "Function for `ffap-alist' to return path for MODULE."
   (when-let ((process (python-shell-get-process))
+             (ready (python-shell-with-shell-buffer
+                      (python-util-comint-end-of-output-p)))
              (module-file
               (python-shell-send-string-no-output
                (format "%s\nprint(__FFAP_get_module_path(%s))"
@@ -4912,7 +4922,9 @@ If not FORCE-INPUT is passed then what 
`python-eldoc--get-symbol-at-point'
 returns will be used.  If not FORCE-PROCESS is passed what
 `python-shell-get-process' returns is used."
   (let ((process (or force-process (python-shell-get-process))))
-    (when process
+    (when (and process
+               (python-shell-with-shell-buffer
+                 (python-util-comint-end-of-output-p)))
       (let* ((input (or force-input
                         (python-eldoc--get-symbol-at-point)))
              (docstring
@@ -5365,6 +5377,7 @@ likely an invalid python file."
                            ;; block and the current line, otherwise it
                            ;; is not an opening block.
                            (save-excursion
+                             (python-nav-end-of-statement)
                              (forward-line)
                              (let ((no-back-indent t))
                                (save-match-data
@@ -5658,6 +5671,13 @@ This is for compatibility with Emacs < 24.4."
          comint-last-prompt)
         (t nil)))
 
+(defun python-util-comint-end-of-output-p ()
+  "Return non-nil if the last prompt matches input prompt."
+  (when-let ((prompt (python-util-comint-last-prompt)))
+    (python-shell-comint-end-of-output-p
+     (buffer-substring-no-properties
+      (car prompt) (cdr prompt)))))
+
 (defun python-util-forward-comment (&optional direction)
   "Python mode specific version of `forward-comment'.
 Optional argument DIRECTION defines the direction to move to."
diff --git a/lisp/progmodes/ruby-mode.el b/lisp/progmodes/ruby-mode.el
index 955daa393c..17467b5554 100644
--- a/lisp/progmodes/ruby-mode.el
+++ b/lisp/progmodes/ruby-mode.el
@@ -849,7 +849,7 @@ The style of the comment is controlled by 
`ruby-encoding-magic-comment-style'."
     (back-to-indentation)
     (current-column)))
 
-(defun ruby-indent-line (&optional ignored)
+(defun ruby-indent-line (&optional _ignored)
   "Correct the indentation of the current Ruby line."
   (interactive)
   (ruby-indent-to (ruby-calculate-indent)))
@@ -1576,7 +1576,7 @@ With ARG, do it many times.  Negative ARG means move 
forward."
         ((error)))
       i))))
 
-(defun ruby-indent-exp (&optional ignored)
+(defun ruby-indent-exp (&optional _ignored)
   "Indent each line in the balanced expression following the point."
   (interactive "*P")
   (let ((here (point-marker)) start top column (nest t))
diff --git a/lisp/progmodes/simula.el b/lisp/progmodes/simula.el
index 7e9aeab8fe..9aa8a994ed 100644
--- a/lisp/progmodes/simula.el
+++ b/lisp/progmodes/simula.el
@@ -1,7 +1,6 @@
 ;;; simula.el --- SIMULA 87 code editing commands for Emacs  -*- 
lexical-binding: t; -*-
 
-;; Copyright (C) 1992, 1994, 1996, 2001-2022 Free Software Foundation,
-;; Inc.
+;; Copyright (C) 1992-2022 Free Software Foundation, Inc.
 
 ;; Author: Hans Henrik Eriksen <hhe@ifi.uio.no>
 ;; Maintainer: emacs-devel@gnu.org
@@ -246,31 +245,19 @@ for SIMULA mode to function correctly."
 (defvar simula-font-lock-keywords simula-font-lock-keywords-1
   "Default expressions to highlight in Simula mode.")
 
-; The following function is taken from cc-mode.el,
-; it determines the flavor of the Emacs running
-
-(defvar simula-mode-menu
-  '(["Indent Line"           simula-indent-line t]
-    ["Backward Statement"     simula-previous-statement t]
-    ["Forward Statement"      simula-next-statement t]
-    ["Backward Up Level"      simula-backward-up-level t]
-    ["Forward Down Statement" simula-forward-down-level t])
-  "Emacs menu for SIMULA mode.")
-
-(defvar simula-mode-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map "\C-c\C-u"   #'simula-backward-up-level)
-    (define-key map "\C-c\C-p"   #'simula-previous-statement)
-    (define-key map "\C-c\C-d"   #'simula-forward-down-level)
-    (define-key map "\C-c\C-n"   #'simula-next-statement)
-    ;; (define-key map "\C-c\C-g"   #'simula-goto-definition)
-    ;; (define-key map "\C-c\C-h"   #'simula-standard-help)
-    (define-key map "\177"       #'backward-delete-char-untabify)
-    (define-key map ":"          #'simula-electric-label)
-    (define-key map "\e\C-q"     #'simula-indent-exp)
-    ;; (define-key map "\t"         #'simula-indent-command)
-    map)
-  "Keymap used in `simula-mode'.")
+(defvar-keymap simula-mode-map
+  :doc "Keymap used in `simula-mode'."
+  "C-c C-u"    #'simula-backward-up-level
+  "C-c C-p"    #'simula-previous-statement
+  "C-c C-d"    #'simula-forward-down-level
+  "C-c C-n"    #'simula-next-statement
+  ;; "C-c C-g" #'simula-goto-definition
+  ;; "C-c C-h" #'simula-standard-help
+  "DEL"        #'backward-delete-char-untabify
+  ":"          #'simula-electric-label
+  "C-M-q"      #'simula-indent-exp
+  ;; "TAB"     #'simula-indent-command
+  )
 
 (easy-menu-define simula-mode-menu simula-mode-map
   "Menu for `simula-mode'."
@@ -1560,7 +1547,6 @@ If not nil and not t, move to limit of search and return 
nil."
 (let (abbrevs-changed)
   (simula-install-standard-abbrevs))
 
-;; Hilit mode support.
 
 ;; obsolete
 
diff --git a/lisp/progmodes/sql.el b/lisp/progmodes/sql.el
index b950f93f2a..a1c0aa76de 100644
--- a/lisp/progmodes/sql.el
+++ b/lisp/progmodes/sql.el
@@ -1358,37 +1358,33 @@ specified, it's `sql-product' or `sql-connection' must 
match."
 
 ;; Keymap for sql-interactive-mode.
 
-(defvar sql-interactive-mode-map
-  (let ((map (make-sparse-keymap)))
-    (set-keymap-parent map comint-mode-map)
-    (define-key map (kbd "C-j") 'sql-accumulate-and-indent)
-    (define-key map (kbd "C-c C-w") 'sql-copy-column)
-    (define-key map (kbd "O") 'sql-magic-go)
-    (define-key map (kbd "o") 'sql-magic-go)
-    (define-key map (kbd ";") 'sql-magic-semicolon)
-    (define-key map (kbd "C-c C-l a") 'sql-list-all)
-    (define-key map (kbd "C-c C-l t") 'sql-list-table)
-    map)
-  "Mode map used for `sql-interactive-mode'.
-Based on `comint-mode-map'.")
+(defvar-keymap sql-interactive-mode-map
+  :doc "Mode map used for `sql-interactive-mode'.
+Based on `comint-mode-map'."
+  :parent comint-mode-map
+  "C-j"       #'sql-accumulate-and-indent
+  "C-c C-w"   #'sql-copy-column
+  "O"         #'sql-magic-go
+  "o"         #'sql-magic-go
+  ";"         #'sql-magic-semicolon
+  "C-c C-l a" #'sql-list-all
+  "C-c C-l t" #'sql-list-table)
 
 ;; Keymap for sql-mode.
 
-(defvar sql-mode-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map (kbd "C-c C-c") 'sql-send-paragraph)
-    (define-key map (kbd "C-c C-r") 'sql-send-region)
-    (define-key map (kbd "C-c C-s") 'sql-send-string)
-    (define-key map (kbd "C-c C-b") 'sql-send-buffer)
-    (define-key map (kbd "C-c C-n") 'sql-send-line-and-next)
-    (define-key map (kbd "C-c C-i") 'sql-product-interactive)
-    (define-key map (kbd "C-c C-z") 'sql-show-sqli-buffer)
-    (define-key map (kbd "C-c C-l a") 'sql-list-all)
-    (define-key map (kbd "C-c C-l t") 'sql-list-table)
-    (define-key map [remap beginning-of-defun] 'sql-beginning-of-statement)
-    (define-key map [remap end-of-defun] 'sql-end-of-statement)
-    map)
-  "Mode map used for `sql-mode'.")
+(defvar-keymap sql-mode-map
+  :doc "Mode map used for `sql-mode'."
+  "C-c C-c"   #'sql-send-paragraph
+  "C-c C-r"   #'sql-send-region
+  "C-c C-s"   #'sql-send-string
+  "C-c C-b"   #'sql-send-buffer
+  "C-c C-n"   #'sql-send-line-and-next
+  "C-c C-i"   #'sql-product-interactive
+  "C-c C-z"   #'sql-show-sqli-buffer
+  "C-c C-l a" #'sql-list-all
+  "C-c C-l t" #'sql-list-table
+  "<remap> <beginning-of-defun>" #'sql-beginning-of-statement
+  "<remap> <end-of-defun>"       #'sql-end-of-statement)
 
 ;; easy menu for sql-mode.
 
@@ -3030,9 +3026,10 @@ displayed."
 
     ;; Our start must be between them
     (goto-char last)
-    ;; Find a beginning-of-stmt that's not in a comment
+    ;; Find a beginning-of-stmt that's not in a string or comment
     (while (and (re-search-forward regexp next t 1)
-                (nth 7 (syntax-ppss)))
+                (or (nth 3 (syntax-ppss))
+                    (nth 7 (syntax-ppss))))
       (goto-char (match-end 0)))
     (goto-char
      (if (match-data)
@@ -3062,8 +3059,9 @@ displayed."
       ;; If we found another end-of-stmt
       (if (not (apply re-search term nil t n nil))
           (setq arg 0)
-        ;; count it if we're not in a comment
-        (unless (nth 7 (syntax-ppss))
+        ;; count it if we're not in a string or comment
+        (unless (or (nth 3 (syntax-ppss))
+                    (nth 7 (syntax-ppss)))
           (setq arg (- arg (cl-signum arg))))))
     (goto-char (if (match-data)
                    (match-end 0)
diff --git a/lisp/progmodes/verilog-mode.el b/lisp/progmodes/verilog-mode.el
index d6b8edaa36..310a9be4f6 100644
--- a/lisp/progmodes/verilog-mode.el
+++ b/lisp/progmodes/verilog-mode.el
@@ -9630,7 +9630,7 @@ Returns REGEXP and list of ( (signal_name 
connection_name)... )."
 
 (defun verilog-read-auto-template (module)
   "Look for an auto_template for the instantiation of the given MODULE.
-If found returns `verilog-read-auto-template-inside' structure."
+If found returns `verilog-read-auto-template-middle' structure."
   (save-excursion
     ;; Find beginning
     (let ((pt (point)))
@@ -10024,7 +10024,7 @@ Used for __FLAGS__ in `verilog-expand-command'."
 
 (defvar verilog-dir-cache-preserving nil
   "If true, the directory cache is enabled, and file system changes are 
ignored.
-See `verilog-dir-exists-p' and `verilog-dir-files'.")
+See `verilog-dir-file-exists-p' and `verilog-dir-files'.")
 
 ;; If adding new cached variable, add also to verilog-preserve-dir-cache
 (defvar verilog-dir-cache-list nil
diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el
index afb4509913..bb36688ef8 100644
--- a/lisp/progmodes/xref.el
+++ b/lisp/progmodes/xref.el
@@ -898,7 +898,7 @@ ITEMS is an xref item which " ; FIXME: Expand documentation.
             (perform-replace from to t t nil nil multi-query-replace-map)))
     (unless did-it-once
       (user-error
-       "Cannot use subset of matches of identifier for global renaming"))
+       "Cannot perform global renaming of symbols using find-definition 
results"))
     (when (and continue (not buf-pairs))
       (message "All results processed"))))
 
diff --git a/lisp/replace.el b/lisp/replace.el
index 8f81ec33a6..c7ae77d128 100644
--- a/lisp/replace.el
+++ b/lisp/replace.el
@@ -2818,7 +2818,7 @@ see the documentation of `replace-match' to find out how 
to simulate
 `case-replace'.
 
 This function returns nil if there were no matches to make, or
-the user cancelled the call.
+the user canceled the call.
 
 REPLACEMENTS is either a string, a list of strings, or a cons cell
 containing a function and its first argument.  The function is
diff --git a/lisp/rot13.el b/lisp/rot13.el
index c063725de8..5d1c46e483 100644
--- a/lisp/rot13.el
+++ b/lisp/rot13.el
@@ -85,9 +85,16 @@ and END, and return the encrypted string."
 
 ;;;###autoload
 (defun rot13-region (start end)
-  "ROT13 encrypt the region between START and END in current buffer."
+  "ROT13 encrypt the region between START and END in current buffer.
+If invoked interactively and the buffer is read-only, a message
+will be printed instead."
   (interactive "r")
-  (translate-region start end rot13-translate-table))
+  (condition-case nil
+      (translate-region start end rot13-translate-table)
+    (buffer-read-only
+     (when (called-interactively-p 'interactive)
+       (let ((dec (rot13-string (buffer-substring start end))))
+         (message "Buffer is read-only:\n%s" (string-trim dec)))))))
 
 ;;;###autoload
 (defun rot13-other-window ()
diff --git a/lisp/savehist.el b/lisp/savehist.el
index 8924c8dde2..f1d3e50d94 100644
--- a/lisp/savehist.el
+++ b/lisp/savehist.el
@@ -1,6 +1,6 @@
 ;;; savehist.el --- Save minibuffer history  -*- lexical-binding:t -*-
 
-;; Copyright (C) 1997, 2005-2022 Free Software Foundation, Inc.
+;; Copyright (C) 1997-2022 Free Software Foundation, Inc.
 
 ;; Author: Hrvoje Nikšić <hrvoje.niksic@avl.com>
 ;; Maintainer: emacs-devel@gnu.org
@@ -41,10 +41,6 @@
 ;; You can also explicitly save history with `M-x savehist-save' and
 ;; load it by loading the `savehist-file' with `M-x load-file'.
 
-;; If you are using a version of Emacs that does not ship with this
-;; package, be sure to have `savehist.el' in a directory that is in
-;; your load-path, and to byte-compile it.
-
 ;;; Code:
 
 ;; User variables
diff --git a/lisp/simple.el b/lisp/simple.el
index e804f717b0..a53b7b1d0d 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -2491,9 +2491,14 @@ Also see `suggest-key-bindings'."
 
 (defvar execute-extended-command--binding-timer nil)
 
+(defun execute-extended-command--describe-binding-msg (function binding 
shorter)
+  (format-message "You can run the command `%s' with %s"
+                  function
+                  (cond (shorter (concat "M-x " shorter))
+                        ((stringp binding) binding)
+                        (t (key-description binding)))))
+
 (defun execute-extended-command (prefixarg &optional command-name typed)
-  ;; Based on Fexecute_extended_command in keyboard.c of Emacs.
-  ;; Aaron S. Hawley <aaron.s.hawley(at)gmail.com> 2009-08-24
   "Read a command name, then read the arguments and call the command.
 To pass a prefix argument to the command you are
 invoking, give a prefix argument to `execute-extended-command'."
@@ -2516,7 +2521,7 @@ invoking, give a prefix argument to 
`execute-extended-command'."
                       (not executing-kbd-macro)
                       (where-is-internal function overriding-local-map t)))
          (delay-before-suggest 0)
-         (find-shorter nil))
+         find-shorter shorter)
     (unless (commandp function)
       (error "`%s' is not a valid command name" command-name))
     ;; If we're executing a command that's remapped, we can't actually
@@ -2540,11 +2545,11 @@ invoking, give a prefix argument to 
`execute-extended-command'."
     ;; flight.
     (when execute-extended-command--binding-timer
       (cancel-timer execute-extended-command--binding-timer))
-    ;; If this command displayed something in the echo area, then
-    ;; postpone the display of our suggestion message a bit.
     (when (and suggest-key-bindings
                (or binding
                    (and extended-command-suggest-shorter typed)))
+      ;; If this command displayed something in the echo area, then
+      ;; postpone the display of our suggestion message a bit.
       (setq delay-before-suggest
             (cond
              ((zerop (length (current-message))) 0)
@@ -2556,7 +2561,7 @@ invoking, give a prefix argument to 
`execute-extended-command'."
                  (symbolp function)
                  (> (length (symbol-name function)) 2))
         ;; There's no binding for CMD.  Let's try and find the shortest
-        ;; string to use in M-x.
+        ;; string to use in M-x.  But don't actually do anything yet.
         (setq find-shorter t))
       (when (or binding find-shorter)
         (setq execute-extended-command--binding-timer
@@ -2570,15 +2575,12 @@ invoking, give a prefix argument to 
`execute-extended-command'."
                    (when find-shorter
                      (while-no-input
                        ;; FIXME: Can be slow.  Cache it maybe?
-                       (setq binding (execute-extended-command--shorter
+                       (setq shorter (execute-extended-command--shorter
                                       (symbol-name function) typed))))
-                   (when binding
+                   (when (or binding shorter)
                      (with-temp-message
-                         (format-message "You can run the command `%s' with %s"
-                                         function
-                                         (if (stringp binding)
-                                             (concat "M-x " binding " RET")
-                                           (key-description binding)))
+                         (execute-extended-command--describe-binding-msg
+                          function binding shorter)
                        (sit-for (if (numberp suggest-key-bindings)
                                     suggest-key-bindings
                                   2))))))))))))
@@ -2647,10 +2649,7 @@ function as needed."
       ((or `(lambda ,_args . ,body) `(closure ,_env ,_args . ,body)
            `(autoload ,_file . ,body))
        (let ((doc (car body)))
-        (when (and (funcall docstring-p doc)
-                   ;; Handle a doc reference--but these never come last
-                   ;; in the function body, so reject them if they are last.
-                   (or (cdr body) (eq 'autoload (car-safe function))))
+        (when (funcall docstring-p doc)
            doc)))
       (_ (signal 'invalid-function (list function))))))
 
diff --git a/lisp/startup.el b/lisp/startup.el
index c7faf4abc6..70267fc857 100644
--- a/lisp/startup.el
+++ b/lisp/startup.el
@@ -458,7 +458,8 @@ or `CVS', and any subdirectory that contains a file named 
`.nosearch'."
        ;; The Windows version doesn't report meaningful inode numbers, so
        ;; use the canonicalized absolute file name of the directory instead.
        (setq attrs (or canonicalized
-                       (file-attribute-file-number (file-attributes 
this-dir))))
+                       (file-attribute-file-identifier
+                         (file-attributes this-dir))))
        (unless (member attrs normal-top-level-add-subdirs-inode-list)
          (push attrs normal-top-level-add-subdirs-inode-list)
          (dolist (file contents)
@@ -1197,7 +1198,7 @@ please check its value")
                          ("--user") ("--iconic") ("--icon-type") ("--quick")
                         ("--no-blinking-cursor") ("--basic-display")
                          ("--dump-file") ("--temacs") ("--seccomp")
-                         ("--init-directory")))
+                         ("--init-directory" "--no-comp-spawn")))
              (argi (pop args))
              (orig-argi argi)
              argval)
@@ -1254,6 +1255,9 @@ please check its value")
         ((equal argi "-no-site-file")
          (setq site-run-file nil)
          (put 'site-run-file 'standard-value '(nil)))
+         ((equal argi "-no-comp-spawn")
+          (defvar comp-no-spawn)
+          (setq comp-no-spawn t))
         ((equal argi "-debug-init")
          (setq init-file-debug t))
         ((equal argi "-iconic")
diff --git a/lisp/subr.el b/lisp/subr.el
index 56ce9fa69b..6b83196d05 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -161,16 +161,18 @@ of previous VARs.
     `(progn . ,(nreverse exps))))
 
 (defmacro setq-local (&rest pairs)
-  "Make variables in PAIRS buffer-local and assign them the corresponding 
values.
+  "Make each VARIABLE buffer-local and assign to it the corresponding VALUE.
 
-PAIRS is a list of variable/value pairs.  For each variable, make
-it buffer-local and assign it the corresponding value.  The
-variables are literal symbols and should not be quoted.
+The arguments are variable/value pairs  For each VARIABLE in a pair,
+make VARIABLE buffer-local and assign to it the corresponding VALUE
+of the pair.  The VARIABLEs are literal symbols and should not be quoted.
 
-The second VALUE is not computed until after the first VARIABLE
-is set, and so on; each VALUE can use the new value of variables
-set earlier in the `setq-local'.  The return value of the
-`setq-local' form is the value of the last VALUE.
+The VALUE of the Nth pair is not computed until after the VARIABLE
+of the (N-1)th pair is set; thus, each VALUE can use the new VALUEs
+of VARIABLEs set by earlier pairs.
+
+The return value of the `setq-local' form is the VALUE of the last
+pair.
 
 \(fn [VARIABLE VALUE]...)"
   (declare (debug setq))
@@ -344,7 +346,7 @@ in compilation warnings about unused variables.
              ;; FIXME: This let often leads to "unused var" warnings.
              `((let ((,var ,counter)) ,@(cddr spec)))))))
 
-(defmacro declare (&rest _specs)
+(defmacro declare (&rest specs)
   "Do not evaluate any arguments, and return nil.
 If a `declare' form appears as the first form in the body of a
 `defun' or `defmacro' form, SPECS specifies various additional
@@ -355,8 +357,16 @@ The possible values of SPECS are specified by
 `defun-declarations-alist' and `macro-declarations-alist'.
 
 For more information, see info node `(elisp)Declare Form'."
-  ;; FIXME: edebug spec should pay attention to defun-declarations-alist.
-  nil)
+  ;; `declare' is handled directly by `defun/defmacro' rather than here.
+  ;; If we get here, it's because there's a `declare' somewhere not attached
+  ;; to a `defun/defmacro', i.e. a `declare' which doesn't do what it's
+  ;; intended to do.
+  (let ((form `(declare . ,specs)))  ;; FIXME: WIBNI we had &whole?
+    (macroexp-warn-and-return
+     (format-message "Stray `declare' form: %S" form)
+     ;; Make a "unique" harmless form to circumvent
+     ;; the cache in `macroexp-warn-and-return'.
+     `(progn ',form nil) nil 'compile-only)))
 
 (defmacro ignore-errors (&rest body)
   "Execute BODY; if an error occurs, return nil.
@@ -1209,7 +1219,7 @@ Subkeymaps may be modified but are not canonicalized."
 
 (defun keyboard-translate (from to)
   "Translate character FROM to TO on the current terminal.
-This is a legacy function; see `keymap-translate' for the
+This is a legacy function; see `key-translate' for the
 recommended function to use instead.
 
 This function creates a `keyboard-translate-table' if necessary
@@ -1288,7 +1298,7 @@ KEY is a string or vector representing a sequence of 
keystrokes."
 
 (defun local-key-binding (keys &optional accept-default)
   "Return the binding for command KEYS in current local keymap only.
-This is a legacy function; see `keymap-local-binding' for the
+This is a legacy function; see `keymap-local-lookup' for the
 recommended function to use instead.
 
 KEYS is a string or vector, a sequence of keystrokes.
@@ -1302,7 +1312,7 @@ about this."
 
 (defun global-key-binding (keys &optional accept-default)
   "Return the binding for command KEYS in current global keymap only.
-This is a legacy function; see `keymap-global-binding' for the
+This is a legacy function; see `keymap-global-lookup' for the
 recommended function to use instead.
 
 KEYS is a string or vector, a sequence of keystrokes.
@@ -3262,7 +3272,14 @@ An obsolete, but still supported form is
 where the optional arg MILLISECONDS specifies an additional wait period,
 in milliseconds; this was useful when Emacs was built without
 floating point support."
-  (declare (advertised-calling-convention (seconds &optional nodisp) "22.1"))
+  (declare (advertised-calling-convention (seconds &optional nodisp) "22.1")
+           (compiler-macro
+            (lambda (form)
+              (if (not (or (numberp nodisp) obsolete)) form
+                (macroexp-warn-and-return
+                 "Obsolete calling convention for 'sit-for'"
+                 `(,(car form) (+ ,seconds (/ (or ,nodisp 0) 1000.0)) 
,obsolete)
+                 '(obsolete sit-for))))))
   ;; This used to be implemented in C until the following discussion:
   ;; https://lists.gnu.org/r/emacs-devel/2006-07/msg00401.html
   ;; Then it was moved here using an implementation based on an idle timer,
diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el
index abefd996a8..eb4cec4861 100644
--- a/lisp/tab-bar.el
+++ b/lisp/tab-bar.el
@@ -33,7 +33,8 @@
 
 (eval-when-compile
   (require 'cl-lib)
-  (require 'seq))
+  (require 'seq)
+  (require 'icons))
 
 
 (defgroup tab-bar nil
@@ -155,25 +156,43 @@ For easier selection of tabs by their numbers, consider 
customizing
 
 (defun tab-bar--load-buttons ()
   "Load the icons for the tab buttons."
-  (when (and tab-bar-new-button
-             (not (get-text-property 0 'display tab-bar-new-button)))
-    ;; This file is pre-loaded so only here we can use the right 
data-directory:
-    (add-text-properties 0 (length tab-bar-new-button)
-                         `(display (image :type xpm
-                                          :file "tabs/new.xpm"
-                                          :margin ,tab-bar-button-margin
-                                          :ascent center))
-                         tab-bar-new-button))
-
-  (when (and tab-bar-close-button
-             (not (get-text-property 0 'display tab-bar-close-button)))
-    ;; This file is pre-loaded so only here we can use the right 
data-directory:
-    (add-text-properties 0 (length tab-bar-close-button)
-                         `(display (image :type xpm
-                                          :file "tabs/close.xpm"
-                                          :margin ,tab-bar-button-margin
-                                          :ascent center))
-                         tab-bar-close-button)))
+  (require 'icons)
+  (unless (iconp 'tab-bar-new)
+    (define-icon tab-bar-new nil
+      `((image "tabs/new.xpm"
+               :margin ,tab-bar-button-margin
+               :ascent center)
+        ;; (emoji "➕")
+        ;; (symbol "+")
+        (text " + "))
+      "Icon for creating a new tab."
+      :version "29.1"
+      :help-echo "New tab"))
+  (setq tab-bar-new-button (icon-string 'tab-bar-new))
+
+  (unless (iconp 'tab-bar-close)
+    (define-icon tab-bar-close nil
+      `((image "tabs/close.xpm"
+               :margin ,tab-bar-button-margin
+               :ascent center)
+        ;; (emoji " ❌")
+        ;; (symbol "✕") ;; "ⓧ"
+        (text " x"))
+      "Icon for closing the clicked tab."
+      :version "29.1"
+      :help-echo "Click to close tab"))
+  (setq tab-bar-close-button (propertize (icon-string 'tab-bar-close)
+                                         'close-tab t))
+
+  (unless (iconp 'tab-bar-menu-bar)
+    (define-icon tab-bar-menu-bar nil
+      '(;; (emoji "🍔")
+        (symbol "☰")
+        (text "Menu" :face tab-bar-tab-inactive))
+      "Icon for for the menu bar."
+      :version "29.1"
+      :help-echo "Menu bar"))
+  (setq tab-bar-menu-bar-button (icon-string 'tab-bar-menu-bar)))
 
 (defun tab-bar--tab-bar-lines-for-frame (frame)
   "Determine and return the value of `tab-bar-lines' for FRAME.
@@ -721,7 +740,7 @@ If a function returns nil, it doesn't directly affect the
 tab bar appearance, but can do that by some side-effect.
 If the list ends with `tab-bar-format-align-right' and
 `tab-bar-format-global', then after enabling `display-time-mode'
-(or any other mode that uses `global-mode-string'),
+\(or any other mode that uses `global-mode-string'),
 it will display time aligned to the right on the tab bar instead
 of the mode line.  Replacing `tab-bar-format-tabs' with
 `tab-bar-format-tabs-groups' will group tabs on the tab bar."
@@ -914,14 +933,16 @@ when the tab is current.  Return the result as a keymap."
   (let* ((rest (cdr (memq 'tab-bar-format-align-right tab-bar-format)))
          (rest (tab-bar-format-list rest))
          (rest (mapconcat (lambda (item) (nth 2 item)) rest ""))
-         (hpos (string-pixel-width (propertize rest 'face 'tab-bar)))
+         (hpos (progn
+                 (add-face-text-property 0 (length rest) 'tab-bar t rest)
+                 (string-pixel-width rest)))
          (str (propertize " " 'display `(space :align-to (- right (,hpos))))))
     `((align-right menu-item ,str ignore))))
 
 (defun tab-bar-format-global ()
   "Produce display of `global-mode-string' in the tab bar.
 When `tab-bar-format-global' is added to `tab-bar-format'
-(possibly appended after `tab-bar-format-align-right'),
+\(possibly appended after `tab-bar-format-align-right'),
 then modes that display information on the mode line
 using `global-mode-string' will display the same text
 on the tab bar instead."
@@ -944,7 +965,146 @@ on the tab bar instead."
 
 (defun tab-bar-make-keymap-1 ()
   "Generate an actual keymap from `tab-bar-map', without caching."
-  (append tab-bar-map (tab-bar-format-list tab-bar-format)))
+  (let ((items (tab-bar-format-list tab-bar-format)))
+    (when tab-bar-auto-width
+      (setq items (tab-bar-auto-width items)))
+    (append tab-bar-map items)))
+
+
+(defcustom tab-bar-auto-width t
+  "Automatically resize width of tabs on tab bar to fill available tab-bar 
space.
+When non-nil, the widths of the tabs on the tab bar are
+automatically resized so that their width is evenly distributed
+across the tab bar.  This keeps the widths of the tabs
+independent of the length of the buffer names shown on each tab;
+the tab widths change only when tabs are added or deleted, or
+when the frame's dimensions change.  This also avoids as much as
+possible wrapping a long tab bar to a second tab-bar line.
+
+The automatic resizing of tabs takes place as long as tabs are no
+wider than allowed by the value of `tab-bar-auto-width-max', and
+at least as wide as specified by the value of
+`tab-bar-auto-width-min'.
+
+When this variable is nil, the width of each tab is determined by the
+length of the tab's name."
+  :type 'boolean
+  :group 'tab-bar
+  :version "29.1")
+
+(defcustom tab-bar-auto-width-max '(220 20)
+  "Maximum width for automatic resizing of width of tab-bar tabs.
+This determines the maximum width of tabs before their names will be
+truncated on display.
+The value should be a list of two numbers: the first is the maximum
+width of tabs in pixels for GUI frames, the second is the maximum
+width of tabs in characters on TTY frames.
+If the value of this variable is nil, there is no limit on maximum
+width.
+This variable has effect only when `tab-bar-auto-width' is non-nil."
+  :type '(choice
+          (const :tag "No limit" nil)
+          (list (integer :tag "Max width (pixels)" :value 220)
+                (integer :tag "Max width (chars)" :value 20)))
+  :initialize 'custom-initialize-default
+  :set (lambda (sym val)
+         (set-default sym val)
+         (setq tab-bar--fixed-width-hash nil))
+  :group 'tab-bar
+  :version "29.1")
+
+(defvar tab-bar-auto-width-min '(20 2)
+  "Minimum width of tabs for automatic resizing under `tab-bar-auto-width'.
+The value should be a list of two numbers, giving the minimum width
+as the number of pixels for GUI frames and the number of characters
+for text-mode frames.  Tabs whose width is smaller than this will not
+be narrowed.
+It's not recommended to change this value since with larger values, the
+tab bar might wrap to the second line when it shouldn't.")
+
+(defvar tab-bar-auto-width-faces
+  '( tab-bar-tab tab-bar-tab-inactive
+     tab-bar-tab-ungrouped
+     tab-bar-tab-group-inactive)
+  "Resize tabs only with these faces.")
+
+(defvar tab-bar--fixed-width-hash nil
+  "Memoization table for `tab-bar-auto-width'.")
+
+(defun tab-bar-auto-width (items)
+  "Return tab-bar items with resized tab names."
+  (unless tab-bar--fixed-width-hash
+    (define-hash-table-test 'tab-bar--fixed-width-hash-test
+                            #'equal-including-properties
+                            #'sxhash-equal-including-properties)
+    (setq tab-bar--fixed-width-hash
+          (make-hash-table :test 'tab-bar--fixed-width-hash-test)))
+  (let ((tabs nil)    ;; list of resizable tabs
+        (non-tabs "") ;; concatenated names of non-resizable tabs
+        (width 0))    ;; resize tab names to this width
+    (dolist (item items)
+      (when (and (eq (nth 1 item) 'menu-item) (stringp (nth 2 item)))
+        (if (memq (get-text-property 0 'face (nth 2 item))
+                  tab-bar-auto-width-faces)
+            (push item tabs)
+          (unless (eq (nth 0 item) 'align-right)
+            (setq non-tabs (concat non-tabs (nth 2 item)))))))
+    (when tabs
+      (add-face-text-property 0 (length non-tabs) 'tab-bar t non-tabs)
+      (setq width (/ (- (frame-inner-width)
+                        (string-pixel-width non-tabs))
+                     (length tabs)))
+      (when tab-bar-auto-width-min
+        (setq width (max width (if window-system
+                                   (nth 0 tab-bar-auto-width-min)
+                                 (nth 1 tab-bar-auto-width-min)))))
+      (when tab-bar-auto-width-max
+        (setq width (min width (if window-system
+                                   (nth 0 tab-bar-auto-width-max)
+                                 (nth 1 tab-bar-auto-width-max)))))
+      (dolist (item tabs)
+        (setf (nth 2 item)
+              (with-memoization (gethash (list (selected-frame)
+                                               width (nth 2 item))
+                                         tab-bar--fixed-width-hash)
+                (let* ((name (nth 2 item))
+                       (len (length name))
+                       (close-p (get-text-property (1- len) 'close-tab name))
+                       (continue t)
+                       (prev-width (string-pixel-width name))
+                       curr-width)
+                  (cond
+                   ((< prev-width width)
+                    (let* ((space (apply 'propertize " "
+                                         (text-properties-at 0 name)))
+                           (ins-pos (- len (if close-p 1 0)))
+                           (prev-name name))
+                      (while continue
+                        (setf (substring name ins-pos ins-pos) space)
+                        (setq curr-width (string-pixel-width name))
+                        (if (and (< curr-width width)
+                                 (not (eq curr-width prev-width)))
+                            (setq prev-width curr-width
+                                  prev-name name)
+                          ;; Set back a shorter name
+                          (setq name prev-name
+                                continue nil)))))
+                   ((> prev-width width)
+                    (let ((del-pos1 (if close-p -2 -1))
+                          (del-pos2 (if close-p -1 nil)))
+                      (while continue
+                        (setf (substring name del-pos1 del-pos2) "")
+                        (setq curr-width (string-pixel-width name))
+                        (if (and (> curr-width width)
+                                 (not (eq curr-width prev-width)))
+                            (setq prev-width curr-width)
+                          (setq continue nil)))
+                      (let* ((len (length name))
+                             (pos (- len (if close-p 1 0))))
+                        (add-face-text-property
+                         (max 0 (- pos 2)) (max 0 pos) 'shadow nil name)))))
+                  name)))))
+    items))
 
 
 ;; Some window-configuration parameters don't need to be persistent.
@@ -1091,7 +1251,8 @@ Negative TAB-NUMBER counts tabs from the end of the tab 
bar."
          (to-number (cond ((< tab-number 0) (+ (length tabs) (1+ tab-number)))
                           ((zerop tab-number) (1+ from-index))
                           (t tab-number)))
-         (to-index (1- (max 1 (min to-number (length tabs))))))
+         (to-index (1- (max 1 (min to-number (length tabs)))))
+         (minibuffer-was-active (minibuffer-window-active-p 
(selected-window))))
 
     (unless (eq from-index to-index)
       (let* ((from-tab (tab-bar--tab))
@@ -1117,7 +1278,7 @@ Negative TAB-NUMBER counts tabs from the end of the tab 
bar."
                 (wc-history-back (alist-get 'wc-history-back to-tab))
                 (wc-history-forward (alist-get 'wc-history-forward to-tab)))
 
-            (set-window-configuration wc)
+            (set-window-configuration wc nil t)
 
             ;; set-window-configuration does not restore the value of
             ;; point in the current buffer, so restore it separately.
@@ -1145,8 +1306,22 @@ Negative TAB-NUMBER counts tabs from the end of the tab 
bar."
                        tab-bar-history-forward))))
 
          (ws
+          ;; `window-state-put' fails when called in the minibuffer
+          (when (minibuffer-selected-window)
+            (select-window (minibuffer-selected-window)))
           (window-state-put ws nil 'safe)))
 
+        ;; Select the minibuffer when it was active before switching tabs
+        (when (and minibuffer-was-active (active-minibuffer-window))
+          (select-window (active-minibuffer-window)))
+
+        ;; When the minibuffer was activated in one tab, but exited in
+        ;; another tab, then after going back to the first tab, it has
+        ;; such inconsistent state that the current buffer is the minibuffer,
+        ;; but its window is not active.  So try to undo this mess.
+        (when (and (minibufferp) (not (active-minibuffer-window)))
+          (other-window 1))
+
         (when tab-bar-history-mode
           (setq tab-bar-history-omit t))
 
@@ -1881,7 +2056,7 @@ This navigates back in the history of window 
configurations."
                    (cons tab-bar-history-old
                          (gethash (selected-frame) tab-bar-history-forward))
                    tab-bar-history-forward)
-          (set-window-configuration wc)
+          (set-window-configuration wc nil t)
           (when (and (markerp wc-point) (marker-buffer wc-point))
             (goto-char wc-point)))
       (message "No more tab back history"))))
@@ -1900,7 +2075,7 @@ This navigates forward in the history of window 
configurations."
                    (cons tab-bar-history-old
                          (gethash (selected-frame) tab-bar-history-back))
                    tab-bar-history-back)
-          (set-window-configuration wc)
+          (set-window-configuration wc nil t)
           (when (and (markerp wc-point) (marker-buffer wc-point))
             (goto-char wc-point)))
       (message "No more tab forward history"))))
@@ -1916,22 +2091,27 @@ and can restore them."
   :global t :group 'tab-bar
   (if tab-bar-history-mode
       (progn
-        (when (and tab-bar-mode (not (get-text-property 0 'display 
tab-bar-back-button)))
-          ;; This file is pre-loaded so only here we can use the right 
data-directory:
-          (add-text-properties 0 (length tab-bar-back-button)
-                               `(display (image :type xpm
-                                                :file "tabs/left-arrow.xpm"
-                                                :margin ,tab-bar-button-margin
-                                                :ascent center))
-                               tab-bar-back-button))
-        (when (and tab-bar-mode (not (get-text-property 0 'display 
tab-bar-forward-button)))
-          ;; This file is pre-loaded so only here we can use the right 
data-directory:
-          (add-text-properties 0 (length tab-bar-forward-button)
-                               `(display (image :type xpm
-                                                :file "tabs/right-arrow.xpm"
-                                                :margin ,tab-bar-button-margin
-                                                :ascent center))
-                               tab-bar-forward-button))
+        (require 'icons)
+
+        (unless (iconp 'tab-bar-back)
+          (define-icon tab-bar-back nil
+            `((image "tabs/left-arrow.xpm"
+                     :margin ,tab-bar-button-margin
+                     :ascent center)
+              (text " < "))
+            "Icon for going back in tab history."
+            :version "29.1"))
+        (setq tab-bar-back-button (icon-string 'tab-bar-back))
+
+        (unless (iconp 'tab-bar-forward)
+          (define-icon tab-bar-forward nil
+            `((image "tabs/right-arrow.xpm"
+                     :margin ,tab-bar-button-margin
+                     :ascent center)
+              (text " > "))
+            "Icon for going forward in tab history."
+            :version "29.1"))
+        (setq tab-bar-forward-button (icon-string 'tab-bar-forward))
 
         (add-hook 'pre-command-hook 'tab-bar--history-pre-change)
         (add-hook 'window-configuration-change-hook 'tab-bar--history-change))
@@ -2168,7 +2348,7 @@ with those specified by the selected window 
configuration."
    ((framep all-frames) (list all-frames))
    (t (list (selected-frame)))))
 
-(defun tab-bar-get-buffer-tab (buffer-or-name &optional all-frames 
ignore-current-tab)
+(defun tab-bar-get-buffer-tab (buffer-or-name &optional all-frames 
ignore-current-tab all-tabs)
   "Return the tab that owns the window whose buffer is BUFFER-OR-NAME.
 BUFFER-OR-NAME may be a buffer or a buffer name, and defaults to
 the current buffer.
@@ -2186,14 +2366,20 @@ selected frame and no others.
 
 When the optional argument IGNORE-CURRENT-TAB is non-nil,
 don't take into account the buffers in the currently selected tab.
-Otherwise, prefer buffers of the current tab."
+Otherwise, prefer buffers of the current tab.
+
+When the optional argument ALL-TABS is non-nil, return a list of all tabs
+that contain the buffer BUFFER-OR-NAME."
   (let ((buffer (if buffer-or-name
                     (get-buffer buffer-or-name)
-                  (current-buffer))))
+                  (current-buffer)))
+        buffer-tabs)
     (when (bufferp buffer)
-      (seq-some
+      (funcall
+       (if all-tabs #'seq-each #'seq-some)
        (lambda (frame)
-         (seq-some
+         (funcall
+          (if all-tabs #'seq-each #'seq-some)
           (lambda (tab)
             (when (if (eq (car tab) 'current-tab)
                       (get-buffer-window buffer frame)
@@ -2205,8 +2391,9 @@ Otherwise, prefer buffers of the current tab."
                        (memq buffer buffers)
                        ;; writable window-state
                        (member (buffer-name buffer) buffers))))
-              (append tab `((index . ,(tab-bar--tab-index tab nil frame))
-                            (frame . ,frame)))))
+              (push (append tab `((index . ,(tab-bar--tab-index tab nil frame))
+                                  (frame . ,frame)))
+                    buffer-tabs)))
           (let* ((tabs (funcall tab-bar-tabs-function frame))
                  (current-tab (tab-bar--current-tab-find tabs)))
             (setq tabs (remq current-tab tabs))
@@ -2215,7 +2402,8 @@ Otherwise, prefer buffers of the current tab."
                 tabs
               ;; Make sure current-tab is at the beginning of tabs.
               (cons current-tab tabs)))))
-       (tab-bar--reusable-frames all-frames)))))
+       (tab-bar--reusable-frames all-frames))
+      (if all-tabs (nreverse buffer-tabs) (car (last buffer-tabs))))))
 
 (defun display-buffer-in-tab (buffer alist)
   "Display BUFFER in a tab using display actions in ALIST.
diff --git a/lisp/tab-line.el b/lisp/tab-line.el
index 94e8f29a95..99a785ee3e 100644
--- a/lisp/tab-line.el
+++ b/lisp/tab-line.el
@@ -483,7 +483,7 @@ which the tab will represent."
     (dolist (fn tab-line-tab-face-functions)
       (setf face (funcall fn tab tabs face buffer-p selected-p)))
     (apply 'propertize
-           (concat (propertize name
+           (concat (propertize (string-replace "%" "%%" name) ;; (bug#57848)
                                'keymap tab-line-tab-map
                                'help-echo (if selected-p "Current tab"
                                             "Click to select tab")
@@ -572,19 +572,31 @@ For use in `tab-line-tab-face-functions'."
 
 (defvar tab-line-auto-hscroll)
 
+(defun tab-line-cache-key-default (_tabs)
+  "Return default list of cache keys."
+  (list
+   ;; for setting face 'tab-line-tab-current'
+   (mode-line-window-selected-p)
+   ;; for `tab-line-tab-face-modified'
+   (and (memq 'tab-line-tab-face-modified
+              tab-line-tab-face-functions)
+        (buffer-file-name)
+        (buffer-modified-p))))
+
+(defvar tab-line-cache-key-function #'tab-line-cache-key-default
+  "Function that adds more cache keys.
+It is called with one argument, a list of tabs, and should return a list
+of cache keys.  You can use `add-function' to add more cache keys.")
+
 (defun tab-line-format ()
   "Format for displaying the tab line of the selected window."
   (let* ((tabs (funcall tab-line-tabs-function))
-         (cache-key (list tabs
-                          ;; handle buffer renames
-                          (buffer-name (window-buffer))
-                          ;; handle tab-line scrolling
-                          (window-parameter nil 'tab-line-hscroll)
-                          ;; for setting face 'tab-line-tab-current'
-                          (mode-line-window-selected-p)
-                          (and (memq 'tab-line-tab-face-modified
-                                     tab-line-tab-face-functions)
-                               (buffer-file-name) (buffer-modified-p))))
+         (cache-key (append (list tabs
+                                  ;; handle buffer renames
+                                  (buffer-name (window-buffer))
+                                  ;; handle tab-line scrolling
+                                  (window-parameter nil 'tab-line-hscroll))
+                            (funcall tab-line-cache-key-function tabs)))
          (cache (window-parameter nil 'tab-line-cache)))
     ;; Enable auto-hscroll again after it was disabled on manual scrolling.
     ;; The moment to enable it is when the window-buffer was updated.
@@ -620,7 +632,8 @@ the selected tab visible."
     (let ((truncate-partial-width-windows nil)
           (inhibit-modification-hooks t)
           show-arrows)
-      (setq truncate-lines nil)
+      (setq truncate-lines nil
+            word-wrap nil)
       (erase-buffer)
       (apply 'insert strings)
       (goto-char (point-min))
diff --git a/lisp/textmodes/css-mode.el b/lisp/textmodes/css-mode.el
index d2a35bd550..55dced96b7 100644
--- a/lisp/textmodes/css-mode.el
+++ b/lisp/textmodes/css-mode.el
@@ -875,26 +875,24 @@ cannot be completed sensibly: `custom-ident',
     (modify-syntax-entry ?? "." st)
     st))
 
-(defvar css-mode-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map [remap info-lookup-symbol] 'css-lookup-symbol)
-    ;; `info-complete-symbol' is not used.
-    (define-key map [remap complete-symbol] 'completion-at-point)
-    (define-key map "\C-c\C-f" 'css-cycle-color-format)
-    (easy-menu-define css-menu map "CSS mode menu"
-      '("CSS"
-        :help "CSS-specific features"
-        ["Reformat block" fill-paragraph
-         :help "Reformat declaration block or fill comment at point"]
-        ["Cycle color format" css-cycle-color-format
-         :help "Cycle color at point between different formats"]
-        "-"
-        ["Describe symbol" css-lookup-symbol
-         :help "Display documentation for a CSS symbol"]
-        ["Complete symbol" completion-at-point
-         :help "Complete symbol before point"]))
-    map)
-  "Keymap used in `css-mode'.")
+(defvar-keymap css-mode-map
+  :doc "Keymap used in `css-mode'."
+  "<remap> <info-lookup-symbol>" #'css-lookup-symbol
+  ;; `info-complete-symbol' is not used.
+  "<remap> <complete-symbol>" #'completion-at-point
+  "C-c C-f" #'css-cycle-color-format
+  :menu
+  '("CSS"
+    :help "CSS-specific features"
+    ["Reformat block" fill-paragraph
+     :help "Reformat declaration block or fill comment at point"]
+    ["Cycle color format" css-cycle-color-format
+     :help "Cycle color at point between different formats"]
+    "-"
+    ["Describe symbol" css-lookup-symbol
+     :help "Display documentation for a CSS symbol"]
+    ["Complete symbol" completion-at-point
+     :help "Complete symbol before point"]))
 
 (eval-and-compile
   (defconst css--uri-re
diff --git a/lisp/textmodes/emacs-news-mode.el 
b/lisp/textmodes/emacs-news-mode.el
index d9decae4df..ebb31da9cf 100644
--- a/lisp/textmodes/emacs-news-mode.el
+++ b/lisp/textmodes/emacs-news-mode.el
@@ -73,12 +73,9 @@
 
 (defun emacs-news--mode-common ()
   (setq-local font-lock-defaults '(emacs-news-mode-font-lock-keywords t))
-  ;; This `outline-regexp' matches leading spaces inserted
-  ;; by the current implementation of `outline-minor-mode-use-buttons'.
-  (setq-local outline-regexp "\\(?: +\\)?\\(\\*+\\) "
-              outline-level (lambda () (length (match-string 1)))
-              outline-minor-mode-cycle t
-              outline-minor-mode-highlight 'append)
+  (setq-local outline-minor-mode-cycle t
+              outline-minor-mode-highlight 'append
+              outline-minor-mode-use-buttons 'in-margins)
   (outline-minor-mode)
   (setq-local imenu-generic-expression outline-imenu-generic-expression)
   (emacs-etc--hide-local-variables))
diff --git a/lisp/textmodes/flyspell.el b/lisp/textmodes/flyspell.el
index a66b72cfd0..11039f2963 100644
--- a/lisp/textmodes/flyspell.el
+++ b/lisp/textmodes/flyspell.el
@@ -2131,7 +2131,9 @@ But don't look beyond what's visible on the screen."
          ;; only reset if a new overlay exists
          (setq flyspell-auto-correct-previous-pos nil)
 
-         (let ((overlay-list (overlays-in (point-min) position))
+         (let ((overlay-list (seq-sort-by
+                               #'overlay-start #'>
+                               (overlays-in (point-min) position)))
                (new-overlay 'dummy-value))
 
            ;; search for previous (new) flyspell overlay
diff --git a/lisp/textmodes/less-css-mode.el b/lisp/textmodes/less-css-mode.el
index 5d17b390f4..bfb5566e89 100644
--- a/lisp/textmodes/less-css-mode.el
+++ b/lisp/textmodes/less-css-mode.el
@@ -24,7 +24,7 @@
 ;;; Commentary:
 
 ;; This mode provides syntax highlighting for Less CSS files
-;; (http://lesscss.org/), plus optional support for compilation of
+;; (https://lesscss.org/), plus optional support for compilation of
 ;; .less files to .css files at the time they are saved: use
 ;; `less-css-compile-at-save' to enable this.
 ;;
diff --git a/lisp/textmodes/page-ext.el b/lisp/textmodes/page-ext.el
index 05c02308e5..b133b1e9e3 100644
--- a/lisp/textmodes/page-ext.el
+++ b/lisp/textmodes/page-ext.el
@@ -276,19 +276,17 @@ Used by `pages-directory-for-addresses' function."
 
 ;;; Key bindings for page handling functions
 
-(defvar pages--ctl-x-ctl-p-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map "\C-n" #'pages-next-page)
-    (define-key map "\C-p" #'pages-previous-page)
-    (define-key map "\C-a" #'pages-add-new-page)
-    (define-key map "\C-m" #'mark-page)
-    (define-key map "\C-s" #'pages-search)
-    (define-key map "s"    #'pages-sort-buffer)
-    (define-key map "\C-l" #'pages-set-delimiter)
-    (define-key map "\C-d" #'pages-directory)
-    (define-key map "d"    #'pages-directory-for-addresses)
-    map)
-  "Keymap for subcommands of C-x C-p, which are for page handling.")
+(defvar-keymap pages--ctl-x-ctl-p-map
+  :doc "Keymap for subcommands of \\`C-x C-p', which are for page handling."
+  "C-n" #'pages-next-page
+  "C-p" #'pages-previous-page
+  "C-a" #'pages-add-new-page
+  "C-m" #'mark-page
+  "C-s" #'pages-search
+  "s"   #'pages-sort-buffer
+  "C-l" #'pages-set-delimiter
+  "C-d" #'pages-directory
+  "d"   #'pages-directory-for-addresses)
 
 ;; FIXME: Merely loading a package shouldn't have this kind of side-effects!
 (global-unset-key "\C-x\C-p")
@@ -476,14 +474,12 @@ contain matches to the regexp.)")
 
 (define-obsolete-variable-alias 'pages-directory-map
   'pages-directory-mode-map "26.1")
-(defvar pages-directory-mode-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map "\C-c\C-c"     #'pages-directory-goto)
-    (define-key map "\C-m"         #'pages-directory-goto)
-    (define-key map "\C-c\C-p\C-a" #'pages-add-new-page)
-    (define-key map [mouse-2]      #'pages-directory-goto)
-    map)
-  "Keymap for the pages-directory-buffer.")
+(defvar-keymap pages-directory-mode-map
+  :doc "Keymap for the pages-directory-buffer."
+  "C-c C-c"     #'pages-directory-goto
+  "RET"         #'pages-directory-goto
+  "C-c C-p C-a" #'pages-add-new-page
+  "<mouse-2>"   #'pages-directory-goto)
 
 (defvar pages-original-delimiter "^\f"
   "Default page delimiter.")
diff --git a/lisp/textmodes/sgml-mode.el b/lisp/textmodes/sgml-mode.el
index 7d691430ec..7ce30cba8a 100644
--- a/lisp/textmodes/sgml-mode.el
+++ b/lisp/textmodes/sgml-mode.el
@@ -268,7 +268,7 @@ Currently, only Latin-1 characters are supported.")
   ;; prefer tidy because (o)nsgmls is often built without --enable-http
   ;; which makes it next to useless
   (cond ((executable-find "tidy")
-         ;; tidy is available from http://tidy.sourceforge.net/
+         ;; tidy is available from https://tidy.sourceforge.net/
          "tidy --gnu-emacs yes -utf8 -e -q")
         ((executable-find "nsgmls")
          ;; nsgmls is a free SGML parser in the SP suite available from
@@ -276,7 +276,7 @@ Currently, only Latin-1 characters are supported.")
          "nsgmls -s")
         ((executable-find "onsgmls")
          ;; onsgmls is the community version of `nsgmls'
-         ;; hosted on http://openjade.sourceforge.net/
+         ;; hosted on https://openjade.sourceforge.net/
          "onsgmls -s")
         (t "Install (o)nsgmls, tidy, or some other SGML validator, and set 
`sgml-validate-command'"))
   "The command to validate an SGML document.
diff --git a/lisp/textmodes/table.el b/lisp/textmodes/table.el
index 964f94228b..2f34a58b5b 100644
--- a/lisp/textmodes/table.el
+++ b/lisp/textmodes/table.el
@@ -125,9 +125,7 @@
 ;; are tired of guessing how it works come back to this document
 ;; again.
 ;;
-;; To use the package regularly place this file in the site library
-;; directory and add the next expression in your init file.  Make
-;; sure that directory is included in the `load-path'.
+;; To use the package regularly, add this to your init file:
 ;;
 ;;   (require 'table)
 ;;
@@ -266,11 +264,6 @@
 ;; all a table is still a part of text in the buffer.  Only the
 ;; special behaviors exist inside each cell through text properties.
 ;;
-;; `table-generate-html' which appeared in earlier releases is
-;; deprecated in favor of `table-generate-source'.  Now HTML is
-;; treated as one of the languages used for describing the table's
-;; logical structure.
-;;
 ;;
 ;; -------
 ;; Keymap:
diff --git a/lisp/thingatpt.el b/lisp/thingatpt.el
index 462f87d3c1..9dda3e1fcb 100644
--- a/lisp/thingatpt.el
+++ b/lisp/thingatpt.el
@@ -441,7 +441,7 @@ the bounds of a possible ill-formed URI (one lacking a 
scheme)."
       ;; Otherwise, find the bounds within which a URI may exist.  The
       ;; method is similar to `ffap-string-at-point'.  Note that URIs
       ;; may contain parentheses but may not contain spaces (RFC3986).
-      (let* ((allowed-chars "--:=&?$+@-Z_[:alpha:]~#,%;*()!'")
+      (let* ((allowed-chars "--:=&?$+@-Z_[:alpha:]~#,%;*()!'[]")
             (skip-before "^[0-9a-zA-Z]")
             (skip-after  ":;.,!?'")
             (pt (point))
diff --git a/lisp/thread.el b/lisp/thread.el
index 1e6e9e75a7..c0cc5feb97 100644
--- a/lisp/thread.el
+++ b/lisp/thread.el
@@ -58,20 +58,18 @@ An EVENT has the format
   :type 'number
   :version "27.1")
 
-(defvar thread-list-mode-map
-  (let ((map (make-sparse-keymap)))
-    (set-keymap-parent map tabulated-list-mode-map)
-    (define-key map "b" #'thread-list-pop-to-backtrace)
-    (define-key map "s" nil)
-    (define-key map "sq" #'thread-list-send-quit-signal)
-    (define-key map "se" #'thread-list-send-error-signal)
-    (easy-menu-define nil map ""
-      '("Threads"
-        ["Show backtrace" thread-list-pop-to-backtrace t]
-       ["Send Quit Signal" thread-list-send-quit-signal t]
-        ["Send Error Signal" thread-list-send-error-signal t]))
-    map)
-  "Local keymap for `thread-list-mode' buffers.")
+(defvar-keymap thread-list-mode-map
+  :doc "Local keymap for `thread-list-mode' buffers."
+  :parent tabulated-list-mode-map
+  "b"   #'thread-list-pop-to-backtrace
+  "s"   nil
+  "s q" #'thread-list-send-quit-signal
+  "s e" #'thread-list-send-error-signal
+  :menu
+  '("Threads"
+    ["Show backtrace" thread-list-pop-to-backtrace t]
+    ["Send Quit Signal" thread-list-send-quit-signal t]
+    ["Send Error Signal" thread-list-send-error-signal t]))
 
 (define-derived-mode thread-list-mode tabulated-list-mode "Thread-List"
   "Major mode for monitoring Lisp threads."
diff --git a/lisp/transient.el b/lisp/transient.el
index a415858970..0919c2c3ef 100644
--- a/lisp/transient.el
+++ b/lisp/transient.el
@@ -68,26 +68,6 @@
 (defvar display-line-numbers) ; since Emacs 26.1
 (defvar Man-notify-method)
 
-(define-obsolete-function-alias 'define-transient-command
-  'transient-define-prefix "Transient 0.3.0")
-(define-obsolete-function-alias 'define-suffix-command
-  'transient-define-suffix "Transient 0.3.0")
-(define-obsolete-function-alias 'define-infix-command
-  'transient-define-infix "Transient 0.3.0")
-(define-obsolete-function-alias 'define-infix-argument
-  #'transient-define-argument "Transient 0.3.0")
-
-(define-obsolete-variable-alias 'transient--source-buffer
-  'transient--original-buffer "Transient 0.2.0")
-(define-obsolete-variable-alias 'current-transient-prefix
-  'transient-current-prefix "Transient 0.3.0")
-(define-obsolete-variable-alias 'current-transient-command
-  'transient-current-command "Transient 0.3.0")
-(define-obsolete-variable-alias 'current-transient-suffixes
-  'transient-current-suffixes "Transient 0.3.0")
-(define-obsolete-variable-alias 'post-transient-hook
-  'transient-exit-hook "Transient 0.3.0")
-
 (defmacro transient--with-emergency-exit (&rest body)
   (declare (indent defun))
   `(condition-case err
@@ -893,8 +873,20 @@ to the setup function:
        (put ',name 'transient--prefix
             (,(or class 'transient-prefix) :command ',name ,@slots))
        (put ',name 'transient--layout
-            ',(cl-mapcan (lambda (s) (transient--parse-child name s))
-                         suffixes)))))
+            (list ,@(cl-mapcan (lambda (s) (transient--parse-child name s))
+                               suffixes))))))
+
+(defmacro transient-define-groups (name &rest groups)
+  "Define one or more GROUPS and store them in symbol NAME.
+GROUPS, defined using this macro, can be used inside the
+definition of transient prefix commands, by using the symbol
+NAME where a group vector is expected.  GROUPS has the same
+form as for `transient-define-prefix'."
+  (declare (debug (&define name [&rest vectorp]))
+           (indent defun))
+  `(put ',name 'transient--layout
+        (list ,@(cl-mapcan (lambda (group) (transient--parse-child name group))
+                           groups))))
 
 (defmacro transient-define-suffix (name arglist &rest args)
   "Define NAME as a transient suffix command.
@@ -1000,9 +992,8 @@ example, sets a variable use `transient-define-infix' 
instead.
           (push k keys)
           (push v keys))))
     (while (let ((arg (car args)))
-             (if (vectorp arg)
-                 (setcar args (eval (cdr (backquote-process arg))))
-               (and arg (symbolp arg))))
+             (or (vectorp arg)
+                 (and arg (symbolp arg))))
       (push (pop args) suffixes))
     (list (if (eq (car-safe class) 'quote)
               (cadr class)
@@ -1035,17 +1026,24 @@ example, sets a variable use `transient-define-infix' 
instead.
       (when (stringp car)
         (setq args (plist-put args :description pop)))
       (while (keywordp car)
-        (let ((k pop))
-          (if (eq k :class)
-              (setq class pop)
-            (setq args (plist-put args k pop)))))
-      (vector (or level transient--default-child-level)
-              (or class
-                  (if (vectorp car)
-                      'transient-columns
-                    'transient-column))
-              args
-              (cl-mapcan (lambda (s) (transient--parse-child prefix s)) 
spec)))))
+        (let ((key pop)
+              (val pop))
+          (cond ((eq key :class)
+                 (setq class (macroexp-quote val)))
+                ((or (symbolp val)
+                     (and (listp val) (not (eq (car val) 'lambda))))
+                 (setq args (plist-put args key (macroexp-quote val))))
+                ((setq args (plist-put args key val))))))
+      (list 'vector
+            (or level transient--default-child-level)
+            (or class
+                (if (vectorp car)
+                    (quote 'transient-columns)
+                  (quote 'transient-column)))
+            (and args (cons 'list args))
+            (cons 'list
+                  (cl-mapcan (lambda (s) (transient--parse-child prefix s))
+                             spec))))))
 
 (defun transient--parse-suffix (prefix spec)
   (let (level class args)
@@ -1057,17 +1055,19 @@ example, sets a variable use `transient-define-infix' 
instead.
       (when (or (stringp car)
                 (vectorp car))
         (setq args (plist-put args :key pop)))
-      (when (or (stringp car)
-                (eq (car-safe car) 'lambda)
-                (and (symbolp car)
-                     (not (commandp car))
-                     (commandp (cadr spec))))
+      (cond
+       ((or (stringp car)
+            (eq (car-safe car) 'lambda))
         (setq args (plist-put args :description pop)))
+       ((and (symbolp car)
+             (not (commandp car))
+             (commandp (cadr spec)))
+        (setq args (plist-put args :description (macroexp-quote pop)))))
       (cond
        ((keywordp car)
         (error "Need command, got %S" car))
        ((symbolp car)
-        (setq args (plist-put args :command pop)))
+        (setq args (plist-put args :command (macroexp-quote pop))))
        ((and (commandp car)
              (not (stringp car)))
         (let ((cmd pop)
@@ -1076,7 +1076,7 @@ example, sets a variable use `transient-define-infix' 
instead.
                                    (or (plist-get args :description)
                                        (plist-get args :key))))))
           (defalias sym cmd)
-          (setq args (plist-put args :command sym))))
+          (setq args (plist-put args :command (macroexp-quote sym)))))
        ((or (stringp car)
             (and car (listp car)))
         (let ((arg pop))
@@ -1090,11 +1090,11 @@ example, sets a variable use `transient-define-infix' 
instead.
                (setq args (plist-put args :shortarg shortarg)))
              (setq args (plist-put args :argument arg))))
           (setq args (plist-put args :command
-                                (intern (format "transient:%s:%s"
-                                                prefix arg))))
+                                (list 'quote (intern (format "transient:%s:%s"
+                                                             prefix arg)))))
           (cond ((and car (not (keywordp car)))
                  (setq class 'transient-option)
-                 (setq args (plist-put args :reader pop)))
+                 (setq args (plist-put args :reader (macroexp-quote pop))))
                 ((not (string-suffix-p "=" arg))
                  (setq class 'transient-switch))
                 (t
@@ -1102,17 +1102,23 @@ example, sets a variable use `transient-define-infix' 
instead.
        (t
         (error "Needed command or argument, got %S" car)))
       (while (keywordp car)
-        (let ((k pop))
-          (cl-case k
-            (:class (setq class pop))
-            (:level (setq level pop))
-            (t (setq args (plist-put args k pop)))))))
+        (let ((key pop)
+              (val pop))
+          (cond ((eq key :class) (setq class val))
+                ((eq key :level) (setq level val))
+                ((eq (car-safe val) '\,)
+                 (setq args (plist-put args key (cadr val))))
+                ((or (symbolp val)
+                     (and (listp val) (not (eq (car val) 'lambda))))
+                 (setq args (plist-put args key (macroexp-quote val))))
+                ((setq args (plist-put args key val)))))))
     (unless (plist-get args :key)
       (when-let ((shortarg (plist-get args :shortarg)))
         (setq args (plist-put args :key shortarg))))
-    (list (or level transient--default-child-level)
-          (or class 'transient-suffix)
-          args)))
+    (list 'list
+          (or level transient--default-child-level)
+          (macroexp-quote (or class 'transient-suffix))
+          (cons 'list args))))
 
 (defun transient--default-infix-command ()
   (cons 'lambda
@@ -1139,6 +1145,22 @@ example, sets a variable use `transient-define-infix' 
instead.
     (and (string-match "\\`\\(-[a-zA-Z]\\)\\(\\'\\|=\\)" arg)
          (match-string 1 arg))))
 
+(defun transient-parse-suffix (prefix suffix)
+  "Parse SUFFIX, to be added to PREFIX.
+PREFIX is a prefix command, a symbol.
+SUFFIX is a suffix command or a group specification (of
+  the same forms as expected by `transient-define-prefix').
+Intended for use in PREFIX's `:setup-children' function."
+  (eval (car (transient--parse-child prefix suffix))))
+
+(defun transient-parse-suffixes (prefix suffixes)
+  "Parse SUFFIXES, to be added to PREFIX.
+PREFIX is a prefix command, a symbol.
+SUFFIXES is a list of suffix command or a group specification
+  (of the same forms as expected by `transient-define-prefix').
+Intended for use in PREFIX's `:setup-children' function."
+  (mapcar (apply-partially #'transient-parse-suffix prefix) suffixes))
+
 ;;; Edit
 
 (defun transient--insert-suffix (prefix loc suffix action &optional keep-other)
@@ -1148,6 +1170,7 @@ example, sets a variable use `transient-define-infix' 
instead.
                 (string suffix)))
          (mem (transient--layout-member loc prefix))
          (elt (car mem)))
+    (setq suf (eval suf))
     (cond
      ((not mem)
       (message "Cannot insert %S into %s; %s not found"
@@ -1448,7 +1471,10 @@ probably use this instead:
           transient-current-prefix)
       (cl-find-if (lambda (obj)
                     (eq (transient--suffix-command obj)
-                        (or command this-command)))
+                        ;; When `this-command' is `transient-set-level',
+                        ;; its reader needs to know what command is being
+                        ;; configured.
+                        (or command this-original-command)))
                   (or transient--suffixes
                       transient-current-suffixes))
     (when-let* ((obj (get (or command this-command) 'transient--suffix))
@@ -1555,32 +1581,39 @@ to `transient-predicate-map'.  Also see 
`transient-base-map'.")
 
 (put 'transient-common-commands
      'transient--layout
-     (cl-mapcan
-      (lambda (s) (transient--parse-child 'transient-common-commands s))
-      `([:hide ,(lambda ()
-                  (and (not (memq (car (bound-and-true-p
-                                        transient--redisplay-key))
-                                  transient--common-command-prefixes))
-                       (not transient-show-common-commands)))
-         ["Value commands"
-          ("C-x s  " "Set"            transient-set)
-          ("C-x C-s" "Save"           transient-save)
-          ("C-x C-k" "Reset"          transient-reset)
-          ("C-x p  " "Previous value" transient-history-prev)
-          ("C-x n  " "Next value"     transient-history-next)]
-         ["Sticky commands"
-          ;; Like `transient-sticky-map' except that
-          ;; "C-g" has to be bound to a different command.
-          ("C-g" "Quit prefix or transient" transient-quit-one)
-          ("C-q" "Quit transient stack"     transient-quit-all)
-          ("C-z" "Suspend transient stack"  transient-suspend)]
-         ["Customize"
-          ("C-x t" transient-toggle-common
-           :description ,(lambda ()
-                           (if transient-show-common-commands
-                               "Hide common commands"
-                             "Show common permanently")))
-          ("C-x l" "Show/hide suffixes" transient-set-level)]])))
+     (list
+      (eval
+       (car (transient--parse-child
+             'transient-common-commands
+             (vector
+              :hide
+              (lambda ()
+                (and (not (memq
+                           (car (bound-and-true-p transient--redisplay-key))
+                           transient--common-command-prefixes))
+                     (not transient-show-common-commands)))
+              (vector
+               "Value commands"
+               (list "C-x s  " "Set"            #'transient-set)
+               (list "C-x C-s" "Save"           #'transient-save)
+               (list "C-x C-k" "Reset"          #'transient-reset)
+               (list "C-x p  " "Previous value" #'transient-history-prev)
+               (list "C-x n  " "Next value"     #'transient-history-next))
+              (vector
+               "Sticky commands"
+               ;; Like `transient-sticky-map' except that
+               ;; "C-g" has to be bound to a different command.
+               (list "C-g" "Quit prefix or transient" #'transient-quit-one)
+               (list "C-q" "Quit transient stack"     #'transient-quit-all)
+               (list "C-z" "Suspend transient stack"  #'transient-suspend))
+              (vector
+               "Customize"
+               (list "C-x t" 'transient-toggle-common :description
+                     (lambda ()
+                       (if transient-show-common-commands
+                           "Hide common commands"
+                         "Show common permanently")))
+               (list "C-x l" "Show/hide suffixes" #'transient-set-level))))))))
 
 (defvar transient-popup-navigation-map
   (let ((map (make-sparse-keymap)))
@@ -2176,7 +2209,8 @@ value.  Otherwise return CHILDREN as is."
                                   ;; used to call another command
                                   ;; that also uses the minibuffer.
                                   (equal
-                                   (string-to-multibyte (this-command-keys))
+                                   (ignore-errors
+                                     (string-to-multibyte (this-command-keys)))
                                    (format "\M-x%s\r" this-command))))))
                 (transient--debug 'post-command-hook "act: %s" act)
                 (when act
@@ -3501,7 +3535,7 @@ Optional support for popup buttons is also implemented 
here."
 
 (cl-defmethod transient-format-description ((obj transient-child))
   "The `description' slot may be a function, in which case that is
-called inside the correct buffer (see `transient-insert-group')
+called inside the correct buffer (see `transient--insert-group')
 and its value is returned to the caller."
   (and-let* ((desc (oref obj description)))
     (if (functionp desc)
@@ -3669,7 +3703,14 @@ manpage, then try to jump to the correct location."
 
 (defun transient--describe-function (fn)
   (describe-function (if (symbolp fn) fn 'transient--anonymous-infix-argument))
-  (select-window (get-buffer-window (help-buffer))))
+  (unless (derived-mode-p 'help-mode)
+    (when-let* ((buf (get-buffer "*Help*"))
+                (win (or (and buf (get-buffer-window buf))
+                         (cl-find-if (lambda (win)
+                                       (with-current-buffer (window-buffer win)
+                                         (derived-mode-p 'help-mode)))
+                                     (window-list)))))
+      (select-window win))))
 
 (defun transient--anonymous-infix-argument ()
   "Cannot show any documentation for this anonymous infix command.
diff --git a/lisp/url/url-util.el b/lisp/url/url-util.el
index 147a643c9f..95c0fe14f3 100644
--- a/lisp/url/url-util.el
+++ b/lisp/url/url-util.el
@@ -63,9 +63,9 @@ If a list, it is a list of the types of messages to be 
logged."
          (and (listp url-debug) (memq tag url-debug)))
       (with-current-buffer (get-buffer-create "*URL-DEBUG*")
        (goto-char (point-max))
-       (insert (symbol-name tag) " -> " (apply 'format args) "\n")
+       (insert (symbol-name tag) " -> " (apply #'format args) "\n")
        (if (numberp url-debug)
-           (apply 'message args)))))
+           (apply #'message args)))))
 
 ;;;###autoload
 (defun url-parse-args (str &optional nodowncase)
@@ -125,23 +125,13 @@ conversion.  Replaces these characters as follows:
     <  ==>  &lt;
     >  ==>  &gt;
     \"  ==>  &quot;"
-  (if (string-match "[&<>\"]" string)
-      (with-current-buffer (get-buffer-create " *entity*")
-       (erase-buffer)
-       (buffer-disable-undo (current-buffer))
-       (insert string)
-       (goto-char (point-min))
-       (while (progn
-                (skip-chars-forward "^&<>\"")
-                (not (eobp)))
-         (insert (cdr (assq (char-after (point))
-                            '((?\" . "&quot;")
-                              (?& . "&amp;")
-                              (?< . "&lt;")
-                              (?> . "&gt;")))))
-         (delete-char 1))
-       (buffer-string))
-    string))
+  (replace-regexp-in-string "[&<>\"]"
+                            (lambda (c) (cdr (assq (aref c 0)
+                                             '((?\" . "&quot;")
+                                               (?& . "&amp;")
+                                               (?< . "&lt;")
+                                               (?> . "&gt;")))))
+                           string))
 
 ;;;###autoload
 (defun url-normalize-url (url)
@@ -169,7 +159,7 @@ Will not do anything if `url-show-status' is nil."
          (= url-lazy-message-time
             (setq url-lazy-message-time (time-convert nil 'integer))))
       nil
-    (apply 'message args)))
+    (apply #'message args)))
 
 ;;;###autoload
 (defun url-get-normalized-date (&optional specified-time)
@@ -186,7 +176,7 @@ Will not do anything if `url-show-status' is nil."
   #'string-trim-left "29.1")
 
 (define-obsolete-function-alias 'url-pretty-length
-  'file-size-human-readable "24.4")
+  #'file-size-human-readable "24.4")
 
 ;;;###autoload
 (defun url-display-message (fmt &rest args)
@@ -206,7 +196,7 @@ Will not do anything if `url-show-status' is nil."
   (round (* 100 (/ x (float y)))))
 
 ;;;###autoload
-(defalias 'url-basepath 'url-file-directory)
+(defalias 'url-basepath #'url-file-directory)
 
 ;;;###autoload
 (defun url-file-directory (file)
@@ -395,8 +385,7 @@ if character N is allowed."
                 (aref url-encoding-table byte)))
             (if (multibyte-string-p string)
                 (encode-coding-string string 'utf-8)
-              string)
-            ""))
+              string)))
 
 (defconst url-host-allowed-chars
   ;; Allow % to avoid re-encoding %-encoded sequences.
diff --git a/lisp/url/url.el b/lisp/url/url.el
index b4ece5faeb..92057742ca 100644
--- a/lisp/url/url.el
+++ b/lisp/url/url.el
@@ -293,7 +293,7 @@ how long to wait for a response before giving up."
 (declare-function mm-display-part "mm-decode"
                  (handle &optional no-default force))
 
-(defun url-mm-callback (&rest ignored)
+(defun url-mm-callback (&rest _ignored)
   (let ((handle (mm-dissect-buffer t)))
     (url-mark-buffer-as-dead (current-buffer))
     (with-current-buffer
diff --git a/lisp/vc/smerge-mode.el b/lisp/vc/smerge-mode.el
index 003b26eca4..e13894d6b5 100644
--- a/lisp/vc/smerge-mode.el
+++ b/lisp/vc/smerge-mode.el
@@ -23,11 +23,10 @@
 ;;; Commentary:
 
 ;; Provides a lightweight alternative to emerge/ediff.
-;; To use it, simply add to your .emacs the following lines:
 ;;
-;;   (autoload 'smerge-mode "smerge-mode" nil t)
+;; To use it, simply type `M-x smerge-mode'.
 ;;
-;; you can even have it turned on automatically with the following
+;; You can even have it turned on automatically with the following
 ;; piece of code in your .emacs:
 ;;
 ;;   (defun sm-try-smerge ()
diff --git a/lisp/vc/vc-bzr.el b/lisp/vc/vc-bzr.el
index 6f77f99555..8f00441e81 100644
--- a/lisp/vc/vc-bzr.el
+++ b/lisp/vc/vc-bzr.el
@@ -532,6 +532,12 @@ in the branch repository (or whose status not be 
determined)."
     (add-hook 'after-save-hook #'vc-bzr-resolve-when-done nil t)
     (vc-message-unresolved-conflicts buffer-file-name)))
 
+(defun vc-bzr-clone (remote directory rev)
+  (if rev
+      (vc-bzr-command nil 0 '() "branch" "-r" rev remote directory)
+    (vc-bzr-command nil 0 '() "branch" remote directory))
+  directory)
+
 (defun vc-bzr-version-dirstate (dir)
   "Try to return as a string the bzr revision ID of directory DIR.
 This uses the dirstate file's parent revision entry.
diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el
index ea06ccaf87..a1ff03144b 100644
--- a/lisp/vc/vc-git.el
+++ b/lisp/vc/vc-git.el
@@ -1267,6 +1267,12 @@ This prompts for a branch to merge from."
       (add-hook 'after-save-hook #'vc-git-resolve-when-done nil 'local))
     (vc-message-unresolved-conflicts buffer-file-name)))
 
+(defun vc-git-clone (remote directory rev)
+  (if rev
+      (vc-git--out-ok "clone" "--branch" rev remote directory)
+    (vc-git--out-ok "clone" remote directory))
+  directory)
+
 ;;; HISTORY FUNCTIONS
 
 (autoload 'vc-setup-buffer "vc-dispatcher")
@@ -1625,6 +1631,19 @@ This requires git 1.8.4 or later, for the \"-L\" option 
of \"git log\"."
                    (expand-file-name fname (vc-git-root default-directory))))
          revision)))))
 
+(defun vc-git-last-change (file line)
+  (vc-buffer-sync)
+  (let ((file (file-relative-name file (vc-git-root (buffer-file-name)))))
+    (with-temp-buffer
+      (when (vc-git--out-ok
+             "blame" "--porcelain"
+             (format "-L%d,+1" line)
+             file)
+        (goto-char (point-min))
+        (save-match-data
+          (when (looking-at "\\`\\([[:alnum:]]+\\)[[:space:]]+")
+            (match-string 1)))))))
+
 ;;; TAG/BRANCH SYSTEM
 
 (declare-function vc-read-revision "vc"
@@ -1864,7 +1883,8 @@ This command shares argument histories with \\[rgrep] and 
\\[grep]."
   "Show the contents of stash NAME."
   (interactive (list (vc-git-stash-read "Show stash: ")))
   (vc-setup-buffer "*vc-git-stash*")
-  (vc-git-command "*vc-git-stash*" 'async nil "stash" "show" "-p" name)
+  (vc-git-command "*vc-git-stash*" 'async nil
+                  "stash" "show" "--color=never" "-p" name)
   (set-buffer "*vc-git-stash*")
   (setq buffer-read-only t)
   (diff-mode)
@@ -2025,19 +2045,23 @@ FILE can be nil."
                     (setq ok nil))))))
     (and ok str)))
 
-(defun vc-git-symbolic-commit (commit)
-  "Translate COMMIT string into symbolic form.
-Returns nil if not possible."
+(defun vc-git-symbolic-commit (commit &optional force)
+  "Translate revision string of COMMIT to a symbolic form.
+If the optional argument FORCE is non-nil, the returned value is
+allowed to include revision specifications like \"master~8\"
+\(the 8th parent of the commit currently pointed to by the master
+branch), otherwise such revision specifications are rejected, and
+the function returns nil."
   (and commit
-       (let ((name (with-temp-buffer
-                     (and
-                      (vc-git--out-ok "name-rev" "--name-only" commit)
-                      (goto-char (point-min))
-                      (= (forward-line 2) 1)
-                      (bolp)
-                      (buffer-substring-no-properties (point-min)
-                                                      (1- (point-max)))))))
-         (and name (not (string= name "undefined")) name))))
+       (with-temp-buffer
+         (and
+          (vc-git--out-ok "name-rev" "--no-undefined" "--name-only" commit)
+          (goto-char (point-min))
+          (or force (not (looking-at "^.*[~^].*$" t)))
+          (= (forward-line 2) 1)
+          (bolp)
+          (buffer-substring-no-properties (point-min)
+                                          (1- (point-max)))))))
 
 (defvar-keymap vc-dir-git-mode-map
   "z c" #'vc-git-stash
diff --git a/lisp/vc/vc-hg.el b/lisp/vc/vc-hg.el
index 2eebe2d543..90903255e0 100644
--- a/lisp/vc/vc-hg.el
+++ b/lisp/vc/vc-hg.el
@@ -51,6 +51,7 @@
 ;; - receive-file (file rev)                   ?? PROBABLY NOT NEEDED
 ;; - unregister (file)                         OK
 ;; * checkin (files rev comment)               OK
+;; - checkin-patch (patch-string comment)      OK
 ;; * find-revision (file rev buffer)           OK
 ;; * checkout (file &optional rev)             OK
 ;; * revert (file &optional contents-done)     OK
@@ -1189,16 +1190,31 @@ It is based on `log-edit-mode', and has Hg-specific 
extensions.")
 (defun vc-hg-checkin (files comment &optional _rev)
   "Hg-specific version of `vc-backend-checkin'.
 REV is ignored."
-  (let ((amend-extract-fn
-         (lambda (value)
-           (when (equal value "yes")
-             (list "--amend")))))
-    (apply #'vc-hg-command nil 0 files
-           (nconc (list "commit" "-m")
-                  (log-edit-extract-headers `(("Author" . "--user")
-                                              ("Date" . "--date")
-                                              ("Amend" . ,amend-extract-fn))
-                                            comment)))))
+  (apply #'vc-hg-command nil 0 files
+         (nconc (list "commit" "-m")
+                (vc-hg--extract-headers comment))))
+
+(defun vc-hg-checkin-patch (patch-string comment)
+  (let ((patch-file (make-temp-file "hg-patch")))
+    (write-region patch-string nil patch-file)
+    (unwind-protect
+        (progn
+          (apply #'vc-hg-command nil 0 nil
+                 (nconc (list "import" "--bypass" patch-file "-m")
+                        (vc-hg--extract-headers comment)))
+          (vc-hg-command nil 0 nil
+                         "update"
+                         "--merge" "--tool" "internal:local"
+                         "tip"))
+      (delete-file patch-file))))
+
+(defun vc-hg--extract-headers (comment)
+  (log-edit-extract-headers `(("Author" . "--user")
+                              ("Date" . "--date")
+                              ("Amend" . (lambda (value)
+                                           (when (equal value "yes")
+                                             (list "--amend")))))
+                            comment))
 
 (defun vc-hg-find-revision (file rev buffer)
   (let ((coding-system-for-read 'binary)
@@ -1250,6 +1266,12 @@ REV is the revision to check out into WORKFILE."
     (add-hook 'after-save-hook #'vc-hg-resolve-when-done nil t)
     (vc-message-unresolved-conflicts buffer-file-name)))
 
+(defun vc-hg-clone (remote directory rev)
+  (if rev
+      (vc-hg-command nil 0 '() "clone" "--rev" rev remote directory)
+    (vc-hg-command nil 0 '() "clone" remote directory))
+
+  directory)
 
 ;; Modeled after the similar function in vc-bzr.el
 (defun vc-hg-revert (file &optional contents-done)
diff --git a/lisp/vc/vc-svn.el b/lisp/vc/vc-svn.el
index 9c2bdf6674..1b43ca5787 100644
--- a/lisp/vc/vc-svn.el
+++ b/lisp/vc/vc-svn.el
@@ -364,7 +364,7 @@ DIRECTORY or absolute."
   (with-temp-buffer
     (when (zerop (vc-svn-command
                   t t nil "propget" "svn:ignore" (expand-file-name directory)))
-      (split-string (buffer-string) "\n"))))
+      (split-string (buffer-string) "\n" t))))
 
 (defun vc-svn-find-admin-dir (file)
   "Return the administrative directory of FILE."
@@ -817,6 +817,13 @@ Set file properties accordingly.  If FILENAME is non-nil, 
return its status."
                       "info" "--show-item" "repos-root-url")
       (buffer-substring-no-properties (point-min) (1- (point-max))))))
 
+(defun vc-svn-clone (remote directory rev)
+  (if rev
+      (vc-svn-command nil 0 '() "checkout" "--revision" rev remote directory)
+    (vc-svn-command nil 0 '() "checkout" remote directory))
+
+  (file-name-concat directory "trunk"))
+
 (provide 'vc-svn)
 
 ;;; vc-svn.el ends here
diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el
index 60481318e3..513fbb23fe 100644
--- a/lisp/vc/vc.el
+++ b/lisp/vc/vc.el
@@ -448,6 +448,11 @@
 ;; - mergebase (rev1 &optional rev2)
 ;;
 ;;   Return the common ancestor between REV1 and REV2 revisions.
+;;
+;; - last-change (file line)
+;;
+;;   Return the most recent revision of FILE that made a change
+;;   on LINE.
 
 ;; TAG/BRANCH SYSTEM
 ;;
@@ -584,6 +589,15 @@
 ;;   buffer should be inserted into an inline patch.  If the two last
 ;;   properties are omitted, `point-min' and `point-max' will
 ;;   respectively be used instead.
+;;
+;; - clone (remote directory rev)
+;;
+;;   Attempt to clone a REMOTE repository, into a local DIRECTORY.
+;;   Returns a string with the directory with the contents of the
+;;   repository if successful, otherwise nil.  With a non-nil value
+;;   for REV the backend will attempt to check out a specific
+;;   revision, if possible without first checking out the default
+;;   branch.
 
 ;;; Changes from the pre-25.1 API:
 ;;
@@ -1683,6 +1697,47 @@ Runs the normal hooks `vc-before-checkin-hook' and 
`vc-checkin-hook'."
    backend
    patch-string))
 
+(defun vc-default-checkin-patch (_backend patch-string comment)
+  (pcase-let* ((`(,backend ,files) (with-temp-buffer
+                                     (insert patch-string)
+                                     (diff-vc-deduce-fileset)))
+               (tmpdir (make-temp-file "vc-checkin-patch" t)))
+    (dolist (f files)
+      (make-directory (file-name-directory (expand-file-name f tmpdir)) t)
+      (copy-file (expand-file-name f)
+                 (expand-file-name f tmpdir)))
+    (unwind-protect
+        (progn
+          (dolist (f files)
+            (with-current-buffer (find-file-noselect f)
+              (vc-revert-file buffer-file-name)))
+          (with-temp-buffer
+            ;; Trying to support CVS too.  Assuming that vc-diff
+            ;; there will usually have diff root in default-directory.
+            (when (vc-find-backend-function backend 'root)
+              (setq-local default-directory
+                          (vc-call-backend backend 'root (car files))))
+            (unless (eq 0
+                        (call-process-region patch-string
+                                             nil
+                                             "patch"
+                                             nil
+                                             t
+                                             nil
+                                             "-p1"
+                                             "-r" null-device
+                                             "--no-backup-if-mismatch"
+                                             "-i" "-"))
+              (user-error "Patch failed: %s" (buffer-string))))
+          (vc-call-backend backend 'checkin files comment))
+      (dolist (f files)
+        (copy-file (expand-file-name f tmpdir)
+                   (expand-file-name f)
+                   t)
+        (with-current-buffer (get-file-buffer f)
+          (revert-buffer t t t)))
+      (delete-directory tmpdir t))))
+
 ;;; Additional entry points for examining version histories
 
 ;; (defun vc-default-diff-tree (backend dir rev1 rev2)
@@ -3315,7 +3370,7 @@ If nil, no default will be used.  This option may be set 
locally."
                   (buffer &optional type description disposition))
 (declare-function log-view-get-marked "log-view" ())
 
-(defun vc-default-prepare-patch (rev)
+(defun vc-default-prepare-patch (_backend rev)
   (let ((backend (vc-backend buffer-file-name)))
     (with-current-buffer (generate-new-buffer " *vc-default-prepare-patch*")
       (vc-diff-internal
@@ -3341,8 +3396,12 @@ invidividual commits.
 When invoked interactively in a Log View buffer with marked
 revisions, those revisions will be used."
   (interactive
-   (let ((revs (or (log-view-get-marked)
-                   (vc-read-multiple-revisions "Revisions: ")))
+   (let ((revs (vc-read-multiple-revisions
+                "Revisions: " nil nil nil
+                (or (and-let* ((revs (log-view-get-marked)))
+                      (mapconcat #'identity revs ","))
+                    (and-let* ((file (buffer-file-name)))
+                      (vc-working-revision file)))))
          to)
      (require 'message)
      (while (null (setq to (completing-read-multiple
@@ -3366,19 +3425,19 @@ revisions, those revisions will be used."
                               'prepare-patch rev))
                            revisions)))
       (if vc-prepare-patches-separately
-          (dolist (patch patches)
+          (dolist (patch (reverse patches)
+                         (message "Prepared %d patch%s..." (length patches)
+                                  (if (length> patches 1) "es" "")))
             (compose-mail addressee
                           (plist-get patch :subject)
                           nil nil nil nil
-                          `((kill-buffer ,(plist-get patch :buffer))
-                            (exit-recursive-edit)))
+                          `((kill-buffer ,(plist-get patch :buffer))))
             (rfc822-goto-eoh) (forward-line)
             (save-excursion             ;don't jump to the end
               (insert-buffer-substring
                (plist-get patch :buffer)
                (plist-get patch :body-start)
-               (plist-get patch :body-end)))
-            (recursive-edit))
+               (plist-get patch :body-end))))
         (compose-mail addressee subject nil nil nil nil
                       (mapcar
                        (lambda (p)
@@ -3506,6 +3565,43 @@ to provide the `find-revision' operation instead."
   (interactive)
   (vc-call-backend (vc-backend buffer-file-name) 'check-headers))
 
+(defun vc-clone (remote &optional backend directory rev)
+  "Use BACKEND to clone REMOTE into DIRECTORY.
+If successful, returns the a 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))
+  (if backend
+      (progn
+        (unless (memq backend vc-handled-backends)
+          (error "Unknown VC backend %s" backend))
+        (vc-call-backend backend 'clone remote directory rev))
+    (catch 'ok
+      (dolist (backend vc-handled-backends)
+        (ignore-error vc-not-supported
+          (when-let ((res (vc-call-backend
+                           backend 'clone
+                           remote directory rev)))
+            (throw 'ok res)))))))
+
+(declare-function log-view-current-tag "log-view" (&optional pos))
+(defun vc-default-last-change (_backend file line)
+  "Default `last-change' implementation.
+It returns the last revision that changed LINE number in FILE."
+  (unless (file-exists-p file)
+    (signal 'file-error "File doesn't exist"))
+  (with-temp-buffer
+    (vc-call-backend (vc-backend file) 'annotate-command
+                     file (current-buffer))
+    (goto-char (point-min))
+    (forward-line (1- line))
+    (let ((rev (vc-call-backend
+                (vc-backend file)
+                'annotate-extract-revision-at-line)))
+      (if (consp rev) (car rev) rev))))
+
 
 
 ;; These things should probably be generally available
diff --git a/lisp/vcursor.el b/lisp/vcursor.el
index e7dd1ba715..896df91983 100644
--- a/lisp/vcursor.el
+++ b/lisp/vcursor.el
@@ -1,7 +1,6 @@
 ;;; vcursor.el --- manipulate an alternative ("virtual") cursor  -*- 
lexical-binding: t; -*-
 
-;; Copyright (C) 1994, 1996, 1998, 2001-2022 Free Software Foundation,
-;; Inc.
+;; Copyright (C) 1994-2022 Free Software Foundation, Inc.
 
 ;; Author: Peter Stephenson <pws@ibmth.df.unipi.it>
 ;; Maintainer: emacs-devel@gnu.org
@@ -39,7 +38,7 @@
 ;;   off after any operation not involving the vcursor, but the
 ;;   vcursor itself will be left alone.
 ;; - works on dumb terminals
-;; - new keymap vcursor-map for binding to a prefix key
+;; - new keymap `vcursor-map' for binding to a prefix key
 ;; - `vcursor-compare-windows' substantially improved
 ;; - `vcursor-execute-{key,command}' much better about using the
 ;;   right keymaps and arranging for the correct windows to be used
@@ -339,8 +338,6 @@ disable the vcursor."
                (cons 'meta key)
              key))))
 
-;; (defvar vcursor)
-
 (defun vcursor-bind-keys (var value)
   "Alter the value of the variable VAR to VALUE, binding keys as required.
 VAR is usually `vcursor-key-bindings'.  Normally this function is called
@@ -468,38 +465,36 @@ scrolling set this.  It is used by the 
`vcursor-auto-disable' code.")
 (defvar vcursor-temp-goal-column nil
   "Keeps track of temporary goal columns for the virtual cursor.")
 
-(defvar vcursor-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map "t" #'vcursor-use-vcursor-map)
-
-    (define-key map "\C-p" #'vcursor-previous-line)
-    (define-key map "\C-n" #'vcursor-next-line)
-    (define-key map "\C-b" #'vcursor-backward-char)
-    (define-key map "\C-f" #'vcursor-forward-char)
-
-    (define-key map "\r"   #'vcursor-disable)
-    (define-key map " "    #'vcursor-copy)
-    (define-key map "\C-y" #'vcursor-copy-word)
-    (define-key map "\C-i" #'vcursor-toggle-copy)
-    (define-key map "<"    #'vcursor-beginning-of-buffer)
-    (define-key map ">"    #'vcursor-end-of-buffer)
-    (define-key map "\M-v" #'vcursor-scroll-down)
-    (define-key map "\C-v" #'vcursor-scroll-up)
-    (define-key map "o"    #'vcursor-other-window)
-    (define-key map "g"    #'vcursor-goto)
-    (define-key map "x"    #'vcursor-swap-point)
-    (define-key map "\C-s" #'vcursor-isearch-forward)
-    (define-key map "\C-r" #'vcursor-isearch-backward)
-    (define-key map "\C-a" #'vcursor-beginning-of-line)
-    (define-key map "\C-e" #'vcursor-end-of-line)
-    (define-key map "\M-w" #'vcursor-forward-word)
-    (define-key map "\M-b" #'vcursor-backward-word)
-    (define-key map "\M-l" #'vcursor-copy-line)
-    (define-key map "c"    #'vcursor-compare-windows)
-    (define-key map "k"    #'vcursor-execute-key)
-    (define-key map "\M-x" #'vcursor-execute-command)
-    map)
-  "Keymap for vcursor command.")
+(defvar-keymap vcursor-map
+  :doc "Keymap for vcursor command."
+  "t"   #'vcursor-use-vcursor-map
+
+  "C-p" #'vcursor-previous-line
+  "C-n" #'vcursor-next-line
+  "C-b" #'vcursor-backward-char
+  "C-f" #'vcursor-forward-char
+
+  "RET" #'vcursor-disable
+  "SPC" #'vcursor-copy
+  "C-y" #'vcursor-copy-word
+  "C-i" #'vcursor-toggle-copy
+  "<"   #'vcursor-beginning-of-buffer
+  ">"   #'vcursor-end-of-buffer
+  "M-v" #'vcursor-scroll-down
+  "C-v" #'vcursor-scroll-up
+  "o"   #'vcursor-other-window
+  "g"   #'vcursor-goto
+  "x"   #'vcursor-swap-point
+  "C-s" #'vcursor-isearch-forward
+  "C-r" #'vcursor-isearch-backward
+  "C-a" #'vcursor-beginning-of-line
+  "C-e" #'vcursor-end-of-line
+  "M-w" #'vcursor-forward-word
+  "M-b" #'vcursor-backward-word
+  "M-l" #'vcursor-copy-line
+  "c"   #'vcursor-compare-windows
+  "k"   #'vcursor-execute-key
+  "M-x" #'vcursor-execute-command)
 ;; This seems unused, but it was done as part of define-prefix-command,
 ;; so let's keep it for now.
 (fset 'vcursor-map vcursor-map)
@@ -515,7 +510,6 @@ scrolling set this.  It is used by the 
`vcursor-auto-disable' code.")
 If that's disabled, don't go anywhere but don't complain."
   ;; This is where we go off-mass-shell.  Assume there is a
   ;; save-excursion to get us back to the pole, er, point.
-
   (and (overlayp vcursor-overlay)
        (overlay-buffer vcursor-overlay)
        (set-buffer (overlay-buffer vcursor-overlay))
@@ -538,7 +532,6 @@ always considered, and the value of `pop-up-frames' is 
always respected).
 
 Returns nil if the virtual cursor is not visible anywhere suitable.
 Set `vcursor-window' to the returned value as a side effect."
-
   ;; The order of priorities (respecting NOT-THIS) is (1)
   ;; vcursor-window if the virtual cursor is visible there (2) any
   ;; window displaying the virtual cursor (3) vcursor-window provided
@@ -547,7 +540,6 @@ Set `vcursor-window' to the returned value as a side 
effect."
   ;; buffer (5) with NEW-WIN, a window selected by display-buffer (so
   ;; the variables pop-up-windows and pop-up-frames are significant)
   ;; (6) nil.
-
   (let ((thiswin (selected-window)) winok winbuf)
     (save-excursion
       (vcursor-locate)
@@ -652,7 +644,6 @@ This is called by most of the virtual-cursor motion 
commands."
 If the virtual cursor is (or was recently) visible in another window,
 switch to that first.  Without a prefix ARG, disable the virtual
 cursor as well."
-
   (interactive "P")
   (and (vcursor-find-window) (select-window vcursor-window))
   (let ((buf (and vcursor-overlay (overlay-buffer vcursor-overlay))))
@@ -667,7 +658,6 @@ cursor as well."
 The virtual cursor window becomes the selected window and the old
 window becomes the virtual cursor window.  If the virtual cursor would
 not be visible otherwise, display it in another window."
-
   (interactive)
   (let ((buf (current-buffer)) (here (point)) (win (selected-window)))
     (vcursor-goto) ; will disable the vcursor
@@ -679,14 +669,12 @@ not be visible otherwise, display it in another window."
 (defun vcursor-scroll-up (&optional n)
   "Scroll up the vcursor window ARG lines or near full screen if none.
 The vcursor will always appear in an unselected window."
-
   (interactive "P")
   (vcursor-window-funcall #'scroll-up n))
 
 (defun vcursor-scroll-down (&optional n)
   "Scroll down the vcursor window ARG lines or near full screen if none.
 The vcursor will always appear in an unselected window."
-
   (interactive "P")
   (vcursor-window-funcall #'scroll-down n))
 
@@ -694,7 +682,6 @@ The vcursor will always appear in an unselected window."
   "Perform forward incremental search in the virtual cursor window.
 The virtual cursor is moved to the resulting point; the ordinary
 cursor stays where it was."
-
   (interactive "P")
   (vcursor-window-funcall #'isearch-forward rep norecurs)
   )
@@ -703,7 +690,6 @@ cursor stays where it was."
   "Perform backward incremental search in the virtual cursor window.
 The virtual cursor is moved to the resulting point; the ordinary
 cursor stays where it was."
-
   (interactive "P")
   (vcursor-window-funcall #'isearch-backward rep norecurs)
   )
@@ -719,7 +705,6 @@ ARGS.  In this case, a new window will not be created if 
the vcursor
 is visible in the current one."
 ;; that's to avoid messing up compatibility with old versions
 ;; by introducing a new argument, which would have to come before ARGS.
-
   (vcursor-find-window (not (and (listp func) (vcursor-check t))) t)
   (save-excursion
     (let ((sw (selected-window)) text)
@@ -751,7 +736,6 @@ is called.
 
 This is called by most of the virtual-cursor copying commands to find
 out how much to copy."
-
   (vcursor-check)
   (with-current-buffer (overlay-buffer vcursor-overlay)
     (save-excursion
@@ -792,7 +776,6 @@ active at the same point as the real cursor.
 
 Copying mode is always turned off: the next use of the vcursor will
 not copy text until you turn it on again."
-
   (interactive "P")
   (if (overlayp vcursor-overlay)
       (progn
@@ -1078,7 +1061,6 @@ With no argument, copy to the end of the current line.
 Behavior with regard to newlines is similar (but not identical) to
 `kill-line'; the main difference is that whitespace at the end of the
 line is treated like ordinary characters."
-
   (interactive "P")
   (let* ((num (prefix-numeric-value arg))
         (count (vcursor-get-char-count #'end-of-line num)))
diff --git a/lisp/whitespace.el b/lisp/whitespace.el
index d7b83ef34a..791a0a0b4e 100644
--- a/lisp/whitespace.el
+++ b/lisp/whitespace.el
@@ -272,42 +272,40 @@
     indentation empty space-after-tab
     space-mark tab-mark newline-mark
     missing-newline-at-eof)
-  "Specify which kind of blank is visualized.
+  "Determine the kinds of whitespace are visualized.
 
-It's a list containing some or all of the following values:
+The value is a list containing one or more of the following symbols:
 
-   face                 enable all visualization via faces (see below).
+   face                 visualize by using faces (see below).
 
-   trailing             trailing blanks are visualized via faces.
-                        It has effect only if `face' (see above)
+   trailing             visualize trailing blanks via faces.
+                        This has effect only if `face' (see above)
                         is present in `whitespace-style'.
 
-   tabs                 TABs are visualized via faces.
-                        It has effect only if `face' (see above)
+   tabs                 visualize TABs via faces.
+                        This has effect only if `face' (see above)
                         is present in `whitespace-style'.
 
-   spaces               SPACEs and HARD SPACEs are visualized via
+   spaces               visualize SPACEs and HARD SPACEs via
                         faces.
-                        It has effect only if `face' (see above)
+                        This has effect only if `face' (see above)
                         is present in `whitespace-style'.
 
-   lines                lines which have columns beyond
-                        `whitespace-line-column' are highlighted via
-                        faces.
+   lines                highlight lines which have columns beyond
+                        `whitespace-line-column' via faces.
                         Whole line is highlighted.
-                        It has precedence over `lines-tail' and
+                        This has precedence over `lines-tail' and
                         `lines-char' (see below).
-                        It has effect only if `face' (see above)
+                        This has effect only if `face' (see above)
                         is present in `whitespace-style'.
 
-   lines-tail           lines which have columns beyond
-                        `whitespace-line-column' are highlighted via
-                        faces.
-                        But only the part of line which goes
-                        beyond `whitespace-line-column' column.
-                        It has effect only if `lines' (see above)
-                        is not present in `whitespace-style'
-                        and if `face' (see above) is present in
+   lines-tail           highlighted lines which have columns beyond
+                        `whitespace-line-column' via faces.
+                        Only the part of line which goes beyond
+                        `whitespace-line-column' column.
+                        This has effect only if `lines' (see above)
+                        is NOT present in `whitespace-style',
+                        and if `face' (see above) IS present in
                         `whitespace-style'.
 
    lines-char           lines which have columns beyond
@@ -319,82 +317,81 @@ It's a list containing some or all of the following 
values:
                         in `whitespace-style' and if `face' (see
                         above) is present in `whitespace-style'.
 
-   newline              NEWLINEs are visualized via faces.
-                        It has effect only if `face' (see above)
+   newline              visualize NEWLINEs via faces.
+                        This has effect only if `face' (see above)
                         is present in `whitespace-style'.
 
-   missing-newline-at-eof Missing newline at the end of the file is
-                        visualized via faces.
-                        It has effect only if `face' (see above)
+   missing-newline-at-eof visualize missing newline at the end of
+                        the file via faces.
+                        This has effect only if `face' (see above)
                         is present in `whitespace-style'.
 
-   empty                empty lines at beginning and/or end of buffer
-                        are visualized via faces.
-                        It has effect only if `face' (see above)
+   empty                visualize empty lines at beginning and/or
+                        end of buffer via faces.
+                        This has effect only if `face' (see above)
                         is present in `whitespace-style'.
 
-   indentation::tab     `tab-width' or more SPACEs at beginning of line
-                        are visualized via faces.
-                        It has effect only if `face' (see above)
+   indentation::tab     visualize `tab-width' or more SPACEs at
+                        beginning of line via faces.
+                        This has effect only if `face' (see above)
                         is present in `whitespace-style'.
 
-   indentation::space   TABs at beginning of line are visualized via
+   indentation::space   visualize TABs at beginning of line via
                         faces.
-                        It has effect only if `face' (see above)
+                        This has effect only if `face' (see above)
                         is present in `whitespace-style'.
 
-   indentation          `tab-width' or more SPACEs at beginning of line
-                        are visualized, if `indent-tabs-mode' (which
-                        see) is non-nil; otherwise, TABs at beginning
-                        of line are visualized via faces.
-                        It has effect only if `face' (see above)
+   indentation          visualize `tab-width' or more SPACEs at
+                        beginning of line, if `indent-tabs-mode' (which
+                        see) is non-nil; otherwise, visualize TABs
+                        at beginning of line via faces.
+                        This has effect only if `face' (see above)
                         is present in `whitespace-style'.
 
-   big-indent           Big indentations are visualized via faces.
-                        It has effect only if `face' (see above)
+   big-indent           visualize big indentations via faces.
+                        This has effect only if `face' (see above)
                         is present in `whitespace-style'.
 
-   space-after-tab::tab         `tab-width' or more SPACEs after a TAB
-                                are visualized via faces.
-                                It has effect only if `face' (see above)
+   space-after-tab::tab         visualize `tab-width' or more SPACEs
+                                after a TAB via faces.
+                                This has effect only if `face' (see above)
                                 is present in `whitespace-style'.
 
-   space-after-tab::space       TABs are visualized when `tab-width' or
+   space-after-tab::space       visualize TABs when `tab-width' or
                                 more SPACEs occur after a TAB, via
                                 faces.
-                                It has effect only if `face' (see above)
+                                This has effect only if `face' (see above)
                                 is present in `whitespace-style'.
 
-   space-after-tab              `tab-width' or more SPACEs after a TAB
-                                are visualized, if `indent-tabs-mode'
+   space-after-tab              visualize `tab-width' or more SPACEs
+                                after a TAB, if `indent-tabs-mode'
                                 (which see) is non-nil; otherwise,
-                                the TABs are visualized via faces.
-                                It has effect only if `face' (see above)
+                                visualize the TABs via faces.
+                                This has effect only if `face' (see above)
                                 is present in `whitespace-style'.
 
-   space-before-tab::tab        SPACEs before TAB are visualized via
-                                faces.
-                                It has effect only if `face' (see above)
+   space-before-tab::tab        visualize SPACEs before TAB via faces.
+                                This has effect only if `face' (see above)
                                 is present in `whitespace-style'.
 
-   space-before-tab::space      TABs are visualized when SPACEs occur
+   space-before-tab::space      visualize TABs when SPACEs occur
                                 before TAB, via faces.
-                                It has effect only if `face' (see above)
+                                This has effect only if `face' (see above)
                                 is present in `whitespace-style'.
 
-   space-before-tab             SPACEs before TAB are visualized, if
+   space-before-tab             visualize SPACEs before TAB, if
                                 `indent-tabs-mode' (which see) is
-                                non-nil; otherwise, the TABs are
-                                visualized via faces.
-                                It has effect only if `face' (see above)
+                                non-nil; otherwise, visualize TABs
+                                via faces.
+                                This has effect only if `face' (see above)
                                 is present in `whitespace-style'.
 
-   space-mark           SPACEs and HARD SPACEs are visualized via
+   space-mark           visualize SPACEs and HARD SPACEs via
                         display table.
 
-   tab-mark             TABs are visualized via display table.
+   tab-mark             visualize TABs via display table.
 
-   newline-mark         NEWLINEs are visualized via display table.
+   newline-mark         visualize NEWLINEs via display table.
 
 Any other value is ignored.
 
@@ -404,8 +401,7 @@ via display table.
 There is an evaluation order for some values, if they are
 included in `whitespace-style' list.  For example, if
 indentation, indentation::tab and/or indentation::space are
-included in `whitespace-style' list.  The evaluation order for
-these values is:
+included in `whitespace-style' list, the evaluation order is:
 
  * For indentation:
    1. indentation
@@ -477,7 +473,9 @@ Used when `whitespace-style' includes the value `spaces'.")
     (((class color) (background light))
      :background "LightYellow" :foreground "lightgray")
     (t :inverse-video t))
-  "Face used to visualize SPACE."
+  "Face used to visualize SPACE.
+
+See `whitespace-space-regexp'."
   :group 'whitespace)
 
 
@@ -492,7 +490,9 @@ Used when `whitespace-style' includes the value `spaces'.")
     (((class color) (background light))
      :background "LemonChiffon3" :foreground "lightgray")
     (t :inverse-video t))
-  "Face used to visualize HARD SPACE."
+  "Face used to visualize HARD SPACE.
+
+See `whitespace-hspace-regexp'."
   :group 'whitespace)
 
 
@@ -508,7 +508,9 @@ Used when `whitespace-style' includes the value `tabs'.")
     (((class color) (background light))
      :background "beige"  :foreground "lightgray")
     (t :inverse-video t))
-  "Face used to visualize TAB."
+  "Face used to visualize TAB.
+
+See `whitespace-tab-regexp'."
   :group 'whitespace)
 
 
@@ -542,7 +544,9 @@ Used when `whitespace-style' includes the value 
`trailing'.")
   '((default :weight bold)
     (((class mono)) :inverse-video t :underline t)
     (t :background "red1" :foreground "yellow"))
-  "Face used to visualize trailing blanks."
+  "Face used to visualize trailing blanks.
+
+See `whitespace-trailing-regexp'."
   :group 'whitespace)
 
 
@@ -570,7 +574,9 @@ Used when `whitespace-style' includes the value 
`space-before-tab'.")
 (defface whitespace-space-before-tab
   '((((class mono)) :inverse-video t :weight bold :underline t)
     (t :background "DarkOrange" :foreground "firebrick"))
-  "Face used to visualize SPACEs before TAB."
+  "Face used to visualize SPACEs before TAB.
+
+See `whitespace-space-before-tab-regexp'."
   :group 'whitespace)
 
 
@@ -582,13 +588,17 @@ Used when `whitespace-style' includes the value 
`indentation'.")
 (defface whitespace-indentation
   '((((class mono)) :inverse-video t :weight bold :underline t)
     (t :background "yellow" :foreground "firebrick"))
-  "Face used to visualize `tab-width' or more SPACEs at beginning of line."
+  "Face used to visualize `tab-width' or more SPACEs at beginning of line.
+
+See `whitespace-indentation-regexp'."
   :group 'whitespace)
 
 (defface whitespace-big-indent
   '((((class mono)) :inverse-video t :weight bold :underline t)
     (t :background "red" :foreground "firebrick"))
-  "Face used to visualize big indentation."
+  "Face used to visualize big indentation.
+
+See `whitespace-big-indent-regexp'."
   :group 'whitespace)
 
 (defface whitespace-missing-newline-at-eof
@@ -604,7 +614,9 @@ Used when `whitespace-style' includes the value `empty'.")
 (defface whitespace-empty
   '((((class mono)) :inverse-video t :weight bold :underline t)
     (t :background "yellow" :foreground "firebrick" :extend t))
-  "Face used to visualize empty lines at beginning and/or end of buffer."
+  "Face used to visualize empty lines at beginning and/or end of buffer.
+
+See `whitespace-empty-at-bob-regexp' and `whitespace-empty-at-eob-regexp."
   :group 'whitespace)
 
 
@@ -617,14 +629,17 @@ Used when `whitespace-style' includes the value 
`space-after-tab'.")
 (defface whitespace-space-after-tab
   '((((class mono)) :inverse-video t :weight bold :underline t)
     (t :background "yellow" :foreground "firebrick"))
-  "Face used to visualize `tab-width' or more SPACEs after TAB."
+  "Face used to visualize `tab-width' or more SPACEs after TAB.
+
+See `whitespace-space-after-tab-regexp'."
   :group 'whitespace)
 
 
 (defcustom whitespace-hspace-regexp
   "\\(\u00A0+\\)"
-  "Specify HARD SPACE characters regexp.
+  "Regexp to match HARD SPACE characters that should be visualized.
 
+The HARD SPACE characters are highlighted using the `whitespace-hspace' face.
 Here are some examples:
 
    \"\\\\(^\\xA0+\\\\)\"               \
@@ -636,19 +651,21 @@ visualize leading and/or trailing HARD SPACEs.
    \"\\t\\\\(\\xA0+\\\\)\\t\"          \
 visualize only HARD SPACEs between TABs.
 
-NOTE: Enclose always by \\\\( and \\\\) the elements to highlight.
+NOTE: Always enclose the elements to highlight in \\\\(...\\\\).
       Use exactly one pair of enclosing \\\\( and \\\\).
 
-Used when `whitespace-style' includes `spaces'."
+This variable is used when `whitespace-style' includes `spaces'."
   :type '(regexp :tag "HARD SPACE Chars")
   :group 'whitespace)
 
 
 (defcustom whitespace-space-regexp "\\( +\\)"
-  "Specify SPACE characters regexp.
+  "Regexp to match SPACE characters that should be visualized.
 
-If you're using `mule' package, there may be other characters
-besides \" \" that should be considered SPACE.
+The SPACE characters are highlighted using the `whitespace-space' face.
+By default only ASCII SPACE character is visualized, but if you
+are typing in some non-Latin language, there may be other
+characters besides \" \" that should be considered SPACE.
 
 Here are some examples:
 
@@ -658,19 +675,21 @@ Here are some examples:
 visualize leading and/or trailing SPACEs.
    \"\\t\\\\( +\\\\)\\t\"      visualize only SPACEs between TABs.
 
-NOTE: Enclose always by \\\\( and \\\\) the elements to highlight.
+NOTE: Always enclose the elements to highlight in \\\\(...\\\\).
       Use exactly one pair of enclosing \\\\( and \\\\).
 
-Used when `whitespace-style' includes `spaces'."
+This variable is used when `whitespace-style' includes `spaces'."
   :type '(regexp :tag "SPACE Chars")
   :group 'whitespace)
 
 
 (defcustom whitespace-tab-regexp "\\(\t+\\)"
-  "Specify TAB characters regexp.
+  "Regexp to match TAB characters that should be visualized.
 
-If you're using `mule' package, there may be other characters
-besides \"\\t\" that should be considered TAB.
+The TAB characters are highlighted using the `whitespace-tab' face.
+By default only ASCII TAB character is visualized, but if you
+are typing in some non-Latin language, there may be other
+characters besides \" \" that should be considered a TAB.
 
 Here are some examples:
 
@@ -680,37 +699,40 @@ Here are some examples:
 visualize leading and/or trailing TABs.
    \" \\\\(\\t+\\\\) \"        visualize only TABs between SPACEs.
 
-NOTE: Enclose always by \\\\( and \\\\) the elements to highlight.
+NOTE: Always enclose the elements to highlight in \\\\(...\\\\).
       Use exactly one pair of enclosing \\\\( and \\\\).
 
-Used when `whitespace-style' includes `tabs'."
+This variable is used when `whitespace-style' includes `tabs'."
   :type '(regexp :tag "TAB Chars")
   :group 'whitespace)
 
 
 (defcustom whitespace-trailing-regexp
   "\\([\t \u00A0]+\\)$"
-  "Specify trailing characters regexp.
+  "Regexp to match trailing characters that should be visualized.
 
+The trailing characters are highlighted using the `whitespace-trailing' face.
 There may be other characters besides:
 
    \" \"  \"\\t\"  \"\\u00A0\"
 
 that should be considered blank.
 
-NOTE: Enclose always by \"\\\\(\" and \"\\\\)$\" the elements to highlight.
+NOTE: Always enclose the elements to highlight in \"\\\\(\"...\"\\\\)$\".
       Use exactly one pair of enclosing elements above.
 
-Used when `whitespace-style' includes `trailing'."
+This variable is used when `whitespace-style' includes `trailing'."
   :type '(regexp :tag "Trailing Chars")
   :group 'whitespace)
 
 
 (defcustom whitespace-space-before-tab-regexp "\\( +\\)\\(\t+\\)"
-  "Specify SPACEs before TAB regexp.
+  "Regexp to match SPACEs before TAB that should be visualized.
 
-Used when `whitespace-style' includes `space-before-tab',
-`space-before-tab::tab' or  `space-before-tab::space'."
+The SPACE characters are highlighted using the `whitespace-space-before-tab'
+face.
+This variable is used when `whitespace-style' includes
+`space-before-tab', `space-before-tab::tab' or `space-before-tab::space'."
   :type '(regexp :tag "SPACEs Before TAB")
   :group 'whitespace)
 
@@ -718,12 +740,15 @@ Used when `whitespace-style' includes `space-before-tab',
 (defcustom whitespace-indentation-regexp
   '("^\t*\\(\\( \\{%d\\}\\)+\\)[^\n\t]"
     . "^ *\\(\t+\\)[^\n]")
-  "Specify regexp for `tab-width' or more SPACEs at beginning of line.
+  "Regexps to match indentation whitespace that should be visualized.
 
-It is a cons where the cons car is used for SPACEs visualization
-and the cons cdr is used for TABs visualization.
+The value should be a cons whose car specifies the regexp to match
+visualization of SPACEs, and the cdr specifies the regexp to match
+visualization of TABs.
 
-Used when `whitespace-style' includes `indentation',
+The indentation characters are highlighted using the `whitespace-indentationp'
+face.
+This variable is used when `whitespace-style' includes `indentation',
 `indentation::tab' or  `indentation::space'."
   :type '(cons (string :tag "Indentation SPACEs")
               (regexp :tag "Indentation TABs"))
@@ -731,17 +756,19 @@ Used when `whitespace-style' includes `indentation',
 
 
 (defcustom whitespace-empty-at-bob-regexp "\\`\\([ \t\n]*\\(?:\n\\|$\\)\\)"
-  "Specify regexp for empty lines at beginning of buffer.
+  "Regexp to match empty lines at beginning of buffer that should be 
visualized.
 
-Used when `whitespace-style' includes `empty'."
+The empty lines are highlighted using the `whitespace-empty' face.
+This variable is used when `whitespace-style' includes `empty'."
   :type '(regexp :tag "Empty Lines At Beginning Of Buffer")
   :group 'whitespace)
 
 
 (defcustom whitespace-empty-at-eob-regexp "^\\([ \t\n]+\\)\\'"
-  "Specify regexp for empty lines at end of buffer.
+  "Regexp to match empty lines at end of buffer that should be visualized.
 
-Used when `whitespace-style' includes `empty'."
+The empty lines are highlighted using the `whitespace-empty' face.
+This variable is used when `whitespace-style' includes `empty'."
   :type '(regexp :tag "Empty Lines At End Of Buffer")
   :group 'whitespace)
 
@@ -749,12 +776,16 @@ Used when `whitespace-style' includes `empty'."
 (defcustom whitespace-space-after-tab-regexp
   '("\t+\\(\\( \\{%d,\\}\\)+\\)"
     . "\\(\t+\\) \\{%d,\\}")
-  "Specify regexp for `tab-width' or more SPACEs after TAB.
+  "Regexps to match multiple SPACEs after TAB that should be visualized.
 
-It is a cons where the cons car is used for SPACEs visualization
-and the cons cdr is used for TABs visualization.
+The SPACE and TAB characters will be visualized if there at least
+as many SPACEs as `tab-width' after a TAB.
+The value should be a cons whose car is used for SPACEs visualization
+and whose cdr is used for TABs visualization.
 
-Used when `whitespace-style' includes `space-after-tab',
+The SPACE characters are highlighted using the `whitespace-space-after-tab'
+face.
+This variable is used when `whitespace-style' includes `space-after-tab',
 `space-after-tab::tab' or `space-after-tab::space'."
   :type '(cons (string :tag "SPACEs After TAB")
               string)
@@ -762,28 +793,33 @@ Used when `whitespace-style' includes `space-after-tab',
 
 (defcustom whitespace-big-indent-regexp
   "^\\(\\(?:\t\\{4,\\}\\| \\{32,\\}\\)[\t ]*\\)"
-  "Specify big indentation regexp.
+  "Regexp to match big indentation at BOL that should be visualized.
 
-If you're using `mule' package, there may be other characters
-besides \"\\t\" that should be considered TAB.
+The indentation characters are highlighted using the `whitespace-big-indent'
+face.
+If you're using non-Latin languages, there may be other characters
+besides \"\\t\" that should be considered a TAB.
 
-NOTE: Enclose always by \\\\( and \\\\) the elements to highlight.
+NOTE: Always enclose the elements to highlight in \\\\(...\\\\).
       Use exactly one pair of enclosing \\\\( and \\\\).
 
-Used when `whitespace-style' includes `big-indent'."
+This variable is used when `whitespace-style' includes `big-indent'."
   :version "25.1"
   :type '(regexp :tag "Detect too much indentation at the beginning of a line")
   :group 'whitespace)
 
 
 (defcustom whitespace-line-column 80
-  "Specify column beyond which the line is highlighted.
+  "Column beyond which the line is highlighted.
+
+The value must be an integer or nil.  If nil, use the value
+of the `fill-column' variable.
 
-It must be an integer or nil.  If nil, the `fill-column' variable value is
-used.
+The characters beyond the column specified by this variable are
+highlighted using the `whitespace-line' face.
 
-Used when `whitespace-style' includes `lines', `lines-tail' or
-`lines-char'."
+This variable is used when `whitespace-style' includes `lines',
+`lines-tail' or `lines-char'."
   :type '(choice :tag "Line Length Limit"
                 (integer :tag "Line Length")
                 (const :tag "Use fill-column" nil))
@@ -811,7 +847,7 @@ Used when `whitespace-style' includes `lines', `lines-tail' 
or
     ;; If this is a problem for you, please, comment the line below.
     (tab-mark     ?\t    [?» ?\t] [?\\ ?\t])   ; tab - right guillemet
     )
-  "Specify an alist of mappings for displaying characters.
+  "Alist of mappings for displaying characters.
 
 Each element has the following form:
 
@@ -831,15 +867,15 @@ KIND    is the kind of character.
 CHAR    is the character to be mapped.
 
 VECTOR  is a vector of characters to be displayed in place of CHAR.
-        The first display vector that can be displayed is used;
+        The first vector that can be displayed by the terminal is used;
         if no display vector for a mapping can be displayed, then
         that character is displayed unmodified.
 
 The NEWLINE character is displayed using the face given by
 `whitespace-newline' variable.
 
-Used when `whitespace-style' includes `tab-mark', `space-mark' or
-`newline-mark'."
+This variable is used when `whitespace-style' includes `tab-mark',
+`space-mark' or `newline-mark'."
   :type '(repeat
          (list :tag "Character Mapping"
                (choice :tag "Char Kind"
@@ -861,8 +897,7 @@ Used when `whitespace-style' includes `tab-mark', 
`space-mark' or
 Global `whitespace-mode' is controlled by the command
 `global-whitespace-mode'.
 
-If nil, means no modes have `whitespace-mode' automatically
-turned on.
+If nil, no modes have `whitespace-mode' automatically turned on.
 
 If t, all modes that support `whitespace-mode' have it
 automatically turned on.
@@ -889,16 +924,16 @@ C++ modes only."
 (defcustom whitespace-action nil
   "Specify which action is taken when a buffer is visited or written.
 
-It's a list containing some or all of the following values:
+The value is a list containing one or more of the following symbols:
 
    nil                  no action is taken.
 
-   cleanup              cleanup any bogus whitespace always when local
+   cleanup              always cleanup any bogus whitespace when local
                         whitespace is turned on.
                         See `whitespace-cleanup' and
                         `whitespace-cleanup-region'.
 
-   report-on-bogus      report if there is any bogus whitespace always
+   report-on-bogus      always report if there is any bogus whitespace
                         when local whitespace is turned on.
 
    auto-cleanup         cleanup any bogus whitespace when buffer is
@@ -906,8 +941,8 @@ It's a list containing some or all of the following values:
                         See `whitespace-cleanup' and
                         `whitespace-cleanup-region'.
 
-   abort-on-bogus       abort if there is any bogus whitespace and the
-                        buffer is written.
+   abort-on-bogus       signal an error when writing the buffer if there is
+                        any bogus whitespace in the buffer.
 
    warn-if-read-only    give a warning if `cleanup' or `auto-cleanup'
                         is included in `whitespace-action' and the
diff --git a/lisp/winner.el b/lisp/winner.el
index 174b698e7b..c8354b18be 100644
--- a/lisp/winner.el
+++ b/lisp/winner.el
@@ -328,6 +328,14 @@ You may want to include buffer names such as *Help*, 
*Apropos*,
     map)
   "Keymap for Winner mode.")
 
+(defvar-keymap winner-repeat-map
+  :doc "Keymap to repeat winner key sequences.  Used in `repeat-mode'."
+  "<left>"  #'winner-undo
+  "<right>" #'winner-redo)
+
+(put #'winner-undo 'repeat-map 'winner-repeat-map)
+(put #'winner-redo 'repeat-map 'winner-repeat-map)
+
 
 ;;;###autoload
 (define-minor-mode winner-mode
diff --git a/lisp/x-dnd.el b/lisp/x-dnd.el
index 2bda67fe3f..058ab99f5c 100644
--- a/lisp/x-dnd.el
+++ b/lisp/x-dnd.el
@@ -675,7 +675,15 @@ with coordinates relative to the root window."
 (defun x-dnd-get-drop-rectangle (window posn)
   "Return the drag-and-drop rectangle at POSN on WINDOW."
   (if (or dnd-scroll-margin
-          (not (windowp window)))
+          (not (windowp window))
+          ;; Drops on the scroll bar aren't allowed, but the mouse
+          ;; rectangle can be set while still on the scroll bar,
+          ;; causing the drag initiator to never send an XdndPosition
+          ;; event that will an XdndStatus message with the accept
+          ;; flag set to be set, even after the mouse enters the
+          ;; window text area.  To prevent that, simply don't generate
+          ;; a mouse rectangle when an area is set.
+          (posn-area posn))
       '(0 0 0 0)
     (let ((window-rectangle (x-dnd-get-window-rectangle window))
           object-rectangle)
@@ -1640,8 +1648,9 @@ VERSION is the version of the XDND protocol understood by 
SOURCE."
                                 desired-name
                                 (or file-name-coding-system
                                     default-file-name-coding-system)))
-            (let ((name (funcall x-dnd-direct-save-function
-                                 t desired-name)))
+            (let ((name (expand-file-name
+                         (funcall x-dnd-direct-save-function
+                                  t desired-name))))
               (setq save-to name save-to-remote name))
             (when save-to
               (if (file-remote-p save-to)
diff --git a/lisp/xwidget.el b/lisp/xwidget.el
index 41a1190c64..7195ba9d89 100644
--- a/lisp/xwidget.el
+++ b/lisp/xwidget.el
@@ -20,15 +20,17 @@
 ;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
 
 ;;; Commentary:
-;;
-;; See xwidget.c for more api functions.
+
+;; See the node "(emacs)Embedded WebKit Widgets" in the Emacs manual for
+;; help on user-facing features, and "(elisp)Embedded Native Widgets" in
+;; the Emacs Lisp reference manual for help on more API functions.
+
+;;; Code:
 
 ;; This breaks compilation when we don't have xwidgets.
 ;; And is pointless when we do, since it's in C and so preloaded.
 ;;(require 'xwidget-internal)
 
-;;; Code:
-
 (require 'cl-lib)
 (require 'bookmark)
 (require 'format-spec)
@@ -1198,7 +1200,7 @@ Press 
\\<xwidget-webkit-isearch-mode-map>\\[xwidget-webkit-isearch-exit] to exit
     (xwidget-webkit-goto-history xwidget-webkit-history--session id))
   (xwidget-webkit-history-reload))
 
-(defun xwidget-webkit-history-reload (&rest ignored)
+(defun xwidget-webkit-history-reload (&rest _ignored)
   "Reload the current history buffer."
   (interactive)
   (setq tabulated-list-entries nil)
diff --git a/nt/INSTALL b/nt/INSTALL
index 0b8ca98c8a..81d4c6293c 100644
--- a/nt/INSTALL
+++ b/nt/INSTALL
@@ -214,7 +214,7 @@ build will run on Windows 9X and newer systems).
   of the 'bsdtar' program to unpack the tarballs.  'bsdtar' is
   available as part of the 'libarchive' package from here:
 
-    http://sourceforge.net/projects/ezwinports/files/
+    https://sourceforge.net/projects/ezwinports/files/
 
   The recommended place to install these packages is a single tree
   starting from some directory on a drive other than the system drive
@@ -242,16 +242,16 @@ build will run on Windows 9X and newer systems).
    . Texinfo (needed to produce the Info manuals when building from
      the repository, and for "make install")
 
-     Available from http://sourceforge.net/projects/ezwinports/files/.
+     Available from https://sourceforge.net/projects/ezwinports/files/.
 
    . pkg-config (invoked by the configure script to look for optional
      packages)
 
-     Available from http://sourceforge.net/projects/ezwinports/files/.
+     Available from https://sourceforge.net/projects/ezwinports/files/.
 
    . gzip (needed to compress files during "make install")
 
-     Available from http://gnuwin32.sourceforge.net/packages/gzip.htm.
+     Available from https://gnuwin32.sourceforge.net/packages/gzip.htm.
 
   Each package might list other packages as prerequisites on its
   download page (under "Runtime requirements"); download those as
@@ -294,7 +294,7 @@ build will run on Windows 9X and newer systems).
    . Additional package (needed only if building from the repository):
      Autoconf.  It is available from here:
 
-       
http://sourceforge.net/projects/ezwinports/files/autoconf-2.65-msys-bin.zip/download
+       
https://sourceforge.net/projects/ezwinports/files/autoconf-2.65-msys-bin.zip/download
 
   MSYS packages are distributed as .tar.lzma compressed archives.  To
   install the packages manually, we recommend to use the Windows port
@@ -642,7 +642,7 @@ build will run on Windows 9X and newer systems).
 
   To support XPM images (required for color tool-bar icons), you will
   need the libXpm library.  It is available from the ezwinports site,
-  http://sourceforge.net/projects/ezwinports/files/ and from
+  https://sourceforge.net/projects/ezwinports/files/ and from
   https://ftp.gnu.org/gnu/emacs/windows/.
 
   For PNG images, we recommend to use versions 1.4.x and later of
@@ -665,7 +665,7 @@ build will run on Windows 9X and newer systems).
   For GIF images, we recommend to use versions 5.0.0 or later of
   giflib, as it is much enhanced wrt previous versions.  You can find
   precompiled binaries and headers for giflib on the ezwinports site,
-  http://sourceforge.net/projects/ezwinports/files/ and on
+  https://sourceforge.net/projects/ezwinports/files/ and on
   https://ftp.gnu.org/gnu/emacs/windows/.
 
   Version 5.0.0 and later of giflib are binary incompatible with
@@ -689,7 +689,7 @@ build will run on Windows 9X and newer systems).
   Pre-built versions of librsvg and its dependencies can be found
   here:
 
-    http://sourceforge.net/projects/ezwinports/files/
+    https://sourceforge.net/projects/ezwinports/files/
 
     This site includes a minimal (as much as possible for librsvg)
     build of the library and its dependencies; it is also more
@@ -739,7 +739,7 @@ build will run on Windows 9X and newer systems).
 
   For WebP images you will need libwebp.  You can find it here:
 
-    http://sourceforge.net/projects/ezwinports/files/
+    https://sourceforge.net/projects/ezwinports/files/
 
   Note: the MS-Windows binary distribution on the Google site:
 
@@ -779,7 +779,7 @@ build will run on Windows 9X and newer systems).
   session.
 
   You can get pre-built binaries (including any required DLL and the
-  header files) at http://sourceforge.net/projects/ezwinports/files/
+  header files) at https://sourceforge.net/projects/ezwinports/files/
   and on https://ftp.gnu.org/gnu/emacs/windows/.
 
 * Optional libxml2 support
@@ -801,7 +801,7 @@ build will run on Windows 9X and newer systems).
   One place where you can get pre-built Windows binaries of libxml2
   (including any required DLL and the header files) is here:
 
-     http://sourceforge.net/projects/ezwinports/files/
+     https://sourceforge.net/projects/ezwinports/files/
      https://ftp.gnu.org/gnu/emacs/windows/
 
   For runtime support of libxml2, you will also need to install the
@@ -809,7 +809,7 @@ build will run on Windows 9X and newer systems).
   be available to the compiler when you compile with libxml2 support.
   A MinGW port of libiconv can be found on the MinGW site:
 
-   http://sourceforge.net/projects/mingw/files/MinGW/Base/libiconv/
+   https://sourceforge.net/projects/mingw/files/MinGW/Base/libiconv/
 
   You need the libiconv-X.Y.Z-N-mingw32-dev.tar.lzma tarball from that
   site.
diff --git a/nt/INSTALL.W64 b/nt/INSTALL.W64
index fd8f60bb0b..9261c82db1 100644
--- a/nt/INSTALL.W64
+++ b/nt/INSTALL.W64
@@ -19,7 +19,7 @@ Emacs with the full repository, or less if you're using a 
release tarball.
 * Set up the MinGW-w64 / MSYS2 build environment
 
 MinGW-w64 provides a complete runtime for projects built with GCC for 64-bit
-Windows -- it's located at http://mingw-w64.org/.
+Windows -- it's located at https://mingw-w64.org/.
 
 MSYS2 is a Cygwin-derived software distribution for Windows which provides
 build tools for MinGW-w64 -- see https://msys2.github.io/.
diff --git a/nt/inc/ms-w32.h b/nt/inc/ms-w32.h
index 2dd9a9a476..98e31df70c 100644
--- a/nt/inc/ms-w32.h
+++ b/nt/inc/ms-w32.h
@@ -27,7 +27,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include <mingw_time.h>
 
 /* MinGW-w64 gcc does not automotically define a macro for
-   differentiating it fom MinGW gcc. We need to test the presence of
+   differentiating it from MinGW gcc. We need to test the presence of
    __MINGW64_VERSION_MAJOR in _mingw.h: */
 #ifdef __MINGW32__
 # include <_mingw.h>
diff --git a/oldXMenu/Activate.c b/oldXMenu/Activate.c
index e679c2ffed..781c05bd02 100644
--- a/oldXMenu/Activate.c
+++ b/oldXMenu/Activate.c
@@ -122,7 +122,6 @@ int x_menu_grab_keyboard = 1;
 static Wait_func wait_func;
 static void* wait_data;
 static Translate_func translate_func = NULL;
-static Expose_func expose_func = NULL;
 
 void
 XMenuActivateSetWaitFunction (Wait_func func, void *data)
@@ -137,12 +136,6 @@ XMenuActivateSetTranslateFunction (Translate_func func)
   translate_func = func;
 }
 
-void
-XMenuActivateSetExposeFunction (Expose_func func)
-{
-  expose_func = func;
-}
-
 int
 XMenuActivate(
     register Display *display,         /* Display to put menu on. */
@@ -346,9 +339,6 @@ XMenuActivate(
                    feq = feq_tmp;
                }
                else if (_XMEventHandler) (*_XMEventHandler)(&event);
-
-               if (expose_func)
-                 expose_func (&event);
                break;
            }
            if (event_xmp->activated) {
diff --git a/oldXMenu/XMenu.h b/oldXMenu/XMenu.h
index 54061235ae..2eee18a384 100644
--- a/oldXMenu/XMenu.h
+++ b/oldXMenu/XMenu.h
@@ -259,7 +259,6 @@ typedef void (*Wait_func)(void*);
    XPutBackEvent on an equivalent artificial core event on any
    function it wants to translate.  */
 typedef void (*Translate_func)(XEvent *);
-typedef void (*Expose_func)(XEvent *);
 
 /*
  * XMenu library routine declarations.
@@ -281,7 +280,6 @@ int XMenuLocate(Display *display, XMenu *menu, int p_num, 
int s_num, int x_pos,
 void XMenuSetFreeze(XMenu *menu, int freeze);
 void XMenuActivateSetWaitFunction(Wait_func func, void *data);
 void XMenuActivateSetTranslateFunction(Translate_func func);
-void XMenuActivateSetExposeFunction(Expose_func func);
 int XMenuActivate(Display *display, XMenu *menu, int *p_num, int *s_num, int 
x_pos, int y_pos, unsigned int event_mask, char **data, void (*help_callback) 
(char const *, int, int));
 char *XMenuPost(Display *display, XMenu *menu, int *p_num, int *s_num, int 
x_pos, int y_pos, int event_mask);
 int XMenuDeletePane(Display *display, XMenu *menu, int p_num);
diff --git a/src/ChangeLog.13 b/src/ChangeLog.13
index abf2a9421a..6eb54dfb2c 100644
--- a/src/ChangeLog.13
+++ b/src/ChangeLog.13
@@ -10183,7 +10183,7 @@
        (w32_wnd_proc): Handle bottom divider width.
        For WM_WINDOWPOSCHANGING return zero if we resize pixelwise.
        (Fx_create_frame): Default divider width parameters.
-       Caclulate sizes pixelwise.  Add vertical drag cursor support.
+       Calculate sizes pixelwise.  Add vertical drag cursor support.
        (x_create_tip_frame): Default divider widths to zero.
        Pixelize call to change_frame_size.
        (Fx_show_tip): Add handling of divider widths.  Pixelize window
@@ -11147,7 +11147,7 @@
 2013-11-01  Claudio Bley  <claudio.bley@googlemail.com>
 
        * image.c (pbm_next_char): New function.
-       See http://netpbm.sourceforge.net/doc/pbm.html for the details.
+       See https://netpbm.sourceforge.net/doc/pbm.html for the details.
        (pbm_scan_number): Use it.
        (Qlibjpeg_version): New variable.
        (syms_of_image): DEFSYM and initialize it.
@@ -14215,7 +14215,7 @@
        * w32.c (PEXCEPTION_POINTERS, PEXCEPTION_RECORD, PCONTEXT): Define
        variables of these types so that GDB would know about them, as aid
        for debugging fatal exceptions.  (Bug#15024)  See also
-       http://sourceware.org/ml/gdb/2013-08/msg00010.html for related
+       https://sourceware.org/ml/gdb/2013-08/msg00010.html for related
        discussions.
 
 2013-08-08  Jan Djärv  <jan.h.d@swipnet.se>
diff --git a/src/ChangeLog.7 b/src/ChangeLog.7
index e893a2a6d8..9c6fd810d3 100644
--- a/src/ChangeLog.7
+++ b/src/ChangeLog.7
@@ -1215,19 +1215,19 @@
 
        * ccl.c: Change term translation to code conversion, then change
        terms unify/unification to translate/translation respectively
-       throughtout the file.
+       throughout the file.
 
        * charset.c: Change terms unify/unification to
-       translate/translation respectively throughtout the file.
+       translate/translation respectively throughout the file.
        (ONE_BYTE_CHAR_WIDTH): Delete unnecessary continuation line at the
        tail.
 
        * charset.h: Change terms unify/unification to
-       translate/translation respectively throughtout the file.
+       translate/translation respectively throughout the file.
        (GET_TRANSLATION_TABLE): Name changed from UNIFICATION_ID_TABLE.
 
        * coding.c: Change terms unify/unification to
-       translate/translation respectively throughtout the file.
+       translate/translation respectively throughout the file.
        (encode_coding_iso2022): Fix bug in encoding a text ending by a
        composite character.
        (check_composing_code): If we are decoding the last block of data,
diff --git a/src/Makefile.in b/src/Makefile.in
index c18f04da80..088716b5fe 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -426,25 +426,26 @@ ALL_CXX_CFLAGS = $(EMACS_CFLAGS) \
 
 ## lastfile must follow all files whose initialized data areas should
 ## be dumped as pure by dump-emacs.
-base_obj = dispnew.o frame.o scroll.o xdisp.o menu.o $(XMENU_OBJ) window.o \
-       charset.o coding.o category.o ccl.o character.o chartab.o bidi.o \
-       $(CM_OBJ) term.o terminal.o xfaces.o $(XOBJ) $(GTK_OBJ) $(DBUS_OBJ) \
-       emacs.o keyboard.o macros.o keymap.o sysdep.o \
-       bignum.o buffer.o filelock.o insdel.o marker.o \
-       minibuf.o fileio.o dired.o \
-       cmds.o casetab.o casefiddle.o indent.o search.o regex-emacs.o undo.o \
-       alloc.o pdumper.o data.o doc.o editfns.o callint.o \
-       eval.o floatfns.o fns.o sort.o font.o print.o lread.o $(MODULES_OBJ) \
-       syntax.o $(UNEXEC_OBJ) bytecode.o comp.o $(DYNLIB_OBJ) \
-       process.o gnutls.o callproc.o \
-       region-cache.o sound.o timefns.o atimer.o \
+base_obj = dispnew.o frame.o scroll.o xdisp.o menu.o $(XMENU_OBJ) window.o     
\
+       charset.o coding.o category.o ccl.o character.o chartab.o bidi.o       \
+       $(CM_OBJ) term.o terminal.o xfaces.o $(XOBJ) $(GTK_OBJ) $(DBUS_OBJ)    \
+       emacs.o keyboard.o macros.o keymap.o sysdep.o                          \
+       bignum.o buffer.o filelock.o insdel.o marker.o                         \
+       minibuf.o fileio.o dired.o                                             \
+       cmds.o casetab.o casefiddle.o indent.o search.o regex-emacs.o undo.o   \
+       alloc.o pdumper.o data.o doc.o editfns.o callint.o                     \
+       eval.o floatfns.o fns.o sort.o font.o print.o lread.o $(MODULES_OBJ)   \
+       syntax.o $(UNEXEC_OBJ) bytecode.o comp.o $(DYNLIB_OBJ)                 \
+       process.o gnutls.o callproc.o                                          \
+       region-cache.o sound.o timefns.o atimer.o                              \
        doprnt.o intervals.o textprop.o composite.o xml.o lcms.o $(NOTIFY_OBJ) \
-       $(XWIDGETS_OBJ) \
-       profiler.o decompress.o \
-       thread.o systhread.o sqlite.o \
-       $(if $(HYBRID_MALLOC),sheap.o) \
-       $(MSDOS_OBJ) $(MSDOS_X_OBJ) $(NS_OBJ) $(CYGWIN_OBJ) $(FONT_OBJ) \
-       $(W32_OBJ) $(WINDOW_SYSTEM_OBJ) $(XGSELOBJ) $(JSON_OBJ) \
+       $(XWIDGETS_OBJ)                                                        \
+       profiler.o decompress.o                                                \
+       thread.o systhread.o sqlite.o                                          \
+       itree.o                                                                \
+       $(if $(HYBRID_MALLOC),sheap.o)                                         \
+       $(MSDOS_OBJ) $(MSDOS_X_OBJ) $(NS_OBJ) $(CYGWIN_OBJ) $(FONT_OBJ)        \
+       $(W32_OBJ) $(WINDOW_SYSTEM_OBJ) $(XGSELOBJ) $(JSON_OBJ)                \
        $(HAIKU_OBJ) $(PGTK_OBJ)
 doc_obj = $(base_obj) $(NS_OBJC_OBJ)
 obj = $(doc_obj) $(HAIKU_CXX_OBJ)
@@ -498,7 +499,7 @@ all: ../native-lisp
 endif
 .PHONY: all
 
-dmpstruct_headers=$(srcdir)/lisp.h $(srcdir)/buffer.h \
+dmpstruct_headers=$(srcdir)/lisp.h $(srcdir)/buffer.h $(srcdir)/itree.h \
        $(srcdir)/intervals.h $(srcdir)/charset.h $(srcdir)/bignum.h
 ifeq ($(CHECK_STRUCTS),true)
 pdumper.o: dmpstruct.h
diff --git a/src/alloc.c b/src/alloc.c
index a85636862a..de49a9863f 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -1,7 +1,6 @@
 /* Storage allocation and gc for GNU Emacs Lisp interpreter.
 
-Copyright (C) 1985-1986, 1988, 1993-1995, 1997-2022 Free Software
-Foundation, Inc.
+Copyright (C) 1985-2022  Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -48,6 +47,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include "blockinput.h"
 #include "pdumper.h"
 #include "termhooks.h"         /* For struct terminal.  */
+#include "itree.h"
 #ifdef HAVE_WINDOW_SYSTEM
 #include TERM_HEADER
 #endif /* HAVE_WINDOW_SYSTEM */
@@ -3142,6 +3142,11 @@ cleanup_vector (struct Lisp_Vector *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))
@@ -3710,18 +3715,20 @@ build_symbol_with_pos (Lisp_Object symbol, Lisp_Object 
position)
   return val;
 }
 
-/* Return a new overlay with specified START, END and PLIST.  */
+/* Return a new (deleted) overlay with PLIST.  */
 
 Lisp_Object
-build_overlay (Lisp_Object start, Lisp_Object end, Lisp_Object plist)
+build_overlay (bool front_advance, bool rear_advance,
+               Lisp_Object plist)
 {
   struct Lisp_Overlay *p = ALLOCATE_PSEUDOVECTOR (struct Lisp_Overlay, plist,
                                                  PVEC_OVERLAY);
   Lisp_Object overlay = make_lisp_ptr (p, Lisp_Vectorlike);
-  OVERLAY_START (overlay) = start;
-  OVERLAY_END (overlay) = end;
+  struct itree_node *node = xmalloc (sizeof (*node));
+  itree_node_init (node, front_advance, rear_advance, overlay);
+  p->interval = node;
+  p->buffer = NULL;
   set_overlay_plist (overlay, plist);
-  p->next = NULL;
   return overlay;
 }
 
@@ -5955,8 +5962,7 @@ visit_buffer_root (struct gc_root_visitor visitor,
   /* Buffers that are roots don't have intervals, an undo list, or
      other constructs that real buffers have.  */
   eassert (buffer->base_buffer == NULL);
-  eassert (buffer->overlays_before == NULL);
-  eassert (buffer->overlays_after == NULL);
+  eassert (buffer->overlays == NULL);
 
   /* Visit the buffer-locals.  */
   visit_vectorlike_root (visitor, (struct Lisp_Vector *) buffer, type);
@@ -6290,6 +6296,11 @@ garbage_collect (void)
   image_prune_animation_caches (false);
 #endif
 
+  /* ELisp code run by `gc-post-hook' could result in itree iteration,
+     which must not happen while the itree is already busy.  See
+     bug#58639.  */
+  eassert (!itree_iterator_busy_p ());
+
   if (!NILP (Vpost_gc_hook))
     {
       specpdl_ref gc_count = inhibit_garbage_collection ();
@@ -6512,16 +6523,25 @@ mark_char_table (struct Lisp_Vector *ptr, enum 
pvec_type pvectype)
 /* Mark the chain of overlays starting at PTR.  */
 
 static void
-mark_overlay (struct Lisp_Overlay *ptr)
+mark_overlay (struct Lisp_Overlay *ov)
 {
-  for (; ptr && !vectorlike_marked_p (&ptr->header); ptr = ptr->next)
-    {
-      set_vectorlike_marked (&ptr->header);
-      /* These two are always markers and can be marked fast.  */
-      set_vectorlike_marked (&XMARKER (ptr->start)->header);
-      set_vectorlike_marked (&XMARKER (ptr->end)->header);
-      mark_object (ptr->plist);
-    }
+  /* We don't mark the `itree_node` object, because it is managed manually
+     rather than by the GC.  */
+  eassert (BASE_EQ (ov->interval->data, make_lisp_ptr (ov, Lisp_Vectorlike)));
+  set_vectorlike_marked (&ov->header);
+  mark_object (ov->plist);
+}
+
+/* Mark the overlay subtree rooted at NODE.  */
+
+static void
+mark_overlays (struct itree_node *node)
+{
+  if (node == NULL)
+    return;
+  mark_object (node->data);
+  mark_overlays (node->left);
+  mark_overlays (node->right);
 }
 
 /* Mark Lisp_Objects and special pointers in BUFFER.  */
@@ -6545,8 +6565,8 @@ mark_buffer (struct buffer *buffer)
   if (!BUFFER_LIVE_P (buffer))
       mark_object (BVAR (buffer, undo_list));
 
-  mark_overlay (buffer->overlays_before);
-  mark_overlay (buffer->overlays_after);
+  if (buffer->overlays)
+    mark_overlays (buffer->overlays->root);
 
   /* If this is an indirect buffer, mark its base buffer.  */
   if (buffer->base_buffer &&
diff --git a/src/buffer.c b/src/buffer.c
index d4a0c37bed..9be2c4a970 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -1,7 +1,6 @@
 /* Buffer manipulation primitives for GNU Emacs.
 
-Copyright (C) 1985-1989, 1993-1995, 1997-2022 Free Software Foundation,
-Inc.
+Copyright (C) 1985-2022  Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -44,6 +43,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include "keymap.h"
 #include "frame.h"
 #include "xwidget.h"
+#include "itree.h"
 #include "pdumper.h"
 
 #ifdef WINDOWSNT
@@ -116,7 +116,7 @@ static Lisp_Object QSFundamental;   /* A string 
"Fundamental".  */
 
 static void alloc_buffer_text (struct buffer *, ptrdiff_t);
 static void free_buffer_text (struct buffer *b);
-static struct Lisp_Overlay * copy_overlays (struct buffer *, struct 
Lisp_Overlay *);
+static void copy_overlays (struct buffer *, struct buffer *);
 static void modify_overlay (struct buffer *, ptrdiff_t, ptrdiff_t);
 static Lisp_Object buffer_lisp_local_variables (struct buffer *, bool);
 static Lisp_Object buffer_local_variables_1 (struct buffer *buf, int offset, 
Lisp_Object sym);
@@ -638,52 +638,33 @@ even if it is dead.  The return value is never nil.  */)
   return buffer;
 }
 
-
-/* Return a list of overlays which is a copy of the overlay list
-   LIST, but for buffer B.  */
-
-static struct Lisp_Overlay *
-copy_overlays (struct buffer *b, struct Lisp_Overlay *list)
+static void
+add_buffer_overlay (struct buffer *b, struct Lisp_Overlay *ov,
+                    ptrdiff_t begin, ptrdiff_t end)
 {
-  struct Lisp_Overlay *result = NULL, *tail = NULL;
-
-  for (; list; list = list->next)
-    {
-      Lisp_Object overlay, start, end;
-      struct Lisp_Marker *m;
-
-      eassert (MARKERP (list->start));
-      m = XMARKER (list->start);
-      start = build_marker (b, m->charpos, m->bytepos);
-      XMARKER (start)->insertion_type = m->insertion_type;
-
-      eassert (MARKERP (list->end));
-      m = XMARKER (list->end);
-      end = build_marker (b, m->charpos, m->bytepos);
-      XMARKER (end)->insertion_type = m->insertion_type;
-
-      overlay = build_overlay (start, end, Fcopy_sequence (list->plist));
-      if (tail)
-       tail = tail->next = XOVERLAY (overlay);
-      else
-       result = tail = XOVERLAY (overlay);
-    }
-
-  return result;
+  eassert (! ov->buffer);
+  if (! b->overlays)
+    b->overlays = itree_create ();
+  ov->buffer = b;
+  itree_insert (b->overlays, ov->interval, begin, end);
 }
 
-/* Set an appropriate overlay of B.  */
+/* Copy overlays of buffer FROM to buffer TO.  */
 
 static void
-set_buffer_overlays_before (struct buffer *b, struct Lisp_Overlay *o)
+copy_overlays (struct buffer *from, struct buffer *to)
 {
-  b->overlays_before = o;
-}
+  eassert (to && ! to->overlays);
+  struct itree_node *node;
 
-static void
-set_buffer_overlays_after (struct buffer *b, struct Lisp_Overlay *o)
-{
-  b->overlays_after = o;
+  ITREE_FOREACH (node, from->overlays, PTRDIFF_MIN, PTRDIFF_MAX, ASCENDING)
+    {
+      Lisp_Object ov = node->data;
+      Lisp_Object copy = build_overlay (node->front_advance,
+                                        node->rear_advance,
+                                        Fcopy_sequence (OVERLAY_PLIST (ov)));
+      add_buffer_overlay (to, XOVERLAY (copy), node->begin, node->end);
+    }
 }
 
 bool
@@ -726,8 +707,7 @@ clone_per_buffer_values (struct buffer *from, struct buffer 
*to)
 
   memcpy (to->local_flags, from->local_flags, sizeof to->local_flags);
 
-  set_buffer_overlays_before (to, copy_overlays (to, from->overlays_before));
-  set_buffer_overlays_after (to, copy_overlays (to, from->overlays_after));
+  copy_overlays (from, to);
 
   /* Get (a copy of) the alist of Lisp-level local variables of FROM
      and install that in TO.  */
@@ -926,17 +906,25 @@ does not run the hooks `kill-buffer-hook',
   return buf;
 }
 
-/* Mark OV as no longer associated with B.  */
+static void
+remove_buffer_overlay (struct buffer *b, struct Lisp_Overlay *ov)
+{
+  eassert (b->overlays);
+  eassert (ov->buffer == b);
+  itree_remove (ov->buffer->overlays, ov->interval);
+  ov->buffer = NULL;
+}
+
+/* Mark OV as no longer associated with its buffer.  */
 
 static void
-drop_overlay (struct buffer *b, struct Lisp_Overlay *ov)
+drop_overlay (struct Lisp_Overlay *ov)
 {
-  eassert (b == XBUFFER (Fmarker_buffer (ov->start)));
-  modify_overlay (b, marker_position (ov->start),
-                 marker_position (ov->end));
-  unchain_marker (XMARKER (ov->start));
-  unchain_marker (XMARKER (ov->end));
+  if (! ov->buffer)
+    return;
 
+  modify_overlay (ov->buffer, overlay_start (ov), overlay_end (ov));
+  remove_buffer_overlay (ov->buffer, ov);
 }
 
 /* Delete all overlays of B and reset its overlay lists.  */
@@ -944,26 +932,94 @@ drop_overlay (struct buffer *b, struct Lisp_Overlay *ov)
 void
 delete_all_overlays (struct buffer *b)
 {
-  struct Lisp_Overlay *ov, *next;
+  struct itree_node *node;
+
+  if (! b->overlays)
+    return;
 
-  /* FIXME: Since each drop_overlay will scan BUF_MARKERS to unlink its
-     markers, we have an unneeded O(N^2) behavior here.  */
-  for (ov = b->overlays_before; ov; ov = next)
+  /* FIXME: This loop sets the overlays' `buffer` field to NULL but
+     doesn't set the itree_nodes' `parent`, `left` and `right`
+     fields accordingly.  I believe it's harmless, but a bit untidy since
+     other parts of the code are careful to set those fields to NULL when
+     the overlay is deleted.
+     Of course, we can't set them to NULL from within the iteration
+     because the iterator may need them (tho we could if we added
+     an ITREE_POST_ORDER iteration order).  */
+  ITREE_FOREACH (node, b->overlays, PTRDIFF_MIN, PTRDIFF_MAX, ASCENDING)
     {
-      drop_overlay (b, ov);
-      next = ov->next;
-      ov->next = NULL;
+      modify_overlay (b, node->begin, node->end);
+      /* Where are the nodes freed ? --ap */
+      XOVERLAY (node->data)->buffer = NULL;
     }
+  itree_clear (b->overlays);
+}
 
-  for (ov = b->overlays_after; ov; ov = next)
+static void
+free_buffer_overlays (struct buffer *b)
+{
+  /* Actually this does not free any overlay, but the tree only.  --ap */
+  if (b->overlays)
     {
-      drop_overlay (b, ov);
-      next = ov->next;
-      ov->next = NULL;
+      itree_destroy (b->overlays);
+      b->overlays = NULL;
     }
+}
+
+/* Adjust the position of overlays in the current buffer according to
+   MULTIBYTE.
+
+   Assume that positions currently correspond to byte positions, if
+   MULTIBYTE is true and to character positions if not.
+*/
+
+static void
+set_overlays_multibyte (bool multibyte)
+{
+  if (! current_buffer->overlays || Z == Z_BYTE)
+    return;
+
+  struct itree_node **nodes = NULL;
+  struct itree_tree *tree = current_buffer->overlays;
+  const intmax_t size = itree_size (tree);
+
+  /* We can't use `itree_node_set_region` at the same time
+     as we iterate over the itree, so we need an auxiliary storage
+     to keep the list of nodes.  */
+  USE_SAFE_ALLOCA;
+  SAFE_NALLOCA (nodes, 1, size);
+  {
+    struct itree_node *node, **cursor = nodes;
+    ITREE_FOREACH (node, tree, PTRDIFF_MIN, PTRDIFF_MAX, ASCENDING)
+      *(cursor++) = node;
+  }
+
+  for (int i = 0; i < size; ++i, ++nodes)
+    {
+      struct itree_node * const node = *nodes;
 
-  set_buffer_overlays_before (b, NULL);
-  set_buffer_overlays_after (b, NULL);
+      if (multibyte)
+        {
+          ptrdiff_t begin = itree_node_begin (tree, node);
+          ptrdiff_t end = itree_node_end (tree, node);
+
+          /* This models the behavior of markers.  (The behavior of
+             text-intervals differs slightly.) */
+          while (begin < Z_BYTE
+                 && !CHAR_HEAD_P (FETCH_BYTE (begin)))
+            begin++;
+          while (end < Z_BYTE
+                 && !CHAR_HEAD_P (FETCH_BYTE (end)))
+            end++;
+          itree_node_set_region (tree, node, BYTE_TO_CHAR (begin),
+                                    BYTE_TO_CHAR (end));
+        }
+      else
+        {
+          itree_node_set_region (tree, node, CHAR_TO_BYTE (node->begin),
+                                    CHAR_TO_BYTE (node->end));
+        }
+    }
+  SAFE_FREE ();
 }
 
 /* Reinitialize everything about a buffer except its name and contents
@@ -993,9 +1049,7 @@ reset_buffer (register struct buffer *b)
   b->auto_save_failure_time = 0;
   bset_auto_save_file_name (b, Qnil);
   bset_read_only (b, Qnil);
-  set_buffer_overlays_before (b, NULL);
-  set_buffer_overlays_after (b, NULL);
-  b->overlay_center = BEG;
+  b->overlays = NULL;
   bset_mark_active (b, Qnil);
   bset_point_before_scroll (b, Qnil);
   bset_file_format (b, Qnil);
@@ -1978,10 +2032,8 @@ cleaning up all windows currently displaying the buffer 
to be killed. */)
 
       /* Perhaps we should explicitly free the interval tree here...  */
     }
-  /* Since we've unlinked the markers, the overlays can't be here any more
-     either.  */
-  set_buffer_overlays_before (b, NULL);
-  set_buffer_overlays_after (b, NULL);
+  delete_all_overlays (b);
+  free_buffer_overlays (b);
 
   /* Reset the local variables, so that this buffer's local values
      won't be protected from GC.  They would be protected
@@ -2381,6 +2433,23 @@ advance_to_char_boundary (ptrdiff_t byte_pos)
   return byte_pos;
 }
 
+static void
+swap_buffer_overlays (struct buffer *buffer, struct buffer *other)
+{
+  struct itree_node *node;
+
+  ITREE_FOREACH (node, buffer->overlays, PTRDIFF_MIN, PTRDIFF_MAX, ASCENDING)
+    XOVERLAY (node->data)->buffer = other;
+
+  ITREE_FOREACH (node, other->overlays, PTRDIFF_MIN, PTRDIFF_MAX, ASCENDING)
+    XOVERLAY (node->data)->buffer = buffer;
+
+  /* Swap the interval trees. */
+  void *tmp = buffer->overlays;
+  buffer->overlays = other->overlays;
+  other->overlays = tmp;
+}
+
 DEFUN ("buffer-swap-text", Fbuffer_swap_text, Sbuffer_swap_text,
        1, 1, 0,
        doc: /* Swap the text between current buffer and BUFFER.
@@ -2452,9 +2521,6 @@ results, see Info node `(elisp)Swapping Text'.  */)
   current_buffer->prevent_redisplay_optimizations_p = 1;
   other_buffer->prevent_redisplay_optimizations_p = 1;
   swapfield (long_line_optimizations_p, bool_bf);
-  swapfield (overlays_before, struct Lisp_Overlay *);
-  swapfield (overlays_after, struct Lisp_Overlay *);
-  swapfield (overlay_center, ptrdiff_t);
   swapfield_ (undo_list, Lisp_Object);
   swapfield_ (mark, Lisp_Object);
   swapfield_ (mark_active, Lisp_Object); /* Belongs with the `mark'.  */
@@ -2481,6 +2547,7 @@ results, see Info node `(elisp)Swapping Text'.  */)
   current_buffer->text->end_unchanged = current_buffer->text->gpt;
   other_buffer->text->beg_unchanged = other_buffer->text->gpt;
   other_buffer->text->end_unchanged = other_buffer->text->gpt;
+  swap_buffer_overlays (current_buffer, other_buffer);
   {
     struct Lisp_Marker *m;
     for (m = BUF_MARKERS (current_buffer); m; m = m->next)
@@ -2597,7 +2664,8 @@ current buffer is cleared.  */)
 
       /* Do this first, so it can use CHAR_TO_BYTE
         to calculate the old correspondences.  */
-      set_intervals_multibyte (0);
+      set_intervals_multibyte (false);
+      set_overlays_multibyte (false);
 
       bset_enable_multibyte_characters (current_buffer, Qnil);
 
@@ -2784,7 +2852,8 @@ current buffer is cleared.  */)
       /* FIXME: Is it worth the trouble, really?  Couldn't we just throw
          away all the text-properties instead of trying to guess how
          to adjust them?  AFAICT the result is not reliable anyway.  */
-      set_intervals_multibyte (1);
+      set_intervals_multibyte (true);
+      set_overlays_multibyte (true);
     }
 
   if (!EQ (old_undo, Qt))
@@ -2867,272 +2936,159 @@ the normal hook `change-major-mode-hook'.  */)
 }
 
 
-/* Find all the overlays in the current buffer that contain position POS.
+/* Find all the overlays in the current buffer that overlap the range
+   [BEG, END).
+
+   If EMPTY is true, include empty overlays in that range and also at
+   END, provided END denotes the position at the end of the accessible
+   part of the buffer.
+
+   If TRAILING is true, include overlays that begin at END, provided
+   END denotes the position at the end of the accessible part of the
+   buffer.
+
    Return the number found, and store them in a vector in *VEC_PTR.
    Store in *LEN_PTR the size allocated for the vector.
    Store in *NEXT_PTR the next position after POS where an overlay starts,
-     or ZV if there are no more overlays between POS and ZV.
-   Store in *PREV_PTR the previous position before POS where an overlay ends,
-     or where an overlay starts which ends at or after POS;
-     or BEGV if there are no such overlays from BEGV to POS.
-   NEXT_PTR and/or PREV_PTR may be 0, meaning don't store that info.
+     or ZV if there are no more overlays.
+   NEXT_PTR may be 0, meaning don't store that info.
 
    *VEC_PTR and *LEN_PTR should contain a valid vector and size
    when this function is called.
 
-   If EXTEND, make the vector bigger if necessary.
-   If not, never extend the vector,
-   and store only as many overlays as will fit.
+   If EXTEND, make the vector bigger if necessary.  If not, never
+   extend the vector, and store only as many overlays as will fit.
    But still return the total number of overlays.
-
-   If CHANGE_REQ, any position written into *PREV_PTR or
-   *NEXT_PTR is guaranteed to be not equal to POS, unless it is the
-   default (BEGV or ZV).  */
+*/
 
 ptrdiff_t
-overlays_at (EMACS_INT pos, bool extend, Lisp_Object **vec_ptr,
-            ptrdiff_t *len_ptr,
-            ptrdiff_t *next_ptr, ptrdiff_t *prev_ptr, bool change_req)
+overlays_in (ptrdiff_t beg, ptrdiff_t end, bool extend,
+            Lisp_Object **vec_ptr, ptrdiff_t *len_ptr,
+            bool empty, bool trailing,
+             ptrdiff_t *next_ptr)
 {
   ptrdiff_t idx = 0;
   ptrdiff_t len = *len_ptr;
-  Lisp_Object *vec = *vec_ptr;
   ptrdiff_t next = ZV;
-  ptrdiff_t prev = BEGV;
-  bool inhibit_storing = 0;
-
-  for (struct Lisp_Overlay *tail = current_buffer->overlays_before;
-       tail; tail = tail->next)
-    {
-      Lisp_Object overlay = make_lisp_ptr (tail, Lisp_Vectorlike);
-      Lisp_Object start = OVERLAY_START (overlay);
-      Lisp_Object end = OVERLAY_END (overlay);
-      ptrdiff_t endpos = OVERLAY_POSITION (end);
-      if (endpos < pos)
-       {
-         if (prev < endpos)
-           prev = endpos;
-         break;
-       }
-      ptrdiff_t startpos = OVERLAY_POSITION (start);
-      /* This one ends at or after POS
-        so its start counts for PREV_PTR if it's before POS.  */
-      if (prev < startpos && startpos < pos)
-       prev = startpos;
-      if (endpos == pos)
-       continue;
-      if (startpos <= pos)
-       {
-         if (idx == len)
-           {
-             /* The supplied vector is full.
-                Either make it bigger, or don't store any more in it.  */
-             if (extend)
-               {
-                 vec = xpalloc (vec, len_ptr, 1, OVERLAY_COUNT_MAX,
-                                sizeof *vec);
-                 *vec_ptr = vec;
-                 len = *len_ptr;
-               }
-             else
-               inhibit_storing = 1;
-           }
+  Lisp_Object *vec = *vec_ptr;
+  struct itree_node *node;
 
-         if (!inhibit_storing)
-           vec[idx] = overlay;
-         /* Keep counting overlays even if we can't return them all.  */
-         idx++;
-       }
-      else if (startpos < next)
-       next = startpos;
-    }
+  /* Extend the search range if overlays beginning at ZV are
+     wanted.  */
+  ptrdiff_t search_end = ZV;
+  if (end >= ZV && (empty || trailing))
+    ++search_end;
 
-  for (struct Lisp_Overlay *tail = current_buffer->overlays_after;
-       tail; tail = tail->next)
+  ITREE_FOREACH (node, current_buffer->overlays, beg, search_end,
+                 ASCENDING)
     {
-      Lisp_Object overlay = make_lisp_ptr (tail, Lisp_Vectorlike);
-      Lisp_Object start = OVERLAY_START (overlay);
-      Lisp_Object end = OVERLAY_END (overlay);
-      ptrdiff_t startpos = OVERLAY_POSITION (start);
-      if (pos < startpos)
-       {
-         if (startpos < next)
-           next = startpos;
-         break;
-       }
-      ptrdiff_t endpos = OVERLAY_POSITION (end);
-      if (pos < endpos)
-       {
-         if (idx == len)
-           {
-             if (extend)
-               {
-                 vec = xpalloc (vec, len_ptr, 1, OVERLAY_COUNT_MAX,
-                                sizeof *vec);
-                 *vec_ptr = vec;
-                 len = *len_ptr;
-               }
-             else
-               inhibit_storing = 1;
-           }
+      if (node->begin > end)
+        {
+          next = min (next, node->begin);
+          ITREE_FOREACH_ABORT ();
+          break;
+        }
+      else if (node->begin == end)
+        {
+          next = node->begin;
+          if ((! empty || end < ZV) && beg < end)
+            {
+              ITREE_FOREACH_ABORT ();
+              break;
+            }
+          if (empty && node->begin != node->end)
+            continue;
+        }
 
-         if (!inhibit_storing)
-           vec[idx] = overlay;
-         idx++;
+      if (! empty && node->begin == node->end)
+        continue;
 
-         if (startpos < pos && startpos > prev)
-           prev = startpos;
-       }
-      else if (endpos < pos && endpos > prev)
-       prev = endpos;
-      else if (endpos == pos && startpos > prev
-              && (!change_req || startpos < pos))
-       prev = startpos;
+      if (extend && idx == len)
+        {
+          vec = xpalloc (vec, len_ptr, 1, OVERLAY_COUNT_MAX,
+                         sizeof *vec);
+          *vec_ptr = vec;
+          len = *len_ptr;
+        }
+      if (idx < len)
+        vec[idx] = node->data;
+      /* Keep counting overlays even if we can't return them all.  */
+      idx++;
     }
-
   if (next_ptr)
-    *next_ptr = next;
-  if (prev_ptr)
-    *prev_ptr = prev;
+    *next_ptr = next ? next : ZV;
+
   return idx;
 }
-
-/* Find all the overlays in the current buffer that overlap the range
-   BEG-END, or are empty at BEG, or are empty at END provided END
-   denotes the position at the end of the current buffer.
 
-   Return the number found, and store them in a vector in *VEC_PTR.
-   Store in *LEN_PTR the size allocated for the vector.
-   Store in *NEXT_PTR the next position after POS where an overlay starts,
-     or ZV if there are no more overlays.
-   Store in *PREV_PTR the previous position before POS where an overlay ends,
-     or BEGV if there are no previous overlays.
-   NEXT_PTR and/or PREV_PTR may be 0, meaning don't store that info.
+/* Find all non-empty overlays in the current buffer that contain
+   position POS.
 
-   *VEC_PTR and *LEN_PTR should contain a valid vector and size
-   when this function is called.
+   See overlays_in for the meaning of the arguments.
+  */
 
-   If EXTEND, make the vector bigger if necessary.
-   If not, never extend the vector,
-   and store only as many overlays as will fit.
-   But still return the total number of overlays.  */
+ptrdiff_t
+overlays_at (ptrdiff_t pos, bool extend,
+             Lisp_Object **vec_ptr, ptrdiff_t *len_ptr,
+             ptrdiff_t *next_ptr)
+{
+  return overlays_in (pos, pos + 1, extend, vec_ptr, len_ptr,
+                     false, true, next_ptr);
+}
 
-static ptrdiff_t
-overlays_in (EMACS_INT beg, EMACS_INT end, bool extend,
-            Lisp_Object **vec_ptr, ptrdiff_t *len_ptr,
-            ptrdiff_t *next_ptr, ptrdiff_t *prev_ptr)
+ptrdiff_t
+next_overlay_change (ptrdiff_t pos)
 {
-  ptrdiff_t idx = 0;
-  ptrdiff_t len = *len_ptr;
-  Lisp_Object *vec = *vec_ptr;
   ptrdiff_t next = ZV;
-  ptrdiff_t prev = BEGV;
-  bool inhibit_storing = 0;
-  bool end_is_Z = end == ZV;
+  struct itree_node *node;
 
-  for (struct Lisp_Overlay *tail = current_buffer->overlays_before;
-       tail; tail = tail->next)
+  ITREE_FOREACH (node, current_buffer->overlays, pos, next, ASCENDING)
     {
-      Lisp_Object overlay = make_lisp_ptr (tail, Lisp_Vectorlike);
-      Lisp_Object ostart = OVERLAY_START (overlay);
-      Lisp_Object oend = OVERLAY_END (overlay);
-      ptrdiff_t endpos = OVERLAY_POSITION (oend);
-      if (endpos < beg)
-       {
-         if (prev < endpos)
-           prev = endpos;
-         break;
-       }
-      ptrdiff_t startpos = OVERLAY_POSITION (ostart);
-      /* Count an interval if it overlaps the range, is empty at the
-        start of the range, or is empty at END provided END denotes the
-        end of the buffer.  */
-      if ((beg < endpos && startpos < end)
-         || (startpos == endpos
-             && (beg == endpos || (end_is_Z && endpos == end))))
-       {
-         if (idx == len)
-           {
-             /* The supplied vector is full.
-                Either make it bigger, or don't store any more in it.  */
-             if (extend)
-               {
-                 vec = xpalloc (vec, len_ptr, 1, OVERLAY_COUNT_MAX,
-                                sizeof *vec);
-                 *vec_ptr = vec;
-                 len = *len_ptr;
-               }
-             else
-               inhibit_storing = 1;
-           }
-
-         if (!inhibit_storing)
-           vec[idx] = overlay;
-         /* Keep counting overlays even if we can't return them all.  */
-         idx++;
-       }
-      else if (startpos < next)
-       next = startpos;
+      if (node->begin > pos)
+        {
+          /* If we reach this branch, node->begin must be the least upper bound
+             of pos, because the search is limited to [pos,next) . */
+          eassert (node->begin < next);
+          next = node->begin;
+          ITREE_FOREACH_ABORT ();
+          break;
+        }
+      else if (node->begin < node->end && node->end < next)
+        {
+          next = node->end;
+          ITREE_FOREACH_NARROW (pos, next);
+        }
     }
 
-  for (struct Lisp_Overlay *tail = current_buffer->overlays_after;
-       tail; tail = tail->next)
-    {
-      Lisp_Object overlay = make_lisp_ptr (tail, Lisp_Vectorlike);
-      Lisp_Object ostart = OVERLAY_START (overlay);
-      Lisp_Object oend = OVERLAY_END (overlay);
-      ptrdiff_t startpos = OVERLAY_POSITION (ostart);
-      if (end < startpos)
-       {
-         if (startpos < next)
-           next = startpos;
-         break;
-       }
-      ptrdiff_t endpos = OVERLAY_POSITION (oend);
-      /* Count an interval if it overlaps the range, is empty at the
-        start of the range, or is empty at END provided END denotes the
-        end of the buffer.  */
-      if ((beg < endpos && startpos < end)
-         || (startpos == endpos
-             && (beg == endpos || (end_is_Z && endpos == end))))
-       {
-         if (idx == len)
-           {
-             if (extend)
-               {
-                 vec = xpalloc (vec, len_ptr, 1, OVERLAY_COUNT_MAX,
-                                sizeof *vec);
-                 *vec_ptr = vec;
-                 len = *len_ptr;
-               }
-             else
-               inhibit_storing = 1;
-           }
+  return next;
+}
 
-         if (!inhibit_storing)
-           vec[idx] = overlay;
-         idx++;
-       }
-      else if (endpos < beg && endpos > prev)
-       prev = endpos;
+ptrdiff_t
+previous_overlay_change (ptrdiff_t pos)
+{
+  struct itree_node *node;
+  ptrdiff_t prev = BEGV;
+
+  ITREE_FOREACH (node, current_buffer->overlays, prev, pos, DESCENDING)
+    {
+      if (node->end < pos)
+        prev = node->end;
+      else
+        prev = max (prev, node->begin);
+      ITREE_FOREACH_NARROW (prev, pos);
     }
 
-  if (next_ptr)
-    *next_ptr = next;
-  if (prev_ptr)
-    *prev_ptr = prev;
-  return idx;
+  return prev;
 }
 
-
 /* Return true if there exists an overlay with a non-nil
    `mouse-face' property overlapping OVERLAY.  */
 
 bool
 mouse_face_overlay_overlaps (Lisp_Object overlay)
 {
-  ptrdiff_t start = OVERLAY_POSITION (OVERLAY_START (overlay));
-  ptrdiff_t end = OVERLAY_POSITION (OVERLAY_END (overlay));
+  ptrdiff_t start = OVERLAY_START (overlay);
+  ptrdiff_t end = OVERLAY_END (overlay);
   ptrdiff_t n, i, size;
   Lisp_Object *v, tem;
   Lisp_Object vbuf[10];
@@ -3140,11 +3096,11 @@ mouse_face_overlay_overlaps (Lisp_Object overlay)
 
   size = ARRAYELTS (vbuf);
   v = vbuf;
-  n = overlays_in (start, end, 0, &v, &size, NULL, NULL);
+  n = overlays_in (start, end, 0, &v, &size, true, false, NULL);
   if (n > size)
     {
       SAFE_NALLOCA (v, 1, n);
-      overlays_in (start, end, 0, &v, &n, NULL, NULL);
+      overlays_in (start, end, 0, &v, &n, true, false, NULL);
     }
 
   for (i = 0; i < n; ++i)
@@ -3169,11 +3125,11 @@ disable_line_numbers_overlay_at_eob (void)
 
   size = ARRAYELTS (vbuf);
   v = vbuf;
-  n = overlays_in (ZV, ZV, 0, &v, &size, NULL, NULL);
+  n = overlays_in (ZV, ZV, 0, &v, &size, false, false, NULL);
   if (n > size)
     {
       SAFE_NALLOCA (v, 1, n);
-      overlays_in (ZV, ZV, 0, &v, &n, NULL, NULL);
+      overlays_in (ZV, ZV, 0, &v, &n, false, false, NULL);
     }
 
   for (i = 0; i < n; ++i)
@@ -3186,47 +3142,28 @@ disable_line_numbers_overlay_at_eob (void)
 }
 
 
-/* Fast function to just test if we're at an overlay boundary.  */
+/* Fast function to just test if we're at an overlay boundary.
+
+   Returns true if some overlay starts or ends (or both) at POS,
+*/
 bool
 overlay_touches_p (ptrdiff_t pos)
 {
-  for (struct Lisp_Overlay *tail = current_buffer->overlays_before;
-       tail; tail = tail->next)
-    {
-      Lisp_Object overlay = make_lisp_ptr (tail, Lisp_Vectorlike);
-      eassert (OVERLAYP (overlay));
+  struct itree_node *node;
 
-      ptrdiff_t endpos = OVERLAY_POSITION (OVERLAY_END (overlay));
-      if (endpos < pos)
-       break;
-      if (endpos == pos || OVERLAY_POSITION (OVERLAY_START (overlay)) == pos)
-       return 1;
-    }
-
-  for (struct Lisp_Overlay *tail = current_buffer->overlays_after;
-       tail; tail = tail->next)
-    {
-      Lisp_Object overlay = make_lisp_ptr (tail, Lisp_Vectorlike);
-      eassert (OVERLAYP (overlay));
-
-      ptrdiff_t startpos = OVERLAY_POSITION (OVERLAY_START (overlay));
-      if (pos < startpos)
-       break;
-      if (startpos == pos || OVERLAY_POSITION (OVERLAY_END (overlay)) == pos)
-       return 1;
-    }
-  return 0;
+  /* We need to find overlays ending in pos, as well as empty ones at
+     pos. */
+  ITREE_FOREACH (node, current_buffer->overlays, pos - 1, pos + 1, DESCENDING)
+    if (node->begin == pos || node->end == pos)
+      {
+        ITREE_FOREACH_ABORT ();
+        return true;
+      }
+  return false;
 }
-
-struct sortvec
-{
-  Lisp_Object overlay;
-  ptrdiff_t beg, end;
-  EMACS_INT priority;
-  EMACS_INT spriority;         /* Secondary priority.  */
-};
 
-static int
+
+int
 compare_overlays (const void *v1, const void *v2)
 {
   const struct sortvec *s1 = v1;
@@ -3255,6 +3192,33 @@ compare_overlays (const void *v1, const void *v2)
     return XLI (s1->overlay) < XLI (s2->overlay) ? -1 : 1;
 }
 
+void
+make_sortvec_item (struct sortvec *item, Lisp_Object overlay)
+{
+  Lisp_Object tem;
+  /* This overlay is good and counts: put it into sortvec.  */
+  item->overlay = overlay;
+  item->beg = OVERLAY_START (overlay);
+  item->end = OVERLAY_END (overlay);
+  tem = Foverlay_get (overlay, Qpriority);
+  if (NILP (tem))
+    {
+      item->priority = 0;
+      item->spriority = 0;
+    }
+  else if (FIXNUMP (tem))
+    {
+      item->priority = XFIXNUM (tem);
+      item->spriority = 0;
+    }
+  else if (CONSP (tem))
+    {
+      Lisp_Object car = XCAR (tem);
+      Lisp_Object cdr = XCDR (tem);
+      item->priority  = FIXNUMP (car) ? XFIXNUM (car) : 0;
+      item->spriority = FIXNUMP (cdr) ? XFIXNUM (cdr) : 0;
+    }
+}
 /* Sort an array of overlays by priority.  The array is modified in place.
    The return value is the new size; this may be smaller than the original
    size if some of the overlays were invalid or were window-specific.  */
@@ -3271,47 +3235,18 @@ sort_overlays (Lisp_Object *overlay_vec, ptrdiff_t 
noverlays, struct window *w)
 
   for (i = 0, j = 0; i < noverlays; i++)
     {
-      Lisp_Object tem;
       Lisp_Object overlay;
 
       overlay = overlay_vec[i];
       if (OVERLAYP (overlay)
-         && OVERLAY_POSITION (OVERLAY_START (overlay)) > 0
-         && OVERLAY_POSITION (OVERLAY_END (overlay)) > 0)
+         && OVERLAY_START (overlay) > 0
+         && OVERLAY_END (overlay) > 0)
        {
-         /* If we're interested in a specific window, then ignore
-            overlays that are limited to some other window.  */
-         if (w)
-           {
-             Lisp_Object window;
-
-             window = Foverlay_get (overlay, Qwindow);
-             if (WINDOWP (window) && XWINDOW (window) != w)
-               continue;
-           }
-
-         /* This overlay is good and counts: put it into sortvec.  */
-         sortvec[j].overlay = overlay;
-         sortvec[j].beg = OVERLAY_POSITION (OVERLAY_START (overlay));
-         sortvec[j].end = OVERLAY_POSITION (OVERLAY_END (overlay));
-         tem = Foverlay_get (overlay, Qpriority);
-         if (NILP (tem))
-           {
-             sortvec[j].priority = 0;
-             sortvec[j].spriority = 0;
-           }
-         else if (FIXNUMP (tem))
-           {
-             sortvec[j].priority = XFIXNUM (tem);
-             sortvec[j].spriority = 0;
-           }
-         else if (CONSP (tem))
-           {
-             Lisp_Object car = XCAR (tem);
-             Lisp_Object cdr = XCDR (tem);
-             sortvec[j].priority  = FIXNUMP (car) ? XFIXNUM (car) : 0;
-             sortvec[j].spriority = FIXNUMP (cdr) ? XFIXNUM (cdr) : 0;
-           }
+          /* If we're interested in a specific window, then ignore
+             overlays that are limited to some other window.  */
+          if (w && ! overlay_matches_window (w, overlay))
+            continue;
+          make_sortvec_item (sortvec + j, overlay);
          j++;
        }
     }
@@ -3426,25 +3361,27 @@ ptrdiff_t
 overlay_strings (ptrdiff_t pos, struct window *w, unsigned char **pstr)
 {
   bool multibyte = ! NILP (BVAR (current_buffer, enable_multibyte_characters));
+  struct itree_node *node;
 
   overlay_heads.used = overlay_heads.bytes = 0;
   overlay_tails.used = overlay_tails.bytes = 0;
-  for (struct Lisp_Overlay *ov = current_buffer->overlays_before;
-       ov; ov = ov->next)
+
+  ITREE_FOREACH (node, current_buffer->overlays, pos - 1, pos + 1, DESCENDING)
     {
-      Lisp_Object overlay = make_lisp_ptr (ov, Lisp_Vectorlike);
+      Lisp_Object overlay = node->data;
       eassert (OVERLAYP (overlay));
 
-      ptrdiff_t startpos = OVERLAY_POSITION (OVERLAY_START (overlay));
-      ptrdiff_t endpos = OVERLAY_POSITION (OVERLAY_END (overlay));
-      if (endpos < pos)
-       break;
+      ptrdiff_t startpos = node->begin;
+      ptrdiff_t endpos = node->end;
+
       if (endpos != pos && startpos != pos)
        continue;
       Lisp_Object window = Foverlay_get (overlay, Qwindow);
       if (WINDOWP (window) && XWINDOW (window) != w)
        continue;
       Lisp_Object str;
+      /* FIXME: Are we really sure that `record_overlay_string` can
+         never cause a non-local exit?  */
       if (startpos == pos
          && (str = Foverlay_get (overlay, Qbefore_string), STRINGP (str)))
        record_overlay_string (&overlay_heads, str,
@@ -3459,36 +3396,7 @@ overlay_strings (ptrdiff_t pos, struct window *w, 
unsigned char **pstr)
                               Foverlay_get (overlay, Qpriority),
                               endpos - startpos);
     }
-  for (struct Lisp_Overlay *ov = current_buffer->overlays_after;
-       ov; ov = ov->next)
-    {
-      Lisp_Object overlay = make_lisp_ptr (ov, Lisp_Vectorlike);
-      eassert (OVERLAYP (overlay));
 
-      ptrdiff_t startpos = OVERLAY_POSITION (OVERLAY_START (overlay));
-      ptrdiff_t endpos = OVERLAY_POSITION (OVERLAY_END (overlay));
-      if (startpos > pos)
-       break;
-      if (endpos != pos && startpos != pos)
-       continue;
-      Lisp_Object window = Foverlay_get (overlay, Qwindow);
-      if (WINDOWP (window) && XWINDOW (window) != w)
-       continue;
-      Lisp_Object str;
-      if (startpos == pos
-         && (str = Foverlay_get (overlay, Qbefore_string), STRINGP (str)))
-       record_overlay_string (&overlay_heads, str,
-                              (startpos == endpos
-                               ? Foverlay_get (overlay, Qafter_string)
-                               : Qnil),
-                              Foverlay_get (overlay, Qpriority),
-                              endpos - startpos);
-      else if (endpos == pos
-              && (str = Foverlay_get (overlay, Qafter_string), STRINGP (str)))
-       record_overlay_string (&overlay_tails, str, Qnil,
-                              Foverlay_get (overlay, Qpriority),
-                              endpos - startpos);
-    }
   if (overlay_tails.used > 1)
     qsort (overlay_tails.buf, overlay_tails.used, sizeof (struct sortstr),
           cmp_for_strings);
@@ -3543,384 +3451,71 @@ overlay_strings (ptrdiff_t pos, struct window *w, 
unsigned char **pstr)
     }
   return 0;
 }
-
-/* Shift overlays in BUF's overlay lists, to center the lists at POS.  */
 
+
 void
-recenter_overlay_lists (struct buffer *buf, ptrdiff_t pos)
+adjust_overlays_for_insert (ptrdiff_t pos, ptrdiff_t length, bool 
before_markers)
 {
-  struct Lisp_Overlay *prev, *next;
-
-  /* See if anything in overlays_before should move to overlays_after.  */
-
-  /* We don't strictly need prev in this loop; it should always be nil.
-     But we use it for symmetry and in case that should cease to be true
-     with some future change.  */
-  prev = NULL;
-  for (struct Lisp_Overlay *tail = buf->overlays_before;
-       tail; prev = tail, tail = next)
-    {
-      next = tail->next;
-      Lisp_Object overlay = make_lisp_ptr (tail, Lisp_Vectorlike);
-      eassert (OVERLAYP (overlay));
-
-      Lisp_Object beg = OVERLAY_START (overlay);
-      Lisp_Object end = OVERLAY_END (overlay);
-
-      if (OVERLAY_POSITION (end) > pos)
-       {
-         /* OVERLAY needs to be moved.  */
-         ptrdiff_t where = OVERLAY_POSITION (beg);
-         struct Lisp_Overlay *other, *other_prev;
-
-         /* Splice the cons cell TAIL out of overlays_before.  */
-         if (prev)
-           prev->next = next;
-         else
-           set_buffer_overlays_before (buf, next);
-
-         /* Search thru overlays_after for where to put it.  */
-         other_prev = NULL;
-         for (other = buf->overlays_after; other;
-              other_prev = other, other = other->next)
-           {
-             Lisp_Object otheroverlay = make_lisp_ptr (other, Lisp_Vectorlike);
-             eassert (OVERLAYP (otheroverlay));
-
-             Lisp_Object otherbeg = OVERLAY_START (otheroverlay);
-             if (OVERLAY_POSITION (otherbeg) >= where)
-               break;
-           }
-
-         /* Add TAIL to overlays_after before OTHER.  */
-         tail->next = other;
-         if (other_prev)
-           other_prev->next = tail;
-         else
-           set_buffer_overlays_after (buf, tail);
-         tail = prev;
-       }
-      else
-       /* We've reached the things that should stay in overlays_before.
-          All the rest of overlays_before must end even earlier,
-          so stop now.  */
-       break;
-    }
-
-  /* See if anything in overlays_after should be in overlays_before.  */
-  prev = NULL;
-  for (struct Lisp_Overlay *tail = buf->overlays_after;
-       tail; prev = tail, tail = next)
+  if (!current_buffer->indirections)
+    itree_insert_gap (current_buffer->overlays, pos, length, before_markers);
+  else
     {
-      next = tail->next;
-      Lisp_Object overlay = make_lisp_ptr (tail, Lisp_Vectorlike);
-      eassert (OVERLAYP (overlay));
-
-      Lisp_Object beg = OVERLAY_START (overlay);
-      Lisp_Object end = OVERLAY_END (overlay);
-
-      /* Stop looking, when we know that nothing further
-        can possibly end before POS.  */
-      if (OVERLAY_POSITION (beg) > pos)
-       break;
-
-      if (OVERLAY_POSITION (end) <= pos)
-       {
-         /* OVERLAY needs to be moved.  */
-         ptrdiff_t where = OVERLAY_POSITION (end);
-         struct Lisp_Overlay *other, *other_prev;
-
-         /* Splice the cons cell TAIL out of overlays_after.  */
-         if (prev)
-           prev->next = next;
-         else
-           set_buffer_overlays_after (buf, next);
-
-         /* Search thru overlays_before for where to put it.  */
-         other_prev = NULL;
-         for (other = buf->overlays_before; other;
-              other_prev = other, other = other->next)
-           {
-             Lisp_Object otheroverlay = make_lisp_ptr (other, Lisp_Vectorlike);
-             eassert (OVERLAYP (otheroverlay));
-
-             Lisp_Object otherend = OVERLAY_END (otheroverlay);
-             if (OVERLAY_POSITION (otherend) <= where)
-               break;
-           }
-
-         /* Add TAIL to overlays_before before OTHER.  */
-         tail->next = other;
-         if (other_prev)
-           other_prev->next = tail;
-         else
-           set_buffer_overlays_before (buf, tail);
-         tail = prev;
-       }
+      struct buffer *base = current_buffer->base_buffer
+                            ? current_buffer->base_buffer
+                            : current_buffer;
+      Lisp_Object tail, other;
+      itree_insert_gap (base->overlays, pos, length, before_markers);
+      FOR_EACH_LIVE_BUFFER (tail, other)
+        if (XBUFFER (other)->base_buffer == base)
+         itree_insert_gap (XBUFFER (other)->overlays, pos, length,
+                           before_markers);
     }
-
-  buf->overlay_center = pos;
 }
 
-void
-adjust_overlays_for_insert (ptrdiff_t pos, ptrdiff_t length)
-{
-  /* After an insertion, the lists are still sorted properly,
-     but we may need to update the value of the overlay center.  */
-  if (current_buffer->overlay_center >= pos)
-    current_buffer->overlay_center += length;
-}
-
-void
-adjust_overlays_for_delete (ptrdiff_t pos, ptrdiff_t length)
+static void
+adjust_overlays_for_delete_in_buffer (struct buffer * buf,
+                                      ptrdiff_t pos, ptrdiff_t length)
 {
-  if (current_buffer->overlay_center < pos)
-    /* The deletion was to our right.  No change needed; the before- and
-       after-lists are still consistent.  */
-    ;
-  else if (current_buffer->overlay_center - pos > length)
-    /* The deletion was to our left.  We need to adjust the center value
-       to account for the change in position, but the lists are consistent
-       given the new value.  */
-    current_buffer->overlay_center -= length;
-  else
-    /* We're right in the middle.  There might be things on the after-list
-       that now belong on the before-list.  Recentering will move them,
-       and also update the center point.  */
-    recenter_overlay_lists (current_buffer, pos);
-}
-
-/* Fix up overlays that were garbled as a result of permuting markers
-   in the range START through END.  Any overlay with at least one
-   endpoint in this range will need to be unlinked from the overlay
-   list and reinserted in its proper place.
-   Such an overlay might even have negative size at this point.
-   If so, we'll make the overlay empty. */
-void
-fix_start_end_in_overlays (register ptrdiff_t start, register ptrdiff_t end)
-{
-  struct Lisp_Overlay *before_list UNINIT;
-  struct Lisp_Overlay *after_list UNINIT;
-  /* These are either nil, indicating that before_list or after_list
-     should be assigned, or the cons cell the cdr of which should be
-     assigned.  */
-  struct Lisp_Overlay *beforep = NULL, *afterp = NULL;
-  /* 'Parent', likewise, indicates a cons cell or
-     current_buffer->overlays_before or overlays_after, depending
-     which loop we're in.  */
-  struct Lisp_Overlay *parent;
-
-  /* This algorithm shifts links around instead of consing and GCing.
-     The loop invariant is that before_list (resp. after_list) is a
-     well-formed list except that its last element, the CDR of beforep
-     (resp. afterp) if beforep (afterp) isn't nil or before_list
-     (after_list) if it is, is still uninitialized.  So it's not a bug
-     that before_list isn't initialized, although it may look
-     strange.  */
-  parent = NULL;
-  for (struct Lisp_Overlay *tail = current_buffer->overlays_before;
-       tail; tail = tail->next)
-    {
-      Lisp_Object overlay = make_lisp_ptr (tail, Lisp_Vectorlike);
-
-      ptrdiff_t endpos = OVERLAY_POSITION (OVERLAY_END (overlay));
-      ptrdiff_t startpos = OVERLAY_POSITION (OVERLAY_START (overlay));
+  Lisp_Object hit_list = Qnil;
+  struct itree_node *node;
 
-      /* If the overlay is backwards, make it empty.  */
-      if (endpos < startpos)
-       {
-         startpos = endpos;
-         Fset_marker (OVERLAY_START (overlay), make_fixnum (startpos),
-                      Qnil);
-       }
+  /* Ideally, the evaporate check would be done directly within
+     `itree_delete_gap`, but that code isn't supposed to know about overlays,
+     only about `itree_node`s, so it would break an abstraction boundary.  */
+  itree_delete_gap (buf->overlays, pos, length);
 
-      if (endpos < start)
-       break;
+  /* Delete any zero-sized overlays at position POS, if the `evaporate'
+     property is set.  */
 
-      if (endpos < end
-         || (startpos >= start && startpos < end))
-       {
-         /* Add it to the end of the wrong list.  Later on,
-            recenter_overlay_lists will move it to the right place.  */
-         if (endpos < current_buffer->overlay_center)
-           {
-             if (!afterp)
-               after_list = tail;
-             else
-               afterp->next = tail;
-             afterp = tail;
-           }
-         else
-           {
-             if (!beforep)
-               before_list = tail;
-             else
-               beforep->next = tail;
-             beforep = tail;
-           }
-         if (!parent)
-           set_buffer_overlays_before (current_buffer, tail->next);
-         else
-           parent->next = tail->next;
-       }
-      else
-       parent = tail;
-    }
-  parent = NULL;
-  for (struct Lisp_Overlay *tail = current_buffer->overlays_after;
-       tail; tail = tail->next)
+  ITREE_FOREACH (node, buf->overlays, pos, pos, ASCENDING)
     {
-      Lisp_Object overlay = make_lisp_ptr (tail, Lisp_Vectorlike);
-
-      ptrdiff_t startpos = OVERLAY_POSITION (OVERLAY_START (overlay));
-      ptrdiff_t endpos = OVERLAY_POSITION (OVERLAY_END (overlay));
-
-      /* If the overlay is backwards, make it empty.  */
-      if (endpos < startpos)
-       {
-         startpos = endpos;
-         Fset_marker (OVERLAY_START (overlay), make_fixnum (startpos),
-                      Qnil);
-       }
-
-      if (startpos >= end)
-       break;
-
-      if (startpos >= start
-         || (endpos >= start && endpos < end))
-       {
-         if (endpos < current_buffer->overlay_center)
-           {
-             if (!afterp)
-               after_list = tail;
-             else
-               afterp->next = tail;
-             afterp = tail;
-           }
-         else
-           {
-             if (!beforep)
-               before_list = tail;
-             else
-               beforep->next = tail;
-             beforep = tail;
-           }
-         if (!parent)
-           set_buffer_overlays_after (current_buffer, tail->next);
-         else
-           parent->next = tail->next;
-       }
-      else
-       parent = tail;
+      if (node->end == pos && node->begin == pos
+          && ! NILP (Foverlay_get (node->data, Qevaporate)))
+        hit_list = Fcons (node->data, hit_list);
     }
 
-  /* Splice the constructed (wrong) lists into the buffer's lists,
-     and let the recenter function make it sane again.  */
-  if (beforep)
-    {
-      beforep->next = current_buffer->overlays_before;
-      set_buffer_overlays_before (current_buffer, before_list);
-    }
-
-  if (afterp)
-    {
-      afterp->next = current_buffer->overlays_after;
-      set_buffer_overlays_after (current_buffer, after_list);
-    }
-  recenter_overlay_lists (current_buffer, current_buffer->overlay_center);
+  for (; CONSP (hit_list); hit_list = XCDR (hit_list))
+    Fdelete_overlay (XCAR (hit_list));
 }
 
-/* We have two types of overlay: the one whose ending marker is
-   after-insertion-marker (this is the usual case) and the one whose
-   ending marker is before-insertion-marker.  When `overlays_before'
-   contains overlays of the latter type and the former type in this
-   order and both overlays end at inserting position, inserting a text
-   increases only the ending marker of the latter type, which results
-   in incorrect ordering of `overlays_before'.
-
-   This function fixes ordering of overlays in the slot
-   `overlays_before' of the buffer *BP.  Before the insertion, `point'
-   was at PREV, and now is at POS.  */
-
 void
-fix_overlays_before (struct buffer *bp, ptrdiff_t prev, ptrdiff_t pos)
+adjust_overlays_for_delete (ptrdiff_t pos, ptrdiff_t length)
 {
-  /* If parent is nil, replace overlays_before; otherwise, parent->next.  */
-  struct Lisp_Overlay *tail = bp->overlays_before, *parent = NULL, *right_pair;
-  Lisp_Object tem;
-  ptrdiff_t end UNINIT;
-
-  /* After the insertion, the several overlays may be in incorrect
-     order.  The possibility is that, in the list `overlays_before',
-     an overlay which ends at POS appears after an overlay which ends
-     at PREV.  Since POS is greater than PREV, we must fix the
-     ordering of these overlays, by moving overlays ends at POS before
-     the overlays ends at PREV.  */
-
-  /* At first, find a place where disordered overlays should be linked
-     in.  It is where an overlay which end before POS exists. (i.e. an
-     overlay whose ending marker is after-insertion-marker if disorder
-     exists).  */
-  while (tail
-        && (tem = make_lisp_ptr (tail, Lisp_Vectorlike),
-            (end = OVERLAY_POSITION (OVERLAY_END (tem))) >= pos))
-    {
-      parent = tail;
-      tail = tail->next;
-    }
-
-  /* If we don't find such an overlay,
-     or the found one ends before PREV,
-     or the found one is the last one in the list,
-     we don't have to fix anything.  */
-  if (!tail)
-    return;
-  if (end < prev || !tail->next)
-    return;
-
-  right_pair = parent;
-  parent = tail;
-  tail = tail->next;
-
-  /* Now, end position of overlays in the list TAIL should be before
-     or equal to PREV.  In the loop, an overlay which ends at POS is
-     moved ahead to the place indicated by the CDR of RIGHT_PAIR.  If
-     we found an overlay which ends before PREV, the remaining
-     overlays are in correct order.  */
-  while (tail)
+  if (!current_buffer->indirections)
+    adjust_overlays_for_delete_in_buffer (current_buffer, pos, length);
+  else
     {
-      tem = make_lisp_ptr (tail, Lisp_Vectorlike);
-      end = OVERLAY_POSITION (OVERLAY_END (tem));
-
-      if (end == pos)
-       {                       /* This overlay is disordered. */
-         struct Lisp_Overlay *found = tail;
-
-         /* Unlink the found overlay.  */
-         tail = found->next;
-         parent->next = tail;
-         /* Move an overlay at RIGHT_PLACE to the next of the found one,
-            and link it into the right place.  */
-         if (!right_pair)
-           {
-             found->next = bp->overlays_before;
-             set_buffer_overlays_before (bp, found);
-           }
-         else
-           {
-             found->next = right_pair->next;
-             right_pair->next = found;
-           }
-       }
-      else if (end == prev)
-       {
-         parent = tail;
-         tail = tail->next;
-       }
-      else                     /* No more disordered overlay. */
-       break;
+      struct buffer *base = current_buffer->base_buffer
+                            ? current_buffer->base_buffer
+                            : current_buffer;
+      Lisp_Object tail, other;
+      adjust_overlays_for_delete_in_buffer (base, pos, length);
+      FOR_EACH_LIVE_BUFFER (tail, other)
+        if (XBUFFER (other)->base_buffer == base)
+          adjust_overlays_for_delete_in_buffer (XBUFFER (other), pos, length);
     }
 }
+
 
 DEFUN ("overlayp", Foverlayp, Soverlayp, 1, 1, 0,
        doc: /* Return t if OBJECT is an overlay.  */)
@@ -3942,7 +3537,7 @@ for the rear of the overlay advance when text is inserted 
there
   (Lisp_Object beg, Lisp_Object end, Lisp_Object buffer,
    Lisp_Object front_advance, Lisp_Object rear_advance)
 {
-  Lisp_Object overlay;
+  Lisp_Object ov;
   struct buffer *b;
 
   if (NILP (buffer))
@@ -3950,6 +3545,10 @@ for the rear of the overlay advance when text is 
inserted there
   else
     CHECK_BUFFER (buffer);
 
+  b = XBUFFER (buffer);
+  if (! BUFFER_LIVE_P (b))
+    error ("Attempt to create overlay in a dead buffer");
+
   if (MARKERP (beg) && !BASE_EQ (Fmarker_buffer (beg), buffer))
     signal_error ("Marker points into wrong buffer", beg);
   if (MARKERP (end) && !BASE_EQ (Fmarker_buffer (end), buffer))
@@ -3964,39 +3563,16 @@ for the rear of the overlay advance when text is 
inserted there
       temp = beg; beg = end; end = temp;
     }
 
-  b = XBUFFER (buffer);
-
-  beg = Fset_marker (Fmake_marker (), beg, buffer);
-  end = Fset_marker (Fmake_marker (), end, buffer);
-
-  if (!NILP (front_advance))
-    XMARKER (beg)->insertion_type = 1;
-  if (!NILP (rear_advance))
-    XMARKER (end)->insertion_type = 1;
-
-  overlay = build_overlay (beg, end, Qnil);
-
-  /* Put the new overlay on the wrong list.  */
-  end = OVERLAY_END (overlay);
-  if (OVERLAY_POSITION (end) < b->overlay_center)
-    {
-      eassert (b->overlays_after || (XOVERLAY (overlay)->next == NULL));
-      XOVERLAY (overlay)->next = b->overlays_after;
-      set_buffer_overlays_after (b, XOVERLAY (overlay));
-    }
-  else
-    {
-      eassert (b->overlays_before || (XOVERLAY (overlay)->next == NULL));
-      XOVERLAY (overlay)->next = b->overlays_before;
-      set_buffer_overlays_before (b, XOVERLAY (overlay));
-    }
-  /* This puts it in the right list, and in the right order.  */
-  recenter_overlay_lists (b, b->overlay_center);
+  ptrdiff_t obeg = clip_to_bounds (BUF_BEG (b), XFIXNUM (beg), BUF_Z (b));
+  ptrdiff_t oend = clip_to_bounds (obeg, XFIXNUM (end), BUF_Z (b));
+  ov = build_overlay (! NILP (front_advance),
+                      ! NILP (rear_advance), Qnil);
+  add_buffer_overlay (b, XOVERLAY (ov), obeg, oend);
 
   /* We don't need to redisplay the region covered by the overlay, because
      the overlay has no properties at the moment.  */
 
-  return overlay;
+  return ov;
 }
 
 /* Mark a section of BUF as needing redisplay because of overlays changes.  */
@@ -4018,35 +3594,6 @@ modify_overlay (struct buffer *buf, ptrdiff_t start, 
ptrdiff_t end)
   modiff_incr (&BUF_OVERLAY_MODIFF (buf), 1);
 }
 
-/* Remove OVERLAY from LIST.  */
-
-static struct Lisp_Overlay *
-unchain_overlay (struct Lisp_Overlay *list, struct Lisp_Overlay *overlay)
-{
-  register struct Lisp_Overlay *tail, **prev = &list;
-
-  for (tail = list; tail; prev = &tail->next, tail = *prev)
-    if (tail == overlay)
-      {
-       *prev = overlay->next;
-       overlay->next = NULL;
-       break;
-      }
-  return list;
-}
-
-/* Remove OVERLAY from both overlay lists of B.  */
-
-static void
-unchain_both (struct buffer *b, Lisp_Object overlay)
-{
-  struct Lisp_Overlay *ov = XOVERLAY (overlay);
-
-  set_buffer_overlays_before (b, unchain_overlay (b->overlays_before, ov));
-  set_buffer_overlays_after (b, unchain_overlay (b->overlays_after, ov));
-  eassert (XOVERLAY (overlay)->next == NULL);
-}
-
 DEFUN ("move-overlay", Fmove_overlay, Smove_overlay, 3, 4, 0,
        doc: /* Set the endpoints of OVERLAY to BEG and END in BUFFER.
 If BUFFER is omitted, leave OVERLAY in the same buffer it inhabits now.
@@ -4057,12 +3604,11 @@ buffer.  */)
   struct buffer *b, *ob = 0;
   Lisp_Object obuffer;
   specpdl_ref count = SPECPDL_INDEX ();
-  ptrdiff_t n_beg, n_end;
   ptrdiff_t o_beg UNINIT, o_end UNINIT;
 
   CHECK_OVERLAY (overlay);
   if (NILP (buffer))
-    buffer = Fmarker_buffer (OVERLAY_START (overlay));
+    buffer = Foverlay_buffer (overlay);
   if (NILP (buffer))
     XSETBUFFER (buffer, current_buffer);
   CHECK_BUFFER (buffer);
@@ -4084,37 +3630,31 @@ buffer.  */)
       temp = beg; beg = end; end = temp;
     }
 
-  specbind (Qinhibit_quit, Qt);
+  specbind (Qinhibit_quit, Qt); /* FIXME: Why?  */
 
-  obuffer = Fmarker_buffer (OVERLAY_START (overlay));
+  obuffer = Foverlay_buffer (overlay);
   b = XBUFFER (buffer);
 
+  ptrdiff_t n_beg = clip_to_bounds (BUF_BEG (b), XFIXNUM (beg), BUF_Z (b));
+  ptrdiff_t n_end = clip_to_bounds (n_beg, XFIXNUM (end), BUF_Z (b));
+
   if (!NILP (obuffer))
     {
       ob = XBUFFER (obuffer);
 
-      o_beg = OVERLAY_POSITION (OVERLAY_START (overlay));
-      o_end = OVERLAY_POSITION (OVERLAY_END (overlay));
+      o_beg = OVERLAY_START (overlay);
+      o_end = OVERLAY_END (overlay);
+    }
 
-      unchain_both (ob, overlay);
+  if (! BASE_EQ (buffer, obuffer))
+    {
+      if (! NILP (obuffer))
+        remove_buffer_overlay (XBUFFER (obuffer), XOVERLAY (overlay));
+      add_buffer_overlay (XBUFFER (buffer), XOVERLAY (overlay), n_beg, n_end);
     }
   else
-    /* An overlay not associated with any buffer will normally have its
-       `next' field set to NULL, but not always: when killing a buffer,
-       we just set its overlays_after and overlays_before to NULL without
-       manually setting each overlay's `next' field to NULL.
-       Let's correct it here, to simplify subsequent assertions.
-       FIXME: Maybe the better fix is to change `kill-buffer'!?  */
-    XOVERLAY (overlay)->next = NULL;
-
-  eassert (XOVERLAY (overlay)->next == NULL);
-
-  /* Set the overlay boundaries, which may clip them.  */
-  Fset_marker (OVERLAY_START (overlay), beg, buffer);
-  Fset_marker (OVERLAY_END (overlay), end, buffer);
-
-  n_beg = marker_position (OVERLAY_START (overlay));
-  n_end = marker_position (OVERLAY_END (overlay));
+    itree_node_set_region (b->overlays, XOVERLAY (overlay)->interval,
+                              n_beg, n_end);
 
   /* If the overlay has changed buffers, do a thorough redisplay.  */
   if (!BASE_EQ (buffer, obuffer))
@@ -4137,8 +3677,6 @@ buffer.  */)
        modify_overlay (b, min (o_beg, n_beg), max (o_end, n_end));
     }
 
-  eassert (XOVERLAY (overlay)->next == NULL);
-
   /* Delete the overlay if it is empty after clipping and has the
      evaporate property.  */
   if (n_beg == n_end && !NILP (Foverlay_get (overlay, Qevaporate)))
@@ -4148,26 +3686,10 @@ buffer.  */)
            contrary to `Fdelete_overlay's assumptions.
          - Most of the work done by Fdelete_overlay has already been done
            here for other reasons.  */
-      drop_overlay (XBUFFER (buffer), XOVERLAY (overlay));
+      drop_overlay (XOVERLAY (overlay));
       return unbind_to (count, overlay);
     }
 
-  /* Put the overlay into the new buffer's overlay lists, first on the
-     wrong list.  */
-  if (n_end < b->overlay_center)
-    {
-      XOVERLAY (overlay)->next = b->overlays_after;
-      set_buffer_overlays_after (b, XOVERLAY (overlay));
-    }
-  else
-    {
-      XOVERLAY (overlay)->next = b->overlays_before;
-      set_buffer_overlays_before (b, XOVERLAY (overlay));
-    }
-
-  /* This puts it in the right list, and in the right order.  */
-  recenter_overlay_lists (b, b->overlay_center);
-
   return unbind_to (count, overlay);
 }
 
@@ -4175,21 +3697,18 @@ DEFUN ("delete-overlay", Fdelete_overlay, 
Sdelete_overlay, 1, 1, 0,
        doc: /* Delete the overlay OVERLAY from its buffer.  */)
   (Lisp_Object overlay)
 {
-  Lisp_Object buffer;
   struct buffer *b;
   specpdl_ref count = SPECPDL_INDEX ();
 
   CHECK_OVERLAY (overlay);
 
-  buffer = Fmarker_buffer (OVERLAY_START (overlay));
-  if (NILP (buffer))
+  b = OVERLAY_BUFFER (overlay);
+  if (! b)
     return Qnil;
 
-  b = XBUFFER (buffer);
   specbind (Qinhibit_quit, Qt);
 
-  unchain_both (b, overlay);
-  drop_overlay (b, XOVERLAY (overlay));
+  drop_overlay (XOVERLAY (overlay));
 
   /* When deleting an overlay with before or after strings, turn off
      display optimizations for the affected buffer, on the basis that
@@ -4220,8 +3739,10 @@ DEFUN ("overlay-start", Foverlay_start, Soverlay_start, 
1, 1, 0,
   (Lisp_Object overlay)
 {
   CHECK_OVERLAY (overlay);
+  if (! OVERLAY_BUFFER (overlay))
+    return Qnil;
 
-  return (Fmarker_position (OVERLAY_START (overlay)));
+  return make_fixnum (OVERLAY_START (overlay));
 }
 
 DEFUN ("overlay-end", Foverlay_end, Soverlay_end, 1, 1, 0,
@@ -4229,8 +3750,10 @@ DEFUN ("overlay-end", Foverlay_end, Soverlay_end, 1, 1, 
0,
   (Lisp_Object overlay)
 {
   CHECK_OVERLAY (overlay);
+  if (! OVERLAY_BUFFER (overlay))
+    return Qnil;
 
-  return (Fmarker_position (OVERLAY_END (overlay)));
+  return make_fixnum (OVERLAY_END (overlay));
 }
 
 DEFUN ("overlay-buffer", Foverlay_buffer, Soverlay_buffer, 1, 1, 0,
@@ -4238,9 +3761,16 @@ DEFUN ("overlay-buffer", Foverlay_buffer, 
Soverlay_buffer, 1, 1, 0,
 Return nil if OVERLAY has been deleted.  */)
   (Lisp_Object overlay)
 {
+  Lisp_Object buffer;
+
   CHECK_OVERLAY (overlay);
 
-  return Fmarker_buffer (OVERLAY_START (overlay));
+  if (! OVERLAY_BUFFER (overlay))
+    return Qnil;
+
+  XSETBUFFER (buffer, OVERLAY_BUFFER (overlay));
+
+  return buffer;
 }
 
 DEFUN ("overlay-properties", Foverlay_properties, Soverlay_properties, 1, 1, 0,
@@ -4251,7 +3781,7 @@ OVERLAY.  */)
 {
   CHECK_OVERLAY (overlay);
 
-  return Fcopy_sequence (XOVERLAY (overlay)->plist);
+  return Fcopy_sequence (OVERLAY_PLIST (overlay));
 }
 
 
@@ -4279,8 +3809,7 @@ interest.  */)
 
   /* Put all the overlays we want in a vector in overlay_vec.
      Store the length in len.  */
-  noverlays = overlays_at (XFIXNUM (pos), 1, &overlay_vec, &len,
-                          NULL, NULL, 0);
+  noverlays = overlays_at (XFIXNUM (pos), true, &overlay_vec, &len, NULL);
 
   if (!NILP (sorted))
     noverlays = sort_overlays (overlay_vec, noverlays,
@@ -4306,7 +3835,9 @@ and also contained within the specified region.
 
 Empty overlays are included in the result if they are located at BEG,
 between BEG and END, or at END provided END denotes the position at the
-end of the accessible part of the buffer.  */)
+end of the accessible part of the buffer.
+
+The resulting list of overlays is in an arbitrary unpredictable order.  */)
   (Lisp_Object beg, Lisp_Object end)
 {
   ptrdiff_t len, noverlays;
@@ -4325,7 +3856,7 @@ end of the accessible part of the buffer.  */)
   /* Put all the overlays we want in a vector in overlay_vec.
      Store the length in len.  */
   noverlays = overlays_in (XFIXNUM (beg), XFIXNUM (end), 1, &overlay_vec, &len,
-                          NULL, NULL);
+                           true, false, NULL);
 
   /* Make a list of them all.  */
   result = Flist (noverlays, overlay_vec);
@@ -4341,39 +3872,12 @@ If there are no overlay boundaries from POS to 
(point-max),
 the value is (point-max).  */)
   (Lisp_Object pos)
 {
-  ptrdiff_t i, len, noverlays;
-  ptrdiff_t endpos;
-  Lisp_Object *overlay_vec;
-
   CHECK_FIXNUM_COERCE_MARKER (pos);
 
   if (!buffer_has_overlays ())
     return make_fixnum (ZV);
 
-  len = 10;
-  overlay_vec = xmalloc (len * sizeof *overlay_vec);
-
-  /* Put all the overlays we want in a vector in overlay_vec.
-     Store the length in len.
-     endpos gets the position where the next overlay starts.  */
-  noverlays = overlays_at (XFIXNUM (pos), 1, &overlay_vec, &len,
-                          &endpos, 0, 1);
-
-  /* If any of these overlays ends before endpos,
-     use its ending point instead.  */
-  for (i = 0; i < noverlays; i++)
-    {
-      Lisp_Object oend;
-      ptrdiff_t oendpos;
-
-      oend = OVERLAY_END (overlay_vec[i]);
-      oendpos = OVERLAY_POSITION (oend);
-      if (oendpos < endpos)
-       endpos = oendpos;
-    }
-
-  xfree (overlay_vec);
-  return make_fixnum (endpos);
+  return make_fixnum (next_overlay_change (XFIXNUM (pos)));
 }
 
 DEFUN ("previous-overlay-change", Fprevious_overlay_change,
@@ -4383,32 +3887,15 @@ If there are no overlay boundaries from (point-min) to 
POS,
 the value is (point-min).  */)
   (Lisp_Object pos)
 {
-  ptrdiff_t prevpos;
-  Lisp_Object *overlay_vec;
-  ptrdiff_t len;
 
   CHECK_FIXNUM_COERCE_MARKER (pos);
 
   if (!buffer_has_overlays ())
     return make_fixnum (BEGV);
 
-  /* At beginning of buffer, we know the answer;
-     avoid bug subtracting 1 below.  */
-  if (XFIXNUM (pos) == BEGV)
-    return pos;
-
-  len = 10;
-  overlay_vec = xmalloc (len * sizeof *overlay_vec);
-
-  /* Put all the overlays we want in a vector in overlay_vec.
-     Store the length in len.
-     prevpos gets the position of the previous change.  */
-  overlays_at (XFIXNUM (pos), 1, &overlay_vec, &len,
-              0, &prevpos, 1);
-
-  xfree (overlay_vec);
-  return make_fixnum (prevpos);
+  return make_fixnum (previous_overlay_change (XFIXNUM (pos)));
 }
+
 
 /* These functions are for debugging overlays.  */
 
@@ -4418,19 +3905,16 @@ The car has all the overlays before the overlay center;
 the cdr has all the overlays after the overlay center.
 Recentering overlays moves overlays between these lists.
 The lists you get are copies, so that changing them has no effect.
-However, the overlays you get are the real objects that the buffer uses.  */)
+However, the overlays you get are the real objects that the buffer uses. */)
   (void)
 {
-  Lisp_Object before = Qnil, after = Qnil;
+  Lisp_Object overlays = Qnil;
+  struct itree_node *node;
 
-  for (struct Lisp_Overlay *ol = current_buffer->overlays_before;
-       ol; ol = ol->next)
-    before = Fcons (make_lisp_ptr (ol, Lisp_Vectorlike), before);
-  for (struct Lisp_Overlay *ol = current_buffer->overlays_after;
-       ol; ol = ol->next)
-    after = Fcons (make_lisp_ptr (ol, Lisp_Vectorlike), after);
+  ITREE_FOREACH (node, current_buffer->overlays, BEG, Z, DESCENDING)
+    overlays = Fcons (node->data, overlays);
 
-  return Fcons (Fnreverse (before), Fnreverse (after));
+  return Fcons (overlays, Qnil);
 }
 
 DEFUN ("overlay-recenter", Foverlay_recenter, Soverlay_recenter, 1, 1, 0,
@@ -4439,11 +3923,8 @@ That makes overlay lookup faster for positions near POS 
(but perhaps slower
 for positions far away from POS).  */)
   (Lisp_Object pos)
 {
-  ptrdiff_t p;
   CHECK_FIXNUM_COERCE_MARKER (pos);
-
-  p = clip_to_bounds (PTRDIFF_MIN, XFIXNUM (pos), PTRDIFF_MAX);
-  recenter_overlay_lists (current_buffer, p);
+  /* Noop */
   return Qnil;
 }
 
@@ -4460,12 +3941,13 @@ DEFUN ("overlay-put", Foverlay_put, Soverlay_put, 3, 3, 
0,
 VALUE will be returned.*/)
   (Lisp_Object overlay, Lisp_Object prop, Lisp_Object value)
 {
-  Lisp_Object tail, buffer;
+  Lisp_Object tail;
+  struct buffer *b;
   bool changed;
 
   CHECK_OVERLAY (overlay);
 
-  buffer = Fmarker_buffer (OVERLAY_START (overlay));
+  b = OVERLAY_BUFFER (overlay);
 
   for (tail = XOVERLAY (overlay)->plist;
        CONSP (tail) && CONSP (XCDR (tail));
@@ -4481,15 +3963,14 @@ VALUE will be returned.*/)
   set_overlay_plist
     (overlay, Fcons (prop, Fcons (value, XOVERLAY (overlay)->plist)));
  found:
-  if (! NILP (buffer))
+  if (b)
     {
       if (changed)
-       modify_overlay (XBUFFER (buffer),
-                       marker_position (OVERLAY_START (overlay)),
-                       marker_position (OVERLAY_END   (overlay)));
+       modify_overlay (b, OVERLAY_START (overlay),
+                        OVERLAY_END (overlay));
       if (EQ (prop, Qevaporate) && ! NILP (value)
-         && (OVERLAY_POSITION (OVERLAY_START (overlay))
-             == OVERLAY_POSITION (OVERLAY_END (overlay))))
+         && (OVERLAY_START (overlay)
+              == OVERLAY_END (overlay)))
        Fdelete_overlay (overlay);
     }
 
@@ -4560,70 +4041,33 @@ report_overlay_modification (Lisp_Object start, 
Lisp_Object end, bool after,
 
   if (!after)
     {
+      struct itree_node *node;
+      EMACS_INT begin_arg = XFIXNUM (start);
+      EMACS_INT end_arg = XFIXNUM (end);
       /* We are being called before a change.
         Scan the overlays to find the functions to call.  */
       last_overlay_modification_hooks_used = 0;
-      for (struct Lisp_Overlay *tail = current_buffer->overlays_before;
-          tail; tail = tail->next)
-       {
-         ptrdiff_t startpos, endpos;
-         Lisp_Object ostart, oend;
-
-         Lisp_Object overlay = make_lisp_ptr (tail, Lisp_Vectorlike);
-
-         ostart = OVERLAY_START (overlay);
-         oend = OVERLAY_END (overlay);
-         endpos = OVERLAY_POSITION (oend);
-         if (XFIXNAT (start) > endpos)
-           break;
-         startpos = OVERLAY_POSITION (ostart);
-         if (insertion && (XFIXNAT (start) == startpos
-                           || XFIXNAT (end) == startpos))
-           {
-             Lisp_Object prop = Foverlay_get (overlay, Qinsert_in_front_hooks);
-             if (!NILP (prop))
-               add_overlay_mod_hooklist (prop, overlay);
-           }
-         if (insertion && (XFIXNAT (start) == endpos
-                           || XFIXNAT (end) == endpos))
-           {
-             Lisp_Object prop = Foverlay_get (overlay, Qinsert_behind_hooks);
-             if (!NILP (prop))
-               add_overlay_mod_hooklist (prop, overlay);
-           }
-         /* Test for intersecting intervals.  This does the right thing
-            for both insertion and deletion.  */
-         if (XFIXNAT (end) > startpos && XFIXNAT (start) < endpos)
-           {
-             Lisp_Object prop = Foverlay_get (overlay, Qmodification_hooks);
-             if (!NILP (prop))
-               add_overlay_mod_hooklist (prop, overlay);
-           }
-       }
 
-      for (struct Lisp_Overlay *tail = current_buffer->overlays_after;
-          tail; tail = tail->next)
+      if (! current_buffer->overlays)
+        return;
+      ITREE_FOREACH (node, current_buffer->overlays,
+                     begin_arg - (insertion ? 1 : 0),
+                     end_arg   + (insertion ? 1 : 0),
+                     ASCENDING)
        {
-         ptrdiff_t startpos, endpos;
-         Lisp_Object ostart, oend;
-
-         Lisp_Object overlay = make_lisp_ptr (tail, Lisp_Vectorlike);
-
-         ostart = OVERLAY_START (overlay);
-         oend = OVERLAY_END (overlay);
-         startpos = OVERLAY_POSITION (ostart);
-         endpos = OVERLAY_POSITION (oend);
-         if (XFIXNAT (end) < startpos)
-           break;
-         if (insertion && (XFIXNAT (start) == startpos
-                           || XFIXNAT (end) == startpos))
+          Lisp_Object overlay = node->data;
+         ptrdiff_t obegin = OVERLAY_START (overlay);
+         ptrdiff_t oend = OVERLAY_END (overlay);
+
+         if (insertion && (begin_arg == obegin
+                           || end_arg == obegin))
            {
              Lisp_Object prop = Foverlay_get (overlay, Qinsert_in_front_hooks);
              if (!NILP (prop))
                add_overlay_mod_hooklist (prop, overlay);
            }
-         if (insertion && (XFIXNAT (start) == endpos
-                           || XFIXNAT (end) == endpos))
+         if (insertion && (begin_arg == oend
+                           || end_arg == oend))
            {
              Lisp_Object prop = Foverlay_get (overlay, Qinsert_behind_hooks);
              if (!NILP (prop))
@@ -4631,7 +4075,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 (XFIXNAT (end) > startpos && XFIXNAT (start) < endpos)
+         if (! insertion || (end_arg > obegin && begin_arg < oend))
            {
              Lisp_Object prop = Foverlay_get (overlay, Qmodification_hooks);
              if (!NILP (prop))
@@ -4639,7 +4083,6 @@ report_overlay_modification (Lisp_Object start, 
Lisp_Object end, bool after,
            }
        }
     }
-
   {
     /* Call the functions recorded in last_overlay_modification_hooks.
        First copy the vector contents, in case some of these hooks
@@ -4662,7 +4105,7 @@ report_overlay_modification (Lisp_Object start, 
Lisp_Object end, bool after,
           (which makes its markers' buffers be nil), or that (due to
           some bug) it belongs to a different buffer.  Only run this
           hook if the overlay belongs to the current buffer.  */
-       if (XMARKER (OVERLAY_START (overlay_i))->buffer == current_buffer)
+       if (OVERLAY_BUFFER (overlay_i) == current_buffer)
          call_overlay_mod_hooks (prop_i, overlay_i, after, arg1, arg2, arg3);
       }
 
@@ -4684,40 +4127,6 @@ call_overlay_mod_hooks (Lisp_Object list, Lisp_Object 
overlay, bool after,
     }
 }
 
-/* Delete any zero-sized overlays at position POS, if the `evaporate'
-   property is set.  */
-void
-evaporate_overlays (ptrdiff_t pos)
-{
-  Lisp_Object hit_list = Qnil;
-  if (pos <= current_buffer->overlay_center)
-    for (struct Lisp_Overlay *tail = current_buffer->overlays_before;
-        tail; tail = tail->next)
-      {
-       Lisp_Object overlay = make_lisp_ptr (tail, Lisp_Vectorlike);
-       ptrdiff_t endpos = OVERLAY_POSITION (OVERLAY_END (overlay));
-       if (endpos < pos)
-         break;
-       if (endpos == pos && OVERLAY_POSITION (OVERLAY_START (overlay)) == pos
-           && ! NILP (Foverlay_get (overlay, Qevaporate)))
-         hit_list = Fcons (overlay, hit_list);
-      }
-  else
-    for (struct Lisp_Overlay *tail = current_buffer->overlays_after;
-        tail; tail = tail->next)
-      {
-       Lisp_Object overlay = make_lisp_ptr (tail, Lisp_Vectorlike);
-       ptrdiff_t startpos = OVERLAY_POSITION (OVERLAY_START (overlay));
-       if (startpos > pos)
-         break;
-       if (startpos == pos && OVERLAY_POSITION (OVERLAY_END (overlay)) == pos
-           && ! NILP (Foverlay_get (overlay, Qevaporate)))
-         hit_list = Fcons (overlay, hit_list);
-      }
-  for (; CONSP (hit_list); hit_list = XCDR (hit_list))
-    Fdelete_overlay (XCAR (hit_list));
-}
-
 /***********************************************************************
                         Allocation with mmap
  ***********************************************************************/
@@ -5339,9 +4748,7 @@ init_buffer_once (void)
   bset_mark_active (&buffer_defaults, Qnil);
   bset_file_format (&buffer_defaults, Qnil);
   bset_auto_save_file_format (&buffer_defaults, Qt);
-  set_buffer_overlays_before (&buffer_defaults, NULL);
-  set_buffer_overlays_after (&buffer_defaults, NULL);
-  buffer_defaults.overlay_center = BEG;
+  buffer_defaults.overlays = NULL;
 
   XSETFASTINT (BVAR (&buffer_defaults, tab_width), 8);
   bset_truncate_lines (&buffer_defaults, Qnil);
@@ -5546,6 +4953,48 @@ defvar_per_buffer (struct Lisp_Buffer_Objfwd *bo_fwd, 
const char *namestring,
     emacs_abort ();
 }
 
+#ifdef ITREE_DEBUG
+static Lisp_Object
+make_lispy_itree_node (const struct itree_node *node)
+{
+  return listn (12,
+                intern (":begin"),
+                make_fixnum (node->begin),
+                intern (":end"),
+                make_fixnum (node->end),
+                intern (":limit"),
+                make_fixnum (node->limit),
+                intern (":offset"),
+                make_fixnum (node->offset),
+                intern (":rear-advance"),
+                node->rear_advance ? Qt : Qnil,
+                intern (":front-advance"),
+                node->front_advance ? Qt : Qnil);
+}
+
+static Lisp_Object
+overlay_tree (const struct itree_tree *tree,
+              const struct itree_node *node)
+{
+  if (node == ITREE_NULL)
+    return Qnil;
+  return list3 (make_lispy_itree_node (node),
+                overlay_tree (tree, node->left),
+                overlay_tree (tree, node->right));
+}
+
+DEFUN ("overlay-tree", Foverlay_tree, Soverlay_tree, 0, 1, 0,
+       doc: /* Get the overlay tree for BUFFER.  */)
+     (Lisp_Object buffer)
+{
+  struct buffer *b = decode_buffer (buffer);
+  if (! b->overlays)
+    return Qnil;
+  return overlay_tree (b->overlays, b->overlays->root);
+}
+#endif
+
+
 
 /* Initialize the buffer routines.  */
 void
@@ -6517,6 +5966,10 @@ There is no reason to change that value except for 
debugging purposes.  */);
 
   DEFSYM (Qautosaved, "autosaved");
 
+#ifdef ITREE_DEBUG
+  defsubr (&Soverlay_tree);
+#endif
+
   DEFSYM (Qkill_buffer__possibly_save, "kill-buffer--possibly-save");
 
   DEFSYM (Qbuffer_stale_function, "buffer-stale-function");
diff --git a/src/buffer.h b/src/buffer.h
index cbdbae798b..2e80c8a7b0 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -1,7 +1,6 @@
 /* Header file for the buffer manipulation primitives.
 
-Copyright (C) 1985-1986, 1993-1995, 1997-2022 Free Software Foundation,
-Inc.
+Copyright (C) 1985-2022  Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -26,6 +25,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 
 #include "character.h"
 #include "lisp.h"
+#include "itree.h"
 
 INLINE_HEADER_BEGIN
 
@@ -697,16 +697,8 @@ struct buffer
      display optimizations must be used.  */
   bool_bf long_line_optimizations_p : 1;
 
-  /* List of overlays that end at or before the current center,
-     in order of end-position.  */
-  struct Lisp_Overlay *overlays_before;
-
-  /* List of overlays that end after  the current center,
-     in order of start-position.  */
-  struct Lisp_Overlay *overlays_after;
-
-  /* Position where the overlay lists are centered.  */
-  ptrdiff_t overlay_center;
+  /* The inveral tree containing this buffer's overlays. */
+  struct itree_tree *overlays;
 
   /* Changes in the buffer are recorded here for undo, and t means
      don't record anything.  This information belongs to the base
@@ -716,6 +708,14 @@ struct buffer
   Lisp_Object undo_list_;
 };
 
+struct sortvec
+{
+  Lisp_Object overlay;
+  ptrdiff_t beg, end;
+  EMACS_INT priority;
+  EMACS_INT spriority;         /* Secondary priority.  */
+};
+
 INLINE bool
 BUFFERP (Lisp_Object a)
 {
@@ -1170,9 +1170,11 @@ extern EMACS_INT fix_position (Lisp_Object);
 extern void delete_all_overlays (struct buffer *);
 extern void reset_buffer (struct buffer *);
 extern void compact_buffer (struct buffer *);
-extern void evaporate_overlays (ptrdiff_t);
-extern ptrdiff_t overlays_at (EMACS_INT, bool, Lisp_Object **,
-                             ptrdiff_t *, ptrdiff_t *, ptrdiff_t *, bool);
+extern ptrdiff_t overlays_at (ptrdiff_t, bool, Lisp_Object **, ptrdiff_t *, 
ptrdiff_t *);
+extern ptrdiff_t overlays_in (ptrdiff_t, ptrdiff_t, bool, Lisp_Object **,
+                              ptrdiff_t *,  bool, bool, ptrdiff_t *);
+extern ptrdiff_t previous_overlay_change (ptrdiff_t);
+extern ptrdiff_t next_overlay_change (ptrdiff_t);
 extern ptrdiff_t sort_overlays (Lisp_Object *, ptrdiff_t, struct window *);
 extern void recenter_overlay_lists (struct buffer *, ptrdiff_t);
 extern ptrdiff_t overlay_strings (ptrdiff_t, struct window *, unsigned char 
**);
@@ -1186,6 +1188,7 @@ extern void fix_overlays_before (struct buffer *, 
ptrdiff_t, ptrdiff_t);
 extern void mmap_set_vars (bool);
 extern void restore_buffer (Lisp_Object);
 extern void set_buffer_if_live (Lisp_Object);
+extern Lisp_Object build_overlay (bool, bool, Lisp_Object);
 
 /* Return B as a struct buffer pointer, defaulting to the current buffer.  */
 
@@ -1226,18 +1229,16 @@ record_unwind_current_buffer (void)
    This macro might evaluate its args multiple times,
    and it treat some args as lvalues.  */
 
-#define GET_OVERLAYS_AT(posn, overlays, noverlays, nextp, chrq)                
\
+#define GET_OVERLAYS_AT(posn, overlays, noverlays, next)                \
   do {                                                                 \
     ptrdiff_t maxlen = 40;                                             \
     SAFE_NALLOCA (overlays, 1, maxlen);                                        
\
-    (noverlays) = overlays_at (posn, false, &(overlays), &maxlen,      \
-                              nextp, NULL, chrq);                      \
+    (noverlays) = overlays_at (posn, false, &(overlays), &maxlen, next); \
     if ((noverlays) > maxlen)                                          \
       {                                                                        
\
        maxlen = noverlays;                                             \
        SAFE_NALLOCA (overlays, 1, maxlen);                             \
-       (noverlays) = overlays_at (posn, false, &(overlays), &maxlen,   \
-                                  nextp, NULL, chrq);                  \
+       (noverlays) = overlays_at (posn, false, &(overlays), &maxlen, next); \
       }                                                                        
\
   } while (false)
 
@@ -1272,7 +1273,8 @@ set_buffer_intervals (struct buffer *b, INTERVAL i)
 INLINE bool
 buffer_has_overlays (void)
 {
-  return current_buffer->overlays_before || current_buffer->overlays_after;
+  return current_buffer->overlays
+         && (current_buffer->overlays->root != NULL);
 }
 
 /* Functions for accessing a character or byte,
@@ -1390,25 +1392,69 @@ buffer_window_count (struct buffer *b)
 
 /* Overlays */
 
-/* Return the marker that stands for where OV starts in the buffer.  */
+INLINE ptrdiff_t
+overlay_start (struct Lisp_Overlay *ov)
+{
+  if (! ov->buffer)
+    return -1;
+  return itree_node_begin (ov->buffer->overlays, ov->interval);
+}
+
+INLINE ptrdiff_t
+overlay_end (struct Lisp_Overlay *ov)
+{
+  if (! ov->buffer)
+    return -1;
+  return itree_node_end (ov->buffer->overlays, ov->interval);
+}
 
-#define OVERLAY_START(OV) XOVERLAY (OV)->start
+/* Return the start of OV in its buffer, or -1 if OV is not associated
+   with any buffer.  */
 
-/* Return the marker that stands for where OV ends in the buffer.  */
+INLINE ptrdiff_t
+OVERLAY_START (Lisp_Object ov)
+{
+  return overlay_start (XOVERLAY (ov));
+}
 
-#define OVERLAY_END(OV) XOVERLAY (OV)->end
+/* Return the end of OV in its buffer, or -1. */
+
+INLINE ptrdiff_t
+OVERLAY_END (Lisp_Object ov)
+{
+  return overlay_end (XOVERLAY (ov));
+}
 
 /* Return the plist of overlay OV.  */
 
-#define OVERLAY_PLIST(OV) XOVERLAY (OV)->plist
+INLINE Lisp_Object
+OVERLAY_PLIST (Lisp_Object ov)
+{
+  return XOVERLAY (ov)->plist;
+}
 
-/* Return the actual buffer position for the marker P.
-   We assume you know which buffer it's pointing into.  */
+/* Return the buffer of overlay OV. */
 
-INLINE ptrdiff_t
-OVERLAY_POSITION (Lisp_Object p)
+INLINE struct buffer *
+OVERLAY_BUFFER (Lisp_Object ov)
 {
-  return marker_position (p);
+  return XOVERLAY (ov)->buffer;
+}
+
+/* Return true, if OV's rear-advance is set. */
+
+INLINE bool
+OVERLAY_REAR_ADVANCE_P (Lisp_Object ov)
+{
+  return XOVERLAY (ov)->interval->rear_advance;
+}
+
+/* Return true, if OV's front-advance is set. */
+
+INLINE bool
+OVERLAY_FRONT_ADVANCE_P (Lisp_Object ov)
+{
+  return XOVERLAY (ov)->interval->front_advance;
 }
 
 
@@ -1692,4 +1738,7 @@ dec_both (ptrdiff_t *charpos, ptrdiff_t *bytepos)
 
 INLINE_HEADER_END
 
+int compare_overlays (const void *v1, const void *v2);
+void make_sortvec_item (struct sortvec *item, Lisp_Object overlay);
+
 #endif /* EMACS_BUFFER_H */
diff --git a/src/callproc.c b/src/callproc.c
index 2d457b3c84..f9f840e544 100644
--- a/src/callproc.c
+++ b/src/callproc.c
@@ -39,7 +39,10 @@ extern char **environ;
   && (defined HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR        \
       || defined HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR_NP) \
   && defined HAVE_DECL_POSIX_SPAWN_SETSID                   \
-  && HAVE_DECL_POSIX_SPAWN_SETSID == 1
+  && HAVE_DECL_POSIX_SPAWN_SETSID == 1                     \
+  /* posix_spawnattr_setflags rejects POSIX_SPAWN_SETSID on \
+     Haiku */                                              \
+  && !defined HAIKU
 # include <spawn.h>
 # define USABLE_POSIX_SPAWN 1
 #else
@@ -645,6 +648,7 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int 
filefd,
 
 #ifndef MSDOS
 
+  child_signal_init ();
   block_input ();
   block_child_signal (&oldset);
 
@@ -1306,29 +1310,29 @@ emacs_posix_spawn_init_actions 
(posix_spawn_file_actions_t *actions,
     return error;
 
   error = posix_spawn_file_actions_adddup2 (actions, std_in,
-                                            STDIN_FILENO);
+                                           STDIN_FILENO);
   if (error != 0)
     goto out;
 
   error = posix_spawn_file_actions_adddup2 (actions, std_out,
-                                            STDOUT_FILENO);
+                                           STDOUT_FILENO);
   if (error != 0)
     goto out;
 
   error = posix_spawn_file_actions_adddup2 (actions,
-                                            std_err < 0 ? std_out
-                                                        : std_err,
-                                            STDERR_FILENO);
+                                           std_err < 0 ? std_out
+                                                       : std_err,
+                                           STDERR_FILENO);
   if (error != 0)
     goto out;
 
-  error =
-#ifdef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR
-    posix_spawn_file_actions_addchdir
+  /* Haiku appears to have linkable posix_spawn_file_actions_chdir,
+     but it always fails.  So use the _np function instead.  */
+#if defined HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR && !defined HAIKU
+  error = posix_spawn_file_actions_addchdir (actions, cwd);
 #else
-    posix_spawn_file_actions_addchdir_np
+  error = posix_spawn_file_actions_addchdir_np (actions, cwd);
 #endif
-    (actions, cwd);
   if (error != 0)
     goto out;
 
@@ -1347,9 +1351,9 @@ emacs_posix_spawn_init_attributes (posix_spawnattr_t 
*attributes,
     return error;
 
   error = posix_spawnattr_setflags (attributes,
-                                    POSIX_SPAWN_SETSID
-                                      | POSIX_SPAWN_SETSIGDEF
-                                      | POSIX_SPAWN_SETSIGMASK);
+                                   POSIX_SPAWN_SETSID
+                                   | POSIX_SPAWN_SETSIGDEF
+                                   | POSIX_SPAWN_SETSIGMASK);
   if (error != 0)
     goto out;
 
diff --git a/src/comp.c b/src/comp.c
index c91236f333..e878f43320 100644
--- a/src/comp.c
+++ b/src/comp.c
@@ -8364,8 +8364,21 @@ The last directory of this list is assumed to be the 
system one.  */);
   Vnative_comp_eln_load_path = Fcons (build_string ("../native-lisp/"), Qnil);
 
   DEFVAR_BOOL ("comp-enable-subr-trampolines", comp_enable_subr_trampolines,
-              doc: /* If non-nil enable primitive trampoline synthesis.
-This makes primitive functions redefinable or advisable effectively.  */);
+              doc: /* If non-nil, enable primitive trampoline synthesis.
+This makes Emacs respect redefinition or advises of primitive functions
+when they are called from Lisp code natively-compiled at `native-comp-speed'
+of 2.
+
+By default, this is enabled, and when Emacs sees a redefined or advised
+primitive called from natively-compiled Lisp, it generates a trampoline
+for it on-the-fly.
+
+Disabling this, when a trampoline for a redefined or advised primitive is
+not available from previous compilations, means that such redefinition
+or advise will not have effect on calls from natively-compiled Lisp code.
+That is, calls to primitives without existing trampolines from
+natively-compiled Lisp will behave as if the primitive was called
+directly from C.  */);
 
   DEFVAR_LISP ("comp-installed-trampolines-h", Vcomp_installed_trampolines_h,
               doc: /* Hash table subr-name -> installed trampoline.
diff --git a/src/dired.c b/src/dired.c
index 1b4edf2048..ef729df5d2 100644
--- a/src/dired.c
+++ b/src/dired.c
@@ -928,7 +928,7 @@ Elements of the attribute list are:
 Large integers are bignums, so `eq' might not work on them.
 On most filesystems, the combination of the inode and the device
 identifier uniquely identifies the file.  This unique file identification
-is provided by the access function `file-attribute-file-number'.
+is provided by the access function `file-attribute-file-identifier'.
 
 On MS-Windows, performance depends on `w32-get-true-file-attributes',
 which see.
diff --git a/src/dispnew.c b/src/dispnew.c
index 2568ba1086..5a9ba8909e 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -3152,10 +3152,19 @@ redraw_frame (struct frame *f)
   update_begin (f);
   if (FRAME_MSDOS_P (f))
     FRAME_TERMINAL (f)->set_terminal_modes_hook (FRAME_TERMINAL (f));
+
+  if (FRAME_WINDOW_P (f))
+    /* Garbage the frame now.  Otherwise, platforms that support
+       double buffering will display the blank contents of the frame
+       even though the frame should be redrawn at some point in the
+       future.  */
+    SET_FRAME_GARBAGED (f);
+
   clear_frame (f);
   clear_current_matrices (f);
   update_end (f);
   fset_redisplay (f);
+
   /* Mark all windows as inaccurate, so that every window will have
      its redisplay done.  */
   mark_window_display_accurate (FRAME_ROOT_WINDOW (f), 0);
diff --git a/src/editfns.c b/src/editfns.c
index 3f9618edb0..17dca4708e 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -1,6 +1,6 @@
 /* Lisp functions pertaining to editing.                 -*- coding: utf-8 -*-
 
-Copyright (C) 1985-1987, 1989, 1993-2022 Free Software Foundation, Inc.
+Copyright (C) 1985-2022  Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -265,51 +265,20 @@ If you set the marker not to point anywhere, the buffer 
will have no mark.  */)
 
 /* Find all the overlays in the current buffer that touch position POS.
    Return the number found, and store them in a vector in VEC
-   of length LEN.  */
+   of length LEN.
+
+   Note: this can return overlays that do not touch POS.  The caller
+   should filter these out. */
 
 static ptrdiff_t
-overlays_around (EMACS_INT pos, Lisp_Object *vec, ptrdiff_t len)
+overlays_around (ptrdiff_t pos, Lisp_Object *vec, ptrdiff_t len)
 {
-  ptrdiff_t idx = 0;
-
-  for (struct Lisp_Overlay *tail = current_buffer->overlays_before;
-       tail; tail = tail->next)
-    {
-      Lisp_Object overlay = make_lisp_ptr (tail, Lisp_Vectorlike);
-      Lisp_Object end = OVERLAY_END (overlay);
-      ptrdiff_t endpos = OVERLAY_POSITION (end);
-      if (endpos < pos)
-         break;
-      Lisp_Object start = OVERLAY_START (overlay);
-      ptrdiff_t startpos = OVERLAY_POSITION (start);
-      if (startpos <= pos)
-       {
-         if (idx < len)
-           vec[idx] = overlay;
-         /* Keep counting overlays even if we can't return them all.  */
-         idx++;
-       }
-    }
-
-  for (struct Lisp_Overlay *tail = current_buffer->overlays_after;
-       tail; tail = tail->next)
-    {
-      Lisp_Object overlay = make_lisp_ptr (tail, Lisp_Vectorlike);
-      Lisp_Object start = OVERLAY_START (overlay);
-      ptrdiff_t startpos = OVERLAY_POSITION (start);
-      if (pos < startpos)
-       break;
-      Lisp_Object end = OVERLAY_END (overlay);
-      ptrdiff_t endpos = OVERLAY_POSITION (end);
-      if (pos <= endpos)
-       {
-         if (idx < len)
-           vec[idx] = overlay;
-         idx++;
-       }
-    }
-
-  return idx;
+  /* Find all potentially rear-advance overlays at (POS - 1).  Find
+     all overlays at POS, so end at (POS + 1).  Find even empty
+     overlays, which due to the way 'overlays-in' works implies that
+     we might also fetch empty overlays starting at (POS + 1).  */
+  return overlays_in (pos - 1, pos + 1, false, &vec, &len,
+                     true, false, NULL);
 }
 
 DEFUN ("get-pos-property", Fget_pos_property, Sget_pos_property, 2, 3, 0,
@@ -369,11 +338,12 @@ at POSITION.  */)
          if (!NILP (tem))
            {
              /* Check the overlay is indeed active at point.  */
-             Lisp_Object start = OVERLAY_START (ol), finish = OVERLAY_END (ol);
-             if ((OVERLAY_POSITION (start) == posn
-                  && XMARKER (start)->insertion_type == 1)
-                 || (OVERLAY_POSITION (finish) == posn
-                     && XMARKER (finish)->insertion_type == 0))
+             if ((OVERLAY_START (ol) == posn
+                  && OVERLAY_FRONT_ADVANCE_P (ol))
+                 || (OVERLAY_END (ol) == posn
+                     && ! OVERLAY_REAR_ADVANCE_P (ol))
+                 || OVERLAY_START (ol) > posn
+                 || OVERLAY_END (ol) < posn)
                ; /* The overlay will not cover a char inserted at point.  */
              else
                {
@@ -4526,7 +4496,6 @@ ring.  */)
       transpose_markers (start1, end1, start2, end2,
                         start1_byte, start1_byte + len1_byte,
                         start2_byte, start2_byte + len2_byte);
-      fix_start_end_in_overlays (start1, end2);
     }
   else
     {
diff --git a/src/emacs-module.c b/src/emacs-module.c
index fcdf103c19..35d6e9e0d7 100644
--- a/src/emacs-module.c
+++ b/src/emacs-module.c
@@ -561,7 +561,7 @@ static struct Lisp_Module_Function *
 allocate_module_function (void)
 {
   return ALLOCATE_PSEUDOVECTOR (struct Lisp_Module_Function,
-                                interactive_form, PVEC_MODULE_FUNCTION);
+                                command_modes, PVEC_MODULE_FUNCTION);
 }
 
 #define XSET_MODULE_FUNCTION(var, ptr) \
diff --git a/src/emacs.c b/src/emacs.c
index 43e81b912c..c4c8bfc82f 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -82,6 +82,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #endif /* HAVE_WINDOW_SYSTEM */
 
 #include "bignum.h"
+#include "itree.h"
 #include "intervals.h"
 #include "character.h"
 #include "buffer.h"
@@ -431,9 +432,9 @@ terminate_due_to_signal (int sig, int backtrace_limit)
           if (sig == SIGTERM || sig == SIGHUP || sig == SIGINT)
            {
              /* Avoid abort in shut_down_emacs if we were interrupted
-                by SIGINT in noninteractive usage, as in that case we
-                don't care about the message stack.  */
-             if (sig == SIGINT && noninteractive)
+                in noninteractive usage, as in that case we don't
+                care about the message stack.  */
+             if (noninteractive)
                clear_message_stack ();
              Fkill_emacs (make_fixnum (sig), Qnil);
            }
@@ -1931,6 +1932,7 @@ Using an Emacs configured with --with-x-toolkit=lucid 
does not have this problem
   running_asynch_code = 0;
   init_random ();
   init_xfaces ();
+  init_itree ();
 
 #if defined HAVE_JSON && !defined WINDOWSNT
   init_json ();
@@ -2564,6 +2566,7 @@ static const struct standard_args standard_args[] =
   { "-init-directory", "--init-directory", 30, 1 },
   { "-no-x-resources", "--no-x-resources", 40, 0 },
   { "-no-site-file", "--no-site-file", 40, 0 },
+  { "-no-comp-spawn", "--no-comp-spawn", 60, 0 },
   { "-u", "--user", 30, 1 },
   { "-user", 0, 30, 1 },
   { "-debug-init", "--debug-init", 20, 0 },
@@ -3102,6 +3105,8 @@ You must run Emacs in batch mode in order to dump it.  */)
   gflags.will_dump_with_unexec_ = false;
   gflags.dumped_with_unexec_ = true;
 
+  forget_itree ();
+
   alloc_unexec_pre ();
 
   unexec (SSDATA (filename), !NILP (symfile) ? SSDATA (symfile) : 0);
diff --git a/src/eval.c b/src/eval.c
index 8810136c04..ea23829948 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -484,8 +484,7 @@ usage: (setq [SYM VAL]...)  */)
       /* Like for eval_sub, we do not check declared_special here since
         it's been done when let-binding.  */
       Lisp_Object lex_binding
-       = ((!NILP (Vinternal_interpreter_environment) /* Mere optimization!  */
-           && SYMBOLP (sym))
+       = (SYMBOLP (sym)
           ? Fassq (sym, Vinternal_interpreter_environment)
           : Qnil);
       if (!NILP (lex_binding))
@@ -551,8 +550,12 @@ usage: (function ARG)  */)
          CHECK_STRING (docstring);
          cdr = Fcons (XCAR (cdr), Fcons (docstring, XCDR (XCDR (cdr))));
        }
-      return Fcons (Qclosure, Fcons (Vinternal_interpreter_environment,
-                                    cdr));
+      if (NILP (Vinternal_make_interpreted_closure_function))
+        return Fcons (Qclosure, Fcons (Vinternal_interpreter_environment, 
cdr));
+      else
+        return call2 (Vinternal_make_interpreted_closure_function,
+                      Fcons (Qlambda, cdr),
+                      Vinternal_interpreter_environment);
     }
   else
     /* Simply quote the argument.  */
@@ -1713,6 +1716,7 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object 
data, bool keyboard_quit)
   Lisp_Object clause = Qnil;
   struct handler *h;
 
+  eassert (!itree_iterator_busy_p ());
   if (gc_in_progress || waiting_for_input)
     emacs_abort ();
 
@@ -2374,9 +2378,7 @@ eval_sub (Lisp_Object form)
         We do not pay attention to the declared_special flag here, since we
         already did that when let-binding the variable.  */
       Lisp_Object lex_binding
-       = (!NILP (Vinternal_interpreter_environment) /* Mere optimization!  */
-          ? Fassq (form, Vinternal_interpreter_environment)
-          : Qnil);
+       = Fassq (form, Vinternal_interpreter_environment);
       return !NILP (lex_binding) ? XCDR (lex_binding) : Fsymbol_value (form);
     }
 
@@ -2392,7 +2394,7 @@ eval_sub (Lisp_Object form)
       if (max_lisp_eval_depth < 100)
        max_lisp_eval_depth = 100;
       if (lisp_eval_depth > max_lisp_eval_depth)
-       xsignal0 (Qexcessive_lisp_nesting);
+       xsignal1 (Qexcessive_lisp_nesting, make_fixnum (lisp_eval_depth));
     }
 
   Lisp_Object original_fun = XCAR (form);
@@ -2433,7 +2435,9 @@ eval_sub (Lisp_Object form)
 
       else if (XSUBR (fun)->max_args == UNEVALLED)
        val = (XSUBR (fun)->function.aUNEVALLED) (args_left);
-      else if (XSUBR (fun)->max_args == MANY)
+      else if (XSUBR (fun)->max_args == MANY
+              || XSUBR (fun)->max_args > 8)
+
        {
          /* Pass a vector of evaluated arguments.  */
          Lisp_Object *vals;
@@ -2966,7 +2970,7 @@ usage: (funcall FUNCTION &rest ARGUMENTS)  */)
       if (max_lisp_eval_depth < 100)
        max_lisp_eval_depth = 100;
       if (lisp_eval_depth > max_lisp_eval_depth)
-       xsignal0 (Qexcessive_lisp_nesting);
+       xsignal1 (Qexcessive_lisp_nesting, make_fixnum (lisp_eval_depth));
     }
 
   count = record_in_backtrace (args[0], &args[1], nargs - 1);
@@ -2996,7 +3000,8 @@ funcall_subr (struct Lisp_Subr *subr, ptrdiff_t numargs, 
Lisp_Object *args)
   if (numargs >= subr->min_args)
     {
       /* Conforming call to finite-arity subr.  */
-      if (numargs <= subr->max_args)
+      if (numargs <= subr->max_args
+         && subr->max_args <= 8)
        {
          Lisp_Object argbuf[8];
          Lisp_Object *a;
@@ -3032,15 +3037,13 @@ funcall_subr (struct Lisp_Subr *subr, ptrdiff_t 
numargs, Lisp_Object *args)
              return subr->function.a8 (a[0], a[1], a[2], a[3], a[4], a[5],
                                        a[6], a[7]);
            default:
-             /* If a subr takes more than 8 arguments without using MANY
-                or UNEVALLED, we need to extend this function to support it.
-                Until this is done, there is no way to call the function.  */
-             emacs_abort ();
+             emacs_abort ();   /* Can't happen. */
            }
        }
 
       /* Call to n-adic subr.  */
-      if (subr->max_args == MANY)
+      if (subr->max_args == MANY
+         || subr->max_args > 8)
        return subr->function.aMANY (numargs, args);
     }
 
@@ -4357,6 +4360,11 @@ alist of active lexical bindings.  */);
      (Just imagine if someone makes it buffer-local).  */
   Funintern (Qinternal_interpreter_environment, Qnil);
 
+  DEFVAR_LISP ("internal-make-interpreted-closure-function",
+              Vinternal_make_interpreted_closure_function,
+              doc: /* Function to filter the env when constructing a closure.  
*/);
+  Vinternal_make_interpreted_closure_function = Qnil;
+
   Vrun_hooks = intern_c_string ("run-hooks");
   staticpro (&Vrun_hooks);
 
diff --git a/src/fileio.c b/src/fileio.c
index b9a57dd580..92335b639c 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -4167,8 +4167,7 @@ by calling `format-decode', which see.  */)
                  bset_read_only (buf, Qnil);
                  bset_filename (buf, Qnil);
                  bset_undo_list (buf, Qt);
-                 eassert (buf->overlays_before == NULL);
-                 eassert (buf->overlays_after == NULL);
+                 eassert (buf->overlays == NULL);
 
                  set_buffer_internal (buf);
                  Ferase_buffer ();
@@ -6363,7 +6362,7 @@ init_fileio (void)
      For more on why fsync does not suffice even if it works properly, see:
      Roche X. Necessary step(s) to synchronize filename operations on disk.
      Austin Group Defect 672, 2013-03-19
-     http://austingroupbugs.net/view.php?id=672  */
+     https://austingroupbugs.net/view.php?id=672  */
   write_region_inhibit_fsync = noninteractive;
 }
 
diff --git a/src/fns.c b/src/fns.c
index 4055792382..035fa12935 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -1,7 +1,6 @@
 /* Random utility Lisp functions.
 
-Copyright (C) 1985-1987, 1993-1995, 1997-2022 Free Software Foundation,
-Inc.
+Copyright (C) 1985-2022  Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -2473,15 +2472,15 @@ with PROP is done using PREDICATE, which defaults to 
`eq'.
 This function doesn't signal an error if PLIST is invalid.  */)
   (Lisp_Object plist, Lisp_Object prop, Lisp_Object predicate)
 {
-  Lisp_Object tail = plist;
   if (NILP (predicate))
     return plist_get (plist, prop);
 
+  Lisp_Object tail = plist;
   FOR_EACH_TAIL_SAFE (tail)
     {
       if (! CONSP (XCDR (tail)))
        break;
-      if (!NILP (call2 (predicate, prop, XCAR (tail))))
+      if (!NILP (call2 (predicate, XCAR (tail), prop)))
        return XCAR (XCDR (tail));
       tail = XCDR (tail);
     }
@@ -2489,7 +2488,7 @@ This function doesn't signal an error if PLIST is 
invalid.  */)
   return Qnil;
 }
 
-/* Faster version of the above that works with EQ only */
+/* Faster version of Fplist_get that works with EQ only.  */
 Lisp_Object
 plist_get (Lisp_Object plist, Lisp_Object prop)
 {
@@ -2498,7 +2497,7 @@ plist_get (Lisp_Object plist, Lisp_Object prop)
     {
       if (! CONSP (XCDR (tail)))
        break;
-      if (EQ (prop, XCAR (tail)))
+      if (EQ (XCAR (tail), prop))
        return XCAR (XCDR (tail));
       tail = XCDR (tail);
     }
@@ -2532,15 +2531,15 @@ use `(setq x (plist-put x prop val))' to be sure to use 
the new value.
 The PLIST is modified by side effects.  */)
   (Lisp_Object plist, Lisp_Object prop, Lisp_Object val, Lisp_Object predicate)
 {
-  Lisp_Object prev = Qnil, tail = plist;
   if (NILP (predicate))
     return plist_put (plist, prop, val);
+  Lisp_Object prev = Qnil, tail = plist;
   FOR_EACH_TAIL (tail)
     {
       if (! CONSP (XCDR (tail)))
        break;
 
-      if (!NILP (call2 (predicate, prop, XCAR (tail))))
+      if (!NILP (call2 (predicate, XCAR (tail), prop)))
        {
          Fsetcar (XCDR (tail), val);
          return plist;
@@ -2558,6 +2557,7 @@ The PLIST is modified by side effects.  */)
   return plist;
 }
 
+/* Faster version of Fplist_put that works with EQ only.  */
 Lisp_Object
 plist_put (Lisp_Object plist, Lisp_Object prop, Lisp_Object val)
 {
@@ -2567,7 +2567,7 @@ plist_put (Lisp_Object plist, Lisp_Object prop, 
Lisp_Object val)
       if (! CONSP (XCDR (tail)))
        break;
 
-      if (EQ (prop, XCAR (tail)))
+      if (EQ (XCAR (tail), prop))
        {
          Fsetcar (XCDR (tail), val);
          return plist;
@@ -2595,6 +2595,51 @@ It can be retrieved with `(get SYMBOL PROPNAME)'.  */)
     (symbol, plist_put (XSYMBOL (symbol)->u.s.plist, propname, value));
   return value;
 }
+
+DEFUN ("plist-member", Fplist_member, Splist_member, 2, 3, 0,
+       doc: /* Return non-nil if PLIST has the property PROP.
+PLIST is a property list, which is a list of the form
+\(PROP1 VALUE1 PROP2 VALUE2 ...).
+
+The comparison with PROP is done using PREDICATE, which defaults to
+`eq'.
+
+Unlike `plist-get', this allows you to distinguish between a missing
+property and a property with the value nil.
+The value is actually the tail of PLIST whose car is PROP.  */)
+  (Lisp_Object plist, Lisp_Object prop, Lisp_Object predicate)
+{
+  if (NILP (predicate))
+    return plist_member (plist, prop);
+  Lisp_Object tail = plist;
+  FOR_EACH_TAIL (tail)
+    {
+      if (!NILP (call2 (predicate, XCAR (tail), prop)))
+       return tail;
+      tail = XCDR (tail);
+      if (! CONSP (tail))
+       break;
+    }
+  CHECK_TYPE (NILP (tail), Qplistp, plist);
+  return Qnil;
+}
+
+/* Faster version of Fplist_member that works with EQ only.  */
+Lisp_Object
+plist_member (Lisp_Object plist, Lisp_Object prop)
+{
+  Lisp_Object tail = plist;
+  FOR_EACH_TAIL (tail)
+    {
+      if (EQ (XCAR (tail), prop))
+       return tail;
+      tail = XCDR (tail);
+      if (! CONSP (tail))
+       break;
+    }
+  CHECK_TYPE (NILP (tail), Qplistp, plist);
+  return Qnil;
+}
 
 DEFUN ("eql", Feql, Seql, 2, 2, 0,
        doc: /* Return t if the two args are `eq' or are indistinguishable 
numbers.
@@ -2751,10 +2796,9 @@ internal_equal (Lisp_Object o1, Lisp_Object o2, enum 
equal_kind equal_kind,
          return mpz_cmp (*xbignum_val (o1), *xbignum_val (o2)) == 0;
        if (OVERLAYP (o1))
          {
-           if (!internal_equal (OVERLAY_START (o1), OVERLAY_START (o2),
-                                equal_kind, depth + 1, ht)
-               || !internal_equal (OVERLAY_END (o1), OVERLAY_END (o2),
-                                   equal_kind, depth + 1, ht))
+           if (OVERLAY_BUFFER (o1) != OVERLAY_BUFFER (o2)
+               || OVERLAY_START (o1) != OVERLAY_START (o2)
+               || OVERLAY_END (o1) != OVERLAY_END (o2))
              return false;
            o1 = XOVERLAY (o1)->plist;
            o2 = XOVERLAY (o2)->plist;
@@ -3388,43 +3432,6 @@ FILENAME are suppressed.  */)
    bottleneck of Widget operation.  Here is their translation to C,
    for the sole reason of efficiency.  */
 
-DEFUN ("plist-member", Fplist_member, Splist_member, 2, 3, 0,
-       doc: /* Return non-nil if PLIST has the property PROP.
-PLIST is a property list, which is a list of the form
-\(PROP1 VALUE1 PROP2 VALUE2 ...).
-
-The comparison with PROP is done using PREDICATE, which defaults to
-`eq'.
-
-Unlike `plist-get', this allows you to distinguish between a missing
-property and a property with the value nil.
-The value is actually the tail of PLIST whose car is PROP.  */)
-  (Lisp_Object plist, Lisp_Object prop, Lisp_Object predicate)
-{
-  Lisp_Object tail = plist;
-  if (NILP (predicate))
-    predicate = Qeq;
-  FOR_EACH_TAIL (tail)
-    {
-      if (!NILP (call2 (predicate, XCAR (tail), prop)))
-       return tail;
-      tail = XCDR (tail);
-      if (! CONSP (tail))
-       break;
-    }
-  CHECK_TYPE (NILP (tail), Qplistp, plist);
-  return Qnil;
-}
-
-/* plist_member isn't used much in the Emacs sources, so just provide
-   a shim so that the function name follows the same pattern as
-   plist_get/plist_put.  */
-Lisp_Object
-plist_member (Lisp_Object plist, Lisp_Object prop)
-{
-  return Fplist_member (plist, prop, Qnil);
-}
-
 DEFUN ("widget-put", Fwidget_put, Swidget_put, 3, 3, 0,
        doc: /* In WIDGET, set PROPERTY to VALUE.
 The value can later be retrieved with `widget-get'.  */)
@@ -3652,7 +3659,7 @@ static signed char const 
base64_char_to_value[2][UCHAR_MAX] =
 static ptrdiff_t base64_encode_1 (const char *, char *, ptrdiff_t, bool, bool,
                                  bool, bool);
 static ptrdiff_t base64_decode_1 (const char *, char *, ptrdiff_t, bool,
-                                 bool, ptrdiff_t *);
+                                 bool, bool, ptrdiff_t *);
 
 static Lisp_Object base64_encode_region_1 (Lisp_Object, Lisp_Object, bool,
                                           bool, bool);
@@ -3915,7 +3922,7 @@ base64_encode_1 (const char *from, char *to, ptrdiff_t 
length,
 
 
 DEFUN ("base64-decode-region", Fbase64_decode_region, Sbase64_decode_region,
-       2, 3, "r",
+       2, 4, "r",
        doc: /* Base64-decode the region between BEG and END.
 Return the length of the decoded data.
 
@@ -3926,8 +3933,11 @@ system.
 
 If the region can't be decoded, signal an error and don't modify the buffer.
 Optional third argument BASE64URL determines whether to use the URL variant
-of the base 64 encoding, as defined in RFC 4648.  */)
-     (Lisp_Object beg, Lisp_Object end, Lisp_Object base64url)
+of the base 64 encoding, as defined in RFC 4648.
+If optional fourth argument INGORE-INVALID is non-nil invalid characters
+are ignored instead of signaling an error.  */)
+     (Lisp_Object beg, Lisp_Object end, Lisp_Object base64url,
+      Lisp_Object ignore_invalid)
 {
   ptrdiff_t ibeg, iend, length, allength;
   char *decoded;
@@ -3953,7 +3963,8 @@ of the base 64 encoding, as defined in RFC 4648.  */)
   move_gap_both (XFIXNAT (beg), ibeg);
   decoded_length = base64_decode_1 ((char *) BYTE_POS_ADDR (ibeg),
                                    decoded, length, !NILP (base64url),
-                                   multibyte, &inserted_chars);
+                                   multibyte, !NILP (ignore_invalid),
+                                   &inserted_chars);
   if (decoded_length > allength)
     emacs_abort ();
 
@@ -3986,11 +3997,13 @@ of the base 64 encoding, as defined in RFC 4648.  */)
 }
 
 DEFUN ("base64-decode-string", Fbase64_decode_string, Sbase64_decode_string,
-       1, 2, 0,
+       1, 3, 0,
        doc: /* Base64-decode STRING and return the result as a string.
 Optional argument BASE64URL determines whether to use the URL variant of
-the base 64 encoding, as defined in RFC 4648.  */)
-     (Lisp_Object string, Lisp_Object base64url)
+the base 64 encoding, as defined in RFC 4648.
+If optional third argument IGNORE-INVALID is non-nil invalid characters are
+ignored instead of signaling an error.  */)
+     (Lisp_Object string, Lisp_Object base64url, Lisp_Object ignore_invalid)
 {
   char *decoded;
   ptrdiff_t length, decoded_length;
@@ -4006,7 +4019,8 @@ the base 64 encoding, as defined in RFC 4648.  */)
   /* The decoded result should be unibyte. */
   ptrdiff_t decoded_chars;
   decoded_length = base64_decode_1 (SSDATA (string), decoded, length,
-                                   !NILP (base64url), 0, &decoded_chars);
+                                   !NILP (base64url), false,
+                                   !NILP (ignore_invalid), &decoded_chars);
   if (decoded_length > length)
     emacs_abort ();
   else if (decoded_length >= 0)
@@ -4023,12 +4037,13 @@ the base 64 encoding, as defined in RFC 4648.  */)
 
 /* Base64-decode the data at FROM of LENGTH bytes into TO.  If
    MULTIBYTE, the decoded result should be in multibyte
-   form.  Store the number of produced characters in *NCHARS_RETURN.  */
+   form.  If IGNORE_INVALID, ignore invalid base64 characters.
+   Store the number of produced characters in *NCHARS_RETURN.  */
 
 static ptrdiff_t
 base64_decode_1 (const char *from, char *to, ptrdiff_t length,
-                bool base64url,
-                bool multibyte, ptrdiff_t *nchars_return)
+                bool base64url, bool multibyte, bool ignore_invalid,
+                ptrdiff_t *nchars_return)
 {
   char const *f = from;
   char const *flim = from + length;
@@ -4054,7 +4069,7 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t 
length,
          c = *f++;
          v1 = b64_char_to_value[c];
        }
-      while (v1 < 0);
+      while (v1 < 0 || (v1 == 0 && ignore_invalid));
 
       if (v1 == 0)
        return -1;
@@ -4069,7 +4084,7 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t 
length,
          c = *f++;
          v1 = b64_char_to_value[c];
        }
-      while (v1 < 0);
+      while (v1 < 0 || (v1 == 0 && ignore_invalid));
 
       if (v1 == 0)
        return -1;
@@ -4088,7 +4103,7 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t 
length,
        {
          if (f == flim)
            {
-             if (!base64url)
+             if (!base64url && !ignore_invalid)
                return -1;
              *nchars_return = nchars;
              return e - to;
@@ -4096,7 +4111,7 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t 
length,
          c = *f++;
          v1 = b64_char_to_value[c];
        }
-      while (v1 < 0);
+      while (v1 < 0 || (v1 == 0 && ignore_invalid));
 
       if (c == '=')
        {
@@ -4130,7 +4145,7 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t 
length,
        {
          if (f == flim)
            {
-             if (!base64url)
+             if (!base64url && !ignore_invalid)
                return -1;
              *nchars_return = nchars;
              return e - to;
@@ -4138,7 +4153,7 @@ base64_decode_1 (const char *from, char *to, ptrdiff_t 
length,
          c = *f++;
          v1 = b64_char_to_value[c];
        }
-      while (v1 < 0);
+      while (v1 < 0 || (v1 == 0 && ignore_invalid));
 
       if (c == '=')
        continue;
@@ -5077,6 +5092,7 @@ sxhash_obj (Lisp_Object obj, int depth)
                    ? 42
                    : sxhash_vector (obj, depth));
          }
+       /* FIXME: Use `switch`.  */
        else if (pvec_type == PVEC_BIGNUM)
          return sxhash_bignum (obj);
        else if (pvec_type == PVEC_MARKER)
@@ -5091,8 +5107,8 @@ sxhash_obj (Lisp_Object obj, int depth)
          return sxhash_bool_vector (obj);
        else if (pvec_type == PVEC_OVERLAY)
          {
-           EMACS_UINT hash = sxhash_obj (OVERLAY_START (obj), depth);
-           hash = sxhash_combine (hash, sxhash_obj (OVERLAY_END (obj), depth));
+           EMACS_UINT hash = OVERLAY_START (obj);
+           hash = sxhash_combine (hash, OVERLAY_END (obj));
            hash = sxhash_combine (hash, sxhash_obj (XOVERLAY (obj)->plist, 
depth));
            return SXHASH_REDUCE (hash);
          }
diff --git a/src/gnutls.c b/src/gnutls.c
index a0de0238c4..7f0aaf85a4 100644
--- a/src/gnutls.c
+++ b/src/gnutls.c
@@ -2790,6 +2790,10 @@ Any GnuTLS extension with ID up to 100
 
   capabilities = Fcons (intern("gnutls"), capabilities);
 
+#  ifdef HAVE_GNUTLS_EXT__DUMBFW
+  capabilities = Fcons (intern("ClientHello Padding"), capabilities);
+#  endif
+
 # ifdef HAVE_GNUTLS3
   capabilities = Fcons (intern("gnutls3"), capabilities);
   capabilities = Fcons (intern("digests"), capabilities);
@@ -2807,16 +2811,14 @@ Any GnuTLS extension with ID up to 100
       const char* name = gnutls_ext_get_name(ext);
       if (name != NULL)
         {
-          capabilities = Fcons (intern(name), capabilities);
+          Lisp_Object cap = intern (name);
+          if (NILP (Fmemq (cap, capabilities)))
+            capabilities = Fcons (cap, capabilities);
         }
     }
 #  endif
 # endif          /* HAVE_GNUTLS3 */
 
-#  ifdef HAVE_GNUTLS_EXT__DUMBFW
-  capabilities = Fcons (intern("ClientHello Padding"), capabilities);
-#  endif
-
 # ifdef WINDOWSNT
   Vlibrary_cache = Fcons (Fcons (Qgnutls, capabilities), Vlibrary_cache);
 # endif /* WINDOWSNT */
diff --git a/src/haiku_support.cc b/src/haiku_support.cc
index 0f8e26d0db..3a98285677 100644
--- a/src/haiku_support.cc
+++ b/src/haiku_support.cc
@@ -653,6 +653,24 @@ public:
       Quit ();
     else if (msg->what == B_CLIPBOARD_CHANGED)
       haiku_write (CLIPBOARD_CHANGED_EVENT, &rq);
+    else if (msg->what == B_KEY_MAP_LOADED)
+      {
+       /* Install the new keymap.  Or rather, clear key_map -- Emacs
+          will fetch it again from the main thread the next time it
+          is needed.  */
+       if (key_map_lock.Lock ())
+         {
+           if (key_map)
+             free (key_map);
+
+           if (key_chars)
+             free (key_chars);
+
+           key_map = NULL;
+           key_chars = NULL;
+           key_map_lock.Unlock ();
+         }
+      }
     else
       BApplication::MessageReceived (msg);
   }
diff --git a/src/haikuterm.c b/src/haikuterm.c
index 838eb128fa..4e32b74716 100644
--- a/src/haikuterm.c
+++ b/src/haikuterm.c
@@ -232,6 +232,9 @@ haiku_frame_up_to_date (struct frame *f)
       be_evict_font_cache ();
       up_to_date_count = 0;
     }
+
+  /* Mark the frame as complete.  */
+  FRAME_COMPLETE_P (f) = true;
   unblock_input ();
 }
 
@@ -265,6 +268,8 @@ haiku_clear_frame (struct frame *f)
 
   mark_window_cursors_off (XWINDOW (FRAME_ROOT_WINDOW (f)));
 
+  FRAME_COMPLETE_P (f) = false;
+
   block_input ();
   BView_draw_lock (view, true, 0, 0, FRAME_PIXEL_WIDTH (f),
                   FRAME_PIXEL_HEIGHT (f));
@@ -1436,6 +1441,9 @@ haiku_clip_to_row (struct window *w, struct glyph_row 
*row,
 static void
 haiku_update_begin (struct frame *f)
 {
+  /* Mark the frame as incomplete so it is not flushed upon handling
+     input.  */
+  FRAME_COMPLETE_P (f) = false;
 }
 
 static void
@@ -2959,6 +2967,10 @@ haiku_flush (struct frame *f)
   if (FRAME_DIRTY_P (f) && !buffer_flipping_blocked_p ())
     haiku_flip_buffers (f);
 
+  /* The frame is complete again as its contents were just
+     flushed.  */
+  FRAME_COMPLETE_P (f) = true;
+
   if (FRAME_VISIBLE_P (f) && !FRAME_TOOLTIP_P (f))
     BWindow_Flush (FRAME_HAIKU_WINDOW (f));
 }
@@ -3086,10 +3098,15 @@ haiku_make_fullscreen_consistent (struct frame *f)
 static void
 haiku_flush_dirty_back_buffer_on (struct frame *f)
 {
-  if (!FRAME_GARBAGED_P (f)
-      && !buffer_flipping_blocked_p ()
-      && FRAME_DIRTY_P (f))
-    haiku_flip_buffers (f);
+  if (FRAME_GARBAGED_P (f)
+      || buffer_flipping_blocked_p ()
+      /* If the frame is not already up to date, do not flush buffers
+        on input, as that will result in flicker.  */
+      || !FRAME_COMPLETE_P (f)
+      || !FRAME_DIRTY_P (f))
+    return;
+
+  haiku_flip_buffers (f);
 }
 
 /* N.B. that support for TYPE must be explicitly added to
@@ -3135,6 +3152,7 @@ haiku_read_socket (struct terminal *terminal, struct 
input_event *hold_quit)
   int button_or_motion_p, do_help;
   enum haiku_event_type type;
   struct input_event inev, inev2;
+  struct frame *mouse_frame;
 
   message_count = 0;
   button_or_motion_p = 0;
@@ -3252,9 +3270,13 @@ haiku_read_socket (struct terminal *terminal, struct 
input_event *hold_quit)
                    || !EQ (f->tool_bar_window, hlinfo->mouse_face_window)
                    || !EQ (f->tab_bar_window, hlinfo->mouse_face_window)))
              {
+               mouse_frame = hlinfo->mouse_face_mouse_frame;
+
                clear_mouse_face (hlinfo);
                hlinfo->mouse_face_hidden = true;
-               haiku_flush_dirty_back_buffer_on (f);
+
+               if (mouse_frame)
+                 haiku_flush_dirty_back_buffer_on (mouse_frame);
              }
 
            inev.code = b->keysym ? b->keysym : b->multibyte_char;
diff --git a/src/haikuterm.h b/src/haikuterm.h
index 86274fd42a..70e8cf948b 100644
--- a/src/haikuterm.h
+++ b/src/haikuterm.h
@@ -174,6 +174,10 @@ struct haiku_output
      displayed yet.  */
   bool_bf dirty_p : 1;
 
+  /* Whether or not the frame is complete, i.e. safe to flush on
+     input.  */
+  bool_bf complete_p : 1;
+
   struct font *font;
 
   /* The pending position we're waiting for. */
@@ -275,6 +279,7 @@ struct scroll_bar
 #define XSCROLL_BAR(vec) ((struct scroll_bar *) XVECTOR (vec))
 
 #define FRAME_DIRTY_P(f)               (FRAME_OUTPUT_DATA (f)->dirty_p)
+#define FRAME_COMPLETE_P(f)            (FRAME_OUTPUT_DATA (f)->complete_p)
 #define MAKE_FRAME_DIRTY(f)            (FRAME_DIRTY_P (f) = 1)
 #define FRAME_OUTPUT_DATA(f)           ((f)->output_data.haiku)
 #define FRAME_HAIKU_WINDOW(f)          (FRAME_OUTPUT_DATA (f)->window)
diff --git a/src/image.c b/src/image.c
index 1e323ba66a..600c32571e 100644
--- a/src/image.c
+++ b/src/image.c
@@ -1843,7 +1843,9 @@ image_clear_image (struct frame *f, struct image *img)
 {
   block_input ();
   image_clear_image_1 (f, img,
-                  CLEAR_IMAGE_PIXMAP | CLEAR_IMAGE_MASK | CLEAR_IMAGE_COLORS);
+                      (CLEAR_IMAGE_PIXMAP
+                       | CLEAR_IMAGE_MASK
+                       | CLEAR_IMAGE_COLORS));
   unblock_input ();
 }
 
@@ -2980,7 +2982,8 @@ lookup_image (struct frame *f, Lisp_Object spec, int 
face_id)
       unblock_input ();
     }
 
-  /* We're using IMG, so set its timestamp to `now'.  */
+  /* IMG is now being used, so set its timestamp to the current
+     time.  */
   img->timestamp = current_timespec ();
 
   /* Value is the image id.  */
@@ -3238,12 +3241,13 @@ x_create_x_image_and_pixmap (struct frame *f, int 
width, int height, int depth,
 static void
 x_destroy_x_image (XImage *ximg)
 {
-  eassert (input_blocked_p ());
   if (ximg)
     {
       xfree (ximg->data);
       ximg->data = NULL;
     }
+
+  XDestroyImage (ximg);
 }
 
 # if !defined USE_CAIRO && defined HAVE_XRENDER
@@ -6224,26 +6228,28 @@ static void
 image_from_emacs_colors (struct frame *f, struct image *img, Emacs_Color 
*colors)
 {
   int x, y;
-  Emacs_Pix_Container oimg = NULL;
+  Emacs_Pix_Container ximage;
   Emacs_Color *p;
 
+  ximage = NULL;
+
   init_color_table ();
 
   image_clear_image_1 (f, img, CLEAR_IMAGE_PIXMAP | CLEAR_IMAGE_COLORS);
   image_create_x_image_and_pixmap (f, img, img->width, img->height, 0,
-                                  &oimg, 0);
+                                  &ximage, 0);
   p = colors;
   for (y = 0; y < img->height; ++y)
     for (x = 0; x < img->width; ++x, ++p)
       {
        unsigned long pixel;
        pixel = lookup_rgb_color (f, p->red, p->green, p->blue);
-       PUT_PIXEL (oimg, x, y, pixel);
+       PUT_PIXEL (ximage, x, y, pixel);
       }
 
   xfree (colors);
 
-  image_put_x_image (f, img, oimg, 0);
+  image_put_x_image (f, img, ximage, false);
 #ifdef COLOR_TABLE_SUPPORT
   img->colors = colors_in_color_table (&img->ncolors);
   free_color_table ();
@@ -12207,7 +12213,15 @@ non-numeric, there is no explicit limit on the size of 
images.  */);
 # endif
   DEFSYM (Qgobject, "gobject");
 #endif /* HAVE_NTGUI  */
-#endif /* HAVE_RSVG  */
+#elif defined HAVE_NATIVE_IMAGE_API                    \
+  && ((defined HAVE_NS && defined NS_IMPL_COCOA)       \
+      || defined HAVE_HAIKU)
+  DEFSYM (Qsvg, "svg");
+
+  /* On Haiku, the SVG translator may not be installed.  */
+  if (image_can_use_native_api (Qsvg))
+    add_image_type (Qsvg);
+#endif
 
 #ifdef HAVE_NS
   DEFSYM (Qheic, "heic");
diff --git a/src/indent.c b/src/indent.c
index aa905f387b..4671ccccf9 100644
--- a/src/indent.c
+++ b/src/indent.c
@@ -224,9 +224,6 @@ skip_invisible (ptrdiff_t pos, ptrdiff_t *next_boundary_p, 
ptrdiff_t to, Lisp_Ob
   XSETFASTINT (position, pos);
   XSETBUFFER (buffer, current_buffer);
 
-  /* Give faster response for overlay lookup near POS.  */
-  recenter_overlay_lists (current_buffer, pos);
-
   /* We must not advance farther than the next overlay change.
      The overlay change might change the invisible property;
      or there might be overlay strings to be displayed there.  */
@@ -518,7 +515,7 @@ check_display_width (ptrdiff_t pos, ptrdiff_t col, 
ptrdiff_t *endpos)
        {
          ptrdiff_t start;
          if (OVERLAYP (overlay))
-           *endpos = OVERLAY_POSITION (OVERLAY_END (overlay));
+           *endpos = OVERLAY_END (overlay);
          else
            get_property_and_range (pos, Qdisplay, &val, &start, endpos, Qnil);
 
@@ -637,6 +634,11 @@ scan_for_column (ptrdiff_t *endpos, EMACS_INT *goalcol,
            scan_byte = CHAR_TO_BYTE (scan);
          if (scan >= end)
            goto endloop;
+         /* We may have over-stepped cmp_it.stop_pos while skipping
+            the invisible text.  If so, update cmp_it.stop_pos.  */
+         if (scan > cmp_it.stop_pos && cmp_it.id < 0)
+           composition_reseat_it (&cmp_it, scan, scan_byte, end,
+                                  w, -1, NULL, Qnil);
        }
 
       /* Test reaching the goal column.  We do this after skipping
@@ -1358,6 +1360,9 @@ compute_motion (ptrdiff_t from, ptrdiff_t frombyte, 
EMACS_INT fromvpos,
              pos = newpos;
              pos_byte = CHAR_TO_BYTE (pos);
            }
+         if (newpos > cmp_it.stop_pos && cmp_it.id < 0)
+           composition_reseat_it (&cmp_it, pos, pos_byte, to,
+                                  win, -1, NULL, Qnil);
 
          rarely_quit (++quit_count);
        }
diff --git a/src/insdel.c b/src/insdel.c
index 38d5fbda00..03ce59b340 100644
--- a/src/insdel.c
+++ b/src/insdel.c
@@ -268,6 +268,7 @@ adjust_markers_for_delete (ptrdiff_t from, ptrdiff_t 
from_byte,
          m->bytepos = from_byte;
        }
     }
+  adjust_overlays_for_delete (from, to - from);
 }
 
 
@@ -284,7 +285,6 @@ adjust_markers_for_insert (ptrdiff_t from, ptrdiff_t 
from_byte,
                           ptrdiff_t to, ptrdiff_t to_byte, bool before_markers)
 {
   struct Lisp_Marker *m;
-  bool adjusted = 0;
   ptrdiff_t nchars = to - from;
   ptrdiff_t nbytes = to_byte - from_byte;
 
@@ -300,8 +300,6 @@ adjust_markers_for_insert (ptrdiff_t from, ptrdiff_t 
from_byte,
            {
              m->bytepos = to_byte;
              m->charpos = to;
-             if (m->insertion_type)
-               adjusted = 1;
            }
        }
       else if (m->bytepos > from_byte)
@@ -310,15 +308,7 @@ adjust_markers_for_insert (ptrdiff_t from, ptrdiff_t 
from_byte,
          m->charpos += nchars;
        }
     }
-
-  /* Adjusting only markers whose insertion-type is t may result in
-     - disordered start and end in overlays, and
-     - disordered overlays in the slot `overlays_before' of current_buffer.  */
-  if (adjusted)
-    {
-      fix_start_end_in_overlays (from, to);
-      fix_overlays_before (current_buffer, from, to);
-    }
+  adjust_overlays_for_insert (from, to - from, before_markers);
 }
 
 /* Adjust point for an insertion of NBYTES bytes, which are NCHARS characters.
@@ -355,6 +345,11 @@ adjust_markers_for_replace (ptrdiff_t from, ptrdiff_t 
from_byte,
   ptrdiff_t diff_bytes = new_bytes - old_bytes;
 
   adjust_suspend_auto_hscroll (from, from + old_chars);
+
+  /* FIXME: When OLD_CHARS is 0, this "replacement" is really just an
+     insertion, but the behavior we provide here in that case is that of
+     `insert-before-markers` rather than that of `insert`.
+     Maybe not a bug, but not a feature either.  */
   for (m = BUF_MARKERS (current_buffer); m; m = m->next)
     {
       if (m->bytepos >= prev_to_byte)
@@ -370,6 +365,10 @@ adjust_markers_for_replace (ptrdiff_t from, ptrdiff_t 
from_byte,
     }
 
   check_markers ();
+
+  adjust_overlays_for_insert (from + old_chars, new_chars, true);
+  if (old_chars)
+    adjust_overlays_for_delete (from, old_chars);
 }
 
 /* Starting at POS (BYTEPOS), find the byte position corresponding to
@@ -929,7 +928,6 @@ insert_1_both (const char *string,
   if (Z - GPT < END_UNCHANGED)
     END_UNCHANGED = Z - GPT;
 
-  adjust_overlays_for_insert (PT, nchars);
   adjust_markers_for_insert (PT, PT_BYTE,
                             PT + nchars, PT_BYTE + nbytes,
                             before_markers);
@@ -1055,7 +1053,6 @@ insert_from_string_1 (Lisp_Object string, ptrdiff_t pos, 
ptrdiff_t pos_byte,
   if (Z - GPT < END_UNCHANGED)
     END_UNCHANGED = Z - GPT;
 
-  adjust_overlays_for_insert (PT, nchars);
   adjust_markers_for_insert (PT, PT_BYTE, PT + nchars,
                             PT_BYTE + outgoing_nbytes,
                             before_markers);
@@ -1127,9 +1124,8 @@ insert_from_gap (ptrdiff_t nchars, ptrdiff_t nbytes, bool 
text_at_gap_tail)
 
   insert_from_gap_1 (nchars, nbytes, text_at_gap_tail);
 
-  adjust_overlays_for_insert (ins_charpos, nchars);
   adjust_markers_for_insert (ins_charpos, ins_bytepos,
-                            ins_charpos + nchars, ins_bytepos + nbytes, 0);
+                            ins_charpos + nchars, ins_bytepos + nbytes, false);
 
   if (buffer_intervals (current_buffer))
     {
@@ -1269,10 +1265,9 @@ insert_from_buffer_1 (struct buffer *buf,
   if (Z - GPT < END_UNCHANGED)
     END_UNCHANGED = Z - GPT;
 
-  adjust_overlays_for_insert (PT, nchars);
   adjust_markers_for_insert (PT, PT_BYTE, PT + nchars,
                             PT_BYTE + outgoing_nbytes,
-                            0);
+                            false);
 
   offset_intervals (current_buffer, PT, nchars);
 
@@ -1328,17 +1323,12 @@ adjust_after_replace (ptrdiff_t from, ptrdiff_t 
from_byte,
                                len, len_byte);
   else
     adjust_markers_for_insert (from, from_byte,
-                              from + len, from_byte + len_byte, 0);
+                              from + len, from_byte + len_byte, false);
 
   if (nchars_del > 0)
     record_delete (from, prev_text, false);
   record_insert (from, len);
 
-  if (len > nchars_del)
-    adjust_overlays_for_insert (from, len - nchars_del);
-  else if (len < nchars_del)
-    adjust_overlays_for_delete (from, nchars_del - len);
-
   offset_intervals (current_buffer, from, len - nchars_del);
 
   if (from < PT)
@@ -1350,8 +1340,6 @@ adjust_after_replace (ptrdiff_t from, ptrdiff_t from_byte,
 
   check_markers ();
 
-  if (len == 0)
-    evaporate_overlays (from);
   modiff_incr (&MODIFF, nchars_del + len);
   CHARS_MODIFF = MODIFF;
 }
@@ -1519,14 +1507,9 @@ replace_range (ptrdiff_t from, ptrdiff_t to, Lisp_Object 
new,
         which make the original byte positions of the markers
         invalid.  */
       adjust_markers_bytepos (from, from_byte, from + inschars,
-                             from_byte + outgoing_insbytes, 1);
+                             from_byte + outgoing_insbytes, true);
     }
 
-  /* Adjust the overlay center as needed.  This must be done after
-     adjusting the markers that bound the overlays.  */
-  adjust_overlays_for_delete (from, nchars_del);
-  adjust_overlays_for_insert (from, inschars);
-
   offset_intervals (current_buffer, from, inschars - nchars_del);
 
   /* Get the intervals for the part of the string we are inserting--
@@ -1542,9 +1525,6 @@ replace_range (ptrdiff_t from, ptrdiff_t to, Lisp_Object 
new,
                  (from_byte + outgoing_insbytes
                   - (PT_BYTE < to_byte ? PT_BYTE : to_byte)));
 
-  if (outgoing_insbytes == 0)
-    evaporate_overlays (from);
-
   check_markers ();
 
   modiff_incr (&MODIFF, nchars_del + inschars);
@@ -1652,18 +1632,10 @@ replace_range_2 (ptrdiff_t from, ptrdiff_t from_byte,
             sequences which make the original byte positions of the
             markers invalid.  */
          adjust_markers_bytepos (from, from_byte, from + inschars,
-                                 from_byte + insbytes, 1);
+                                 from_byte + insbytes, true);
        }
     }
 
-  /* Adjust the overlay center as needed.  This must be done after
-     adjusting the markers that bound the overlays.  */
-  if (nchars_del != inschars)
-    {
-      adjust_overlays_for_insert (from, inschars);
-      adjust_overlays_for_delete (from + inschars, nchars_del);
-    }
-
   offset_intervals (current_buffer, from, inschars - nchars_del);
 
   /* Relocate point as if it were a marker.  */
@@ -1676,9 +1648,6 @@ replace_range_2 (ptrdiff_t from, ptrdiff_t from_byte,
        adjust_point (inschars - nchars_del, insbytes - nbytes_del);
     }
 
-  if (insbytes == 0)
-    evaporate_overlays (from);
-
   check_markers ();
 
   modiff_incr (&MODIFF, nchars_del + inschars);
@@ -1866,10 +1835,6 @@ del_range_2 (ptrdiff_t from, ptrdiff_t from_byte,
 
   offset_intervals (current_buffer, from, - nchars_del);
 
-  /* Adjust the overlay center as needed.  This must be done after
-     adjusting the markers that bound the overlays.  */
-  adjust_overlays_for_delete (from, nchars_del);
-
   GAP_SIZE += nbytes_del;
   ZV_BYTE -= nbytes_del;
   Z_BYTE -= nbytes_del;
@@ -1891,8 +1856,6 @@ del_range_2 (ptrdiff_t from, ptrdiff_t from_byte,
 
   check_markers ();
 
-  evaporate_overlays (from);
-
   return deletion;
 }
 
diff --git a/src/intervals.c b/src/intervals.c
index 7f11981557..78f4f6b617 100644
--- a/src/intervals.c
+++ b/src/intervals.c
@@ -1836,8 +1836,8 @@ adjust_for_invis_intang (ptrdiff_t pos, ptrdiff_t 
test_offs, ptrdiff_t adj,
             == (test_offs == 0 ? 1 : -1))
          /* Invisible property is from an overlay.  */
          : (test_offs == 0
-            ? XMARKER (OVERLAY_START (invis_overlay))->insertion_type == 0
-            : XMARKER (OVERLAY_END (invis_overlay))->insertion_type == 1)))
+            ? ! OVERLAY_FRONT_ADVANCE_P (invis_overlay)
+            : OVERLAY_REAR_ADVANCE_P (invis_overlay))))
     pos += adj;
 
   return pos;
diff --git a/src/itree.c b/src/itree.c
new file mode 100644
index 0000000000..ae69c97d6d
--- /dev/null
+++ b/src/itree.c
@@ -0,0 +1,1449 @@
+/* This file implements an efficient interval data-structure.
+
+Copyright (C) 2017-2022  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 <http://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+#include <math.h>
+
+#include "itree.h"
+
+/*
+   Intervals of the form [BEGIN, END), are stored as nodes inside a RB
+   tree, ordered by BEGIN.  The core operation of this tree (besides
+   insert, remove, etc.) is finding all intervals intersecting with
+   some given interval.  In order to perform this operation
+   efficiently, every node stores a third value called LIMIT. (See
+   https://en.wikipedia.org/wiki/Interval_tree#Augmented_tree and its
+   source Introduction to Algorithms, Cormen et al. .)
+
+   ==== Finding intervals ====
+
+   If we search for all intervals intersecting with (X, Y], we look at
+   some node and test whether
+
+   NODE.BEGIN > Y
+
+   Due to the invariant of the search tree, we know, that we may
+   safely prune NODE's right subtree if this test succeeds, since all
+   intervals begin strictly after Y.
+
+   But we can not make such an assumptions about the left tree, since
+   all we know is that the intervals in this subtree must start before
+   or at NODE.BEGIN.  So we can't tell, whether they end before X or
+   not.  To solve this problem we add another attribute to each node,
+   called LIMIT.
+
+   The LIMIT of a node is the largest END value occurring in the nodes
+   subtree (including the node itself).  Thus, we may look at the left
+   child of some NODE and test whether
+
+   NODE.left.LIMIT < X
+
+   and this tells us, if all intervals in the left subtree of NODE end
+   before X and if they can be pruned.
+
+   Conversely, if this inequality is false, the left subtree must
+   contain at least one intersecting interval, giving a resulting time
+   complexity of O(K*log(N)) for this operation, where K is the size
+   of the result set and N the size of the tree.
+
+   ==== FIXME: bug#58342 some important operations remain slow ===
+
+   The amortized costs of Emacs' previous-overlay-change and
+   next-overlay-change functions are O(N) with this data structure.
+   The root problem is that we only have an order for the BEG field,
+   but not the END.  The previous/next overlay change operations need
+   to find the nearest point where there is *either* an interval BEG
+   or END point, but there is no efficient way to narrow the search
+   space over END positions.
+
+   Consider the case where next-overlay-change is called at POS, all
+   interval BEG positions are less than pos POS and all interval END
+   posistions are after.  These END positions have no order, and so
+   *every* interval must be examined.  This is at least O(N).  The
+   previous-overlay-change case is similar.  The root issue is that
+   the iterative "narrowing" approach is not guaranteed to reduce the
+   search space in logarithmic time, since END is not ordered in the
+   tree.
+
+   One might argue that the LIMIT value will do this narrowing, but
+   this narrowing is O(K*log(N)) where K is the size of the result
+   set.  If we are interested in finding the node in a range with the
+   smallest END, we might have to examine all K nodes in that range.
+   In the case of the *-overlay-channge functions, K may well be equal
+   to N.
+
+   Ideally, a tree based data structure for overlays would have
+   O(log(N)) performance for previous-overlay-change and
+   next-overlay-change, as these are called in performance sensitive
+   situations such as redisplay.  The only way I can think of
+   achieving this is by keeping one ordering by BEG and a separate
+   ordering by END, and then performing logic quite similar to the
+   current Emacs overlays-before and overlays-after lists.
+
+   ==== Adjusting intervals ====
+
+   Since this data-structure will be used for overlays in an Emacs
+   buffer, a second core operation is the ability to insert and delete
+   gaps in the tree.  This models the insertion and deletion of text
+   in a buffer and the effects it may have on the positions of
+   overlays.
+
+   Consider this: Something gets inserted at position P into a buffer
+   and assume that all overlays occur strictly after P.  Ordinarily,
+   we would have to iterate all overlays and increment their BEGIN and
+   END values accordingly (the insertion of text pushes them back).
+   In order to avoid this, we introduce yet another node attribute,
+   called OFFSET.
+
+   The OFFSET of some some subtree, represented by its root, is the
+   amount of shift that needs to be applied to its BEGIN, END and
+   LIMIT values, in order to get to the actual buffer positions.
+   Coming back to the example, all we would need to do in this case,
+   is to increment the OFFSET of the tree's root, without any
+   traversal of the tree itself.
+
+   As a consequence, the real values of BEGIN, END and LIMIT of some
+   NODE need to be computed by incrementing them by the sum of NODE's
+   OFFSET and all of its ancestors offsets.  Therefore, we store a
+   counter (otick) inside every node and also the tree, by which we
+   remember the fact, that a node's path to the root has no offsets
+   applied (i.e. its values are up to date).  This is the case if some
+   node's value differs from the tree's one, the later of which is
+   incremented whenever some node's offset has changed.  */
+
+/* +=======================================================================+
+ * | Stack
+ * +=======================================================================+ */
+
+typedef uintptr_t nodeptr_and_flag;
+
+static inline nodeptr_and_flag
+make_nav (struct itree_node *ptr, bool flag)
+{
+  uintptr_t v = (uintptr_t) ptr;
+  /* We assume alignment imposes the LSB is clear for us to use it.  */
+  eassert (!(v & 1));
+  return v | !!flag;
+}
+
+static inline struct itree_node *
+nav_nodeptr (nodeptr_and_flag nav)
+{
+  return (struct itree_node *) (nav & (~(uintptr_t)1));
+}
+
+static inline bool
+nav_flag (nodeptr_and_flag nav)
+{
+  return (bool) (nav & 1);
+}
+
+/* Simple dynamic array. */
+struct interval_stack
+{
+  nodeptr_and_flag *nodes;
+  size_t size;
+  size_t length;
+};
+
+/* This is just a simple dynamic array with stack semantics. */
+
+static struct interval_stack*
+interval_stack_create (intmax_t initial_size)
+{
+  struct interval_stack *stack = xmalloc (sizeof (struct interval_stack));
+  stack->size = max (0, initial_size);
+  stack->nodes = xmalloc (stack->size * sizeof (struct itree_node*));
+  stack->length = 0;
+  return stack;
+}
+
+static void
+interval_stack_destroy (struct interval_stack *stack)
+{
+  if (! stack)
+    return;
+  if (stack->nodes)
+    xfree (stack->nodes);
+  xfree (stack);
+}
+
+static void
+interval_stack_clear (struct interval_stack *stack)
+{
+  stack->length = 0;
+}
+
+static inline void
+interval_stack_ensure_space (struct interval_stack *stack, uintmax_t nelements)
+{
+  if (nelements > stack->size)
+    {
+      stack->size = (nelements + 1) * 2;
+      stack->nodes = xrealloc (stack->nodes,
+                              stack->size * sizeof (*stack->nodes));
+    }
+}
+
+/* Push NODE on the STACK, while settings its visited flag to FLAG. */
+
+static inline void
+interval_stack_push_flagged (struct interval_stack *stack,
+                            struct itree_node *node, bool flag)
+{
+  eassert (node);
+
+  /* FIXME: While the stack used in the iterator is bounded by the tree
+     depth and could be easily pre-allocated to a large enough size to avoid
+     this "ensure" check, `interval_stack_push` is also used elsewhere to
+     simply collect some subset of the overlays, where it's only bounded by
+     the total number of overlays in the buffer (which can be large and thus
+     preferably not pre-allocated needlessly).  */
+  interval_stack_ensure_space (stack, stack->length + 1);
+
+  stack->nodes[stack->length] = make_nav (node, flag);
+  stack->length++;
+}
+
+static inline void
+interval_stack_push (struct interval_stack *stack, struct itree_node *node)
+{
+  interval_stack_push_flagged (stack, node, false);
+}
+
+static inline nodeptr_and_flag
+interval_stack_pop (struct interval_stack *stack)
+{
+  if (stack->length == 0)
+    return make_nav (NULL, false);
+  return stack->nodes[--stack->length];
+}
+
+
+/* +-----------------------------------------------------------------------+ */
+
+/* State used when iterating interval. */
+struct itree_iterator
+{
+  struct interval_stack *stack;
+  ptrdiff_t begin;
+  ptrdiff_t end;
+
+  /* A copy of the tree's `otick`.  */
+  uintmax_t otick;
+  enum itree_order order;
+  bool running;
+  const char *file;
+  int line;
+};
+
+/* Ideally, every iteration would use its own `iter` object, so we could
+   have several iterations active at the same time.  In practice, iterations
+   are limited by the fact we don't allow modifying the tree at the same
+   time, making the use of nested iterations quite rare anyway.
+   So we just use a single global iterator instead for now.  */
+static struct itree_iterator *iter = NULL;
+
+static int
+interval_tree_max_height (const struct itree_tree *tree)
+{
+  return 2 * log (tree->size + 1) / log (2) + 0.5;
+}
+
+/* Allocate a new iterator for TREE. */
+
+static struct itree_iterator *
+itree_iterator_create (struct itree_tree *tree)
+{
+  struct itree_iterator *g = xmalloc (sizeof *g);
+  /* 19 here just avoids starting with a silly-small stack.
+     FIXME: Since this stack only needs to be about 2*max_depth
+     in the worst case, we could completely pre-allocate it to something
+     like word-bit-size * 2 and then never worry about growing it.  */
+  const int size = (tree ? interval_tree_max_height (tree) : 19) + 1;
+
+  g->stack = interval_stack_create (size);
+  g->running = false;
+  g->begin = 0;
+  g->end = 0;
+  g->file = NULL;
+  g->line = 0;
+  return g;
+}
+
+void
+init_itree (void)
+{
+  eassert (!iter);
+  iter = itree_iterator_create (NULL);
+}
+
+#ifdef HAVE_UNEXEC
+void
+forget_itree (void)
+{
+  iter = NULL;
+}
+#endif
+
+struct check_subtree_result
+{
+  /* Node count of the tree.  */
+  int size;
+
+  /* Limit of the tree (max END).  */
+  ptrdiff_t limit;
+
+  /* Black height of the tree.  */
+  int black_height;
+};
+
+static struct check_subtree_result
+check_subtree (struct itree_node *node,
+              bool check_red_black_invariants, uintmax_t tree_otick,
+              ptrdiff_t offset, ptrdiff_t min_begin,
+              ptrdiff_t max_begin)
+{
+  struct check_subtree_result result = { .size = 0,
+                                        .limit = PTRDIFF_MIN,
+                                        .black_height = 0 };
+  if (node == NULL)
+    return result;
+
+  /* Validate structure.  */
+  eassert (node->left == NULL || node->left->parent == node);
+  eassert (node->right == NULL || node->right->parent == node);
+
+  /* Validate otick.  A node's otick must be <= to the tree's otick
+     and <= to its parent's otick.
+
+     Note: we cannot assert that (NODE.otick == NODE.parent.otick)
+     implies (NODE.offset == 0) because interval_tree_inherit_offset()
+     doesn't always update otick.  It could, but it is not clear there
+     is a need.  */
+  eassert (node->otick <= tree_otick);
+  eassert (node->parent == NULL || node->otick <= node->parent->otick);
+  eassert (node->otick != tree_otick || node->offset == 0);
+
+  offset += node->offset;
+  ptrdiff_t begin = node->begin + offset;
+  ptrdiff_t end = node->end + offset;
+  ptrdiff_t limit = node->limit + offset;
+
+  eassert (min_begin <= max_begin);
+  eassert (min_begin <= begin);
+  eassert (begin <= max_begin);
+  eassert (end <= limit);
+
+  struct check_subtree_result left_result
+    = check_subtree (node->left, check_red_black_invariants,
+                    tree_otick, offset, min_begin, begin);
+  struct check_subtree_result right_result
+    = check_subtree (node->right, check_red_black_invariants,
+                    tree_otick, offset, begin, max_begin);
+
+  eassert (left_result.limit <= limit);
+  eassert (right_result.limit <= limit);
+  eassert (limit == max (end, max (left_result.limit, right_result.limit)));
+
+  if (check_red_black_invariants)
+    {
+      eassert (left_result.black_height == right_result.black_height);
+      eassert (node->parent == NULL || !node->red || !node->parent->red);
+    }
+
+  result.size = 1 + left_result.size + right_result.size;
+  result.limit = limit;
+  result.black_height = (node->red ? 0 : 1) + left_result.black_height;
+  return result;
+}
+
+/* Validate invariants for TREE.  If CHECK_RED_BLACK_INVARIANTS, red
+   nodes with red children are considered invalid.
+
+   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
+   the invariants.  When ENABLE_OVERLAY_CHECKING is 1 it checks the
+   entire tree and validates all invariants.
+*/
+static bool
+check_tree (struct itree_tree *tree,
+           bool check_red_black_invariants)
+{
+  eassert (tree != NULL);
+  eassert (tree->size >= 0);
+  eassert ((tree->size == 0) == (tree->root == NULL));
+  if (tree->root == NULL)
+    return true;
+  eassert (tree->root->parent == NULL);
+  eassert (!check_red_black_invariants || !tree->root->red);
+
+  struct itree_node *node = tree->root;
+  struct check_subtree_result result
+    = check_subtree (node, check_red_black_invariants, tree->otick,
+                    node->offset, PTRDIFF_MIN,
+                    PTRDIFF_MAX);
+  eassert (result.size == tree->size);
+
+  /* The only way this function fails is eassert().  */
+  return true;
+}
+
+/* +=======================================================================+
+ * | Internal Functions
+ * +=======================================================================+ */
+
+static bool
+null_safe_is_red (struct itree_node *node)
+{
+  return node != NULL && node->red;
+}
+
+static bool
+null_safe_is_black (struct itree_node *node)
+{
+  return node == NULL || !node->red; /* NULL nodes are black */
+}
+
+static inline ptrdiff_t
+itree_newlimit (struct itree_node *node)
+{
+  eassert (node != NULL);
+  return max (node->end,
+             max (node->left == NULL
+                    ? PTRDIFF_MIN
+                    : node->left->limit + node->left->offset,
+                  node->right == NULL
+                    ? PTRDIFF_MIN
+                    : node->right->limit + node->right->offset));
+}
+
+/* Update NODE's limit attribute according to its children. */
+
+static void
+interval_tree_update_limit (struct itree_node *node)
+{
+  if (node == NULL)
+    return;
+
+  node->limit = itree_newlimit (node);
+}
+
+/* Apply NODE's offset to its begin, end and limit values and
+   propagate it to its children.
+
+   Does nothing, if NODE is clean, i.e. NODE.otick = tree.otick .
+*/
+
+static void
+interval_tree_inherit_offset (uintmax_t otick, struct itree_node *node)
+{
+  eassert (node->parent == NULL || node->parent->otick >= node->otick);
+  if (node->otick == otick)
+    {
+      eassert (node->offset == 0);
+      return;
+    }
+
+  /* Offsets can be inherited from dirty nodes (with out of date
+     otick) during removal, since we do not travel down from the root
+     in that case.  In this case rotations are performed on
+     potentially "dirty" nodes, where we only need to make sure the
+     *local* offsets are zero.  */
+
+  if (node->offset)
+    {
+      node->begin += node->offset;
+      node->end   += node->offset;
+      node->limit += node->offset;
+      if (node->left != NULL)
+       node->left->offset += node->offset;
+      if (node->right != NULL)
+       node->right->offset += node->offset;
+      node->offset = 0;
+    }
+  /* The only thing that matters about `otick` is whether it's equal to
+     that of the tree.  We could also "blindly" inherit from parent->otick,
+     but we need to tree's `otick` anyway for when there's no parent.  */
+  if (node->parent == NULL || node->parent->otick == otick)
+    node->otick = otick;
+}
+
+/* Update limit of NODE and its ancestors.  Stop when it becomes
+   stable, i.e. new_limit = old_limit.  */
+
+static void
+interval_tree_propagate_limit (struct itree_node *node)
+{
+  ptrdiff_t newlimit;
+
+  if (node == NULL)
+    return;
+
+  while (1)
+    {
+      newlimit = itree_newlimit (node);
+
+      if (newlimit == node->limit)
+       break;
+      node->limit = newlimit;
+      if (node->parent == NULL)
+       break;
+      node = node->parent;
+    }
+}
+
+static struct itree_node*
+interval_tree_validate (struct itree_tree *tree, struct itree_node *node)
+{
+
+  if (tree->otick == node->otick || node == NULL)
+    return node;
+  if (node != tree->root)
+    interval_tree_validate (tree, node->parent);
+
+  interval_tree_inherit_offset (tree->otick, node);
+  return node;
+}
+
+/* +=======================================================================+
+ * | Tree operations
+ * +=======================================================================+ */
+
+/* Initialize an allocated node. */
+
+void
+itree_node_init (struct itree_node *node,
+                bool front_advance, bool rear_advance,
+                Lisp_Object data)
+{
+  node->parent = NULL;
+  node->left = NULL;
+  node->right = NULL;
+  node->begin = -1;
+  node->end = -1;
+  node->front_advance = front_advance;
+  node->rear_advance = rear_advance;
+  node->data = data;
+}
+
+/* Return NODE's begin value, computing it if necessary. */
+
+ptrdiff_t
+itree_node_begin (struct itree_tree *tree,
+                 struct itree_node *node)
+{
+  interval_tree_validate (tree, node);
+  return node->begin;
+}
+
+/* Return NODE's end value, computing it if necessary. */
+
+ptrdiff_t
+itree_node_end (struct itree_tree *tree,
+               struct itree_node *node)
+{
+  interval_tree_validate (tree, node);
+  return node->end;
+}
+
+/* Allocate an itree_tree.  Free with itree_destroy.  */
+
+struct itree_tree *
+itree_create (void)
+{
+  struct itree_tree *tree = xmalloc (sizeof (*tree));
+  itree_clear (tree);
+  return tree;
+}
+
+/* Reset the tree TREE to its empty state.  */
+
+void
+itree_clear (struct itree_tree *tree)
+{
+  tree->root = NULL;
+  tree->otick = 1;
+  tree->size = 0;
+}
+
+#ifdef ITREE_TESTING
+/* Initialize a pre-allocated tree (presumably on the stack).  */
+
+static void
+interval_tree_init (struct itree_tree *tree)
+{
+  itree_clear (tree);
+}
+#endif
+
+/* Release a tree, freeing its allocated memory.  */
+void
+itree_destroy (struct itree_tree *tree)
+{
+  eassert (tree->root == NULL);
+  xfree (tree);
+}
+
+/* Return the number of nodes in TREE.  */
+
+intmax_t
+itree_size (struct itree_tree *tree)
+{
+  return tree->size;
+}
+
+/* Perform the familiar left-rotation on node NODE.  */
+
+static void
+interval_tree_rotate_left (struct itree_tree *tree,
+                          struct itree_node *node)
+{
+  eassert (node->right != NULL);
+
+  struct itree_node *right = node->right;
+
+  interval_tree_inherit_offset (tree->otick, node);
+  interval_tree_inherit_offset (tree->otick, right);
+
+  /* Turn right's left subtree into node's right subtree.  */
+  node->right = right->left;
+  if (right->left != NULL)
+    right->left->parent = node;
+
+  /* right's parent was node's parent.  */
+  if (right != NULL)
+    right->parent = node->parent;
+
+  /* Get the parent to point to right instead of node.  */
+  if (node != tree->root)
+    {
+      if (node == node->parent->left)
+       node->parent->left = right;
+      else
+       node->parent->right = right;
+    }
+  else
+    tree->root = right;
+
+  /* Put node on right's left.  */
+  right->left = node;
+  if (node != NULL)
+    node->parent = right;
+
+  /* Order matters here.  */
+  interval_tree_update_limit (node);
+  interval_tree_update_limit (right);
+}
+
+/* Perform the familiar right-rotation on node NODE.  */
+
+static void
+interval_tree_rotate_right (struct itree_tree *tree,
+                           struct itree_node *node)
+{
+  eassert (tree && node && node->left != NULL);
+
+  struct itree_node *left = node->left;
+
+  interval_tree_inherit_offset (tree->otick, node);
+  interval_tree_inherit_offset (tree->otick, left);
+
+  node->left = left->right;
+  if (left->right != NULL)
+    left->right->parent = node;
+
+  if (left != NULL)
+    left->parent = node->parent;
+  if (node != tree->root)
+    {
+      if (node == node->parent->right)
+       node->parent->right = left;
+      else
+       node->parent->left = left;
+    }
+  else
+    tree->root = left;
+
+  left->right = node;
+  if (node != NULL)
+    node->parent = left;
+
+  interval_tree_update_limit (left);
+  interval_tree_update_limit (node);
+}
+
+/* Repair the tree after an insertion.
+   The new NODE was added as red, so we may have 2 reds in a row.
+   Rebalance the parents as needed to re-establish the RB invariants.  */
+
+static void
+interval_tree_insert_fix (struct itree_tree *tree,
+                         struct itree_node *node)
+{
+  eassert (tree->root->red == false);
+
+  while (null_safe_is_red (node->parent))
+    {
+      /* NODE is red and its parent is red.  This is a violation of
+        red-black tree property #3.  */
+      eassert (node->red);
+
+      if (node->parent == node->parent->parent->left)
+       {
+         /* We're on the left side of our grandparent, and OTHER is
+            our "uncle".  */
+         struct itree_node *uncle = node->parent->parent->right;
+
+         if (null_safe_is_red (uncle)) /* case 1.a */
+           {
+             /* Uncle and parent are red but should be black because
+                NODE is red.  Change the colors accordingly and
+                proceed with the grandparent.  */
+             node->parent->red = false;
+             uncle->red = false;
+             node->parent->parent->red = true;
+             node = node->parent->parent;
+           }
+         else
+           {
+             /* Parent and uncle have different colors; parent is
+                red, uncle is black.  */
+             if (node == node->parent->right) /* case 2.a */
+               {
+                 node = node->parent;
+                 interval_tree_rotate_left (tree, node);
+               }
+             /* case 3.a */
+             node->parent->red = false;
+             node->parent->parent->red = true;
+             interval_tree_rotate_right (tree, node->parent->parent);
+           }
+       }
+      else
+       {
+         /* This is the symmetrical case of above.  */
+         struct itree_node *uncle = node->parent->parent->left;
+
+         if (null_safe_is_red (uncle)) /* case 1.b */
+           {
+             node->parent->red = false;
+             uncle->red = false;
+             node->parent->parent->red = true;
+             node = node->parent->parent;
+           }
+         else
+           {
+             if (node == node->parent->left) /* case 2.b */
+               {
+                 node = node->parent;
+                 interval_tree_rotate_right (tree, node);
+               }
+             /* case 3.b */
+             node->parent->red = false;
+             node->parent->parent->red = true;
+             interval_tree_rotate_left (tree, node->parent->parent);
+           }
+       }
+    }
+
+  /* The root may have been changed to red due to the algorithm.
+     Set it to black so that property #5 is satisfied.  */
+  tree->root->red = false;
+  eassert (check_tree (tree, true)); /* FIXME: Too expensive.  */
+}
+
+/* Insert a NODE into the TREE.
+   Note, that inserting a node twice results in undefined behavior.  */
+
+static void
+interval_tree_insert (struct itree_tree *tree, struct itree_node *node)
+{
+  eassert (node && node->begin <= node->end);
+  /* FIXME: The assertion below fails because `delete_all_overlays`
+     doesn't set left/right/parent to NULL.  */
+  /* eassert (node->left == NULL && node->right == NULL
+            && node->parent == NULL) */;
+  eassert (check_tree (tree, true)); /* FIXME: Too expensive.  */
+
+  struct itree_node *parent = NULL;
+  struct itree_node *child = tree->root;
+  uintmax_t otick = tree->otick;
+  /* It's the responsibility of the caller to set `otick` on the node,
+     to "confirm" that the begin/end fields are up to date.  */
+  eassert (node->otick == otick);
+
+  /* Find the insertion point, accumulate node's offset and update
+     ancestors limit values.  */
+  while (child != NULL)
+    {
+      interval_tree_inherit_offset (otick, child);
+      parent = child;
+      eassert (child->offset == 0);
+      child->limit = max (child->limit, node->end);
+      /* This suggests that nodes in the right subtree are strictly
+        greater.  But this is not true due to later rotations.  */
+      child = node->begin <= child->begin ? child->left : child->right;
+    }
+
+  /* Insert the node */
+  if (parent == NULL)
+    tree->root = node;
+  else if (node->begin <= parent->begin)
+    parent->left = node;
+  else
+    parent->right = node;
+
+  /* Init the node */
+  node->parent = parent;
+  node->left = NULL;
+  node->right = NULL;
+  node->offset = 0;
+  node->limit = node->end;
+  eassert (node->parent == NULL || node->parent->otick >= node->otick);
+
+  /* Fix/update the tree */
+  ++tree->size;
+  if (node == tree->root)
+    node->red = false;
+  else
+    {
+      node->red = true;
+      eassert (check_tree (tree, false)); /* FIXME: Too expensive.  */
+      interval_tree_insert_fix (tree, node);
+    }
+}
+
+void
+itree_insert (struct itree_tree *tree, struct itree_node *node,
+             ptrdiff_t begin, ptrdiff_t end)
+{
+  node->begin = begin;
+  node->end = end;
+  node->otick = tree->otick;
+  interval_tree_insert (tree, node);
+}
+
+/* Safely modify a node's interval. */
+
+void
+itree_node_set_region (struct itree_tree *tree,
+                      struct itree_node *node,
+                      ptrdiff_t begin, ptrdiff_t end)
+{
+  interval_tree_validate (tree, node);
+  if (begin != node->begin)
+    {
+      itree_remove (tree, node);
+      node->begin = min (begin, PTRDIFF_MAX - 1);
+      node->end = max (node->begin, end);
+      interval_tree_insert (tree, node);
+    }
+  else if (end != node->end)
+    {
+      node->end = max (node->begin, end);
+      eassert (node != NULL);
+      interval_tree_propagate_limit (node);
+    }
+}
+
+/* Return true, if NODE is a member of TREE. */
+
+static bool
+interval_tree_contains (struct itree_tree *tree, struct itree_node *node)
+{
+  eassert (iter && node);
+  struct itree_node *other;
+  ITREE_FOREACH (other, tree, node->begin, PTRDIFF_MAX, ASCENDING)
+    if (other == node)
+      {
+       ITREE_FOREACH_ABORT ();
+       return true;
+      }
+
+  return false;
+}
+
+static bool
+itree_limit_is_stable (struct itree_node *node)
+{
+  if (node == NULL)
+    return true;
+  ptrdiff_t newlimit = itree_newlimit (node);
+  return (newlimit == node->limit);
+}
+
+static struct itree_node*
+interval_tree_subtree_min (uintmax_t otick, struct itree_node *node)
+{
+  if (node == NULL)
+    return node;
+  while ((interval_tree_inherit_offset (otick, node),
+         node->left != NULL))
+    node = node->left;
+  return node;
+}
+
+/* Repair the tree after a deletion.
+   The black-depth of NODE is one less than that of its sibling,
+   so re-balance the parents to re-establish the RB invariants.  */
+
+static void
+interval_tree_remove_fix (struct itree_tree *tree,
+                         struct itree_node *node,
+                         struct itree_node *parent)
+{
+  if (parent == NULL)
+    eassert (node == tree->root);
+  else
+    eassert (node == NULL || node->parent == parent);
+
+  while (parent != NULL && null_safe_is_black (node))
+    {
+      eassert (node == parent->left || node == parent->right);
+
+      if (node == parent->left)
+       {
+         struct itree_node *other = parent->right;
+
+         if (null_safe_is_red (other)) /* case 1.a */
+           {
+             other->red = false;
+             parent->red = true;
+             interval_tree_rotate_left (tree, parent);
+             other = parent->right;
+           }
+         eassume (other != NULL);
+
+         if (null_safe_is_black (other->left) /* 2.a */
+             && null_safe_is_black (other->right))
+           {
+             other->red = true;
+             node = parent;
+             eassert (node != NULL);
+             parent = node->parent;
+           }
+         else
+           {
+             if (null_safe_is_black (other->right)) /* 3.a */
+               {
+                 other->left->red = false;
+                 other->red = true;
+                 interval_tree_rotate_right (tree, other);
+                 other = parent->right;
+               }
+             other->red = parent->red; /* 4.a */
+             parent->red = false;
+             other->right->red = false;
+             interval_tree_rotate_left (tree, parent);
+             node = tree->root;
+             parent = NULL;
+           }
+       }
+      else
+       {
+         struct itree_node *other = parent->left;
+
+         if (null_safe_is_red (other)) /* 1.b */
+           {
+             other->red = false;
+             parent->red = true;
+             interval_tree_rotate_right (tree, parent);
+             other = parent->left;
+           }
+         eassume (other != NULL);
+
+         if (null_safe_is_black (other->right) /* 2.b */
+             && null_safe_is_black (other->left))
+           {
+             other->red = true;
+             node = parent;
+             eassert (node != NULL);
+             parent = node->parent;
+           }
+         else
+           {
+             if (null_safe_is_black (other->left)) /* 3.b */
+               {
+                 other->right->red = false;
+                 other->red = true;
+                 interval_tree_rotate_left (tree, other);
+                 other = parent->left;
+               }
+
+             other->red = parent->red; /* 4.b */
+             parent->red = false;
+             other->left->red = false;
+             interval_tree_rotate_right (tree, parent);
+             node = tree->root;
+             parent = NULL;
+           }
+       }
+    }
+
+  if (node != NULL)
+    node->red = false;
+}
+
+/* Return accumulated offsets of NODE's parents.  */
+static ptrdiff_t
+itree_total_offset (struct itree_node *node)
+{
+  eassert (node != NULL);
+  ptrdiff_t offset = 0;
+  while (node->parent != NULL)
+    {
+      node = node->parent;
+      offset += node->offset;
+    }
+  return offset;
+}
+
+/* Replace DEST with SOURCE as a child of DEST's parent.  Adjusts
+   *only* the parent linkage of SOURCE and either the parent's child
+   link the tree root.
+
+   Warning: DEST is left unmodified.  SOURCE's child links are
+   unchanged.  Caller is responsible for recalculation of `limit`.
+   Requires both nodes to be using the same effective `offset`.  */
+static void
+interval_tree_replace_child (struct itree_tree *tree,
+                            struct itree_node *source,
+                            struct itree_node *dest)
+{
+  eassert (tree && dest != NULL);
+  eassert (source == NULL
+          || itree_total_offset (source) == itree_total_offset (dest));
+
+  if (dest == tree->root)
+    tree->root = source;
+  else if (dest == dest->parent->left)
+    dest->parent->left = source;
+  else
+    dest->parent->right = source;
+
+  if (source != NULL)
+    source->parent = dest->parent;
+}
+/* Replace DEST with SOURCE in the tree.  Copies the following fields
+   from DEST to SOURCE: red, parent, left, right.  Also updates
+   parent, left and right in surrounding nodes to point to SOURCE.
+
+   Warning: DEST is left unmodified.  Caller is responsible for
+   recalculation of `limit`.  Requires both nodes to be using the same
+   effective `offset`. */
+static void
+interval_tree_transplant (struct itree_tree *tree,
+                         struct itree_node *source,
+                         struct itree_node *dest)
+{
+  interval_tree_replace_child (tree, source, dest);
+  source->left = dest->left;
+  if (source->left != NULL)
+    source->left->parent = source;
+  source->right = dest->right;
+  if (source->right != NULL)
+    source->right->parent = source;
+  source->red = dest->red;
+}
+
+/* Remove NODE from TREE and return it.  NODE must exist in TREE.  */
+
+struct itree_node*
+itree_remove (struct itree_tree *tree, struct itree_node *node)
+{
+  eassert (interval_tree_contains (tree, node));
+  eassert (check_tree (tree, true)); /* FIXME: Too expensive.  */
+
+  /* Find `splice`, the leaf node to splice out of the tree.  When
+     `node` has at most one child this is `node` itself.  Otherwise,
+     it is the in order successor of `node`.  */
+  interval_tree_inherit_offset (tree->otick, node);
+  struct itree_node *splice
+    = (node->left == NULL || node->right == NULL)
+       ? node
+       : interval_tree_subtree_min (tree->otick, node->right);
+
+  /* Find `subtree`, the only child of `splice` (may be NULL).  Note:
+     `subtree` will not be modified other than changing its parent to
+     `splice`.  */
+  eassert (splice->left == NULL || splice->right == NULL);
+  struct itree_node *subtree
+    = (splice->left != NULL) ? splice->left : splice->right;
+
+  /* Save a pointer to the parent of where `subtree` will eventually
+     be in `subtree_parent`.  */
+  struct itree_node *subtree_parent
+    = (splice->parent != node) ? splice->parent : splice;
+
+  /* If `splice` is black removing it may violate Red-Black
+     invariants, so note this for later.  */
+
+  /* Replace `splice` with `subtree` under subtree's parent.  If
+     `splice` is black, this creates a red-red violation, so remember
+     this now as the field can be overwritten when splice is
+     transplanted below.  */
+  interval_tree_replace_child (tree, subtree, splice);
+  bool removed_black = !splice->red;
+
+  /* Replace `node` with `splice` in the tree and propagate limit
+     upwards, if necessary.  Note: Limit propagation can stabilize at
+     any point, so we must call from bottom to top for every node that
+     has a new child.  */
+  if (splice != node)
+    {
+      interval_tree_transplant (tree, splice, node);
+      interval_tree_propagate_limit (subtree_parent);
+      if (splice != subtree_parent)
+       interval_tree_update_limit (splice);
+    }
+  interval_tree_propagate_limit (splice->parent);
+
+  --tree->size;
+
+  /* Fix any black height violation caused by removing a black node.  */
+  if (removed_black)
+    interval_tree_remove_fix (tree, subtree, subtree_parent);
+
+  eassert ((tree->size == 0) == (tree->root == NULL));
+  eassert (check_tree (tree, true)); /* FIXME: Too expensive.  */
+
+  /* Clear fields related to the tree for sanity while debugging.  */
+  node->red = false;
+  node->right = node->left = node->parent = NULL;
+  node->limit = 0;
+
+  /* Must be clean (all offsets applied).  Also, some callers rely on
+     node's otick being the tree's otick.  */
+  eassert (node->otick == tree->otick);
+  eassert (node->offset == 0);
+
+  return node;
+}
+
+bool
+itree_iterator_busy_p (void)
+{
+  return (iter && iter->running);
+}
+
+/* Start a iterator enumerating all intervals in [BEGIN,END) in the
+   given ORDER.  Only one iterator per tree can be running at any time.  */
+
+struct itree_iterator *
+itree_iterator_start (struct itree_tree *tree, ptrdiff_t begin,
+                     ptrdiff_t end, enum itree_order order,
+                     const char *file, int line)
+{
+  eassert (iter);
+  if (iter->running)
+    {
+      fprintf (stderr,
+              "Detected nested iteration!\nOuter: %s:%d\nInner: %s:%d\n",
+              iter->file, iter->line, file, line);
+      emacs_abort ();
+    }
+  iter->begin = begin;
+  iter->end = end;
+  iter->otick = tree->otick;
+  iter->order = order;
+  interval_stack_clear (iter->stack);
+  if (begin <= end && tree->root != NULL)
+    interval_stack_push_flagged (iter->stack, tree->root, false);
+  iter->file = file;
+  iter->line = line;
+  iter->running = true;
+  /* interval_stack_ensure_space (iter->stack,
+                                 2 * interval_tree_max_height (tree)); */
+  return iter;
+}
+
+/* Stop using the iterator. */
+
+void
+itree_iterator_finish (struct itree_iterator *iter)
+{
+  eassert (iter && iter->running);
+  iter->running = false;
+}
+
+
+/* +=======================================================================+
+ * | Insert/Delete Gaps
+ * +=======================================================================+ */
+
+/* Insert a gap at POS of length LENGTH expanding all intervals
+   intersecting it, while respecting their rear_advance and
+   front_advance setting.
+
+   If BEFORE_MARKERS is non-zero, all overlays beginning/ending at POS
+   are treated as if their front_advance/rear_advance was true. */
+
+void
+itree_insert_gap (struct itree_tree *tree,
+                 ptrdiff_t pos, ptrdiff_t length, bool before_markers)
+{
+  if (!tree || length <= 0 || tree->root == NULL)
+    return;
+  uintmax_t ootick = tree->otick;
+
+  /* FIXME: Don't allocate iterator/stack anew every time. */
+
+  /* Nodes with front_advance starting at pos may mess up the tree
+     order, so we need to remove them first.  This doesn't apply for
+     `before_markers` since in that case, all positions move identically
+     regardless of `front_advance` or `rear_advance`.  */
+  struct interval_stack *saved = interval_stack_create (0);
+  struct itree_node *node = NULL;
+  if (!before_markers)
+    {
+      ITREE_FOREACH (node, tree, pos, pos + 1, PRE_ORDER)
+       {
+         if (node->begin == pos && node->front_advance
+             /* If we have front_advance and !rear_advance and
+                the overlay is empty, make sure we don't move
+                begin past end by pretending it's !front_advance.  */
+             && (node->begin != node->end || node->rear_advance))
+           interval_stack_push (saved, node);
+       }
+    }
+  for (size_t i = 0; i < saved->length; ++i)
+    itree_remove (tree, nav_nodeptr (saved->nodes[i]));
+
+  /* We can't use an iterator here, because we can't effectively
+     narrow AND shift some subtree at the same time.  */
+  if (tree->root != NULL)
+    {
+      const int size = interval_tree_max_height (tree) + 1;
+      struct interval_stack *stack = interval_stack_create (size);
+      interval_stack_push (stack, tree->root);
+      nodeptr_and_flag nav;
+      while ((nav = interval_stack_pop (stack),
+             node = nav_nodeptr (nav)))
+       {
+         /* Process in pre-order. */
+         interval_tree_inherit_offset (tree->otick, node);
+         if (pos > node->limit)
+           continue;
+         if (node->right != NULL)
+           {
+             if (node->begin > pos)
+               {
+                 /* All nodes in this subtree are shifted by length.  */
+                 node->right->offset += length;
+                 ++tree->otick;
+               }
+             else
+               interval_stack_push (stack, node->right);
+           }
+         if (node->left != NULL)
+           interval_stack_push (stack, node->left);
+
+         if (before_markers
+             ? node->begin >= pos
+             : node->begin > pos) /* node->begin == pos => !front-advance  */
+           node->begin += length;
+         if (node->end > pos
+             || (node->end == pos && (before_markers || node->rear_advance)))
+           {
+             node->end += length;
+             eassert (node != NULL);
+             interval_tree_propagate_limit (node);
+           }
+       }
+      interval_stack_destroy (stack);
+    }
+
+  /* Reinsert nodes starting at POS having front-advance.  */
+  uintmax_t notick = tree->otick;
+  nodeptr_and_flag nav;
+  while ((nav = interval_stack_pop (saved),
+         node = nav_nodeptr (nav)))
+    {
+      eassert (node->otick == ootick);
+      eassert (node->begin == pos);
+      eassert (node->end > pos || node->rear_advance);
+      node->begin += length;
+      node->end += length;
+      node->otick = notick;
+      interval_tree_insert (tree, node);
+    }
+
+  interval_stack_destroy (saved);
+}
+
+/* Delete a gap at POS of length LENGTH, contracting all intervals
+   intersecting it.  */
+
+void
+itree_delete_gap (struct itree_tree *tree,
+                 ptrdiff_t pos, ptrdiff_t length)
+{
+  if (!tree || length <= 0 || tree->root == NULL)
+    return;
+
+  /* FIXME: Don't allocate stack anew every time.  */
+
+  /* Can't use the iterator here, because by decrementing begin, we
+     might unintentionally bring shifted nodes back into our search space.  */
+  const int size = interval_tree_max_height (tree) + 1;
+  struct interval_stack *stack = interval_stack_create (size);
+  struct itree_node *node;
+
+  interval_stack_push (stack, tree->root);
+  nodeptr_and_flag nav;
+  while ((nav = interval_stack_pop (stack)))
+    {
+      node = nav_nodeptr (nav);
+      interval_tree_inherit_offset (tree->otick, node);
+      if (pos > node->limit)
+       continue;
+      if (node->right != NULL)
+       {
+         if (node->begin > pos + length)
+           {
+             /* Shift right subtree to the left. */
+             node->right->offset -= length;
+             ++tree->otick;
+           }
+         else
+           interval_stack_push (stack, node->right);
+       }
+      if (node->left != NULL)
+       interval_stack_push (stack, node->left);
+
+      if (pos < node->begin)
+       node->begin = max (pos, node->begin - length);
+      if (node->end > pos)
+       {
+         node->end = max (pos , node->end - length);
+         eassert (node != NULL);
+         interval_tree_propagate_limit (node);
+       }
+    }
+  interval_stack_destroy (stack);
+}
+
+
+
+/* +=======================================================================+
+ * | Iterator
+ * +=======================================================================+ */
+
+/* Return true, if NODE's interval intersects with [BEGIN, END).
+   Note: We always include empty nodes at BEGIN (and not at END),
+   but if BEGIN==END, then we don't include non-empty nodes starting
+   at BEGIN or ending at END.  This seems to match the behavior of the
+   old overlays code but it's not clear if it's The Right Thing
+   (e.g. it breaks the expectation that if NODE1 is included, then
+   a NODE2 strictly bigger than NODE1 should also be included).  */
+
+static inline bool
+interval_node_intersects (const struct itree_node *node,
+                         ptrdiff_t begin, ptrdiff_t end)
+{
+  return (begin < node->end && node->begin < end)
+    || (node->begin == node->end && begin == node->begin);
+}
+
+/* Return the next node of the iterator in the order given when it was
+   started; or NULL if there are no more nodes. */
+
+struct itree_node *
+itree_iterator_next (struct itree_iterator *g)
+{
+  eassert (g && g->running);
+
+  struct itree_node *const null = NULL;
+  struct itree_node *node;
+
+  /* The `visited` flag stored in each node is used here (and only here):
+     We keep a "workstack" of nodes we need to consider.  This stack
+     consist of nodes of two types: nodes that we have decided
+     should be returned by the iterator, and nodes which we may
+     need to consider (including checking their children).
+     We start an iteration with a stack containing just the root
+     node marked as "not visited" which means that it (and its children)
+     needs to be considered but we haven't yet decided whether it's included
+     in the iterator's output.  */
+
+  do
+    {
+      nodeptr_and_flag nav;
+      bool visited;
+      while ((nav = interval_stack_pop (g->stack),
+             node = nav_nodeptr (nav),
+             visited = nav_flag (nav),
+             node && !visited))
+       {
+         struct itree_node *const left = node->left;
+         struct itree_node *const right = node->right;
+
+         interval_tree_inherit_offset (g->otick, node);
+         eassert (itree_limit_is_stable (node));
+         switch (g->order)
+           {
+           case ITREE_ASCENDING:
+             if (right != null && node->begin <= g->end)
+               interval_stack_push_flagged (g->stack, right, false);
+             if (interval_node_intersects (node, g->begin, g->end))
+               interval_stack_push_flagged (g->stack, node, true);
+             /* Node's children may still be off-set and we need to add it.  */
+             if (left != null && g->begin <= left->limit + left->offset)
+               interval_stack_push_flagged (g->stack, left, false);
+             break;
+           case ITREE_DESCENDING:
+             if (left != null && g->begin <= left->limit + left->offset)
+               interval_stack_push_flagged (g->stack, left, false);
+             if (interval_node_intersects (node, g->begin, g->end))
+               interval_stack_push_flagged (g->stack, node, true);
+             if (right != null && node->begin <= g->end)
+               interval_stack_push_flagged (g->stack, right, false);
+             break;
+           case ITREE_PRE_ORDER:
+             if (right != null && node->begin <= g->end)
+               interval_stack_push_flagged (g->stack, right, false);
+             if (left != null && g->begin <= left->limit + left->offset)
+               interval_stack_push_flagged (g->stack, left, false);
+             if (interval_node_intersects (node, g->begin, g->end))
+               interval_stack_push_flagged (g->stack, node, true);
+             break;
+           }
+       }
+      /* Node may have been invalidated by itree_iterator_narrow
+        after it was pushed: Check if it still intersects. */
+    } while (node && ! interval_node_intersects (node, g->begin, g->end));
+
+  return node;
+}
+
+/* Limit G to the new interval [BEGIN, END), which must be a subset of
+   the current one.  I.E. it can't grow on either side. */
+
+void
+itree_iterator_narrow (struct itree_iterator *g,
+                      ptrdiff_t begin, ptrdiff_t end)
+{
+  eassert (g && g->running);
+  eassert (begin >= g->begin);
+  eassert (end <= g->end);
+  g->begin = max (begin, g->begin);
+  g->end = min (end, g->end);
+}
diff --git a/src/itree.h b/src/itree.h
new file mode 100644
index 0000000000..10ee0897c3
--- /dev/null
+++ b/src/itree.h
@@ -0,0 +1,186 @@
+/* This file implements an efficient interval data-structure.
+
+Copyright (C) 2017-2022  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 <http://www.gnu.org/licenses/>.  */
+
+#ifndef ITREE_H
+#define ITREE_H
+#include <config.h>
+#include <stddef.h>
+#include <inttypes.h>
+
+#include "lisp.h"
+
+/* The tree and node structs are mainly here, so they can be
+   allocated.
+
+   NOTE: The only time where it is safe to modify node.begin and
+   node.end directly, is while the node is not part of any tree.
+
+   NOTE: It is safe to read node.begin and node.end directly, if the
+   node came from an iterator, because it validates the nodes it
+   returns as a side-effect.  See ITREE_FOREACH.
+ */
+
+struct itree_node
+{
+  /* The normal parent, left and right links found in binary trees.
+     See also `red`, below, which completes the Red-Black tree
+     representation.  */
+  struct itree_node *parent;
+  struct itree_node *left;
+  struct itree_node *right;
+
+  /* The following five fields comprise the interval abstraction.
+
+     BEGIN, END are buffer positions describing the range.  When a
+     node is in a tree these fields are read only, written only by
+     itree functions.
+
+     The LIMIT, OFFSET and OTICK fields should be considered internal
+     to itree.c and used only by itree functions.
+
+     LIMIT is a buffer position, the maximum of END of this node and
+     its children.  See itree.c for its use.
+
+     OFFSET is in buffer position units, and will be non-zero only
+     when the node is dirty.
+
+     OTICK determines whether BEGIN, END, LIMIT and OFFSET are
+     considered dirty.  A node is clean when its OTICK is equal to the
+     OTICK of its tree (see struct itree_tree).  Otherwise, it is
+     dirty.
+
+     In a clean node, BEGIN, END and LIMIT are correct buffer
+     positions, and OFFSET is zero.  The parent of a clean node is
+     also clean, recursively.
+
+     In a dirty node, the node's OTICK won't equal its tree's OTICK,
+     and its OFFSET may be non-zero.  At all times the descendents of
+     a dirty node are also dirty.  BEGIN, END and LIMIT require
+     adjustment before use as buffer positions.
+
+     NOTE: BEGIN and END must not be modified while the node is part
+     of a tree.  Use itree_insert_gap and itree_delete_gap instead.
+
+     NOTE: The interval iterators ensure nodes are clean before
+     yielding them, so BEGIN and END may be safely used as buffer
+     positions then.  */
+
+  ptrdiff_t begin;             /* The beginning of this interval. */
+  ptrdiff_t end;               /* The end of the interval. */
+  ptrdiff_t limit;             /* The maximum end in this subtree. */
+  ptrdiff_t offset;            /* The amount of shift to apply to this 
subtree. */
+  uintmax_t otick;              /* offset modified tick */
+  Lisp_Object data;             /* Exclusively used by the client. */
+  bool_bf red : 1;
+  bool_bf rear_advance : 1;     /* Same as for marker and overlays.  */
+  bool_bf front_advance : 1;    /* Same as for marker and overlays.  */
+};
+
+struct itree_tree
+{
+  struct itree_node *root;
+  uintmax_t otick;              /* offset tick, compared with node's otick. */
+  intmax_t size;                /* Number of nodes in the tree. */
+};
+
+enum itree_order
+  {
+    ITREE_ASCENDING,
+    ITREE_DESCENDING,
+    ITREE_PRE_ORDER,
+  };
+
+extern void init_itree (void);
+#ifdef HAVE_UNEXEC
+extern void forget_itree (void);
+#endif
+extern void itree_node_init (struct itree_node *, bool, bool, Lisp_Object);
+extern ptrdiff_t itree_node_begin (struct itree_tree *, struct itree_node *);
+extern ptrdiff_t itree_node_end (struct itree_tree *, struct itree_node *);
+extern void itree_node_set_region (struct itree_tree *, struct itree_node *,
+                                  ptrdiff_t, ptrdiff_t);
+extern struct itree_tree *itree_create (void);
+extern void itree_destroy (struct itree_tree *);
+extern intmax_t itree_size (struct itree_tree *);
+extern void itree_clear (struct itree_tree *);
+extern void itree_insert (struct itree_tree *, struct itree_node *,
+                         ptrdiff_t, ptrdiff_t);
+extern struct itree_node *itree_remove (struct itree_tree *,
+                                       struct itree_node *);
+extern void itree_insert_gap (struct itree_tree *, ptrdiff_t, ptrdiff_t, bool);
+extern void itree_delete_gap (struct itree_tree *, ptrdiff_t, ptrdiff_t);
+
+/* Iteration functions.  Almost all code should use ITREE_FOREACH
+   instead.  */
+extern bool itree_iterator_busy_p (void);
+extern struct itree_iterator *itree_iterator_start (struct itree_tree *,
+                                                   ptrdiff_t,
+                                                   ptrdiff_t,
+                                                   enum itree_order,
+                                                   const char *, int);
+extern void itree_iterator_narrow (struct itree_iterator *, ptrdiff_t,
+                                  ptrdiff_t);
+extern void itree_iterator_finish (struct itree_iterator *);
+extern struct itree_node *itree_iterator_next (struct itree_iterator *);
+
+/* Iterate over the intervals between BEG and END in the tree T.
+   N will hold successive nodes.  ORDER can be one of : `ASCENDING`,
+   `DESCENDING`, or `PRE_ORDER`.
+   It should be used as:
+
+      ITREE_FOREACH (n, t, beg, end, order)
+        {
+          .. do the thing with n ..
+        }
+
+   BEWARE:
+   - The expression T may be evaluated more than once, so make sure
+     it is cheap and pure.
+   - Only a single iteration can happen at a time, so make sure none of the
+     code within the loop can start another tree iteration, i.e. it shouldn't
+     be able to run ELisp code, nor GC since GC can run ELisp by way
+     of `post-gc-hook`.
+   - If you need to exit the loop early, you *have* to call `ITREE_ABORT`
+     just before exiting (e.g. with `break` or `return`).
+   - Non-local exits are not supported within the body of the loop.
+   - Don't modify the tree during the iteration.
+ */
+#define ITREE_FOREACH(n, t, beg, end, order)                        \
+  /* FIXME: We'd want to declare `x` right here, but I can't figure out
+     how to make that work here: the `for` syntax only allows a single
+     clause for the var declarations where we need 2 different types.
+     We could use the `struct {foo x; bar y; } p;` trick to declare two
+     vars `p.x` and `p.y` of unrelated types, but then none of the names
+     of the vars matches the `n` we receive :-(.  */                \
+  if (!t)                                                           \
+    { }                                                             \
+  else                                                              \
+    for (struct itree_iterator *itree_iter_                         \
+            = itree_iterator_start (t, beg, end, ITREE_##order,     \
+                                        __FILE__, __LINE__);        \
+          ((n = itree_iterator_next (itree_iter_))                  \
+           || (itree_iterator_finish (itree_iter_), false));)
+
+#define ITREE_FOREACH_ABORT() \
+  itree_iterator_finish (itree_iter_)
+
+#define ITREE_FOREACH_NARROW(beg, end) \
+  itree_iterator_narrow (itree_iter_, beg, end)
+
+#endif
diff --git a/src/keyboard.c b/src/keyboard.c
index 8ab4a451b4..069cf0627b 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -503,9 +503,11 @@ echo_add_key (Lisp_Object c)
   if ((NILP (echo_string) || SCHARS (echo_string) == 0)
       && help_char_p (c))
     {
-      AUTO_STRING (str, " (Type ? for further options)");
+      AUTO_STRING (str, " (Type ? for further options, q for quick help)");
       AUTO_LIST2 (props, Qface, Qhelp_key_binding);
       Fadd_text_properties (make_fixnum (7), make_fixnum (8), props, str);
+      Fadd_text_properties (make_fixnum (30), make_fixnum (31), props,
+str);
       new_string = concat2 (new_string, str);
     }
 
@@ -1268,7 +1270,6 @@ command_loop_1 (void)
 {
   modiff_count prev_modiff = 0;
   struct buffer *prev_buffer = NULL;
-  bool already_adjusted = 0;
 
   kset_prefix_arg (current_kboard, Qnil);
   kset_last_prefix_arg (current_kboard, Qnil);
@@ -1458,8 +1459,6 @@ command_loop_1 (void)
       safe_run_hooks_maybe_narrowed (Qpre_command_hook,
                                     XWINDOW (selected_window));
 
-      already_adjusted = 0;
-
       if (NILP (Vthis_command))
        /* nil means key is undefined.  */
        call0 (Qundefined);
@@ -1615,9 +1614,8 @@ command_loop_1 (void)
                   the automatic composition, we must update the
                   display.  */
                windows_or_buffers_changed = 21;
-             if (!already_adjusted)
-               adjust_point_for_property (last_point_position,
-                                          MODIFF != prev_modiff);
+             adjust_point_for_property (last_point_position,
+                                        MODIFF != prev_modiff);
            }
          else if (PT > BEGV && PT < ZV
                   && (composition_adjust_point (last_point_position, PT)
@@ -1699,8 +1697,8 @@ adjust_point_for_property (ptrdiff_t last_pt, bool 
modified)
          && display_prop_intangible_p (val, overlay, PT, PT_BYTE)
          && (!OVERLAYP (overlay)
              ? get_property_and_range (PT, Qdisplay, &val, &beg, &end, Qnil)
-             : (beg = OVERLAY_POSITION (OVERLAY_START (overlay)),
-                end = OVERLAY_POSITION (OVERLAY_END (overlay))))
+             : (beg = OVERLAY_START (overlay),
+                end = OVERLAY_END (overlay)))
          && (beg < PT /* && end > PT   <- It's always the case.  */
              || (beg <= PT && STRINGP (val) && SCHARS (val) == 0)))
        {
diff --git a/src/lisp.h b/src/lisp.h
index 2029ce8e5c..17485d07a1 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -1,7 +1,6 @@
 /* Fundamental definitions for GNU Emacs Lisp interpreter. -*- coding: utf-8 
-*-
 
-Copyright (C) 1985-1987, 1993-1995, 1997-2022 Free Software Foundation,
-Inc.
+Copyright (C) 1985-2022  Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -645,10 +644,8 @@ extern bool initialized;
 extern struct gflags
 {
   /* True means this Emacs instance was born to dump.  */
-#if defined HAVE_PDUMPER || defined HAVE_UNEXEC
   bool will_dump_ : 1;
   bool will_bootstrap_ : 1;
-#endif
 #ifdef HAVE_PDUMPER
   /* Set in an Emacs process that will likely dump with pdumper; all
      Emacs processes may dump with pdumper, however.  */
@@ -2640,10 +2637,9 @@ struct Lisp_Overlay
 */
   {
     union vectorlike_header header;
-    Lisp_Object start;
-    Lisp_Object end;
     Lisp_Object plist;
-    struct Lisp_Overlay *next;
+    struct buffer *buffer;        /* eassert (live buffer || NULL). */
+    struct itree_node *interval;
   } GCALIGNED_STRUCT;
 
 struct Lisp_Misc_Ptr
@@ -3219,10 +3215,11 @@ CHECK_SUBR (Lisp_Object x)
  `minargs' should be a number, the minimum number of arguments allowed.
  `maxargs' should be a number, the maximum number of arguments allowed,
     or else MANY or UNEVALLED.
-    MANY means pass a vector of evaluated arguments,
-        in the form of an integer number-of-arguments
-        followed by the address of a vector of Lisp_Objects
-        which contains the argument values.
+    MANY means there are &rest arguments.  Here we pass a vector
+        of evaluated arguments in the form of an integer
+        number-of-arguments followed by the address of a vector of
+        Lisp_Objects which contains the argument values.  (We also use
+        this convention when calling a subr with more than 8 parameters.)
     UNEVALLED means pass the list of unevaluated arguments
  `intspec' says how interactive arguments are to be fetched.
     If the string starts with a `(', `intspec' is evaluated and the resulting
@@ -4454,7 +4451,6 @@ extern Lisp_Object make_float (double);
 extern void display_malloc_warning (void);
 extern specpdl_ref inhibit_garbage_collection (void);
 extern Lisp_Object build_symbol_with_pos (Lisp_Object, Lisp_Object);
-extern Lisp_Object build_overlay (Lisp_Object, Lisp_Object, Lisp_Object);
 extern void free_cons (struct Lisp_Cons *);
 extern void init_alloc_once (void);
 extern void init_alloc (void);
@@ -4726,7 +4722,7 @@ extern void syms_of_editfns (void);
 extern bool mouse_face_overlay_overlaps (Lisp_Object);
 extern Lisp_Object disable_line_numbers_overlay_at_eob (void);
 extern AVOID nsberror (Lisp_Object);
-extern void adjust_overlays_for_insert (ptrdiff_t, ptrdiff_t);
+extern void adjust_overlays_for_insert (ptrdiff_t, ptrdiff_t, bool);
 extern void adjust_overlays_for_delete (ptrdiff_t, ptrdiff_t);
 extern void fix_start_end_in_overlays (ptrdiff_t, ptrdiff_t);
 extern void report_overlay_modification (Lisp_Object, Lisp_Object, bool,
@@ -4951,6 +4947,7 @@ extern bool running_asynch_code;
 
 /* Defined in process.c.  */
 struct Lisp_Process;
+extern void child_signal_init (void);
 extern void kill_buffer_processes (Lisp_Object);
 extern int wait_reading_process_output (intmax_t, int, int, bool, Lisp_Object,
                                        struct Lisp_Process *, int);
diff --git a/src/lread.c b/src/lread.c
index dfa4d9afb5..c28324dc35 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -1236,7 +1236,8 @@ Return t if the file exists and loads successfully.  */)
   /* If file name is magic, call the handler.  */
   handler = Ffind_file_name_handler (file, Qload);
   if (!NILP (handler))
-    return call5 (handler, Qload, file, noerror, nomessage, nosuffix);
+    return
+      call6 (handler, Qload, file, noerror, nomessage, nosuffix, must_suffix);
 
   /* The presence of this call is the result of a historical accident:
      it used to be in every file-operation and when it got removed
@@ -5619,7 +5620,8 @@ from the file, and matches them against this regular 
expression.
 When the regular expression matches, the file is considered to be safe
 to load.  */);
   Vbytecomp_version_regexp
-    = build_pure_c_string ("^;;;.\\(in Emacs version\\|bytecomp version 
FSF\\)");
+    = build_pure_c_string
+        ("^;;;.\\(?:in Emacs version\\|bytecomp version FSF\\)");
 
   DEFSYM (Qlexical_binding, "lexical-binding");
   DEFVAR_LISP ("lexical-binding", Vlexical_binding,
diff --git a/src/menu.c b/src/menu.c
index c52e9258a1..a8cb942a19 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -24,6 +24,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include "lisp.h"
 #include "character.h"
 #include "coding.h"
+#include "dispextern.h"
 #include "keyboard.h"
 #include "keymap.h"
 #include "frame.h"
@@ -1391,6 +1392,17 @@ x_popup_menu_1 (Lisp_Object position, Lisp_Object menu)
 
   run_hook (Qx_pre_popup_menu_hook);
 
+#ifdef HAVE_WINDOW_SYSTEM
+  /* Cancel the hourglass timer.  Depending on how the show_menu_hook
+     is implemented, the hourglass window can either be mapped (or on
+     non-X systems, the hourglass cursor can be defined) either while
+     the menu is active, or while it is deactivated.  Both situations
+     lead to annoying cursor and/or screen flicker and a failure to
+     detect input immediately after a popup menu generated by Custom
+     is unmapped.  */
+  cancel_hourglass ();
+#endif
+
   /* Display them in a menu, but not if F is the initial frame that
      doesn't have its hooks set (e.g., in a batch session), because
      such a frame cannot display menus.  */
diff --git a/src/msdos.h b/src/msdos.h
index 24697bcf24..1b304cf02b 100644
--- a/src/msdos.h
+++ b/src/msdos.h
@@ -123,7 +123,6 @@ extern void x_set_menu_bar_lines (struct frame *, 
Lisp_Object, Lisp_Object);
 #define XGetGeometry(p1,p2,p3,p4,p5,p6,p7,p8,p9)
 #define DisplayWidth(p1,p2) (SELECTED_FRAME()->text_cols)
 #define DisplayHeight(p1,p2) (SELECTED_FRAME()->text_lines)
-#define XMenuSetAEQ (void)
 #define XMenuSetFreeze (void)
 #define XMenuRecompute (void)
 #define XM_FAILURE -1
diff --git a/src/nsimage.m b/src/nsimage.m
index 9cb5090dd0..dd8768664a 100644
--- a/src/nsimage.m
+++ b/src/nsimage.m
@@ -74,8 +74,10 @@ ns_can_use_native_image_api (Lisp_Object type)
     imageType = @"com.compuserve.gif";
   else if (EQ (type, Qtiff))
     imageType = @"public.tiff";
+#ifndef HAVE_RSVG
   else if (EQ (type, Qsvg))
     imageType = @"public.svg-image";
+#endif
   else if (EQ (type, Qheic))
     imageType = @"public.heic";
 
diff --git a/src/nsterm.m b/src/nsterm.m
index 1fc72d83f6..507f2a9e7d 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -4237,7 +4237,12 @@ ns_draw_glyphless_glyph_string_foreground (struct 
glyph_string *s)
 
   for (i = 0; i < s->nchars; i++, glyph++)
     {
-      char buf[7];
+#ifdef GCC_LINT
+      enum { PACIFY_GCC_BUG_81401 = 1 };
+#else
+      enum { PACIFY_GCC_BUG_81401 = 0 };
+#endif
+      char buf[8 + PACIFY_GCC_BUG_81401];
       char *str = NULL;
       int len = glyph->u.glyphless.len;
 
@@ -4263,7 +4268,7 @@ ns_draw_glyphless_glyph_string_foreground (struct 
glyph_string *s)
        {
          unsigned int ch = glyph->u.glyphless.ch;
          eassume (ch <= MAX_CHAR);
-         snprintf (buf, 7, "%0*X", ch < 0x10000 ? 4 : 6, ch);
+         snprintf (buf, 8, "%0*X", ch < 0x10000 ? 4 : 6, ch);
          str = buf;
        }
 
@@ -7051,6 +7056,36 @@ ns_create_font_panel_buttons (id target, SEL select, SEL 
cancel_action)
   processingCompose = NO;
 }
 
+static Lisp_Object
+ns_in_echo_area_1 (void *ptr)
+{
+  Lisp_Object in_echo_area;
+  specpdl_ref count;
+
+  count = SPECPDL_INDEX ();
+  specbind (Qinhibit_quit, Qt);
+  in_echo_area = safe_call (1, Qns_in_echo_area);
+
+  return unbind_to (count, in_echo_area);
+}
+
+static Lisp_Object
+ns_in_echo_area_2 (enum nonlocal_exit exit, Lisp_Object error)
+{
+  return Qnil;
+}
+
+static bool
+ns_in_echo_area (void)
+{
+  Lisp_Object in_echo_area;
+
+  in_echo_area
+    = internal_catch_all (ns_in_echo_area_1, NULL,
+                         ns_in_echo_area_2);
+
+  return !NILP (in_echo_area);
+}
 
 /* Used to position char selection windows, etc.  */
 - (NSRect)firstRectForCharacterRange: (NSRange)theRange
@@ -7064,7 +7099,7 @@ ns_create_font_panel_buttons (id target, SEL select, SEL 
cancel_action)
   if (NS_KEYLOG)
     NSLog (@"firstRectForCharRange request");
 
-  if (WINDOWP (echo_area_window) && ! NILP (call0 (intern 
("ns-in-echo-area"))))
+  if (WINDOWP (echo_area_window) && ns_in_echo_area ())
     win = XWINDOW (echo_area_window);
   else
     win = XWINDOW (FRAME_SELECTED_WINDOW (emacsframe));
@@ -9158,6 +9193,7 @@ ns_create_font_panel_buttons (id target, SEL select, SEL 
cancel_action)
   NSTRACE ("[EmacsWindow dealloc]");
 
   /* We need to release the toolbar ourselves.  */
+  [self setToolbar: nil];
   [[self toolbar] release];
 
   /* Also the last button press event .  */
@@ -11006,6 +11042,7 @@ respectively.  */);
   DEFSYM (Qcondensed, "condensed");
   DEFSYM (Qreverse_italic, "reverse-italic");
   DEFSYM (Qexpanded, "expanded");
+  DEFSYM (Qns_in_echo_area, "ns-in-echo-area");
 
 #ifdef NS_IMPL_COCOA
   Fprovide (Qcocoa, Qnil);
diff --git a/src/pdumper.c b/src/pdumper.c
index b6575812fc..75fe697dcd 100644
--- a/src/pdumper.c
+++ b/src/pdumper.c
@@ -2133,17 +2133,64 @@ dump_marker (struct dump_context *ctx, const struct 
Lisp_Marker *marker)
   return finish_dump_pvec (ctx, &out->header);
 }
 
+static dump_off
+dump_interval_node (struct dump_context *ctx, struct itree_node *node,
+                    dump_off parent_offset)
+{
+#if CHECK_STRUCTS && !defined (HASH_itree_node_50DE304F13)
+# error "itree_node changed. See CHECK_STRUCTS comment in config.h."
+#endif
+  struct itree_node out;
+  dump_object_start (ctx, &out, sizeof (out));
+  if (node->parent)
+    dump_field_fixup_later (ctx, &out, node, &node->parent);
+  if (node->left)
+    dump_field_fixup_later (ctx, &out, node, &node->parent);
+  if (node->right)
+    dump_field_fixup_later (ctx, &out, node, &node->parent);
+  DUMP_FIELD_COPY (&out, node, begin);
+  DUMP_FIELD_COPY (&out, node, end);
+  DUMP_FIELD_COPY (&out, node, limit);
+  DUMP_FIELD_COPY (&out, node, offset);
+  DUMP_FIELD_COPY (&out, node, otick);
+  dump_field_lv (ctx, &out, node, &node->data, WEIGHT_STRONG);
+  DUMP_FIELD_COPY (&out, node, red);
+  DUMP_FIELD_COPY (&out, node, rear_advance);
+  DUMP_FIELD_COPY (&out, node, front_advance);
+  dump_off offset = dump_object_finish (ctx, &out, sizeof (out));
+  if (node->parent)
+      dump_remember_fixup_ptr_raw
+       (ctx,
+        offset + dump_offsetof (struct itree_node, parent),
+        dump_interval_node (ctx, node->parent, offset));
+  if (node->left)
+      dump_remember_fixup_ptr_raw
+       (ctx,
+        offset + dump_offsetof (struct itree_node, left),
+        dump_interval_node (ctx, node->left, offset));
+  if (node->right)
+      dump_remember_fixup_ptr_raw
+       (ctx,
+        offset + dump_offsetof (struct itree_node, right),
+        dump_interval_node (ctx, node->right, offset));
+  return offset;
+}
+
 static dump_off
 dump_overlay (struct dump_context *ctx, const struct Lisp_Overlay *overlay)
 {
-#if CHECK_STRUCTS && !defined (HASH_Lisp_Overlay_72EADA9882)
+#if CHECK_STRUCTS && !defined (HASH_Lisp_Overlay_EB4C05D8D2)
 # error "Lisp_Overlay changed. See CHECK_STRUCTS comment in config.h."
 #endif
   START_DUMP_PVEC (ctx, &overlay->header, struct Lisp_Overlay, out);
   dump_pseudovector_lisp_fields (ctx, &out->header, &overlay->header);
-  dump_field_lv_rawptr (ctx, out, overlay, &overlay->next,
-                        Lisp_Vectorlike, WEIGHT_STRONG);
-  return finish_dump_pvec (ctx, &out->header);
+  dump_field_fixup_later (ctx, &out, overlay, &overlay->interval);
+  dump_off offset = finish_dump_pvec (ctx, &out->header);
+  dump_remember_fixup_ptr_raw
+    (ctx,
+     offset + dump_offsetof (struct Lisp_Overlay, interval),
+     dump_interval_node (ctx, overlay->interval, offset));
+  return offset;
 }
 
 static void
@@ -2701,7 +2748,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_AA373AEE10
+#if CHECK_STRUCTS && !defined HASH_buffer_193CAA5E45
 # error "buffer changed. See CHECK_STRUCTS comment in config.h."
 #endif
   struct buffer munged_buffer = *in_buffer;
@@ -2816,13 +2863,12 @@ dump_buffer (struct dump_context *ctx, const struct 
buffer *in_buffer)
   DUMP_FIELD_COPY (out, buffer, inhibit_buffer_hooks);
   DUMP_FIELD_COPY (out, buffer, long_line_optimizations_p);
 
-  dump_field_lv_rawptr (ctx, out, buffer, &buffer->overlays_before,
-                        Lisp_Vectorlike, WEIGHT_NORMAL);
-
-  dump_field_lv_rawptr (ctx, out, buffer, &buffer->overlays_after,
-                        Lisp_Vectorlike, WEIGHT_NORMAL);
+  if (buffer->overlays && buffer->overlays->root != NULL)
+    /* We haven't implemented the code to dump overlays.  */
+    emacs_abort ();
+  else
+    out->overlays = NULL;
 
-  DUMP_FIELD_COPY (out, buffer, overlay_center);
   dump_field_lv (ctx, out, buffer, &buffer->undo_list_,
                  WEIGHT_STRONG);
   dump_off offset = finish_dump_pvec (ctx, &out->header);
diff --git a/src/pgtkterm.c b/src/pgtkterm.c
index 491ba33882..13f6c6c3c4 100644
--- a/src/pgtkterm.c
+++ b/src/pgtkterm.c
@@ -511,16 +511,16 @@ pgtk_free_frame_resources (struct frame *f)
 
   if (FRAME_X_OUTPUT (f)->scrollbar_foreground_css_provider != NULL)
     {
-      GtkCssProvider *old =
-       FRAME_X_OUTPUT (f)->scrollbar_foreground_css_provider;
+      GtkCssProvider *old
+       = FRAME_X_OUTPUT (f)->scrollbar_foreground_css_provider;
       g_object_unref (old);
       FRAME_X_OUTPUT (f)->scrollbar_foreground_css_provider = NULL;
     }
 
   if (FRAME_X_OUTPUT (f)->scrollbar_background_css_provider != NULL)
     {
-      GtkCssProvider *old =
-       FRAME_X_OUTPUT (f)->scrollbar_background_css_provider;
+      GtkCssProvider *old
+       = FRAME_X_OUTPUT (f)->scrollbar_background_css_provider;
       g_object_unref (old);
       FRAME_X_OUTPUT (f)->scrollbar_background_css_provider = NULL;
     }
@@ -714,40 +714,42 @@ pgtk_set_window_size (struct frame *f, bool 
change_gravity,
 
 void
 pgtk_iconify_frame (struct frame *f)
-/* --------------------------------------------------------------------------
-     External: Iconify window
-   -------------------------------------------------------------------------- 
*/
 {
+  GtkWindow *window;
+
   /* Don't keep the highlight on an invisible frame.  */
+
   if (FRAME_DISPLAY_INFO (f)->highlight_frame == f)
-    FRAME_DISPLAY_INFO (f)->highlight_frame = 0;
+    FRAME_DISPLAY_INFO (f)->highlight_frame = NULL;
+
+  /* If the frame is already iconified, return.  */
 
   if (FRAME_ICONIFIED_P (f))
     return;
 
-  block_input ();
+  /* Child frames on PGTK have no outer widgets.  In that case, simply
+     refuse to iconify the frame.  */
 
   if (FRAME_GTK_OUTER_WIDGET (f))
     {
       if (!FRAME_VISIBLE_P (f))
        gtk_widget_show_all (FRAME_GTK_OUTER_WIDGET (f));
 
-      gtk_window_iconify (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)));
-      SET_FRAME_VISIBLE (f, 0);
-      SET_FRAME_ICONIFIED (f, true);
-      unblock_input ();
-      return;
-    }
+      window = GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f));
 
-  /* Make sure the X server knows where the window should be positioned,
-     in case the user deiconifies with the window manager.  */
-  if (!FRAME_VISIBLE_P (f) && !FRAME_ICONIFIED_P (f))
-    pgtk_set_offset (f, f->left_pos, f->top_pos, 0);
+      gtk_window_iconify (window);
 
-  SET_FRAME_ICONIFIED (f, true);
-  SET_FRAME_VISIBLE (f, 0);
+      /* Don't make the frame iconified here.  Doing so will cause it
+        to be skipped by redisplay, until GDK says it is deiconified
+        (see window_state_event for more details).  However, if the
+        window server rejects the iconification request, GDK will
+        never tell Emacs about the iconification not happening,
+        leading to the frame not being redisplayed until the next
+        window state change.  */
 
-  unblock_input ();
+      /* SET_FRAME_VISIBLE (f, 0);
+        SET_FRAME_ICONIFIED (f, true); */
+    }
 }
 
 static gboolean
@@ -1331,8 +1333,8 @@ fill_background_by_face (struct frame *f, struct face 
*face, int x, int y,
 
   if (face->stipple != 0)
     {
-      cairo_pattern_t *mask =
-       FRAME_DISPLAY_INFO (f)->bitmaps[face->stipple - 1].pattern;
+      cairo_pattern_t *mask
+       = FRAME_DISPLAY_INFO (f)->bitmaps[face->stipple - 1].pattern;
 
       double r = ((face->foreground >> 16) & 0xff) / 255.0;
       double g = ((face->foreground >> 8) & 0xff) / 255.0;
@@ -1604,8 +1606,8 @@ pgtk_draw_glyphless_glyph_string_foreground (struct 
glyph_string *s)
 
          /* It is assured that all LEN characters in STR is ASCII.  */
          for (j = 0; j < len; j++)
-           char2b[j] =
-             s->font->driver->encode_char (s->font, str[j]) & 0xFFFF;
+           char2b[j]
+             = s->font->driver->encode_char (s->font, str[j]) & 0xFFFF;
          s->font->driver->draw (s, 0, upper_len,
                                 x + glyph->slice.glyphless.upper_xoff,
                                 s->ybase + glyph->slice.glyphless.upper_yoff,
@@ -2956,8 +2958,8 @@ pgtk_draw_window_cursor (struct window *w, struct 
glyph_row *glyph_row, int x,
 
       if (w == XWINDOW (f->selected_window))
        {
-         int frame_x =
-           WINDOW_TO_FRAME_PIXEL_X (w, x) + WINDOW_LEFT_FRINGE_WIDTH (w);
+         int frame_x = (WINDOW_TO_FRAME_PIXEL_X (w, x)
+                        + WINDOW_LEFT_FRINGE_WIDTH (w));
          int frame_y = WINDOW_TO_FRAME_PIXEL_Y (w, y);
          pgtk_im_set_cursor_location (f, frame_x, frame_y,
                                       w->phys_cursor_width,
@@ -4516,16 +4518,29 @@ pgtk_free_pixmap (struct frame *f, Emacs_Pixmap pixmap)
 void
 pgtk_focus_frame (struct frame *f, bool noactivate)
 {
-  struct pgtk_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+  struct pgtk_display_info *dpyinfo;
+  GtkWidget *widget;
+  GtkWindow *window;
 
-  GtkWidget *wid = FRAME_WIDGET (f);
+  dpyinfo = FRAME_DISPLAY_INFO (f);
 
-  if (dpyinfo->x_focus_frame != f && wid != NULL)
+  if (FRAME_GTK_OUTER_WIDGET (f) && !noactivate)
     {
-      block_input ();
-      gtk_widget_grab_focus (wid);
-      unblock_input ();
+      /* The user says it is okay to activate the frame.  Call
+        gtk_window_present_with_time.  If the timestamp specified
+        (actually a display serial on Wayland) is new enough, then
+        any Wayland compositor supporting gtk_surface1_present will
+        cause the frame to be activated.  */
+
+      window = GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f));
+      gtk_window_present_with_time (window, dpyinfo->last_user_time);
+      return;
     }
+
+  widget = FRAME_WIDGET (f);
+
+  if (widget)
+    gtk_widget_grab_focus (widget);
 }
 
 static void
@@ -5142,13 +5157,15 @@ static gboolean
 key_press_event (GtkWidget *widget, GdkEvent *event, gpointer *user_data)
 {
   union buffered_input_event inev;
-  ptrdiff_t nbytes = 0;
+  ptrdiff_t nbytes;
   Mouse_HLInfo *hlinfo;
   struct frame *f;
+  struct pgtk_display_info *dpyinfo;
 
   f = pgtk_any_window_to_frame (gtk_widget_get_window (widget));
   EVENT_INIT (inev.ie);
   hlinfo = MOUSE_HL_INFO (f);
+  nbytes = 0;
 
   /* If mouse-highlight is an integer, input clears out
      mouse highlighting.  */
@@ -5179,6 +5196,12 @@ key_press_event (GtkWidget *widget, GdkEvent *event, 
gpointer *user_data)
       Lisp_Object c;
       guint state;
 
+      dpyinfo = FRAME_DISPLAY_INFO (f);
+
+      /* Set the last user time for pgtk_focus_frame to work
+        correctly.  */
+      dpyinfo->last_user_time = event->key.time;
+
       state = event->key.state;
 
       /* While super is pressed, the input method will always always
@@ -5212,8 +5235,8 @@ key_press_event (GtkWidget *widget, GdkEvent *event, 
gpointer *user_data)
 
       /* Common for all keysym input events.  */
       XSETFRAME (inev.ie.frame_or_window, f);
-      inev.ie.modifiers =
-       pgtk_gtk_to_emacs_modifiers (FRAME_DISPLAY_INFO (f), modifiers);
+      inev.ie.modifiers
+       = pgtk_gtk_to_emacs_modifiers (FRAME_DISPLAY_INFO (f), modifiers);
       inev.ie.timestamp = event->key.time;
 
       /* First deal with keysyms which have defined
@@ -5361,11 +5384,37 @@ done:
   return TRUE;
 }
 
+static struct pgtk_display_info *
+pgtk_display_info_for_display (GdkDisplay *dpy)
+{
+  struct pgtk_display_info *dpyinfo;
+
+  for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
+    {
+      if (dpyinfo->display == dpy)
+       return dpyinfo;
+    }
+
+  return NULL;
+}
+
 static gboolean
 key_release_event (GtkWidget *widget,
                   GdkEvent *event,
                   gpointer *user_data)
 {
+  GdkDisplay *display;
+  struct pgtk_display_info *dpyinfo;
+
+  display = gtk_widget_get_display (widget);
+  dpyinfo = pgtk_display_info_for_display (display);
+
+  if (dpyinfo)
+    /* This is needed on Wayland because of some brain dead
+       compositors.  Without them, we would not have to keep track of
+       the serial of key release events.  */
+    dpyinfo->last_user_time = event->key.time;
+
   return TRUE;
 }
 
@@ -5420,9 +5469,7 @@ map_event (GtkWidget *widget,
       /* Check if fullscreen was specified before we where mapped the
          first time, i.e. from the command line.  */
       if (!FRAME_X_OUTPUT (f)->has_been_visible)
-       {
-         set_fullscreen_state (f);
-       }
+       set_fullscreen_state (f);
 
       if (!iconified)
        {
@@ -5465,24 +5512,6 @@ window_state_event (GtkWidget *widget,
   inev.ie.kind = NO_EVENT;
   inev.ie.arg = Qnil;
 
-  if (f)
-    {
-      if (new_state & GDK_WINDOW_STATE_FOCUSED)
-       {
-         if (FRAME_ICONIFIED_P (f))
-           {
-             /* Gnome shell does not iconify us when C-z is pressed.
-                It hides the frame.  So if our state says we aren't
-                hidden anymore, treat it as deiconified.  */
-             SET_FRAME_VISIBLE (f, 1);
-             SET_FRAME_ICONIFIED (f, false);
-             FRAME_X_OUTPUT (f)->has_been_visible = true;
-             inev.ie.kind = DEICONIFY_EVENT;
-             XSETFRAME (inev.ie.frame_or_window, f);
-           }
-       }
-    }
-
   if (new_state & GDK_WINDOW_STATE_FULLSCREEN)
     store_frame_param (f, Qfullscreen, Qfullboth);
   else if (new_state & GDK_WINDOW_STATE_MAXIMIZED)
@@ -5500,14 +5529,37 @@ window_state_event (GtkWidget *widget,
   else
     store_frame_param (f, Qfullscreen, Qnil);
 
+  /* The Wayland protocol provides no way for the client to know
+     whether or not one of its toplevels has actually been
+     deiconified.  It only provides a request for clients to iconify a
+     toplevel, without even the ability to determine whether or not
+     the iconification request was rejected by the display server.
+
+     GDK computes the iconified state by sending a window state event
+     containing only GDK_WINDOW_STATE_ICONIFIED immediately after
+     gtk_window_iconify is called.  That is error-prone if the request
+     to iconify the frame was rejected by the display server, but is
+     not the main problem here, as Wayland compositors only rarely
+     reject such requests.  GDK also assumes that it can clear the
+     iconified state upon receiving the next toplevel configure event
+     from the display server.  Unfortunately, such events can be sent
+     by Wayland compositors while the frame is iconified, and may also
+     not be sent upon deiconification.  So, no matter what Emacs does,
+     the iconification state of a frame is likely to be wrong under
+     one situation or another.  */
+
   if (new_state & GDK_WINDOW_STATE_ICONIFIED)
-    SET_FRAME_ICONIFIED (f, true);
+    {
+      SET_FRAME_ICONIFIED (f, true);
+      SET_FRAME_VISIBLE (f, false);
+    }
   else
     {
       FRAME_X_OUTPUT (f)->has_been_visible = true;
       inev.ie.kind = DEICONIFY_EVENT;
       XSETFRAME (inev.ie.frame_or_window, f);
       SET_FRAME_ICONIFIED (f, false);
+      SET_FRAME_VISIBLE (f, true);
     }
 
   if (new_state & GDK_WINDOW_STATE_STICKY)
@@ -5899,9 +5951,10 @@ construct_mouse_click (struct input_event *result,
   result->kind = MOUSE_CLICK_EVENT;
   result->code = event->button - 1;
   result->timestamp = event->time;
-  result->modifiers =
-    (pgtk_gtk_to_emacs_modifiers (FRAME_DISPLAY_INFO (f), event->state) |
-     (event->type == GDK_BUTTON_RELEASE ? up_modifier : down_modifier));
+  result->modifiers = (pgtk_gtk_to_emacs_modifiers (FRAME_DISPLAY_INFO (f),
+                                                   event->state)
+                      | (event->type == GDK_BUTTON_RELEASE
+                         ? up_modifier : down_modifier));
 
   XSETINT (result->x, event->x);
   XSETINT (result->y, event->y);
@@ -5966,6 +6019,10 @@ button_event (GtkWidget *widget, GdkEvent *event,
        }
     }
 
+  /* Set the last user time, used to activate the frame in
+     pgtk_focus_frame.  */
+  dpyinfo->last_user_time = event->button.time;
+
   if (f)
     {
       /* Is this in the tab-bar?  */
@@ -5984,10 +6041,7 @@ button_event (GtkWidget *widget, GdkEvent *event,
              (f, x, y, event->type == GDK_BUTTON_PRESS,
               pgtk_gtk_to_emacs_modifiers (dpyinfo, event->button.state));
        }
-    }
 
-  if (f)
-    {
       if (!(tab_bar_p && NILP (tab_bar_arg)) && !tool_bar_p)
        {
          if (ignore_next_mouse_click_timeout)
@@ -6055,8 +6109,8 @@ scroll_event (GtkWidget *widget, GdkEvent *event, 
gpointer *user_data)
 
   inev.ie.kind = NO_EVENT;
   inev.ie.timestamp = event->scroll.time;
-  inev.ie.modifiers =
-    pgtk_gtk_to_emacs_modifiers (FRAME_DISPLAY_INFO (f), event->scroll.state);
+  inev.ie.modifiers
+    = pgtk_gtk_to_emacs_modifiers (FRAME_DISPLAY_INFO (f), 
event->scroll.state);
   XSETINT (inev.ie.x, event->scroll.x);
   XSETINT (inev.ie.y, event->scroll.y);
   XSETFRAME (inev.ie.frame_or_window, f);
@@ -6594,6 +6648,44 @@ pgtk_selection_event (GtkWidget *widget, GdkEvent *event,
   return FALSE;
 }
 
+/* Display a warning message if the PGTK port is being used under X;
+   that is not supported.  */
+
+static void
+pgtk_display_x_warning (GdkDisplay *display)
+{
+  GtkWidget *dialog_widget, *label, *content_area;
+  GtkDialog *dialog;
+  GtkWindow *window;
+  GdkScreen *screen;
+
+  /* Do this instead of GDK_IS_X11_DISPLAY because the GDK X header
+     pulls in Xlib, which conflicts with definitions in pgtkgui.h.  */
+  if (strcmp (G_OBJECT_TYPE_NAME (display),
+             "GdkX11Display"))
+    return;
+
+  dialog_widget = gtk_dialog_new ();
+  dialog = GTK_DIALOG (dialog_widget);
+  window = GTK_WINDOW (dialog_widget);
+  screen = gdk_display_get_default_screen (display);
+  content_area = gtk_dialog_get_content_area (dialog);
+
+  gtk_window_set_title (window, "Warning");
+  gtk_window_set_screen (window, screen);
+
+  label = gtk_label_new ("You are trying to run Emacs configured with"
+                         " the \"pure-GTK\" interface under the X Window"
+                         " System.  That configuration is unsupported and"
+                         " will lead to sporadic crashes during transfer of"
+                         " large selection data.  It will also lead to"
+                         " various problems with keyboard input.");
+  gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+  gtk_container_add (GTK_CONTAINER (content_area), label);
+  gtk_widget_show (label);
+  gtk_widget_show (dialog_widget);
+}
+
 /* Open a connection to X display DISPLAY_NAME, and return
    the structure that describes the open display.
    If we cannot contact the display, return null.  */
@@ -6697,6 +6789,9 @@ pgtk_term_init (Lisp_Object display_name, char 
*resource_name)
       return 0;
     }
 
+  /* If the PGTK port is being used under X, complain very loudly, as
+     that isn't supported.  */
+  pgtk_display_x_warning (dpy);
 
   dpyinfo = xzalloc (sizeof *dpyinfo);
   pgtk_initialize_display_info (dpyinfo);
@@ -6940,10 +7035,9 @@ pgtk_parse_color (struct frame *f, const char 
*color_name,
       color->red = rgba.red * 65535;
       color->green = rgba.green * 65535;
       color->blue = rgba.blue * 65535;
-      color->pixel =
-       (color->red >> 8) << 16 |
-       (color->green >> 8) << 8 |
-       (color->blue >> 8) << 0;
+      color->pixel = ((color->red >> 8) << 16
+                     | (color->green >> 8) << 8
+                     | (color->blue >> 8) << 0);
       return 1;
     }
   return 0;
@@ -7072,10 +7166,9 @@ If set to a non-float value, there will be no wait at 
all.  */);
   Vpgtk_wait_for_event_timeout = make_float (0.1);
 
   DEFVAR_LISP ("pgtk-keysym-table", Vpgtk_keysym_table,
-              doc: /* Hash table of character codes indexed by X keysym codes. 
 */);
-  Vpgtk_keysym_table =
-    make_hash_table (hashtest_eql, 900, DEFAULT_REHASH_SIZE,
-                    DEFAULT_REHASH_THRESHOLD, Qnil, false);
+    doc: /* Hash table of character codes indexed by X keysym codes.  */);
+  Vpgtk_keysym_table = make_hash_table (hashtest_eql, 900, DEFAULT_REHASH_SIZE,
+                                       DEFAULT_REHASH_THRESHOLD, Qnil, false);
 
   window_being_scrolled = Qnil;
   staticpro (&window_being_scrolled);
@@ -7113,13 +7206,13 @@ pgtk_begin_cr_clip (struct frame *f)
 
   if (!cr)
     {
-      cairo_surface_t *surface =
-       gdk_window_create_similar_surface (gtk_widget_get_window
-                                          (FRAME_GTK_WIDGET (f)),
-                                          CAIRO_CONTENT_COLOR_ALPHA,
-                                          FRAME_CR_SURFACE_DESIRED_WIDTH (f),
-                                          FRAME_CR_SURFACE_DESIRED_HEIGHT
-                                          (f));
+      cairo_surface_t *surface
+       = gdk_window_create_similar_surface (gtk_widget_get_window
+                                            (FRAME_GTK_WIDGET (f)),
+                                            CAIRO_CONTENT_COLOR_ALPHA,
+                                            FRAME_CR_SURFACE_DESIRED_WIDTH (f),
+                                            FRAME_CR_SURFACE_DESIRED_HEIGHT
+                                            (f));
 
       cr = FRAME_CR_CONTEXT (f) = cairo_create (surface);
       cairo_surface_destroy (surface);
diff --git a/src/pgtkterm.h b/src/pgtkterm.h
index fcc6c5310e..b6bd10dcb4 100644
--- a/src/pgtkterm.h
+++ b/src/pgtkterm.h
@@ -262,6 +262,13 @@ struct pgtk_output
   unsigned long background_color;
   void *toolbar;
 
+  /* The "time" of the last user interaction on this display.  Set
+     upon button and key press and release events.
+
+     Under the GDK Wayland backend, this is actually an event
+     serial.  */
+  guint32 last_user_time;
+
   /* Cursors */
   Emacs_Cursor current_cursor;
   Emacs_Cursor text_cursor;
@@ -357,8 +364,8 @@ struct pgtk_output
   /* The tool bar in this frame  */
   GtkWidget *toolbar_widget;
   /* True if tool bar is packed into the hbox widget (i.e. vertical).  */
-  bool_bf toolbar_in_hbox:1;
-  bool_bf toolbar_is_packed:1;
+  bool_bf toolbar_in_hbox : 1;
+  bool_bf toolbar_is_packed : 1;
 
   GtkTooltip *ttip_widget;
   GtkWidget *ttip_lbl;
diff --git a/src/print.c b/src/print.c
index 1c96ec14b8..07560518c4 100644
--- a/src/print.c
+++ b/src/print.c
@@ -1,7 +1,6 @@
 /* Lisp object printing and output streams.
 
-Copyright (C) 1985-1986, 1988, 1993-1995, 1997-2022 Free Software
-Foundation, Inc.
+Copyright (C) 1985-2022  Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -594,8 +593,7 @@ temp_output_buffer_setup (const char *bufname)
   bset_read_only (current_buffer, Qnil);
   bset_filename (current_buffer, Qnil);
   bset_undo_list (current_buffer, Qt);
-  eassert (current_buffer->overlays_before == NULL);
-  eassert (current_buffer->overlays_after == NULL);
+  eassert (current_buffer->overlays == NULL);
   bset_enable_multibyte_characters
     (current_buffer, BVAR (&buffer_defaults, enable_multibyte_characters));
   specbind (Qinhibit_read_only, Qt);
@@ -1745,15 +1743,15 @@ print_vectorlike (Lisp_Object obj, Lisp_Object 
printcharfun, bool escapeflag,
 
     case PVEC_OVERLAY:
       print_c_string ("#<overlay ", printcharfun);
-      if (! XMARKER (OVERLAY_START (obj))->buffer)
+      if (! OVERLAY_BUFFER (obj))
        print_c_string ("in no buffer", printcharfun);
       else
        {
          int len = sprintf (buf, "from %"pD"d to %"pD"d in ",
-                            marker_position (OVERLAY_START (obj)),
-                            marker_position (OVERLAY_END   (obj)));
+                            OVERLAY_START (obj),
+                            OVERLAY_END   (obj));
          strout (buf, len, len, printcharfun);
-         print_string (BVAR (XMARKER (OVERLAY_START (obj))->buffer, name),
+         print_string (BVAR (OVERLAY_BUFFER (obj), name),
                        printcharfun);
        }
       printchar ('>', printcharfun);
@@ -2019,8 +2017,8 @@ print_vectorlike (Lisp_Object obj, Lisp_Object 
printcharfun, bool escapeflag,
            i = sprintf (buf, " stmt=%p", XSQLITE (obj)->stmt);
            strout (buf, i, i, printcharfun);
          }
-       i = sprintf (buf, " name=%s", XSQLITE (obj)->name);
-       strout (buf, i, i, printcharfun);
+       print_c_string (" name=", printcharfun);
+       print_c_string (XSQLITE (obj)->name, printcharfun);
        printchar ('>', printcharfun);
       }
       break;
diff --git a/src/process.c b/src/process.c
index 358899cded..5144c5d6c9 100644
--- a/src/process.c
+++ b/src/process.c
@@ -292,7 +292,6 @@ static int child_signal_read_fd = -1;
    descriptor to notify `wait_reading_process_output' of process
    status changes.  */
 static int child_signal_write_fd = -1;
-static void child_signal_init (void);
 #ifndef WINDOWSNT
 static void child_signal_read (int, void *);
 #endif
@@ -7323,7 +7322,7 @@ process has been transmitted to the serial port.  */)
 
 /* Set up `child_signal_read_fd' and `child_signal_write_fd'.  */
 
-static void
+void
 child_signal_init (void)
 {
   /* Either both are initialized, or both are uninitialized.  */
diff --git a/src/regex-emacs.c b/src/regex-emacs.c
index 9b2c14c413..626560911f 100644
--- a/src/regex-emacs.c
+++ b/src/regex-emacs.c
@@ -3446,14 +3446,18 @@ static bool bcmp_translate (re_char *, re_char *, 
ptrdiff_t,
 
 /* Call before fetching a character with *d.  This switches over to
    string2 if necessary.
+   `reset' is executed before backtracking if there are no more characters.
    Check re_match_2_internal for a discussion of why end_match_2 might
    not be within string2 (but be equal to end_match_1 instead).  */
-#define PREFETCH()                                                     \
+#define PREFETCH(reset)                                                        
\
   while (d == dend)                                                    \
     {                                                                  \
       /* End of string2 => fail.  */                                   \
       if (dend == end_match_2)                                         \
-       goto fail;                                                      \
+        {                                                              \
+         reset;                                                        \
+         goto fail;                                                    \
+       }                                                               \
       /* End of string1 => advance to string2.  */                     \
       d = string2;                                                     \
       dend = end_match_2;                                              \
@@ -4252,7 +4256,7 @@ re_match_2_internal (struct re_pattern_buffer *bufp,
                int pat_charlen, buf_charlen;
                int pat_ch, buf_ch;
 
-               PREFETCH ();
+               PREFETCH (d = dfail);
                if (multibyte)
                  pat_ch = string_char_and_length (p, &pat_charlen);
                else
@@ -4280,7 +4284,7 @@ re_match_2_internal (struct re_pattern_buffer *bufp,
                int pat_charlen;
                int pat_ch, buf_ch;
 
-               PREFETCH ();
+               PREFETCH (d = dfail);
                if (multibyte)
                  {
                    pat_ch = string_char_and_length (p, &pat_charlen);
@@ -4486,7 +4490,7 @@ re_match_2_internal (struct re_pattern_buffer *bufp,
                if (d2 == dend2) break;
 
                /* If necessary, advance to next segment in data.  */
-               PREFETCH ();
+               PREFETCH (d = dfail);
 
                /* How many characters left in this segment to match.  */
                dcnt = dend - d;
diff --git a/src/search.c b/src/search.c
index b5d6a442c0..1c5831b6de 100644
--- a/src/search.c
+++ b/src/search.c
@@ -1558,7 +1558,6 @@ simple_search (EMACS_INT n, unsigned char *pat,
        while (1)
          {
            /* Try matching at position POS.  */
-           ptrdiff_t this_pos = pos;
            ptrdiff_t this_pos_byte = pos_byte;
            ptrdiff_t this_len = len;
            unsigned char *p = pat;
@@ -1580,7 +1579,6 @@ simple_search (EMACS_INT n, unsigned char *pat,
                p += charlen;
 
                this_pos_byte += buf_charlen;
-               this_pos++;
              }
 
            if (this_len == 0)
@@ -2824,11 +2822,21 @@ Return value is undefined if the last search failed.  
*/)
 }
 
 DEFUN ("match-data", Fmatch_data, Smatch_data, 0, 3, 0,
-       doc: /* Return a list describing what the last search matched.
-Element 2N is `(match-beginning N)'; element 2N + 1 is `(match-end N)'.
-All the elements are markers or nil (nil if the Nth pair didn't match)
-if the last match was on a buffer; integers or nil if a string was matched.
-Use `set-match-data' to reinstate the data in this list.
+       doc: /* Return a list of positions that record text matched by the last 
search.
+Element 2N of the returned list is the position of the beginning of the
+match of the Nth subexpression; it corresponds to `(match-beginning N)';
+element 2N + 1 is the position of the end of the match of the Nth
+subexpression; it corresponds to `(match-end N)'.  See `match-beginning'
+and `match-end'.
+If the last search was on a buffer, all the elements are by default
+markers or nil (nil when the Nth pair didn't match); they are integers
+or nil if the search was on a string.  But if the optional argument
+INTEGERS is non-nil, the elements that represent buffer positions are
+always integers, not markers, and (if the search was on a buffer) the
+buffer itself is appended to the list as one additional element.
+
+Use `set-match-data' to reinstate the match data from the elements of
+this list.
 
 Note that non-matching optional groups at the end of the regexp are
 elided instead of being represented with two `nil's each.  For instance:
@@ -2838,16 +2846,13 @@ elided instead of being represented with two `nil's 
each.  For instance:
     (match-data))
   => (0 1 nil nil 0 1)
 
-If INTEGERS (the optional first argument) is non-nil, always use
-integers (rather than markers) to represent buffer positions.  In
-this case, and if the last match was in a buffer, the buffer will get
-stored as one additional element at the end of the list.
-
-If REUSE is a list, reuse it as part of the value.  If REUSE is long
-enough to hold all the values, and if INTEGERS is non-nil, no consing
-is done.
+If REUSE is a list, store the value in REUSE by destructively modifying it.
+If REUSE is long enough to hold all the values, its length remains the
+same, and any unused elements are set to nil.  If REUSE is not long
+enough, it is extended.  Note that if REUSE is long enough and INTEGERS
+is non-nil, no consing is done to make the return value; this minimizes GC.
 
-If optional third arg RESEAT is non-nil, any previous markers on the
+If optional third argument RESEAT is non-nil, any previous markers on the
 REUSE list will be modified to point to nowhere.
 
 Return value is undefined if the last search failed.  */)
diff --git a/src/sqlite.c b/src/sqlite.c
index 1526e344e5..ac860f55bc 100644
--- a/src/sqlite.c
+++ b/src/sqlite.c
@@ -50,8 +50,11 @@ DEF_DLL_FN (SQLITE_API int, sqlite3_bind_int64,
 DEF_DLL_FN (SQLITE_API int, sqlite3_bind_double, (sqlite3_stmt*, int, double));
 DEF_DLL_FN (SQLITE_API int, sqlite3_bind_null, (sqlite3_stmt*, int));
 DEF_DLL_FN (SQLITE_API int, sqlite3_bind_int, (sqlite3_stmt*, int, int));
+DEF_DLL_FN (SQLITE_API int, sqlite3_extended_errcode, (sqlite3*));
 DEF_DLL_FN (SQLITE_API const char*, sqlite3_errmsg, (sqlite3*));
+#if SQLITE_VERSION_NUMBER >= 3007015
 DEF_DLL_FN (SQLITE_API const char*, sqlite3_errstr, (int));
+#endif
 DEF_DLL_FN (SQLITE_API int, sqlite3_step, (sqlite3_stmt*));
 DEF_DLL_FN (SQLITE_API int, sqlite3_changes, (sqlite3*));
 DEF_DLL_FN (SQLITE_API int, sqlite3_column_count, (sqlite3_stmt*));
@@ -88,8 +91,11 @@ DEF_DLL_FN (SQLITE_API int, sqlite3_load_extension,
 # undef sqlite3_bind_double
 # undef sqlite3_bind_null
 # undef sqlite3_bind_int
+# undef sqlite3_extended_errcode
 # undef sqlite3_errmsg
-# undef sqlite3_errstr
+# if SQLITE_VERSION_NUMBER >= 3007015
+#  undef sqlite3_errstr
+# endif
 # undef sqlite3_step
 # undef sqlite3_changes
 # undef sqlite3_column_count
@@ -113,8 +119,11 @@ DEF_DLL_FN (SQLITE_API int, sqlite3_load_extension,
 # define sqlite3_bind_double fn_sqlite3_bind_double
 # define sqlite3_bind_null fn_sqlite3_bind_null
 # define sqlite3_bind_int fn_sqlite3_bind_int
+# define sqlite3_extended_errcode fn_sqlite3_extended_errcode
 # define sqlite3_errmsg fn_sqlite3_errmsg
-# define sqlite3_errstr fn_sqlite3_errstr
+# if SQLITE_VERSION_NUMBER >= 3007015
+#  define sqlite3_errstr fn_sqlite3_errstr
+# endif
 # define sqlite3_step fn_sqlite3_step
 # define sqlite3_changes fn_sqlite3_changes
 # define sqlite3_column_count fn_sqlite3_column_count
@@ -141,8 +150,11 @@ load_dll_functions (HMODULE library)
   LOAD_DLL_FN (library, sqlite3_bind_double);
   LOAD_DLL_FN (library, sqlite3_bind_null);
   LOAD_DLL_FN (library, sqlite3_bind_int);
+  LOAD_DLL_FN (library, sqlite3_extended_errcode);
   LOAD_DLL_FN (library, sqlite3_errmsg);
+#if SQLITE_VERSION_NUMBER >= 3007015
   LOAD_DLL_FN (library, sqlite3_errstr);
+#endif
   LOAD_DLL_FN (library, sqlite3_step);
   LOAD_DLL_FN (library, sqlite3_changes);
   LOAD_DLL_FN (library, sqlite3_column_count);
@@ -233,13 +245,13 @@ check_sqlite (Lisp_Object db, bool is_statement)
   init_sqlite_functions ();
   CHECK_SQLITE (db);
   if (is_statement && !XSQLITE (db)->is_statement)
-    xsignal1 (Qerror, build_string ("Invalid set object"));
+    xsignal1 (Qsqlite_error, build_string ("Invalid set object"));
   else if (!is_statement && XSQLITE (db)->is_statement)
-    xsignal1 (Qerror, build_string ("Invalid database object"));
+    xsignal1 (Qsqlite_error, build_string ("Invalid database object"));
   if (!is_statement && !XSQLITE (db)->db)
-    xsignal1 (Qerror, build_string ("Database closed"));
+    xsignal1 (Qsqlite_error, build_string ("Database closed"));
   else if (is_statement && !XSQLITE (db)->db)
-    xsignal1 (Qerror, build_string ("Statement closed"));
+    xsignal1 (Qsqlite_error, build_string ("Statement closed"));
 }
 
 static int db_count = 0;
@@ -259,7 +271,7 @@ If FILE is nil, an in-memory database will be opened 
instead.  */)
 #endif
 
   if (!init_sqlite_functions ())
-    xsignal1 (Qerror, build_string ("sqlite support is not available"));
+    xsignal1 (Qsqlite_error, build_string ("sqlite support is not available"));
 
   if (!NILP (file))
     name = ENCODE_FILE (Fexpand_file_name (file, Qnil));
@@ -272,7 +284,7 @@ If FILE is nil, an in-memory database will be opened 
instead.  */)
       name = CALLN (Fformat, memory_fmt, make_int (++db_count));
       flags |= SQLITE_OPEN_MEMORY;
 #else
-      xsignal1 (Qerror, build_string ("sqlite in-memory is not available"));
+      xsignal1 (Qsqlite_error, build_string ("sqlite in-memory is not 
available"));
 #endif
     }
 
@@ -342,7 +354,7 @@ bind_values (sqlite3 *db, sqlite3_stmt *stmt, Lisp_Object 
values)
          if (blob)
            {
              if (SBYTES (value) != SCHARS (value))
-               xsignal1 (Qerror, build_string ("BLOB values must be unibyte"));
+               xsignal1 (Qsqlite_error, build_string ("BLOB values must be 
unibyte"));
            ret = sqlite3_bind_blob (stmt, i + 1,
                                       SSDATA (value), SBYTES (value),
                                       NULL);
@@ -422,16 +434,29 @@ row_to_value (sqlite3_stmt *stmt)
 }
 
 static Lisp_Object
-sqlite_prepare_errmsg (int code, sqlite3 *sdb)
+sqlite_prepare_errdata (int code, sqlite3 *sdb)
 {
-  Lisp_Object errmsg = build_string (sqlite3_errstr (code));
+  Lisp_Object errcode = make_fixnum (code);
+  const char *errmsg = sqlite3_errmsg (sdb);
+  Lisp_Object lerrmsg = errmsg ? build_string (errmsg) : Qnil;
+  Lisp_Object errstr, ext_errcode;
+
+#if SQLITE_VERSION_NUMBER >= 3007015
+  errstr = build_string (sqlite3_errstr (code));
+#else
+  /* The internet says this is identical to sqlite3_errstr (code).  */
+  errstr = lerrmsg;
+#endif
+
   /* More details about what went wrong.  */
-  const char *sql_error = sqlite3_errmsg (sdb);
-  if (sql_error)
-    return CALLN (Fformat, build_string ("%s (%s)"),
-                 errmsg, build_string (sql_error));
-  else
-    return errmsg;
+#if SQLITE_VERSION_NUMBER >= 3006005
+  ext_errcode = make_fixnum (sqlite3_extended_errcode (sdb));
+#else
+  /* What value to use here?  */
+  ext_errcode = make_fixnum (0);
+#endif
+
+  return list4 (errstr, lerrmsg, errcode, ext_errcode);
 }
 
 DEFUN ("sqlite-execute", Fsqlite_execute, Ssqlite_execute, 2, 3, 0,
@@ -447,7 +472,7 @@ Value is the number of affected rows.  */)
   check_sqlite (db, false);
   CHECK_STRING (query);
   if (!(NILP (values) || CONSP (values) || VECTORP (values)))
-    xsignal1 (Qerror, build_string ("VALUES must be a list or a vector"));
+    xsignal1 (Qsqlite_error, build_string ("VALUES must be a list or a 
vector"));
 
   sqlite3 *sdb = XSQLITE (db)->db;
   Lisp_Object errmsg = Qnil,
@@ -466,7 +491,7 @@ Value is the number of affected rows.  */)
          sqlite3_reset (stmt);
        }
 
-      errmsg = sqlite_prepare_errmsg (ret, sdb);
+      errmsg = sqlite_prepare_errdata (ret, sdb);
       goto exit;
     }
 
@@ -505,7 +530,7 @@ Value is the number of affected rows.  */)
  exit:
   sqlite3_finalize (stmt);
   xsignal1 (ret == SQLITE_LOCKED || ret == SQLITE_BUSY?
-           Qsqlite_locked_error: Qerror,
+           Qsqlite_locked_error: Qsqlite_error,
            errmsg);
 }
 
@@ -525,14 +550,15 @@ DEFUN ("sqlite-select", Fsqlite_select, Ssqlite_select, 
2, 4, 0,
 If VALUES is non-nil, it should be a list or a vector specifying the
 values that will be interpolated into a parameterized statement.
 
-By default, the return value is a list where the first element is a
-list of column names, and the rest of the elements are the matching data.
+By default, the return value is a list, whose contents depend on
+the value of the optional argument RETURN-TYPE.
 
-RETURN-TYPE can be either nil (which means that the matching data
-should be returned as a list of rows), or `full' (the same, but the
-first element in the return list will be the column names), or `set',
-which means that we return a set object that can be queried with
-`sqlite-next' and other functions to get the data.  */)
+If RETURN-TYPE is nil or omitted, the function returns a list of rows
+matching QUERY.  If RETURN-TYPE is `full', the function returns a
+list whose first element is the list of column names, and the rest
+of the elements are the rows matching QUERY.  If RETURN-TYPE is `set',
+the function returns a set object that can be queried with functions
+like `sqlite-next' etc., in order to get the data.  */)
   (Lisp_Object db, Lisp_Object query, Lisp_Object values,
    Lisp_Object return_type)
 {
@@ -540,7 +566,7 @@ which means that we return a set object that can be queried 
with
   CHECK_STRING (query);
 
   if (!(NILP (values) || CONSP (values) || VECTORP (values)))
-    xsignal1 (Qerror, build_string ("VALUES must be a list or a vector"));
+    xsignal1 (Qsqlite_error, build_string ("VALUES must be a list or a 
vector"));
 
   sqlite3 *sdb = XSQLITE (db)->db;
   Lisp_Object retval = Qnil, errmsg = Qnil,
@@ -553,7 +579,7 @@ which means that we return a set object that can be queried 
with
     {
       if (stmt)
        sqlite3_finalize (stmt);
-      errmsg = sqlite_prepare_errmsg (ret, sdb);
+      errmsg = sqlite_prepare_errdata (ret, sdb);
       goto exit;
     }
 
@@ -589,7 +615,7 @@ which means that we return a set object that can be queried 
with
 
  exit:
   if (! NILP (errmsg))
-    xsignal1 (Qerror, errmsg);
+    xsignal1 (Qsqlite_error, errmsg);
 
   return retval;
 }
@@ -675,7 +701,7 @@ Only modules on Emacs' list of allowed modules can be 
loaded.  */)
     }
 
   if (!do_allow)
-    xsignal (Qerror, build_string ("Module name not on allowlist"));
+    xsignal1 (Qsqlite_error, build_string ("Module name not on allowlist"));
 
   int result = sqlite3_load_extension
                       (XSQLITE (db)->db,
@@ -695,7 +721,7 @@ DEFUN ("sqlite-next", Fsqlite_next, Ssqlite_next, 1, 1, 0,
 
   int ret = sqlite3_step (XSQLITE (set)->stmt);
   if (ret != SQLITE_ROW && ret != SQLITE_OK && ret != SQLITE_DONE)
-    xsignal1 (Qerror, build_string (sqlite3_errmsg (XSQLITE (set)->db)));
+    xsignal1 (Qsqlite_error, build_string (sqlite3_errmsg (XSQLITE 
(set)->db)));
 
   if (ret == SQLITE_DONE)
     {
@@ -794,9 +820,15 @@ syms_of_sqlite (void)
   defsubr (&Ssqlitep);
   defsubr (&Ssqlite_available_p);
 
+  DEFSYM (Qsqlite_error, "sqlite-error");
+  Fput (Qsqlite_error, Qerror_conditions,
+       Fpurecopy (list2 (Qsqlite_error, Qerror)));
+  Fput (Qsqlite_error, Qerror_message,
+       build_pure_c_string ("Database error"));
+
   DEFSYM (Qsqlite_locked_error, "sqlite-locked-error");
   Fput (Qsqlite_locked_error, Qerror_conditions,
-       Fpurecopy (list2 (Qsqlite_locked_error, Qerror)));
+       Fpurecopy (list3 (Qsqlite_locked_error, Qsqlite_error, Qerror)));
   Fput (Qsqlite_locked_error, Qerror_message,
        build_pure_c_string ("Database locked"));
 
diff --git a/src/sysdep.c b/src/sysdep.c
index abb385d138..736723bdf3 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -2432,7 +2432,7 @@ emacs_pipe (int fd[2])
 
 /* Approximate posix_close and POSIX_CLOSE_RESTART well enough for Emacs.
    For the background behind this mess, please see Austin Group defect 529
-   <http://austingroupbugs.net/view.php?id=529>.  */
+   <https://austingroupbugs.net/view.php?id=529>.  */
 
 #ifndef POSIX_CLOSE_RESTART
 # define POSIX_CLOSE_RESTART 1
diff --git a/src/textprop.c b/src/textprop.c
index c22b579af2..ca693fd680 100644
--- a/src/textprop.c
+++ b/src/textprop.c
@@ -1,6 +1,5 @@
 /* Interface code for dealing with text properties.
-   Copyright (C) 1993-1995, 1997, 1999-2022 Free Software Foundation,
-   Inc.
+   Copyright (C) 1993-2022  Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -634,36 +633,40 @@ get_char_property_and_overlay (Lisp_Object position, 
register Lisp_Object prop,
     }
   if (BUFFERP (object))
     {
-      ptrdiff_t noverlays;
-      Lisp_Object *overlay_vec;
-      struct buffer *obuf = current_buffer;
-
-      if (! (BUF_BEGV (XBUFFER (object)) <= pos
-            && pos <= BUF_ZV (XBUFFER (object))))
+      struct buffer *b = XBUFFER (object);
+      struct itree_node *node;
+      struct sortvec items[2];
+      struct sortvec *result = NULL;
+      Lisp_Object result_tem = Qnil;
+
+      if (! (BUF_BEGV (b) <= pos
+            && pos <= BUF_ZV (b)))
        xsignal1 (Qargs_out_of_range, position);
 
-      set_buffer_temp (XBUFFER (object));
-
-      USE_SAFE_ALLOCA;
-      GET_OVERLAYS_AT (pos, overlay_vec, noverlays, NULL, false);
-      noverlays = sort_overlays (overlay_vec, noverlays, w);
-
-      set_buffer_temp (obuf);
-
       /* Now check the overlays in order of decreasing priority.  */
-      while (--noverlays >= 0)
+      ITREE_FOREACH (node, b->overlays, pos, pos + 1, ASCENDING)
        {
-         Lisp_Object tem = Foverlay_get (overlay_vec[noverlays], prop);
-         if (!NILP (tem))
-           {
-             if (overlay)
-               /* Return the overlay we got the property from.  */
-               *overlay = overlay_vec[noverlays];
-             SAFE_FREE ();
-             return tem;
-           }
+         Lisp_Object tem = Foverlay_get (node->data, prop);
+          struct sortvec *this;
+
+         if (NILP (tem) || node->end < pos + 1
+             || (w && ! overlay_matches_window (w, node->data)))
+           continue;
+
+          this = (result == items ? items + 1 : items);
+          make_sortvec_item (this, node->data);
+          if (! result || (compare_overlays (result, this) < 0))
+            {
+              result = this;
+              result_tem = tem;
+            }
        }
-      SAFE_FREE ();
+      if (result)
+        {
+          if (overlay)
+            *overlay = result->overlay;
+          return result_tem;
+        }
     }
 
   if (overlay)
diff --git a/src/w32fns.c b/src/w32fns.c
index 5f652ae9e4..c7eddcba6d 100644
--- a/src/w32fns.c
+++ b/src/w32fns.c
@@ -2734,8 +2734,7 @@ setup_w32_kbdhook (void)
          int i;
 
          CoCreateGuid (&guid);
-         StringFromGUID2 (&guid, newTitle, 64);
-         if (newTitle != NULL)
+         if (oldTitle != NULL && StringFromGUID2 (&guid, newTitle, 64))
            {
              GetConsoleTitleW (oldTitle, 1024);
              SetConsoleTitleW (newTitle);
@@ -8418,7 +8417,7 @@ a ShowWindow flag:
 
   current_dir = ENCODE_FILE (current_dir);
   /* Cannot use filename_to_utf16/ansi with DOCUMENT, since it could
-     be a URL that is not limited to MAX_PATH chararcters.  */
+     be a URL that is not limited to MAX_PATH characters.  */
   doclen = pMultiByteToWideChar (CP_UTF8, multiByteToWideCharFlags,
                                 SSDATA (document), -1, NULL, 0);
   doc_w = xmalloc (doclen * sizeof (wchar_t));
diff --git a/src/window.c b/src/window.c
index 4e8b352e16..f116b9a9d7 100644
--- a/src/window.c
+++ b/src/window.c
@@ -5441,12 +5441,13 @@ window_wants_mode_line (struct window *w)
  * Return 1 if window W wants a header line and is high enough to
  * accommodate it, 0 otherwise.
  *
- * W wants a header line if it's a leaf window and neither a minibuffer
- * nor a pseudo window.  Moreover, its 'window-mode-line-format'
- * parameter must not be 'none' and either that parameter or W's
- * buffer's 'mode-line-format' value must be non-nil.  Finally, W must
- * be higher than its frame's canonical character height and be able to
- * accommodate a mode line too if necessary (the mode line prevails).
+ * W wants a header line if it's a leaf window and neither a
+ * minibuffer nor a pseudo window.  Moreover, its
+ * 'window-header-line-format' parameter must not be 'none' and either
+ * that parameter or W's buffer's 'header-line-format' value must be
+ * non-nil.  Finally, W must be higher than its frame's canonical
+ * character height and be able to accommodate a mode line too if
+ * necessary (the mode line prevails).
  */
 bool
 window_wants_header_line (struct window *w)
@@ -5474,9 +5475,9 @@ window_wants_header_line (struct window *w)
  * accommodate it, 0 otherwise.
  *
  * W wants a tab line if it's a leaf window and neither a minibuffer
- * nor a pseudo window.  Moreover, its 'window-mode-line-format'
+ * nor a pseudo window.  Moreover, its 'window-tab-line-format'
  * parameter must not be 'none' and either that parameter or W's
- * buffer's 'mode-line-format' value must be non-nil.  Finally, W must
+ * buffer's 'tab-line-format' value must be non-nil.  Finally, W must
  * be higher than its frame's canonical character height and be able
  * to accommodate a mode line and a header line too if necessary (the
  * mode line and a header line prevail).
diff --git a/src/window.h b/src/window.h
index 93817a9544..b5d0c69ab5 100644
--- a/src/window.h
+++ b/src/window.h
@@ -1212,6 +1212,16 @@ output_cursor_to (struct window *w, int vpos, int hpos, 
int y, int x)
   w->output_cursor.y = y;
 }
 
+/* Return true, if overlay OV's properties should have an effect in
+   window W. */
+INLINE bool
+overlay_matches_window (const struct window *w, Lisp_Object ov)
+{
+  eassert (OVERLAYP (ov));
+  Lisp_Object  window = Foverlay_get (ov, Qwindow);
+  return (! WINDOWP (window) || XWINDOW (window) == w);
+}
+
 INLINE_HEADER_END
 
 #endif /* not WINDOW_H_INCLUDED */
diff --git a/src/xdisp.c b/src/xdisp.c
index e390de6a33..f6a279636a 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -1,7 +1,6 @@
 /* Display generation from window structure and buffer text.
 
-Copyright (C) 1985-1988, 1993-1995, 1997-2022 Free Software Foundation,
-Inc.
+Copyright (C) 1985-2022  Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -1159,7 +1158,6 @@ static enum move_it_result
 static void get_visually_first_element (struct it *);
 static void compute_stop_pos (struct it *);
 static int face_before_or_after_it_pos (struct it *, bool);
-static ptrdiff_t next_overlay_change (ptrdiff_t);
 static int handle_display_spec (struct it *, Lisp_Object, Lisp_Object,
                                Lisp_Object, struct text_pos *, ptrdiff_t, 
bool);
 static int handle_single_display_spec (struct it *, Lisp_Object, Lisp_Object,
@@ -4168,39 +4166,6 @@ compute_stop_pos (struct it *it)
               && it->stop_charpos >= IT_CHARPOS (*it)));
 }
 
-
-/* Return the position of the next overlay change after POS in
-   current_buffer.  Value is point-max if no overlay change
-   follows.  This is like `next-overlay-change' but doesn't use
-   xmalloc.  */
-
-static ptrdiff_t
-next_overlay_change (ptrdiff_t pos)
-{
-  ptrdiff_t i, noverlays;
-  ptrdiff_t endpos;
-  Lisp_Object *overlays;
-  USE_SAFE_ALLOCA;
-
-  /* Get all overlays at the given position.  */
-  GET_OVERLAYS_AT (pos, overlays, noverlays, &endpos, true);
-
-  /* If any of these overlays ends before endpos,
-     use its ending point instead.  */
-  for (i = 0; i < noverlays; ++i)
-    {
-      Lisp_Object oend;
-      ptrdiff_t oendpos;
-
-      oend = OVERLAY_END (overlays[i]);
-      oendpos = OVERLAY_POSITION (oend);
-      endpos = min (endpos, oendpos);
-    }
-
-  SAFE_FREE ();
-  return endpos;
-}
-
 /* How many characters forward to search for a display property or
    display string.  Searching too far forward makes the bidi display
    sluggish, especially in small windows.  */
@@ -5838,7 +5803,7 @@ handle_single_display_spec (struct it *it, Lisp_Object 
spec, Lisp_Object object,
         overlay's display string/image twice.  */
       if (!NILP (overlay))
        {
-         ptrdiff_t ovendpos = OVERLAY_POSITION (OVERLAY_END (overlay));
+         ptrdiff_t ovendpos = OVERLAY_END (overlay);
 
          /* Some borderline-sane Lisp might call us with the current
             buffer narrowed so that overlay-end is outside the
@@ -6572,6 +6537,8 @@ load_overlay_strings (struct it *it, ptrdiff_t charpos)
   struct overlay_entry entriesbuf[20];
   ptrdiff_t size = ARRAYELTS (entriesbuf);
   struct overlay_entry *entries = entriesbuf;
+  struct itree_node *node;
+
   USE_SAFE_ALLOCA;
 
   if (charpos <= 0)
@@ -6603,27 +6570,24 @@ load_overlay_strings (struct it *it, ptrdiff_t charpos)
     }                                                                  \
   while (false)
 
-  /* Process overlay before the overlay center.  */
-  for (struct Lisp_Overlay *ov = current_buffer->overlays_before;
-       ov; ov = ov->next)
+
+  /* Process overlays.  */
+  ITREE_FOREACH (node, current_buffer->overlays, charpos - 1, charpos + 1, 
DESCENDING)
     {
-      Lisp_Object overlay = make_lisp_ptr (ov, Lisp_Vectorlike);
+      Lisp_Object overlay = node->data;
       eassert (OVERLAYP (overlay));
-      ptrdiff_t start = OVERLAY_POSITION (OVERLAY_START (overlay));
-      ptrdiff_t end = OVERLAY_POSITION (OVERLAY_END (overlay));
-
-      if (end < charpos)
-       break;
+      ptrdiff_t start = node->begin;
+      ptrdiff_t end = node->end;
 
       /* Skip this overlay if it doesn't start or end at IT's current
-        position.  */
+         position.  */
       if (end != charpos && start != charpos)
-       continue;
+        continue;
 
       /* Skip this overlay if it doesn't apply to IT->w.  */
       Lisp_Object window = Foverlay_get (overlay, Qwindow);
       if (WINDOWP (window) && XWINDOW (window) != it->w)
-       continue;
+        continue;
 
       /* If the text ``under'' the overlay is invisible, both before-
         and after-strings from this overlay are visible; start and
@@ -6634,56 +6598,15 @@ load_overlay_strings (struct it *it, ptrdiff_t charpos)
       /* If overlay has a non-empty before-string, record it.  */
       Lisp_Object str;
       if ((start == charpos || (end == charpos && invis != 0))
-         && (str = Foverlay_get (overlay, Qbefore_string), STRINGP (str))
-         && SCHARS (str))
-       RECORD_OVERLAY_STRING (overlay, str, false);
-
-      /* If overlay has a non-empty after-string, record it.  */
-      if ((end == charpos || (start == charpos && invis != 0))
-         && (str = Foverlay_get (overlay, Qafter_string), STRINGP (str))
-         && SCHARS (str))
-       RECORD_OVERLAY_STRING (overlay, str, true);
-    }
-
-  /* Process overlays after the overlay center.  */
-  for (struct Lisp_Overlay *ov = current_buffer->overlays_after;
-       ov; ov = ov->next)
-    {
-      Lisp_Object overlay = make_lisp_ptr (ov, Lisp_Vectorlike);
-      eassert (OVERLAYP (overlay));
-      ptrdiff_t start = OVERLAY_POSITION (OVERLAY_START (overlay));
-      ptrdiff_t end = OVERLAY_POSITION (OVERLAY_END (overlay));
-
-      if (start > charpos)
-       break;
-
-      /* Skip this overlay if it doesn't start or end at IT's current
-        position.  */
-      if (end != charpos && start != charpos)
-       continue;
-
-      /* Skip this overlay if it doesn't apply to IT->w.  */
-      Lisp_Object window = Foverlay_get (overlay, Qwindow);
-      if (WINDOWP (window) && XWINDOW (window) != it->w)
-       continue;
-
-      /* If the text ``under'' the overlay is invisible, it has a zero
-        dimension, and both before- and after-strings apply.  */
-      Lisp_Object invisible = Foverlay_get (overlay, Qinvisible);
-      int invis = TEXT_PROP_MEANS_INVISIBLE (invisible);
-
-      /* If overlay has a non-empty before-string, record it.  */
-      Lisp_Object str;
-      if ((start == charpos || (end == charpos && invis != 0))
-         && (str = Foverlay_get (overlay, Qbefore_string), STRINGP (str))
-         && SCHARS (str))
-       RECORD_OVERLAY_STRING (overlay, str, false);
+          && (str = Foverlay_get (overlay, Qbefore_string), STRINGP (str))
+          && SCHARS (str))
+        RECORD_OVERLAY_STRING (overlay, str, false);
 
       /* If overlay has a non-empty after-string, record it.  */
       if ((end == charpos || (start == charpos && invis != 0))
-         && (str = Foverlay_get (overlay, Qafter_string), STRINGP (str))
-         && SCHARS (str))
-       RECORD_OVERLAY_STRING (overlay, str, true);
+          && (str = Foverlay_get (overlay, Qafter_string), STRINGP (str))
+          && SCHARS (str))
+        RECORD_OVERLAY_STRING (overlay, str, true);
     }
 
 #undef RECORD_OVERLAY_STRING
@@ -7088,11 +7011,11 @@ back_to_previous_line_start (struct it *it)
 static bool
 strings_with_newlines (ptrdiff_t startpos, ptrdiff_t endpos, struct window *w)
 {
-  /* Process overlays before the overlay center.  */
-  for (struct Lisp_Overlay *ov = current_buffer->overlays_before;
-       ov; ov = ov->next)
+  struct itree_node *node;
+  /* Process overlays.  */
+  ITREE_FOREACH (node, current_buffer->overlays, startpos, endpos, DESCENDING)
     {
-      Lisp_Object overlay = make_lisp_ptr (ov, Lisp_Vectorlike);
+      Lisp_Object overlay = node->data;
       eassert (OVERLAYP (overlay));
 
       /* Skip this overlay if it doesn't apply to our window.  */
@@ -7100,14 +7023,8 @@ strings_with_newlines (ptrdiff_t startpos, ptrdiff_t 
endpos, struct window *w)
       if (WINDOWP (window) && XWINDOW (window) != w)
        continue;
 
-      ptrdiff_t ostart = OVERLAY_POSITION (OVERLAY_START (overlay));
-      ptrdiff_t oend = OVERLAY_POSITION (OVERLAY_END (overlay));
-
-      /* Due to the order of overlays in overlays_before, once we get
-        to an overlay whose end position is before STARTPOS, all the
-        rest also end before STARTPOS, and thus are of no concern to us.  */
-      if (oend < startpos)
-       break;
+      ptrdiff_t ostart = node->begin;
+      ptrdiff_t oend = node->end;
 
       /* Skip overlays that don't overlap the range.  */
       if (!((startpos < oend && ostart < endpos)
@@ -7119,49 +7036,17 @@ strings_with_newlines (ptrdiff_t startpos, ptrdiff_t 
endpos, struct window *w)
       str = Foverlay_get (overlay, Qbefore_string);
       if (STRINGP (str) && SCHARS (str)
          && memchr (SDATA (str), '\n', SBYTES (str)))
-       return true;
-      str = Foverlay_get (overlay, Qafter_string);
-      if (STRINGP (str) && SCHARS (str)
-         && memchr (SDATA (str), '\n', SBYTES (str)))
-       return true;
-    }
-
-  /* Process overlays after the overlay center.  */
-  for (struct Lisp_Overlay *ov = current_buffer->overlays_after;
-       ov; ov = ov->next)
-    {
-      Lisp_Object overlay = make_lisp_ptr (ov, Lisp_Vectorlike);
-      eassert (OVERLAYP (overlay));
-
-      /* Skip this overlay if it doesn't apply to our window.  */
-      Lisp_Object window = Foverlay_get (overlay, Qwindow);
-      if (WINDOWP (window) && XWINDOW (window) != w)
-       continue;
-
-      ptrdiff_t ostart = OVERLAY_POSITION (OVERLAY_START (overlay));
-      ptrdiff_t oend = OVERLAY_POSITION (OVERLAY_END (overlay));
-
-      /* Due to the order of overlays in overlays_after, once we get
-        to an overlay whose start position is after ENDPOS, all the
-        rest also start after ENDPOS, and thus are of no concern to us.  */
-      if (ostart > endpos)
-       break;
-
-      /* Skip overlays that don't overlap the range.  */
-      if (!((startpos < oend && ostart < endpos)
-           || (ostart == oend
-               && (startpos == oend || (endpos == ZV && oend == endpos)))))
-       continue;
-
-      Lisp_Object str;
-      str = Foverlay_get (overlay, Qbefore_string);
-      if (STRINGP (str) && SCHARS (str)
-         && memchr (SDATA (str), '\n', SBYTES (str)))
-       return true;
+       {
+         ITREE_FOREACH_ABORT ();
+         return true;
+       }
       str = Foverlay_get (overlay, Qafter_string);
       if (STRINGP (str) && SCHARS (str)
          && memchr (SDATA (str), '\n', SBYTES (str)))
-       return true;
+       {
+         ITREE_FOREACH_ABORT ();
+         return true;
+       }
     }
 
   /* Check for 'display' properties whose values include strings.  */
@@ -7404,7 +7289,7 @@ back_to_previous_visible_line_start (struct it *it)
            && !NILP (val = get_char_property_and_overlay
                      (make_fixnum (pos), Qdisplay, Qnil, &overlay))
            && (OVERLAYP (overlay)
-               ? (beg = OVERLAY_POSITION (OVERLAY_START (overlay)))
+               ? (beg = OVERLAY_START (overlay))
                : get_property_and_range (pos, Qdisplay, &val, &beg, &end, 
Qnil)))
          {
            RESTORE_IT (it, it, it2data);
@@ -10639,7 +10524,6 @@ move_it_to (struct it *it, ptrdiff_t to_charpos, int 
to_x, int to_y, int to_vpos
        }
 
       /* Reset/increment for the next run.  */
-      recenter_overlay_lists (current_buffer, IT_CHARPOS (*it));
       it->current_x = line_start_x;
       line_start_x = 0;
       it->hpos = 0;
@@ -13334,7 +13218,10 @@ unwind_format_mode_line (Lisp_Object vector)
          if (!EQ (frame, WINDOW_FRAME (XWINDOW (old_window))))
            Fselect_window (target_frame_window, Qt);
 
-         if (!NILP (old_top_frame) && !EQ (old_top_frame, frame))
+         if (!NILP (old_top_frame) && !EQ (old_top_frame, frame)
+             /* This could've been destroyed during the formatting,
+                possibly because the terminal was deleted.  */
+             && FRAME_LIVE_P (XFRAME (old_top_frame)))
            Fselect_frame (old_top_frame, Qt);
        }
 
@@ -13437,6 +13324,7 @@ void
 gui_consider_frame_title (Lisp_Object frame)
 {
   struct frame *f = XFRAME (frame);
+  Lisp_Object format_data;
 
   if ((FRAME_WINDOW_P (f)
        || FRAME_MINIBUF_ONLY_P (f)
@@ -13481,15 +13369,19 @@ gui_consider_frame_title (Lisp_Object frame)
       specbind (Qinhibit_redisplay, Qt);
 
       /* Switch to the buffer of selected window of the frame.  Set up
-        mode_line_target so that display_mode_element will output into
-        mode_line_noprop_buf; then display the title.  */
-      record_unwind_protect (unwind_format_mode_line,
-                            format_mode_line_unwind_data
-                            (f, current_buffer, selected_window, false));
+        mode_line_target so that display_mode_element will output
+        into mode_line_noprop_buf; then display the title.  Save the
+        original frame and selected window, and possibly the topmost
+        frame of the tty (for tty frames) into a vector; it will be
+        restored later.  */
+
+      format_data = format_mode_line_unwind_data (f, current_buffer,
+                                                 selected_window,
+                                                 false);
+      record_unwind_protect (unwind_format_mode_line, format_data);
 
       Fselect_window (f->selected_window, Qt);
-      set_buffer_internal_1
-       (XBUFFER (XWINDOW (f->selected_window)->contents));
+      set_buffer_internal_1 (XBUFFER (XWINDOW (f->selected_window)->contents));
       fmt = FRAME_ICONIFIED_P (f) ? Vicon_title_format : Vframe_title_format;
 
       mode_line_target = MODE_LINE_TITLE;
@@ -13502,8 +13394,9 @@ gui_consider_frame_title (Lisp_Object frame)
       /* Make sure that any raw bytes in the title are properly
          represented by their multibyte sequences.  */
       ptrdiff_t nchars = 0;
-      len = str_as_multibyte ((unsigned char *)title,
-                             mode_line_noprop_buf_end - title, len, &nchars);
+      len = str_as_multibyte ((unsigned char *) title,
+                             mode_line_noprop_buf_end - title,
+                             len, &nchars);
       unbind_to (count, Qnil);
 
       /* Set the title only if it's changed.  This avoids consing in
@@ -18917,7 +18810,7 @@ try_cursor_movement (Lisp_Object window, struct 
text_pos startp,
              while (MATRIX_ROW_BOTTOM_Y (row) < last_y
                     && MATRIX_ROW_END_CHARPOS (row) == PT
                     && row < MATRIX_MODE_LINE_ROW (w->current_matrix)
-                    && MATRIX_ROW_START_CHARPOS (row+1) == PT
+                    && MATRIX_ROW_START_CHARPOS (row+1) >= PT
                     && !cursor_row_p (row))
                ++row;
 
@@ -19014,7 +18907,12 @@ try_cursor_movement (Lisp_Object window, struct 
text_pos startp,
                 rc = CURSOR_MOVEMENT_SUCCESS;
            }
 
-         if (PT < MATRIX_ROW_START_CHARPOS (row)
+         if ((PT < MATRIX_ROW_START_CHARPOS (row)
+              && (row == MATRIX_FIRST_TEXT_ROW (w->current_matrix)
+                  /* Don't give up if point is inside invisible text
+                     at the beginning of its glyph row.  */
+                  || (MATRIX_ROW_END_CHARPOS (row-1)
+                      == MATRIX_ROW_START_CHARPOS (row))))
              || PT > MATRIX_ROW_END_CHARPOS (row))
            {
              /* if PT is not in the glyph row, give up.  */
@@ -19096,12 +18994,23 @@ try_cursor_movement (Lisp_Object window, struct 
text_pos startp,
                 continuation lines' rows is implemented for
                 bidi-reordered rows.  */
              bool rv = false;
+             bool pt_invis = false;
+             Lisp_Object val = get_char_property_and_overlay (make_fixnum (PT),
+                                                              Qinvisible,
+                                                              Qnil, NULL);
+
+             if (TEXT_PROP_MEANS_INVISIBLE (val) != 0)
+               pt_invis = true;
 
              do
                {
                  bool at_zv_p = false, exact_match_p = false;
 
-                 if (MATRIX_ROW_START_CHARPOS (row) <= PT
+                 /* If point is in invisible text, we cannot assume
+                    it must be after row's start position, since the
+                    row could have invisible text at its beginning
+                    where point is located.  */
+                 if ((pt_invis || MATRIX_ROW_START_CHARPOS (row) <= PT)
                      && PT <= MATRIX_ROW_END_CHARPOS (row)
                      && cursor_row_p (row))
                    rv |= set_cursor_from_row (w, row, w->current_matrix,
@@ -19133,16 +19042,8 @@ try_cursor_movement (Lisp_Object window, struct 
text_pos startp,
                             invisible text?  In that case, we trust
                             'set_cursor_from_row' to do its job and
                             find the best position for the cursor.  */
-                         if (!exact_match_p)
-                           {
-                             Lisp_Object val =
-                               get_char_property_and_overlay (make_fixnum (PT),
-                                                              Qinvisible,
-                                                              Qnil, NULL);
-
-                             if (TEXT_PROP_MEANS_INVISIBLE (val) != 0)
-                               exact_match_p = true;
-                           }
+                         if (!exact_match_p && pt_invis)
+                           exact_match_p = true;
                        }
                      if (at_zv_p || exact_match_p)
                        {
@@ -23252,10 +23153,18 @@ extend_face_to_end_of_line (struct it *it)
      this is called when redisplaying a non-selected window, with
      point temporarily moved to window-point.  */
   specbind (Qinhibit_quit, Qt);
-  const int extend_face_id = (it->face_id == DEFAULT_FACE_ID
-                              || it->s != NULL)
-    ? DEFAULT_FACE_ID
-    : face_at_pos (it, LFACE_EXTEND_INDEX);
+  /* The default face, possibly remapped. */
+  struct face *default_face =
+    FACE_FROM_ID_OR_NULL (f, lookup_basic_face (it->w, f, DEFAULT_FACE_ID));
+  if (!default_face)
+    return;
+
+  const int extend_face_id =
+    (it->face_id == default_face->id || it->s != NULL)
+    ? it->face_id
+    : (it->glyph_row->ends_at_zv_p
+       ? default_face->id
+       : face_at_pos (it, LFACE_EXTEND_INDEX));
   unbind_to (count, Qnil);
 
   /* Face extension extends the background and box of IT->extend_face_id
@@ -23292,14 +23201,8 @@ extend_face_to_end_of_line (struct it *it)
   if (!ASCII_CHAR_P (it->c))
     it->face_id = FACE_FOR_CHAR (f, face, 0, -1, Qnil);
 
-  /* The default face, possibly remapped. */
-  struct face *default_face =
-    FACE_FROM_ID (f, lookup_basic_face (it->w, f, DEFAULT_FACE_ID));
 
 #ifdef HAVE_WINDOW_SYSTEM
-  if (default_face == NULL)
-    error ("extend_face_to_end_of_line: default_face is not set!");
-
   if (FRAME_WINDOW_P (f))
     {
       /* If the row is empty, add a space with the current face of IT,
@@ -24601,13 +24504,6 @@ display_line (struct it *it, int cursor_vpos)
   it->stretch_adjust = 0;
   it->line_number_produced_p = false;
 
-  /* Arrange the overlays nicely for our purposes.  Usually, we call
-     display_line on only one line at a time, in which case this
-     can't really hurt too much, or we call it on lines which appear
-     one after another in the buffer, in which case all calls to
-     recenter_overlay_lists but the first will be pretty cheap.  */
-  recenter_overlay_lists (current_buffer, IT_CHARPOS (*it));
-
   /* If we are going to display the cursor's line, account for the
      hscroll of that line.  We subtract the window's min_hscroll,
      because that was already accounted for in init_iterator.  */
@@ -33668,8 +33564,14 @@ coords_in_mouse_face_p (struct window *w, int hpos, 
int vpos)
 bool
 cursor_in_mouse_face_p (struct window *w)
 {
-  int hpos = w->phys_cursor.hpos;
   int vpos = w->phys_cursor.vpos;
+
+  /* If the cursor is outside the matrix glyph rows, it cannot be
+     within the mouse face.  */
+  if (!(0 <= vpos && vpos < w->current_matrix->nrows))
+    return false;
+
+  int hpos = w->phys_cursor.hpos;
   struct glyph_row *row = MATRIX_ROW (w->current_matrix, vpos);
 
   /* When the window is hscrolled, cursor hpos can legitimately be out
@@ -34912,7 +34814,7 @@ note_mouse_highlight (struct frame *f, int x, int y)
   struct buffer *b;
 
   /* When a menu is active, don't highlight because this looks odd.  */
-#if defined (USE_X_TOOLKIT) || (defined (USE_GTK) && !defined (HAVE_PGTK)) || 
defined (HAVE_NS) || defined (MSDOS)
+#if defined (HAVE_X_WINDOWS) || defined (HAVE_NS) || defined (MSDOS)
   if (popup_activated ())
     return;
 #endif
@@ -35239,7 +35141,7 @@ note_mouse_highlight (struct frame *f, int x, int y)
       if (BUFFERP (object))
        {
          /* Put all the overlays we want in a vector in overlay_vec.  */
-         GET_OVERLAYS_AT (pos, overlay_vec, noverlays, NULL, false);
+         GET_OVERLAYS_AT (pos, overlay_vec, noverlays, NULL);
          /* Sort overlays into increasing priority order.  */
          noverlays = sort_overlays (overlay_vec, noverlays, w);
        }
@@ -35267,7 +35169,7 @@ note_mouse_highlight (struct frame *f, int x, int y)
          || (!hlinfo->mouse_face_hidden
              && OVERLAYP (hlinfo->mouse_face_overlay)
              /* It's possible the overlay was deleted (Bug#35273).  */
-              && XMARKER (OVERLAY_START (hlinfo->mouse_face_overlay))->buffer
+              && OVERLAY_BUFFER (hlinfo->mouse_face_overlay)
               && mouse_face_overlay_overlaps (hlinfo->mouse_face_overlay)))
        {
          /* Find the highest priority overlay with a mouse-face.  */
diff --git a/src/xfaces.c b/src/xfaces.c
index 5e3a47d7f8..ed76db9adb 100644
--- a/src/xfaces.c
+++ b/src/xfaces.c
@@ -6540,8 +6540,7 @@ face_at_buffer_position (struct window *w, ptrdiff_t pos,
   USE_SAFE_ALLOCA;
   {
     ptrdiff_t next_overlay;
-
-    GET_OVERLAYS_AT (pos, overlay_vec, noverlays, &next_overlay, false);
+    GET_OVERLAYS_AT (pos, overlay_vec, noverlays, &next_overlay);
     if (next_overlay < endpos)
       endpos = next_overlay;
   }
@@ -6594,7 +6593,6 @@ face_at_buffer_position (struct window *w, ptrdiff_t pos,
     {
       for (prop = Qnil, i = noverlays - 1; i >= 0 && NILP (prop); --i)
        {
-         Lisp_Object oend;
          ptrdiff_t oendpos;
 
          prop = Foverlay_get (overlay_vec[i], propname);
@@ -6607,8 +6605,7 @@ face_at_buffer_position (struct window *w, ptrdiff_t pos,
              merge_face_ref (w, f, prop, attrs, true, NULL, attr_filter);
            }
 
-         oend = OVERLAY_END (overlay_vec[i]);
-         oendpos = OVERLAY_POSITION (oend);
+         oendpos = OVERLAY_END (overlay_vec[i]);
          if (oendpos < endpos)
            endpos = oendpos;
        }
@@ -6617,7 +6614,6 @@ face_at_buffer_position (struct window *w, ptrdiff_t pos,
     {
       for (i = 0; i < noverlays; i++)
        {
-         Lisp_Object oend;
          ptrdiff_t oendpos;
 
          prop = Foverlay_get (overlay_vec[i], propname);
@@ -6625,11 +6621,10 @@ face_at_buffer_position (struct window *w, ptrdiff_t 
pos,
          if (!NILP (prop))
            merge_face_ref (w, f, prop, attrs, true, NULL, attr_filter);
 
-         oend = OVERLAY_END (overlay_vec[i]);
-         oendpos = OVERLAY_POSITION (oend);
-         if (oendpos < endpos)
-           endpos = oendpos;
-       }
+          oendpos = OVERLAY_END (overlay_vec[i]);
+          if (oendpos < endpos)
+            endpos = oendpos;
+        }
     }
 
   *endptr = endpos;
diff --git a/src/xfns.c b/src/xfns.c
index 9112448899..3ff7a8c286 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -7913,7 +7913,7 @@ Otherwise, the return value is a vector with the 
following fields:
 
 DEFUN ("x-translate-coordinates", Fx_translate_coordinates,
        Sx_translate_coordinates,
-       1, 5, 0, doc: /* Translate coordinates from FRAME.
+       1, 6, 0, doc: /* Translate coordinates from FRAME.
 Translate the given coordinates SOURCE-X and SOURCE-Y from
 SOURCE-WINDOW's coordinate space to that of DEST-WINDOW, on FRAME.
 
@@ -7929,16 +7929,21 @@ Return a list of (X Y CHILD) if the given coordinates 
are on the same
 screen, or nil otherwise, where X and Y are the coordinates in
 DEST-WINDOW's coordinate space, and CHILD is the window ID of any
 mapped child in DEST-WINDOW at those coordinates, or nil if there is
-no such window.  */)
+no such window.  If REQUIRE-CHILD is nil, avoid fetching CHILD if it
+would result in an avoidable request to the X server, thereby
+improving performance when the X connection is over a slow network.
+Otherwise, always obtain the mapped child window from the X
+server.  */)
   (Lisp_Object frame, Lisp_Object source_window,
    Lisp_Object dest_window, Lisp_Object source_x,
-   Lisp_Object source_y)
+   Lisp_Object source_y, Lisp_Object require_child)
 {
   struct x_display_info *dpyinfo;
   struct frame *source_frame;
   int dest_x, dest_y;
   Window child_return, src, dest;
   Bool rc;
+  Lisp_Object temp_result;
 
   dpyinfo = check_x_display_info (frame);
   dest_x = 0;
@@ -7956,6 +7961,8 @@ no such window.  */)
       dest_y = XFIXNUM (source_y);
     }
 
+  source_frame = NULL;
+
   if (!NILP (source_window))
     CONS_TO_INTEGER (source_window, Window, src);
   else
@@ -7964,6 +7971,17 @@ no such window.  */)
       src = FRAME_X_WINDOW (source_frame);
     }
 
+  /* If require_child is nil, try to avoid an avoidable roundtrip to
+     the X server.  */
+  if (NILP (require_child) && source_frame)
+    {
+      temp_result
+       = x_handle_translate_coordinates (source_frame, dest_window, dest_x,
+                                         dest_y);
+      if (!NILP (temp_result))
+       return temp_result;
+    }
+
   if (!src)
     src = dpyinfo->root_window;
 
@@ -8443,7 +8461,17 @@ compute_tip_xy (struct frame *f, Lisp_Object parms, 
Lisp_Object dx,
       unblock_input ();
 
       XSETFRAME (frame, f);
-      attributes = Fx_display_monitor_attributes_list (frame);
+
+#if defined HAVE_XRANDR || defined USE_GTK
+      if (!NILP (FRAME_DISPLAY_INFO (f)->last_monitor_attributes_list))
+       /* Use cached values if available to avoid fetching the
+          monitor list from the X server.  If XRandR is not
+          available, then fetching the attributes will probably not
+          sync anyway, and will thus be relatively harmless.  */
+       attributes = FRAME_DISPLAY_INFO (f)->last_monitor_attributes_list;
+      else
+#endif
+       attributes = Fx_display_monitor_attributes_list (frame);
 
       /* Try to determine the monitor where the mouse pointer is and
          its geometry.  See bug#22549.  */
@@ -8693,9 +8721,6 @@ Text larger than the specified size is clipped.  */)
   int old_windows_or_buffers_changed = windows_or_buffers_changed;
   specpdl_ref count = SPECPDL_INDEX ();
   Lisp_Object window, size, tip_buf;
-  Window child;
-  XWindowAttributes child_attrs;
-  int dest_x_return, dest_y_return;
   bool displayed;
 #ifdef ENABLE_CHECKING
   struct glyph_row *row, *end;
@@ -8946,41 +8971,6 @@ Text larger than the specified size is clipped.  */)
 
   /* Show tooltip frame.  */
   block_input ();
-  /* If the display is composited, then WM_TRANSIENT_FOR must be set
-     as well, or else the compositing manager won't display
-     decorations correctly, even though the tooltip window is override
-     redirect. See
-     https://specifications.freedesktop.org/wm-spec/1.4/ar01s08.html
-
-     Perhaps WM_TRANSIENT_FOR should be used in place of
-     override-redirect anyway.  The ICCCM only recommends
-     override-redirect if the pointer will be grabbed.  */
-
-  if (XTranslateCoordinates (FRAME_X_DISPLAY (f),
-                            FRAME_DISPLAY_INFO (f)->root_window,
-                            FRAME_DISPLAY_INFO (f)->root_window,
-                            root_x, root_y, &dest_x_return,
-                            &dest_y_return, &child)
-      && child != None)
-    {
-      /* But only if the child is not override-redirect, which can
-        happen if the pointer is above a menu.  */
-
-      if (XGetWindowAttributes (FRAME_X_DISPLAY (f),
-                               child, &child_attrs)
-         || child_attrs.override_redirect)
-       XDeleteProperty (FRAME_X_DISPLAY (tip_f),
-                        FRAME_X_WINDOW (tip_f),
-                        FRAME_DISPLAY_INFO (tip_f)->Xatom_wm_transient_for);
-      else
-       XSetTransientForHint (FRAME_X_DISPLAY (tip_f),
-                             FRAME_X_WINDOW (tip_f), child);
-    }
-  else
-    XDeleteProperty (FRAME_X_DISPLAY (tip_f),
-                    FRAME_X_WINDOW (tip_f),
-                    FRAME_DISPLAY_INFO (tip_f)->Xatom_wm_transient_for);
-
 #ifndef USE_XCB
   XMoveResizeWindow (FRAME_X_DISPLAY (tip_f), FRAME_X_WINDOW (tip_f),
                     root_x, root_y, width, height);
@@ -9452,13 +9442,21 @@ usual X keysyms.  Value is `lambda' if we cannot 
determine if both keys are
 present and mapped to the usual X keysyms.  */)
   (Lisp_Object frame)
 {
+#ifdef HAVE_XKB
+  XkbDescPtr kb;
+  struct frame *f;
+  Display *dpy;
+  Lisp_Object have_keys;
+  int delete_keycode, backspace_keycode, i;
+#endif
+
 #ifndef HAVE_XKB
   return Qlambda;
 #else
-  XkbDescPtr kb;
-  struct frame *f = decode_window_system_frame (frame);
-  Display *dpy = FRAME_X_DISPLAY (f);
-  Lisp_Object have_keys;
+  delete_keycode = 0;
+  backspace_keycode = 0;
+  f = decode_window_system_frame (frame);
+  dpy = FRAME_X_DISPLAY (f);
 
   if (!FRAME_DISPLAY_INFO (f)->supports_xkb)
     return Qlambda;
@@ -9474,50 +9472,39 @@ present and mapped to the usual X keysyms.  */)
      XK_Delete are mapped to any key.  But if any of those are mapped to
      some non-intuitive key combination (Meta-Shift-Ctrl-whatever) and the
      user doesn't know about it, it is better to return false here.
-     It is more obvious to the user what to do if she/he has two keys
+     It is more obvious to the user what to do if there are two keys
      clearly marked with names/symbols and one key does something not
-     expected (i.e. she/he then tries the other).
+     expected (and the user then tries the other).
      The cases where Backspace/Delete is mapped to some other key combination
      are rare, and in those cases, normal-erase-is-backspace can be turned on
      manually.  */
 
   have_keys = Qnil;
-  kb = XkbGetMap (dpy, XkbAllMapComponentsMask, XkbUseCoreKbd);
-  if (kb)
+  kb = FRAME_DISPLAY_INFO (f)->xkb_desc;
+  if (kb && kb->names)
     {
-      int delete_keycode = 0, backspace_keycode = 0, i;
-
-      if (XkbGetNames (dpy, XkbAllNamesMask, kb) == Success)
+      for (i = kb->min_key_code; (i < kb->max_key_code
+                                 && (delete_keycode == 0
+                                     || backspace_keycode == 0));
+          ++i)
        {
-         for (i = kb->min_key_code;
-              (i < kb->max_key_code
-               && (delete_keycode == 0 || backspace_keycode == 0));
-              ++i)
-           {
-             /* The XKB symbolic key names can be seen most easily in
-                the PS file generated by `xkbprint -label name
-                $DISPLAY'.  */
-             if (memcmp ("DELE", kb->names->keys[i].name, 4) == 0)
-               delete_keycode = i;
-             else if (memcmp ("BKSP", kb->names->keys[i].name, 4) == 0)
-               backspace_keycode = i;
-           }
-
-         XkbFreeNames (kb, 0, True);
+         /* The XKB symbolic key names can be seen most easily in
+            the PS file generated by `xkbprint -label name
+            $DISPLAY'.  */
+         if (!memcmp ("DELE", kb->names->keys[i].name, 4))
+           delete_keycode = i;
+         else if (!memcmp ("BKSP", kb->names->keys[i].name, 4))
+           backspace_keycode = i;
        }
 
-      /* As of libX11-1.6.2, XkbGetMap manual says that you should use
-        XkbFreeClientMap to free the data returned by XkbGetMap.  But
-        this function just frees the data referenced from KB and not
-        KB itself.  To free KB as well, call XkbFreeKeyboard.  */
-      XkbFreeKeyboard (kb, XkbAllMapComponentsMask, True);
-
-      if (delete_keycode
-         && backspace_keycode
+      if (delete_keycode && backspace_keycode
          && XKeysymToKeycode (dpy, XK_Delete) == delete_keycode
          && XKeysymToKeycode (dpy, XK_BackSpace) == backspace_keycode)
        have_keys = Qt;
     }
+  else
+    /* The keyboard names couldn't be obtained for some reason.  */
+    have_keys = Qlambda;
   unblock_input ();
   return have_keys;
 #endif
diff --git a/src/xmenu.c b/src/xmenu.c
index 1452b3c6d1..756842c2fe 100644
--- a/src/xmenu.c
+++ b/src/xmenu.c
@@ -294,10 +294,13 @@ x_menu_translate_generic_event (XEvent *event)
 #endif
 
 #if !defined USE_X_TOOLKIT && !defined USE_GTK
-static void
-x_menu_expose_event (XEvent *event)
+static int
+x_menu_dispatch_event (XEvent *event)
 {
   x_dispatch_event (event, event->xexpose.display);
+
+  /* The return doesn't really matter.  */
+  return 0;
 }
 #endif
 #endif /* ! MSDOS */
@@ -1521,26 +1524,15 @@ create_and_show_popup_menu (struct frame *f, 
widget_value *first_wv,
 
   if (use_pos_func)
     {
-      Window dummy_window;
-
       /* Not invoked by a click.  pop up at x/y.  */
       pos_func = menu_position_func;
 
       /* Adjust coordinates to be root-window-relative.  */
       block_input ();
-      XTranslateCoordinates (FRAME_X_DISPLAY (f),
-
-                             /* From-window, to-window.  */
-                             FRAME_X_WINDOW (f),
-                             FRAME_DISPLAY_INFO (f)->root_window,
-
-                             /* From-position, to-position.  */
-                             x, y, &x, &y,
-
-                             /* Child of win.  */
-                             &dummy_window);
+      x_translate_coordinates_to_root (f, x, y, &x, &y);
 #ifdef HAVE_GTK3
-      /* Use window scaling factor to adjust position for hidpi screens. */
+      /* Use window scaling factor to adjust position for scaled
+        outputs.  */
       x /= xg_get_scale (f);
       y /= xg_get_scale (f);
 #endif
@@ -1743,7 +1735,6 @@ create_and_show_popup_menu (struct frame *f, widget_value 
*first_wv,
   XButtonPressedEvent *event = &(dummy.xbutton);
   LWLIB_ID menu_id;
   Widget menu;
-  Window dummy_window;
 #if defined HAVE_XINPUT2 && defined USE_MOTIF
   XEvent property_dummy;
   Atom property_atom;
@@ -1775,17 +1766,7 @@ create_and_show_popup_menu (struct frame *f, 
widget_value *first_wv,
   /* Adjust coordinates to be root-window-relative.  */
   block_input ();
   x += FRAME_LEFT_SCROLL_BAR_AREA_WIDTH (f);
-  XTranslateCoordinates (FRAME_X_DISPLAY (f),
-
-                         /* From-window, to-window.  */
-                         FRAME_X_WINDOW (f),
-                         FRAME_DISPLAY_INFO (f)->root_window,
-
-                         /* From-position, to-position.  */
-                         x, y, &x, &y,
-
-                         /* Child of win.  */
-                         &dummy_window);
+  x_translate_coordinates_to_root (f, x, y, &x, &y);
   unblock_input ();
 
   event->x_root = x;
@@ -2559,6 +2540,8 @@ pop_down_menu (void *arg)
     }
 #endif
 
+  /* Decrement the popup_activated_flag.  */
+  popup_activated_flag = 0;
 #endif /* HAVE_X_WINDOWS */
 
   unblock_input ();
@@ -2569,9 +2552,6 @@ Lisp_Object
 x_menu_show (struct frame *f, int x, int y, int menuflags,
             Lisp_Object title, const char **error_name)
 {
-#ifdef HAVE_X_WINDOWS
-  Window dummy_window;
-#endif
   Window root;
   XMenu *menu;
   int pane, selidx, lpane, status;
@@ -2620,17 +2600,7 @@ x_menu_show (struct frame *f, int x, int y, int 
menuflags,
   inhibit_garbage_collection ();
 
 #ifdef HAVE_X_WINDOWS
-  XTranslateCoordinates (FRAME_X_DISPLAY (f),
-
-                         /* From-window, to-window.  */
-                         FRAME_X_WINDOW (f),
-                         FRAME_DISPLAY_INFO (f)->root_window,
-
-                         /* From-position, to-position.  */
-                         x, y, &x, &y,
-
-                         /* Child of win.  */
-                         &dummy_window);
+  x_translate_coordinates_to_root (f, x, y, &x, &y);
 #else
   /* MSDOS without X support.  */
   x += f->left_pos;
@@ -2782,21 +2752,22 @@ x_menu_show (struct frame *f, int x, int y, int 
menuflags,
       y += 1.5 * height/ (maxlines + 2);
     }
 
-  XMenuSetAEQ (menu, true);
   XMenuSetFreeze (menu, true);
   pane = selidx = 0;
 
 #ifndef MSDOS
   DEFER_SELECTIONS;
 
-  XMenuActivateSetWaitFunction (x_menu_wait_for_event, FRAME_X_DISPLAY (f));
+  XMenuActivateSetWaitFunction (x_menu_wait_for_event,
+                               FRAME_X_DISPLAY (f));
+  XMenuEventHandler (x_menu_dispatch_event);
+
   /* When the input extension is in use, the owner_events grab will
      report extension events on frames, which the XMenu library does
      not normally understand.  */
 #ifdef HAVE_XINPUT2
   XMenuActivateSetTranslateFunction (x_menu_translate_generic_event);
 #endif
-  XMenuActivateSetExposeFunction (x_menu_expose_event);
 #endif
 
   record_unwind_protect_ptr (pop_down_menu,
@@ -2822,6 +2793,12 @@ x_menu_show (struct frame *f, int x, int y, int 
menuflags,
     }
 #endif
 
+#ifdef HAVE_X_WINDOWS
+  /* Increment the popup flag; this prevents nested popups from being
+     displayed by user Lisp code in help-echo callbacks, and also
+     prevents mouse face from being displayed.  */
+  popup_activated_flag = 1;
+#endif
   status = XMenuActivate (FRAME_X_DISPLAY (f), menu, &pane, &selidx,
                           x, y, ButtonReleaseMask, &datap,
                           menu_help_callback);
diff --git a/src/xselect.c b/src/xselect.c
index 66782d4172..9480ac18c1 100644
--- a/src/xselect.c
+++ b/src/xselect.c
@@ -918,6 +918,13 @@ x_handle_selection_request (struct selection_input_event 
*event)
        }
       /* Save conversion results */
       lisp_data_to_selection_data (dpyinfo, multprop, &cs);
+
+      /* If cs.type is ATOM, change it to ATOM_PAIR.  This is because
+        the parameters to a MULTIPLE are ATOM_PAIRs.  */
+
+      if (cs.type == XA_ATOM)
+       cs.type = dpyinfo->Xatom_ATOM_PAIR;
+
       XChangeProperty (dpyinfo->display, requestor, property,
                       cs.type, cs.format, PropModeReplace,
                       cs.data, cs.size);
@@ -2376,12 +2383,19 @@ On Nextstep, TERMINAL is unused.  */)
 {
   Window owner;
   Atom atom;
+#ifdef HAVE_XFIXES
+  Window temp_owner;
+#endif
   struct frame *f = frame_for_x_selection (terminal);
   struct x_display_info *dpyinfo;
 
   CHECK_SYMBOL (selection);
-  if (NILP (selection)) selection = QPRIMARY;
-  if (EQ (selection, Qt)) selection = QSECONDARY;
+
+  if (NILP (selection))
+    selection = QPRIMARY;
+
+  if (EQ (selection, Qt))
+    selection = QSECONDARY;
 
   if (!f)
     return Qnil;
@@ -2392,10 +2406,22 @@ On Nextstep, TERMINAL is unused.  */)
     return Qt;
 
   atom = symbol_to_x_atom (dpyinfo, selection);
-  if (atom == 0) return Qnil;
+
+  if (!atom)
+    return Qnil;
+
+#ifdef HAVE_XFIXES
+  /* See if this information can be obtained without a roundtrip.  */
+  temp_owner = x_find_selection_owner (dpyinfo, atom);
+
+  if (temp_owner != X_INVALID_WINDOW)
+    return (temp_owner != None ? Qt : Qnil);
+#endif
+
   block_input ();
   owner = XGetSelectionOwner (dpyinfo->display, atom);
   unblock_input ();
+
   return (owner ? Qt : Qnil);
 }
 
@@ -2768,7 +2794,6 @@ x_handle_dnd_message (struct frame *f, const 
XClientMessageEvent *event,
   unsigned char *data = (unsigned char *) event->data.b;
   int idata[5];
   ptrdiff_t i;
-  Window child_return;
 
   for (i = 0; i < dpyinfo->x_dnd_atoms_length; ++i)
     if (dpyinfo->x_dnd_atoms[i] == event->message_type) break;
@@ -2803,11 +2828,7 @@ x_handle_dnd_message (struct frame *f, const 
XClientMessageEvent *event,
   if (!root_window_coords)
     x_relative_mouse_position (f, &x, &y);
   else
-    XTranslateCoordinates (dpyinfo->display,
-                          dpyinfo->root_window,
-                          FRAME_X_WINDOW (f),
-                          root_x, root_y,
-                          &x, &y, &child_return);
+    x_translate_coordinates (f, root_x, root_y, &x, &y);
 
   bufp->kind = DRAG_N_DROP_EVENT;
   bufp->frame_or_window = frame;
diff --git a/src/xsettings.c b/src/xsettings.c
index e4a9865d68..15e7ff5499 100644
--- a/src/xsettings.c
+++ b/src/xsettings.c
@@ -54,12 +54,14 @@ typedef unsigned int CARD32;
 #include <gconf/gconf-client.h>
 #endif
 
-#if defined USE_CAIRO || defined HAVE_XFT
 #ifdef USE_CAIRO
 #include <fontconfig/fontconfig.h>
-#else  /* HAVE_XFT */
+#elif defined HAVE_XFT
 #include <X11/Xft/Xft.h>
 #endif
+
+#if defined USE_CAIRO && defined CAIRO_HAS_FT_FONT
+#include <cairo/cairo-ft.h>
 #endif
 
 static char *current_mono_font;
@@ -804,16 +806,30 @@ static void
 apply_xft_settings (Display_Info *dpyinfo,
                     struct xsettings *settings)
 {
-#ifdef HAVE_XFT
+#if defined HAVE_XFT                                   \
+  || (defined USE_CAIRO && defined CAIRO_HAS_FC_FONT   \
+      && defined CAIRO_HAS_FT_FONT)
   FcPattern *pat;
   struct xsettings oldsettings;
   bool changed = false;
+#ifndef HAVE_XFT
+  cairo_font_options_t *options;
+#endif
+
 
   memset (&oldsettings, 0, sizeof (oldsettings));
   pat = FcPatternCreate ();
+#ifdef HAVE_XFT
   XftDefaultSubstitute (dpyinfo->display,
                         XScreenNumberOfScreen (dpyinfo->screen),
                         pat);
+#else
+  FcConfigSubstitute (NULL, pat, FcMatchPattern);
+  options = cairo_font_options_create ();
+  cairo_ft_font_options_substitute (options, pat);
+  cairo_font_options_destroy (options);
+  FcDefaultSubstitute (pat);
+#endif
   FcPatternGetBool (pat, FC_ANTIALIAS, 0, &oldsettings.aa);
   FcPatternGetBool (pat, FC_HINTING, 0, &oldsettings.hinting);
 #ifdef FC_HINT_STYLE
@@ -912,8 +928,11 @@ apply_xft_settings (Display_Info *dpyinfo,
                     - sizeof "%f")
       };
       char buf[sizeof format + d_formats * d_growth + lf_formats * lf_growth];
-
+#ifdef HAVE_XFT
       XftDefaultSet (dpyinfo->display, pat);
+#else
+      FcPatternDestroy (pat);
+#endif
       store_config_changed_event (Qfont_render,
                                  XCAR (dpyinfo->name_list_element));
       Vxft_settings
@@ -925,7 +944,7 @@ apply_xft_settings (Display_Info *dpyinfo,
     }
   else
     FcPatternDestroy (pat);
-#endif /* HAVE_XFT */
+#endif /* HAVE_XFT || (USE_CAIRO && CAIRO_HAS_FC_FONT && CAIRO_HAS_FT_FONT) */
 }
 #endif
 
diff --git a/src/xterm.c b/src/xterm.c
index 0fca9788ce..7a1fd6086c 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -912,11 +912,6 @@ struct x_selection_request_event
 
 struct x_selection_request_event *pending_selection_requests;
 
-/* Compare two request serials A and B with OP, handling
-   wraparound.  */
-#define X_COMPARE_SERIALS(a, op ,b) \
-  (((long) (a) - (long) (b)) op 0)
-
 struct x_atom_ref
 {
   /* Atom name.  */
@@ -1143,7 +1138,6 @@ static Window x_get_window_below (Display *, Window, int, 
int, int *, int *);
 #ifndef USE_TOOLKIT_SCROLL_BARS
 static void x_scroll_bar_redraw (struct scroll_bar *);
 #endif
-static void x_translate_coordinates (struct frame *, int, int, int *, int *);
 
 /* Global state maintained during a drag-and-drop operation.  */
 
@@ -5246,7 +5240,9 @@ xi_convert_button_state (XIButtonState *in, unsigned int 
*out)
     }
 }
 
-/* Return the modifier state in XEV as a standard X modifier mask.  */
+/* Return the modifier state in XEV as a standard X modifier mask.
+   This should be used for non-keyboard events, where the group does
+   not matter.  */
 
 #ifdef USE_GTK
 static
@@ -5264,6 +5260,17 @@ xi_convert_event_state (XIDeviceEvent *xev)
   return mods | buttons;
 }
 
+/* Like the above.  However, buttons are not converted, while the
+   group is.  This should be used for key events being passed to the
+   likes of input methods and Xt.  */
+
+static unsigned int
+xi_convert_event_keyboard_state (XIDeviceEvent *xev)
+{
+  return ((xev->mods.effective & ~(1 << 13 | 1 << 14))
+         | (xev->group.effective << 13));
+}
+
 /* Free all XI2 devices on DPYINFO.  */
 static void
 x_free_xi_devices (struct x_display_info *dpyinfo)
@@ -5302,6 +5309,7 @@ x_free_xi_devices (struct x_display_info *dpyinfo)
 }
 
 #ifdef HAVE_XINPUT2_1
+
 struct xi_known_valuator
 {
   /* The current value of this valuator.  */
@@ -5313,20 +5321,21 @@ struct xi_known_valuator
   /* The next valuator whose value we already know.  */
   struct xi_known_valuator *next;
 };
+
 #endif
 
 static void
-xi_populate_device_from_info (struct xi_device_t *xi_device,
+xi_populate_device_from_info (struct x_display_info *dpyinfo,
+                             struct xi_device_t *xi_device,
                              XIDeviceInfo *device)
 {
 #ifdef HAVE_XINPUT2_1
   struct xi_scroll_valuator_t *valuator;
   struct xi_known_valuator *values, *tem;
-  int actual_valuator_count;
+  int actual_valuator_count, c;
   XIScrollClassInfo *info;
-  XIValuatorClassInfo *val_info;
+  XIValuatorClassInfo *valuator_info;
 #endif
-  int c;
 #ifdef HAVE_XINPUT2_2
   XITouchClassInfo *touch_info;
 #endif
@@ -5335,30 +5344,90 @@ xi_populate_device_from_info (struct xi_device_t 
*xi_device,
   USE_SAFE_ALLOCA;
 #endif
 
+  /* Initialize generic information about the device: its ID, which
+     buttons are currently pressed and thus presumably actively
+     grabbing the device, what kind of device it is (master pointer,
+     master keyboard, slave pointer, slave keyboard, or floating
+     slave), and its attachment.
+
+     Here is a brief description of what device uses and attachments
+     are.  Under XInput 2, user input from individual input devices is
+     multiplexed into specific seats before being delivered, with each
+     seat corresponding to a single on-screen mouse pointer and having
+     its own keyboard focus.  Each seat consists of two virtual
+     devices: the master keyboard and the master pointer, the first of
+     which is used to report all keyboard input, with the other used
+     to report all other input.
+
+     Input from each physical device (mouse, trackpad or keyboard) is
+     then associated with that slave device's paired master device.
+     For example, moving the device "Logitech USB Optical Mouse",
+     enslaved by the master pointer device "Virtual core pointer",
+     will result in movement of the mouse pointer associated with that
+     master device's seat.  If the pointer moves over an Emacs frame,
+     then the frame will receive XI_Enter and XI_Motion events from
+     that master pointer.
+
+     Likewise, keyboard input from the device "AT Translated Set 2
+     keyboard", enslaved by the master keyboard device "Virtual core
+     keyboard", will be reported to its seat's input focus window.
+
+     The device use describes what the device is.  The meanings of
+     MasterPointer, MasterKeyboard, SlavePointer and SlaveKeyboard
+     should be obvious.  FloatingSlave means the device is a slave
+     device that is not associated with any seat, and thus generates
+     no input.
+
+     The device attachment is a device ID whose meaning varies
+     depending on the device use.  If the device is a master device,
+     then the attachment is the device ID of the other device in its
+     seat (the master keyboard for master pointer devices, and vice
+     versa).  Otherwise, it is the ID of the master device the slave
+     device is attached to.  For slave devices not attached to any
+     seat, its value is undefined.  */
+
   xi_device->device_id = device->deviceid;
   xi_device->grab = 0;
-
-#ifdef HAVE_XINPUT2_1
-  actual_valuator_count = 0;
-  xi_device->valuators = xmalloc (sizeof *xi_device->valuators
-                                 * device->num_classes);
-  values = NULL;
-#endif
-
   xi_device->use = device->use;
   xi_device->name = build_string (device->name);
   xi_device->attachment = device->attachment;
 
+  /* Clear the list of active touch points on the device, which are
+     individual touches tracked by a touchscreen.  */
+
 #ifdef HAVE_XINPUT2_2
   xi_device->touchpoints = NULL;
   xi_device->direct_p = false;
 #endif
 
+#ifdef HAVE_XINPUT2_1
+  if (!dpyinfo->xi2_version)
+    {
+      /* Skip everything below as there are no classes of interest on
+        XI 2.0 servers.  */
+      xi_device->valuators = NULL;
+      xi_device->scroll_valuator_count = 0;
+
+      SAFE_FREE ();
+      return;
+    }
+
+  actual_valuator_count = 0;
+  xi_device->valuators = xnmalloc (device->num_classes,
+                                  sizeof *xi_device->valuators);
+  values = NULL;
+
+  /* Initialize device info based on a list of "device classes".
+     Device classes are little pieces of information associated with a
+     device.  Emacs is interested in scroll valuator information and
+     touch handling information, which respectively describe the axes
+     (if any) along which the device's scroll wheel rotates, and how
+     the device reports touch input.  */
+
   for (c = 0; c < device->num_classes; ++c)
     {
       switch (device->classes[c]->type)
        {
-#ifdef HAVE_XINPUT2_1
        case XIScrollClass:
          {
            info = (XIScrollClassInfo *) device->classes[c];
@@ -5370,24 +5439,33 @@ xi_populate_device_from_info (struct xi_device_t 
*xi_device,
            valuator->emacs_value = DBL_MIN;
            valuator->increment = info->increment;
            valuator->number = info->number;
-           valuator->pending_enter_reset = false;
 
            break;
          }
 
        case XIValuatorClass:
          {
-           val_info = (XIValuatorClassInfo *) device->classes[c];
+           valuator_info = (XIValuatorClassInfo *) device->classes[c];
            tem = SAFE_ALLOCA (sizeof *tem);
 
+           /* Avoid restoring bogus values if some driver
+              accidentally specifies relative values in scroll
+              valuator classes how the input extension spec says they
+              should be, but allow restoring values when a value is
+              set, which is how the input extension actually
+              behaves.  */
+
+           if (valuator_info->value == 0.0
+               && valuator_info->mode != XIModeAbsolute)
+             continue;
+
            tem->next = values;
-           tem->number = val_info->number;
-           tem->current_value = val_info->value;
+           tem->number = valuator_info->number;
+           tem->current_value = valuator_info->value;
 
            values = tem;
            break;
          }
-#endif
 
 #ifdef HAVE_XINPUT2_2
        case XITouchClass:
@@ -5401,7 +5479,6 @@ xi_populate_device_from_info (struct xi_device_t 
*xi_device,
        }
     }
 
-#ifdef HAVE_XINPUT2_1
   xi_device->scroll_valuator_count = actual_valuator_count;
 
   /* Now look through all the valuators whose values are already known
@@ -5415,8 +5492,9 @@ xi_populate_device_from_info (struct xi_device_t 
*xi_device,
          if (xi_device->valuators[c].number == tem->number)
            {
              xi_device->valuators[c].invalid_p = false;
-             xi_device->valuators[c].current_value = tem->current_value;
-             xi_device->valuators[c].pending_enter_reset = true;
+             xi_device->valuators[c].current_value
+               = tem->current_value;
+             xi_device->valuators[c].emacs_value = 0.0;
            }
        }
     }
@@ -5478,7 +5556,8 @@ x_cache_xi_devices (struct x_display_info *dpyinfo)
   for (i = 0; i < ndevices; ++i)
     {
       if (infos[i].enabled)
-       xi_populate_device_from_info (&dpyinfo->devices[actual_devices++],
+       xi_populate_device_from_info (dpyinfo,
+                                     &dpyinfo->devices[actual_devices++],
                                      &infos[i]);
     }
 
@@ -5610,11 +5689,14 @@ xi_find_touch_point (struct xi_device_t *device, int 
detail)
 #ifdef HAVE_XINPUT2_1
 
 static void
-xi_reset_scroll_valuators_for_device_id (struct x_display_info *dpyinfo, int 
id,
-                                        bool pending_only)
+xi_reset_scroll_valuators_for_device_id (struct x_display_info *dpyinfo,
+                                        int id)
 {
-  struct xi_device_t *device = xi_device_from_id (dpyinfo, id);
+  struct xi_device_t *device;
   struct xi_scroll_valuator_t *valuator;
+  int i;
+
+  device = xi_device_from_id (dpyinfo, id);
 
   if (!device)
     return;
@@ -5622,14 +5704,9 @@ xi_reset_scroll_valuators_for_device_id (struct 
x_display_info *dpyinfo, int id,
   if (!device->scroll_valuator_count)
     return;
 
-  for (int i = 0; i < device->scroll_valuator_count; ++i)
+  for (i = 0; i < device->scroll_valuator_count; ++i)
     {
       valuator = &device->valuators[i];
-
-      if (pending_only && !valuator->pending_enter_reset)
-       continue;
-
-      valuator->pending_enter_reset = false;
       valuator->invalid_p = true;
       valuator->emacs_value = 0.0;
     }
@@ -7044,6 +7121,13 @@ x_update_begin (struct frame *f)
 #else
   /* Nothing to do.  */
 #endif
+
+#ifdef HAVE_XDBE
+  if (FRAME_X_DOUBLE_BUFFERED_P (f))
+    /* The frame is no longer complete, as it is in the midst of an
+       update.  */
+    FRAME_X_COMPLETE_P (f) = false;
+#endif
 }
 
 /* Draw a vertical window border from (x,y0) to (x,y1)  */
@@ -7190,6 +7274,10 @@ x_flip_and_flush (struct frame *f)
 #ifdef HAVE_XDBE
   if (FRAME_X_NEED_BUFFER_FLIP (f))
     show_back_buffer (f);
+
+  /* The frame is complete again as its contents were just
+     flushed.  */
+  FRAME_X_COMPLETE_P (f) = true;
 #endif
   x_flush (f);
   unblock_input ();
@@ -7240,6 +7328,9 @@ XTframe_up_to_date (struct frame *f)
   if (!buffer_flipping_blocked_p ()
       && FRAME_X_NEED_BUFFER_FLIP (f))
     show_back_buffer (f);
+
+  /* The frame is now complete, as its contents have been drawn.  */
+  FRAME_X_COMPLETE_P (f) = true;
 #endif
 
 #ifdef HAVE_XSYNC
@@ -7721,6 +7812,28 @@ x_set_gtk_user_time (struct frame *f, Time time)
 
 #endif
 
+#if !defined USE_GTK || defined HAVE_XFIXES
+
+/* Create and return a special window for receiving events such as
+   selection notify events, and reporting user time.  The window is an
+   1x1 unmapped override-redirect InputOnly window at -1, -1 relative
+   to the parent, which should prevent it from doing anything.  */
+
+static Window
+x_create_special_window (struct x_display_info *dpyinfo,
+                        Window parent_window)
+{
+  XSetWindowAttributes attrs;
+
+  attrs.override_redirect = True;
+
+  return XCreateWindow (dpyinfo->display, parent_window,
+                       -1, -1, 1, 1, 0, CopyFromParent, InputOnly,
+                       CopyFromParent, CWOverrideRedirect, &attrs);
+}
+
+#endif
+
 /* Not needed on GTK because GTK handles reporting the user time
    itself.  */
 
@@ -7731,7 +7844,6 @@ x_update_frame_user_time_window (struct frame *f)
 {
   struct x_output *output;
   struct x_display_info *dpyinfo;
-  XSetWindowAttributes attrs;
 
   output = FRAME_X_OUTPUT (f);
   dpyinfo = FRAME_DISPLAY_INFO (f);
@@ -7773,12 +7885,16 @@ x_update_frame_user_time_window (struct frame *f)
       if (output->user_time_window == FRAME_OUTER_WINDOW (f)
          || output->user_time_window == None)
        {
-         memset (&attrs, 0, sizeof attrs);
+         /* Create a "user time" window that is used to report user
+            activity on a given frame.  This is used in preference to
+            _NET_WM_USER_TIME, as using a separate window allows the
+            window manager to express interest in other properties
+            while only reading the user time when necessary, thereby
+            improving battery life by not involving the window
+            manager in each key press.  */
 
          output->user_time_window
-           = XCreateWindow (dpyinfo->display, FRAME_X_WINDOW (f),
-                            -1, -1, 1, 1, 0, 0, InputOnly,
-                            CopyFromParent, 0, &attrs);
+           = x_create_special_window (dpyinfo, FRAME_X_WINDOW (f));
 
          XDeleteProperty (dpyinfo->display, FRAME_OUTER_WINDOW (f),
                           dpyinfo->Xatom_net_wm_user_time);
@@ -10806,6 +10922,7 @@ x_clear_frame (struct frame *f)
   /* We have to clear the scroll bars.  If we have changed colors or
      something like that, then they should be notified.  */
   x_scroll_bar_clear (f);
+
   unblock_input ();
 }
 
@@ -10819,6 +10936,16 @@ x_show_hourglass (struct frame *f)
   if (dpy)
     {
       struct x_output *x = FRAME_X_OUTPUT (f);
+
+      /* If the hourglass window is mapped inside a popup menu, input
+        could be lost if the menu is popped down and the grab is
+        relinquished, but the hourglass window is still up.  Just
+        avoid displaying the hourglass at all while popups are
+        active.  */
+
+      if (popup_activated ())
+       return;
+
 #ifdef USE_X_TOOLKIT
       if (x->widget)
 #else
@@ -10857,7 +10984,7 @@ x_show_hourglass (struct frame *f)
                                (xcb_window_t) x->hourglass_window,
                                parent, 0, 0, FRAME_PIXEL_WIDTH (f),
                                FRAME_PIXEL_HEIGHT (f), 0,
-                               XCB_WINDOW_CLASS_INPUT_OUTPUT,
+                               XCB_WINDOW_CLASS_INPUT_ONLY,
                                XCB_COPY_FROM_PARENT, XCB_CW_CURSOR,
                                &cursor);
 #endif
@@ -11372,15 +11499,18 @@ x_new_focus_frame (struct x_display_info *dpyinfo, 
struct frame *frame)
   x_frame_rehighlight (dpyinfo);
 }
 
+#ifdef HAVE_XFIXES
+
 /* True if the display in DPYINFO supports a version of Xfixes
    sufficient for pointer blanking.  */
-#ifdef HAVE_XFIXES
+
 static bool
-x_probe_xfixes_extension (struct x_display_info *dpyinfo)
+x_fixes_pointer_blanking_supported (struct x_display_info *dpyinfo)
 {
   return (dpyinfo->xfixes_supported_p
          && dpyinfo->xfixes_major >= 4);
 }
+
 #endif /* HAVE_XFIXES */
 
 /* Toggle mouse pointer visibility on frame F using the XFixes
@@ -11451,7 +11581,7 @@ x_toggle_visible_pointer (struct frame *f, bool 
invisible)
   /* But if Xfixes is available, try using it instead.  */
   if (dpyinfo->invisible_cursor == None)
     {
-      if (x_probe_xfixes_extension (dpyinfo))
+      if (x_fixes_pointer_blanking_supported (dpyinfo))
        {
          dpyinfo->fixes_pointer_blanking = true;
          xfixes_toggle_visible_pointer (f, invisible);
@@ -11479,7 +11609,7 @@ XTtoggle_invisible_pointer (struct frame *f, bool 
invisible)
   block_input ();
 #ifdef HAVE_XFIXES
   if (FRAME_DISPLAY_INFO (f)->fixes_pointer_blanking
-      && x_probe_xfixes_extension (FRAME_DISPLAY_INFO (f)))
+      && x_fixes_pointer_blanking_supported (FRAME_DISPLAY_INFO (f)))
     xfixes_toggle_visible_pointer (f, invisible);
   else
 #endif
@@ -11487,13 +11617,16 @@ XTtoggle_invisible_pointer (struct frame *f, bool 
invisible)
   unblock_input ();
 }
 
-/* Handle FocusIn and FocusOut state changes for FRAME.
-   If FRAME has focus and there exists more than one frame, puts
-   a FOCUS_IN_EVENT into *BUFP.  */
+/* Handle FocusIn and FocusOut state changes for FRAME.  If FRAME has
+   focus and there exists more than one frame, puts a FOCUS_IN_EVENT
+   into *BUFP.  Note that this code is not used to handle focus
+   changes on builds that can use the X Input extension for handling
+   input focus when it is available (currently the no toolkit and GTK
+   3 toolkits).  */
 
 static void
-x_focus_changed (int type, int state, struct x_display_info *dpyinfo, struct 
frame *frame,
-                struct input_event *bufp)
+x_focus_changed (int type, int state, struct x_display_info *dpyinfo,
+                struct frame *frame, struct input_event *bufp)
 {
   if (type == FocusIn)
     {
@@ -12975,129 +13108,185 @@ xi_get_scroll_valuator (struct xi_device_t *device, 
int number)
   return NULL;
 }
 
-#endif
+/* Check if EVENT, a DeviceChanged event, contains any scroll
+   valuators.  */
 
-/* Handle EVENT, a DeviceChanged event.  Look up the device that
-   changed, and update its information with the data in EVENT.  */
+static bool
+xi_has_scroll_valuators (XIDeviceChangedEvent *event)
+{
+  int i;
+
+  for (i = 0; i < event->num_classes; ++i)
+    {
+      if (event->classes[i]->type == XIScrollClass)
+       return true;
+    }
+
+  return false;
+}
+
+/* Repopulate the information (touchpoint tracking information, scroll
+   valuators, etc) in DEVICE with the device classes provided in
+   CLASSES.  This is called upon receiving a DeviceChanged event.
+
+   This function is not present on XI 2.0 as there are no worthwhile
+   classes there.  */
 
 static void
-xi_handle_device_changed (struct x_display_info *dpyinfo,
-                         struct xi_device_t *device,
-                         XIDeviceChangedEvent *event)
+xi_handle_new_classes (struct x_display_info *dpyinfo, struct xi_device_t 
*device,
+                      XIAnyClassInfo **classes, int num_classes)
 {
-#ifdef HAVE_XINPUT2_1
-  XIDeviceInfo *info;
   XIScrollClassInfo *scroll;
-  int i, ndevices;
   struct xi_scroll_valuator_t *valuator;
   XIValuatorClassInfo *valuator_info;
-#endif
+  int i;
 #ifdef HAVE_XINPUT2_2
-  struct xi_touch_point_t *tem, *last;
   XITouchClassInfo *touch;
 #endif
 
-#ifdef HAVE_XINPUT2_1
-  /* When a DeviceChange event is received for a master device, we
-     don't get any scroll valuators along with it.  This is possibly
-     an X server bug but I really don't want to dig any further, so
-     fetch the scroll valuators manually.  (bug#57020) */
-
-  x_catch_errors (dpyinfo->display);
-  info = XIQueryDevice (dpyinfo->display, event->deviceid,
-                       /* ndevices is always 1 if a deviceid is
-                          specified.  If the request fails, NULL will
-                          be returned.  */
-                       &ndevices);
-  x_uncatch_errors ();
+  if (dpyinfo->xi2_version < 1)
+    /* Emacs is connected to an XI 2.0 server, which reports no
+       classes of interest.  */
+    return;
 
-  if (info)
-    {
-      device->valuators = xrealloc (device->valuators,
-                                   (info->num_classes
-                                    * sizeof *device->valuators));
-      device->scroll_valuator_count = 0;
+  device->valuators = xnmalloc (num_classes,
+                               sizeof *device->valuators);
+  device->scroll_valuator_count = 0;
 #ifdef HAVE_XINPUT2_2
-      device->direct_p = false;
+  device->direct_p = false;
 #endif
 
-      for (i = 0; i < info->num_classes; ++i)
+  for (i = 0; i < num_classes; ++i)
+    {
+      switch (classes[i]->type)
        {
-         switch (info->classes[i]->type)
-           {
-           case XIScrollClass:
-             scroll = (XIScrollClassInfo *) info->classes[i];
-
-             valuator = &device->valuators[device->scroll_valuator_count++];
-             valuator->horizontal = (scroll->scroll_type
-                                     == XIScrollTypeHorizontal);
-             valuator->invalid_p = true;
-             valuator->emacs_value = DBL_MIN;
-             valuator->increment = scroll->increment;
-             valuator->number = scroll->number;
-             break;
+       case XIScrollClass:
+         scroll = (XIScrollClassInfo *) classes[i];
+
+         valuator = &device->valuators[device->scroll_valuator_count++];
+         valuator->horizontal = (scroll->scroll_type
+                                 == XIScrollTypeHorizontal);
+         valuator->invalid_p = true;
+         valuator->emacs_value = 0;
+         valuator->increment = scroll->increment;
+         valuator->number = scroll->number;
+         break;
 
 #ifdef HAVE_XINPUT2_2
-           case XITouchClass:
-             touch = (XITouchClassInfo *) info->classes[i];
+       case XITouchClass:
+         touch = (XITouchClassInfo *) classes[i];
 
-             if (touch->mode == XIDirectTouch)
-               device->direct_p = true;
-             break;
+         if (touch->mode == XIDirectTouch)
+           device->direct_p = true;
+         break;
 #endif
-           }
        }
+    }
 
-      /* Restore the values of any scroll valuators that we already
-        know about.  */
+  /* Restore the values of any scroll valuators that we already
+     know about.  */
 
-      for (i = 0; i < info->num_classes; ++i)
-       {
-         switch (info->classes[i]->type)
-           {
-           case XIValuatorClass:
-             valuator_info = (XIValuatorClassInfo *) info->classes[i];
+  for (i = 0; i < num_classes; ++i)
+    {
+      if (classes[i]->type != XIValuatorClass)
+       continue;
 
-             valuator = xi_get_scroll_valuator (device,
-                                                valuator_info->number);
-             if (valuator)
-               {
-                 valuator->invalid_p = false;
-                 valuator->current_value = valuator_info->value;
+      valuator_info = (XIValuatorClassInfo *) classes[i];
 
-                 /* Make sure that this is reset if the pointer moves
-                    into a window of ours.
+      /* Avoid restoring bogus values if some driver accidentally
+        specifies relative values in scroll valuator classes how the
+        input extension spec says they should be, but allow restoring
+        values when a value is set, which is how the input extension
+        actually behaves.  */
 
-                    Otherwise the valuator state could be left
-                    invalid if the DeviceChange event happened with
-                    the pointer outside any Emacs frame. */
-                 valuator->pending_enter_reset = true;
-               }
+      if (valuator_info->value == 0.0
+         && valuator_info->mode != XIModeAbsolute)
+       continue;
 
-             break;
-           }
-       }
+      valuator = xi_get_scroll_valuator (device,
+                                        valuator_info->number);
+
+      if (!valuator)
+       continue;
+
+      valuator->invalid_p = false;
+      valuator->current_value = valuator_info->value;
+      valuator->emacs_value = 0;
+
+      break;
+    }
+}
+
+#endif
+
+/* Handle EVENT, a DeviceChanged event.  Look up the device that
+   changed, and update its information with the data in EVENT.  */
 
+static void
+xi_handle_device_changed (struct x_display_info *dpyinfo,
+                         struct xi_device_t *device,
+                         XIDeviceChangedEvent *event)
+{
+#ifdef HAVE_XINPUT2_1
+  int ndevices;
+  XIDeviceInfo *info;
+#endif
 #ifdef HAVE_XINPUT2_2
-      /* The device is no longer a DirectTouch device, so
-        remove any touchpoints that we might have
-        recorded.  */
-      if (!device->direct_p)
-       {
-         tem = device->touchpoints;
+  struct xi_touch_point_t *tem, *last;
+#endif
 
-         while (tem)
-           {
-             last = tem;
-             tem = tem->next;
-             xfree (last);
-           }
+#ifdef HAVE_XINPUT2_1
+  if (xi_has_scroll_valuators (event))
+    /* Scroll valuators are provided by this event.  Use the values
+       provided in this event to populate the device's new scroll
+       valuator list: if this event is a SlaveSwitch event caused by
+       wheel movement, then querying for the device info will probably
+       return the value after the wheel movement, leading to a delta
+       of 0 being computed upon handling the subsequent XI_Motion
+       event.  (bug#58980) */
+    xi_handle_new_classes (dpyinfo, device, event->classes,
+                          event->num_classes);
+  else
+    {
+      /* When a DeviceChange event is received for a master device,
+        the X server sometimes does not send any scroll valuators
+        along with it.  This is possibly an X server bug but I really
+        don't want to dig any further, so fetch the scroll valuators
+        manually.  (bug#57020) */
 
-         device->touchpoints = NULL;
-       }
+      x_catch_errors (dpyinfo->display);
+      info = XIQueryDevice (dpyinfo->display, event->deviceid,
+                           /* ndevices is always 1 if a deviceid is
+                              specified.  If the request fails, NULL will
+                              be returned.  */
+                           &ndevices);
+      x_uncatch_errors ();
+
+      if (!info)
+       return;
+
+      /* info contains the classes currently associated with the
+        event.  Apply them.  */
+      xi_handle_new_classes (dpyinfo, device, info->classes,
+                            info->num_classes);
+    }
 #endif
 
-      XIFreeDeviceInfo (info);
+#ifdef HAVE_XINPUT2_2
+  /* The device is no longer a DirectTouch device, so remove any
+     touchpoints that we might have recorded.  */
+  if (!device->direct_p)
+    {
+      tem = device->touchpoints;
+
+      while (tem)
+       {
+         last = tem;
+         tem = tem->next;
+         xfree (last);
+       }
+
+      device->touchpoints = NULL;
     }
 #endif
 }
@@ -13349,7 +13538,7 @@ x_find_modifier_meanings (struct x_display_info 
*dpyinfo)
 #ifdef HAVE_XKB
   int i;
   int found_meta_p = false;
-  uint vmodmask;
+  unsigned int vmodmask;
 #endif
 
   dpyinfo->meta_mod_mask = 0;
@@ -13608,7 +13797,7 @@ x_compute_root_window_offset (struct frame *f, int 
root_x, int root_y,
    many cases while handling events, which would otherwise result in
    slowdowns over slow network connections.  */
 
-static void
+void
 x_translate_coordinates (struct frame *f, int root_x, int root_y,
                         int *x_out, int *y_out)
 {
@@ -13644,6 +13833,68 @@ x_translate_coordinates (struct frame *f, int root_x, 
int root_y,
     }
 }
 
+/* Translate the given coordinates from the edit window of FRAME,
+   taking into account any cached root window offsets.  This is mainly
+   used from the popup menu code.  */
+
+void
+x_translate_coordinates_to_root (struct frame *f, int x, int y,
+                                int *x_out, int *y_out)
+{
+  struct x_output *output;
+  Window dummy;
+
+  output = FRAME_X_OUTPUT (f);
+
+  if (output->window_offset_certain_p)
+    {
+      /* Use the cached root window offset.  */
+      *x_out = x + output->root_x;
+      *y_out = y + output->root_y;
+
+      return;
+    }
+
+  /* Otherwise, do the transform manually and compute and cache the
+     root window position.  */
+  if (!XTranslateCoordinates (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+                             FRAME_DISPLAY_INFO (f)->root_window,
+                             x, y, x_out, y_out, &dummy))
+    *x_out = 0, *y_out = 0;
+  else
+    {
+      /* Cache the root window offset of the edit window.  */
+      output->window_offset_certain_p = true;
+      output->root_x = *x_out - x;
+      output->root_y = *y_out - y;
+    }
+}
+
+/* Do x-translate-coordinates, but try to avoid a roundtrip to the X
+   server at the cost of not returning `child', which most callers
+   have no reason to use.  */
+
+Lisp_Object
+x_handle_translate_coordinates (struct frame *f, Lisp_Object dest_window,
+                               int source_x, int source_y)
+{
+  if (NILP (dest_window))
+    {
+      /* We are translating coordinates from a frame to the root
+        window.  Avoid a roundtrip if possible by using cached
+        coordinates.  */
+
+      if (!FRAME_X_OUTPUT (f)->window_offset_certain_p)
+       return Qnil;
+
+      return list3 (make_fixnum (source_x + FRAME_X_OUTPUT (f)->root_x),
+                   make_fixnum (source_y + FRAME_X_OUTPUT (f)->root_y),
+                   Qnil);
+    }
+
+  return Qnil;
+}
+
 /* The same, but for an XIDeviceEvent.  */
 
 #ifdef HAVE_XINPUT2
@@ -17033,18 +17284,21 @@ x_net_wm_state (struct frame *f, Window window)
 
 /* Flip back buffers on F if it has undrawn content.  */
 
-#ifdef HAVE_XDBE
 static void
-flush_dirty_back_buffer_on (struct frame *f)
+x_flush_dirty_back_buffer_on (struct frame *f)
 {
-  block_input ();
-  if (!FRAME_GARBAGED_P (f)
-      && !buffer_flipping_blocked_p ()
-      && FRAME_X_NEED_BUFFER_FLIP (f))
-    show_back_buffer (f);
-  unblock_input ();
-}
+#ifdef HAVE_XDBE
+  if (FRAME_GARBAGED_P (f)
+      || buffer_flipping_blocked_p ()
+      /* If the frame is not already up to date, do not flush buffers
+        on input, as that will result in flicker.  */
+      || !FRAME_X_COMPLETE_P (f)
+      || !FRAME_X_NEED_BUFFER_FLIP (f))
+    return;
+
+  show_back_buffer (f);
 #endif
+}
 
 #ifdef HAVE_GTK3
 void
@@ -17754,6 +18008,7 @@ x_coords_from_dnd_message (struct x_display_info 
*dpyinfo,
 static void
 x_handle_wm_state (struct frame *f, struct input_event *ie)
 {
+  struct x_display_info *dpyinfo;
   Atom type;
   int format;
   unsigned long nitems, bytes_after;
@@ -17761,10 +18016,11 @@ x_handle_wm_state (struct frame *f, struct 
input_event *ie)
   unsigned long *state;
 
   data = NULL;
+  dpyinfo = FRAME_DISPLAY_INFO (f);
 
   if (XGetWindowProperty (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
-                         FRAME_DISPLAY_INFO (f)->Xatom_wm_state, 0, 2,
-                         False, AnyPropertyType, &type, &format, &nitems,
+                         dpyinfo->Xatom_wm_state, 0, 2, False,
+                         AnyPropertyType, &type, &format, &nitems,
                          &bytes_after, &data) != Success)
     return;
 
@@ -17789,6 +18045,20 @@ x_handle_wm_state (struct frame *f, struct input_event 
*ie)
       ie->kind = DEICONIFY_EVENT;
       XSETFRAME (ie->frame_or_window, f);
     }
+  else if (state[0] == IconicState
+          /* _NET_WM_STATE_HIDDEN should be used if the window
+             manager supports that.  */
+          && !x_wm_supports (f, dpyinfo->Xatom_net_wm_state_hidden))
+    {
+      /* The frame is actually iconified right now.  Mark it as
+        such.  */
+
+      SET_FRAME_VISIBLE (f, 0);
+      SET_FRAME_ICONIFIED (f, true);
+
+      ie->kind = ICONIFY_EVENT;
+      XSETFRAME (ie->frame_or_window, f);
+    }
 
   /* state[0] can also be WithdrawnState, meaning that the window has
      been withdrawn and is no longer iconified.  However, Emacs sets
@@ -17798,6 +18068,46 @@ x_handle_wm_state (struct frame *f, struct input_event 
*ie)
   XFree (data);
 }
 
+#ifdef HAVE_XFIXES
+
+static bool
+x_handle_selection_monitor_event (struct x_display_info *dpyinfo,
+                                 const XEvent *event)
+{
+  XFixesSelectionNotifyEvent *notify;
+  int i;
+
+  notify = (XFixesSelectionNotifyEvent *) event;
+
+  if (notify->window != dpyinfo->selection_tracking_window)
+    return false;
+
+  for (i = 0; i < dpyinfo->n_monitored_selections; ++i)
+    {
+      /* We don't have to keep track of timestamps here.  */
+      if (notify->selection == dpyinfo->monitored_selections[i].name)
+       dpyinfo->monitored_selections[i].owner = notify->owner;
+    }
+
+  return true;
+}
+
+Window
+x_find_selection_owner (struct x_display_info *dpyinfo, Atom selection)
+{
+  int i;
+
+  for (i = 0; i < dpyinfo->n_monitored_selections; ++i)
+    {
+      if (selection == dpyinfo->monitored_selections[i].name)
+       return dpyinfo->monitored_selections[i].owner;
+    }
+
+  return X_INVALID_WINDOW;
+}
+
+#endif
+
 /* Handles the XEvent EVENT on display DPYINFO.
 
    *FINISH is X_EVENT_GOTO_OUT if caller should stop reading events.
@@ -17824,7 +18134,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
   Time gen_help_time UNINIT;
 #endif
   ptrdiff_t nbytes = 0;
-  struct frame *any, *f = NULL;
+  struct frame *any, *f = NULL, *mouse_frame;
   Mouse_HLInfo *hlinfo = &dpyinfo->mouse_highlight;
   /* This holds the state XLookupString needs to implement dead keys
      and other tricks known as "compose processing".  _X Window System_
@@ -17845,7 +18155,11 @@ handle_one_xevent (struct x_display_info *dpyinfo,
   GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpyinfo->display);
 #endif
   int dx, dy;
+
+  /* Avoid warnings when SAFE_ALLOCA is not actually used.  */
+#if defined HAVE_XINPUT2 || defined HAVE_XKB || defined HAVE_X_I18N
   USE_SAFE_ALLOCA;
+#endif
 
   /* This function is not reentrant, so input should be blocked before
      it is called.  */
@@ -18461,7 +18775,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
        /* If drag-and-drop or another modal dialog/menu is in
           progress, handle SelectionRequest events immediately, by
-          pushing it onto the selecction queue.  */
+          pushing it onto the selection queue.  */
 
        if (x_use_pending_selection_requests)
          {
@@ -18759,8 +19073,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
              x_real_positions (f, &f->left_pos, &f->top_pos);
 
              /* Perhaps reparented due to a WM restart.  Reset this.  */
-             FRAME_DISPLAY_INFO (f)->wm_type = X_WMTYPE_UNKNOWN;
-             FRAME_DISPLAY_INFO (f)->net_supported_window = 0;
+             dpyinfo->wm_type = X_WMTYPE_UNKNOWN;
+             dpyinfo->net_supported_window = 0;
 
 #ifndef USE_GTK
              /* The window manager could have restarted and the new
@@ -18788,11 +19102,12 @@ handle_one_xevent (struct x_display_info *dpyinfo,
       goto OTHER;
 
     case Expose:
-      f = x_window_to_frame (dpyinfo, event->xexpose.window);
+
 #ifdef HAVE_XWIDGETS
       {
-       struct xwidget_view *xv =
-         xwidget_view_from_window (event->xexpose.window);
+       struct xwidget_view *xv;
+
+       xv = xwidget_view_from_window (event->xexpose.window);
 
        if (xv)
          {
@@ -18801,6 +19116,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
          }
       }
 #endif
+
+      f = x_window_to_frame (dpyinfo, event->xexpose.window);
       if (f)
         {
           if (!FRAME_VISIBLE_P (f))
@@ -19148,9 +19465,14 @@ handle_one_xevent (struct x_display_info *dpyinfo,
              || !EQ (f->tab_bar_window, hlinfo->mouse_face_window))
          )
         {
-          clear_mouse_face (hlinfo);
-          hlinfo->mouse_face_hidden = true;
-        }
+         mouse_frame = hlinfo->mouse_face_mouse_frame;
+
+         clear_mouse_face (hlinfo);
+         hlinfo->mouse_face_hidden = true;
+
+         if (mouse_frame)
+           x_flush_dirty_back_buffer_on (mouse_frame);
+       }
 
 #if defined USE_MOTIF && defined USE_TOOLKIT_SCROLL_BARS
       if (f == 0)
@@ -19224,7 +19546,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
           *finish = X_EVENT_DROP;
 #endif
 
-          xkey.state |= x_emacs_to_x_modifiers (FRAME_DISPLAY_INFO (f),
+          xkey.state |= x_emacs_to_x_modifiers (dpyinfo,
                                                extra_keyboard_modifiers);
           modifiers = xkey.state;
 
@@ -19341,7 +19663,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
          /* Common for all keysym input events.  */
          XSETFRAME (inev.ie.frame_or_window, f);
          inev.ie.modifiers
-           = x_x_to_emacs_modifiers (FRAME_DISPLAY_INFO (f), modifiers);
+           = x_x_to_emacs_modifiers (dpyinfo, modifiers);
          inev.ie.timestamp = xkey.time;
 
          /* First deal with keysyms which have defined
@@ -19599,28 +19921,16 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
     case EnterNotify:
       x_display_set_last_user_time (dpyinfo, event->xcrossing.time,
-                                   event->xcrossing.send_event, false);
-
-#ifdef HAVE_XINPUT2
-      /* For whatever reason, the X server continues to deliver
-        EnterNotify and LeaveNotify events despite us selecting for
-        related XI_Enter and XI_Leave events.  It's not just our
-        problem, since windows created by "xinput test-xi2" suffer
-        from the same defect.  Simply ignore all such events while
-        the input extension is enabled.  (bug#57468) */
-
-      if (dpyinfo->supports_xi2)
-       goto OTHER;
-#endif
-
-      if (x_top_window_to_frame (dpyinfo, event->xcrossing.window))
-       x_detect_focus_change (dpyinfo, any, event, &inev.ie);
+                                   event->xcrossing.send_event,
+                                   false);
 
 #ifdef HAVE_XWIDGETS
       {
-       struct xwidget_view *xvw = xwidget_view_from_window 
(event->xcrossing.window);
+       struct xwidget_view *xvw;
        Mouse_HLInfo *hlinfo;
 
+       xvw = xwidget_view_from_window (event->xcrossing.window);
+
        if (xvw)
          {
            xwidget_motion_or_crossing (xvw, event);
@@ -19630,17 +19940,32 @@ handle_one_xevent (struct x_display_info *dpyinfo,
              {
                clear_mouse_face (hlinfo);
                hlinfo->mouse_face_mouse_frame = 0;
+               x_flush_dirty_back_buffer_on (xvw->frame);
              }
 
            if (any_help_event_p)
-             {
-               do_help = -1;
-             }
+             do_help = -1;
+
            goto OTHER;
          }
       }
 #endif
 
+#ifdef HAVE_XINPUT2
+      /* For whatever reason, the X server continues to deliver
+        EnterNotify and LeaveNotify events despite us selecting for
+        related XI_Enter and XI_Leave events.  It's not just our
+        problem, since windows created by "xinput test-xi2" suffer
+        from the same defect.  Simply ignore all such events while
+        the input extension is enabled.  (bug#57468) */
+
+      if (dpyinfo->supports_xi2)
+       goto OTHER;
+#endif
+
+      if (x_top_window_to_frame (dpyinfo, event->xcrossing.window))
+       x_detect_focus_change (dpyinfo, any, event, &inev.ie);
+
       f = any;
 
       if (f && event->xcrossing.window == FRAME_X_WINDOW (f))
@@ -19733,12 +20058,27 @@ handle_one_xevent (struct x_display_info *dpyinfo,
         the input extension is enabled.  (bug#57468) */
 
       if (dpyinfo->supports_xi2)
-       goto OTHER;
+       {
+#if !defined USE_X_TOOLKIT && (!defined USE_GTK || defined HAVE_GTK3)
+         goto OTHER;
+#else
+         /* 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
+            face persisting if a popup is activated by clicking on a
+            button, and then dismissed by releasing the mouse button
+            outside the frame, in which case no XI_Enter event is
+            generated for the grab.  */
+         goto just_clear_mouse_face;
+#endif
+       }
 #endif
 
 #ifdef HAVE_XWIDGETS
       {
-       struct xwidget_view *xvw = xwidget_view_from_window 
(event->xcrossing.window);
+       struct xwidget_view *xvw;
+
+       xvw = xwidget_view_from_window (event->xcrossing.window);
 
        if (xvw)
          {
@@ -19751,6 +20091,11 @@ handle_one_xevent (struct x_display_info *dpyinfo,
       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:
+#endif
+
 #if defined USE_X_TOOLKIT
       /* If the mouse leaves the edit widget, then any mouse highlight
         should be cleared.  */
@@ -19783,6 +20128,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                  certainly no longer on any text in the frame.  */
               clear_mouse_face (hlinfo);
               hlinfo->mouse_face_mouse_frame = 0;
+             x_flush_dirty_back_buffer_on (f);
             }
 
           /* Generate a nil HELP_EVENT to cancel a help-echo.
@@ -19840,7 +20186,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
         help_echo_string = Qnil;
 
        if (hlinfo->mouse_face_hidden)
-          {
+         {
             hlinfo->mouse_face_hidden = false;
             clear_mouse_face (hlinfo);
           }
@@ -20092,7 +20438,9 @@ handle_one_xevent (struct x_display_info *dpyinfo,
           f = 0;
 #endif
 #ifdef HAVE_XWIDGETS
-       struct xwidget_view *xvw = xwidget_view_from_window 
(event->xmotion.window);
+       struct xwidget_view *xvw;
+
+       xvw = xwidget_view_from_window (event->xmotion.window);
 
        if (xvw)
          xwidget_motion_or_crossing (xvw, event);
@@ -20171,6 +20519,9 @@ handle_one_xevent (struct x_display_info *dpyinfo,
         if (!NILP (help_echo_string)
             || !NILP (previous_help_echo_string))
          do_help = 1;
+
+       if (f)
+         x_flush_dirty_back_buffer_on (f);
         goto OTHER;
       }
 
@@ -20467,7 +20818,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
            {
              int old_left = f->left_pos;
              int old_top = f->top_pos;
-             Lisp_Object frame = Qnil;
+             Lisp_Object frame;
 
              XSETFRAME (frame, f);
 
@@ -20540,7 +20891,9 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                                        event->xbutton.send_event, true);
 
 #ifdef HAVE_XWIDGETS
-       struct xwidget_view *xvw = xwidget_view_from_window 
(event->xbutton.window);
+       struct xwidget_view *xvw;
+
+       xvw = xwidget_view_from_window (event->xbutton.window);
 
        if (xvw)
          {
@@ -20664,7 +21017,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                                              event->xbutton.time);
                      }
                    else if (x_dnd_last_seen_window != None
-                       && x_dnd_last_protocol_version != -1)
+                            && x_dnd_last_protocol_version != -1)
                      {
                        x_dnd_pending_finish_target = x_dnd_last_seen_toplevel;
                        x_dnd_waiting_for_finish_proto = 
x_dnd_last_protocol_version;
@@ -20782,8 +21135,11 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
            if (FRAME_PARENT_FRAME (f) || (hf && frame_ancestor_p (f, hf)))
              {
+               x_ignore_errors_for_next_request (dpyinfo);
                XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
                                RevertToParent, event->xbutton.time);
+               x_stop_ignoring_errors (dpyinfo);
+
                if (FRAME_PARENT_FRAME (f))
                  XRaiseWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f));
              }
@@ -20837,9 +21193,12 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                 tab_bar_p = EQ (window, f->tab_bar_window);
 
                 if (tab_bar_p)
-                 tab_bar_arg = handle_tab_bar_click
-                   (f, x, y, event->xbutton.type == ButtonPress,
-                    x_x_to_emacs_modifiers (dpyinfo, event->xbutton.state));
+                 {
+                   tab_bar_arg = handle_tab_bar_click
+                     (f, x, y, event->xbutton.type == ButtonPress,
+                      x_x_to_emacs_modifiers (dpyinfo, event->xbutton.state));
+                   x_flush_dirty_back_buffer_on (f);
+                 }
               }
 
 #if ! defined (USE_GTK)
@@ -20857,9 +21216,12 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                                  || f->last_tool_bar_item != -1));
 
                 if (tool_bar_p && event->xbutton.button < 4)
-                 handle_tool_bar_click
-                   (f, x, y, event->xbutton.type == ButtonPress,
-                    x_x_to_emacs_modifiers (dpyinfo, event->xbutton.state));
+                 {
+                   handle_tool_bar_click
+                     (f, x, y, event->xbutton.type == ButtonPress,
+                      x_x_to_emacs_modifiers (dpyinfo, event->xbutton.state));
+                   x_flush_dirty_back_buffer_on (f);
+                 }
               }
 #endif /* !USE_GTK */
 
@@ -21189,20 +21551,21 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                 related to those grabs arrive.  The only way to
                 remedy this is to never reset scroll valuators on a
                 grab-related crossing event.  (bug#57476) */
+
              if (enter->mode != XINotifyUngrab
                  && enter->mode != XINotifyGrab
                  && enter->mode != XINotifyPassiveGrab
                  && enter->mode != XINotifyPassiveUngrab)
-               xi_reset_scroll_valuators_for_device_id (dpyinfo, 
enter->deviceid,
-                                                        true);
+               xi_reset_scroll_valuators_for_device_id (dpyinfo,
+                                                        enter->deviceid);
 #endif
 
              {
 #ifdef HAVE_XWIDGETS
-               struct xwidget_view *xwidget_view = xwidget_view_from_window 
(enter->event);
-#endif
+               struct xwidget_view *xwidget_view;
+
+               xwidget_view = xwidget_view_from_window (enter->event);
 
-#ifdef HAVE_XWIDGETS
                if (xwidget_view)
                  {
                    xwidget_motion_or_crossing (xwidget_view, event);
@@ -21307,28 +21670,32 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                 retrieve the value of a valuator outside of each motion
                 event.
 
-                As such, to prevent wildly inaccurate results when the
-                valuators have changed outside Emacs, we reset our
-                records of each valuator's value whenever the pointer
-                moves out of a frame (and not into one of its
-                children, which we know about).  */
+                As such, to prevent wildly inaccurate results when
+                the valuators have changed outside Emacs, we reset
+                our records of each valuator's value whenever the
+                pointer moves out of a frame.  Ideally, this would
+                ignore events with a detail of XINotifyInferior, as
+                the window the pointer moved to would be one known to
+                Emacs, but the code to keep track of which valuators
+                had to be reset upon the corresponding XI_Enter event
+                was very complicated and kept running into server
+                bugs.  */
 #ifdef HAVE_XINPUT2_1
-             if (leave->detail != XINotifyInferior && any
-                 /* xfwm4 selects for button events on the frame
-                    window, resulting in passive grabs being
-                    generated along with the delivery of emulated
-                    button events; this then interferes with
-                    scrolling, since device valuators will constantly
-                    be reset as the crossing events related to those
-                    grabs arrive.  The only way to remedy this is to
-                    never reset scroll valuators on a grab-related
-                    crossing event.  (bug#57476) */
-                 && leave->mode != XINotifyUngrab
+             /* xfwm4 selects for button events on the frame window,
+                resulting in passive grabs being generated along with
+                the delivery of emulated button events; this then
+                interferes with scrolling, since device valuators
+                will constantly be reset as the crossing events
+                related to those grabs arrive.  The only way to
+                remedy this is to never reset scroll valuators on a
+                grab-related crossing event.  (bug#57476) */
+
+             if (leave->mode != XINotifyUngrab
                  && leave->mode != XINotifyGrab
                  && leave->mode != XINotifyPassiveUngrab
                  && leave->mode != XINotifyPassiveGrab)
                xi_reset_scroll_valuators_for_device_id (dpyinfo,
-                                                        leave->deviceid, 
false);
+                                                        leave->deviceid);
 #endif
 
              x_display_set_last_user_time (dpyinfo, leave->time,
@@ -21336,8 +21703,9 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
 #ifdef HAVE_XWIDGETS
              {
-               struct xwidget_view *xvw
-                 = xwidget_view_from_window (leave->event);
+               struct xwidget_view *xvw;
+
+               xvw = xwidget_view_from_window (leave->event);
 
                if (xvw)
                  {
@@ -21359,20 +21727,6 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                 masks are set on the frame widget's window.  */
              f = x_window_to_frame (dpyinfo, leave->event);
 
-             /* Also do this again here, since the test for `any'
-                above may not have found a frame, as that usually
-                just looks up a top window on Xt builds.  */
-
-#ifdef HAVE_XINPUT2_1
-             if (leave->detail != XINotifyInferior && f
-                 && leave->mode != XINotifyUngrab
-                 && leave->mode != XINotifyGrab
-                 && leave->mode != XINotifyPassiveUngrab
-                 && leave->mode != XINotifyPassiveGrab)
-               xi_reset_scroll_valuators_for_device_id (dpyinfo,
-                                                        leave->deviceid, 
false);
-#endif
-
              if (!f)
                f = x_top_window_to_frame (dpyinfo, leave->event);
 #endif
@@ -21398,6 +21752,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                         certainly no longer on any text in the frame.  */
                      clear_mouse_face (hlinfo);
                      hlinfo->mouse_face_mouse_frame = 0;
+                     x_flush_dirty_back_buffer_on (f);
                    }
 
                  /* Generate a nil HELP_EVENT to cancel a help-echo.
@@ -22073,6 +22428,9 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
                  do_help = 1;
                }
+
+             if (f)
+               x_flush_dirty_back_buffer_on (f);
              goto XI_OTHER;
            }
 
@@ -22483,9 +22841,13 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                        }
 #else
                      /* Non-no toolkit builds without GTK 3 use core
-                        events to handle focus.  */
+                        events to handle focus.  Errors are still
+                        caught here in case the window is not
+                        viewable.  */
+                     x_ignore_errors_for_next_request (dpyinfo);
                      XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW 
(f),
                                      RevertToParent, xev->time);
+                     x_stop_ignoring_errors (dpyinfo);
 #endif
                      if (FRAME_PARENT_FRAME (f))
                        XRaiseWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW 
(f));
@@ -22592,9 +22954,12 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                      tab_bar_p = EQ (window, f->tab_bar_window);
 
                      if (tab_bar_p)
-                       tab_bar_arg = handle_tab_bar_click
-                         (f, x, y, xev->evtype == XI_ButtonPress,
-                          x_x_to_emacs_modifiers (dpyinfo, bv.state));
+                       {
+                         tab_bar_arg = handle_tab_bar_click
+                           (f, x, y, xev->evtype == XI_ButtonPress,
+                            x_x_to_emacs_modifiers (dpyinfo, bv.state));
+                         x_flush_dirty_back_buffer_on (f);
+                       }
                    }
 
 #if ! defined (USE_GTK)
@@ -22619,10 +22984,13 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                                        || f->last_tool_bar_item != -1));
 
                      if (tool_bar_p && xev->detail < 4)
-                       handle_tool_bar_click_with_device
-                         (f, x, y, xev->evtype == XI_ButtonPress,
-                          x_x_to_emacs_modifiers (dpyinfo, bv.state),
-                          source ? source->name : Qt);
+                       {
+                         handle_tool_bar_click_with_device
+                           (f, x, y, xev->evtype == XI_ButtonPress,
+                            x_x_to_emacs_modifiers (dpyinfo, bv.state),
+                            source ? source->name : Qt);
+                         x_flush_dirty_back_buffer_on (f);
+                       }
                    }
 #endif /* !USE_GTK */
 
@@ -22734,7 +23102,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
              char *copy_bufptr = copy_buffer;
              int copy_bufsiz = sizeof (copy_buffer);
              ptrdiff_t i;
-             uint old_state;
+             unsigned int old_state;
              struct xi_device_t *device, *source;
 
              coding = Qlatin_1;
@@ -22760,8 +23128,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                  copy.xkey.root = xev->root;
                  copy.xkey.subwindow = xev->child;
                  copy.xkey.time = xev->time;
-                 copy.xkey.state = ((xev->mods.effective & ~(1 << 13 | 1 << 
14))
-                                    | (xev->group.effective << 13));
+                 copy.xkey.state = xi_convert_event_keyboard_state (xev);
                  xi_convert_button_state (&xev->buttons, &copy.xkey.state);
 
                  copy.xkey.x = lrint (xev->event_x);
@@ -22817,8 +23184,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
              xkey.root = xev->root;
              xkey.subwindow = xev->child;
              xkey.time = xev->time;
-             xkey.state = ((xev->mods.effective & ~(1 << 13 | 1 << 14))
-                           | (xev->group.effective << 13));
+             xkey.state = xi_convert_event_keyboard_state (xev);
 
              xkey.x = lrint (xev->event_x);
              xkey.y = lrint (xev->event_y);
@@ -22882,8 +23248,9 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 #ifdef HAVE_XKB
              if (dpyinfo->xkb_desc)
                {
-                 uint xkb_state = state;
-                 xkb_state &= ~(1 << 13 | 1 << 14);
+                 unsigned int xkb_state;
+
+                 xkb_state = state & ~(1 << 13 | 1 << 14);
                  xkb_state |= xev->group.effective << 13;
 
                  if (!XkbTranslateKeyCode (dpyinfo->xkb_desc, keycode,
@@ -22919,8 +23286,13 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                      || !EQ (f->tab_bar_window, hlinfo->mouse_face_window))
                  )
                {
+                 mouse_frame = hlinfo->mouse_face_mouse_frame;
+
                  clear_mouse_face (hlinfo);
                  hlinfo->mouse_face_hidden = true;
+
+                 if (mouse_frame)
+                   x_flush_dirty_back_buffer_on (mouse_frame);
                }
 
              if (f != 0)
@@ -23231,8 +23603,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
              xkey.root = xev->root;
              xkey.subwindow = xev->child;
              xkey.time = xev->time;
-             xkey.state = ((xev->mods.effective & ~(1 << 13 | 1 << 14))
-                           | (xev->group.effective << 13));
+             xkey.state = xi_convert_event_keyboard_state (xev);
              xkey.x = lrint (xev->event_x);
              xkey.y = lrint (xev->event_y);
              xkey.x_root = lrint (xev->root_x);
@@ -23299,7 +23670,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                      /* Handle all disabled devices now, to prevent
                         things happening out-of-order later.  */
 
-                     if (ndevices)
+                     if (n_disabled)
                        {
                          xi_disable_devices (dpyinfo, disabled, n_disabled);
                          n_disabled = 0;
@@ -23334,7 +23705,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                              memset (dpyinfo->devices + dpyinfo->num_devices - 
1,
                                      0, sizeof *dpyinfo->devices);
                              device = &dpyinfo->devices[dpyinfo->num_devices - 
1];
-                             xi_populate_device_from_info (device, info);
+                             xi_populate_device_from_info (dpyinfo, device, 
info);
                            }
 
                          if (info)
@@ -23625,8 +23996,9 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                    xi_compute_root_window_offset_pinch (any, pev);
 
                  inev.ie.kind = PINCH_EVENT;
-                 inev.ie.modifiers = x_x_to_emacs_modifiers 
(FRAME_DISPLAY_INFO (any),
-                                                             
pev->mods.effective);
+                 inev.ie.modifiers
+                   = x_x_to_emacs_modifiers (dpyinfo, pev->mods.effective);
+
                  XSETINT (inev.ie.x, lrint (pev->event_x));
                  XSETINT (inev.ie.y, lrint (pev->event_y));
                  XSETFRAME (inev.ie.frame_or_window, any);
@@ -23704,12 +24076,12 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                                         | XkbModifierMapMask
                                         | XkbVirtualModsMask),
                                        dpyinfo->xkb_desc) == Success)
-                   XkbGetNames (dpyinfo->display,
-                                XkbGroupNamesMask | XkbVirtualModNamesMask,
+                   XkbGetNames (dpyinfo->display, XkbAllNamesMask,
                                 dpyinfo->xkb_desc);
                  else
                    {
-                     XkbFreeKeyboard (dpyinfo->xkb_desc, XkbAllComponentsMask, 
True);
+                     XkbFreeKeyboard (dpyinfo->xkb_desc,
+                                      XkbAllComponentsMask, True);
                      dpyinfo->xkb_desc = NULL;
                    }
                }
@@ -23723,8 +24095,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                                                 XkbUseCoreKbd);
 
                  if (dpyinfo->xkb_desc)
-                   XkbGetNames (dpyinfo->display,
-                                XkbGroupNamesMask | XkbVirtualModNamesMask,
+                   XkbGetNames (dpyinfo->display, XkbAllNamesMask,
                                 dpyinfo->xkb_desc);
                }
 
@@ -24014,6 +24385,17 @@ handle_one_xevent (struct x_display_info *dpyinfo,
          if (inev.ie.kind != NO_EVENT)
            x_dnd_update_tooltip_now ();
        }
+#endif
+#ifdef HAVE_XFIXES
+      if (dpyinfo->xfixes_supported_p
+         && event->type == (dpyinfo->xfixes_event_base
+                            + XFixesSelectionNotify)
+         && x_handle_selection_monitor_event (dpyinfo, event))
+       /* GTK 3 crashes if an XFixesSelectionNotify arrives with a
+          window other than the root window, because it wants to know
+          the screen in order to determine the compositing manager
+          selection name.  (bug#58584) */
+       *finish = X_EVENT_DROP;
 #endif
     OTHER:
 #ifdef USE_X_TOOLKIT
@@ -24084,19 +24466,10 @@ handle_one_xevent (struct x_display_info *dpyinfo,
       count++;
     }
 
-  /* Sometimes event processing draws to either F or ANY outside
-     redisplay.  To ensure that these changes become visible, draw
-     them here.  */
-
-#ifdef HAVE_XDBE
-  if (f)
-    flush_dirty_back_buffer_on (f);
-
-  if (any && any != f)
-    flush_dirty_back_buffer_on (any);
+#if defined HAVE_XINPUT2 || defined HAVE_XKB || defined HAVE_X_I18N
+  SAFE_FREE ();
 #endif
 
-  SAFE_FREE ();
   return count;
 }
 
@@ -24119,7 +24492,12 @@ x_dispatch_event (XEvent *event, Display *display)
   dpyinfo = x_display_info_for_display (display);
 
   if (dpyinfo)
-    handle_one_xevent (dpyinfo, event, &finish, 0);
+    {
+      /* Block input before calling x_dispatch_event.  */
+      block_input ();
+      handle_one_xevent (dpyinfo, event, &finish, 0);
+      unblock_input ();
+    }
 
   return finish;
 }
@@ -24708,6 +25086,48 @@ static struct x_error_message_stack *x_error_message;
 /* The amount of items (depth) in that stack.  */
 int x_error_message_count;
 
+/* Compare various request serials while handling wraparound.  Treat a
+   difference of more than X_ULONG_MAX / 2 as wraparound.
+
+   Note that these functions truncate serials to 32 bits before
+   comparison.  */
+
+static bool
+x_is_serial_more_than (unsigned int a, unsigned int b)
+{
+  if (a > b)
+    return true;
+
+  return (b - a > X_ULONG_MAX / 2);
+}
+
+static bool
+x_is_serial_more_than_or_equal_to (unsigned int a, unsigned int b)
+{
+  if (a >= b)
+    return true;
+
+  return (b - a > X_ULONG_MAX / 2);
+}
+
+static bool
+x_is_serial_less_than (unsigned int a, unsigned int b)
+{
+  if (a < b)
+    return true;
+
+  return (a - b > X_ULONG_MAX / 2);
+}
+
+static bool
+x_is_serial_less_than_or_equal_to (unsigned int a, unsigned int b)
+{
+  if (a <= b)
+    return true;
+
+  return (a - b > X_ULONG_MAX / 2);
+}
+
 static struct x_error_message_stack *
 x_find_error_handler (Display *dpy, XErrorEvent *event)
 {
@@ -24717,8 +25137,8 @@ x_find_error_handler (Display *dpy, XErrorEvent *event)
 
   while (stack)
     {
-      if (X_COMPARE_SERIALS (event->serial, >=,
-                            stack->first_request)
+      if (x_is_serial_more_than_or_equal_to (event->serial,
+                                            stack->first_request)
          && dpy == stack->dpy)
        return stack;
 
@@ -24821,11 +25241,11 @@ x_request_can_fail (struct x_display_info *dpyinfo,
        failable_requests < dpyinfo->next_failable_request;
        failable_requests++)
     {
-      if (X_COMPARE_SERIALS (request, >=,
-                            failable_requests->start)
+      if (x_is_serial_more_than_or_equal_to (request,
+                                            failable_requests->start)
          && (!failable_requests->end
-             || X_COMPARE_SERIALS (request, <=,
-                                   failable_requests->end)))
+             || x_is_serial_less_than_or_equal_to (request,
+                                                   failable_requests->end)))
        return failable_requests;
     }
 
@@ -24843,11 +25263,11 @@ x_clean_failable_requests (struct x_display_info 
*dpyinfo)
 
   for (first = dpyinfo->failable_requests; first < last; first++)
     {
-      if (X_COMPARE_SERIALS (first->start, >,
-                            LastKnownRequestProcessed (dpyinfo->display))
+      if (x_is_serial_more_than (first->start,
+                                LastKnownRequestProcessed (dpyinfo->display))
          || !first->end
-         || X_COMPARE_SERIALS (first->end, >,
-                               LastKnownRequestProcessed (dpyinfo->display)))
+         || x_is_serial_more_than (first->end,
+                                   LastKnownRequestProcessed 
(dpyinfo->display)))
        break;
     }
 
@@ -24926,8 +25346,7 @@ x_stop_ignoring_errors (struct x_display_info *dpyinfo)
   /* Abort if no request was made since
      `x_ignore_errors_for_next_request'.  */
 
-  if (X_COMPARE_SERIALS (range->end, <,
-                        range->start))
+  if (x_is_serial_less_than (range->end, range->start))
     emacs_abort ();
 
 #ifdef HAVE_GTK3
@@ -25567,6 +25986,14 @@ x_new_font (struct frame *f, Lisp_Object font_object, 
int fontset)
 
 #ifdef HAVE_X11R6
 
+/* HAVE_X11R6 means Xlib conforms to the R6 specification or later.
+   HAVE_X11R6_XIM, OTOH, means that Emacs should try to use R6-style
+   callback driven input method initialization.  They are separate
+   because Sun apparently ships buggy Xlib with some versions of
+   Solaris... */
+
+#ifdef HAVE_X11R6_XIM
+
 /* If preedit text is set on F, cancel preedit, free the text, and
    generate the appropriate events to cancel the preedit display.
 
@@ -25632,6 +26059,8 @@ xim_destroy_callback (XIM xim, XPointer client_data, 
XPointer call_data)
   unblock_input ();
 }
 
+#endif
+
 #endif /* HAVE_X11R6 */
 
 /* Open the connection to the XIM server on display DPYINFO.
@@ -26993,6 +27422,64 @@ xembed_request_focus (struct frame *f)
                         XEMBED_REQUEST_FOCUS, 0, 0, 0);
 }
 
+static Bool
+server_timestamp_predicate (Display *display, XEvent *xevent,
+                           XPointer arg)
+{
+  XID *args = (XID *) arg;
+
+  if (xevent->type == PropertyNotify
+      && xevent->xproperty.window == args[0]
+      && xevent->xproperty.atom == args[1])
+    return True;
+
+  return False;
+}
+
+/* Get the server time.  The X server is guaranteed to deliver the
+   PropertyNotify event, so there is no reason to use x_if_event.  */
+
+static Time
+x_get_server_time (struct frame *f)
+{
+  Atom property_atom;
+  XEvent property_dummy;
+  struct x_display_info *dpyinfo;
+  XID client_data[2];
+#if defined HAVE_XSYNC && !defined USE_GTK && defined HAVE_CLOCK_GETTIME
+  uint_fast64_t current_monotonic_time;
+#endif
+
+  /* If the server time is the same as the monotonic time, avoid a
+     roundtrip by using that instead.  */
+
+#if defined HAVE_XSYNC && !defined USE_GTK && defined HAVE_CLOCK_GETTIME
+  if (FRAME_DISPLAY_INFO (f)->server_time_monotonic_p)
+    {
+      current_monotonic_time = x_sync_current_monotonic_time ();
+
+      if (current_monotonic_time)
+       /* Truncate the time to CARD32.  */
+       return (current_monotonic_time / 1000) & X_ULONG_MAX;
+    }
+#endif
+
+  dpyinfo = FRAME_DISPLAY_INFO (f);
+  property_atom = dpyinfo->Xatom_EMACS_SERVER_TIME_PROP;
+  client_data[0] = FRAME_OUTER_WINDOW (f);
+  client_data[1] = property_atom;
+
+  XChangeProperty (dpyinfo->display, FRAME_OUTER_WINDOW (f),
+                  property_atom, XA_ATOM, 32,
+                  PropModeReplace,
+                  (unsigned char *) &property_atom, 1);
+
+  XIfEvent (dpyinfo->display, &property_dummy,
+           server_timestamp_predicate, (XPointer) client_data);
+
+  return property_dummy.xproperty.time;
+}
+
 /* Activate frame with Extended Window Manager Hints */
 
 static void
@@ -27000,6 +27487,7 @@ x_ewmh_activate_frame (struct frame *f)
 {
   XEvent msg;
   struct x_display_info *dpyinfo;
+  Time time;
 
   dpyinfo = FRAME_DISPLAY_INFO (f);
 
@@ -27020,6 +27508,43 @@ x_ewmh_activate_frame (struct frame *f)
       msg.xclient.data.l[3] = 0;
       msg.xclient.data.l[4] = 0;
 
+      /* No frame is currently focused on that display, so apply any
+        bypass for focus stealing prevention that the user has
+        specified.  */
+      if (!dpyinfo->x_focus_frame)
+       {
+         if (EQ (Vx_allow_focus_stealing, Qimitate_pager))
+           msg.xclient.data.l[0] = 2;
+         else if (EQ (Vx_allow_focus_stealing, Qnewer_time))
+           {
+             block_input ();
+             time = x_get_server_time (f);
+#ifdef USE_GTK
+             x_set_gtk_user_time (f, time);
+#endif
+             /* Temporarily override dpyinfo->x_focus_frame so the
+                user time property is set on the right window.  */
+             dpyinfo->x_focus_frame = f;
+             x_display_set_last_user_time (dpyinfo, time, true, true);
+             dpyinfo->x_focus_frame = NULL;
+             unblock_input ();
+
+             msg.xclient.data.l[1] = time;
+           }
+         else if (EQ (Vx_allow_focus_stealing, Qraise_and_focus))
+           {
+             time = x_get_server_time (f);
+
+             x_ignore_errors_for_next_request (dpyinfo);
+             XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
+                             RevertToParent, time);
+             XRaiseWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f));
+             x_stop_ignoring_errors (dpyinfo);
+
+             return;
+           }
+       }
+
       XSendEvent (dpyinfo->display, dpyinfo->root_window,
                  False, (SubstructureRedirectMask
                          | SubstructureNotifyMask), &msg);
@@ -27040,6 +27565,25 @@ x_get_focus_frame (struct frame *f)
   return lisp_focus;
 }
 
+/* Return the toplevel parent of F, if it is a child frame.
+   Otherwise, return NULL.  */
+
+static struct frame *
+x_get_toplevel_parent (struct frame *f)
+{
+  struct frame *parent;
+
+  if (!FRAME_PARENT_FRAME (f))
+    return NULL;
+
+  parent = FRAME_PARENT_FRAME (f);
+
+  while (FRAME_PARENT_FRAME (parent))
+    parent = FRAME_PARENT_FRAME (parent);
+
+  return parent;
+}
+
 /* In certain situations, when the window manager follows a
    click-to-focus policy, there seems to be no way around calling
    XSetInputFocus to give another frame the input focus.
@@ -27053,6 +27597,7 @@ static void
 x_focus_frame (struct frame *f, bool noactivate)
 {
   struct x_display_info *dpyinfo;
+  Time time;
 
   dpyinfo = FRAME_DISPLAY_INFO (f);
 
@@ -27064,6 +27609,18 @@ x_focus_frame (struct frame *f, bool noactivate)
   else
     {
       if (!noactivate
+         /* If F is override-redirect, use SetInputFocus instead.
+            Override-redirect frames are not subject to window
+            management.  */
+         && !FRAME_OVERRIDE_REDIRECT (f)
+         /* If F is a child frame, use SetInputFocus instead.  This
+            may not work if its parent is not activated.  */
+         && !FRAME_PARENT_FRAME (f)
+         /* If the focus is being transferred from a child frame to
+            its toplevel parent, also use SetInputFocus.  */
+         && (!dpyinfo->x_focus_frame
+             || (x_get_toplevel_parent (dpyinfo->x_focus_frame)
+                 != f))
          && x_wm_supports (f, dpyinfo->Xatom_net_active_window))
        {
          /* When window manager activation is possible, use it
@@ -27078,16 +27635,25 @@ x_focus_frame (struct frame *f, bool noactivate)
       /* Ignore any BadMatch error this request might result in.  */
       x_ignore_errors_for_next_request (dpyinfo);
       if (NILP (Vx_no_window_manager))
-       XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
-                       /* It is invalid to use CurrentTime according to
-                          the ICCCM:
-
-                          Clients that use a SetInputFocus request must
-                          set the time field to the timestamp of the
-                          event that caused them to make the
-                          attempt. [...] Note that clients must not use
-                          CurrentTime in the time field. */
-                       RevertToParent, dpyinfo->last_user_time);
+       {
+         /* Use the last user time.  It is invalid to use CurrentTime
+            according to the ICCCM:
+
+              Clients that use a SetInputFocus request must set the
+              time field to the timestamp of the event that caused
+              them to make the attempt. [...] Note that clients must
+              not use CurrentTime in the time field.  */
+         time = dpyinfo->last_user_time;
+
+         /* Unless the focus doesn't belong to Emacs anymore and
+            `x-allow-focus-stealing' is set to Qnewer_time.  */
+         if (EQ (Vx_allow_focus_stealing, Qnewer_time)
+             && !dpyinfo->x_focus_frame)
+           time = x_get_server_time (f);
+
+         XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
+                         RevertToParent, time);
+       }
       else
        XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
                        /* But when no window manager is in use, we
@@ -28036,7 +28602,9 @@ x_wm_set_size_hint (struct frame *f, long flags, bool 
user_position)
   Window window = FRAME_OUTER_WINDOW (f);
 #ifdef USE_X_TOOLKIT
   WMShellWidget shell;
+#ifndef USE_MOTIF
   bool hints_changed;
+#endif
 #endif
 
   if (!window)
@@ -28063,11 +28631,14 @@ x_wm_set_size_hint (struct frame *f, long flags, bool 
user_position)
          shell->wm.size_hints.flags |= USPosition;
        }
 
+#ifndef USE_MOTIF
       hints_changed
        = widget_update_wm_size_hints (f->output_data.x->widget,
                                       f->output_data.x->edit_widget);
+#else
+      widget_update_wm_size_hints (f->output_data.x->widget,
+                                  f->output_data.x->edit_widget);
 
-#ifdef USE_MOTIF
       /* Do this all over again for the benefit of Motif, which always
         knows better than the programmer.  */
       shell->wm.size_hints.flags &= ~(PPosition | USPosition);
@@ -28527,9 +29098,10 @@ xi_check_toolkit (Display *display)
 
 #endif
 
-/* Open a connection to X display DISPLAY_NAME, and return
-   the structure that describes the open display.
-   If we cannot contact the display, return null.  */
+/* Open a connection to X display DISPLAY_NAME, and return the
+   structure that describes the open display.  If obtaining the XCB
+   connection or toolkit-specific display fails, return NULL.  Signal
+   an error if opening the display itself failed.  */
 
 struct x_display_info *
 x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name)
@@ -28547,6 +29119,21 @@ x_term_init (Lisp_Object display_name, char 
*xrm_option, char *resource_name)
   GdkDisplay *gdpy;
   GdkScreen *gscr;
 #endif
+#ifdef HAVE_XFIXES
+  Lisp_Object tem, lisp_name;
+  int num_fast_selections;
+  Atom selection_name;
+#ifdef USE_XCB
+  xcb_get_selection_owner_cookie_t *selection_cookies;
+  xcb_get_selection_owner_reply_t *selection_reply;
+  xcb_generic_error_t *selection_error;
+#endif
+#endif
+  int i;
+
+#if defined HAVE_XFIXES && defined USE_XCB
+  USE_SAFE_ALLOCA;
+#endif
 
   block_input ();
 
@@ -28556,9 +29143,13 @@ x_term_init (Lisp_Object display_name, char 
*xrm_option, char *resource_name)
       ++x_initialized;
     }
 
-  if (! x_display_ok (SSDATA (display_name)))
+#if defined USE_X_TOOLKIT || defined USE_GTK
+
+  if (!x_display_ok (SSDATA (display_name)))
     error ("Display %s can't be opened", SSDATA (display_name));
 
+#endif
+
 #ifdef USE_GTK
   {
 #define NUM_ARGV 10
@@ -28685,13 +29276,26 @@ x_term_init (Lisp_Object display_name, char 
*xrm_option, char *resource_name)
   /* Detect failure.  */
   if (dpy == 0)
     {
+#if !defined USE_X_TOOLKIT && !defined USE_GTK
+      /* Avoid opening a display three times (once in dispextern.c
+        upon startup, once in x_display_ok, and once above) to
+        determine whether or not the display is alive on no toolkit
+        builds, where no toolkit initialization happens at all.  */
+
+      error ("Display %s can't be opened", SSDATA (display_name));
+#endif
+
       unblock_input ();
+
+#if defined HAVE_XFIXES && defined USE_XCB
+      SAFE_FREE ();
+#endif
       return 0;
     }
 
 #ifdef USE_XCB
   xcb_conn = XGetXCBConnection (dpy);
-  if (xcb_conn == 0)
+  if (!xcb_conn)
     {
 #ifdef USE_GTK
       xg_display_close (dpy);
@@ -28704,6 +29308,10 @@ x_term_init (Lisp_Object display_name, char 
*xrm_option, char *resource_name)
 #endif /* ! USE_GTK */
 
       unblock_input ();
+
+#if defined HAVE_XFIXES && defined USE_XCB
+      SAFE_FREE ();
+#endif
       return 0;
     }
 #endif
@@ -29173,11 +29781,10 @@ x_term_init (Lisp_Object display_name, char 
*xrm_option, char *resource_name)
          xi_select_hierarchy_events (dpyinfo);
 #endif
 
+         dpyinfo->xi2_version = minor;
          x_cache_xi_devices (dpyinfo);
        }
     }
-
-  dpyinfo->xi2_version = minor;
  skip_xi_setup:
   ;
 #endif
@@ -29256,8 +29863,7 @@ x_term_init (Lisp_Object display_name, char 
*xrm_option, char *resource_name)
                                     XkbUseCoreKbd);
 
       if (dpyinfo->xkb_desc)
-       XkbGetNames (dpyinfo->display,
-                    XkbGroupNamesMask | XkbVirtualModNamesMask,
+       XkbGetNames (dpyinfo->display, XkbAllNamesMask,
                     dpyinfo->xkb_desc);
 
       XkbSelectEvents (dpyinfo->display, XkbUseCoreKbd,
@@ -29267,9 +29873,10 @@ x_term_init (Lisp_Object display_name, char 
*xrm_option, char *resource_name)
 #endif
 
 #ifdef HAVE_XFIXES
-  int xfixes_event_base, xfixes_error_base;
+  int xfixes_error_base;
   dpyinfo->xfixes_supported_p
-    = XFixesQueryExtension (dpyinfo->display, &xfixes_event_base,
+    = XFixesQueryExtension (dpyinfo->display,
+                           &dpyinfo->xfixes_event_base,
                            &xfixes_error_base);
 
   if (dpyinfo->xfixes_supported_p)
@@ -29320,7 +29927,6 @@ x_term_init (Lisp_Object display_name, char 
*xrm_option, char *resource_name)
           XScreenNumberOfScreen (dpyinfo->screen));
 
   {
-    int i;
     enum { atom_count = ARRAYELTS (x_atom_refs) };
     /* 1 for _XSETTINGS_SN.  */
     enum { total_atom_count = 2 + atom_count };
@@ -29393,21 +29999,30 @@ x_term_init (Lisp_Object display_name, char 
*xrm_option, char *resource_name)
   {
     XrmValue d, fr, to;
     Font font;
+    XFontStruct *query_result;
 
     dpy = dpyinfo->display;
-    d.addr = (XPointer)&dpy;
+    d.addr = (XPointer) &dpy;
     d.size = sizeof (Display *);
     fr.addr = (char *) XtDefaultFont;
     fr.size = sizeof (XtDefaultFont);
     to.size = sizeof (Font *);
-    to.addr = (XPointer)&font;
+    to.addr = (XPointer) &font;
     x_catch_errors (dpy);
     if (!XtCallConverter (dpy, XtCvtStringToFont, &d, 1, &fr, &to, NULL))
       emacs_abort ();
-    if (x_had_errors_p (dpy) || !XQueryFont (dpy, font))
+    query_result = XQueryFont (dpy, font);
+
+    /* Set the dialog font to some fallback (here, 9x15) if the font
+       specified is invalid.  */
+    if (x_had_errors_p (dpy) || !font)
       XrmPutLineResource (&xrdb, "Emacs.dialog.*.font: 9x15");
-    /* Do not free XFontStruct returned by the above call to XQueryFont.
-       This leads to X protocol errors at XtCloseDisplay (Bug#18403).  */
+
+    /* Do not destroy the font struct returned above with XFreeFont;
+       that also destroys the font, leading to to X protocol errors at
+       XtCloseDisplay.  Just free the font info structure.
+       (Bug#18403) */
+    XFreeFontInfo (NULL, query_result, 1);
     x_uncatch_errors ();
   }
 #endif
@@ -29488,8 +30103,102 @@ x_term_init (Lisp_Object display_name, char 
*xrm_option, char *resource_name)
   dpyinfo->protected_windows_max = 256;
 #endif
 
+#ifdef HAVE_XFIXES
+  /* Initialize selection tracking for the selections in
+     x-fast-selection-list.  */
+
+  if (CONSP (Vx_fast_selection_list)
+      && dpyinfo->xfixes_supported_p
+      && dpyinfo->xfixes_major >= 1)
+    {
+      num_fast_selections = 0;
+      tem = Vx_fast_selection_list;
+
+      FOR_EACH_TAIL_SAFE (tem)
+       {
+         if (!SYMBOLP (XCAR (tem)))
+           continue;
+
+         num_fast_selections++;
+       }
+
+      dpyinfo->n_monitored_selections = num_fast_selections;
+      dpyinfo->selection_tracking_window
+       = x_create_special_window (dpyinfo, dpyinfo->root_window);
+      dpyinfo->monitored_selections
+       = xmalloc (num_fast_selections
+                  * sizeof *dpyinfo->monitored_selections);
+
+      num_fast_selections = 0;
+      tem = Vx_fast_selection_list;
+
+      FOR_EACH_TAIL_SAFE (tem)
+       {
+         lisp_name = XCAR (tem);
+
+         if (!SYMBOLP (lisp_name))
+           continue;
+
+         selection_name = symbol_to_x_atom (dpyinfo, lisp_name);
+         dpyinfo->monitored_selections[num_fast_selections++].name
+           = selection_name;
+         dpyinfo->monitored_selections[num_fast_selections - 1].owner
+           = X_INVALID_WINDOW;
+
+         /* Select for selection input.  */
+         XFixesSelectSelectionInput (dpyinfo->display,
+                                     dpyinfo->selection_tracking_window,
+                                     selection_name,
+                                     (XFixesSetSelectionOwnerNotifyMask
+                                      | XFixesSetSelectionOwnerNotifyMask
+                                      | XFixesSelectionClientCloseNotifyMask));
+       }
+
+#ifdef USE_XCB
+      selection_cookies = SAFE_ALLOCA (sizeof *selection_cookies
+                                      * num_fast_selections);
+#endif
+
+      /* Now, ask for the current owners of all those selections.  */
+      for (i = 0; i < num_fast_selections; ++i)
+       {
+#ifdef USE_XCB
+         selection_cookies[i]
+           = xcb_get_selection_owner (dpyinfo->xcb_connection,
+                                      dpyinfo->monitored_selections[i].name);
+#else
+         dpyinfo->monitored_selections[i].owner
+           = XGetSelectionOwner (dpyinfo->display,
+                                 dpyinfo->monitored_selections[i].name);
+#endif
+       }
+
+#ifdef USE_XCB
+      for (i = 0; i < num_fast_selections; ++i)
+       {
+         selection_reply
+           = xcb_get_selection_owner_reply (dpyinfo->xcb_connection,
+                                            selection_cookies[i],
+                                            &selection_error);
+
+         if (selection_reply)
+           {
+             dpyinfo->monitored_selections[i].owner
+               = selection_reply->owner;
+             free (selection_reply);
+           }
+         else if (selection_error)
+           free (selection_error);
+       }
+#endif
+    }
+#endif
+
   unblock_input ();
 
+#if defined HAVE_XFIXES && defined USE_XCB
+  SAFE_FREE ();
+#endif
   return dpyinfo;
 }
 
@@ -29625,6 +30334,10 @@ x_delete_display (struct x_display_info *dpyinfo)
   xfree (dpyinfo->x_id_name);
   xfree (dpyinfo->x_dnd_atoms);
   xfree (dpyinfo->color_cells);
+#ifdef HAVE_XFIXES
+  if (dpyinfo->monitored_selections)
+    xfree (dpyinfo->monitored_selections);
+#endif
 #ifdef USE_TOOLKIT_SCROLL_BARS
   xfree (dpyinfo->protected_windows);
 #endif
@@ -29796,11 +30509,6 @@ x_delete_terminal (struct terminal *terminal)
         closing all the displays.  */
       XrmDestroyDatabase (dpyinfo->rdb);
 #endif
-
-#ifdef HAVE_XKB
-      if (dpyinfo->xkb_desc)
-       XkbFreeKeyboard (dpyinfo->xkb_desc, XkbAllComponentsMask, True);
-#endif
 #ifdef USE_GTK
       xg_display_close (dpyinfo->display);
 #else
@@ -29810,9 +30518,6 @@ x_delete_terminal (struct terminal *terminal)
       XCloseDisplay (dpyinfo->display);
 #endif
 #endif /* ! USE_GTK */
-
-      if (dpyinfo->modmap)
-       XFreeModifiermap (dpyinfo->modmap);
       /* Do not close the connection here because it's already closed
         by X(t)CloseDisplay (Bug#18403).  */
       dpyinfo->display = NULL;
@@ -29825,6 +30530,18 @@ x_delete_terminal (struct terminal *terminal)
   else if (dpyinfo->connection >= 0)
     emacs_close (dpyinfo->connection);
 
+  /* Free the keyboard and modifier maps here; that is safe to do
+     without a display, and not doing so leads to a lot of data being
+     leaked upon IO error.  */
+
+#ifdef HAVE_XKB
+  if (dpyinfo->xkb_desc)
+    XkbFreeKeyboard (dpyinfo->xkb_desc, XkbAllComponentsMask, True);
+#endif
+
+  if (dpyinfo->modmap)
+    XFreeModifiermap (dpyinfo->modmap);
+
   /* No more input on this descriptor.  */
   delete_keyboard_wait_descriptor (dpyinfo->connection);
   /* Mark as dead. */
@@ -30007,7 +30724,7 @@ mark_xterm (void)
 {
   Lisp_Object val;
 #if defined HAVE_XINPUT2 || defined USE_TOOLKIT_SCROLL_BARS \
-  || defined HAVE_XRANDR || defined USE_GTK
+  || defined HAVE_XRANDR || defined USE_GTK || defined HAVE_X_I18N
   struct x_display_info *dpyinfo;
 #if defined HAVE_XINPUT2 || defined USE_TOOLKIT_SCROLL_BARS
   int i;
@@ -30231,8 +30948,14 @@ x_get_keyboard_modifiers (struct x_display_info 
*dpyinfo)
   /* This sometimes happens when the function is called during display
      initialization, which can happen while obtaining vendor specific
      keysyms.  */
+
+#ifdef HAVE_XKB
   if (!dpyinfo->xkb_desc && !dpyinfo->modmap)
     x_find_modifier_meanings (dpyinfo);
+#else
+  if (!dpyinfo->modmap)
+    x_find_modifier_meanings (dpyinfo);
+#endif
 
   return list5 (make_uint (dpyinfo->hyper_mod_mask),
                make_uint (dpyinfo->super_mod_mask),
@@ -30352,6 +31075,9 @@ With MS Windows, Haiku windowing or Nextstep, the value 
is t.  */);
   Fput (Qsuper, Qmodifier_value, make_fixnum (super_modifier));
   DEFSYM (QXdndSelection, "XdndSelection");
   DEFSYM (Qx_selection_alias_alist, "x-selection-alias-alist");
+  DEFSYM (Qimitate_pager, "imitate-pager");
+  DEFSYM (Qnewer_time, "newer-time");
+  DEFSYM (Qraise_and_focus, "raise-and-focus");
 
   DEFVAR_LISP ("x-ctrl-keysym", Vx_ctrl_keysym,
     doc: /* Which keys Emacs uses for the ctrl modifier.
@@ -30592,4 +31318,43 @@ It should accept a single argument, a string 
describing the locale of
 the input method, and return a coding system that can decode keyboard
 input generated by said input method.  */);
   Vx_input_coding_function = Qnil;
+
+  DEFVAR_LISP ("x-fast-selection-list", Vx_fast_selection_list,
+    doc: /* List of selections for which `x-selection-exists-p' should be fast.
+
+List of selection names as atoms that will be monitored by Emacs for
+ownership changes when the X server supports the XFIXES extension.
+The result of the monitoring is then used by `x-selection-exists-p' to
+avoid a server round trip, which is important as it is called while
+updating the tool bar.  The value of this variable is only read upon
+connection setup.  */);
+  /* The default value of this variable is chosen so that updating the
+     tool bar does not require a call to _XReply.  */
+  Vx_fast_selection_list = list1 (QCLIPBOARD);
+
+  DEFVAR_LISP ("x-allow-focus-stealing", Vx_allow_focus_stealing,
+    doc: /* How to bypass window manager focus stealing prevention.
+
+Some window managers prevent `x-focus-frame' from activating the given
+frame when Emacs is in the background, which is especially prone to
+cause problems when the Emacs server wants to activate itself.
+
+In addition, when an old-fashioned (pre-EWMH) window manager is being
+run and `x-no-window-manager' is nil, the X server will not let Emacs
+focus itself if another program was focused after the last time Emacs
+obtained the input focus.
+
+This variable specifies the strategy used to activate frames when that
+is the case, and has several valid values (any other value means to
+not bypass window manager focus stealing prevention):
+
+  - The symbol `imitate-pager', which means to pretend that Emacs is a
+    pager.
+
+  - The symbol `newer-time', which means to fetch the current time
+    from the X server and use it to activate the frame.
+
+  - The symbol `raise-and-focus', which means to raise the window and
+    focus it manually.  */);
+  Vx_allow_focus_stealing = Qnewer_time;
 }
diff --git a/src/xterm.h b/src/xterm.h
index b68a234faa..1124dcceb4 100644
--- a/src/xterm.h
+++ b/src/xterm.h
@@ -213,20 +213,32 @@ struct color_name_cache_entry
 #ifdef HAVE_XINPUT2
 
 #ifdef HAVE_XINPUT2_1
+
 struct xi_scroll_valuator_t
 {
-  bool invalid_p;
-  bool pending_enter_reset;
+  /* The ID of the valuator.  */
+  int number;
+
+  /* Whether or not it represents X axis movement.  */
+  bool_bf horizontal : 1;
+
+  /* Whether or not the value is currently invalid.  */
+  bool_bf invalid_p : 1;
+
+  /* The current value.  */
   double current_value;
+
+  /* Value used to tally up deltas until a threshold is met.  */
   double emacs_value;
-  double increment;
 
-  int number;
-  int horizontal;
+  /* The scroll increment.  */
+  double increment;
 };
+
 #endif
 
 #ifdef HAVE_XINPUT2_2
+
 struct xi_touch_point_t
 {
   struct xi_touch_point_t *next;
@@ -234,6 +246,7 @@ struct xi_touch_point_t
   int number;
   double x, y;
 };
+
 #endif
 
 struct xi_device_t
@@ -295,8 +308,7 @@ struct xi_device_t
 };
 #endif
 
-Status x_parse_color (struct frame *f, const char *color_name,
-                     XColor *color);
+extern Status x_parse_color (struct frame *, const char *, XColor *);
 
 struct x_failable_request
 {
@@ -308,6 +320,22 @@ struct x_failable_request
   unsigned long end;
 };
 
+#ifdef HAVE_XFIXES
+
+struct x_monitored_selection
+{
+  /* The name of the selection.  */
+  Atom name;
+
+  /* The current owner of the selection.  */
+  Window owner;
+};
+
+/* An invalid window.  */
+#define X_INVALID_WINDOW 0xffffffff
+
+#endif
+
 
 /* For each X display, we have a structure that records
    information about it.  */
@@ -778,6 +806,7 @@ struct x_display_info
   bool xfixes_supported_p;
   int xfixes_major;
   int xfixes_minor;
+  int xfixes_event_base;
 #endif
 
 #ifdef HAVE_XSYNC
@@ -828,6 +857,17 @@ struct x_display_info
   /* Pointer to the next request in `failable_requests'.  */
   struct x_failable_request *next_failable_request;
 
+#ifdef HAVE_XFIXES
+  /* Array of selections being monitored and their owners.  */
+  struct x_monitored_selection *monitored_selections;
+
+  /* Window used to monitor those selections.  */
+  Window selection_tracking_window;
+
+  /* The number of those selections.  */
+  int n_monitored_selections;
+#endif
+
   /* The pending drag-and-drop time for middle-click based
      drag-and-drop emulation.  */
   Time pending_dnd_time;
@@ -916,11 +956,6 @@ struct x_output
   Picture picture;
 #endif
 
-  /* Flag that indicates whether we've modified the back buffer and
-     need to publish our modifications to the front buffer at a
-     convenient time.  */
-  bool need_buffer_flip;
-
   /* The X window used for the bitmap icon;
      or 0 if we don't have a bitmap icon.  */
   Window icon_desc;
@@ -1091,6 +1126,18 @@ struct x_output
      and inactive states.  */
   bool_bf alpha_identical_p : 1;
 
+#ifdef HAVE_XDBE
+  /* Flag that indicates whether we've modified the back buffer and
+     need to publish our modifications to the front buffer at a
+     convenient time.  */
+  bool_bf need_buffer_flip : 1;
+
+  /* Flag that indicates whether or not the frame contents are
+     complete and can be safely flushed while handling async
+     input.  */
+  bool_bf complete : 1;
+#endif
+
 #ifdef HAVE_X_I18N
   /* Input context (currently, this means Compose key handler setup).  */
   XIC xic;
@@ -1248,6 +1295,10 @@ extern void x_mark_frame_dirty (struct frame *f);
 
 /* Return the need-buffer-flip flag for frame F.  */
 #define FRAME_X_NEED_BUFFER_FLIP(f) ((f)->output_data.x->need_buffer_flip)
+
+/* Return whether or not the frame F has been completely drawn.  Used
+   while handling async input.  */
+#define FRAME_X_COMPLETE_P(f) ((f)->output_data.x->complete)
 #endif
 
 /* Return the outermost X window associated with the frame F.  */
@@ -1645,6 +1696,10 @@ extern void x_cr_draw_frame (cairo_t *, struct frame *);
 extern Lisp_Object x_cr_export_frames (Lisp_Object, cairo_surface_type_t);
 #endif
 
+#ifdef HAVE_XFIXES
+extern Window x_find_selection_owner (struct x_display_info *, Atom);
+#endif
+
 #ifdef HAVE_XRENDER
 extern void x_xrender_color_from_gc_background (struct frame *, GC,
                                                XRenderColor *, bool);
@@ -1653,6 +1708,12 @@ extern void x_xr_apply_ext_clip (struct frame *, GC);
 extern void x_xr_reset_ext_clip (struct frame *);
 #endif
 
+extern void x_translate_coordinates (struct frame *, int, int, int *, int *);
+extern void x_translate_coordinates_to_root (struct frame *, int, int,
+                                            int *, int *);
+extern Lisp_Object x_handle_translate_coordinates (struct frame *, Lisp_Object,
+                                                  int, int);
+
 extern Bool x_query_pointer (Display *, Window, Window *, Window *, int *,
                             int *, int *, int *, unsigned int *);
 
diff --git a/test/lisp/apropos-tests.el b/test/lisp/apropos-tests.el
index 289700abf7..917c08b911 100644
--- a/test/lisp/apropos-tests.el
+++ b/test/lisp/apropos-tests.el
@@ -120,14 +120,15 @@
   (should (apropos-true-hit "foo bar baz" '("foo" "bar"))))
 
 (ert-deftest apropos-tests-format-plist ()
-  (setplist 'foo '(a 1 b (2 3) c nil))
-  (apropos-parse-pattern '("b"))
-  (should (equal (apropos-format-plist 'foo ", ")
-                 "a 1, b (2 3), c nil"))
-  (should (equal (apropos-format-plist 'foo ", " t)
-                 "b (2 3)"))
-  (apropos-parse-pattern '("d"))
-  (should-not (apropos-format-plist 'foo ", " t)))
+  (let ((foo (make-symbol "foo")))
+    (setplist foo '(a 1 b (2 3) c nil))
+    (apropos-parse-pattern '("b"))
+    (should (equal (apropos-format-plist foo ", ")
+                   "a 1, b (2 3), c nil"))
+    (should (equal (apropos-format-plist foo ", " t)
+                   "b (2 3)"))
+    (apropos-parse-pattern '("d"))
+    (should-not (apropos-format-plist foo ", " t))))
 
 (provide 'apropos-tests)
 ;;; apropos-tests.el ends here
diff --git a/test/lisp/autorevert-tests.el b/test/lisp/autorevert-tests.el
index 4bbff6d057..568820ec42 100644
--- a/test/lisp/autorevert-tests.el
+++ b/test/lisp/autorevert-tests.el
@@ -507,7 +507,7 @@ This expects `auto-revert--messages' to be bound by
                  (should (equal (auto-revert-test--buffer-string buf-1) "1-a"))
                  (auto-revert-test--write-file "1-b" file-1)
                  ;; Since the file is deleted, it needs at least
-                 ;; `autorevert-interval' to recognize the new file,
+                 ;; `auto-revert-interval' to recognize the new file,
                  ;; while polling.  So increase the timeout.
                  (auto-revert-test--wait-for-buffer-text
                   buf-1 "1-b" (* 2 (auto-revert--timeout)))
diff --git a/test/lisp/calendar/icalendar-tests.el 
b/test/lisp/calendar/icalendar-tests.el
index 7f8cd47914..2e9353a09b 100644
--- a/test/lisp/calendar/icalendar-tests.el
+++ b/test/lisp/calendar/icalendar-tests.el
@@ -1310,7 +1310,7 @@ SUMMARY:and diary-anniversary
                                 "import-real-world-2003-05-29.diary-european"
                                 "import-real-world-2003-05-29.diary-american")
 
-  ;; created with http://apps.marudot.com/ical/
+  ;; created with https://apps.marudot.com/ical/
   (icalendar-tests--test-import "import-real-world-no-dst.ics"
                                 nil
                                 "import-real-world-no-dst.diary-european"
diff --git a/test/lisp/cedet/semantic-utest.el 
b/test/lisp/cedet/semantic-utest.el
index 24a467474b..b577b19808 100644
--- a/test/lisp/cedet/semantic-utest.el
+++ b/test/lisp/cedet/semantic-utest.el
@@ -609,7 +609,6 @@ INSERTME is the text to be inserted after the deletion."
   (semantic-utest-generic (semantic-utest-fname "phptest.php") 
semantic-utest-PHP-buffer-contents semantic-utest-PHP-name-contents '("fun1") 
"fun2" "%^@")
   )
 
-;look at http://mfgames.com/linux/csharp-mode
 (ert-deftest semantic-utest-Csharp() ;; hmm i don't even know how to edit a 
scharp file. need a csharp mode implementation i suppose
   (skip-unless (featurep 'csharp-mode))
   (semantic-utest-generic (semantic-utest-fname "csharptest.cs") 
semantic-utest-Csharp-buffer-contents  semantic-utest-Csharp-name-contents   
'("fun2") "//1" "//deleted line")
diff --git a/test/lisp/dired-tests.el b/test/lisp/dired-tests.el
index 09becc7fe7..18b0257e01 100644
--- a/test/lisp/dired-tests.el
+++ b/test/lisp/dired-tests.el
@@ -354,6 +354,17 @@
         (should (equal "subdir" (dired-get-filename 'local t)))))))
 
 
+(ert-deftest dired-test-bug59047 ()
+  "Test for https://debbugs.gnu.org/59047 ."
+  (dired (list (expand-file-name "src" source-directory)
+               "cygw32.c" "alloc.c" "w32xfns.c" "xdisp.c"))
+  (dired-hide-all)
+  (dired-hide-all)
+  (dired-next-line 1)
+  (should (equal 'dired-hide-details-detail
+                 (get-text-property
+                  (1+ (line-beginning-position)) 'invisible))))
+
 (defmacro dired-test-with-temp-dirs (just-empty-dirs &rest body)
   "Helper macro for Bug#27940 test."
   (declare (indent 1) (debug body))
diff --git a/test/lisp/dnd-tests.el b/test/lisp/dnd-tests.el
index 67b660fc12..bdadc0f280 100644
--- a/test/lisp/dnd-tests.el
+++ b/test/lisp/dnd-tests.el
@@ -416,7 +416,7 @@ This function only tries to handle strings."
   ;; system specific test is in x-dnd-tests.el.  When running this
   ;; interactively, keep in mind that there are only two file managers
   ;; which are known to implement XDS correctly: System G (see
-  ;; http://nps-systemg.sourceforge.net), and Emacs itself.  GTK file
+  ;; https://nps-systemg.sourceforge.net), and Emacs itself.  GTK file
   ;; managers such as Nautilus will not work, since they prefer the
   ;; `text/uri-list' selection target to `XdndDirectSave0', contrary
   ;; to the XDS specification.
diff --git a/test/lisp/elide-head-tests.el b/test/lisp/elide-head-tests.el
index 6f351170f1..429ef26657 100644
--- a/test/lisp/elide-head-tests.el
+++ b/test/lisp/elide-head-tests.el
@@ -3,7 +3,6 @@
 ;; Copyright (C) 2020-2022 Free Software Foundation, Inc.
 
 ;; Author: Simen Heggestøyl <simenheg@gmail.com>
-;; Keywords:
 
 ;; This file is part of GNU Emacs.
 
@@ -20,10 +19,6 @@
 ;; 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:
-
-;;
-
 ;;; Code:
 
 (require 'elide-head)
@@ -169,6 +164,22 @@
  ***************************************************************************/
 " "This program is distributed in the hope that")
 
+;; from mentor.el    [no "/" in the gnu.org URL]
+(elide-head--add-test gpl3-5 "\
+;; Mentor 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, or (at your option)
+;; any later version.
+;;
+;; Mentor 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 Mentor.  If not, see <https://www.gnu.org/licenses>.
+" "Mentor is distributed in the hope that")
+
 
 ;;; GPLv2
 
diff --git a/test/lisp/emacs-lisp/bindat-tests.el 
b/test/lisp/emacs-lisp/bindat-tests.el
index 0c03c51e2e..2abf714852 100644
--- a/test/lisp/emacs-lisp/bindat-tests.el
+++ b/test/lisp/emacs-lisp/bindat-tests.el
@@ -252,7 +252,24 @@
     (should (equal (bindat-unpack spec "abc\0") "abc"))
     ;; Missing null terminator.
     (should-error (bindat-unpack spec ""))
-    (should-error (bindat-unpack spec "a"))))
+    (should-error (bindat-unpack spec "a")))
+
+  (ert-deftest bindat-test--strz-array-unpack ()
+    (should (equal (bindat-unpack spec [#x61 #x62 #x63 #x00]) "abc"))))
+
+(let ((spec (bindat-type str 3)))
+  (ert-deftest bindat-test--str-simple-array-unpack ()
+    (should (equal (bindat-unpack spec [#x61 #x62 #x63]) "abc"))))
+
+(let ((spec (bindat-type
+              (first u8)
+              (string str 3)
+              (last uint 16))))
+  (ert-deftest bindat-test--str-combined-array-unpack ()
+    (let ((unpacked (bindat-unpack spec [#xff #x63 #x62 #x61 #xff #xff])))
+      (should (equal (bindat-get-field unpacked 'string) "cba"))
+      (should (equal (bindat-get-field unpacked 'first) (- (expt 2 8) 1)))
+      (should (equal (bindat-get-field unpacked 'last) (- (expt 2 16) 1))))))
 
 (let ((spec '((x strz 2))))
   (ert-deftest bindat-test--strz-legacy-fixedlen-len ()
diff --git a/test/lisp/emacs-lisp/cconv-tests.el 
b/test/lisp/emacs-lisp/cconv-tests.el
index 37470f863f..e666fe0a4c 100644
--- a/test/lisp/emacs-lisp/cconv-tests.el
+++ b/test/lisp/emacs-lisp/cconv-tests.el
@@ -351,11 +351,18 @@
   (let ((f (let ((d 51695))
              (lambda (data)
                (interactive (progn (setq d (1+ d)) (list d)))
-               (list (called-interactively-p 'any) data)))))
-    (should (equal (list (call-interactively f)
-                         (funcall f 51695)
-                         (call-interactively f))
-                   '((t 51696) (nil 51695) (t 51697))))))
+               (list (called-interactively-p 'any) data))))
+        (f-interp
+         (eval '(let ((d 51695))
+                  (lambda (data)
+                    (interactive (progn (setq d (1+ d)) (list d)))
+                    (list (called-interactively-p 'any) data)))
+               t)))
+    (dolist (f (list f f-interp))
+      (should (equal (list (call-interactively f)
+                           (funcall f 51695)
+                           (call-interactively f))
+                     '((t 51696) (nil 51695) (t 51697)))))))
 
 (provide 'cconv-tests)
 ;;; cconv-tests.el ends here
diff --git a/test/lisp/emacs-lisp/cl-extra-tests.el 
b/test/lisp/emacs-lisp/cl-extra-tests.el
index 297e413d85..6a34cd681e 100644
--- a/test/lisp/emacs-lisp/cl-extra-tests.el
+++ b/test/lisp/emacs-lisp/cl-extra-tests.el
@@ -32,8 +32,28 @@
 (ert-deftest cl-getf ()
   (let ((plist '(x 1 y nil)))
     (should (eq (cl-getf plist 'x) 1))
-    (should (eq (cl-getf plist 'y :none) nil))
-    (should (eq (cl-getf plist 'z :none) :none))))
+    (should-not (cl-getf plist 'y :none))
+    (should (eq (cl-getf plist 'z :none) :none))
+    (should (eq (cl-incf (cl-getf plist 'x 10) 2) 3))
+    (should (equal plist '(x 3 y nil)))
+    (should-error (cl-incf (cl-getf plist 'y 10) 4) :type 'wrong-type-argument)
+    (should (equal plist '(x 3 y nil)))
+    (should (eq (cl-incf (cl-getf plist 'z 10) 5) 15))
+    (should (equal plist '(z 15 x 3 y nil))))
+  (let ((plist '(x 1 y)))
+    (should (eq (cl-getf plist 'x) 1))
+    (should (eq (cl-getf plist 'y :none) :none))
+    (should (eq (cl-getf plist 'z :none) :none))
+    (should (eq (cl-incf (cl-getf plist 'x 10) 2) 3))
+    (should (equal plist '(x 3 y)))
+    (should (eq (cl-incf (cl-getf plist 'y 10) 4) 14))
+    (should (equal plist '(y 14 x 3 y))))
+  (let ((plist '(x 1 y . 2)))
+    (should (eq (cl-getf plist 'x) 1))
+    (should (eq (cl-incf (cl-getf plist 'x 10) 2) 3))
+    (should (equal plist '(x 3 y . 2)))
+    (should-error (cl-getf plist 'y :none) :type 'wrong-type-argument)
+    (should-error (cl-getf plist 'z :none) :type 'wrong-type-argument)))
 
 (ert-deftest cl-extra-test-mapc ()
   (let ((lst '(a b c))
diff --git a/test/lisp/emacs-lisp/cl-generic-tests.el 
b/test/lisp/emacs-lisp/cl-generic-tests.el
index 56b766769e..8e807b1591 100644
--- a/test/lisp/emacs-lisp/cl-generic-tests.el
+++ b/test/lisp/emacs-lisp/cl-generic-tests.el
@@ -297,5 +297,27 @@ Edebug symbols (Bug#42672)."
                      (intern "cl-defgeneric/edebug/method/2 (number)")
                      'cl-defgeneric/edebug/method/2))))))
 
+(cl-defgeneric cl-generic-tests--acc (x &optional y)
+  (declare (advertised-calling-convention (x) "671.2")))
+
+(cl-defmethod cl-generic-tests--acc ((x float)) (+ x 5.0))
+
+(ert-deftest cl-generic-tests--advertised-calling-convention-bug58563 ()
+  (should (equal (get-advertised-calling-convention
+                  (indirect-function 'cl-generic-tests--acc))
+                 '(x)))
+  (should
+   (condition-case err
+       (let ((lexical-binding t)
+             (byte-compile-debug t)
+             (byte-compile-error-on-warn t))
+         (byte-compile '(cl-defmethod cl-generic-tests--acc ((x list))
+                          (declare (advertised-calling-convention (y) "1.1"))
+                          (cons x '(5 5 5 5 5))))
+         nil)
+     (error
+      (and (eq 'error (car err))
+           (string-match "Stray.*declare" (cadr err)))))))
+
 (provide 'cl-generic-tests)
 ;;; cl-generic-tests.el ends here
diff --git a/test/lisp/emacs-lisp/cl-macs-tests.el 
b/test/lisp/emacs-lisp/cl-macs-tests.el
index f742637ee3..160ac59113 100644
--- a/test/lisp/emacs-lisp/cl-macs-tests.el
+++ b/test/lisp/emacs-lisp/cl-macs-tests.el
@@ -803,4 +803,10 @@ See Bug#57915."
             (macroexpand form)
             (should (string-empty-p messages))))))))
 
+(ert-deftest cl-&key-arguments ()
+  (cl-flet ((fn (&key x) x))
+    (should-error (fn :x))
+    (should (eq (fn :x :a) :a))))
+
+
 ;;; cl-macs-tests.el ends here
diff --git a/test/lisp/emacs-lisp/comp-tests.el 
b/test/lisp/emacs-lisp/comp-tests.el
new file mode 100644
index 0000000000..082b641fe3
--- /dev/null
+++ b/test/lisp/emacs-lisp/comp-tests.el
@@ -0,0 +1,77 @@
+;;; comp-tests.el --- Tests for comp.el  -*- lexical-binding:t -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+
+(require 'ert)
+(require 'ert-x)
+(require 'comp)
+
+(defvar comp-native-version-dir)
+(defvar native-comp-eln-load-path)
+
+(defmacro with-test-native-compile-prune-cache (&rest body)
+  (declare (indent 0) (debug t))
+  `(ert-with-temp-directory testdir
+     (setq testdir (expand-file-name "eln-cache" testdir))
+     (make-directory testdir)
+     (let* ((c1 (expand-file-name "29.0.50-cur" testdir))
+            (c2 (expand-file-name "29.0.50-old" testdir))
+            (native-comp-eln-load-path (list testdir))
+            (comp-native-version-dir "29.0.50-cur"))
+       (dolist (d (list c1 c2))
+         (make-directory d)
+         (with-temp-file (expand-file-name "some.eln" d) (insert "foo"))
+         (with-temp-file (expand-file-name "some.eln.tmp" d) (insert "foo")))
+       ,@body)))
+
+(ert-deftest test-native-compile-prune-cache ()
+  (skip-unless (featurep 'native-compile))
+  (with-test-native-compile-prune-cache
+    (native-compile-prune-cache)
+    (should (file-directory-p c1))
+    (should (file-regular-p (expand-file-name "some.eln" c1)))
+    (should (file-regular-p (expand-file-name "some.eln.tmp" c1)))
+    (should-not (file-directory-p c2))
+    (should-not (file-regular-p (expand-file-name "some.eln" c2)))
+    (should-not (file-regular-p (expand-file-name "some.eln.tmp" c2)))))
+
+(ert-deftest test-native-compile-prune-cache/delete-only-eln ()
+  (skip-unless (featurep 'native-compile))
+  (with-test-native-compile-prune-cache
+    (with-temp-file (expand-file-name "keep1.txt" c1) (insert "foo"))
+    (with-temp-file (expand-file-name "keep2.txt" c2) (insert "foo"))
+    (native-compile-prune-cache)
+    (should (file-regular-p (expand-file-name "keep1.txt" c1)))
+    (should (file-regular-p (expand-file-name "keep2.txt" c2)))))
+
+(ert-deftest test-native-compile-prune-cache/dont-delete-in-parent-of-cache ()
+  (skip-unless (featurep 'native-compile))
+  (with-test-native-compile-prune-cache
+    (let ((f1 (expand-file-name "../some.eln" testdir))
+          (f2 (expand-file-name "some.eln" testdir)))
+      (with-temp-file f1 (insert "foo"))
+      (with-temp-file f2 (insert "foo"))
+      (native-compile-prune-cache)
+      (should (file-regular-p f1))
+      (should (file-regular-p f2)))))
+
+;;; comp-tests.el ends here
diff --git a/test/lisp/emacs-lisp/gv-tests.el b/test/lisp/emacs-lisp/gv-tests.el
index 0757e3c7aa..69a7bcf7dd 100644
--- a/test/lisp/emacs-lisp/gv-tests.el
+++ b/test/lisp/emacs-lisp/gv-tests.el
@@ -157,55 +157,42 @@ its getter (Bug#41853)."
                       (push 123 (gv-setter-edebug-get 'gv-setter-edebug
                                                       
'gv-setter-edebug-prop))))
         (print form (current-buffer)))
-      ;; Only check whether evaluation works in general.
-      (eval-buffer)))
+      ;; Silence "Edebug: foo" messages.
+      (let ((inhibit-message t))
+        ;; Only check whether evaluation works in general.
+        (eval-buffer))))
   (should (equal (get 'gv-setter-edebug 'gv-setter-edebug-prop) '(123))))
 
 (ert-deftest gv-plist-get ()
-  (require 'cl-lib)
-
-  ;; Simple setf usage for plist-get.
-  (should (equal (let ((target '(:a "a" :b "b" :c "c")))
-                   (setf (plist-get target :b) "modify")
-                   target)
-                 '(:a "a" :b "modify" :c "c")))
-
-  ;; Other function (cl-rotatef) usage for plist-get.
-  (should (equal (let ((target '(:a "a" :b "b" :c "c")))
-                   (cl-rotatef (plist-get target :b) (plist-get target :c))
-                   target)
-                 '(:a "a" :b "c" :c "b")))
-
-  ;; Add new key value pair at top of list if setf for missing key.
-  (should (equal (let ((target '(:a "a" :b "b" :c "c")))
-                   (setf (plist-get target :d) "modify")
-                   target)
-                 '(:d "modify" :a "a" :b "b" :c "c")))
+  ;; Simple `setf' usage for `plist-get'.
+  (let ((target (list :a "a" :b "b" :c "c")))
+    (setf (plist-get target :b) "modify")
+    (should (equal target '(:a "a" :b "modify" :c "c")))
+    (setf (plist-get target ":a" #'string=) "mogrify")
+    (should (equal target '(:a "mogrify" :b "modify" :c "c"))))
+
+  ;; Other function (`cl-rotatef') usage for `plist-get'.
+  (let ((target (list :a "a" :b "b" :c "c")))
+    (cl-rotatef (plist-get target :b) (plist-get target :c))
+    (should (equal target '(:a "a" :b "c" :c "b")))
+    (cl-rotatef (plist-get target ":a" #'string=)
+                (plist-get target ":b" #'string=))
+    (should (equal target '(:a "c" :b "a" :c "b"))))
+
+  ;; Add new key value pair at top of list if `setf' for missing key.
+  (let ((target (list :a "a" :b "b" :c "c")))
+    (setf (plist-get target :d) "modify")
+    (should (equal target '(:d "modify" :a "a" :b "b" :c "c")))
+    (setf (plist-get target :e #'string=) "mogrify")
+    (should (equal target '(:e "mogrify" :d "modify" :a "a" :b "b" :c "c"))))
 
   ;; Rotate with missing value.
   ;; The value corresponding to the missing key is assumed to be nil.
-  (should (equal (let ((target '(:a "a" :b "b" :c "c")))
-                   (cl-rotatef (plist-get target :b) (plist-get target :d))
-                   target)
-                 '(:d "b" :a "a" :b nil :c "c")))
-
-  ;; Simple setf usage for plist-get. (symbol plist)
-  (should (equal (let ((target '(a "a" b "b" c "c")))
-                   (setf (plist-get target 'b) "modify")
-                   target)
-                 '(a "a" b "modify" c "c")))
-
-  ;; Other function (cl-rotatef) usage for plist-get. (symbol plist)
-  (should (equal (let ((target '(a "a" b "b" c "c")))
-                   (cl-rotatef (plist-get target 'b) (plist-get target 'c))
-                   target)
-                 '(a "a" b "c" c "b"))))
-
-;; `ert-deftest' messes up macroexpansion when the test file itself is
-;; compiled (see Bug #24402).
-
-;; Local Variables:
-;; no-byte-compile: t
-;; End:
+  (let ((target (list :a "a" :b "b" :c "c")))
+    (cl-rotatef (plist-get target :b) (plist-get target :d))
+    (should (equal target '(:d "b" :a "a" :b nil :c "c")))
+    (cl-rotatef (plist-get target ":e" #'string=)
+                (plist-get target ":d" #'string=))
+    (should (equal target '(":e" "b" :d nil :a "a" :b nil :c "c")))))
 
 ;;; gv-tests.el ends here
diff --git a/test/lisp/emacs-lisp/hierarchy-tests.el 
b/test/lisp/emacs-lisp/hierarchy-tests.el
index 41d3f2f3cc..97a0f7ba52 100644
--- a/test/lisp/emacs-lisp/hierarchy-tests.el
+++ b/test/lisp/emacs-lisp/hierarchy-tests.el
@@ -552,5 +552,148 @@
     (hierarchy-sort organisms)
     (should (equal (hierarchy-roots organisms) '(animal plant)))))
 
+(defun hierarchy-examples-delayed--find-number (num)
+  "Find a number, NUM, by adding 1s together until you reach it.
+This is entire contrived and mostly meant to be purposefully inefficient to
+not be possible on a large scale.
+Running the number 200 causes this function to crash; running this function in
+`hierarchy-add-tree' with a root of 80 and no delayed children causes that to
+ crash.
+If generating hierarchy children is not delayed, tests for that functionality
+should fail as this function will crash."
+
+  (funcall (lambda (funct) (funcall funct 1 funct))
+           (lambda (n funct)
+             (if (< n num)
+                 (+ 1 (funcall funct (+ 1 n) funct))
+               1))))
+
+(defun hierarchy-examples-delayed--childrenfn (hier-elem)
+  "Return the children of HIER-ELEM.
+Basially, feed the number, minus 1, to 
`hierarchy-examples-delayed--find-number'
+and then create a list of the number plus 0.0–0.9."
+
+  (when (> hier-elem 1)
+    (let ((next (hierarchy-examples-delayed--find-number (1- hier-elem))))
+      (mapcar (lambda (dec) (+ next dec)) '(.0 .1 .2 .3 .4 .5 .6 .7 .8 .9)))))
+
+(ert-deftest hierarchy-delayed-add-one-root ()
+  (let ((parentfn (lambda (_) nil))
+        (hierarchy (hierarchy-new)))
+    (hierarchy-add-tree hierarchy 190 parentfn
+                        #'hierarchy-examples-delayed--childrenfn nil t)
+    (should (equal (hierarchy-roots hierarchy) '(190)))))
+
+(ert-deftest hierarchy-delayed-add-one-item-with-parent ()
+  (let ((parentfn (lambda (item)
+                    (cl-case item
+                      (190 191))))
+        (hierarchy (hierarchy-new)))
+    (hierarchy-add-tree hierarchy 190 parentfn
+                        #'hierarchy-examples-delayed--childrenfn nil t)
+    (should (equal (hierarchy-roots hierarchy) '(191)))
+    (should (equal (hierarchy-children hierarchy 191) '(190)))
+    (should (equal (hierarchy-children hierarchy 190) '()))))
+
+(ert-deftest hierarchy-delayed-add-one-item-with-parent-and-grand-parent ()
+  (let ((parentfn (lambda (item)
+                    (cl-case item
+                      (190 191)
+                      (191 192))))
+        (hierarchy (hierarchy-new)))
+    (hierarchy-add-tree hierarchy 190 parentfn
+                        #'hierarchy-examples-delayed--childrenfn nil t)
+    (should (equal (hierarchy-roots hierarchy) '(192)))
+    (should (equal (hierarchy-children hierarchy 192) '(191)))
+    (should (equal (hierarchy-children hierarchy 191) '(190)))
+    (should (equal (hierarchy-children hierarchy 190) '()))))
+
+(ert-deftest hierarchy-delayed-add-same-root-twice ()
+  (let ((parentfn (lambda (_) nil))
+        (hierarchy (hierarchy-new)))
+    (hierarchy-add-tree hierarchy 190 parentfn
+                        #'hierarchy-examples-delayed--childrenfn nil t)
+    (hierarchy-add-tree hierarchy 190 parentfn
+                        #'hierarchy-examples-delayed--childrenfn nil t)
+    (should (equal (hierarchy-roots hierarchy) '(190)))))
+
+(ert-deftest hierarchy-delayed-add-same-child-twice ()
+  (let ((parentfn (lambda (item)
+                    (cl-case item
+                      (190 191))))
+        (hierarchy (hierarchy-new)))
+    (hierarchy-add-tree hierarchy 190 parentfn
+                        #'hierarchy-examples-delayed--childrenfn nil t)
+    (hierarchy-add-tree hierarchy 190 parentfn
+                        #'hierarchy-examples-delayed--childrenfn nil t)
+    (should (equal (hierarchy-roots hierarchy) '(191)))
+    (should (equal (hierarchy-children hierarchy 191) '(190)))
+    (should (equal (hierarchy-children hierarchy 190) '()))))
+
+(ert-deftest hierarchy-delayed-add-item-and-its-parent ()
+  (let ((parentfn (lambda (item)
+                    (cl-case item
+                      (190 191))))
+        (hierarchy (hierarchy-new)))
+    (hierarchy-add-tree hierarchy 190 parentfn
+                        #'hierarchy-examples-delayed--childrenfn nil t)
+    (hierarchy-add-tree hierarchy 191 parentfn
+                        #'hierarchy-examples-delayed--childrenfn nil t)
+    (should (equal (hierarchy-roots hierarchy) '(191)))
+    (should (equal (hierarchy-children hierarchy 191) '(190)))
+    (should (equal (hierarchy-children hierarchy 190) '()))))
+
+(ert-deftest hierarchy-delayed-add-item-and-its-child ()
+  (let ((parentfn (lambda (item)
+                    (cl-case item
+                      (190 191))))
+        (hierarchy (hierarchy-new)))
+    (hierarchy-add-tree hierarchy 191 parentfn
+                        #'hierarchy-examples-delayed--childrenfn nil t)
+    (hierarchy-add-tree hierarchy 190 parentfn
+                        #'hierarchy-examples-delayed--childrenfn nil t)
+    (should (equal (hierarchy-roots hierarchy) '(191)))
+    (should (equal (hierarchy-children hierarchy 191) '(190)))
+    (should (equal (hierarchy-children hierarchy 190) '()))))
+
+(ert-deftest hierarchy-delayed-add-two-items-sharing-parent ()
+  (let ((parentfn (lambda (item)
+                    (cl-case item
+                      (190 191)
+                      (190.5 191))))
+        (hierarchy (hierarchy-new)))
+    (hierarchy-add-tree hierarchy 190 parentfn
+                        #'hierarchy-examples-delayed--childrenfn nil t)
+    (hierarchy-add-tree hierarchy 190.5 parentfn
+                        #'hierarchy-examples-delayed--childrenfn nil t)
+    (should (equal (hierarchy-roots hierarchy) '(191)))
+    (should (equal (hierarchy-children hierarchy 191) '(190 190.5)))))
+
+(ert-deftest hierarchy-delayed-add-two-hierarchies ()
+  (let ((parentfn (lambda (item)
+                    (cl-case item
+                      (190 191)
+                      (circle 'shape))))
+        (hierarchy (hierarchy-new)))
+    (hierarchy-add-tree hierarchy 190 parentfn
+                        #'hierarchy-examples-delayed--childrenfn nil t)
+    (hierarchy-add-tree hierarchy 'circle parentfn)
+    (should (equal (hierarchy-roots hierarchy) '(191 shape)))
+    (should (equal (hierarchy-children hierarchy 191) '(190)))
+    (should (equal (hierarchy-children hierarchy 'shape) '(circle)))))
+
+(ert-deftest hierarchy-delayed-add-trees ()
+  (let ((parentfn (lambda (item)
+                    (cl-case item
+                      (190 '191)
+                      (190.5 '191)
+                      (191 '192))))
+        (hierarchy (hierarchy-new)))
+    (hierarchy-add-trees hierarchy '(190 190.5) parentfn
+                         #'hierarchy-examples-delayed--childrenfn nil t)
+    (should (equal (hierarchy-roots hierarchy) '(192)))
+    (should (equal (hierarchy-children hierarchy '192) '(191)))
+    (should (equal (hierarchy-children hierarchy '191) '(190 190.5)))))
+
 (provide 'hierarchy-tests)
 ;;; hierarchy-tests.el ends here
diff --git a/test/lisp/emacs-lisp/map-tests.el 
b/test/lisp/emacs-lisp/map-tests.el
index 314a1c9e30..75ebe59431 100644
--- a/test/lisp/emacs-lisp/map-tests.el
+++ b/test/lisp/emacs-lisp/map-tests.el
@@ -29,10 +29,13 @@
 (require 'ert)
 (require 'map)
 
+(eval-when-compile
+  (require 'cl-lib))
+
 (defmacro with-maps-do (var &rest body)
   "Successively bind VAR to an alist, plist, vector, and hash-table.
 Each map is built from the following alist data:
-  \\='((0 . 3) (1 . 4) (2 . 5)).
+  ((0 . 3) (1 . 4) (2 . 5))
 Evaluate BODY for each created map."
   (declare (indent 1) (debug (symbolp body)))
   (let ((alist (make-symbol "alist"))
@@ -84,18 +87,96 @@ Evaluate BODY for each created map."
   (with-empty-maps-do map
     (should (= 5 (map-elt map 0 5)))))
 
-(ert-deftest test-map-elt-testfn ()
+(ert-deftest test-map-elt-testfn-alist ()
+  "Test the default alist predicate of `map-elt'."
   (let* ((a (string ?a))
          (map `((,a . 0) (,(string ?b) . 1))))
-    (should (= (map-elt map a) 0))
-    (should (= (map-elt map "a") 0))
-    (should (= (map-elt map (string ?a)) 0))
-    (should (= (map-elt map "b") 1))
-    (should (= (map-elt map (string ?b)) 1))))
+    (should (= 0 (map-elt map a)))
+    (should (= 0 (map-elt map "a")))
+    (should (= 0 (map-elt map (string ?a))))
+    (should (= 1 (map-elt map "b")))
+    (should (= 1 (map-elt map (string ?b))))
+    (with-suppressed-warnings ((callargs map-elt))
+      (should (= 0 (map-elt map 'a nil #'string=)))
+      (should (= 1 (map-elt map 'b nil #'string=))))))
+
+(ert-deftest test-map-elt-testfn-plist ()
+  "Test the default plist predicate of `map-elt'."
+  (let* ((a (string ?a))
+         (map `(,a 0 "b" 1)))
+    (should-not (map-elt map "a"))
+    (should-not (map-elt map "b"))
+    (should-not (map-elt map (string ?a)))
+    (should-not (map-elt map (string ?b)))
+    (should (= 0 (map-elt map a)))
+    (with-suppressed-warnings ((callargs map-elt))
+      (should (= 0 (map-elt map a nil #'equal)))
+      (should (= 0 (map-elt map "a" nil #'equal)))
+      (should (= 0 (map-elt map (string ?a) nil #'equal)))
+      (should (= 1 (map-elt map "b" nil #'equal)))
+      (should (= 1 (map-elt map (string ?b) nil #'equal))))))
+
+(ert-deftest test-map-elt-gv ()
+  "Test the generalized variable `map-elt'."
+  (let ((sort (lambda (map) (sort (map-pairs map) #'car-less-than-car))))
+    (with-empty-maps-do map
+      ;; Empty map, without default.
+      (should-error (cl-incf (map-elt map 1)) :type 'wrong-type-argument)
+      (with-suppressed-warnings ((callargs map-elt))
+        (should-error (cl-incf (map-elt map 1.0 nil #'=))
+                      :type 'wrong-type-argument))
+      (should (map-empty-p map))
+      ;; Empty map, with default.
+      (if (vectorp map)
+          (progn
+            (should-error (cl-incf (map-elt map 1 3)) :type 'args-out-of-range)
+            (with-suppressed-warnings ((callargs map-elt))
+              (should-error (cl-incf (map-elt map 1 3 #'=))
+                            :type 'args-out-of-range))
+            (should (map-empty-p map)))
+        (should (= (cl-incf (map-elt map 1 3) 10) 13))
+        (with-suppressed-warnings ((callargs map-elt))
+          (should (= (cl-incf (map-elt map 2.0 5 #'=) 12) 17)))
+        (should (equal (funcall sort map) '((1 . 13) (2.0 . 17))))))
+    (with-maps-do map
+      ;; Nonempty map, without predicate.
+      (should (= (cl-incf (map-elt map 1 3) 10) 14))
+      (should (equal (funcall sort map) '((0 . 3) (1 . 14) (2 . 5))))
+      ;; Nonempty map, with predicate.
+      (with-suppressed-warnings ((callargs map-elt))
+        (pcase-exhaustive map
+          ((pred consp)
+           (should (= (cl-incf (map-elt map 2.0 6 #'=) 12) 17))
+           (should (equal (funcall sort map) '((0 . 3) (1 . 14) (2 . 17))))
+           (should (= (cl-incf (map-elt map 0 7 #'=) 13) 16))
+           (should (equal (funcall sort map) '((0 . 16) (1 . 14) (2 . 17)))))
+          ((pred vectorp)
+           (should-error (cl-incf (map-elt map 2.0 6 #'=))
+                         :type 'wrong-type-argument)
+           (should (equal (funcall sort map) '((0 . 3) (1 . 14) (2 . 5))))
+           (should (= (cl-incf (map-elt map 2 6 #'=) 12) 17))
+           (should (equal (funcall sort map) '((0 . 3) (1 . 14) (2 . 17))))
+           (should (= (cl-incf (map-elt map 0 7 #'=) 13) 16))
+           (should (equal (funcall sort map) '((0 . 16) (1 . 14) (2 . 17)))))
+          ((pred hash-table-p)
+           (should (= (cl-incf (map-elt map 2.0 6 #'=) 12) 18))
+           (should (member (funcall sort map)
+                           '(((0 . 3) (1 . 14) (2 . 5) (2.0 . 18))
+                             ((0 . 3) (1 . 14) (2.0 . 18) (2 . 5)))))
+           (should (= (cl-incf (map-elt map 0 7 #'=) 13) 16))
+           (should (member (funcall sort map)
+                           '(((0 . 16) (1 . 14) (2 . 5) (2.0 . 18))
+                             ((0 . 16) (1 . 14) (2.0 . 18) (2 . 5)))))))))))
 
 (ert-deftest test-map-elt-with-nil-value ()
   (should-not (map-elt '((a . 1) (b)) 'b 2)))
 
+(ert-deftest test-map-elt-signature ()
+  "Test that `map-elt' has the right advertised signature.
+See bug#58531#25 and bug#58563."
+  (should (equal (get-advertised-calling-convention (symbol-function 'map-elt))
+                 '(map key &optional default))))
+
 (ert-deftest test-map-put! ()
   (with-maps-do map
     (setf (map-elt map 2) 'hello)
@@ -144,6 +225,24 @@ Evaluate BODY for each created map."
     (should (equal map '(("a" . 1))))
     (should-error (map-put! map (string ?a) val #'eq) :type 'map-not-inplace)))
 
+(ert-deftest test-map-put!-plist ()
+  "Test `map-put!' predicate on plists."
+  (let* ((a (string ?a))
+         (map (list a 0)))
+    (map-put! map a -1)
+    (should (equal map '("a" -1)))
+    (map-put! map 'a 2)
+    (should (equal map '("a" -1 a 2)))
+    (with-suppressed-warnings ((callargs map-put!))
+      (map-put! map 'a -3 #'string=))
+    (should (equal map '("a" -3 a 2)))))
+
+(ert-deftest test-map-put!-signature ()
+  "Test that `map-put!' has the right advertised signature.
+See bug#58531#25 and bug#58563."
+  (should (equal (get-advertised-calling-convention (symbol-function 
'map-put!))
+                 '(map key value))))
+
 (ert-deftest test-map-put-alist-new-key ()
   "Regression test for Bug#23105."
   (let ((alist (list (cons 0 'a))))
@@ -395,13 +494,23 @@ Evaluate BODY for each created map."
         (alist '(("a" . 1) (a . 2))))
     (should (map-contains-key alist 'a))
     (should (map-contains-key plist 'a))
+    ;; FIXME: Why is no warning emitted for these (bug#58563#13)?
     (should (map-contains-key alist 'a #'eq))
     (should (map-contains-key plist 'a #'eq))
     (should (map-contains-key alist key))
+    (should (map-contains-key alist "a"))
+    (should (map-contains-key plist (string ?a) #'equal))
     (should-not (map-contains-key plist key))
     (should-not (map-contains-key alist key #'eq))
     (should-not (map-contains-key plist key #'eq))))
 
+(ert-deftest test-map-contains-key-signature ()
+  "Test that `map-contains-key' has the right advertised signature.
+See bug#58531#25 and bug#58563."
+  (should (equal (get-advertised-calling-convention
+                  (symbol-function 'map-contains-key))
+                 '(map key))))
+
 (ert-deftest test-map-some ()
   (with-maps-do map
     (should (eq (map-some (lambda (k _v) (and (= k 1) 'found)) map)
@@ -515,19 +624,19 @@ Evaluate BODY for each created map."
     (should (equal alist '((key . value))))))
 
 (ert-deftest test-map-setf-alist-overwrite-key ()
-  (let ((alist '((key . value1))))
+  (let ((alist (list (cons 'key 'value1))))
     (should (equal (setf (map-elt alist 'key) 'value2)
                    'value2))
     (should (equal alist '((key . value2))))))
 
 (ert-deftest test-map-setf-plist-insert-key ()
-  (let ((plist '(key value)))
+  (let ((plist (list 'key 'value)))
     (should (equal (setf (map-elt plist 'key2) 'value2)
                    'value2))
     (should (equal plist '(key value key2 value2)))))
 
 (ert-deftest test-map-setf-plist-overwrite-key ()
-  (let ((plist '(key value)))
+  (let ((plist (list 'key 'value)))
     (should (equal (setf (map-elt plist 'key) 'value2)
                    'value2))
     (should (equal plist '(key value2)))))
@@ -535,14 +644,14 @@ Evaluate BODY for each created map."
 (ert-deftest test-hash-table-setf-insert-key ()
   (let ((ht (make-hash-table)))
     (should (equal (setf (map-elt ht 'key) 'value)
-                  'value))
+                   'value))
     (should (equal (map-elt ht 'key) 'value))))
 
 (ert-deftest test-hash-table-setf-overwrite-key ()
   (let ((ht (make-hash-table)))
     (puthash 'key 'value1 ht)
     (should (equal (setf (map-elt ht 'key) 'value2)
-                  'value2))
+                   'value2))
     (should (equal (map-elt ht 'key) 'value2))))
 
 (ert-deftest test-setf-map-with-function ()
@@ -551,8 +660,79 @@ Evaluate BODY for each created map."
     (setf (map-elt map 'foo)
           (funcall (lambda ()
                      (cl-incf num))))
+    (should (equal map '((foo . 1))))
     ;; Check that the function is only called once.
     (should (= num 1))))
 
+(ert-deftest test-map-plist-member ()
+  "Test `map--plist-member' and `map--plist-member-1'."
+  (dolist (mem '(map--plist-member map--plist-member-1))
+    ;; Lambda exercises Lisp implementation.
+    (dolist (= `(nil ,(lambda (a b) (eq a b))))
+      (should-not (funcall mem () 'a =))
+      (should-not (funcall mem '(a) 'b =))
+      (should-not (funcall mem '(a 1) 'b =))
+      (should (equal (funcall mem '(a) 'a =) '(a)))
+      (should (equal (funcall mem '(a . 1) 'a =) '(a . 1)))
+      (should (equal (funcall mem '(a 1 . b) 'a =) '(a 1 . b)))
+      (should (equal (funcall mem '(a 1 b) 'a =) '(a 1 b)))
+      (should (equal (funcall mem '(a 1 b) 'b =) '(b)))
+      (should (equal (funcall mem '(a 1 b . 2) 'a =) '(a 1 b . 2)))
+      (should (equal (funcall mem '(a 1 b . 2) 'b =) '(b . 2)))
+      (should (equal (funcall mem '(a 1 b 2) 'a =) '(a 1 b 2)))
+      (should (equal (funcall mem '(a 1 b 2) 'b =) '(b 2)))
+      (should (equal (should-error (funcall mem '(a . 1) 'b =))
+                     '(wrong-type-argument plistp (a . 1))))
+      (should (equal (should-error (funcall mem '(a 1 . b) 'b =))
+                     '(wrong-type-argument plistp (a 1 . b)))))
+    (should (equal (funcall mem '(a 1 b 2) "a" #'string=) '(a 1 b 2)))
+    (should (equal (funcall mem '(a 1 b 2) "b" #'string=) '(b 2)))))
+
+(ert-deftest test-map-plist-put ()
+  "Test `map--plist-put' and `map--plist-put-1'."
+  (dolist (put '(map--plist-put map--plist-put-1))
+    ;; Lambda exercises Lisp implementation.
+    (dolist (= `(nil ,(lambda (a b) (eq a b))))
+      (let ((l ()))
+        (should (equal (funcall put l 'a 1 =) '(a 1)))
+        (should-not l))
+      (let ((l (list 'a)))
+        (dolist (key '(a b))
+          (should (equal (should-error (funcall put l key 1 =))
+                         '(wrong-type-argument plistp (a)))))
+        (should (equal l '(a))))
+      (let ((l (cons 'a 1)))
+        (dolist (key '(a b))
+          (should (equal (should-error (funcall put l key 1 =))
+                         '(wrong-type-argument plistp (a . 1)))))
+        (should (equal l '(a . 1))))
+      (let ((l (cons 'a (cons 1 'b))))
+        (should (equal (funcall put l 'a 2 =) '(a 2 . b)))
+        (dolist (key '(b c))
+          (should (equal (should-error (funcall put l key 3 =))
+                         '(wrong-type-argument plistp (a 2 . b)))))
+        (should (equal l '(a 2 . b))))
+      (let ((l (list 'a 1 'b)))
+        (should (equal (funcall put l 'a 2 =) '(a 2 b)))
+        (dolist (key '(b c))
+          (should (equal (should-error (funcall put l key 3 =))
+                         '(wrong-type-argument plistp (a 2 b)))))
+        (should (equal l '(a 2 b))))
+      (let ((l (cons 'a (cons 1 (cons 'b 2)))))
+        (should (equal (funcall put l 'a 3 =) '(a 3 b . 2)))
+        (dolist (key '(b c))
+          (should (equal (should-error (funcall put l key 4 =))
+                         '(wrong-type-argument plistp (a 3 b . 2)))))
+        (should (equal l '(a 3 b . 2))))
+      (let ((l (list 'a 1 'b 2)))
+        (should (equal (funcall put l 'a 3 =) '(a 3 b 2)))
+        (should (equal (funcall put l 'b 4 =) '(a 3 b 4)))
+        (should (equal (funcall put l 'c 5 =) '(a 3 b 4 c 5)))
+        (should (equal l '(a 3 b 4 c 5)))))
+    (let ((l (list 'a 1 'b 2)))
+      (should (equal (funcall put l "a" 3 #'string=) '(a 3 b 2)))
+      (should (equal (funcall put l "b" 4 #'string=) '(a 3 b 4)))
+      (should (equal (funcall put l "c" 5 #'string=) '(a 3 b 4 "c" 5))))))
+
 (provide 'map-tests)
 ;;; map-tests.el ends here
diff --git a/test/lisp/erc/erc-dcc-tests.el b/test/lisp/erc/erc-dcc-tests.el
index a1dfbab9dc..8645d7f104 100644
--- a/test/lisp/erc/erc-dcc-tests.el
+++ b/test/lisp/erc/erc-dcc-tests.el
@@ -20,8 +20,9 @@
 ;;; Commentary:
 
 ;;; Code:
-(require 'ert)
+(require 'ert-x)
 (require 'erc-dcc)
+(require 'erc-pcomplete)
 
 (ert-deftest erc-dcc-ctcp-query-send-regexp ()
   (let ((s "DCC SEND \"file name\" 2130706433 9899 1405135128"))
@@ -164,4 +165,120 @@
           (should (eq t (plist-get (car erc-dcc-list) :turbo)))
           (should (equal (pop calls) (list elt "foo.bin" proc))))))))
 
+(defun erc-dcc-tests--pcomplete-common (test-fn)
+  (with-current-buffer (get-buffer-create "*erc-dcc-do-GET-command*")
+    (let* ((proc (start-process "fake" (current-buffer) "sleep" "10"))
+           (elt (list :nick "tester!~tester@fake.irc"
+                      :type 'GET
+                      :peer nil
+                      :parent proc
+                      :ip "127.0.0.1"
+                      :port "9899"
+                      :file "foo.bin"
+                      :size 1405135128))
+           ;;
+           erc-accidental-paste-threshold-seconds
+           erc-insert-modify-hook erc-send-completed-hook
+           erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook)
+      (erc-mode)
+      (pcomplete-erc-setup)
+      (add-hook 'erc-complete-functions #'erc-pcompletions-at-point 0 t)
+      (setq erc-server-process proc
+            erc-input-marker (make-marker)
+            erc-insert-marker (make-marker)
+            erc-server-current-nick "dummy")
+      (setq-local erc-dcc-list (list elt)) ; for interactive noodling
+      (set-process-query-on-exit-flag proc nil)
+      (goto-char (point-max))
+      (set-marker erc-insert-marker (point-max))
+      (erc-display-prompt)
+      (goto-char erc-input-marker)
+      (funcall test-fn))
+    (when noninteractive
+      (kill-buffer))))
+
+(ert-deftest pcomplete/erc-mode/DCC--get-basic ()
+  (erc-dcc-tests--pcomplete-common
+   (lambda ()
+     (insert "/dcc get ")
+     (call-interactively #'completion-at-point)
+     (save-excursion
+       (beginning-of-line)
+       (should (search-forward "/dcc get tester" nil t)))
+     (call-interactively #'completion-at-point)
+     (save-excursion
+       (beginning-of-line)
+       (should (search-forward "/dcc get tester foo.bin" nil t))))))
+
+(ert-deftest pcomplete/erc-mode/DCC--get-1flag ()
+  (erc-dcc-tests--pcomplete-common
+   (lambda ()
+     (goto-char erc-input-marker)
+     (delete-region (point) (point-max))
+     (insert "/dcc get -")
+     (call-interactively #'completion-at-point)
+     (with-current-buffer (get-buffer "*Completions*")
+       (goto-char (point-min))
+       (search-forward "-s")
+       (search-forward "-t"))
+     (insert "s ")
+     (call-interactively #'completion-at-point)
+     (save-excursion
+       (beginning-of-line)
+       (should (search-forward "/dcc get -s tester" nil t)))
+     (call-interactively #'completion-at-point)
+     (save-excursion
+       (beginning-of-line)
+       (should (search-forward "/dcc get -s tester foo.bin" nil t))))))
+
+(ert-deftest pcomplete/erc-mode/DCC--get-2flags ()
+  (erc-dcc-tests--pcomplete-common
+   (lambda ()
+     (goto-char erc-input-marker)
+     (delete-region (point) (point-max))
+     (insert "/dcc get -")
+     (call-interactively #'completion-at-point)
+     (with-current-buffer (get-buffer "*Completions*")
+       (goto-char (point-min))
+       (search-forward "-s")
+       (search-forward "-t"))
+     (insert "s -")
+     (call-interactively #'completion-at-point)
+     (save-excursion
+       (beginning-of-line)
+       (should (search-forward "/dcc get -s -t " nil t)))
+     (call-interactively #'completion-at-point)
+     (save-excursion
+       (beginning-of-line)
+       (should (search-forward "/dcc get -s -t tester" nil t)))
+     (call-interactively #'completion-at-point)
+     (save-excursion
+       (beginning-of-line)
+       (should (search-forward "/dcc get -s -t tester foo.bin" nil t))))))
+
+(ert-deftest pcomplete/erc-mode/DCC--get-2flags-reverse ()
+  (erc-dcc-tests--pcomplete-common
+   (lambda ()
+     (goto-char erc-input-marker)
+     (delete-region (point) (point-max))
+     (insert "/dcc get -")
+     (call-interactively #'completion-at-point)
+     (with-current-buffer (get-buffer "*Completions*")
+       (goto-char (point-min))
+       (search-forward "-s")
+       (search-forward "-t"))
+     (insert "t -")
+     (call-interactively #'completion-at-point)
+     (save-excursion
+       (beginning-of-line)
+       (should (search-forward "/dcc get -t -s " nil t)))
+     (call-interactively #'completion-at-point)
+     (save-excursion
+       (beginning-of-line)
+       (should (search-forward "/dcc get -t -s tester" nil t)))
+     (call-interactively #'completion-at-point)
+     (save-excursion
+       (beginning-of-line)
+       (should (search-forward "/dcc get -t -s tester foo.bin" nil t))))))
+
 ;;; erc-dcc-tests.el ends here
diff --git a/test/lisp/erc/erc-networks-tests.el 
b/test/lisp/erc/erc-networks-tests.el
index 66a334b709..32bdfa11ff 100644
--- a/test/lisp/erc/erc-networks-tests.el
+++ b/test/lisp/erc/erc-networks-tests.el
@@ -20,7 +20,7 @@
 ;;; Code:
 
 (require 'ert-x) ; cl-lib
-(require 'erc-networks)
+(require 'erc)
 
 (defun erc-networks-tests--create-dead-proc (&optional buf)
   (let ((p (start-process "true" (or buf (current-buffer)) "true")))
diff --git a/test/lisp/erc/erc-services-tests.el 
b/test/lisp/erc/erc-services-tests.el
index 8e2b8d2927..c22d4cf75e 100644
--- a/test/lisp/erc/erc-services-tests.el
+++ b/test/lisp/erc/erc-services-tests.el
@@ -469,12 +469,9 @@
     (list (assoc 'secret (cdr found)))))
 
 (defvar erc-join-tests--auth-source-pass-entries
-  '(("irc.gnu.org:irc/#chan"
-     ("port" . "irc") ("user" . "#chan") (secret . "bar"))
-    ("my.gnu.org:irc/#chan"
-     ("port" . "irc") ("user" . "#chan") (secret . "baz"))
-    ("GNU.chat:irc/#chan"
-     ("port" . "irc") ("user" . "#chan") (secret . "foo"))))
+  '(("irc.gnu.org:irc/#chan" (secret . "bar"))
+    ("my.gnu.org:irc/#chan" (secret . "baz"))
+    ("GNU.chat:irc/#chan" (secret . "foo"))))
 
 (ert-deftest erc--auth-source-search--pass-standard ()
   (ert-skip "Pass backend not yet supported")
@@ -506,16 +503,11 @@
   (ert-skip "Pass backend not yet supported")
   (let ((store
          `(,@erc-join-tests--auth-source-pass-entries
-           ("GNU.chat:6697/#chan"
-            ("port" . "6697") ("user" . "#chan") (secret . "spam"))
-           ("my.gnu.org:irc/#fsf"
-            ("port" . "irc") ("user" . "#fsf") (secret . "42"))
-           ("irc.gnu.org:6667"
-            ("port" . "6667") (secret . "sesame"))
-           ("MyHost:irc"
-            ("port" . "irc") (secret . "456"))
-           ("MyHost:6667"
-            ("port" . "6667") (secret . "123"))))
+           ("GNU.chat:6697/#chan" (secret . "spam"))
+           ("my.gnu.org:irc/#fsf" (secret . "42"))
+           ("irc.gnu.org:6667" (secret . "sesame"))
+           ("MyHost:irc" (secret . "456"))
+           ("MyHost:6667" (secret . "123"))))
         (auth-sources '(password-store))
         (auth-source-do-cache nil))
 
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index b2ed29e80e..c88dd9888d 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -24,7 +24,6 @@
 (require 'ert-x)
 (require 'erc)
 (require 'erc-ring)
-(require 'erc-networks)
 
 (ert-deftest erc--read-time-period ()
   (cl-letf (((symbol-function 'read-string) (lambda (&rest _) "")))
@@ -48,27 +47,6 @@
   (cl-letf (((symbol-function 'read-string) (lambda (&rest _) "1d")))
     (should (equal (erc--read-time-period "foo: ") 86400))))
 
-(ert-deftest erc--meta--backend-dependencies ()
-  (with-temp-buffer
-    (insert-file-contents-literally
-     (concat (file-name-sans-extension (symbol-file 'erc)) ".el"))
-    (let ((beg (search-forward ";; Defined in erc-backend"))
-          (end (search-forward "\n\n"))
-          vars)
-      (save-excursion
-        (save-restriction
-          (narrow-to-region beg end)
-          (goto-char (point-min))
-          (with-syntax-table lisp-data-mode-syntax-table
-            (condition-case _
-                (while (push (cadr (read (current-buffer))) vars))
-              (end-of-file)))))
-      (should (= (point) end))
-      (dolist (var vars)
-        (setq var (concat "\\_<" (symbol-name var) "\\_>"))
-        (ert-info (var)
-          (should (save-excursion (search-forward-regexp var nil t))))))))
-
 (ert-deftest erc-with-all-buffers-of-server ()
   (let (proc-exnet
         proc-onet
diff --git a/test/lisp/eshell/em-tramp-tests.el 
b/test/lisp/eshell/em-tramp-tests.el
index 8969c1e229..6cc35ecdb1 100644
--- a/test/lisp/eshell/em-tramp-tests.el
+++ b/test/lisp/eshell/em-tramp-tests.el
@@ -85,4 +85,79 @@
              `(,(format "/sudo:USER@%s:%s" tramp-default-host 
default-directory)
                ("echo" ("-u" "hi")))))))
 
+(ert-deftest em-tramp-test/sudo-shell ()
+  "Test Eshell `sudo' command with -s/--shell option."
+  (dolist (args '(("--shell")
+                  ("-s")))
+    (should (equal
+             (catch 'eshell-replace-command (apply #'eshell/sudo args))
+             `(eshell-trap-errors
+               (eshell-named-command
+                "cd"
+                (list ,(format "/sudo:root@%s:%s"
+                               tramp-default-host default-directory))))))))
+
+(ert-deftest em-tramp-test/sudo-user-shell ()
+  "Test Eshell `sudo' command with -s and -u options."
+  (should (equal
+           (catch 'eshell-replace-command (eshell/sudo "-u" "USER" "-s"))
+           `(eshell-trap-errors
+             (eshell-named-command
+              "cd"
+              (list ,(format "/sudo:USER@%s:%s"
+                             tramp-default-host default-directory)))))))
+
+(ert-deftest em-tramp-test/doas-basic ()
+  "Test Eshell `doas' command with default user."
+  (cl-letf (((symbol-function 'eshell-named-command)
+             #'mock-eshell-named-command))
+    (should (equal
+             (catch 'eshell-external (eshell/doas "echo" "hi"))
+             `(,(format "/doas:root@%s:%s"
+                        tramp-default-host default-directory)
+               ("echo" ("hi")))))
+    (should (equal
+             (catch 'eshell-external (eshell/doas "echo" "-u" "hi"))
+             `(,(format "/doas:root@%s:%s"
+                        tramp-default-host default-directory)
+               ("echo" ("-u" "hi")))))))
+
+(ert-deftest em-tramp-test/doas-user ()
+  "Test Eshell `doas' command with specified user."
+  (cl-letf (((symbol-function 'eshell-named-command)
+             #'mock-eshell-named-command))
+    (should (equal
+             (catch 'eshell-external (eshell/doas "-u" "USER" "echo" "hi"))
+             `(,(format "/doas:USER@%s:%s"
+                        tramp-default-host default-directory)
+               ("echo" ("hi")))))
+    (should (equal
+             (catch 'eshell-external
+               (eshell/doas "-u" "USER" "echo" "-u" "hi"))
+             `(,(format "/doas:USER@%s:%s"
+                        tramp-default-host default-directory)
+               ("echo" ("-u" "hi")))))))
+
+(ert-deftest em-tramp-test/doas-shell ()
+  "Test Eshell `doas' command with -s/--shell option."
+  (dolist (args '(("--shell")
+                  ("-s")))
+    (should (equal
+             (catch 'eshell-replace-command (apply #'eshell/doas args))
+             `(eshell-trap-errors
+               (eshell-named-command
+                "cd"
+                (list ,(format "/doas:root@%s:%s"
+                               tramp-default-host default-directory))))))))
+
+(ert-deftest em-tramp-test/doas-user-shell ()
+  "Test Eshell `doas' command with -s and -u options."
+  (should (equal
+           (catch 'eshell-replace-command (eshell/doas "-u" "USER" "-s"))
+           `(eshell-trap-errors
+             (eshell-named-command
+              "cd"
+              (list ,(format "/doas:USER@%s:%s"
+                             tramp-default-host default-directory)))))))
+
 ;;; em-tramp-tests.el ends here
diff --git a/test/lisp/eshell/esh-ext-tests.el 
b/test/lisp/eshell/esh-ext-tests.el
new file mode 100644
index 0000000000..54191e9409
--- /dev/null
+++ b/test/lisp/eshell/esh-ext-tests.el
@@ -0,0 +1,76 @@
+;;; esh-ext-tests.el --- esh-ext test suite  -*- lexical-binding:t -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Tests for Eshell's external command handling.
+
+;;; Code:
+
+(require 'ert)
+(require 'esh-mode)
+(require 'esh-ext)
+(require 'eshell)
+
+(require 'eshell-tests-helpers
+         (expand-file-name "eshell-tests-helpers"
+                           (file-name-directory (or load-file-name
+                                                    default-directory))))
+
+;;; Tests:
+
+(ert-deftest esh-ext-test/addpath/end ()
+  "Test that \"addpath\" adds paths to the end of $PATH."
+  (with-temp-eshell
+   (let ((eshell-path-env-list '("/some/path" "/other/path"))
+         (expected-path (string-join '("/some/path" "/other/path" "/new/path"
+                                       "/new/path2")
+                                     (path-separator))))
+     (eshell-match-command-output "addpath /new/path /new/path2"
+                                  (concat expected-path "\n"))
+     (eshell-match-command-output "echo $PATH"
+                                  (concat expected-path "\n")))))
+
+(ert-deftest esh-ext-test/addpath/begin ()
+  "Test that \"addpath -b\" adds paths to the beginning of $PATH."
+  (with-temp-eshell
+   (let ((eshell-path-env-list '("/some/path" "/other/path"))
+         (expected-path (string-join '("/new/path" "/new/path2" "/some/path"
+                                       "/other/path")
+                                     (path-separator))))
+     (eshell-match-command-output "addpath -b /new/path /new/path2"
+                                  (concat expected-path "\n"))
+     (eshell-match-command-output "echo $PATH"
+                                  (concat expected-path "\n")))))
+
+(ert-deftest esh-ext-test/addpath/set-locally ()
+  "Test adding to the path temporarily in a subcommand."
+  (let* ((eshell-path-env-list '("/some/path" "/other/path"))
+         (original-path (string-join eshell-path-env-list (path-separator)))
+         (local-path (string-join (append eshell-path-env-list '("/new/path"))
+                                  (path-separator))))
+    (with-temp-eshell
+     (eshell-match-command-output
+      "{ addpath /new/path; env }"
+      (format "PATH=%s\n" (regexp-quote local-path)))
+     ;; After the last command, the previous $PATH value should be restored.
+     (eshell-match-command-output "echo $PATH"
+                                  (concat original-path "\n")))))
+
+;; esh-ext-tests.el ends here
diff --git a/test/lisp/eshell/esh-util-tests.el 
b/test/lisp/eshell/esh-util-tests.el
new file mode 100644
index 0000000000..1cbd015999
--- /dev/null
+++ b/test/lisp/eshell/esh-util-tests.el
@@ -0,0 +1,57 @@
+;;; esh-util-tests.el --- esh-util test suite  -*- lexical-binding:t -*-
+
+;; Copyright (C) 2022 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 'esh-util)
+
+;;; Tests:
+
+(ert-deftest esh-util-test/eshell-stringify/string ()
+  "Test that `eshell-stringify' preserves the value of strings."
+  (should (equal (eshell-stringify "hello") "hello")))
+
+(ert-deftest esh-util-test/eshell-stringify/number ()
+  "Test that `eshell-stringify' converts numbers to strings."
+  (should (equal (eshell-stringify 42) "42"))
+  (should (equal (eshell-stringify 4.2) "4.2")))
+
+(ert-deftest esh-util-test/eshell-stringify/t ()
+  "Test that `eshell-stringify' treats `t' according to `eshell-stringify-t'."
+  (let ((eshell-stringify-t t))
+    (should (equal (eshell-stringify t) "t")))
+  (let ((eshell-stringify-t nil))
+    (should (equal (eshell-stringify t) nil))))
+
+(ert-deftest esh-util-test/eshell-stringify/nil ()
+  "Test that `eshell-stringify' converts nil to a string."
+  (should (equal (eshell-stringify nil) "nil")))
+
+(ert-deftest esh-util-test/eshell-stringify/list ()
+  "Test that `eshell-stringify' correctly stringifies lists."
+  (should (equal (eshell-stringify '(1 2 3)) "(1 2 3)"))
+  (should (equal (eshell-stringify '((1 2) (3 . 4)))
+                 "((1 2)\n (3 . 4))")))
+
+(ert-deftest esh-util-test/eshell-stringify/complex ()
+  "Test that `eshell-stringify' correctly stringifies complex objects."
+  (should (equal (eshell-stringify (list 'quote 'hello)) "'hello")))
+
+;;; 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 cb5b1766bb..245a8e6a26 100644
--- a/test/lisp/eshell/esh-var-tests.el
+++ b/test/lisp/eshell/esh-var-tests.el
@@ -23,8 +23,10 @@
 
 ;;; Code:
 
+(require 'tramp)
 (require 'ert)
 (require 'esh-mode)
+(require 'esh-var)
 (require 'eshell)
 
 (require 'eshell-tests-helpers
@@ -440,6 +442,152 @@ inside double-quotes"
                                "000"))
 
 
+;; Variable-related commands
+
+(ert-deftest esh-var-test/set/env-var ()
+  "Test that `set' with a string variable name sets an environment variable."
+  (with-temp-eshell
+   (eshell-match-command-output "set VAR hello" "hello\n")
+   (should (equal (getenv "VAR") "hello")))
+  (should-not (equal (getenv "VAR") "hello")))
+
+(ert-deftest esh-var-test/set/symbol ()
+  "Test that `set' with a symbol variable name sets a Lisp variable."
+  (let (eshell-test-value)
+    (eshell-command-result-equal "set #'eshell-test-value hello"
+                                 "hello")
+    (should (equal eshell-test-value "hello"))))
+
+(ert-deftest esh-var-test/unset/env-var ()
+  "Test that `unset' with a string variable name unsets an env var."
+  (let ((process-environment (cons "VAR=value" process-environment)))
+    (with-temp-eshell
+     (eshell-match-command-output "unset VAR" "\\`\\'")
+     (should (equal (getenv "VAR") nil)))
+    (should (equal (getenv "VAR") "value"))))
+
+(ert-deftest esh-var-test/unset/symbol ()
+  "Test that `unset' with a symbol variable name unsets a Lisp variable."
+  (let ((eshell-test-value "value"))
+    (eshell-command-result-equal "unset #'eshell-test-value" nil)
+    (should (equal eshell-test-value nil))))
+
+(ert-deftest esh-var-test/setq ()
+  "Test that `setq' sets Lisp variables."
+  (let (eshell-test-value)
+    (eshell-command-result-equal "setq eshell-test-value hello"
+                                 "hello")
+    (should (equal eshell-test-value "hello"))))
+
+(ert-deftest esh-var-test/export ()
+  "Test that `export' sets environment variables."
+  (with-temp-eshell
+   (eshell-match-command-output "export VAR=hello" "\\`\\'")
+   (should (equal (getenv "VAR") "hello"))))
+
+(ert-deftest esh-var-test/local-variables ()
+  "Test that \"VAR=value command\" temporarily sets variables."
+  (with-temp-eshell
+   (push "VAR=value" process-environment)
+   (eshell-match-command-output "VAR=hello env" "VAR=hello\n")
+   (should (equal (getenv "VAR") "value"))))
+
+
+;; Variable aliases
+
+(ert-deftest esh-var-test/alias/function ()
+  "Test using a variable alias defined as a function."
+  (let ((text-quoting-style 'grave))
+    (with-temp-eshell
+     (push `("ALIAS" ,(lambda () "value") nil t) eshell-variable-aliases-list)
+     (eshell-match-command-output "echo $ALIAS" "value\n")
+     (eshell-match-command-output "set ALIAS hello"
+                                  "Variable `ALIAS' is not settable\n"
+                                  nil t))))
+
+(ert-deftest esh-var-test/alias/function-pair ()
+  "Test using a variable alias defined as a pair of getter/setter functions."
+  (with-temp-eshell
+   (let ((eshell-test-value "value"))
+     (push `("ALIAS" (,(lambda () eshell-test-value)
+                      . (lambda (_ value)
+                          (setq eshell-test-value (upcase value))))
+             nil t)
+           eshell-variable-aliases-list)
+     (eshell-match-command-output "echo $ALIAS" "value\n")
+     (eshell-match-command-output "set ALIAS hello" "HELLO\n")
+     (should (equal eshell-test-value "HELLO")))))
+
+(ert-deftest esh-var-test/alias/string ()
+  "Test using a variable alias defined as a string.
+This should get/set the aliased environment variable."
+  (with-temp-eshell
+   (let ((eshell-test-value "lisp-value"))
+     (push "eshell-test-value=env-value" process-environment)
+     (push `("ALIAS" "eshell-test-value") eshell-variable-aliases-list)
+     (eshell-match-command-output "echo $ALIAS" "env-value\n")
+     (eshell-match-command-output "set ALIAS hello" "hello\n")
+     (should (equal (getenv "eshell-test-value") "hello"))
+     (should (equal eshell-test-value "lisp-value")))))
+
+(ert-deftest esh-var-test/alias/string/prefer-lisp ()
+  "Test using a variable alias defined as a string.
+This sets `eshell-prefer-lisp-variables' to t and should get/set
+the aliased Lisp variable."
+  (with-temp-eshell
+   (let ((eshell-test-value "lisp-value")
+         (eshell-prefer-lisp-variables t))
+     (push "eshell-test-value=env-value" process-environment)
+     (push `("ALIAS" "eshell-test-value") eshell-variable-aliases-list)
+     (eshell-match-command-output "echo $ALIAS" "lisp-value\n")
+     (eshell-match-command-output "set ALIAS hello" "hello\n")
+     (should (equal (car process-environment) "eshell-test-value=env-value"))
+     (should (equal eshell-test-value "hello")))))
+
+(ert-deftest esh-var-test/alias/symbol ()
+  "Test using a variable alias defined as a symbol.
+This should get/set the value bound to the symbol."
+  (with-temp-eshell
+   (let ((eshell-test-value "value"))
+     (push '("ALIAS" eshell-test-value) eshell-variable-aliases-list)
+     (eshell-match-command-output "echo $ALIAS" "value\n")
+     (eshell-match-command-output "set ALIAS hello" "hello\n")
+     (should (equal eshell-test-value "hello")))))
+
+(ert-deftest esh-var-test/alias/symbol-pair ()
+  "Test using a variable alias defined as a pair of symbols.
+This should get the value bound to the symbol, but fail to set
+it, since the setter is nil."
+  (with-temp-eshell
+   (let ((eshell-test-value "value")
+         (text-quoting-style 'grave))
+     (push '("ALIAS" (eshell-test-value . nil)) eshell-variable-aliases-list)
+     (eshell-match-command-output "echo $ALIAS" "value\n")
+     (eshell-match-command-output "set ALIAS hello"
+                                  "Variable `ALIAS' is not settable\n"
+                                  nil t))))
+
+(ert-deftest esh-var-test/alias/export ()
+  "Test that `export' properly sets variable aliases."
+  (with-temp-eshell
+   (let ((eshell-test-value "value"))
+     (push `("ALIAS" (,(lambda () eshell-test-value)
+                      . (lambda (_ value) (setq eshell-test-value value)))
+             nil t)
+           eshell-variable-aliases-list)
+     (eshell-match-command-output "export ALIAS=hello" "\\`\\'")
+     (should (equal eshell-test-value "hello")))))
+
+(ert-deftest esh-var-test/alias/local-variables ()
+  "Test that \"VAR=value cmd\" temporarily sets read-only variable aliases."
+  (with-temp-eshell
+   (let ((eshell-test-value "value"))
+     (push `("ALIAS" ,(lambda () eshell-test-value) t t)
+           eshell-variable-aliases-list)
+     (eshell-match-command-output "ALIAS=hello env" "ALIAS=hello\n")
+     (should (equal eshell-test-value "value")))))
+
+
 ;; Built-in variables
 
 (ert-deftest esh-var-test/lines-var ()
@@ -465,6 +613,65 @@ inside double-quotes"
    (eshell-match-command-output "echo $INSIDE_EMACS[, 1]"
                                 "eshell")))
 
+(ert-deftest esh-var-test/path-var/local-directory ()
+  "Test using $PATH in a local directory."
+  (let ((expected-path (string-join (eshell-get-path t) (path-separator))))
+    (with-temp-eshell
+     (eshell-match-command-output "echo $PATH" (regexp-quote expected-path)))))
+
+(ert-deftest esh-var-test/path-var/remote-directory ()
+  "Test using $PATH in a remote directory."
+  (skip-unless (eshell-tests-remote-accessible-p))
+  (let* ((default-directory ert-remote-temporary-file-directory)
+         (expected-path (string-join (eshell-get-path t) (path-separator))))
+    (with-temp-eshell
+     (eshell-match-command-output "echo $PATH" (regexp-quote expected-path)))))
+
+(ert-deftest esh-var-test/path-var/set ()
+  "Test setting $PATH."
+  (let* ((path-to-set-list '("/some/path" "/other/path"))
+         (path-to-set (string-join path-to-set-list (path-separator))))
+    (with-temp-eshell
+     (eshell-match-command-output (concat "set PATH " path-to-set)
+                                  (concat path-to-set "\n"))
+     (eshell-match-command-output "echo $PATH" (concat path-to-set "\n"))
+     (should (equal (eshell-get-path t) path-to-set-list)))))
+
+(ert-deftest esh-var-test/path-var/set-locally ()
+  "Test setting $PATH temporarily for a single command."
+  (let* ((path-to-set-list '("/some/path" "/other/path"))
+         (path-to-set (string-join path-to-set-list (path-separator))))
+    (with-temp-eshell
+     (eshell-match-command-output (concat "set PATH " path-to-set)
+                                  (concat path-to-set "\n"))
+     (eshell-match-command-output "PATH=/local/path env"
+                                  "PATH=/local/path\n")
+     ;; After the last command, the previous $PATH value should be restored.
+     (eshell-match-command-output "echo $PATH" (concat path-to-set "\n"))
+     (should (equal (eshell-get-path t) path-to-set-list)))))
+
+(ert-deftest esh-var-test/path-var/preserve-across-hosts ()
+  "Test that $PATH can be set independently on multiple hosts."
+  (let ((local-directory default-directory)
+        local-path remote-path)
+    (with-temp-eshell
+     ;; Set the $PATH on localhost.
+     (eshell-insert-command "set PATH /local/path")
+     (setq local-path (eshell-last-output))
+     ;; `cd' to a remote host and set the $PATH there too.
+     (eshell-insert-command
+      (format "cd %s" ert-remote-temporary-file-directory))
+     (eshell-insert-command "set PATH /remote/path")
+     (setq remote-path (eshell-last-output))
+     ;; Return to localhost and check that $PATH is the value we set
+     ;; originally.
+     (eshell-insert-command (format "cd %s" local-directory))
+     (eshell-match-command-output "echo $PATH" (regexp-quote local-path))
+     ;; ... and do the same for the remote host.
+     (eshell-insert-command
+      (format "cd %s" ert-remote-temporary-file-directory))
+     (eshell-match-command-output "echo $PATH" (regexp-quote remote-path)))))
+
 (ert-deftest esh-var-test/last-status-var-lisp-command ()
   "Test using the \"last exit status\" ($?) variable with a Lisp command"
   (with-temp-eshell
@@ -472,9 +679,8 @@ inside double-quotes"
                                 "t\n0\n")
    (eshell-match-command-output "zerop 1; echo $?"
                                 "0\n")
-   (let ((debug-on-error nil))
-     (eshell-match-command-output "zerop foo; echo $?"
-                                  "1\n"))))
+   (eshell-match-command-output "zerop foo; echo $?"
+                                "1\n" nil t)))
 
 (ert-deftest esh-var-test/last-status-var-lisp-form ()
   "Test using the \"last exit status\" ($?) variable with a Lisp form"
@@ -484,9 +690,8 @@ inside double-quotes"
                                   "t\n0\n")
      (eshell-match-command-output "(zerop 1); echo $?"
                                   "2\n")
-     (let ((debug-on-error nil))
-       (eshell-match-command-output "(zerop \"foo\"); echo $?"
-                                    "1\n")))))
+     (eshell-match-command-output "(zerop \"foo\"); echo $?"
+                                  "1\n" nil t))))
 
 (ert-deftest esh-var-test/last-status-var-lisp-form-2 ()
   "Test using the \"last exit status\" ($?) variable with a Lisp form.
@@ -497,9 +702,8 @@ This tests when `eshell-lisp-form-nil-is-failure' is nil."
                                   "0\n")
      (eshell-match-command-output "(zerop 0); echo $?"
                                   "0\n")
-     (let ((debug-on-error nil))
-       (eshell-match-command-output "(zerop \"foo\"); echo $?"
-                                    "1\n")))))
+     (eshell-match-command-output "(zerop \"foo\"); echo $?"
+                                  "1\n" nil t))))
 
 (ert-deftest esh-var-test/last-status-var-ext-cmd ()
   "Test using the \"last exit status\" ($?) variable with an external command"
diff --git a/test/lisp/eshell/eshell-tests-helpers.el 
b/test/lisp/eshell/eshell-tests-helpers.el
index 73abfcbb55..1d9674070c 100644
--- a/test/lisp/eshell/eshell-tests-helpers.el
+++ b/test/lisp/eshell/eshell-tests-helpers.el
@@ -31,11 +31,22 @@
 (require 'eshell)
 
 (defvar eshell-history-file-name nil)
+(defvar eshell-last-dir-ring-file-name nil)
 
 (defvar eshell-test--max-subprocess-time 5
   "The maximum amount of time to wait for a subprocess to finish, in seconds.
 See `eshell-wait-for-subprocess'.")
 
+(defun eshell-tests-remote-accessible-p ()
+  "Return if a test involving remote files can proceed.
+If using this function, be sure to load `tramp' near the
+beginning of the test file."
+  (ignore-errors
+    (and
+     (file-remote-p ert-remote-temporary-file-directory)
+     (file-directory-p ert-remote-temporary-file-directory)
+     (file-writable-p ert-remote-temporary-file-directory))))
+
 (defmacro with-temp-eshell (&rest body)
   "Evaluate BODY in a temporary Eshell buffer."
   `(save-current-buffer
@@ -44,6 +55,7 @@ See `eshell-wait-for-subprocess'.")
               ;; back on $HISTFILE.
               (process-environment (cons "HISTFILE" process-environment))
               (eshell-history-file-name nil)
+              (eshell-last-dir-ring-file-name nil)
               (eshell-buffer (eshell t)))
          (unwind-protect
              (with-current-buffer eshell-buffer
@@ -83,26 +95,39 @@ After inserting, call FUNC.  If FUNC is nil, instead call
   (insert-and-inherit command)
   (funcall (or func 'eshell-send-input)))
 
+(defun eshell-last-input ()
+  "Return the input of the last Eshell command."
+  (buffer-substring-no-properties
+   eshell-last-input-start eshell-last-input-end))
+
+(defun eshell-last-output ()
+  "Return the output of the last Eshell command."
+  (buffer-substring-no-properties
+   (eshell-beginning-of-output) (eshell-end-of-output)))
+
 (defun eshell-match-output (regexp)
   "Test whether the output of the last command matches REGEXP."
-  (string-match-p
-    regexp (buffer-substring-no-properties
-            (eshell-beginning-of-output) (eshell-end-of-output))))
+  (string-match-p regexp (eshell-last-output)))
 
 (defun eshell-match-output--explainer (regexp)
   "Explain the result of `eshell-match-output'."
   `(mismatched-output
-    (command ,(buffer-substring-no-properties
-               eshell-last-input-start eshell-last-input-end))
-    (output ,(buffer-substring-no-properties
-              (eshell-beginning-of-output) (eshell-end-of-output)))
+    (command ,(eshell-last-input))
+    (output ,(eshell-last-output))
     (regexp ,regexp)))
 
 (put 'eshell-match-output 'ert-explainer #'eshell-match-output--explainer)
 
-(defun eshell-match-command-output (command regexp &optional func)
-  "Insert a COMMAND at the end of the buffer and match the output with REGEXP."
-  (eshell-insert-command command func)
+(defun eshell-match-command-output (command regexp &optional func
+                                            ignore-errors)
+  "Insert a COMMAND at the end of the buffer and match the output with REGEXP.
+FUNC is the function to call after inserting the text (see
+`eshell-insert-command').
+
+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)))
 
diff --git a/test/lisp/files-x-tests.el b/test/lisp/files-x-tests.el
index 7ee2f0c1a6..b1555a0266 100644
--- a/test/lisp/files-x-tests.el
+++ b/test/lisp/files-x-tests.el
@@ -23,6 +23,7 @@
 
 (require 'ert)
 (require 'files-x)
+(require 'tramp-integration)
 
 (defconst files-x-test--variables1
   '((remote-shell-file-name . "/bin/bash")
@@ -35,16 +36,20 @@
   '((remote-null-device . "/dev/null")))
 (defconst files-x-test--variables4
   '((remote-null-device . "null")))
+(defconst files-x-test--variables5
+  '((remote-lazy-var . nil)
+    (remote-null-device . "/dev/null")))
 (defvar remote-null-device)
+(defvar remote-lazy-var nil)
 (put 'remote-shell-file-name 'safe-local-variable #'identity)
 (put 'remote-shell-command-switch 'safe-local-variable #'identity)
 (put 'remote-shell-interactive-switch 'safe-local-variable #'identity)
 (put 'remote-shell-login-switch 'safe-local-variable #'identity)
 (put 'remote-null-device 'safe-local-variable #'identity)
 
-(defconst files-x-test--application '(:application 'my-application))
+(defconst files-x-test--application '(:application my-application))
 (defconst files-x-test--another-application
-  '(:application 'another-application))
+  '(:application another-application))
 (defconst files-x-test--protocol '(:protocol "my-protocol"))
 (defconst files-x-test--user '(:user "my-user"))
 (defconst files-x-test--machine '(:machine "my-machine"))
@@ -91,6 +96,28 @@
       (connection-local-get-profile-variables 'remote-nullfile)
       files-x-test--variables4))))
 
+(ert-deftest files-x-test-connection-local-update-profile-variables ()
+  "Test updating connection-local profile variables."
+
+  ;; Declare (PROFILE VARIABLES) objects.
+  (let (connection-local-profile-alist connection-local-criteria-alist)
+    (connection-local-set-profile-variables
+     'remote-bash (copy-alist files-x-test--variables1))
+    (should
+     (equal
+      (connection-local-get-profile-variables 'remote-bash)
+      files-x-test--variables1))
+
+    ;; Updating overwrites only the values specified in this call, but
+    ;; retains all the other values from previous calls.
+    (connection-local-update-profile-variables
+     'remote-bash files-x-test--variables2)
+    (should
+     (equal
+      (connection-local-get-profile-variables 'remote-bash)
+      (cons (car files-x-test--variables2)
+            (cdr files-x-test--variables1))))))
+
 (ert-deftest files-x-test-connection-local-set-profiles ()
   "Test setting connection-local profiles."
 
@@ -233,9 +260,12 @@
                  (nreverse (copy-tree files-x-test--variables2)))))
         ;; The variables exist also as local variables.
         (should (local-variable-p 'remote-shell-file-name))
+        (should (local-variable-p 'remote-null-device))
         ;; The proper variable value is set.
         (should
-         (string-equal (symbol-value 'remote-shell-file-name) "/bin/ksh"))))
+         (string-equal (symbol-value 'remote-shell-file-name) "/bin/ksh"))
+        (should
+         (string-equal (symbol-value 'remote-null-device) "/dev/null"))))
 
     ;; The third test case.  Both criteria `files-x-test--criteria1'
     ;; and `files-x-test--criteria2' apply, but there are no double
@@ -274,13 +304,11 @@
         (should-not (local-variable-p 'remote-shell-file-name))
         (should-not (boundp 'remote-shell-file-name))))))
 
-(defvar tramp-connection-local-default-shell-variables)
-(defvar tramp-connection-local-default-system-variables)
-
 (ert-deftest files-x-test-with-connection-local-variables ()
   "Test setting connection-local variables."
 
-  (let (connection-local-profile-alist connection-local-criteria-alist)
+  (let ((connection-local-profile-alist connection-local-profile-alist)
+        (connection-local-criteria-alist connection-local-criteria-alist))
     (connection-local-set-profile-variables
      'remote-bash files-x-test--variables1)
     (connection-local-set-profile-variables
@@ -291,29 +319,6 @@
     (connection-local-set-profiles
      nil 'remote-ksh 'remote-nullfile)
 
-    (with-temp-buffer
-      (let ((enable-connection-local-variables t))
-        (hack-connection-local-variables-apply nil)
-
-       ;; All connection-local variables are set.  They apply in
-        ;; reverse order in `connection-local-variables-alist'.
-        (should
-         (equal connection-local-variables-alist
-               (append
-                (nreverse (copy-tree files-x-test--variables3))
-                (nreverse (copy-tree files-x-test--variables2)))))
-        ;; The variables exist also as local variables.
-        (should (local-variable-p 'remote-shell-file-name))
-        (should (local-variable-p 'remote-null-device))
-        ;; The proper variable values are set.
-        (should
-         (string-equal (symbol-value 'remote-shell-file-name) "/bin/ksh"))
-        (should
-         (string-equal (symbol-value 'remote-null-device) "/dev/null"))
-
-       ;; A candidate connection-local variable is not bound yet.
-        (should-not (local-variable-p 'remote-shell-command-switch))))
-
     (with-temp-buffer
       ;; Use the macro.  We need a remote `default-directory'.
       (let ((enable-connection-local-variables t)
@@ -331,18 +336,18 @@
        (with-connection-local-variables
         ;; All connection-local variables are set.  They apply in
         ;; reverse order in `connection-local-variables-alist'.
-        ;; Since we ha a remote default directory, Tramp's settings
+        ;; Since we have a remote default directory, Tramp's settings
         ;; are appended as well.
          (should
           (equal
            connection-local-variables-alist
           (append
-           (nreverse (copy-tree files-x-test--variables3))
-           (nreverse (copy-tree files-x-test--variables2))
             (nreverse
              (copy-tree tramp-connection-local-default-shell-variables))
             (nreverse
-             (copy-tree tramp-connection-local-default-system-variables)))))
+             (copy-tree tramp-connection-local-default-system-variables))
+           (nreverse (copy-tree files-x-test--variables3))
+           (nreverse (copy-tree files-x-test--variables2)))))
          ;; The variables exist also as local variables.
          (should (local-variable-p 'remote-shell-file-name))
          (should (local-variable-p 'remote-null-device))
@@ -352,15 +357,21 @@
          (should
           (string-equal (symbol-value 'remote-null-device) "/dev/null"))
 
-         ;; Run another instance of `with-connection-local-variables'
-         ;; with a different application.
-         (let ((connection-local-default-application (cadr 
files-x-test--application)))
-          (with-connection-local-variables
-            ;; The proper variable values are set.
-            (should
-             (string-equal (symbol-value 'remote-shell-file-name) "/bin/bash"))
-            (should
-             (string-equal (symbol-value 'remote-null-device) "/dev/null"))))
+         ;; Run `with-connection-local-application-variables' to use a
+         ;; different application.
+        (with-connection-local-application-variables
+             (cadr files-x-test--application)
+         (should
+          (equal
+           connection-local-variables-alist
+          (append
+           (nreverse (copy-tree files-x-test--variables3))
+           (nreverse (copy-tree files-x-test--variables1)))))
+           ;; The proper variable values are set.
+           (should
+            (string-equal (symbol-value 'remote-shell-file-name) "/bin/bash"))
+           (should
+            (string-equal (symbol-value 'remote-null-device) "/dev/null")))
          ;; The variable values are reset.
          (should
           (string-equal (symbol-value 'remote-shell-file-name) "/bin/ksh"))
@@ -376,5 +387,60 @@
        (should-not (boundp 'remote-shell-file-name))
        (should (string-equal (symbol-value 'remote-null-device) "null"))))))
 
+(defun files-x-test--get-lazy-var ()
+  "Get the connection-local value of `remote-lazy-var'.
+If it's not initialized yet, initialize it."
+  (with-connection-local-application-variables
+      (cadr files-x-test--application)
+    (or remote-lazy-var
+        (setq-connection-local remote-lazy-var
+                               (or (file-remote-p default-directory 'host)
+                                   "local")))))
+
+(defun files-x-test--set-lazy-var (value)
+  "Set the connection-local value of `remote-lazy-var'"
+  (with-connection-local-application-variables
+      (cadr files-x-test--application)
+    (setq-connection-local remote-lazy-var value)))
+
+(ert-deftest files-x-test-setq-connection-local ()
+  "Test dynamically setting connection local variables."
+  (let (connection-local-profile-alist connection-local-criteria-alist)
+    (connection-local-set-profile-variables
+     'remote-lazy files-x-test--variables5)
+    (connection-local-set-profiles
+     files-x-test--application
+     'remote-lazy)
+
+    ;; Test the initial local value.
+    (should (equal (files-x-test--get-lazy-var) "local"))
+
+    ;; Set the local value and make sure it retains the value we set.
+    (should (equal (files-x-test--set-lazy-var "here") "here"))
+    (should (equal (files-x-test--get-lazy-var) "here"))
+
+    (let ((default-directory "/method:host:"))
+      ;; Test the initial remote value.
+      (should (equal (files-x-test--get-lazy-var) "host"))
+
+      ;; Set the remote value and make sure it retains the value we set.
+      (should (equal (files-x-test--set-lazy-var "there") "there"))
+      (should (equal (files-x-test--get-lazy-var) "there"))
+      ;; Set another connection-local variable.
+      (with-connection-local-application-variables
+          (cadr files-x-test--application)
+        (setq-connection-local remote-null-device "null")))
+
+    ;; Make sure we get the local value we set above.
+    (should (equal (files-x-test--get-lazy-var) "here"))
+    (should-not (boundp 'remote-null-device))
+
+    ;; Make sure we get the remote values we set above.
+    (let ((default-directory "/method:host:"))
+      (should (equal (files-x-test--get-lazy-var) "there"))
+      (with-connection-local-application-variables
+          (cadr files-x-test--application)
+        (should (equal remote-null-device "null"))))))
+
 (provide 'files-x-tests)
 ;;; files-x-tests.el ends here
diff --git a/test/lisp/image/wallpaper-tests.el 
b/test/lisp/image/wallpaper-tests.el
index cb6818f8c1..a5d3343bd4 100644
--- a/test/lisp/image/wallpaper-tests.el
+++ b/test/lisp/image/wallpaper-tests.el
@@ -99,9 +99,12 @@
                 ("touch" "touch" fil
                  :init-action (lambda () (setq called t)))))
               (wallpaper-command (wallpaper--find-command))
-              (wallpaper-command-args (wallpaper--find-command-args)))
+              (wallpaper-command-args (wallpaper--find-command-args))
+              process)
         (should (functionp (wallpaper-setter-init-action 
wallpaper--current-setter)))
-        (wallpaper-set fil-jpg)
+        (setq process (wallpaper-set fil-jpg))
+        ;; Wait for "touch" process to exit so temp file is removed.
+        (accept-process-output process 3)
         (should called)))))
 
 (ert-deftest wallpaper-set/calls-wallpaper-set-function ()
diff --git a/test/lisp/international/textsec-tests.el 
b/test/lisp/international/textsec-tests.el
index 6b0773dc40..1f7fb97a6b 100644
--- a/test/lisp/international/textsec-tests.el
+++ b/test/lisp/international/textsec-tests.el
@@ -77,7 +77,7 @@
   (should (eq (textsec-restriction-level "切foo")
               'highly-restrictive))
   (should (eq (textsec-restriction-level "հfoo")
-              'moderately-retrictive))
+              'moderately-restrictive))
   (should (eq (textsec-restriction-level "Сirсlе")
               'unrestricted)))
 
diff --git a/test/lisp/net/eudc-resources/ecompleterc 
b/test/lisp/net/eudc-resources/ecompleterc
new file mode 100644
index 0000000000..9019b26c9f
--- /dev/null
+++ b/test/lisp/net/eudc-resources/ecompleterc
@@ -0,0 +1,7 @@
+((mail
+ ("larsi@gnus.org" 38154 1516109510 "Lars Ingebrigtsen <larsi@ecomplete.org>")
+ ("kfogel@red-bean.com" 10 1516065455 "Karl Fogel <kfogel@ecomplete.com>")
+ ("behse@ecomplete.org" 10 1516065455 "behse@ecomplete.org"))
+ (phone
+ ("Lars Ingebrigtsen" 0 0 "+1 234 5678 9012")
+ ("Karl Fogel" 0 0 "+33 701 4567 8901")))
diff --git a/test/lisp/net/eudc-resources/mailrc 
b/test/lisp/net/eudc-resources/mailrc
new file mode 100644
index 0000000000..c565f71837
--- /dev/null
+++ b/test/lisp/net/eudc-resources/mailrc
@@ -0,0 +1,3 @@
+alias lars "Lars Ingebrigtsen <larsi@mail-abbrev.com>"
+alias karl "Karl Fogel <kfogel@mail-abbrev.com>"
+alias emacsheroes lars karl
diff --git a/test/lisp/net/eudc-tests.el b/test/lisp/net/eudc-tests.el
new file mode 100644
index 0000000000..c326dcc793
--- /dev/null
+++ b/test/lisp/net/eudc-tests.el
@@ -0,0 +1,271 @@
+;;; eudc-tests.el --- tests for eudc.el -*- lexical-binding: t -*-
+
+;; Copyright (C) 2022 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 'eudc)
+
+(ert-deftest eudc--plist-member ()
+  "Test `eudc--plist-member' behavior."
+  (dolist (obj '(a (a . a) (a a . a)))
+    (should-error (eudc--plist-member obj nil) :type 'wrong-type-argument))
+  (dolist (plist '((nil) (a) (a a a)))
+    (let ((err `(wrong-type-argument plistp ,(copy-sequence plist))))
+      (dolist (key '(nil a))
+        (should (equal err (should-error (eudc--plist-member plist key)))))))
+  (let ((-nil (string ?n ?i ?l))
+        (-a (string ?a)))
+    (should-not (eudc--plist-member () nil))
+    (should-not (eudc--plist-member () 'a))
+    (should-not (eudc--plist-member '(nil nil) 'a))
+    (should-not (eudc--plist-member '(nil a) 'a))
+    (should-not (eudc--plist-member '(a nil) nil))
+    (should-not (eudc--plist-member '(a a) nil))
+    (should-not (eudc--plist-member '("nil" a) nil))
+    (should-not (eudc--plist-member '("nil" a) -nil))
+    (should-not (eudc--plist-member '("a" a) nil))
+    (should-not (eudc--plist-member '("a" a) -a))
+    (should-not (eudc--plist-member '(nil a nil a) 'a))
+    (should-not (eudc--plist-member '(nil a "a" a) -a))
+    (should (equal (eudc--plist-member '(nil nil) nil) '(nil nil)))
+    (should (equal (eudc--plist-member '(nil a) nil) '(nil a)))
+    (should (equal (eudc--plist-member '(a nil) 'a) '(a nil)))
+    (should (equal (eudc--plist-member '(a a) 'a) '(a a)))
+    (should (equal (eudc--plist-member '(nil nil a nil) 'a) '(a nil)))
+    (should (equal (eudc--plist-member '(nil a a a) 'a) '(a a)))
+    (should (equal (eudc--plist-member '(a a a a) 'a) '(a a a a)))))
+
+(ert-deftest eudc-plist-member ()
+  "Test `eudc-plist-member' behavior."
+  (dolist (obj '(a (a . a) (a a . a)))
+    (should-error (eudc-plist-member obj nil) :type 'wrong-type-argument))
+  (dolist (plist '((nil) (a) (a a a)))
+    (let ((err `(wrong-type-argument plistp ,(copy-sequence plist))))
+      (dolist (key '(nil a))
+        (should (equal err (should-error (eudc-plist-member plist key)))))))
+  (let ((-nil (string ?n ?i ?l))
+        (-a (string ?a)))
+    (should-not (eudc-plist-member () nil))
+    (should-not (eudc-plist-member () 'a))
+    (should-not (eudc-plist-member '(nil nil) 'a))
+    (should-not (eudc-plist-member '(nil a) 'a))
+    (should-not (eudc-plist-member '(a nil) nil))
+    (should-not (eudc-plist-member '(a a) nil))
+    (should-not (eudc-plist-member '("nil" a) nil))
+    (should-not (eudc-plist-member '("nil" a) -nil))
+    (should-not (eudc-plist-member '("a" a) nil))
+    (should-not (eudc-plist-member '("a" a) -a))
+    (should-not (eudc-plist-member '(nil a nil a) 'a))
+    (should-not (eudc-plist-member '(nil a "a" a) -a))
+    (should (eq t (eudc-plist-member '(nil nil) nil)))
+    (should (eq t (eudc-plist-member '(nil a) nil)))
+    (should (eq t (eudc-plist-member '(a nil) 'a)))
+    (should (eq t (eudc-plist-member '(a a) 'a)))
+    (should (eq t (eudc-plist-member '(nil nil a nil) 'a)))
+    (should (eq t (eudc-plist-member '(nil a a a) 'a)))
+    (should (eq t (eudc-plist-member '(a a a a) 'a)))))
+
+(ert-deftest eudc-plist-get ()
+  "Test `eudc-plist-get' behavior."
+  (dolist (obj '(a (a . a) (a a . a)))
+    (should-error (eudc-plist-get obj nil) :type 'wrong-type-argument))
+  (dolist (plist '((nil) (a) (a a a)))
+    (let ((err `(wrong-type-argument plistp ,(copy-sequence plist))))
+      (dolist (key '(nil a))
+        (should (equal err (should-error (eudc-plist-get plist key)))))))
+  (let ((-nil (string ?n ?i ?l))
+        (-a (string ?a)))
+    (should-not (eudc-plist-get () nil))
+    (should-not (eudc-plist-get () 'a))
+    (should-not (eudc-plist-get '(nil nil) nil))
+    (should-not (eudc-plist-get '(nil nil) 'a))
+    (should-not (eudc-plist-get '(nil a) 'a))
+    (should-not (eudc-plist-get '(a nil) nil))
+    (should-not (eudc-plist-get '(a nil) 'a))
+    (should-not (eudc-plist-get '(a a) nil))
+    (should-not (eudc-plist-get '("nil" a) nil))
+    (should-not (eudc-plist-get '("nil" a) -nil))
+    (should-not (eudc-plist-get '("a" a) nil))
+    (should-not (eudc-plist-get '("a" a) -a))
+    (should-not (eudc-plist-get '(nil nil nil a) nil))
+    (should-not (eudc-plist-get '(nil a nil a) 'a))
+    (should-not (eudc-plist-get '(nil a "a" a) -a))
+    (should-not (eudc-plist-get '(a nil a a) 'a))
+    (should (eq 'a (eudc-plist-get '(nil a) nil)))
+    (should (eq 'a (eudc-plist-get '(a a) 'a)))
+    (should (eq 'a (eudc-plist-get '(a a a nil) 'a)))
+    (should (eq 'b (eudc-plist-get () nil 'b)))
+    (should (eq 'b (eudc-plist-get () 'a 'b)))
+    (should (eq 'b (eudc-plist-get '(nil a "a" a) -a 'b)))
+    (should (eq 'b (eudc-plist-get '(a nil "nil" nil) -nil 'b)))))
+
+(ert-deftest eudc-lax-plist-get ()
+  "Test `eudc-lax-plist-get' behavior."
+  (dolist (obj '(a (a . a) (a a . a)))
+    (should-error (eudc-lax-plist-get obj nil) :type 'wrong-type-argument))
+  (dolist (plist '((nil) (a) (a a a)))
+    (let ((err `(wrong-type-argument plistp ,(copy-sequence plist))))
+      (dolist (key '(nil a))
+        (should (equal err (should-error (eudc-lax-plist-get plist key)))))))
+  (let ((-nil (string ?n ?i ?l))
+        (-a (string ?a)))
+    (should-not (eudc-lax-plist-get () nil))
+    (should-not (eudc-lax-plist-get () 'a))
+    (should-not (eudc-lax-plist-get '(nil nil) nil))
+    (should-not (eudc-lax-plist-get '(nil nil) 'a))
+    (should-not (eudc-lax-plist-get '(nil a) 'a))
+    (should-not (eudc-lax-plist-get '(a nil) nil))
+    (should-not (eudc-lax-plist-get '(a nil) 'a))
+    (should-not (eudc-lax-plist-get '(a a) nil))
+    (should-not (eudc-lax-plist-get '("nil" a) nil))
+    (should-not (eudc-lax-plist-get '("nil" a) 'a))
+    (should-not (eudc-lax-plist-get '("a" a) nil))
+    (should-not (eudc-lax-plist-get '("a" a) 'a))
+    (should-not (eudc-lax-plist-get '(nil nil nil a) nil))
+    (should-not (eudc-lax-plist-get '(nil a nil a) 'a))
+    (should-not (eudc-lax-plist-get '(nil a "a" a) 'a))
+    (should-not (eudc-lax-plist-get '(a nil a a) 'a))
+    (should (eq 'a (eudc-lax-plist-get '(nil a) nil)))
+    (should (eq 'a (eudc-lax-plist-get '(a a) 'a)))
+    (should (eq 'a (eudc-lax-plist-get '(a a a nil) 'a)))
+    (should (eq 'b (eudc-lax-plist-get () nil 'b)))
+    (should (eq 'b (eudc-lax-plist-get () 'a 'b)))
+    (should (eq 'a (eudc-lax-plist-get '("nil" a) -nil)))
+    (should (eq 'a (eudc-lax-plist-get '("a" a) -a)))
+    (should (eq 'a (eudc-lax-plist-get '(nil a "a" a) -a)))
+    (should (eq 'b (eudc-lax-plist-get '(nil a "a" a) 'a 'b)))
+    (should (eq 'b (eudc-lax-plist-get '(a nil "nil" nil) nil 'b)))))
+
+;; eudc-rfc5322-quote-phrase (string)
+(ert-deftest eudc-test-rfc5322-quote-phrase ()
+  "Tests for RFC5322 compliant phrase quoting."
+  ;; atext-token "[:alpha:][:digit:]!#$%&'*+/=?^_`{|}~-"
+  (should (equal (eudc-rfc5322-quote-phrase "Foo Bar !#$%&'*+/=?^_`{|}~-")
+                 "Foo Bar !#$%&'*+/=?^_`{|}~-"))
+  (should (equal (eudc-rfc5322-quote-phrase "Foo, Bar !#$%&'*+/=?^_`{|}~-")
+                 "\"Foo, Bar !#$%&'*+/=?^_`{|}~-\"")))
+
+;; eudc-rfc5322-valid-comment-p (string)
+(ert-deftest eudc-test-rfc5322-valid-comment-p ()
+  "Tests for RFC5322 compliant comments."
+  ;; cctext-token "\u005D-\u007E\u002A-\u005B\u0021-\u0027" + fwsp-token (TAB, 
LF, SPC)
+  ;; Printable US-ASCII characters not including "(", ")", or "\".
+  (let ((good-chars (append (number-sequence #x09 #x0a)
+                            (number-sequence #x20 #x20)
+                            (number-sequence #x21 #x27)
+                            (number-sequence #x2a #x5b)
+                            (number-sequence #x5d #x7e)))
+        (bad-chars  (append (number-sequence #x00 #x08)
+                            (number-sequence #x0b #x1f)
+                            (number-sequence #x28 #x29)
+                            (number-sequence #x5c #x5c)
+                            (number-sequence #x7f #xff))))
+    (dolist (gc good-chars)
+      (should (eq (eudc-rfc5322-valid-comment-p (format "%c" gc)) t)))
+    (dolist (bc bad-chars)
+      (should (eq (eudc-rfc5322-valid-comment-p (format "%c" bc)) nil)))))
+
+;; eudc-rfc5322-make-address (address &optional firstname name comment)
+(ert-deftest eudc-test-make-address ()
+  "Tests for RFC5322 compliant email address formatting."
+  (should (equal (eudc-rfc5322-make-address "")
+                 nil))
+  (should (equal (eudc-rfc5322-make-address nil)
+                 nil))
+  (should (equal (eudc-rfc5322-make-address "j.sixpack@example.org")
+                 "j.sixpack@example.org"))
+  (should (equal (eudc-rfc5322-make-address "<j.sixpack@example.org>")
+                 "<j.sixpack@example.org>"))
+  (should (equal (eudc-rfc5322-make-address "j.sixpack@example.org"
+                                            "Joey")
+                 "Joey <j.sixpack@example.org>"))
+  (should (equal (eudc-rfc5322-make-address "j.sixpack@example.org"
+                                            "Joey"
+                                            "Sixpack")
+                 "Joey Sixpack <j.sixpack@example.org>"))
+  (should (equal (eudc-rfc5322-make-address "j.sixpack@example.org"
+                                            "Joey"
+                                            "Sixpack"
+                                            "ten-packs are fine, too")
+                 "Joey Sixpack <j.sixpack@example.org> \
+(ten-packs are fine, too)"))
+  (should (equal (eudc-rfc5322-make-address "j.sixpack@example.org"
+                                            ""
+                                            "Sixpack, Joey")
+                 "\"Sixpack, Joey\" <j.sixpack@example.org>"))
+  (should (equal (eudc-rfc5322-make-address "j.sixpack@example.org"
+                                            nil
+                                            "Sixpack, Joey")
+                 "\"Sixpack, Joey\" <j.sixpack@example.org>"))
+  (should (equal (eudc-rfc5322-make-address "j.sixpack@example.org"
+                                            nil
+                                            nil
+                                            "Duh!")
+                 "j.sixpack@example.org (Duh!)"))
+  (should (equal (eudc-rfc5322-make-address "j.sixpack@example.org"
+                                            nil
+                                            nil
+                                            "Duh\\!")
+                 "j.sixpack@example.org")))
+
+(require 'ert-x) ; ert-with-temp-directory
+
+(defvar ecomplete-database-file (ert-resource-file "ecompleterc"))
+
+(ert-deftest eudcb-ecomplete ()
+  "Test the ecomplete back-end."
+  (ert-with-temp-directory home
+    (with-environment-variables (("HOME" home))
+      (let ((eudc-ignore-options-file t))
+        (should (equal (eudc-ecomplete-query-internal '((mail . "brigts")))
+                       '(((mail . "Lars Ingebrigtsen 
<larsi@ecomplete.org>")))))
+        (should (equal (eudc-ecomplete-query-internal '((mail . "karl")))
+                       '(((mail . "Karl Fogel <kfogel@ecomplete.com>")))))
+        (should (equal (eudc-ecomplete-query-internal '((mail . "behs")))
+                       '(((mail . "behse@ecomplete.org")))))
+        (should (equal (eudc-ecomplete-query-internal '((mail . "louie")))
+                       nil))))))
+
+(ert-with-temp-directory
+ home
+ (ert-deftest eudcb-mailabbrev ()
+   "Test the mailabbrev back-end."
+   (with-environment-variables
+    (("HOME" home))
+    (let ((mail-personal-alias-file (ert-resource-file "mailrc"))
+          (eudc-ignore-options-file t))
+      (should (equal (eudc-mailabbrev-query-internal '((email . "lars")))
+                     '(((email . "larsi@mail-abbrev.com")
+                        (name . "Lars Ingebrigtsen")))))
+      (should (equal (eudc-mailabbrev-query-internal '((name . "lars")))
+                     '(((email . "larsi@mail-abbrev.com")
+                        (name . "Lars Ingebrigtsen")))))
+      (should (equal (eudc-mailabbrev-query-internal '((phone . "lars")))
+                     nil))
+      (should (equal (eudc-mailabbrev-query-internal '((firstname . "karl")))
+                     '(((email . "kfogel@mail-abbrev.com")
+                        (name . "Karl Fogel")))))
+      (should (equal (eudc-mailabbrev-query-internal '((email . "louie")))
+                     nil))
+      (should (equal (eudc-mailabbrev-query-internal '((name . "emacsheroes")))
+                     '(((email . "Lars Ingebrigtsen <larsi@mail-abbrev.com>, \
+Karl Fogel <kfogel@mail-abbrev.com")))))))))
+
+(provide 'eudc-tests)
+;;; eudc-tests.el ends here
diff --git a/test/lisp/net/puny-resources/IdnaTestV2.txt 
b/test/lisp/net/puny-resources/IdnaTestV2.txt
index ed2f32e129..4b8d5984a7 100644
--- a/test/lisp/net/puny-resources/IdnaTestV2.txt
+++ b/test/lisp/net/puny-resources/IdnaTestV2.txt
@@ -2,12 +2,12 @@
 # Date: 2021-08-17, 19:34:01 GMT
 # © 2021 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 http://www.unicode.org/terms_of_use.html
+# For terms of use, see https://www.unicode.org/terms_of_use.html
 #
 # Unicode IDNA Compatible Preprocessing for UTS #46
 # Version: 14.0.0
 #
-# For documentation and usage, see http://www.unicode.org/reports/tr46
+# For documentation and usage, see https://www.unicode.org/reports/tr46
 #
 # Test cases for verifying UTS #46 conformance.
 #
diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el
index 2db4449438..46fef558bf 100644
--- a/test/lisp/net/tramp-tests.el
+++ b/test/lisp/net/tramp-tests.el
@@ -4616,10 +4616,13 @@ This tests also `make-symbolic-link', `file-truename' 
and `add-name-to-file'."
              (load tmp-name 'noerror 'nomessage))
            (should-not (featurep 'tramp-test-load))
            (write-region "(provide 'tramp-test-load)" nil tmp-name)
-           ;; `load' in lread.c does not pass `must-suffix'.  Why?
-           ;;(should-error
-           ;; (load tmp-name nil 'nomessage 'nosuffix 'must-suffix)
-           ;; :type 'file-error)
+           ;; `load' in lread.c passes `must-suffix' since Emacs 29.
+           ;; In Ange-FTP, `must-suffix' is ignored.
+           (when (and (tramp--test-emacs29-p)
+                       (not (tramp--test-ange-ftp-p)))
+             (should-error
+              (load tmp-name nil 'nomessage 'nosuffix 'must-suffix)
+              :type 'file-error))
            (load tmp-name nil 'nomessage 'nosuffix)
            (should (featurep 'tramp-test-load)))
 
diff --git a/test/lisp/progmodes/cperl-mode-resources/here-docs.pl 
b/test/lisp/progmodes/cperl-mode-resources/here-docs.pl
index bb3d4871a9..13d879bf76 100644
--- a/test/lisp/progmodes/cperl-mode-resources/here-docs.pl
+++ b/test/lisp/progmodes/cperl-mode-resources/here-docs.pl
@@ -140,4 +140,70 @@ HERE
 
 . 'indent-level'; # Continuation, should be indented
 
+=head2 Test case 7
+
+An indented HERE document using a bare identifier.
+
+=cut
+
+## test case
+
+$text = <<~HERE;
+look-here
+HERE
+
+$noindent = "New statement in this line";
+
+=head2 Test case 8
+
+A HERE document as an argument to print when printing to a filehandle.
+
+=cut
+
+## test case
+
+print $fh <<~HERE;
+look-here
+HERE
+
+$noindent = "New statement in this line";
+
+=head2 Test case 9
+
+A HERE document as a hash value.
+
+=cut
+
+my %foo = (
+    text => <<~HERE
+look-here
+HERE
+    );
+
+$noindent = "New statement in this line";
+
+=head2 Test case 10
+
+A HERE document as an argument to die.
+
+=cut
+
+1 or die <<HERE;
+look-here
+HERE
+
+$noindent = "New statement in this line";
+
+=head2 Test case 11
+
+A HERE document as an argument to warn.
+
+=cut
+
+1 or warn <<HERE;
+look-here
+HERE
+
+$noindent = "New statement in this line";
+
 __END__
diff --git a/test/lisp/progmodes/python-tests.el 
b/test/lisp/progmodes/python-tests.el
index 9ad2d16930..f871b7bc7d 100644
--- a/test/lisp/progmodes/python-tests.el
+++ b/test/lisp/progmodes/python-tests.el
@@ -46,10 +46,7 @@ always located at the beginning of buffer."
 (defun python-tests-shell-wait-for-prompt ()
   "Wait for the prompt in the shell buffer."
   (python-shell-with-shell-buffer
-    (while (not (if-let ((prompt (python-util-comint-last-prompt)))
-                    (python-shell-comint-end-of-output-p
-                     (buffer-substring-no-properties
-                      (car prompt) (cdr prompt)))))
+    (while (not (python-util-comint-end-of-output-p))
       (sit-for 0.1))))
 
 (defmacro python-tests-with-temp-buffer-with-shell (contents &rest body)
@@ -4396,7 +4393,41 @@ def foo():
          (python-shell-interpreter "/some/path/to/bin/pypy"))
     (should (python-shell-completion-native-interpreter-disabled-p))))
 
-(ert-deftest python-shell-completion-1 ()
+(ert-deftest python-shell-completion-at-point-1 ()
+  (skip-unless (executable-find python-tests-shell-interpreter))
+  (python-tests-with-temp-buffer-with-shell
+   ""
+   (python-shell-with-shell-buffer
+     (insert "import abc")
+     (comint-send-input)
+     (python-tests-shell-wait-for-prompt)
+     (insert "abc.")
+     (should (nth 2 (python-shell-completion-at-point)))
+     (end-of-line 0)
+     (should-not (nth 2 (python-shell-completion-at-point))))))
+
+(ert-deftest python-shell-completion-at-point-native-1 ()
+  (skip-unless (executable-find python-tests-shell-interpreter))
+  (python-tests-with-temp-buffer-with-shell
+   ""
+   (python-shell-completion-native-turn-on)
+   (python-shell-with-shell-buffer
+     (insert "import abc")
+     (comint-send-input)
+     (python-tests-shell-wait-for-prompt)
+     (insert "abc.")
+     (should (nth 2 (python-shell-completion-at-point)))
+     (end-of-line 0)
+     (should-not (nth 2 (python-shell-completion-at-point))))))
+
+
+
+;;; PDB Track integration
+
+
+;;; Symbol completion
+
+(ert-deftest python-completion-at-point-1 ()
   (skip-unless (executable-find python-tests-shell-interpreter))
   (python-tests-with-temp-buffer-with-shell
    "
@@ -4411,7 +4442,7 @@ import abc
      (insert "A")
      (should (completion-at-point)))))
 
-(ert-deftest python-shell-completion-2 ()
+(ert-deftest python-completion-at-point-2 ()
   "Should work regardless of the point in the Shell buffer."
   (skip-unless (executable-find python-tests-shell-interpreter))
   (python-tests-with-temp-buffer-with-shell
@@ -4427,7 +4458,39 @@ import abc
      (insert "abc.")
      (should (completion-at-point)))))
 
-(ert-deftest python-shell-completion-native-1 ()
+(ert-deftest python-completion-at-point-pdb-1 ()
+  "Should not complete PDB commands in Python buffer."
+  (skip-unless (executable-find python-tests-shell-interpreter))
+  (python-tests-with-temp-buffer-with-shell
+   "
+import pdb
+
+pdb.set_trace()
+print('Hello')
+"
+   (let ((inhibit-message t))
+     (python-shell-send-buffer)
+     (python-tests-shell-wait-for-prompt)
+     (goto-char (point-max))
+     (insert "u")
+     (should-not (nth 2 (python-completion-at-point))))))
+
+(ert-deftest python-completion-at-point-while-running-1 ()
+  "Should not try to complete when a program is running in the Shell buffer."
+  (skip-unless (executable-find python-tests-shell-interpreter))
+  (python-tests-with-temp-buffer-with-shell
+   "
+import time
+
+time.sleep(3)
+"
+   (let ((inhibit-message t))
+     (python-shell-send-buffer)
+     (goto-char (point-max))
+     (insert "time.")
+     (should-not (with-timeout (1 t) (completion-at-point))))))
+
+(ert-deftest python-completion-at-point-native-1 ()
   (skip-unless (executable-find python-tests-shell-interpreter))
   (python-tests-with-temp-buffer-with-shell
    "
@@ -4443,7 +4506,7 @@ import abc
      (insert "A")
      (should (completion-at-point)))))
 
-(ert-deftest python-shell-completion-native-2 ()
+(ert-deftest python-completion-at-point-native-2 ()
   "Should work regardless of the point in the Shell buffer."
   (skip-unless (executable-find python-tests-shell-interpreter))
   (python-tests-with-temp-buffer-with-shell
@@ -4460,7 +4523,7 @@ import abc
      (insert "abc.")
      (should (completion-at-point)))))
 
-(ert-deftest python-shell-completion-native-with-ffap-1 ()
+(ert-deftest python-completion-at-point-native-with-ffap-1 ()
   (skip-unless (executable-find python-tests-shell-interpreter))
   (python-tests-with-temp-buffer-with-shell
    "
@@ -4476,7 +4539,7 @@ import abc
      (python-ffap-module-path "abc.")
      (should (completion-at-point)))))
 
-(ert-deftest python-shell-completion-native-with-eldoc-1 ()
+(ert-deftest python-completion-at-point-native-with-eldoc-1 ()
   (skip-unless (executable-find python-tests-shell-interpreter))
   (python-tests-with-temp-buffer-with-shell
    "
@@ -4492,13 +4555,6 @@ import abc
      (python-eldoc-function)
      (should (completion-at-point)))))
 
-
-
-;;; PDB Track integration
-
-
-;;; Symbol completion
-
 
 ;;; Fill paragraph
 
@@ -4508,6 +4564,31 @@ import abc
 
 ;;; FFAP
 
+(ert-deftest python-ffap-module-path-1 ()
+  (skip-unless (executable-find python-tests-shell-interpreter))
+  (python-tests-with-temp-buffer-with-shell
+   "
+import abc
+"
+   (let ((inhibit-message t))
+     (python-shell-send-buffer)
+     (python-tests-shell-wait-for-prompt)
+     (should (file-exists-p (python-ffap-module-path "abc"))))))
+
+(ert-deftest python-ffap-module-path-while-running-1 ()
+  "Should not get module path when a program is running in the Shell buffer."
+  (skip-unless (executable-find python-tests-shell-interpreter))
+  (python-tests-with-temp-buffer-with-shell
+   "
+import abc
+import time
+
+time.sleep(3)
+"
+   (let ((inhibit-message t))
+     (python-shell-send-buffer)
+     (should-not (with-timeout (1 t) (python-ffap-module-path "abc"))))))
+
 
 ;;; Code check
 
@@ -4571,6 +4652,32 @@ some_symbol   some_other_symbol
    (should (string= (python-eldoc--get-symbol-at-point)
                     "some_symbol"))))
 
+(ert-deftest python-eldoc--get-doc-at-point-1 ()
+  (skip-unless (executable-find python-tests-shell-interpreter))
+  (python-tests-with-temp-buffer-with-shell
+   "
+import time
+"
+   (let ((inhibit-message t))
+     (python-shell-send-buffer)
+     (python-tests-shell-wait-for-prompt)
+     (python-tests-look-at "time")
+     (should (python-eldoc--get-doc-at-point)))))
+
+(ert-deftest python-eldoc--get-doc-at-point-while-running-1 ()
+  "Should not get documentation when a program is running in the Shell buffer."
+  (skip-unless (executable-find python-tests-shell-interpreter))
+  (python-tests-with-temp-buffer-with-shell
+   "
+import time
+
+time.sleep(3)
+"
+   (let ((inhibit-message t))
+     (python-shell-send-buffer)
+     (python-tests-look-at "time")
+     (should-not (with-timeout (1 t) (python-eldoc--get-doc-at-point))))))
+
 
 ;;; Imenu
 
@@ -5485,6 +5592,23 @@ else:
      (equal (list (python-tests-look-at "else:" -1 t))
             (python-info-dedenter-opening-block-positions)))))
 
+(ert-deftest python-info-dedenter-opening-block-positions-6 ()
+  "Test multiline block start."
+  (python-tests-with-temp-buffer
+   "
+def func():
+    if (
+        cond1 or
+        cond2
+    ):
+        something()
+        else
+"
+   (python-tests-look-at "else\n")
+    (should
+     (equal (list (python-tests-look-at "if (" -1 t))
+            (python-info-dedenter-opening-block-positions)))))
+
 (ert-deftest python-info-dedenter-opening-block-message-1 ()
   "Test dedenters inside strings are ignored."
   (python-tests-with-temp-buffer
diff --git a/test/lisp/progmodes/ruby-mode-resources/ruby.rb 
b/test/lisp/progmodes/ruby-mode-resources/ruby.rb
index 0c206b1e0c..f39489071e 100644
--- a/test/lisp/progmodes/ruby-mode-resources/ruby.rb
+++ b/test/lisp/progmodes/ruby-mode-resources/ruby.rb
@@ -177,11 +177,11 @@ qux :+,
 b = $:
 c = ??
 
-# Example from http://www.ruby-doc.org/docs/ProgrammingRuby/html/language.html
+# Example from https://ruby-doc.com/docs/ProgrammingRuby/
 d = 4 + 5 +      # no '\' needed
     6 + 7
 
-# Example from http://www.ruby-doc.org/docs/ProgrammingRuby/html/language.html
+# Example from https://www.ruby-doc.org/docs/ProgrammingRuby/
 e = 8 + 9   \
     + 10         # '\' needed
 
diff --git a/test/lisp/simple-tests.el b/test/lisp/simple-tests.el
index 97f425f6f4..d067f3e586 100644
--- a/test/lisp/simple-tests.el
+++ b/test/lisp/simple-tests.el
@@ -73,6 +73,29 @@
     (should (= (count-lines (point) (point)) 0))))
 
 
+;;; `execute-extended-command'
+
+(ert-deftest simple-execute-extended-command--shorter ()
+  ;; This test can be flaky with completion frameworks other than the
+  ;; default, so just skip it in interactive sessions.
+  (skip-unless noninteractive)
+  (should (equal (execute-extended-command--shorter
+                  "display-line-numbers-mode"
+                  "display-line")
+                 "di-n")))
+
+(ert-deftest simple-execute-extended-command--describe-binding-msg ()
+  (should (equal (execute-extended-command--describe-binding-msg
+                  'foo "m" nil)
+                 "You can run the command ‘foo’ with m"))
+  (should (equal (execute-extended-command--describe-binding-msg
+                  'foo [14] nil)
+                 "You can run the command ‘foo’ with C-n"))
+  (should (equal (execute-extended-command--describe-binding-msg
+                  'display-line-numbers-mode nil "di-n")
+                 "You can run the command ‘display-line-numbers-mode’ with M-x 
di-n")))
+
+
 ;;; `transpose-sexps'
 (defmacro simple-test--transpositions (&rest body)
   (declare (indent 0)
diff --git a/test/lisp/subr-tests.el b/test/lisp/subr-tests.el
index 347981e818..cc9610cd39 100644
--- a/test/lisp/subr-tests.el
+++ b/test/lisp/subr-tests.el
@@ -1139,7 +1139,10 @@ final or penultimate step during initialization."))
   (should-not (plistp '(1 . 2)))
   (should (plistp '(1 2 3 4)))
   (should-not (plistp '(1 2 3)))
-  (should-not (plistp '(1 2 3 . 4))))
+  (should-not (plistp '(1 2 3 . 4)))
+  (let ((cycle (list 1 2 3)))
+    (nconc cycle cycle)
+    (should-not (plistp cycle))))
 
 (defun subr-tests--butlast-ref (list &optional n)
   "Reference implementation of `butlast'."
diff --git a/test/lisp/thingatpt-tests.el b/test/lisp/thingatpt-tests.el
index b6d0b1446a..67dd00104b 100644
--- a/test/lisp/thingatpt-tests.el
+++ b/test/lisp/thingatpt-tests.el
@@ -44,6 +44,9 @@
     ;; Non alphanumeric characters can be found in URIs
     ("ftp://example.net/~foo!;#bar=baz&goo=bob"; 3 url 
"ftp://example.net/~foo!;#bar=baz&goo=bob";)
     ("bzr+ssh://user@example.net:5/a%20d,5" 34 url 
"bzr+ssh://user@example.net:5/a%20d,5")
+    ;; IPv6 brackets enclosed in [markup]
+    ("[http://[::1]:8000/foo]"; 10 url "http://[::1]:8000/foo";)
+    ("[http://[fe08::7:8%eth0]]"; 10 url "http://[fe08::7:8%eth0]";)
     ;; <url:...> markup
     ("Url: <url:foo://1.example.com>..." 8 url "foo://1.example.com")
     ("Url: <url:foo://2.example.com>..." 30 url "foo://2.example.com")
diff --git a/test/lisp/time-stamp-tests.el b/test/lisp/time-stamp-tests.el
index 55e37b71d8..1b5ef04436 100644
--- a/test/lisp/time-stamp-tests.el
+++ b/test/lisp/time-stamp-tests.el
@@ -89,11 +89,11 @@
 (iter-defun time-stamp-test-pattern-sequential ()
   "Iterate through each possibility for a part of `time-stamp-pattern'."
   (let ((pattern-value-parts
-         '(("4/" "10/" "-4/" "0/" "")                     ;0: line limit
+         '(("4/" "10/" "-9/" "0/" "")                     ;0: line limit
            ("stamp<" "")                                  ;1: start
-           ("%-d" "%_H" "%^a" "%#Z" "%:A" "%02H" "%%" "") ;2: format part 1
+           ("%-d" "%_H" "%^a" "%#Z" "%:A" "%09z" "%%" "") ;2: format part 1
            (" " "x" ":" "\n" "")                          ;3: format part 2
-           ("%-d" "%_H" "%^a" "%#Z" "%:A" "%02H" "%%")    ;4: format part 3
+           ("%-d" "%_H" "%^a" "%#Z" "%:A" "%09z" "%%")    ;4: format part 3
            (">end" ""))))                                 ;5: end
     (dotimes (cur (length pattern-value-parts))
       (dotimes (cur-index (length (nth cur pattern-value-parts)))
@@ -118,7 +118,7 @@
 (iter-defun time-stamp-test-pattern-multiply ()
   "Iterate through every combination of parts of `time-stamp-pattern'."
   (let ((line-limit-values '("" "4/"))
-        (start-values '("" "stamp<"))
+        (start-values '("" "/stamp/"))
         (format-values '("%%" "%m"))
         (end-values '("" ">end")))
     ;; yield all combinations of the above
diff --git a/test/manual/image-circular-tests.el 
b/test/manual/image-circular-tests.el
index 1299970f82..d2187cbbad 100644
--- a/test/manual/image-circular-tests.el
+++ b/test/manual/image-circular-tests.el
@@ -27,8 +27,11 @@
 
 (require 'ert)
 
+(declare-function image-size "image.c" (spec &optional pixels frame))
+
 (ert-deftest image-test-duplicate-keywords ()
   "Test that duplicate keywords in an image spec lead to rejection."
+  (skip-unless (display-images-p))
   (should-error (image-size `(image :type xbm :type xbm
                                     :data-width 1 :data-height 1
                                     :data ,(bool-vector t))
@@ -36,33 +39,37 @@
 
 (ert-deftest image-test-circular-plist ()
   "Test that a circular image spec is rejected."
-  (should-error
-   (let ((l `(image :type xbm :data-width 1 :data-height 1
-                    :data ,(bool-vector t))))
-     (setcdr (last l) '#1=(:invalid . #1#))
-     (image-size l t))))
+  (skip-unless (display-images-p))
+  (let ((spec `(image :type xbm :data-width 1 :data-height 1
+                      :data ,(bool-vector t)
+                      . ,'#1=(:invalid . #1#))))
+    (should-error (image-size spec t))))
 
 (ert-deftest image-test-:type-property-value ()
   "Test that :type is allowed as a property value in an image spec."
+  (skip-unless (display-images-p))
   (should (equal (image-size `(image :dummy :type :type xbm
                                      :data-width 1 :data-height 1
                                      :data ,(bool-vector t))
                              t)
-                 (cons 1 1))))
+                 '(1 . 1))))
 
 (ert-deftest image-test-circular-specs ()
-  "Test that circular image spec property values do not cause infinite 
recursion."
-  (should
-   (let* ((circ1 (cons :dummy nil))
-          (circ2 (cons :dummy nil))
-          (spec1 `(image :type xbm :data-width 1 :data-height 1
-                         :data ,(bool-vector 1) :ignored ,circ1))
-          (spec2 `(image :type xbm :data-width 1 :data-height 1
+  "Test with circular image spec property values.
+In particular, test that they do not cause infinite recursion."
+  :expected-result :failed ;; FIXME: bug#36403#63.
+  (skip-unless (display-images-p))
+  ;; Two copies needed to warm up image cache.
+  (let* ((circ1 (list :dummy))
+         (circ2 (list :dummy))
+         (spec1 `(image :type xbm :data-width 1 :data-height 1
+                        :data ,(bool-vector 1) :ignored ,circ1))
+         (spec2 `(image :type xbm :data-width 1 :data-height 1
                         :data ,(bool-vector 1) :ignored ,circ2)))
-     (setcdr circ1 circ1)
-     (setcdr circ2 circ2)
-     (and (equal (image-size spec1 t) (cons 1 1))
-          (equal (image-size spec2 t) (cons 1 1))))))
+    (setcdr circ1 circ1)
+    (setcdr circ2 circ2)
+    (should (equal (image-size spec1 t) '(1 . 1)))
+    (should (equal (image-size spec2 t) '(1 . 1)))))
 
 (provide 'image-circular-tests)
 ;;; image-circular-tests.el ends here.
diff --git a/test/manual/noverlay/.gitignore b/test/manual/noverlay/.gitignore
new file mode 100644
index 0000000000..ca7fc452b8
--- /dev/null
+++ b/test/manual/noverlay/.gitignore
@@ -0,0 +1 @@
+itree-tests
diff --git a/test/manual/noverlay/Makefile.in b/test/manual/noverlay/Makefile.in
new file mode 100644
index 0000000000..3c8dba1ce1
--- /dev/null
+++ b/test/manual/noverlay/Makefile.in
@@ -0,0 +1,47 @@
+### @configure_input@
+
+# Copyright (C) 2017-2022 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/>.
+
+PROGRAM = itree-tests
+PACKAGES = check
+top_srcdir = @top_srcdir@
+top_builddir = @top_builddir@
+CPPFLAGS += -I $(top_srcdir)/src
+CFLAGS += -O0 -g3 $(shell pkg-config --cflags $(PACKAGES))
+LDLIBS += $(shell pkg-config --libs $(PACKAGES)) -lm
+OBJECTS = itree-tests.o
+CC = gcc
+EMACS ?= $(top_builddir)/src/emacs
+
+.PHONY: all check clean distclean perf
+
+all: check
+
+check: $(PROGRAM)
+       ./check-sanitize.sh ./$(PROGRAM)
+
+itree-tests.o: emacs-compat.h $(top_srcdir)/src/itree.c 
$(top_srcdir)/src/itree.h
+
+perf:
+       -$(EMACS) -Q -l ./overlay-perf.el -f perf-run-batch
+
+clean:
+       rm -f -- $(OBJECTS) $(PROGRAM)
+
+distclean: clean
+       rm -f -- Makefile
diff --git a/test/manual/noverlay/check-sanitize.sh 
b/test/manual/noverlay/check-sanitize.sh
new file mode 100755
index 0000000000..9a67818dc8
--- /dev/null
+++ b/test/manual/noverlay/check-sanitize.sh
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+### check-sanitize.sh - strip confusing parts of Check error output
+
+## Copyright (C) 2017-2022 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/>.
+
+set -o pipefail
+
+prog=$1
+shift
+
+[ -z "$prog" ] && {
+    echo "usage:$(basename $0) CHECK_PROGRAM";
+    exit 1;
+}
+
+# FIXME: This would be unnecessary if
+# compilation-error-regexp-alist-alist understood libcheck OOTB.
+"$prog" "$@" | sed -e 
's/^\([^:]\+\):\([0-9]\+\):\([PFE]\):\([^:]*\):\([^:]*\):[^:]*:\(.*\)/\1:\2:\3:\4:\5:\6/'
diff --git a/test/manual/noverlay/emacs-compat.h 
b/test/manual/noverlay/emacs-compat.h
new file mode 100644
index 0000000000..d2448b12db
--- /dev/null
+++ b/test/manual/noverlay/emacs-compat.h
@@ -0,0 +1,76 @@
+/* Mock necessary parts of lisp.h.
+
+Copyright (C) 2017-2022 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/>.  */
+
+#ifndef TEST_COMPAT_H
+#define TEST_COMPAT_H
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+typedef int Lisp_Object;
+
+void *
+xmalloc (size_t size)
+{
+  return malloc (size);
+}
+
+void
+xfree (void *ptr)
+{
+  free (ptr);
+}
+
+void *
+xrealloc (void *block, size_t size)
+{
+  return realloc (block, size);
+}
+
+void
+emacs_abort ()
+{
+  fprintf (stderr, "Aborting...\n");
+  exit (EXIT_FAILURE);
+}
+
+#ifndef eassert
+#define eassert(cond)                                                   \
+  do {                                                                  \
+    if (! (cond)) {                                                     \
+      fprintf (stderr, "%s:%d:eassert condition failed: %s\n",          \
+               __FILE__, __LINE__ , # cond);                            \
+      exit (EXIT_FAILURE);                                              \
+    }                                                                   \
+  } while (0)
+#endif
+
+#ifndef eassume
+#define eassume eassert
+#endif
+
+#ifndef max
+#define max(x,y) ((x) >= (y) ? (x) : (y))
+#endif
+#ifndef min
+#define min(x,y) ((x) <= (y) ? (x) : (y))
+#endif
+
+#endif /* TEST_COMPAT_H */
diff --git a/test/manual/noverlay/itree-tests.c 
b/test/manual/noverlay/itree-tests.c
new file mode 100644
index 0000000000..278e65f9bf
--- /dev/null
+++ b/test/manual/noverlay/itree-tests.c
@@ -0,0 +1,1289 @@
+/* Test the interval data-structure in itree.c.
+
+Copyright (c) 2017-2022 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/>.  */
+
+#include <config.h>
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include <check.h>
+#include "emacs-compat.h"
+
+#define EMACS_LISP_H            /* lisp.h inclusion guard */
+#define ITREE_DEBUG 1
+#define ITREE_TESTING
+#include "itree.c"
+
+/* Globals.  */
+
+static struct itree_tree tree;
+static struct itree_node A, B, C, D, E;
+static struct itree_node N_05, N_10, N_15, N_20, N_30, N_40;
+static struct itree_node N_50, N_70, N_80, N_90, N_85, N_95;
+
+/* Basic tests of the itree_tree data-structure.  */
+
+/* 
+===================================================================================+
+ * | Insert
+ * 
+===================================================================================+
 */
+
+/* The graphs below display the trees after each insertion (as they
+   should be).  See the source code for the different cases
+   applied.  */
+
+static void
+test_insert1_setup (void)
+{
+  enum { N = 6 };
+  const int values[N] = {50, 30, 20, 10, 15, 5};
+  struct itree_node *nodes[N] = {&N_50, &N_30, &N_20, &N_10, &N_15, &N_05};
+  interval_tree_init (&tree);
+  for (int i = 0; i < N; ++i)
+    {
+      nodes[i]->begin = nodes[i]->end = values[i];
+      nodes[i]->otick = tree.otick;
+    }
+}
+
+START_TEST (test_insert_1)
+{
+  /*
+   *                 [50]
+   */
+
+  interval_tree_insert (&tree, &N_50);
+  ck_assert (! N_50.red);
+  ck_assert_ptr_eq (&N_50, tree.root);
+}
+END_TEST
+
+START_TEST (test_insert_2)
+{
+  /*
+   *                 [50]
+   *                /
+   *              (30)
+   */
+
+  interval_tree_insert (&tree, &N_50);
+  interval_tree_insert (&tree, &N_30);
+  ck_assert (! N_50.red);
+  ck_assert (N_30.red);
+  ck_assert_ptr_eq (&N_50, tree.root);
+  ck_assert_ptr_eq (N_30.parent, &N_50);
+  ck_assert_ptr_eq (N_50.left, &N_30);
+  ck_assert_ptr_null (N_50.right);
+  ck_assert_ptr_null (N_30.left);
+  ck_assert_ptr_null (N_30.right);
+}
+END_TEST
+
+START_TEST (test_insert_3)
+{
+  /* case 3.a
+   *                [30]
+   *               /    \
+   *             (20)   (50)
+   */
+
+  interval_tree_insert (&tree, &N_50);
+  interval_tree_insert (&tree, &N_30);
+  interval_tree_insert (&tree, &N_20);
+  ck_assert (N_50.red);
+  ck_assert (! N_30.red);
+  ck_assert (N_20.red);
+  ck_assert_ptr_eq (&N_30, tree.root);
+  ck_assert_ptr_eq (N_50.parent, &N_30);
+  ck_assert_ptr_eq (N_30.right, &N_50);
+  ck_assert_ptr_eq (N_30.left, &N_20);
+  ck_assert_ptr_null (N_20.left);
+  ck_assert_ptr_null (N_20.right);
+  ck_assert_ptr_eq (N_20.parent, &N_30);
+}
+END_TEST
+
+START_TEST (test_insert_4)
+{
+  /* 1.a
+   *                [30]
+   *               /    \
+   *             [20]   [50]
+   *             /
+   *           (10)
+   */
+
+  interval_tree_insert (&tree, &N_50);
+  interval_tree_insert (&tree, &N_30);
+  interval_tree_insert (&tree, &N_20);
+  interval_tree_insert (&tree, &N_10);
+  ck_assert (! N_50.red);
+  ck_assert (! N_30.red);
+  ck_assert (! N_20.red);
+  ck_assert (N_10.red);
+  ck_assert_ptr_eq (&N_30, tree.root);
+  ck_assert_ptr_eq (N_50.parent, &N_30);
+  ck_assert_ptr_eq (N_30.right, &N_50);
+  ck_assert_ptr_eq (N_30.left, &N_20);
+  ck_assert_ptr_eq (N_20.left, &N_10);
+  ck_assert_ptr_null (N_20.right);
+  ck_assert_ptr_eq (N_20.parent, &N_30);
+  ck_assert_ptr_eq (N_10.parent, &N_20);
+  ck_assert_ptr_eq (N_20.left, &N_10);
+  ck_assert_ptr_null (N_10.right);
+}
+END_TEST
+
+START_TEST (test_insert_5)
+{
+  /* 2.a
+   *                [30]
+   *               /    \
+   *             [15]   [50]
+   *             /  \
+   *           (10) (20)
+   */
+
+  interval_tree_insert (&tree, &N_50);
+  interval_tree_insert (&tree, &N_30);
+  interval_tree_insert (&tree, &N_20);
+  interval_tree_insert (&tree, &N_10);
+  interval_tree_insert (&tree, &N_15);
+  ck_assert (! N_50.red);
+  ck_assert (! N_30.red);
+  ck_assert (N_20.red);
+  ck_assert (N_10.red);
+  ck_assert (! N_15.red);
+  ck_assert_ptr_eq (&N_30, tree.root);
+  ck_assert_ptr_eq (N_50.parent, &N_30);
+  ck_assert_ptr_eq (N_30.right, &N_50);
+  ck_assert_ptr_eq (N_30.left, &N_15);
+  ck_assert_ptr_null (N_20.left);
+  ck_assert_ptr_null (N_20.right);
+  ck_assert_ptr_eq (N_20.parent, &N_15);
+  ck_assert_ptr_eq (N_10.parent, &N_15);
+  ck_assert_ptr_null (N_20.left);
+  ck_assert_ptr_null (N_10.right);
+  ck_assert_ptr_eq (N_15.right, &N_20);
+  ck_assert_ptr_eq (N_15.left, &N_10);
+  ck_assert_ptr_eq (N_15.parent, &N_30);
+}
+END_TEST
+
+START_TEST (test_insert_6)
+{
+  /* 1.a
+   *                [30]
+   *               /    \
+   *             (15)   [50]
+   *             /  \
+   *           [10] [20]
+   *           /
+   *         (5)
+   */
+
+  interval_tree_insert (&tree, &N_50);
+  interval_tree_insert (&tree, &N_30);
+  interval_tree_insert (&tree, &N_20);
+  interval_tree_insert (&tree, &N_10);
+  interval_tree_insert (&tree, &N_15);
+  interval_tree_insert (&tree, &N_05);
+  ck_assert (! N_50.red);
+  ck_assert (! N_30.red);
+  ck_assert (! N_20.red);
+  ck_assert (! N_10.red);
+  ck_assert (N_15.red);
+  ck_assert (N_05.red);
+  ck_assert_ptr_eq (&N_30, tree.root);
+  ck_assert_ptr_eq (N_50.parent, &N_30);
+  ck_assert_ptr_eq (N_30.right, &N_50);
+  ck_assert_ptr_eq (N_30.left, &N_15);
+  ck_assert_ptr_null (N_20.left);
+  ck_assert_ptr_null (N_20.right);
+  ck_assert_ptr_eq (N_20.parent, &N_15);
+  ck_assert_ptr_eq (N_10.parent, &N_15);
+  ck_assert_ptr_null (N_20.left);
+  ck_assert_ptr_null (N_10.right);
+  ck_assert_ptr_eq (N_15.right, &N_20);
+  ck_assert_ptr_eq (N_15.left, &N_10);
+  ck_assert_ptr_eq (N_15.parent, &N_30);
+  ck_assert_ptr_eq (N_05.parent, &N_10);
+  ck_assert_ptr_eq (N_10.left, &N_05);
+  ck_assert_ptr_null (N_05.right);
+}
+END_TEST
+
+
+
+/* These are the mirror cases to the above ones.  */
+
+static void
+test_insert2_setup (void)
+{
+  enum { N = 6 };
+  const int values[] = {50, 70, 80, 90, 85, 95};
+  struct itree_node *nodes[N] = {&N_50, &N_70, &N_80, &N_90, &N_85, &N_95};
+  interval_tree_init (&tree);
+  for (int i = 0; i < N; ++i)
+    {
+      nodes[i]->begin = nodes[i]->end = values[i];
+      nodes[i]->otick = tree.otick;
+    }
+}
+
+START_TEST (test_insert_7)
+{
+  /*
+   *                 [50]
+   */
+
+  interval_tree_insert (&tree, &N_50);
+  ck_assert (! N_50.red);
+  ck_assert_ptr_eq (&N_50, tree.root);
+}
+END_TEST
+
+START_TEST (test_insert_8)
+{
+  /*
+   *                 [50]
+   *                    \
+   *                   (70)
+   */
+
+  interval_tree_insert (&tree, &N_50);
+  interval_tree_insert (&tree, &N_70);
+  ck_assert (! N_50.red);
+  ck_assert (N_70.red);
+  ck_assert_ptr_eq (&N_50, tree.root);
+  ck_assert_ptr_eq (N_70.parent, &N_50);
+  ck_assert_ptr_eq (N_50.right, &N_70);
+  ck_assert_ptr_null (N_50.left);
+  ck_assert_ptr_null (N_70.right);
+  ck_assert_ptr_null (N_70.left);
+}
+END_TEST
+
+START_TEST (test_insert_9)
+{
+  /* 3.a
+   *                [70]
+   *               /    \
+   *             (50)   (80)
+   */
+
+  interval_tree_insert (&tree, &N_50);
+  interval_tree_insert (&tree, &N_70);
+  interval_tree_insert (&tree, &N_80);
+  ck_assert (N_50.red);
+  ck_assert (! N_70.red);
+  ck_assert (N_80.red);
+  ck_assert_ptr_eq (&N_70, tree.root);
+  ck_assert_ptr_eq (N_50.parent, &N_70);
+  ck_assert_ptr_eq (N_70.right, &N_80);
+  ck_assert_ptr_eq (N_70.left, &N_50);
+  ck_assert_ptr_null (N_80.right);
+  ck_assert_ptr_null (N_80.left);
+  ck_assert_ptr_eq (N_80.parent, &N_70);
+}
+END_TEST
+
+START_TEST (test_insert_10)
+{
+  /* 1.b
+   *                [70]
+   *               /    \
+   *             [50]   [80]
+   *                      \
+   *                      (90)
+   */
+
+  interval_tree_insert (&tree, &N_50);
+  interval_tree_insert (&tree, &N_70);
+  interval_tree_insert (&tree, &N_80);
+  interval_tree_insert (&tree, &N_90);
+  ck_assert (! N_50.red);
+  ck_assert (! N_70.red);
+  ck_assert (! N_80.red);
+  ck_assert (N_90.red);
+  ck_assert_ptr_eq (&N_70, tree.root);
+  ck_assert_ptr_eq (N_50.parent, &N_70);
+  ck_assert_ptr_eq (N_70.right, &N_80);
+  ck_assert_ptr_eq (N_70.left, &N_50);
+  ck_assert_ptr_eq (N_80.right, &N_90);
+  ck_assert_ptr_null (N_80.left);
+  ck_assert_ptr_eq (N_80.parent, &N_70);
+  ck_assert_ptr_eq (N_90.parent, &N_80);
+  ck_assert_ptr_eq (N_80.right, &N_90);
+  ck_assert_ptr_null (N_90.left);
+}
+END_TEST
+
+START_TEST (test_insert_11)
+{
+  /* 2.b
+   *                [70]
+   *               /    \
+   *             [50]   [85]
+   *                    /  \
+   *                  (80) (90)
+   */
+
+  interval_tree_insert (&tree, &N_50);
+  interval_tree_insert (&tree, &N_70);
+  interval_tree_insert (&tree, &N_80);
+  interval_tree_insert (&tree, &N_90);
+  interval_tree_insert (&tree, &N_85);
+  ck_assert (! N_50.red);
+  ck_assert (! N_70.red);
+  ck_assert (N_80.red);
+  ck_assert (N_90.red);
+  ck_assert (! N_85.red);
+  ck_assert_ptr_eq (&N_70, tree.root);
+  ck_assert_ptr_eq (N_50.parent, &N_70);
+  ck_assert_ptr_eq (N_70.right, &N_85);
+  ck_assert_ptr_eq (N_70.left, &N_50);
+  ck_assert_ptr_null (N_80.right);
+  ck_assert_ptr_null (N_80.left);
+  ck_assert_ptr_eq (N_80.parent, &N_85);
+  ck_assert_ptr_eq (N_90.parent, &N_85);
+  ck_assert_ptr_null (N_80.right);
+  ck_assert_ptr_null (N_90.left);
+  ck_assert_ptr_eq (N_85.right, &N_90);
+  ck_assert_ptr_eq (N_85.left, &N_80);
+  ck_assert_ptr_eq (N_85.parent, &N_70);
+
+}
+END_TEST
+
+START_TEST (test_insert_12)
+{
+  /* 1.b
+   *                [70]
+   *               /    \
+   *             [50]   (85)
+   *                    /  \
+   *                  [80] [90]
+   *                         \
+   *                        (95)
+   */
+
+  interval_tree_insert (&tree, &N_50);
+  interval_tree_insert (&tree, &N_70);
+  interval_tree_insert (&tree, &N_80);
+  interval_tree_insert (&tree, &N_90);
+  interval_tree_insert (&tree, &N_85);
+  interval_tree_insert (&tree, &N_95);
+  ck_assert (! N_50.red);
+  ck_assert (! N_70.red);
+  ck_assert (! N_80.red);
+  ck_assert (! N_90.red);
+  ck_assert (N_85.red);
+  ck_assert (N_95.red);
+  ck_assert_ptr_eq (&N_70, tree.root);
+  ck_assert_ptr_eq (N_50.parent, &N_70);
+  ck_assert_ptr_eq (N_70.right, &N_85);
+  ck_assert_ptr_eq (N_70.left, &N_50);
+  ck_assert_ptr_null (N_80.right);
+  ck_assert_ptr_null (N_80.left);
+  ck_assert_ptr_eq (N_80.parent, &N_85);
+  ck_assert_ptr_eq (N_90.parent, &N_85);
+  ck_assert_ptr_null (N_80.right);
+  ck_assert_ptr_null (N_90.left);
+  ck_assert_ptr_eq (N_85.right, &N_90);
+  ck_assert_ptr_eq (N_85.left, &N_80);
+  ck_assert_ptr_eq (N_85.parent, &N_70);
+  ck_assert_ptr_eq (N_95.parent, &N_90);
+  ck_assert_ptr_eq (N_90.right, &N_95);
+  ck_assert_ptr_null (N_95.left);
+}
+END_TEST
+
+START_TEST (test_insert_13)
+{
+  enum { N = 4 };
+  const int values[N] = {10, 20, 30, 40};
+  struct itree_node *nodes[N] = {&N_10, &N_20, &N_30, &N_40};
+  interval_tree_init (&tree);
+  for (int i = 0; i < N; ++i)
+    itree_insert (&tree, nodes[i], values[i], values[i]);
+
+  ck_assert_ptr_eq (tree.root, &N_20);
+  ck_assert_ptr_eq (N_20.left, &N_10);
+  ck_assert_ptr_eq (N_20.right, &N_30);
+  ck_assert_ptr_eq (N_30.right, &N_40);
+  ck_assert (! N_10.red);
+  ck_assert (! N_20.red);
+  ck_assert (! N_30.red);
+  ck_assert (N_40.red);
+}
+END_TEST
+
+START_TEST (test_insert_14)
+{
+  enum { N = 3 };
+  struct itree_node nodes[N];
+  interval_tree_init (&tree);
+
+  for (int i = 0; i < N; ++i)
+    itree_insert (&tree, &nodes[i], 10, 10);
+  for (int i = 0; i < N; ++i)
+    ck_assert (interval_tree_contains (&tree, &nodes[i]));
+}
+END_TEST
+
+
+/* 
+===================================================================================+
+ * | Remove
+ * 
+===================================================================================+
 */
+
+/* Creating proper test trees for the formal tests via insertions is
+   way too tedious, so we just fake it and only test the
+   fix-routine.  */
+static void
+test_remove1_setup (void)
+{
+  interval_tree_init (&tree);
+  tree.root = &B;
+  A.parent = &B; B.parent = NULL; C.parent = &D; D.parent = &B; E.parent = &D;
+  A.left = A.right = C.left = C.right = E.left = E.right = NULL;
+  B.left = &A; B.right = &D;
+  D.left = &C; D.right = &E;
+  A.offset = B.offset = C.offset = D.offset = E.offset = 0;
+  A.otick = B.otick = C.otick = D.otick = E.otick = tree.otick;
+}
+
+/* 1.a -> 2.a
+ *                [B]
+ *               /    \
+ *             [A]    (D)
+ *                    /  \
+ *                 [C]   [E]
+ */
+
+START_TEST (test_remove_1)
+{
+  B.red = A.red = C.red = E.red = false;
+  D.red = true;
+  interval_tree_remove_fix (&tree, &A, &B);
+
+  ck_assert (! A.red);
+  ck_assert (! B.red);
+  ck_assert (C.red);
+  ck_assert (! D.red);
+  ck_assert (! E.red);
+  ck_assert_ptr_eq (A.parent, &B);
+  ck_assert_ptr_eq (B.left, &A);
+  ck_assert_ptr_eq (B.right, &C);
+  ck_assert_ptr_eq (C.parent, &B);
+  ck_assert_ptr_eq (E.parent, &D);
+  ck_assert_ptr_eq (D.right, &E);
+  ck_assert_ptr_eq (D.left, &B);
+  ck_assert_ptr_eq (tree.root, &D);
+}
+END_TEST
+
+/* 2.a */
+START_TEST (test_remove_2)
+{
+  B.red = D.red = A.red = C.red = E.red = false;
+  interval_tree_remove_fix (&tree, &A, &B);
+
+  ck_assert (! A.red);
+  ck_assert (! B.red);
+  ck_assert (! C.red);
+  ck_assert (D.red);
+  ck_assert (! E.red);
+  ck_assert_ptr_eq (A.parent, &B);
+  ck_assert_ptr_eq (B.left, &A);
+  ck_assert_ptr_eq (B.right, &D);
+  ck_assert_ptr_eq (C.parent, &D);
+  ck_assert_ptr_eq (E.parent, &D);
+  ck_assert_ptr_eq (tree.root, &B);
+}
+END_TEST
+
+/* 3.a -> 4.a */
+START_TEST (test_remove_3)
+{
+  D.red = A.red = E.red = false;
+  B.red = C.red = true;
+  interval_tree_remove_fix (&tree, &A, &B);
+
+  ck_assert (! A.red);
+  ck_assert (! B.red);
+  ck_assert (! C.red);
+  ck_assert (! D.red);
+  ck_assert (! E.red);
+  ck_assert_ptr_eq (A.parent, &B);
+  ck_assert_ptr_eq (B.left, &A);
+  ck_assert_ptr_null (B.right);
+  ck_assert_ptr_eq (&C, tree.root);
+  ck_assert_ptr_eq (C.left, &B);
+  ck_assert_ptr_eq (C.right, &D);
+  ck_assert_ptr_eq (E.parent, &D);
+  ck_assert_ptr_null (D.left);
+}
+END_TEST
+
+/* 4.a */
+START_TEST (test_remove_4)
+{
+  B.red = C.red = E.red = true;
+  A.red = D.red = false;
+  interval_tree_remove_fix (&tree, &A, &B);
+
+  ck_assert (! A.red);
+  ck_assert (! B.red);
+  ck_assert (C.red);
+  ck_assert (! D.red);
+  ck_assert (! E.red);
+  ck_assert_ptr_eq (A.parent, &B);
+  ck_assert_ptr_eq (B.left, &A);
+  ck_assert_ptr_eq (B.right, &C);
+  ck_assert_ptr_eq (C.parent, &B);
+  ck_assert_ptr_eq (E.parent, &D);
+  ck_assert_ptr_eq (tree.root, &D);
+}
+END_TEST
+
+
+
+/* These are the mirrored cases.  */
+
+static void
+test_remove2_setup (void)
+{
+  interval_tree_init (&tree);
+  tree.root = &B;
+  A.parent = &B; B.parent = NULL; C.parent = &D; D.parent = &B; E.parent = &D;
+  A.right = A.left = C.right = C.left = E.right = E.left = NULL;
+  B.right = &A; B.left = &D;
+  D.right = &C; D.left = &E;
+}
+
+/* 1.b -> 2.b
+ *                [B]
+ *               /    \
+ *             [A]    (D)
+ *                    /  \
+ *                 [C]   [E]
+ */
+
+START_TEST (test_remove_5)
+{
+  B.red = A.red = C.red = E.red = false;
+  D.red = true;
+  interval_tree_remove_fix (&tree, &A, &B);
+
+  ck_assert (! A.red);
+  ck_assert (! B.red);
+  ck_assert (C.red);
+  ck_assert (! D.red);
+  ck_assert (! E.red);
+  ck_assert_ptr_eq (A.parent, &B);
+  ck_assert_ptr_eq (B.right, &A);
+  ck_assert_ptr_eq (B.left, &C);
+  ck_assert_ptr_eq (C.parent, &B);
+  ck_assert_ptr_eq (E.parent, &D);
+  ck_assert_ptr_eq (D.left, &E);
+  ck_assert_ptr_eq (D.right, &B);
+  ck_assert_ptr_eq (tree.root, &D);
+}
+END_TEST
+
+/* 2.b */
+START_TEST (test_remove_6)
+{
+  B.red = D.red = A.red = C.red = E.red = false;
+  interval_tree_remove_fix (&tree, &A, &B);
+
+  ck_assert (! A.red);
+  ck_assert (! B.red);
+  ck_assert (! C.red);
+  ck_assert (D.red);
+  ck_assert (! E.red);
+  ck_assert_ptr_eq (A.parent, &B);
+  ck_assert_ptr_eq (B.right, &A);
+  ck_assert_ptr_eq (B.left, &D);
+  ck_assert_ptr_eq (C.parent, &D);
+  ck_assert_ptr_eq (E.parent, &D);
+  ck_assert_ptr_eq (tree.root, &B);
+}
+END_TEST
+
+/* 3.b -> 4.b */
+START_TEST (test_remove_7)
+{
+  D.red = A.red = E.red = false;
+  B.red = C.red = true;
+  interval_tree_remove_fix (&tree, &A, &B);
+
+  ck_assert (! A.red);
+  ck_assert (! B.red);
+  ck_assert (! C.red);
+  ck_assert (! D.red);
+  ck_assert (! E.red);
+  ck_assert_ptr_eq (A.parent, &B);
+  ck_assert_ptr_eq (B.right, &A);
+  ck_assert_ptr_null (B.left);
+  ck_assert_ptr_eq (&C, tree.root);
+  ck_assert_ptr_eq (C.right, &B);
+  ck_assert_ptr_eq (C.left, &D);
+  ck_assert_ptr_eq (E.parent, &D);
+  ck_assert_ptr_null (D.right);
+}
+END_TEST
+
+/* 4.b */
+START_TEST (test_remove_8)
+{
+  B.red = C.red = E.red = true;
+  A.red = D.red = false;
+  interval_tree_remove_fix (&tree, &A, &B);
+
+  ck_assert (! A.red);
+  ck_assert (! B.red);
+  ck_assert (C.red);
+  ck_assert (! D.red);
+  ck_assert (! E.red);
+  ck_assert_ptr_eq (A.parent, &B);
+  ck_assert_ptr_eq (B.right, &A);
+  ck_assert_ptr_eq (B.left, &C);
+  ck_assert_ptr_eq (C.parent, &B);
+  ck_assert_ptr_eq (E.parent, &D);
+  ck_assert_ptr_eq (tree.root, &D);
+}
+END_TEST
+
+START_TEST (test_remove_9)
+{
+  enum { N = 4 };
+  const int values[N] = {10, 20, 30, 40};
+  struct itree_node *nodes[N] = {&N_10, &N_20, &N_30, &N_40};
+  interval_tree_init (&tree);
+  for (int i = 0; i < N; ++i)
+    itree_insert (&tree, nodes[i], values[i], values[i]);
+
+  ck_assert (tree.root == &N_20);
+  ck_assert (N_20.left == &N_10);
+  ck_assert (N_20.right == &N_30);
+  ck_assert (N_30.right == &N_40);
+  ck_assert (! N_20.red);
+  ck_assert (! N_10.red);
+  ck_assert (! N_30.red);
+  ck_assert (N_40.red);
+
+  itree_remove (&tree, &N_10);
+
+  ck_assert_ptr_eq (tree.root, &N_30);
+  ck_assert_ptr_null (N_30.parent);
+  ck_assert_ptr_eq (N_30.left, &N_20);
+  ck_assert_ptr_eq (N_30.right, &N_40);
+  ck_assert (! N_20.red);
+  ck_assert (! N_30.red);
+  ck_assert (! N_40.red);
+}
+END_TEST
+
+static void
+shuffle (int *index, int n)
+{
+  for (int i = n - 1; i >= 0; --i)
+    {
+      int j = random () % (i + 1);
+      int h = index[j];
+      index[j] = index[i];
+      index[i] = h;
+    }
+}
+
+START_TEST (test_remove_10)
+{
+  enum { N = 3 };
+  int index[N];
+  for (int i = 0; i < N; ++i)
+    index[i] = i;
+  srand (42);
+  shuffle (index, N);
+
+  interval_tree_init (&tree);
+  struct itree_node nodes[N];
+  for (int i = 0; i < N; ++i)
+    {
+      ptrdiff_t pos = (i + 1) * 10;
+      itree_insert (&tree, &nodes[index[i]], pos, pos + 1);
+    }
+
+  shuffle (index, N);
+  for (int i = 0; i < N; ++i)
+    {
+      ck_assert (interval_tree_contains (&tree, &nodes[index[i]]));
+      itree_remove (&tree, &nodes[index[i]]);
+    }
+  ck_assert_ptr_null (tree.root);
+  ck_assert_int_eq (tree.size, 0);
+}
+END_TEST
+
+
+/* 
+===================================================================================+
+ * | Generator
+ * 
+===================================================================================+
 */
+
+START_TEST (test_generator_1)
+{
+  struct itree_node node, *n;
+  struct itree_iterator *g;
+  interval_tree_init (&tree);
+
+  itree_insert (&tree, &node, 10, 20);
+  g = itree_iterator_start (&tree, 0, 30, ITREE_ASCENDING, NULL, 0);
+  n = itree_iterator_next (g);
+  ck_assert_ptr_eq (n, &node);
+  ck_assert_int_eq (n->begin, 10);
+  ck_assert_int_eq (n->end, 20);
+  ck_assert_ptr_null (itree_iterator_next (g));
+  ck_assert_ptr_null (itree_iterator_next (g));
+  ck_assert_ptr_null (itree_iterator_next (g));
+  itree_iterator_finish (g);
+
+  g = itree_iterator_start (&tree, 30, 50, ITREE_ASCENDING, NULL, 0);
+  ck_assert_ptr_null (itree_iterator_next (g));
+  ck_assert_ptr_null (itree_iterator_next (g));
+  ck_assert_ptr_null (itree_iterator_next (g));
+  itree_iterator_finish (g);
+}
+END_TEST
+
+static void
+test_check_generator (struct itree_tree *tree,
+                      ptrdiff_t begin, ptrdiff_t end,
+                      int n, ...)
+{
+  va_list ap;
+  struct itree_iterator *g =
+    itree_iterator_start (tree, begin, end, ITREE_ASCENDING, NULL, 0);
+
+  va_start (ap, n);
+  for (int i = 0; i < n; ++i)
+    {
+      struct itree_node *node = itree_iterator_next (g);
+      ck_assert_ptr_nonnull (node);
+      ck_assert_int_eq (node->begin, va_arg (ap, ptrdiff_t));
+    }
+  va_end (ap);
+  ck_assert_ptr_null (itree_iterator_next (g));
+  ck_assert_ptr_null (itree_iterator_next (g));
+  itree_iterator_finish (g);
+}
+
+START_TEST (test_generator_2)
+{
+  interval_tree_init (&tree);
+  struct itree_node nodes[3];
+  for (int i = 0; i < 3; ++i)
+    itree_insert (&tree, &nodes[i], 10 * (i + 1), 10 * (i + 2));
+
+  test_check_generator (&tree, 0, 50, 3,
+                        10, 20, 30);
+  test_check_generator (&tree, 0, 10, 0);
+  test_check_generator (&tree, 40, 50, 0);
+  test_check_generator (&tree, 15, 35, 3,
+                        10, 20, 30);
+  test_check_generator (&tree, -100, -50, 0);
+  test_check_generator (&tree, -100, -50, 0);
+  test_check_generator (&tree, 100, 50, 0);
+  test_check_generator (&tree, 100, 150, 0);
+  test_check_generator (&tree, 0, 0, 0);
+  test_check_generator (&tree, 40, 40, 0);
+  test_check_generator (&tree, 30, 30, 0);
+  test_check_generator (&tree, 35, 35, 1,
+                        30);
+}
+END_TEST
+
+static void
+test_create_tree (struct itree_node *nodes, int n, bool doshuffle)
+{
+  int *index = calloc (n, sizeof (int));
+  for (int i = 0; i < n; ++i)
+    index[i] = i;
+  if (doshuffle)
+    {
+      srand (42);
+      shuffle (index, n);
+    }
+
+  interval_tree_init (&tree);
+  for (int i = 0; i < n; ++i)
+    {
+      struct itree_node *node = &nodes[index[i]];
+      itree_insert (&tree, node, node->begin, node->end);
+    }
+  free (index);
+}
+
+START_TEST (test_generator_3)
+{
+  enum { N = 3 };
+  struct itree_node nodes[N] = {{.begin = 10, .end = 10},
+                                {.begin = 10, .end = 10},
+                                {.begin = 10, .end = 10}};
+  test_create_tree (nodes, N, true);
+  test_check_generator (&tree, 0, 10, 0);
+  test_check_generator (&tree, 10, 10, 3,
+                        10, 10, 10);
+  test_check_generator (&tree, 10, 20, 3,
+                        10, 10, 10);
+}
+END_TEST
+
+START_TEST (test_generator_5)
+{
+  enum { N = 4 };
+  struct itree_node nodes[N] = {{.begin = 10, .end = 30},
+                                {.begin = 20, .end = 40},
+                                {.begin = 30, .end = 50},
+                                {.begin = 40, .end = 60}};
+  test_create_tree (nodes, N, false);
+  struct itree_iterator *g =
+    itree_iterator_start (&tree, 0, 100, ITREE_PRE_ORDER, NULL, 0);
+  for (int i = 0; i < N; ++i)
+    {
+      struct itree_node *n = itree_iterator_next (g);
+      ck_assert_ptr_nonnull (n);
+      switch (i)
+        {
+        case 0: ck_assert_int_eq (20, n->begin); break;
+        case 1: ck_assert_int_eq (10, n->begin); break;
+        case 2: ck_assert_int_eq (30, n->begin); break;
+        case 3: ck_assert_int_eq (40, n->begin); break;
+        }
+    }
+  itree_iterator_finish (g);
+}
+END_TEST
+
+START_TEST (test_generator_6)
+{
+  enum { N = 4 };
+  struct itree_node nodes[N] = {{.begin = 10, .end = 30},
+                                {.begin = 20, .end = 40},
+                                {.begin = 30, .end = 50},
+                                {.begin = 40, .end = 60}};
+  test_create_tree (nodes, N, true);
+  struct itree_iterator *g =
+    itree_iterator_start (&tree, 0, 100, ITREE_ASCENDING, NULL, 0);
+  for (int i = 0; i < N; ++i)
+    {
+      struct itree_node *n = itree_iterator_next (g);
+      ck_assert_ptr_nonnull (n);
+      switch (i)
+        {
+        case 0: ck_assert_int_eq (10, n->begin); break;
+        case 1: ck_assert_int_eq (20, n->begin); break;
+        case 2: ck_assert_int_eq (30, n->begin); break;
+        case 3: ck_assert_int_eq (40, n->begin); break;
+        }
+    }
+  itree_iterator_finish (g);
+}
+END_TEST
+
+START_TEST (test_generator_7)
+{
+  enum { N = 4 };
+  struct itree_node nodes[N] = {{.begin = 10, .end = 30},
+                                {.begin = 20, .end = 40},
+                                {.begin = 30, .end = 50},
+                                {.begin = 40, .end = 60}};
+  test_create_tree (nodes, N, true);
+  struct itree_iterator *g =
+    itree_iterator_start (&tree, 0, 100, ITREE_DESCENDING, NULL, 0);
+  for (int i = 0; i < N; ++i)
+    {
+      struct itree_node *n = itree_iterator_next (g);
+      ck_assert_ptr_nonnull (n);
+      switch (i)
+        {
+        case 0: ck_assert_int_eq (40, n->begin); break;
+        case 1: ck_assert_int_eq (30, n->begin); break;
+        case 2: ck_assert_int_eq (20, n->begin); break;
+        case 3: ck_assert_int_eq (10, n->begin); break;
+        }
+    }
+  itree_iterator_finish (g);
+}
+END_TEST
+
+START_TEST (test_generator_8)
+{
+  enum { N = 2 };
+  struct itree_node nodes[N] = {{.begin = 20, .end = 30},
+                                {.begin = 40, .end = 50}};
+  test_create_tree (nodes, N, false);
+  struct itree_iterator *g =
+    itree_iterator_start (&tree, 1, 60, ITREE_DESCENDING, NULL, 0);
+  struct itree_node *n = itree_iterator_next (g);
+  ck_assert_int_eq (n->begin, 40);
+  itree_iterator_narrow (g, 50, 60);
+  n = itree_iterator_next (g);
+  ck_assert_ptr_null (n);
+  itree_iterator_finish (g);
+}
+END_TEST
+
+START_TEST (test_generator_9)
+{
+  enum { N = 2 };
+  struct itree_node nodes[N] = {{.begin = 25, .end = 25},
+                                {.begin = 20, .end = 30}};
+  test_create_tree (nodes, N, false);
+  struct itree_iterator *g =
+    itree_iterator_start (&tree, 1, 30, ITREE_DESCENDING, NULL, 0);
+  struct itree_node *n = itree_iterator_next (g);
+  ck_assert_int_eq (n->begin, 25);
+  itree_iterator_narrow (g, 25, 30);
+  n = itree_iterator_next (g);
+  ck_assert_int_eq (n->begin, 20);
+  itree_iterator_finish (g);
+}
+END_TEST
+
+
+/* 
+===================================================================================+
+ * | Insert Gap
+ * 
+===================================================================================+
 */
+
+static struct itree_tree gap_tree;
+static struct itree_node gap_node;
+
+#define N_BEG (itree_node_begin (&gap_tree, &gap_node))
+#define N_END (itree_node_end (&gap_tree, &gap_node))
+
+static void
+test_setup_gap_node (ptrdiff_t begin, ptrdiff_t end,
+                     bool front_advance, bool rear_advance)
+{
+  interval_tree_init (&gap_tree);
+  gap_node.front_advance = front_advance;
+  gap_node.rear_advance = rear_advance;
+  itree_insert (&gap_tree, &gap_node, begin, end);
+}
+
+static void
+test_setup_gap_node_noadvance (ptrdiff_t begin, ptrdiff_t end)
+{
+  test_setup_gap_node (begin, end, false, false);
+}
+
+START_TEST (test_gap_insert_1)
+{
+  test_setup_gap_node_noadvance (100, 200);
+  itree_insert_gap (&gap_tree, 100 + 10, 20, false);
+  ck_assert_int_eq (N_BEG, 100);
+  ck_assert_int_eq (N_END, 200 + 20);
+}
+END_TEST
+
+START_TEST (test_gap_insert_2)
+{
+  test_setup_gap_node_noadvance (100, 200);
+  itree_insert_gap (&gap_tree, 300, 10, false);
+  ck_assert_int_eq (N_BEG, 100);
+  ck_assert_int_eq (N_END, 200);
+}
+END_TEST
+
+START_TEST (test_gap_insert_3)
+{
+  test_setup_gap_node_noadvance (100, 200);
+  itree_insert_gap (&gap_tree, 0, 15, false);
+  ck_assert_int_eq (N_BEG, 100 + 15);
+  ck_assert_int_eq (N_END, 200 + 15);
+}
+END_TEST
+
+START_TEST (test_gap_insert_4)
+{
+  test_setup_gap_node (100, 200, true, false);
+  itree_insert_gap (&gap_tree, 100, 20, false);
+  ck_assert_int_eq (N_BEG, 100 + 20);
+  ck_assert_int_eq (N_END, 200 + 20);
+
+}
+END_TEST
+
+START_TEST (test_gap_insert_5)
+{
+  test_setup_gap_node_noadvance (100, 200);
+  itree_insert_gap (&gap_tree, 100, 20, false);
+  ck_assert_int_eq (N_BEG, 100);
+  ck_assert_int_eq (N_END, 200 + 20);
+
+}
+END_TEST
+
+START_TEST (test_gap_insert_6)
+{
+  test_setup_gap_node (100, 200, false, true);
+  itree_insert_gap (&gap_tree, 200, 20, false);
+  ck_assert_int_eq (N_BEG, 100);
+  ck_assert_int_eq (N_END, 200 + 20);
+
+}
+END_TEST
+
+START_TEST (test_gap_insert_7)
+{
+  test_setup_gap_node_noadvance (100, 200);
+  itree_insert_gap (&gap_tree, 200, 20, false);
+  ck_assert_int_eq (N_BEG, 100);
+  ck_assert_int_eq (N_END, 200);
+
+}
+END_TEST
+
+START_TEST (test_gap_insert_8)
+{
+  test_setup_gap_node (100, 100, true, true);
+  itree_insert_gap (&gap_tree, 100, 20, false);
+  ck_assert_int_eq (N_BEG, 100 + 20);
+  ck_assert_int_eq (N_END, 100 + 20);
+
+}
+END_TEST
+
+START_TEST (test_gap_insert_9)
+{
+  test_setup_gap_node (100, 100, false, true);
+  itree_insert_gap (&gap_tree, 100, 20, false);
+  ck_assert_int_eq (N_BEG, 100);
+  ck_assert_int_eq (N_END, 100 + 20);
+
+}
+END_TEST
+
+START_TEST (test_gap_insert_10)
+{
+  test_setup_gap_node (100, 100, true, false);
+  itree_insert_gap (&gap_tree, 100, 20, false);
+  ck_assert_int_eq (N_BEG, 100);
+  ck_assert_int_eq (N_END, 100);
+
+}
+END_TEST
+
+START_TEST (test_gap_insert_11)
+{
+  test_setup_gap_node_noadvance (100, 100);
+  itree_insert_gap (&gap_tree, 100, 20, false);
+  ck_assert_int_eq (N_BEG, 100);
+  ck_assert_int_eq (N_END, 100);
+
+}
+END_TEST
+
+
+/* 
+===================================================================================+
+ * | Delete Gap
+ * 
+===================================================================================+
 */
+
+START_TEST (test_gap_delete_1)
+{
+  test_setup_gap_node_noadvance (100, 200);
+  itree_delete_gap (&gap_tree, 100 + 10, 20);
+  ck_assert_int_eq (N_BEG, 100);
+  ck_assert_int_eq (N_END, 200 - 20);
+
+}
+END_TEST
+
+START_TEST (test_gap_delete_2)
+{
+  test_setup_gap_node_noadvance (100, 200);
+  itree_delete_gap (&gap_tree, 200 + 10, 20);
+  ck_assert_int_eq (N_BEG, 100);
+  ck_assert_int_eq (N_END, 200);
+
+}
+END_TEST
+
+START_TEST (test_gap_delete_3)
+{
+  test_setup_gap_node_noadvance (100, 200);
+  itree_delete_gap (&gap_tree, 200, 20);
+  ck_assert_int_eq (N_BEG, 100);
+  ck_assert_int_eq (N_END, 200);
+
+}
+END_TEST
+
+START_TEST (test_gap_delete_4)
+{
+  test_setup_gap_node_noadvance (100, 200);
+  itree_delete_gap (&gap_tree, 100 - 20, 20);
+  ck_assert_int_eq (N_BEG, 100 - 20);
+  ck_assert_int_eq (N_END, 200 - 20);
+
+}
+END_TEST
+
+START_TEST (test_gap_delete_5)
+{
+  test_setup_gap_node_noadvance (100, 200);
+  itree_delete_gap (&gap_tree, 70, 20);
+  ck_assert_int_eq (N_BEG, 100 - 20);
+  ck_assert_int_eq (N_END, 200 - 20);
+
+}
+END_TEST
+
+START_TEST (test_gap_delete_6)
+{
+  test_setup_gap_node_noadvance (100, 200);
+  itree_delete_gap (&gap_tree, 80, 100);
+  ck_assert_int_eq (N_BEG, 80);
+  ck_assert_int_eq (N_END, 100);
+}
+END_TEST
+
+START_TEST (test_gap_delete_7)
+{
+  test_setup_gap_node_noadvance (100, 200);
+  itree_delete_gap (&gap_tree, 120, 100);
+  ck_assert_int_eq (N_BEG, 100);
+  ck_assert_int_eq (N_END, 120);
+}
+END_TEST
+
+START_TEST (test_gap_delete_8)
+{
+  test_setup_gap_node_noadvance (100, 200);
+  itree_delete_gap (&gap_tree, 100 - 20, 200 + 20);
+  ck_assert_int_eq (N_BEG, 100 - 20);
+  ck_assert_int_eq (N_END, 100 - 20);
+
+}
+END_TEST
+
+
+
+static Suite *
+basic_suite ()
+{
+  Suite *s = suite_create ("basic");
+
+  TCase *tc = tcase_create ("insert1");
+  tcase_add_checked_fixture (tc, test_insert1_setup, NULL);
+  tcase_add_test (tc, test_insert_1);
+  tcase_add_test (tc, test_insert_2);
+  tcase_add_test (tc, test_insert_3);
+  tcase_add_test (tc, test_insert_4);
+  tcase_add_test (tc, test_insert_5);
+  tcase_add_test (tc, test_insert_6);
+  suite_add_tcase (s, tc);
+
+  tc = tcase_create ("insert2");
+  tcase_add_checked_fixture (tc, test_insert2_setup, NULL);
+  tcase_add_test (tc, test_insert_7);
+  tcase_add_test (tc, test_insert_8);
+  tcase_add_test (tc, test_insert_9);
+  tcase_add_test (tc, test_insert_10);
+  tcase_add_test (tc, test_insert_11);
+  tcase_add_test (tc, test_insert_12);
+  suite_add_tcase (s, tc);
+
+  tc = tcase_create ("insert3");
+  tcase_add_test (tc, test_insert_13);
+  tcase_add_test (tc, test_insert_14);
+  suite_add_tcase (s, tc);
+
+  tc = tcase_create ("remove1");
+  tcase_add_checked_fixture (tc, test_remove1_setup, NULL);
+  tcase_add_test (tc, test_remove_1);
+  tcase_add_test (tc, test_remove_2);
+  tcase_add_test (tc, test_remove_3);
+  tcase_add_test (tc, test_remove_4);
+  suite_add_tcase (s, tc);
+
+  tc = tcase_create ("remove2");
+  tcase_add_checked_fixture (tc, test_remove2_setup, NULL);
+  tcase_add_test (tc, test_remove_5);
+  tcase_add_test (tc, test_remove_6);
+  tcase_add_test (tc, test_remove_7);
+  tcase_add_test (tc, test_remove_8);
+  suite_add_tcase (s, tc);
+
+  tc = tcase_create ("remove3");
+  tcase_add_test (tc, test_remove_9);
+  tcase_add_test (tc, test_remove_10);
+  suite_add_tcase (s, tc);
+
+  tc = tcase_create ("generator");
+  tcase_add_test (tc, test_generator_1);
+  tcase_add_test (tc, test_generator_2);
+  tcase_add_test (tc, test_generator_3);
+  tcase_add_test (tc, test_generator_5);
+  tcase_add_test (tc, test_generator_6);
+  tcase_add_test (tc, test_generator_7);
+  tcase_add_test (tc, test_generator_8);
+  tcase_add_test (tc, test_generator_9);
+  suite_add_tcase (s, tc);
+
+  tc = tcase_create ("insert_gap");
+  tcase_add_test (tc, test_gap_insert_1);
+  tcase_add_test (tc, test_gap_insert_2);
+  tcase_add_test (tc, test_gap_insert_3);
+  tcase_add_test (tc, test_gap_insert_4);
+  tcase_add_test (tc, test_gap_insert_5);
+  tcase_add_test (tc, test_gap_insert_6);
+  tcase_add_test (tc, test_gap_insert_7);
+  tcase_add_test (tc, test_gap_insert_8);
+  tcase_add_test (tc, test_gap_insert_9);
+  tcase_add_test (tc, test_gap_insert_10);
+  tcase_add_test (tc, test_gap_insert_11);
+  suite_add_tcase (s, tc);
+
+  tc = tcase_create ("delete_gap");
+  tcase_add_test (tc, test_gap_delete_1);
+  tcase_add_test (tc, test_gap_delete_2);
+  tcase_add_test (tc, test_gap_delete_3);
+  tcase_add_test (tc, test_gap_delete_4);
+  tcase_add_test (tc, test_gap_delete_5);
+  tcase_add_test (tc, test_gap_delete_6);
+  tcase_add_test (tc, test_gap_delete_7);
+  tcase_add_test (tc, test_gap_delete_8);
+  suite_add_tcase (s, tc);
+
+  return s;
+}
+
+int
+main (void)
+{
+  Suite *s = basic_suite ();
+  SRunner *sr = srunner_create (s);
+
+  init_itree ();
+  srunner_run_all (sr, CK_ENV);
+  int nfailed = srunner_ntests_failed (sr);
+  srunner_free (sr);
+  return (nfailed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/test/manual/noverlay/many-errors.py 
b/test/manual/noverlay/many-errors.py
new file mode 100644
index 0000000000..fa4ef5f98d
--- /dev/null
+++ b/test/manual/noverlay/many-errors.py
@@ -0,0 +1,2480 @@
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
+def a(x, y, y):
+    return t; pass
diff --git a/test/manual/noverlay/overlay-perf.el 
b/test/manual/noverlay/overlay-perf.el
new file mode 100644
index 0000000000..e84941c08f
--- /dev/null
+++ b/test/manual/noverlay/overlay-perf.el
@@ -0,0 +1,764 @@
+;; -*- lexical-binding:t -*-
+(require 'cl-lib)
+(require 'subr-x)
+(require 'seq)
+(require 'hi-lock)
+
+
+;; 
+===================================================================================+
+;; | Framework
+;; 
+===================================================================================+
+
+(defmacro perf-define-constant-test (name &optional doc &rest body)
+  (declare (indent 1) (debug (symbol &optional string &rest form)))
+  `(progn
+     (put ',name 'perf-constant-test t)
+     (defun ,name nil ,doc ,@body)))
+
+(defmacro perf-define-variable-test (name args &optional doc &rest body)
+  (declare (indent 2) (debug defun))
+  (unless (and (consp args)
+               (= (length args) 1))
+    (error "Function %s should accept exactly one argument." name))
+  `(progn
+     (put ',name 'perf-variable-test t)
+     (defun ,name ,args ,doc ,@body)))
+
+(defmacro perf-define-test-suite (name &rest tests)
+  (declare (indent 1))
+  `(put ',name 'perf-test-suite
+        ,(cons 'list tests)))
+
+(defun perf-constant-test-p (test)
+  (get test 'perf-constant-test))
+
+(defun perf-variable-test-p (test)
+  (get test 'perf-variable-test))
+
+(defun perf-test-suite-p (suite)
+  (not (null (perf-test-suite-elements suite))))
+
+(defun perf-test-suite-elements (suite)
+  (get suite 'perf-test-suite))
+
+(defun perf-expand-suites (test-and-suites)
+  (apply #' append (mapcar (lambda (elt)
+                             (if (perf-test-suite-p elt)
+                                 (perf-test-suite-elements elt)
+                               (list elt)))
+                           test-and-suites)))
+(defun perf-test-p (symbol)
+  (or (perf-variable-test-p symbol)
+      (perf-constant-test-p symbol)))
+
+(defun perf-all-tests ()
+  (let (result)
+    (mapatoms (lambda (symbol)
+                (when (and (fboundp symbol)
+                           (perf-test-p symbol))
+                  (push symbol result))))
+    (sort result #'string-lessp)))
+
+(defvar perf-default-test-argument 4096)
+
+(defun perf-run-1 (&optional k n &rest tests)
+  "Run TESTS K times using N as argument for non-constant ones.
+
+Return test-total elapsed time."
+  (random "")
+  (when (and n (not (numberp n)))
+    (push k tests)
+    (push n tests)
+    (setq n nil k nil))
+  (when (and k (not (numberp k)))
+    (push k tests)
+    (setq k nil))
+  (let* ((k (or k 1))
+         (n (or n perf-default-test-argument))
+         (tests (perf-expand-suites (or tests
+                                        (perf-all-tests))))
+         (variable-tests (seq-filter #'perf-variable-test-p tests))
+         (constant-tests (seq-filter #'perf-constant-test-p tests))
+         (max-test-string-width (perf-max-symbol-length tests)))
+    (unless (seq-every-p #'perf-test-p tests)
+      (error "Some of these are not tests: %s" tests))
+    (cl-labels ((format-result (result)
+                  (cond
+                   ((numberp result) (format "%.2f" result))
+                   ((stringp result) result)
+                   ((null result) "N/A")))
+                (format-test (fn)
+                  (concat (symbol-name fn)
+                          (make-string
+                           (+ (- max-test-string-width
+                                 (length (symbol-name fn)))
+                              1)
+                           ?\s)))
+                (format-summary (results _total)
+                  (let ((min (apply #'min results))
+                        (max (apply #'max results))
+                        (avg (/ (apply #'+ results) (float (length results)))))
+                    (format "n=%d min=%.2f avg=%.2f max=%.2f" (length results) 
min avg max)))
+                (run-test (fn)
+                  (let ((total 0) results)
+                    (dotimes (_ (max 0 k))
+                      (garbage-collect)
+                      (princ (concat " " (format-test fn)))
+                      (let ((result  (condition-case-unless-debug err
+                                         (cond
+                                          ((perf-variable-test-p fn)
+                                           (random "") (car (funcall fn n)))
+                                          ((perf-constant-test-p fn)
+                                           (random "") (car (funcall fn)))
+                                          (t "skip"))
+                                       (error (error-message-string err)))))
+                        (when (numberp result)
+                          (cl-incf total result)
+                          (push result results))
+                        (princ (format-result result))
+                        (terpri)))
+                    (when (> (length results) 1)
+                      (princ (concat "#" (format-test fn)
+                                     (format-summary results total)))
+                      (terpri)))))
+      (when variable-tests
+        (terpri)
+        (dolist (fn variable-tests)
+          (run-test fn)
+          (terpri)))
+      (when constant-tests
+        (dolist (fn constant-tests)
+          (run-test fn)
+          (terpri))))))
+
+(defun perf-run (&optional k n &rest tests)
+  (interactive
+   (let* ((n (if current-prefix-arg
+                 (prefix-numeric-value current-prefix-arg)
+               perf-default-test-argument))
+          (tests (mapcar #'intern
+                         (completing-read-multiple
+                          (format "Run tests (n=%d): " n)
+                          (perf-all-tests) nil t nil 'perf-test-history))))
+     (cons 1 (cons n tests))))
+  (with-current-buffer (get-buffer-create "*perf-results*")
+    (let ((inhibit-read-only t)
+          (standard-output (current-buffer)))
+      (erase-buffer)
+      (apply #'perf-run-1 k n tests)
+      (display-buffer (current-buffer)))))
+
+
+(defun perf-batch-parse-command-line (args)
+  (let ((k 1)
+        (n perf-default-test-argument)
+        tests)
+    (while args
+      (cond ((string-match-p "\\`-[cn]\\'" (car args))
+             (unless (and (cdr args)
+                          (string-match-p "\\`[0-9]+\\'" (cadr args)))
+               (error "%s expectes a natnum argument" (car args)))
+             (if (equal (car args) "-c")
+                 (setq k (string-to-number (cadr args)))
+               (setq n (string-to-number (cadr args))))
+             (setq args (cddr args)))
+            (t (push (intern (pop args)) tests))))
+    (list k n tests)))
+
+
+(defun perf-run-batch ()
+  "Runs tests from `command-line-args-left' and kill emacs."
+  (let ((standard-output #'external-debugging-output))
+    (condition-case err
+        (cl-destructuring-bind (k n tests)
+            (perf-batch-parse-command-line command-line-args-left)
+          (apply #'perf-run-1 k n tests)
+          (save-buffers-kill-emacs))
+      (error
+       (princ (error-message-string err))
+       (save-buffers-kill-emacs)))))
+
+(defconst perf-number-of-columns 70)
+
+(defun perf-insert-lines (n)
+  "Insert N lines into the current buffer."
+  (dotimes (i n)
+    (insert (make-string 70 (if (= (% i 2) 0)
+                                ?.
+                              ?O))
+            ?\n)))
+
+(defun perf-switch-to-buffer-scroll-random (n &optional buffer)
+  (interactive)
+  (set-window-buffer nil (or buffer (current-buffer)))
+  (goto-char (point-min))
+  (redisplay t)
+  (dotimes (_ n)
+    (goto-char (random (point-max)))
+    (recenter)
+    (redisplay t)))
+
+(defun perf-insert-overlays (n &optional create-callback random-p)
+  (if random-p
+      (perf-insert-overlays-random n create-callback)
+    (perf-insert-overlays-sequential n create-callback)))
+
+(defun perf-insert-overlays-sequential (n &optional create-callback)
+  "Insert an overlay every Nth line."
+  (declare (indent 1))
+  (let ((i 0)
+        (create-callback (or create-callback #'ignore)))
+    (save-excursion
+      (goto-char (point-min))
+      (while (not (eobp))
+        (when (= 0 (% i n))
+          (let ((ov (make-overlay (point-at-bol) (point-at-eol))))
+            (funcall create-callback ov)
+            (overlay-put ov 'priority (random (buffer-size)))))
+        (cl-incf i)
+        (forward-line)))))
+
+(defun perf-insert-overlays-random (n &optional create-callback)
+  "Insert an overlay every Nth line."
+  (declare (indent 1))
+  (let ((create-callback (or create-callback #'ignore)))
+    (save-excursion
+      (while (>= (cl-decf n) 0)
+        (let* ((beg (1+ (random (point-max))))
+               (ov (make-overlay beg (+ beg (random 70)))))
+          (funcall create-callback ov)
+          (overlay-put ov 'priority (random (buffer-size))))))))
+
+(defun perf-insert-overlays-hierarchical (n &optional create-callback)
+  (let ((create-callback (or create-callback #'ignore)))
+    (save-excursion
+      (goto-char (point-min))
+      (let ((spacing (floor (/ (/ (count-lines (point-min) (point-max))
+                                  (float 3))
+                               n))))
+        (when (< spacing 1)
+          (error "Hierarchical overlay overflow !!"))
+        (dotimes (i n)
+          (funcall create-callback
+                   (make-overlay (point)
+                                 (save-excursion
+                                   (goto-char (point-max))
+                                   (forward-line (- (* spacing i)))
+                                   (point))))
+
+          (when (eobp)
+            (error "End of buffer in hierarchical overlays"))
+          (forward-line spacing))))))
+
+(defun perf-overlay-ascii-chart (&optional buffer width)
+  (interactive)
+  (save-current-buffer
+    (when buffer (set-buffer buffer))
+    (unless width (setq width 100))
+    (let* ((ovl (sort (overlays-in (point-min) (point-max))
+                      (lambda (ov1 ov2)
+                        (or (<= (overlay-start ov1)
+                                (overlay-start ov2))
+                            (and
+                             (= (overlay-start ov1)
+                                (overlay-start ov2))
+                             (< (overlay-end ov1)
+                                (overlay-end ov2)))))))
+           (ov-width (apply #'max (mapcar (lambda (ov)
+                                            (- (overlay-end ov)
+                                               (overlay-start ov)))
+                                          ovl)))
+           (ov-min (apply #'min (mapcar #'overlay-start ovl)))
+           (ov-max (apply #'max (mapcar #'overlay-end ovl)))
+           (scale (/ (float width) (+ ov-min ov-width))))
+      (with-current-buffer (get-buffer-create "*overlay-ascii-chart*")
+        (let ((inhibit-read-only t))
+          (erase-buffer)
+          (buffer-disable-undo)
+          (insert (format "%06d%s%06d\n" ov-min (make-string (- width 12) ?\s) 
ov-max))
+          (dolist (ov ovl)
+            (let ((length (round (* scale (- (overlay-end ov)
+                                             (overlay-start ov))))))
+              (insert (make-string (round (* scale (overlay-start ov))) ?\s))
+              (cl-case length
+                (0 (insert "O"))
+                (1 (insert "|"))
+                (t (insert (format "|%s|" (make-string (- length 2) ?-)))))
+              (insert "\n")))
+          (goto-char (point-min)))
+        (read-only-mode 1)
+        (pop-to-buffer (current-buffer))))))
+
+(defconst perf-overlay-faces (mapcar #'intern (seq-take hi-lock-face-defaults 
3)))
+
+(defun perf-overlay-face-callback (ov)
+  (overlay-put ov 'face (nth (random (length perf-overlay-faces))
+                             perf-overlay-faces)))
+
+(defun perf-overlay-invisible-callback (ov)
+  (overlay-put ov 'invisble (= 1 (random 2))))
+
+(defun perf-overlay-display-callback (ov)
+  (overlay-put ov 'display (make-string 70 ?*)))
+
+(defmacro perf-define-display-test (overlay-type property-type scroll-type)
+  (let ((name (intern (format "perf-display-%s/%s/%s"
+                              overlay-type property-type scroll-type)))
+        (arg (make-symbol "n")))
+
+    `(perf-define-variable-test ,name (,arg)
+       (with-temp-buffer
+         (perf-insert-lines ,arg)
+         (overlay-recenter (point-max))
+         ,@(perf-define-display-test-1 arg overlay-type property-type 
scroll-type)))))
+
+(defun perf-define-display-test-1 (arg overlay-type property-type scroll-type)
+  (list (append (cl-case overlay-type
+                  (sequential
+                   (list 'perf-insert-overlays-sequential 2))
+                  (hierarchical
+                   `(perf-insert-overlays-hierarchical (/ ,arg 10)))
+                  (random
+                   `(perf-insert-overlays-random (/ ,arg 2)))
+                  (t (error "Invalid insert type: %s" overlay-type)))
+                (list
+                 (cl-case property-type
+                   (display '#'perf-overlay-display-callback)
+                   (face '#'perf-overlay-face-callback)
+                   (invisible '#'perf-overlay-invisible-callback)
+                   (t (error "Invalid overlay type: %s" overlay-type)))))
+        (list 'benchmark-run 1
+              (cl-case scroll-type
+                (scroll '(perf-switch-to-buffer-scroll-up-and-down))
+                (random `(perf-switch-to-buffer-scroll-random (/ ,arg 50)))
+                (t (error "Invalid scroll type: %s" overlay-type))))))
+
+(defun perf-max-symbol-length (symbols)
+  "Return the longest symbol in SYMBOLS, or -1 if symbols is nil."
+  (if (null symbols)
+      -1
+    (apply #'max (mapcar
+                  (lambda (elt)
+                    (length (symbol-name elt)))
+                  symbols))))
+
+(defun perf-insert-text (n)
+  "Insert N character into the current buffer."
+  (let ((ncols 68)
+        (char ?.))
+    (dotimes (_  (/ n ncols))
+      (insert (make-string (1- ncols) char) ?\n))
+    (when (> (% n ncols) 0)
+      (insert (make-string (1- (% n ncols)) char) ?\n))))
+
+(defconst perf-insert-overlays-default-length 24)
+
+(defun perf-insert-overlays-scattered (n &optional length)
+  "Insert N overlays of max length 24 randomly."
+  (dotimes (_ n)
+    (let ((begin (random (1+ (point-max)))))
+      (make-overlay
+       begin (+ begin (random (1+ (or length 
perf-insert-overlays-default-length 0))))))))
+
+(defvar perf-marker-gc-protection nil)
+
+(defun perf-insert-marker-scattered (n)
+  "Insert N marker randomly."
+  (setq perf-marker-gc-protection nil)
+  (dotimes (_ n)
+    (push (copy-marker (random (1+ (point-max))))
+          perf-marker-gc-protection)))
+
+(defun perf-switch-to-buffer-scroll-up-and-down (&optional buffer)
+  (interactive)
+  (set-window-buffer nil (or buffer (current-buffer)))
+  (goto-char (point-min))
+  (redisplay t)
+  (while (condition-case nil
+             (progn (scroll-up) t)
+           (end-of-buffer nil))
+    (redisplay t))
+  (while (condition-case nil
+             (progn (scroll-down) t)
+           (beginning-of-buffer nil))
+    (redisplay t)))
+
+(defun perf-emacs-lisp-setup ()
+  (add-to-list 'imenu-generic-expression
+               '(nil 
"^\\s-*(perf-define\\(?:\\w\\|\\s_\\)*\\s-*\\(\\(?:\\w\\|\\s_\\)+\\)" 1)))
+
+(add-hook 'emacs-lisp-mode 'perf-emacs-lisp-setup)
+
+
+;; 
+===================================================================================+
+;; | Basic performance tests
+;; 
+===================================================================================+
+
+(perf-define-variable-test perf-make-overlay (n)
+  (with-temp-buffer
+    (overlay-recenter (point-min))
+    (benchmark-run 1
+      (dotimes (_ n)
+        (make-overlay 1 1)))))
+
+(perf-define-variable-test perf-make-overlay-continuous (n)
+  (with-temp-buffer
+    (perf-insert-text n)
+    (overlay-recenter (point-max))
+    (benchmark-run 1
+      (dotimes (i n)
+        (make-overlay i (1+ i))))))
+
+(perf-define-variable-test perf-make-overlay-scatter (n)
+  (with-temp-buffer
+    (perf-insert-text n)
+    (benchmark-run 1
+      (perf-insert-overlays-scattered n))))
+
+(perf-define-variable-test perf-delete-overlay (n)
+  (with-temp-buffer
+    (let ((ovls (cl-loop for i from 1 to n
+                        collect (make-overlay 1 1))))
+      (overlay-recenter (point-min))
+      (benchmark-run 1
+        (mapc #'delete-overlay ovls)))))
+
+(perf-define-variable-test perf-delete-overlay-continuous (n)
+  (with-temp-buffer
+    (perf-insert-text n)
+    (let ((ovls (cl-loop for i from 1 to n
+                         collect (make-overlay i (1+ i)))))
+      (overlay-recenter (point-min))
+      (benchmark-run 1
+        (mapc #'delete-overlay ovls)))))
+
+(perf-define-variable-test perf-delete-overlay-scatter (n)
+  (with-temp-buffer
+    (perf-insert-text n)
+    (let ((ovls (progn (perf-insert-overlays-scattered n)
+                       (overlays-in (point-min) (point-max)))))
+      (benchmark-run 1
+        (mapc #'delete-overlay ovls)))))
+
+(perf-define-variable-test perf-overlays-at (n)
+  (with-temp-buffer
+    (perf-insert-text n)
+    (perf-insert-overlays-scattered n)
+    (benchmark-run 1
+      (dotimes (i (point-max))
+        (overlays-at i)))))
+
+(perf-define-variable-test perf-overlays-in (n)
+  (with-temp-buffer
+    (perf-insert-text n)
+    (perf-insert-overlays-scattered n)
+    (let ((len perf-insert-overlays-default-length))
+      (benchmark-run 1
+        (dotimes (i (- (point-max) len))
+          (overlays-in i (+ i len)))))))
+
+(perf-define-variable-test perf-insert-before (n)
+  (with-temp-buffer
+    (perf-insert-text n)
+    (perf-insert-overlays-scattered n)
+    (goto-char 1)
+    (overlay-recenter (point-min))
+    (benchmark-run 1
+      (dotimes (_ (/ n 2))
+        (insert ?X)))))
+
+(perf-define-variable-test perf-insert-before-empty (n)
+  (let ((perf-insert-overlays-default-length 0))
+    (perf-insert-before n)))
+(perf-define-variable-test perf-insert-after-empty (n)
+  (let ((perf-insert-overlays-default-length 0))
+    (perf-insert-after n)))
+(perf-define-variable-test perf-insert-scatter-empty (n)
+  (let ((perf-insert-overlays-default-length 0))
+    (perf-insert-scatter n)))
+(perf-define-variable-test perf-delete-before-empty (n)
+  (let ((perf-insert-overlays-default-length 0))
+    (perf-delete-before n)))
+(perf-define-variable-test perf-delete-after-empty (n)
+  (let ((perf-insert-overlays-default-length 0))
+    (perf-delete-after n)))
+(perf-define-variable-test perf-delete-scatter-empty (n)
+  (let ((perf-insert-overlays-default-length 0))
+    (perf-delete-scatter n)))
+
+(defmacro perf-define-marker-test (type where)
+  (let ((name (intern (format "perf-%s-%s-marker" type where))))
+    `(perf-define-variable-test ,name (n)
+       (with-temp-buffer
+         (perf-insert-text n)
+         (perf-insert-marker-scattered n)
+         (goto-char ,(cl-case where
+                       (after (list 'point-max))
+                       (t (list 'point-min))))
+         (benchmark-run 1
+           (dotimes (_ (/ n 2))
+             ,@(when (eq where 'scatter)
+                 (list '(goto-char (max 1 (random (point-max))))))
+             ,(cl-case type
+                (insert (list 'insert ?X))
+                (delete (list 'delete-char (if (eq where 'after) -1 1))))))))))
+
+(perf-define-test-suite perf-marker-suite
+  (perf-define-marker-test insert before)
+  (perf-define-marker-test insert after)
+  (perf-define-marker-test insert scatter)
+  (perf-define-marker-test delete before)
+  (perf-define-marker-test delete after)
+  (perf-define-marker-test delete scatter))
+
+(perf-define-variable-test perf-insert-after (n)
+  (with-temp-buffer
+    (perf-insert-text n)
+    (perf-insert-overlays-scattered n)
+    (goto-char (point-max))
+    (overlay-recenter (point-max))
+    (benchmark-run 1
+      (dotimes (_ (/ n 2))
+        (insert ?X)))))
+
+(perf-define-variable-test perf-insert-scatter (n)
+  (with-temp-buffer
+    (perf-insert-text n)
+    (perf-insert-overlays-scattered n)
+    (goto-char (point-max))
+    (benchmark-run 1
+      (dotimes (_ (/ n 2))
+        (goto-char (1+ (random (point-max))))
+        (insert ?X)))))
+
+(perf-define-variable-test perf-delete-before (n)
+  (with-temp-buffer
+    (perf-insert-text n)
+    (perf-insert-overlays-scattered n)
+    (goto-char 1)
+    (overlay-recenter (point-min))
+    (benchmark-run 1
+      (dotimes (_ (/ n 2))
+        (delete-char 1)))))
+
+(perf-define-variable-test perf-delete-after (n)
+  (with-temp-buffer
+    (perf-insert-text n)
+    (perf-insert-overlays-scattered n)
+    (goto-char (point-max))
+    (overlay-recenter (point-max))
+    (benchmark-run 1
+      (dotimes (_ (/ n 2))
+        (delete-char -1)))))
+
+(perf-define-variable-test perf-delete-scatter (n)
+  (with-temp-buffer
+    (perf-insert-text n)
+    (perf-insert-overlays-scattered n)
+    (goto-char (point-max))
+    (benchmark-run 1
+      (dotimes (_ (/ n 2))
+        (goto-char (max 1 (random (point-max))))
+        (delete-char 1)))))
+
+(perf-define-test-suite perf-insert-delete-suite
+  'perf-insert-before
+  'perf-insert-after
+  'perf-insert-scatter
+  'perf-delete-before
+  'perf-delete-after
+  'perf-delete-scatter
+  )
+
+
+;; 
+===================================================================================+
+;; | Redisplay (new)
+;; 
+===================================================================================+
+
+;; 5000
+;; 25000
+;; 75000
+
+;; Number of Overlays =  N / 2
+;;
+;; (except for the hierarchical case, where it is divided by 10.)
+
+  ;; . scrolling through a buffer with lots of overlays that affect faces
+  ;;   of characters in the buffer text
+  ;; . scrolling through a buffer with lots of overlays that define
+  ;;   'display' properties which are strings
+  ;; . scrolling through a buffer with lots of overlays that define
+  ;;   'invisible' properties
+
+(perf-define-test-suite perf-display-suite
+  (perf-define-display-test sequential display scroll)
+  (perf-define-display-test sequential display random)
+  (perf-define-display-test sequential face scroll)
+  (perf-define-display-test sequential face random)
+  (perf-define-display-test sequential invisible scroll)
+  (perf-define-display-test sequential invisible random)
+  (perf-define-display-test random display scroll)
+  (perf-define-display-test random display random)
+  (perf-define-display-test random face scroll)
+  (perf-define-display-test random face random)
+  (perf-define-display-test random invisible scroll)
+  (perf-define-display-test random invisible random))
+
+;; |------------|
+;;   |--------|
+;;     |----|
+(perf-define-display-test hierarchical face scroll)
+
+
+
+
+;; 
+===================================================================================+
+;; | Real World
+;; 
+===================================================================================+
+
+(require 'python)
+
+(defconst perf-many-errors-file
+  (expand-file-name "many-errors.py"
+                    (and load-file-name (file-name-directory load-file-name))))
+
+(perf-define-constant-test perf-realworld-flycheck
+  (interactive)
+  (package-initialize)
+  (when (and (require 'flycheck nil t)
+             (file-exists-p perf-many-errors-file)
+             (or (executable-find "pylint")
+                 (executable-find "flake8")))
+    (setq flycheck-python-pylint-executable
+          (executable-find "pylint"))
+    (setq flycheck-python-flake8-executable
+          (executable-find "flake8"))
+    (setq python-indent-guess-indent-offset-verbose nil)
+    (setq flycheck-check-syntax-automatically nil)
+    (setq flycheck-checker-error-threshold nil)
+    (setq flycheck-display-errors-function nil)
+    (with-current-buffer (find-file-noselect perf-many-errors-file)
+      (let* ((done)
+             (flycheck-after-syntax-check-hook
+              (list (lambda () (setq done t)))))
+        (flycheck-mode 1)
+        (flycheck-buffer)
+        (benchmark-run 1
+          (while (not done)
+            (accept-process-output))
+          (perf-switch-to-buffer-scroll-up-and-down)
+          (flycheck-mode -1))))))
+
+;; https://lists.gnu.org/archive/html/emacs-devel/2009-04/msg00242.html
+(defun make-lines-invisible (regexp &optional arg)
+  "Make all lines matching a regexp invisible and intangible.
+With a prefix arg, make it visible again.  It is not necessary
+that REGEXP matches the whole line; if a hit is found, the
+affected line gets automatically selected.
+
+This command affects the whole buffer."
+  (interactive "MRegexp: \nP")
+  (let (ov
+        ovs
+        count)
+    (cond
+     ((equal arg '(4))
+      (setq ovs (overlays-in (point-min) (point-max)))
+      (mapc (lambda (o)
+              (if (overlay-get o 'make-lines-invisible)
+                  (delete-overlay o)))
+            ovs))
+     (t
+      (save-excursion
+        (goto-char (point-min))
+        (setq count 0)
+        (while (re-search-forward regexp nil t)
+          (setq count (1+ count))
+          (if (= (% count 100) 0)
+              (message "%d" count))
+          (setq ov (make-overlay (line-beginning-position)
+                                 (1+ (line-end-position))))
+          (overlay-put ov 'make-lines-invisible t)
+          (overlay-put ov 'invisible t)
+          (overlay-put ov 'intangible t)
+          (goto-char (line-end-position))))))))
+
+(perf-define-constant-test perf-realworld-make-lines-invisible
+  (with-temp-buffer
+    (insert-file-contents "/usr/share/dict/words")
+    (set-window-buffer nil (current-buffer))
+    (redisplay t)
+    (overlay-recenter (point-max))
+    (benchmark-run 1
+      (make-lines-invisible "a"))))
+
+(perf-define-constant-test perf-realworld-line-numbering
+  (interactive)
+  (with-temp-buffer
+    (insert-file-contents "/usr/share/dict/words")
+    (overlay-recenter (point-max))
+    (goto-char (point-min))
+    (let* ((nlines (count-lines (point-min) (point-max)))
+           (line 1)
+           (width 0))
+      (dotimes (i nlines) ;;-with-progress-reporter "Creating overlays"
+        (let ((ov (make-overlay (point) (point)))
+              (str (propertize (format "%04d" line) 'face 'shadow)))
+          (overlay-put ov 'before-string
+                       (propertize " " 'display `((margin left-margin) ,str)))
+          (setq width (max width (length str)))
+          (cl-incf line)
+          (forward-line)))
+      (benchmark-run 1
+        (let ((left-margin-width width))
+          (perf-switch-to-buffer-scroll-up-and-down))))))
+
+(perf-define-test-suite perf-realworld-suite
+  'perf-realworld-flycheck
+  'perf-realworld-make-lines-invisible
+  'perf-realworld-line-numbering)
+
+
+;; 
+===================================================================================+
+;; | next-overlay-change
+;; 
+===================================================================================+
+
+(perf-define-variable-test perf-noc-hierarchical/forward/linear (n)
+  "Search linear for the next change on every line."
+  (with-temp-buffer
+    (perf-insert-lines (* 3 n))
+    (perf-insert-overlays-hierarchical n)
+    (goto-char (point-min))
+    (benchmark-run 1
+      (while (not (eobp))
+        (next-overlay-change (point))
+        (forward-line)))))
+
+(perf-define-variable-test perf-noc-sequential/forward/linear (n)
+  "Search linear for the next change on every line."
+  (with-temp-buffer
+    (perf-insert-lines (* 3 n))
+    (perf-insert-overlays-sequential n)
+    (goto-char (point-min))
+    (benchmark-run 1
+      (while (not (eobp))
+        (next-overlay-change (point))
+        (forward-line)))))
+
+(perf-define-variable-test perf-noc-hierarchical/forward/backnforth (n)
+  "Search back and forth for the next change from `point-min' to `point-max'."
+  (with-temp-buffer
+    (perf-insert-lines (* 3 n))
+    (overlay-recenter (point-max))
+    (perf-insert-overlays-hierarchical n)
+    (goto-char (point-min))
+    (benchmark-run 1
+      (while (not (eobp))
+        (next-overlay-change (point))
+        (next-overlay-change (+ (point) 2))
+        (forward-char)))))
+
+(perf-define-test-suite perf-noc-suite
+  'perf-noc-hierarchical/forward/linear
+  'perf-noc-hierarchical/forward/backnforth
+  'perf-noc-hierarchical/forward/backnforth)
diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el
index 558d05de14..3fc52eaf8b 100644
--- a/test/src/buffer-tests.el
+++ b/test/src/buffer-tests.el
@@ -20,6 +20,7 @@
 ;;; Code:
 
 (require 'ert)
+(require 'seq)
 (require 'ert-x)
 (require 'cl-lib)
 (require 'let-alist)
@@ -274,6 +275,62 @@ with parameters from the *Messages* buffer modification."
   (with-temp-buffer
     (should (eq (buffer-base-buffer (current-buffer)) nil))))
 
+(ert-deftest buffer-tests--overlays-indirect-bug58928 ()
+  (with-temp-buffer
+    (insert "hello world")
+    (let* ((base (current-buffer))
+           (ol1 (make-overlay (+ 2 (point-min)) (+ 8 (point-min))))
+           (ib (make-indirect-buffer
+                base (generate-new-buffer-name "bug58928")))
+           (ol2 (with-current-buffer ib
+                  (make-overlay (+ 2 (point-min)) (+ 8 (point-min))))))
+      (should (equal (overlay-start ol1) (overlay-start ol2)))
+      (should (equal (overlay-end ol1) (overlay-end ol2)))
+      (goto-char (+ 3 (point-min)))
+      (insert "a") (delete-char 2)
+      (should (equal (overlay-start ol1) (overlay-start ol2)))
+      (should (equal (overlay-end ol1) (overlay-end ol2)))
+      (with-current-buffer ib
+        (goto-char (+ 4 (point-min)))
+        (insert "a") (delete-char 2))
+      (should (equal (overlay-start ol1) (overlay-start ol2)))
+      (should (equal (overlay-end ol1) (overlay-end ol2))))))
+
+(ert-deftest buffer-tests--overlays-indirect-evaporate ()
+  "Verify that deleting text evaporates overlays in every related buffer.
+
+Deleting characters from either a base or an indirect buffer
+should evaporate overlays in both."
+  ;; Loop twice, erasing from the base buffer the first time and the
+  ;; indirect buffer the second.
+  (dolist (erase-where '(base indirect))
+    (ert-info ((format "erase-where %S" erase-where))
+      (with-temp-buffer
+        (insert "xxx")
+        (let* ((beg 2)
+               (end 3)
+               (base (current-buffer))
+               (base-overlay (make-overlay beg end base))
+               (indirect (make-indirect-buffer
+                          base
+                          (generate-new-buffer-name
+                           (concat (buffer-name base) "-indirect"))))
+               (indirect-overlay (make-overlay beg end indirect)))
+          (overlay-put base-overlay 'evaporate t)
+          (overlay-put indirect-overlay 'evaporate t)
+          (with-current-buffer (pcase-exhaustive erase-where
+                                 (`base base)
+                                 (`indirect indirect))
+            (delete-region beg end))
+          (ert-info ((prin1-to-string
+                      `(,base ,base-overlay ,indirect ,indirect-overlay)))
+            (should (not (buffer-live-p (overlay-buffer base-overlay))))
+            (should (not (buffer-live-p (overlay-buffer indirect-overlay))))
+            (should (equal nil (with-current-buffer base
+                                 (overlays-in (point-min) (point-max)))))
+            (should (equal nil (with-current-buffer indirect
+                                 (overlays-in (point-min) (point-max)))))))))))
+
 (ert-deftest overlay-evaporation-after-killed-buffer ()
   (let* ((ols (with-temp-buffer
                 (insert "toto")
@@ -859,6 +916,33 @@ with parameters from the *Messages* buffer modification."
       (should-length 1 (overlays-at 15))
       (should-length 1 (overlays-at (point-max))))))
 
+(defun sorted-overlays (overlays)
+  (sort
+   (mapcar (lambda (overlay)
+             (list (overlay-start overlay)
+                   (overlay-end overlay)))
+           overlays)
+   (lambda (first second)
+     (cl-loop for a in first
+              for b in second
+              thereis (< a b)
+              until (> a b)))))
+
+(defun sorted-overlays-at (pos)
+  (sorted-overlays (overlays-at pos)))
+
+(defun sorted-overlays-in (beg end)
+  (sorted-overlays (overlays-in beg end)))
+
+(ert-deftest test-overlays-at-narrow-to-region-end ()
+  ;; See bug#58703.
+  (with-temp-buffer
+   (insert (make-string 30 ?x))
+   (make-overlay 10 11)
+   (narrow-to-region 10 10)
+   (should (equal
+            '((10 11))
+            (sorted-overlays-at 10)))))
 
 ;; +==========================================================================+
 ;; | overlay-in
@@ -936,6 +1020,39 @@ with parameters from the *Messages* buffer modification."
 (deftest-overlays-in-1 ae 9 11 (a) (a 10 10))
 (deftest-overlays-in-1 af 10 11 (a) (a 10 10))
 
+;; behavior for empty range
+(ert-deftest test-overlays-in-empty-range ()
+    (with-temp-buffer
+      (insert (make-string 4 ?x))
+      (cl-loop for start from (point-min) to (point-max)
+               do (cl-loop for end from start to (point-max)
+                           do (when (<= start end)
+                                (make-overlay start end))))
+
+      (cl-loop for pos from (point-min) to (point-max)
+               do (ert-info ((format "after (overlay-recenter %d)" pos))
+                    (overlay-recenter pos)
+                    (should (equal
+                             '((1 1))
+                             (sorted-overlays-in (point-min) (point-min))))
+                    (should (equal
+                             '((1 3) (1 4) (1 5) (2 2))
+                             (sorted-overlays-in 2 2)))
+                    (should (equal
+                             '((1 4) (1 5) (2 4) (2 5) (3 3))
+                             (sorted-overlays-in 3 3)))
+                    (should (equal
+                             '((1 5) (2 5) (3 5) (4 4))
+                             (sorted-overlays-in 4 4)))
+                    (should (equal
+                             '((5 5))
+                             (sorted-overlays-in (point-max) (point-max))))))))
+
+(ert-deftest test-overlays-in-empty-range-bug58672 ()
+  (with-temp-buffer
+    (insert (make-string 10 ?=))
+    (make-overlay 5 7 nil nil t)
+    (should (equal nil (overlays-in 5 5)))))
 
 ;; behavior at point-max
 (ert-deftest test-overlays-in-2 ()
@@ -1047,6 +1164,49 @@ with parameters from the *Messages* buffer modification."
     (should-not (delete-all-overlays))))
 
 
+;; +==========================================================================+
+;; | get-pos-property
+;; +==========================================================================+
+
+(ert-deftest get-pos-property-overlay-beg ()
+  "Test `get-pos-property' at the beginning of an overlay.
+Regression test for bug#58706."
+  (with-temp-buffer
+    (insert (make-string 10000 ?x))
+    (let ((overlay (make-overlay 9999 10001)))
+      (overlay-put overlay 'forty-two 42))
+    (should (equal 42 (get-pos-property 9999 'forty-two)))))
+
+(ert-deftest get-pos-property-overlay-empty-rear-advance ()
+  "Test `get-pos-property' at the end of an empty rear-advance overlay.
+Regression test for bug#58706."
+  (with-temp-buffer
+    (insert (make-string 10000 ?x))
+    (let ((overlay (make-overlay 9999 9999 nil nil t)))
+      (overlay-put overlay 'forty-two 42))
+    (should (equal 42 (get-pos-property 9999 'forty-two)))))
+
+(ert-deftest get-pos-property-overlay-past-rear-advance ()
+  "Test `get-pos-property' past the end of an empty rear-advance overlay.
+Regression test for bug#58706."
+  (with-temp-buffer
+    (insert (make-string 10000 ?x))
+    (let ((overlay (make-overlay 9998 9998 nil nil t)))
+      (overlay-put overlay 'forty-two 42))
+    (should (equal nil (get-pos-property 9999 'forty-two)))))
+
+(ert-deftest get-pos-property-overlay-at-narrowed-end ()
+  "Test `get-pos-property' at the end of a narrowed region.
+Regression test for bug#58706."
+  (with-temp-buffer
+    (insert (make-string 11000 ?x))
+    (narrow-to-region 9998 10000)
+    (let ((overlay (make-overlay 10000 10000 nil t nil)))
+      (overlay-put overlay 'forty-two 42))
+    (should (equal nil (get-pos-property 9999 'forty-two)))))
+
+;; FIXME: add more `get-pos-property' tests
+
 ;; +==========================================================================+
 ;; | get-char-property(-and-overlay)
 ;; +==========================================================================+
@@ -1168,7 +1328,51 @@ with parameters from the *Messages* buffer modification."
       (delete-overlay left)
       (should (= 2 (length (overlays-in 1 (point-max))))))))
 
+;; +==========================================================================+
+;; | Moving overlays with insert-before-markers
+;; +==========================================================================+
 
+(ert-deftest test-overlay-insert-before-markers-at-start ()
+  "`insert-before-markers' always advances an overlay's start.
+Test both front-advance and non-front-advance overlays."
+  (dolist (front-advance '(nil t))
+    (ert-info ((format "front-advance %S" front-advance))
+      (with-temp-buffer
+        (insert "1234")
+        (let* ((beg (1+ (point-min)))
+               (end (1+ beg))
+               (overlay (make-overlay beg end nil front-advance nil)))
+          (goto-char beg)
+          (insert-before-markers "x")
+          (should (equal (1+ beg) (overlay-start overlay)))
+          (should (equal (1+ end) (overlay-end overlay))))))))
+
+(ert-deftest test-overlay-insert-before-markers-at-end ()
+  "`insert-before-markers' always advances an overlay's end.
+Test both rear-advance and non-rear-advance overlays."
+  (dolist (rear-advance '(nil t))
+    (ert-info ((format "rear-advance %S" rear-advance))
+      (with-temp-buffer
+        (insert "1234")
+        (let* ((beg (1+ (point-min)))
+               (end (1+ beg))
+               (overlay (make-overlay beg end nil nil rear-advance)))
+          (goto-char end)
+          (insert-before-markers "x")
+          (should (equal beg (overlay-start overlay)))
+          (should (equal (1+ end) (overlay-end overlay))))))))
+
+(ert-deftest test-overlay-insert-before-markers-empty ()
+  (dolist (advance-args '((nil nil) (t nil) (nil t) (t t)))
+    (ert-info ((format "advance args %S" advance-args))
+      (with-temp-buffer
+        (insert "1234")
+        (let* ((pos (1+ (point-min)))
+               (overlay (apply #'make-overlay pos pos nil advance-args)))
+          (goto-char pos)
+          (insert-before-markers "x")
+          (should (equal (1+ pos) (overlay-start overlay)))
+          (should (equal (1+ pos) (overlay-end overlay))))))))
 
 ;; +==========================================================================+
 ;; | Moving by deletions
@@ -1339,6 +1543,14 @@ with parameters from the *Messages* buffer modification."
         (overlay-put ov 'value i)))
     (should (eq 9 (get-char-property 1 'value)))))
 
+(ert-deftest buffer-tests--overlay-bug58479 ()
+  (with-temp-buffer
+    (insert "ab")
+    (let* ((pos (+ (point-min) 1))
+           (ol (make-overlay pos pos)))
+      (overlay-put ol 'my-prop 'set)
+      (should (null (get-char-property pos 'my-prop))))))
+
 
 ;; +==========================================================================+
 ;; | Other
@@ -1507,173 +1719,6753 @@ with parameters from the *Messages* buffer 
modification."
         (ovshould nonempty-eob-end 4 5)
         (ovshould empty-eob        5 5)))))
 
-(ert-deftest buffer-multibyte-overlong-sequences ()
-  (dolist (uni '("\xE0\x80\x80"
-                 "\xF0\x80\x80\x80"
-                 "\xF8\x8F\xBF\xBF\x80"))
-    (let ((multi (string-to-multibyte uni)))
-      (should
-       (string-equal
-        multi
-        (with-temp-buffer
-          (set-buffer-multibyte nil)
-          (insert uni)
-          (set-buffer-multibyte t)
-          (buffer-string)))))))
-
-;; https://debbugs.gnu.org/33492
-(ert-deftest buffer-tests-buffer-local-variables-undo ()
-  "Test that `buffer-undo-list' appears in `buffer-local-variables'."
+(ert-deftest test-overlay-randomly ()
+  "Exercise overlay code, but perform few assertions.
+
+This test works best when Emacs is configured with
+--enable-checking=yes.  This is a little bit like fuzz testing,
+except this test has no way to reduce to a minimal failing test
+case.  Regardless, by exercising many corner cases bugs can be
+found using Emacs' internal consistency assertions."
+  (let* (
+         ;; The size and slack for the test buffer size.
+         (buffer-size-target 1000)
+         (buffer-size-slack 200)
+
+         ;; Use up to 100 overlays.  We need not use more to observe
+         ;; reasonable variation in the overlay data structures.
+         (overlay-count-limit 100)
+
+         ;; This test maintains a vector of overlays.  Each iteration
+         ;; may append or erase one overlay.
+         (overlays (make-vector overlay-count-limit nil))
+         (overlay-count 0)
+
+         ;; The test is either slowly growing or shrinking the overlay
+         ;; count.  Deletions still occur while growing, and additions
+         ;; still occur while shrinking.  The GROWING variable only
+         ;; controls the relative probability of doing one or the
+         ;; other.
+         (growing t)
+
+         ;; Loop up to 1M times.
+         (iteration-count 0)
+         (iteration-target 100000))
+    (with-temp-buffer
+      (while (< (buffer-size) buffer-size-target)
+        (insert "Sed ut perspiciatis, unde omnis iste natus error sit 
voluptatem
+accusantium doloremque laudantium, totam rem aperiam eaque ipsa,
+quae ab illo inventore veritatis et quasi architecto beatae vitae
+dicta sunt, explicabo.  "))
+
+      (while (< iteration-count iteration-target)
+        (cl-incf iteration-count)
+
+        ;; Toggle GROWING if we've reached a size boundary.  The idea
+        ;; is to initially steadily increase the overlay count, then
+        ;; steadily decrease it, then repeat.
+        (when (and growing (= overlay-count overlay-count-limit))
+          (setq growing nil))
+        (when (and (not growing) (= overlay-count 0))
+          (setq growing t))
+
+        ;; Create or delete a random overlay according to a
+        ;; probability chosen by GROWING.
+        (let ((create-overlay (>= (random 100) (if growing 40 60))))
+          (cond
+           ;; Possibly create a new overlay in a random place in the
+           ;; buffer.  We have two easy choices.  We can choose the
+           ;; overlay BEGIN randomly, then choose its END among the
+           ;; valid remaining buffer posiitions.  Or we could choose
+           ;; the overlay width randomly, then choose a valid BEGIN.
+           ;; We take the former approach, because the overlay data
+           ;; structure is ordered primarily by BEGIN.
+           ((and create-overlay (< overlay-count overlay-count-limit))
+            (let* ((begin (random (buffer-size)))
+                   (end (+ begin (random (- (buffer-size) begin))))
+                   (ov (make-overlay begin end nil
+                                     (= 0 (random 2)) (= 0 (random 2)))))
+              (aset overlays overlay-count ov)
+              (cl-incf overlay-count)))
+           ((and (not create-overlay) (> overlay-count 0))
+
+            ;; Possibly delete a random overlay.
+            (let* ((last-index (1- overlay-count))
+                   (index (random overlay-count))
+                   (ov (aref overlays index)))
+              (when (< index last-index)
+                (aset overlays index (aref overlays last-index)))
+              (aset overlays last-index nil)
+              (cl-decf overlay-count)
+              (delete-overlay ov)))))
+
+        ;; Modify the buffer on occasion, which exercises the
+        ;; insert/remove gap logic in the overlay implementation.
+        (when (and (< (buffer-size) (+ buffer-size-target buffer-size-slack))
+                   (zerop (random 10)))
+          (goto-char (1+ (random (buffer-size))))
+          (insert (+ ?a (random 26))))
+        (when (and (> (buffer-size) (- buffer-size-target buffer-size-slack))
+                   (zerop (random 10)))
+          (goto-char (1+ (random (buffer-size))))
+          (delete-char 1))))))
+
+
+
+;; 
+===================================================================================+
+;; | Autogenerated insert/delete/narrow tests
+;; 
+===================================================================================+
+
+(when nil ;; Let's comment these out for now.
+
+;; (defun test-overlay-generate-test (name)
+;;   (interactive)
+;;   (with-temp-buffer
+;;     (let ((forms nil)
+;;           (buffer-size 64)
+;;           (noverlays 16)
+;;           (nforms 32)
+;;           (dist '(0.5 0.4 0.1)))
+;;       (cl-labels ((brand ()
+;;                     (+ (point-min)
+;;                        (random (1+ (- (point-max) (point-min)))))))
+;;         (cl-macrolet ((push-eval (form)
+;;                         `(cl-destructuring-bind (&rest args)
+;;                              (list ,@(cdr form))
+;;                            (push (cons ',(car form) args) forms)
+;;                            (apply #',(car form) args))))
+;;           (push-eval (insert (make-string buffer-size ?.)))
+;;           (dotimes (_ noverlays)
+;;             (push-eval (make-overlay (brand) (brand)
+;;                                      nil
+;;                                      (= 0 (random 2))
+;;                                      (= 0 (random 2)))))
+;;           (dotimes (_ nforms)
+;;             (push-eval (goto-char (brand)))
+;;             (pcase (/ (random 100) 100.0)
+;;               ((and x (guard (< x (nth 0 dist))))
+;;                (push-eval (insert (make-string (random 16) ?.))))
+;;               ((and x (guard (< x (+ (nth 0 dist) (nth 1 dist)))))
+;;                (push-eval (delete-char (random (1+ (- (point-max) 
(point)))))))
+;;               (_
+;;                (push-eval (widen))
+;;                (push-eval (narrow-to-region (brand) (brand))))))
+;;           `(ert-deftest ,name ()
+;;              (with-temp-buffer
+;;                ,@(nreverse forms)
+;;                (should (equal (test-overlay-regions)
+;;                               ',(test-overlay-regions))))))))))
+
+;; (defun test-overlay-generate-tests (n)
+;;   (let ((namefmt "overlay-autogenerated-test-%d")
+;;         (standard-output (current-buffer))
+;;         (print-length nil)
+;;         (print-level nil)
+;;         (print-quoted t))
+;;     (dotimes (i n)
+;;       (pp (test-overlay-generate-test (intern (format namefmt i))))
+;;       (terpri))))
+
+;; (progn (random "4711") (test-overlay-generate-tests 64))
+
+(ert-deftest overlay-autogenerated-test-0 nil
   (with-temp-buffer
-    (should (assq 'buffer-undo-list (buffer-local-variables)))))
-
-(ert-deftest buffer-tests-inhibit-buffer-hooks ()
-  "Test `get-buffer-create' argument INHIBIT-BUFFER-HOOKS."
-  (let* (run-bluh (bluh (lambda () (setq run-bluh t))))
-    (unwind-protect
-        (let* ( run-kbh  (kbh  (lambda () (setq run-kbh  t)))
-                run-kbqf (kbqf (lambda () (setq run-kbqf t))) )
-
-          ;; Inhibited.
-          (add-hook 'buffer-list-update-hook bluh)
-          (with-current-buffer (generate-new-buffer " foo" t)
-            (add-hook 'kill-buffer-hook kbh nil t)
-            (add-hook 'kill-buffer-query-functions kbqf nil t)
-            (kill-buffer))
-          (with-temp-buffer (ignore))
-          (with-output-to-string (ignore))
-          (should-not run-bluh)
-          (should-not run-kbh)
-          (should-not run-kbqf)
-
-          ;; Not inhibited.
-          (with-current-buffer (generate-new-buffer " foo")
-            (should run-bluh)
-            (add-hook 'kill-buffer-hook kbh nil t)
-            (add-hook 'kill-buffer-query-functions kbqf nil t)
-            (kill-buffer))
-          (should run-kbh)
-          (should run-kbqf))
-      (remove-hook 'buffer-list-update-hook bluh))))
-
-(ert-deftest buffer-tests-inhibit-buffer-hooks-indirect ()
-  "Indirect buffers do not call `get-buffer-create'."
-  (dolist (inhibit '(nil t))
-    (let ((base (get-buffer-create "foo" inhibit)))
-      (unwind-protect
-          (dotimes (_i 11)
-            (let* (flag*
-                   (flag (lambda () (prog1 t (setq flag* t))))
-                   (indirect (make-indirect-buffer base "foo[indirect]" nil
-                                                   inhibit)))
-              (unwind-protect
-                  (progn
-                    (with-current-buffer indirect
-                      (add-hook 'kill-buffer-query-functions flag nil t))
-                    (kill-buffer indirect)
-                    (if inhibit
-                        (should-not flag*)
-                      (should flag*)))
-                (let (kill-buffer-query-functions)
-                  (when (buffer-live-p indirect)
-                    (kill-buffer indirect))))))
-        (let (kill-buffer-query-functions)
-          (when (buffer-live-p base)
-            (kill-buffer base)))))))
-
-(ert-deftest zero-length-overlays-and-not ()
+    (insert "................................................................")
+    (make-overlay 63 7 nil t t)
+    (make-overlay 47 9 nil nil nil)
+    (make-overlay 50 43 nil nil nil)
+    (make-overlay 20 53 nil nil t)
+    (make-overlay 62 4 nil nil t)
+    (make-overlay 40 27 nil t t)
+    (make-overlay 58 44 nil t t)
+    (make-overlay 46 38 nil nil nil)
+    (make-overlay 51 28 nil t nil)
+    (make-overlay 12 53 nil t t)
+    (make-overlay 52 60 nil nil nil)
+    (make-overlay 13 47 nil nil nil)
+    (make-overlay 16 31 nil nil nil)
+    (make-overlay 9 48 nil t t)
+    (make-overlay 43 29 nil nil t)
+    (make-overlay 48 13 nil t nil)
+    (goto-char 44)
+    (delete-char 15)
+    (goto-char 19)
+    (widen)
+    (narrow-to-region 20 8)
+    (goto-char 9)
+    (delete-char 3)
+    (goto-char 16)
+    (insert "..............")
+    (goto-char 12)
+    (delete-char 15)
+    (goto-char 12)
+    (delete-char 4)
+    (goto-char 12)
+    (delete-char 0)
+    (goto-char 12)
+    (insert "......")
+    (goto-char 13)
+    (delete-char 5)
+    (goto-char 8)
+    (insert "...")
+    (goto-char 10)
+    (insert ".............")
+    (goto-char 14)
+    (insert ".......")
+    (goto-char 25)
+    (delete-char 4)
+    (goto-char 26)
+    (insert "...............")
+    (goto-char 27)
+    (insert "...")
+    (goto-char 29)
+    (delete-char 7)
+    (goto-char 24)
+    (insert "...")
+    (goto-char 30)
+    (insert "..........")
+    (goto-char 29)
+    (widen)
+    (narrow-to-region 34 41)
+    (goto-char 40)
+    (delete-char 0)
+    (goto-char 35)
+    (delete-char 4)
+    (goto-char 36)
+    (widen)
+    (narrow-to-region 80 66)
+    (goto-char 74)
+    (delete-char 5)
+    (goto-char 69)
+    (delete-char 5)
+    (goto-char 70)
+    (widen)
+    (narrow-to-region 50 71)
+    (goto-char 66)
+    (insert "...............")
+    (goto-char 54)
+    (insert "...............")
+    (goto-char 84)
+    (insert "....")
+    (goto-char 72)
+    (insert "...........")
+    (goto-char 84)
+    (insert "..........")
+    (goto-char 102)
+    (insert "")
+    (goto-char 80)
+    (delete-char 25)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((4 . 99)
+        (7 . 100)
+        (48 . 99)
+        (48 . 99)
+        (48 . 99)
+        (49 . 99)
+        (49 . 99)
+        (51 . 80)
+        (51 . 99)
+        (80 . 99)
+        (80 . 99)
+        (80 . 99)
+        (99 . 99)
+        (99 . 99)
+        (99 . 99)
+        (99 . 99))))))
+
+(ert-deftest overlay-autogenerated-test-1 nil
   (with-temp-buffer
-    (insert "hello")
-    (let ((long-overlay (make-overlay 2 4))
-          (zero-overlay (make-overlay 3 3)))
-      ;; Exclude.
-      (should (= (length (overlays-at 3)) 1))
-      (should (eq (car (overlays-at 3)) long-overlay))
-      ;; Include.
-      (should (= (length (overlays-in 3 3)) 2))
-      (should (memq long-overlay (overlays-in 3 3)))
-      (should (memq zero-overlay (overlays-in 3 3))))))
+    (insert "................................................................")
+    (make-overlay 17 27 nil nil nil)
+    (make-overlay 13 28 nil nil t)
+    (make-overlay 8 56 nil nil nil)
+    (make-overlay 34 64 nil nil nil)
+    (make-overlay 51 4 nil t t)
+    (make-overlay 1 19 nil nil nil)
+    (make-overlay 53 59 nil nil t)
+    (make-overlay 25 13 nil nil nil)
+    (make-overlay 19 28 nil t nil)
+    (make-overlay 33 23 nil t nil)
+    (make-overlay 10 46 nil t t)
+    (make-overlay 18 39 nil nil nil)
+    (make-overlay 1 49 nil t nil)
+    (make-overlay 57 21 nil t t)
+    (make-overlay 10 58 nil t t)
+    (make-overlay 39 49 nil nil t)
+    (goto-char 37)
+    (delete-char 9)
+    (goto-char 3)
+    (insert "......")
+    (goto-char 38)
+    (delete-char 14)
+    (goto-char 18)
+    (insert "..........")
+    (goto-char 53)
+    (insert "....")
+    (goto-char 49)
+    (delete-char 10)
+    (goto-char 11)
+    (delete-char 12)
+    (goto-char 17)
+    (delete-char 22)
+    (goto-char 8)
+    (insert ".")
+    (goto-char 16)
+    (insert "........")
+    (goto-char 16)
+    (delete-char 5)
+    (goto-char 11)
+    (delete-char 0)
+    (goto-char 22)
+    (insert ".......")
+    (goto-char 18)
+    (delete-char 11)
+    (goto-char 16)
+    (delete-char 0)
+    (goto-char 9)
+    (insert "...........")
+    (goto-char 7)
+    (insert "...............")
+    (goto-char 2)
+    (insert ".......")
+    (goto-char 21)
+    (delete-char 11)
+    (goto-char 13)
+    (insert "..............")
+    (goto-char 17)
+    (delete-char 3)
+    (goto-char 21)
+    (insert "......")
+    (goto-char 15)
+    (delete-char 32)
+    (goto-char 10)
+    (insert "........")
+    (goto-char 25)
+    (widen)
+    (narrow-to-region 15 20)
+    (goto-char 17)
+    (insert ".............")
+    (goto-char 22)
+    (insert "............")
+    (goto-char 21)
+    (delete-char 8)
+    (goto-char 36)
+    (delete-char 1)
+    (goto-char 32)
+    (delete-char 2)
+    (goto-char 21)
+    (insert ".....")
+    (goto-char 31)
+    (insert "......")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((1 . 58)
+        (1 . 58))))))
 
-(ert-deftest test-remove-overlays ()
+(ert-deftest overlay-autogenerated-test-2 nil
   (with-temp-buffer
-    (insert "foo")
-    (make-overlay (point) (point))
-    (should (= (length (overlays-in (point-min) (point-max))) 1))
-    (remove-overlays)
-    (should (= (length (overlays-in (point-min) (point-max))) 0)))
-
+    (insert "................................................................")
+    (make-overlay 15 59 nil t t)
+    (make-overlay 56 16 nil nil nil)
+    (make-overlay 65 51 nil t nil)
+    (make-overlay 14 24 nil t nil)
+    (make-overlay 28 9 nil t nil)
+    (make-overlay 58 50 nil t t)
+    (make-overlay 13 32 nil t t)
+    (make-overlay 12 21 nil t nil)
+    (make-overlay 60 23 nil t nil)
+    (make-overlay 39 38 nil nil t)
+    (make-overlay 15 64 nil t nil)
+    (make-overlay 17 21 nil nil t)
+    (make-overlay 46 23 nil t t)
+    (make-overlay 19 40 nil t nil)
+    (make-overlay 13 48 nil nil t)
+    (make-overlay 35 11 nil t nil)
+    (goto-char 41)
+    (delete-char 19)
+    (goto-char 45)
+    (insert "......")
+    (goto-char 3)
+    (delete-char 32)
+    (goto-char 19)
+    (insert "")
+    (goto-char 16)
+    (insert "...............")
+    (goto-char 2)
+    (insert "")
+    (goto-char 30)
+    (delete-char 0)
+    (goto-char 18)
+    (delete-char 17)
+    (goto-char 2)
+    (insert "...............")
+    (goto-char 12)
+    (insert "...")
+    (goto-char 2)
+    (insert ".............")
+    (goto-char 16)
+    (insert ".......")
+    (goto-char 15)
+    (insert ".......")
+    (goto-char 43)
+    (insert "......")
+    (goto-char 22)
+    (insert ".........")
+    (goto-char 25)
+    (delete-char 1)
+    (goto-char 38)
+    (insert "...............")
+    (goto-char 76)
+    (delete-char 3)
+    (goto-char 12)
+    (delete-char 5)
+    (goto-char 70)
+    (delete-char 9)
+    (goto-char 36)
+    (delete-char 4)
+    (goto-char 18)
+    (insert "...............")
+    (goto-char 52)
+    (delete-char 14)
+    (goto-char 23)
+    (insert "..........")
+    (goto-char 64)
+    (insert "...........")
+    (goto-char 68)
+    (delete-char 21)
+    (goto-char 71)
+    (insert "........")
+    (goto-char 28)
+    (delete-char 43)
+    (goto-char 25)
+    (insert "....")
+    (goto-char 2)
+    (insert "...............")
+    (goto-char 40)
+    (insert "....")
+    (goto-char 56)
+    (delete-char 2)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((51 . 51)
+        (51 . 51)
+        (51 . 51)
+        (51 . 51)
+        (51 . 51)
+        (51 . 51)
+        (51 . 51)
+        (51 . 51)
+        (51 . 51)
+        (51 . 51)
+        (51 . 51)
+        (51 . 51)
+        (51 . 51)
+        (51 . 51)
+        (51 . 51)
+        (51 . 58))))))
+
+(ert-deftest overlay-autogenerated-test-3 nil
   (with-temp-buffer
-    (insert "foo")
+    (insert "................................................................")
+    (make-overlay 53 38 nil t nil)
+    (make-overlay 17 40 nil t t)
+    (make-overlay 64 26 nil t t)
+    (make-overlay 48 24 nil t nil)
+    (make-overlay 21 18 nil nil nil)
+    (make-overlay 2 20 nil nil t)
+    (make-overlay 43 26 nil t t)
+    (make-overlay 56 28 nil t nil)
+    (make-overlay 19 51 nil nil nil)
+    (make-overlay 39 61 nil t nil)
+    (make-overlay 59 12 nil t nil)
+    (make-overlay 65 7 nil t nil)
+    (make-overlay 41 7 nil t nil)
+    (make-overlay 62 50 nil t nil)
+    (make-overlay 7 10 nil t t)
+    (make-overlay 45 28 nil t nil)
+    (goto-char 13)
+    (insert "...")
+    (goto-char 37)
+    (widen)
+    (narrow-to-region 2 10)
+    (goto-char 8)
+    (delete-char 1)
+    (goto-char 3)
+    (delete-char 6)
     (goto-char 2)
-    (make-overlay (point) (point))
-    ;; We only count zero-length overlays at the end of the buffer.
-    (should (= (length (overlays-in 1 2)) 0))
-    (narrow-to-region 1 2)
-    ;; We've now narrowed, so the zero-length overlay is at the end of
-    ;; the (accessible part of the) buffer.
-    (should (= (length (overlays-in 1 2)) 1))
-    (remove-overlays)
-    (should (= (length (overlays-in (point-min) (point-max))) 0))))
+    (insert "...........")
+    (goto-char 5)
+    (widen)
+    (narrow-to-region 55 70)
+    (goto-char 55)
+    (insert "......")
+    (goto-char 64)
+    (delete-char 12)
+    (goto-char 61)
+    (insert ".....")
+    (goto-char 64)
+    (insert "..............")
+    (goto-char 72)
+    (delete-char 6)
+    (goto-char 63)
+    (delete-char 12)
+    (goto-char 63)
+    (delete-char 2)
+    (goto-char 57)
+    (insert "..............")
+    (goto-char 68)
+    (insert "........")
+    (goto-char 77)
+    (delete-char 6)
+    (goto-char 77)
+    (insert ".............")
+    (goto-char 67)
+    (delete-char 0)
+    (goto-char 84)
+    (insert "........")
+    (goto-char 74)
+    (delete-char 12)
+    (goto-char 78)
+    (insert "...")
+    (goto-char 80)
+    (insert "............")
+    (goto-char 69)
+    (insert "......")
+    (goto-char 89)
+    (insert ".")
+    (goto-char 56)
+    (insert "....")
+    (goto-char 100)
+    (insert ".............")
+    (goto-char 114)
+    (delete-char 0)
+    (goto-char 61)
+    (widen)
+    (narrow-to-region 94 50)
+    (goto-char 55)
+    (insert "............")
+    (goto-char 53)
+    (insert ".............")
+    (goto-char 116)
+    (delete-char 3)
+    (goto-char 81)
+    (insert "...............")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((14 . 166)
+        (16 . 164)
+        (26 . 164)
+        (31 . 68)
+        (33 . 165)
+        (35 . 52)
+        (35 . 164)
+        (45 . 164)
+        (46 . 164))))))
+
+(ert-deftest overlay-autogenerated-test-4 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 25 15 nil nil t)
+    (make-overlay 8 13 nil nil nil)
+    (make-overlay 45 49 nil t t)
+    (make-overlay 22 13 nil t t)
+    (make-overlay 34 17 nil nil t)
+    (make-overlay 42 15 nil nil t)
+    (make-overlay 43 28 nil t t)
+    (make-overlay 3 28 nil t nil)
+    (make-overlay 32 61 nil nil t)
+    (make-overlay 30 64 nil t t)
+    (make-overlay 21 39 nil nil t)
+    (make-overlay 32 62 nil t nil)
+    (make-overlay 25 29 nil t nil)
+    (make-overlay 34 43 nil t nil)
+    (make-overlay 9 11 nil t nil)
+    (make-overlay 21 65 nil nil t)
+    (goto-char 21)
+    (delete-char 4)
+    (goto-char 25)
+    (insert "..")
+    (goto-char 53)
+    (insert "..")
+    (goto-char 2)
+    (insert "...............")
+    (goto-char 42)
+    (delete-char 36)
+    (goto-char 23)
+    (delete-char 12)
+    (goto-char 22)
+    (widen)
+    (narrow-to-region 30 32)
+    (goto-char 30)
+    (delete-char 0)
+    (goto-char 31)
+    (delete-char 1)
+    (goto-char 31)
+    (widen)
+    (narrow-to-region 28 27)
+    (goto-char 27)
+    (delete-char 1)
+    (goto-char 27)
+    (delete-char 0)
+    (goto-char 27)
+    (delete-char 0)
+    (goto-char 27)
+    (insert ".")
+    (goto-char 28)
+    (insert "......")
+    (goto-char 34)
+    (delete-char 0)
+    (goto-char 27)
+    (delete-char 5)
+    (goto-char 27)
+    (delete-char 1)
+    (goto-char 27)
+    (insert ".............")
+    (goto-char 30)
+    (insert "..............")
+    (goto-char 37)
+    (delete-char 15)
+    (goto-char 32)
+    (delete-char 2)
+    (goto-char 36)
+    (delete-char 1)
+    (goto-char 34)
+    (delete-char 0)
+    (goto-char 34)
+    (delete-char 1)
+    (goto-char 32)
+    (widen)
+    (narrow-to-region 24 19)
+    (goto-char 21)
+    (delete-char 1)
+    (goto-char 21)
+    (widen)
+    (narrow-to-region 11 38)
+    (goto-char 27)
+    (widen)
+    (narrow-to-region 20 22)
+    (goto-char 20)
+    (delete-char 1)
+    (goto-char 20)
+    (widen)
+    (narrow-to-region 36 4)
+    (goto-char 26)
+    (delete-char 9)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((18 . 25)
+        (21 . 21)
+        (21 . 21)
+        (21 . 22)
+        (21 . 22)
+        (21 . 27)
+        (21 . 27)
+        (22 . 25)
+        (22 . 27)
+        (22 . 28)
+        (26 . 27))))))
+
+(ert-deftest overlay-autogenerated-test-5 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 64 1 nil nil nil)
+    (make-overlay 38 43 nil nil nil)
+    (make-overlay 42 19 nil t nil)
+    (make-overlay 22 12 nil nil nil)
+    (make-overlay 12 30 nil t t)
+    (make-overlay 38 46 nil nil nil)
+    (make-overlay 18 23 nil nil nil)
+    (make-overlay 58 65 nil nil t)
+    (make-overlay 52 41 nil nil nil)
+    (make-overlay 12 26 nil nil nil)
+    (make-overlay 39 4 nil nil nil)
+    (make-overlay 20 1 nil nil t)
+    (make-overlay 36 60 nil nil nil)
+    (make-overlay 24 18 nil t nil)
+    (make-overlay 9 50 nil nil nil)
+    (make-overlay 19 17 nil t nil)
+    (goto-char 40)
+    (insert "")
+    (goto-char 64)
+    (insert ".............")
+    (goto-char 32)
+    (delete-char 40)
+    (goto-char 25)
+    (insert "...")
+    (goto-char 31)
+    (delete-char 1)
+    (goto-char 8)
+    (delete-char 14)
+    (goto-char 20)
+    (delete-char 5)
+    (goto-char 20)
+    (insert "...........")
+    (goto-char 20)
+    (insert ".........")
+    (goto-char 17)
+    (widen)
+    (narrow-to-region 11 21)
+    (goto-char 14)
+    (widen)
+    (narrow-to-region 9 24)
+    (goto-char 24)
+    (insert ".............")
+    (goto-char 30)
+    (widen)
+    (narrow-to-region 47 45)
+    (goto-char 47)
+    (insert ".")
+    (goto-char 46)
+    (widen)
+    (narrow-to-region 30 42)
+    (goto-char 32)
+    (delete-char 0)
+    (goto-char 34)
+    (insert ".......")
+    (goto-char 42)
+    (delete-char 4)
+    (goto-char 39)
+    (delete-char 6)
+    (goto-char 31)
+    (delete-char 6)
+    (goto-char 31)
+    (insert "............")
+    (goto-char 30)
+    (insert "......")
+    (goto-char 50)
+    (delete-char 0)
+    (goto-char 30)
+    (insert "....")
+    (goto-char 53)
+    (insert "............")
+    (goto-char 41)
+    (delete-char 12)
+    (goto-char 52)
+    (insert ".......")
+    (goto-char 56)
+    (insert "...........")
+    (goto-char 68)
+    (insert ".......")
+    (goto-char 52)
+    (insert "......")
+    (goto-char 71)
+    (delete-char 10)
+    (goto-char 47)
+    (insert "")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((20 . 89))))))
 
-(ert-deftest test-kill-buffer-auto-save-default ()
-  (ert-with-temp-file file
-    (let (auto-save)
-      ;; Always answer yes.
-      (cl-letf (((symbol-function #'yes-or-no-p) (lambda (_) t)))
-        (unwind-protect
-            (progn
-              (find-file file)
-              (auto-save-mode t)
-              (insert "foo\n")
-              (should buffer-auto-save-file-name)
-              (setq auto-save buffer-auto-save-file-name)
-              (do-auto-save)
-              (should (file-exists-p auto-save))
-              (kill-buffer (current-buffer))
-              (should (file-exists-p auto-save)))
-          (when auto-save
-            (ignore-errors (delete-file auto-save))))))))
-
-(ert-deftest test-kill-buffer-auto-save-delete ()
-  (ert-with-temp-file file
-    (let (auto-save)
-      (should (file-exists-p file))
-      (setq kill-buffer-delete-auto-save-files t)
-      ;; Always answer yes.
-      (cl-letf (((symbol-function #'yes-or-no-p) (lambda (_) t)))
-        (unwind-protect
-            (progn
-              (find-file file)
-              (auto-save-mode t)
-              (insert "foo\n")
-              (should buffer-auto-save-file-name)
-              (setq auto-save buffer-auto-save-file-name)
-              (do-auto-save)
-              (should (file-exists-p auto-save))
-              ;; This should delete the auto-save file.
-              (kill-buffer (current-buffer))
-              (should-not (file-exists-p auto-save)))
-          (ignore-errors (delete-file file))
-          (when auto-save
-            (ignore-errors (delete-file auto-save)))))
-      ;; Answer no to deletion.
-      (cl-letf (((symbol-function #'yes-or-no-p)
-                 (lambda (prompt)
-                   (not (string-search "Delete auto-save file" prompt)))))
-        (unwind-protect
-            (progn
-              (find-file file)
-              (auto-save-mode t)
-              (insert "foo\n")
-              (should buffer-auto-save-file-name)
-              (setq auto-save buffer-auto-save-file-name)
-              (do-auto-save)
-              (should (file-exists-p auto-save))
-              ;; This should not delete the auto-save file.
-              (kill-buffer (current-buffer))
-              (should (file-exists-p auto-save)))
-          (when auto-save
-            (ignore-errors (delete-file auto-save))))))))
+(ert-deftest overlay-autogenerated-test-6 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 28 59 nil nil nil)
+    (make-overlay 36 21 nil t t)
+    (make-overlay 60 19 nil t nil)
+    (make-overlay 26 30 nil t nil)
+    (make-overlay 47 27 nil nil t)
+    (make-overlay 8 25 nil t t)
+    (make-overlay 57 43 nil t t)
+    (make-overlay 28 61 nil nil t)
+    (make-overlay 42 31 nil nil t)
+    (make-overlay 15 44 nil t nil)
+    (make-overlay 56 38 nil nil nil)
+    (make-overlay 39 44 nil nil t)
+    (make-overlay 50 6 nil t nil)
+    (make-overlay 6 19 nil t nil)
+    (make-overlay 50 44 nil t t)
+    (make-overlay 34 60 nil nil t)
+    (goto-char 27)
+    (insert "...............")
+    (goto-char 23)
+    (insert "..............")
+    (goto-char 50)
+    (widen)
+    (narrow-to-region 53 67)
+    (goto-char 60)
+    (delete-char 0)
+    (goto-char 54)
+    (insert "......")
+    (goto-char 64)
+    (delete-char 1)
+    (goto-char 66)
+    (delete-char 3)
+    (goto-char 58)
+    (insert ".............")
+    (goto-char 58)
+    (insert ".........")
+    (goto-char 76)
+    (insert "...........")
+    (goto-char 57)
+    (insert "....")
+    (goto-char 106)
+    (widen)
+    (narrow-to-region 5 45)
+    (goto-char 31)
+    (delete-char 8)
+    (goto-char 36)
+    (insert "...")
+    (goto-char 6)
+    (insert "........")
+    (goto-char 33)
+    (insert ".............")
+    (goto-char 38)
+    (delete-char 3)
+    (goto-char 28)
+    (delete-char 6)
+    (goto-char 42)
+    (widen)
+    (narrow-to-region 17 25)
+    (goto-char 19)
+    (insert "..............")
+    (goto-char 37)
+    (delete-char 1)
+    (goto-char 22)
+    (delete-char 9)
+    (goto-char 28)
+    (insert "..............")
+    (goto-char 37)
+    (delete-char 3)
+    (goto-char 18)
+    (insert "...............")
+    (goto-char 30)
+    (widen)
+    (narrow-to-region 68 25)
+    (goto-char 38)
+    (delete-char 22)
+    (goto-char 43)
+    (widen)
+    (narrow-to-region 47 96)
+    (goto-char 86)
+    (insert ".")
+    (goto-char 63)
+    (insert "......")
+    (goto-char 78)
+    (widen)
+    (narrow-to-region 61 27)
+    (goto-char 43)
+    (delete-char 8)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((14 . 38)
+        (14 . 132)
+        (16 . 43)
+        (38 . 118)
+        (38 . 126)
+        (38 . 142)
+        (44 . 115)
+        (45 . 129))))))
+
+(ert-deftest overlay-autogenerated-test-7 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 13 50 nil t nil)
+    (make-overlay 28 44 nil nil t)
+    (make-overlay 56 27 nil t nil)
+    (make-overlay 8 34 nil nil nil)
+    (make-overlay 22 8 nil nil t)
+    (make-overlay 8 28 nil t nil)
+    (make-overlay 65 31 nil nil t)
+    (make-overlay 44 8 nil nil nil)
+    (make-overlay 52 64 nil nil t)
+    (make-overlay 52 27 nil t t)
+    (make-overlay 47 32 nil nil nil)
+    (make-overlay 18 62 nil nil nil)
+    (make-overlay 18 24 nil t t)
+    (make-overlay 33 46 nil nil t)
+    (make-overlay 20 8 nil t nil)
+    (make-overlay 51 51 nil t nil)
+    (goto-char 2)
+    (delete-char 46)
+    (goto-char 12)
+    (delete-char 5)
+    (goto-char 2)
+    (delete-char 12)
+    (goto-char 2)
+    (insert "..")
+    (goto-char 2)
+    (widen)
+    (narrow-to-region 2 4)
+    (goto-char 4)
+    (insert "......")
+    (goto-char 4)
+    (widen)
+    (narrow-to-region 4 6)
+    (goto-char 5)
+    (insert "")
+    (goto-char 6)
+    (insert "...............")
+    (goto-char 9)
+    (insert "...")
+    (goto-char 7)
+    (delete-char 13)
+    (goto-char 8)
+    (delete-char 1)
+    (goto-char 9)
+    (insert "...............")
+    (goto-char 24)
+    (delete-char 1)
+    (goto-char 15)
+    (insert "...............")
+    (goto-char 16)
+    (insert "............")
+    (goto-char 17)
+    (delete-char 8)
+    (goto-char 36)
+    (widen)
+    (narrow-to-region 47 38)
+    (goto-char 43)
+    (delete-char 0)
+    (goto-char 46)
+    (delete-char 0)
+    (goto-char 40)
+    (delete-char 4)
+    (goto-char 39)
+    (insert ".......")
+    (goto-char 50)
+    (delete-char 0)
+    (goto-char 47)
+    (insert "...........")
+    (goto-char 45)
+    (insert ".....")
+    (goto-char 38)
+    (delete-char 3)
+    (goto-char 59)
+    (delete-char 1)
+    (goto-char 42)
+    (insert "...............")
+    (goto-char 65)
+    (insert "...........")
+    (goto-char 73)
+    (delete-char 13)
+    (goto-char 72)
+    (insert "....")
+    (goto-char 47)
+    (insert "..")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((2 . 81)
+        (2 . 81)
+        (2 . 81)
+        (2 . 81)
+        (2 . 81)
+        (81 . 81)
+        (81 . 81))))))
+
+(ert-deftest overlay-autogenerated-test-8 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 20 6 nil t nil)
+    (make-overlay 48 13 nil t nil)
+    (make-overlay 58 65 nil nil t)
+    (make-overlay 63 65 nil nil nil)
+    (make-overlay 42 40 nil t t)
+    (make-overlay 40 6 nil nil t)
+    (make-overlay 37 46 nil t nil)
+    (make-overlay 4 14 nil nil nil)
+    (make-overlay 58 44 nil t t)
+    (make-overlay 14 16 nil nil t)
+    (make-overlay 31 61 nil t nil)
+    (make-overlay 34 3 nil nil nil)
+    (make-overlay 11 16 nil t nil)
+    (make-overlay 19 42 nil nil t)
+    (make-overlay 30 9 nil nil t)
+    (make-overlay 63 52 nil t t)
+    (goto-char 57)
+    (delete-char 2)
+    (goto-char 8)
+    (insert "........")
+    (goto-char 30)
+    (insert "...........")
+    (goto-char 35)
+    (insert "...........")
+    (goto-char 66)
+    (insert "...............")
+    (goto-char 53)
+    (delete-char 15)
+    (goto-char 75)
+    (delete-char 10)
+    (goto-char 62)
+    (delete-char 21)
+    (goto-char 52)
+    (delete-char 10)
+    (goto-char 10)
+    (insert "............")
+    (goto-char 42)
+    (insert "...........")
+    (goto-char 68)
+    (insert ".............")
+    (goto-char 12)
+    (insert "........")
+    (goto-char 1)
+    (insert "...............")
+    (goto-char 89)
+    (insert "")
+    (goto-char 94)
+    (insert ".............")
+    (goto-char 57)
+    (insert "...........")
+    (goto-char 130)
+    (insert "...")
+    (goto-char 69)
+    (insert "..")
+    (goto-char 101)
+    (insert "......")
+    (goto-char 128)
+    (delete-char 13)
+    (goto-char 19)
+    (delete-char 100)
+    (goto-char 22)
+    (insert "..")
+    (goto-char 13)
+    (widen)
+    (narrow-to-region 30 16)
+    (goto-char 19)
+    (insert "..........")
+    (goto-char 22)
+    (delete-char 3)
+    (goto-char 19)
+    (insert ".........")
+    (goto-char 17)
+    (insert "..")
+    (goto-char 16)
+    (insert "............")
+    (goto-char 47)
+    (insert ".")
+    (goto-char 50)
+    (insert "..........")
+    (goto-char 70)
+    (delete-char 1)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((32 . 75)
+        (33 . 33)
+        (33 . 33)
+        (33 . 33)
+        (33 . 60)
+        (33 . 75)
+        (33 . 75)
+        (33 . 75)
+        (60 . 75))))))
+
+(ert-deftest overlay-autogenerated-test-9 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 58 13 nil nil nil)
+    (make-overlay 29 4 nil nil t)
+    (make-overlay 3 53 nil nil nil)
+    (make-overlay 31 9 nil t t)
+    (make-overlay 48 30 nil nil nil)
+    (make-overlay 43 50 nil nil nil)
+    (make-overlay 7 27 nil nil t)
+    (make-overlay 30 59 nil nil nil)
+    (make-overlay 42 25 nil nil t)
+    (make-overlay 15 13 nil t t)
+    (make-overlay 39 11 nil t t)
+    (make-overlay 21 62 nil t t)
+    (make-overlay 35 2 nil t nil)
+    (make-overlay 60 53 nil nil t)
+    (make-overlay 64 8 nil nil t)
+    (make-overlay 58 59 nil t t)
+    (goto-char 28)
+    (insert ".............")
+    (goto-char 28)
+    (insert "...............")
+    (goto-char 71)
+    (insert ".......")
+    (goto-char 65)
+    (insert "......")
+    (goto-char 3)
+    (delete-char 12)
+    (goto-char 79)
+    (delete-char 11)
+    (goto-char 65)
+    (widen)
+    (narrow-to-region 12 53)
+    (goto-char 38)
+    (insert ".......")
+    (goto-char 20)
+    (insert ".........")
+    (goto-char 27)
+    (insert "...........")
+    (goto-char 75)
+    (insert "........")
+    (goto-char 85)
+    (insert "............")
+    (goto-char 52)
+    (insert "..........")
+    (goto-char 16)
+    (delete-char 8)
+    (goto-char 15)
+    (insert "...............")
+    (goto-char 112)
+    (insert "")
+    (goto-char 61)
+    (insert "..")
+    (goto-char 29)
+    (delete-char 34)
+    (goto-char 52)
+    (delete-char 32)
+    (goto-char 43)
+    (insert "........")
+    (goto-char 45)
+    (insert "..")
+    (goto-char 35)
+    (insert "...........")
+    (goto-char 29)
+    (insert ".......")
+    (goto-char 75)
+    (widen)
+    (narrow-to-region 69 55)
+    (goto-char 67)
+    (delete-char 2)
+    (goto-char 66)
+    (delete-char 0)
+    (goto-char 62)
+    (delete-char 1)
+    (goto-char 61)
+    (delete-char 3)
+    (goto-char 63)
+    (insert ".")
+    (goto-char 56)
+    (insert ".....")
+    (goto-char 67)
+    (insert ".............")
+    (goto-char 76)
+    (delete-char 3)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((2 . 90)
+        (3 . 90)
+        (3 . 90)
+        (3 . 99)
+        (3 . 117)
+        (3 . 117)
+        (3 . 120)
+        (9 . 118)
+        (13 . 102))))))
+
+(ert-deftest overlay-autogenerated-test-10 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 16 60 nil nil nil)
+    (make-overlay 36 53 nil nil nil)
+    (make-overlay 44 39 nil t t)
+    (make-overlay 61 47 nil t t)
+    (make-overlay 58 39 nil nil t)
+    (make-overlay 23 54 nil nil t)
+    (make-overlay 65 59 nil t t)
+    (make-overlay 13 57 nil nil t)
+    (make-overlay 22 64 nil nil t)
+    (make-overlay 16 19 nil nil nil)
+    (make-overlay 16 1 nil nil t)
+    (make-overlay 28 21 nil t t)
+    (make-overlay 10 62 nil nil nil)
+    (make-overlay 12 18 nil nil t)
+    (make-overlay 15 5 nil nil t)
+    (make-overlay 36 31 nil nil t)
+    (goto-char 42)
+    (insert "...")
+    (goto-char 25)
+    (delete-char 28)
+    (goto-char 30)
+    (delete-char 10)
+    (goto-char 8)
+    (delete-char 9)
+    (goto-char 5)
+    (insert "........")
+    (goto-char 6)
+    (delete-char 2)
+    (goto-char 4)
+    (insert "")
+    (goto-char 21)
+    (insert ".............")
+    (goto-char 6)
+    (delete-char 33)
+    (goto-char 1)
+    (delete-char 1)
+    (goto-char 6)
+    (insert "..........")
+    (goto-char 8)
+    (insert "...........")
+    (goto-char 21)
+    (insert "........")
+    (goto-char 16)
+    (delete-char 18)
+    (goto-char 5)
+    (insert "...")
+    (goto-char 5)
+    (delete-char 8)
+    (goto-char 11)
+    (insert ".")
+    (goto-char 1)
+    (insert ".......")
+    (goto-char 9)
+    (delete-char 9)
+    (goto-char 5)
+    (insert "")
+    (goto-char 8)
+    (delete-char 0)
+    (goto-char 11)
+    (insert "..............")
+    (goto-char 12)
+    (insert "")
+    (goto-char 11)
+    (delete-char 8)
+    (goto-char 7)
+    (delete-char 3)
+    (goto-char 5)
+    (delete-char 3)
+    (goto-char 1)
+    (delete-char 8)
+    (goto-char 1)
+    (insert "....")
+    (goto-char 1)
+    (insert "..")
+    (goto-char 7)
+    (insert "...")
+    (goto-char 8)
+    (widen)
+    (narrow-to-region 9 11)
+    (goto-char 11)
+    (delete-char 0)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((1 . 10)
+        (1 . 10)
+        (1 . 10)
+        (1 . 10)
+        (1 . 10)
+        (1 . 12)
+        (1 . 12)
+        (1 . 12)
+        (10 . 10)
+        (10 . 10)
+        (10 . 12))))))
+
+(ert-deftest overlay-autogenerated-test-11 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 33 18 nil nil nil)
+    (make-overlay 56 38 nil t nil)
+    (make-overlay 2 45 nil nil t)
+    (make-overlay 19 55 nil nil t)
+    (make-overlay 28 42 nil t t)
+    (make-overlay 50 29 nil t nil)
+    (make-overlay 40 63 nil nil nil)
+    (make-overlay 13 2 nil nil t)
+    (make-overlay 26 7 nil t t)
+    (make-overlay 22 25 nil nil nil)
+    (make-overlay 14 14 nil t nil)
+    (make-overlay 15 39 nil t t)
+    (make-overlay 51 22 nil t t)
+    (make-overlay 58 5 nil t nil)
+    (make-overlay 16 10 nil nil nil)
+    (make-overlay 32 33 nil t nil)
+    (goto-char 40)
+    (delete-char 20)
+    (goto-char 45)
+    (delete-char 0)
+    (goto-char 6)
+    (insert "..")
+    (goto-char 45)
+    (insert "...")
+    (goto-char 26)
+    (insert "...............")
+    (goto-char 27)
+    (insert "...........")
+    (goto-char 38)
+    (insert "......")
+    (goto-char 62)
+    (insert "...............")
+    (goto-char 18)
+    (insert "...........")
+    (goto-char 99)
+    (widen)
+    (narrow-to-region 37 17)
+    (goto-char 29)
+    (delete-char 2)
+    (goto-char 28)
+    (delete-char 2)
+    (goto-char 17)
+    (insert ".....")
+    (goto-char 21)
+    (widen)
+    (narrow-to-region 34 96)
+    (goto-char 44)
+    (delete-char 22)
+    (goto-char 39)
+    (insert "..")
+    (goto-char 53)
+    (insert "...............")
+    (goto-char 58)
+    (insert ".............")
+    (goto-char 93)
+    (insert ".........")
+    (goto-char 78)
+    (widen)
+    (narrow-to-region 27 104)
+    (goto-char 93)
+    (delete-char 11)
+    (goto-char 59)
+    (insert "....")
+    (goto-char 59)
+    (insert "..............")
+    (goto-char 74)
+    (delete-char 5)
+    (goto-char 70)
+    (insert ".")
+    (goto-char 37)
+    (insert "...........")
+    (goto-char 34)
+    (delete-char 46)
+    (goto-char 49)
+    (insert "......")
+    (goto-char 55)
+    (insert "...")
+    (goto-char 42)
+    (insert "...")
+    (goto-char 70)
+    (delete-char 8)
+    (goto-char 48)
+    (delete-char 28)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((2 . 62)
+        (5 . 62)
+        (9 . 34)
+        (22 . 61)
+        (33 . 55)
+        (33 . 62)
+        (34 . 34)
+        (34 . 62))))))
+
+(ert-deftest overlay-autogenerated-test-12 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 18 50 nil nil nil)
+    (make-overlay 63 3 nil nil t)
+    (make-overlay 44 20 nil t t)
+    (make-overlay 58 38 nil nil t)
+    (make-overlay 3 17 nil t nil)
+    (make-overlay 31 62 nil t nil)
+    (make-overlay 12 17 nil t nil)
+    (make-overlay 17 52 nil nil nil)
+    (make-overlay 9 35 nil nil nil)
+    (make-overlay 17 38 nil nil nil)
+    (make-overlay 53 54 nil nil t)
+    (make-overlay 65 34 nil t nil)
+    (make-overlay 12 33 nil t nil)
+    (make-overlay 54 58 nil nil nil)
+    (make-overlay 42 26 nil t nil)
+    (make-overlay 2 4 nil t nil)
+    (goto-char 4)
+    (delete-char 26)
+    (goto-char 39)
+    (insert ".")
+    (goto-char 2)
+    (delete-char 14)
+    (goto-char 16)
+    (widen)
+    (narrow-to-region 19 1)
+    (goto-char 7)
+    (delete-char 9)
+    (goto-char 6)
+    (insert ".........")
+    (goto-char 6)
+    (insert "..........")
+    (goto-char 16)
+    (insert ".............")
+    (goto-char 36)
+    (delete-char 1)
+    (goto-char 4)
+    (insert "..........")
+    (goto-char 49)
+    (delete-char 2)
+    (goto-char 16)
+    (insert "............")
+    (goto-char 52)
+    (widen)
+    (narrow-to-region 36 38)
+    (goto-char 37)
+    (delete-char 1)
+    (goto-char 37)
+    (insert ".............")
+    (goto-char 46)
+    (insert ".")
+    (goto-char 40)
+    (delete-char 5)
+    (goto-char 45)
+    (delete-char 0)
+    (goto-char 46)
+    (delete-char 0)
+    (goto-char 40)
+    (insert "..........")
+    (goto-char 39)
+    (delete-char 4)
+    (goto-char 39)
+    (delete-char 3)
+    (goto-char 40)
+    (widen)
+    (narrow-to-region 8 9)
+    (goto-char 8)
+    (delete-char 1)
+    (goto-char 8)
+    (delete-char 0)
+    (goto-char 8)
+    (widen)
+    (narrow-to-region 45 15)
+    (goto-char 40)
+    (insert "...............")
+    (goto-char 29)
+    (delete-char 7)
+    (goto-char 30)
+    (delete-char 6)
+    (goto-char 21)
+    (delete-char 9)
+    (goto-char 22)
+    (insert "...............")
+    (goto-char 51)
+    (insert "..............")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((2 . 92)
+        (2 . 92)
+        (2 . 93)
+        (2 . 96)
+        (2 . 97)
+        (2 . 99))))))
+
+(ert-deftest overlay-autogenerated-test-13 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 18 30 nil t t)
+    (make-overlay 54 37 nil nil t)
+    (make-overlay 16 61 nil nil t)
+    (make-overlay 58 7 nil nil t)
+    (make-overlay 27 39 nil nil t)
+    (make-overlay 39 31 nil nil t)
+    (make-overlay 11 47 nil nil nil)
+    (make-overlay 47 40 nil t t)
+    (make-overlay 27 18 nil nil nil)
+    (make-overlay 33 26 nil nil t)
+    (make-overlay 55 4 nil t t)
+    (make-overlay 62 50 nil t t)
+    (make-overlay 47 65 nil t t)
+    (make-overlay 17 23 nil nil t)
+    (make-overlay 30 31 nil t nil)
+    (make-overlay 10 37 nil t nil)
+    (goto-char 8)
+    (delete-char 6)
+    (goto-char 56)
+    (delete-char 0)
+    (goto-char 28)
+    (insert ".........")
+    (goto-char 19)
+    (insert "..............")
+    (goto-char 4)
+    (delete-char 28)
+    (goto-char 49)
+    (delete-char 4)
+    (goto-char 2)
+    (insert "............")
+    (goto-char 10)
+    (delete-char 37)
+    (goto-char 19)
+    (delete-char 2)
+    (goto-char 20)
+    (delete-char 0)
+    (goto-char 16)
+    (insert "..")
+    (goto-char 8)
+    (widen)
+    (narrow-to-region 12 3)
+    (goto-char 10)
+    (delete-char 2)
+    (goto-char 9)
+    (insert "..")
+    (goto-char 12)
+    (insert "...............")
+    (goto-char 25)
+    (insert ".....")
+    (goto-char 10)
+    (widen)
+    (narrow-to-region 42 18)
+    (goto-char 20)
+    (insert ".......")
+    (goto-char 18)
+    (insert ".........")
+    (goto-char 55)
+    (delete-char 3)
+    (goto-char 48)
+    (insert ".......")
+    (goto-char 52)
+    (delete-char 6)
+    (goto-char 45)
+    (delete-char 11)
+    (goto-char 27)
+    (delete-char 13)
+    (goto-char 22)
+    (insert "...........")
+    (goto-char 19)
+    (delete-char 15)
+    (goto-char 20)
+    (delete-char 0)
+    (goto-char 23)
+    (widen)
+    (narrow-to-region 12 25)
+    (goto-char 16)
+    (insert "..........")
+    (goto-char 25)
+    (widen)
+    (narrow-to-region 2 38)
+    (goto-char 34)
+    (delete-char 0)
+    (goto-char 31)
+    (insert "...............")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((12 . 12)
+        (12 . 12)
+        (12 . 12)
+        (12 . 12)
+        (12 . 53)
+        (12 . 53)
+        (12 . 53)
+        (12 . 53)
+        (12 . 53)
+        (12 . 53)
+        (12 . 55))))))
+
+(ert-deftest overlay-autogenerated-test-14 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 29 37 nil t nil)
+    (make-overlay 15 44 nil nil nil)
+    (make-overlay 31 34 nil nil t)
+    (make-overlay 35 33 nil t t)
+    (make-overlay 4 27 nil t t)
+    (make-overlay 37 5 nil nil t)
+    (make-overlay 58 19 nil nil t)
+    (make-overlay 57 47 nil nil t)
+    (make-overlay 49 5 nil t t)
+    (make-overlay 21 59 nil t t)
+    (make-overlay 42 33 nil t nil)
+    (make-overlay 22 16 nil t t)
+    (make-overlay 9 51 nil t nil)
+    (make-overlay 20 24 nil nil t)
+    (make-overlay 21 7 nil t t)
+    (make-overlay 58 52 nil t t)
+    (goto-char 39)
+    (widen)
+    (narrow-to-region 55 54)
+    (goto-char 54)
+    (insert ".............")
+    (goto-char 55)
+    (insert "............")
+    (goto-char 66)
+    (delete-char 10)
+    (goto-char 62)
+    (insert "...............")
+    (goto-char 82)
+    (delete-char 2)
+    (goto-char 82)
+    (delete-char 0)
+    (goto-char 76)
+    (insert "..............")
+    (goto-char 60)
+    (insert ".............")
+    (goto-char 71)
+    (insert "...............")
+    (goto-char 122)
+    (delete-char 0)
+    (goto-char 93)
+    (delete-char 3)
+    (goto-char 108)
+    (delete-char 1)
+    (goto-char 121)
+    (insert "........")
+    (goto-char 92)
+    (insert "")
+    (goto-char 103)
+    (insert "..........")
+    (goto-char 85)
+    (delete-char 13)
+    (goto-char 116)
+    (delete-char 7)
+    (goto-char 103)
+    (widen)
+    (narrow-to-region 60 27)
+    (goto-char 28)
+    (delete-char 16)
+    (goto-char 35)
+    (insert ".......")
+    (goto-char 47)
+    (insert "........")
+    (goto-char 38)
+    (delete-char 1)
+    (goto-char 43)
+    (insert "..........")
+    (goto-char 59)
+    (insert "........")
+    (goto-char 57)
+    (insert "........")
+    (goto-char 36)
+    (insert "...........")
+    (goto-char 82)
+    (delete-char 11)
+    (goto-char 67)
+    (insert "..........")
+    (goto-char 46)
+    (delete-char 1)
+    (goto-char 47)
+    (insert "......")
+    (goto-char 69)
+    (delete-char 7)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((5 . 28)
+        (5 . 33)
+        (9 . 35)
+        (15 . 28)
+        (19 . 154)
+        (21 . 155)
+        (28 . 28)
+        (28 . 28)
+        (28 . 28)
+        (28 . 28)
+        (31 . 153)
+        (58 . 154))))))
+
+(ert-deftest overlay-autogenerated-test-15 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 3 19 nil t t)
+    (make-overlay 11 18 nil t nil)
+    (make-overlay 28 51 nil nil t)
+    (make-overlay 29 15 nil t t)
+    (make-overlay 46 57 nil t t)
+    (make-overlay 26 24 nil nil nil)
+    (make-overlay 29 43 nil nil nil)
+    (make-overlay 54 29 nil nil nil)
+    (make-overlay 34 52 nil t nil)
+    (make-overlay 10 32 nil nil nil)
+    (make-overlay 28 34 nil nil t)
+    (make-overlay 11 43 nil nil nil)
+    (make-overlay 18 50 nil t t)
+    (make-overlay 28 39 nil nil nil)
+    (make-overlay 62 62 nil t t)
+    (make-overlay 30 62 nil t nil)
+    (goto-char 30)
+    (widen)
+    (narrow-to-region 6 22)
+    (goto-char 9)
+    (insert "..")
+    (goto-char 12)
+    (insert ".............")
+    (goto-char 29)
+    (insert "..............")
+    (goto-char 47)
+    (insert "........")
+    (goto-char 46)
+    (insert ".............")
+    (goto-char 55)
+    (insert "..........")
+    (goto-char 62)
+    (insert "...............")
+    (goto-char 47)
+    (delete-char 49)
+    (goto-char 11)
+    (insert "...........")
+    (goto-char 40)
+    (delete-char 1)
+    (goto-char 27)
+    (insert "..............")
+    (goto-char 51)
+    (insert "......")
+    (goto-char 60)
+    (delete-char 10)
+    (goto-char 37)
+    (insert ".........")
+    (goto-char 69)
+    (insert ".")
+    (goto-char 36)
+    (insert "............")
+    (goto-char 75)
+    (insert ".............")
+    (goto-char 21)
+    (widen)
+    (narrow-to-region 44 21)
+    (goto-char 37)
+    (insert ".............")
+    (goto-char 55)
+    (widen)
+    (narrow-to-region 84 28)
+    (goto-char 58)
+    (widen)
+    (narrow-to-region 96 49)
+    (goto-char 62)
+    (delete-char 0)
+    (goto-char 72)
+    (delete-char 24)
+    (goto-char 61)
+    (widen)
+    (narrow-to-region 105 83)
+    (goto-char 96)
+    (widen)
+    (narrow-to-region 109 46)
+    (goto-char 95)
+    (delete-char 4)
+    (goto-char 81)
+    (insert ".")
+    (goto-char 51)
+    (delete-char 8)
+    (goto-char 52)
+    (insert ".")
+    (goto-char 60)
+    (delete-char 10)
+    (goto-char 50)
+    (insert "......")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((3 . 81)
+        (23 . 88)
+        (66 . 99)
+        (69 . 81)
+        (78 . 85)
+        (81 . 106)
+        (84 . 85)
+        (85 . 90)
+        (85 . 95)
+        (85 . 99)
+        (85 . 107)
+        (85 . 110)
+        (86 . 118)
+        (90 . 108))))))
+
+(ert-deftest overlay-autogenerated-test-16 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 3 55 nil t nil)
+    (make-overlay 45 47 nil nil nil)
+    (make-overlay 23 57 nil t t)
+    (make-overlay 64 55 nil nil nil)
+    (make-overlay 37 26 nil t t)
+    (make-overlay 29 38 nil nil t)
+    (make-overlay 33 3 nil t t)
+    (make-overlay 49 16 nil t nil)
+    (make-overlay 35 56 nil t t)
+    (make-overlay 9 39 nil nil nil)
+    (make-overlay 2 61 nil nil nil)
+    (make-overlay 59 26 nil nil t)
+    (make-overlay 5 50 nil t t)
+    (make-overlay 19 19 nil nil t)
+    (make-overlay 64 21 nil t nil)
+    (make-overlay 21 8 nil nil t)
+    (goto-char 17)
+    (insert ".....")
+    (goto-char 29)
+    (insert "............")
+    (goto-char 42)
+    (delete-char 38)
+    (goto-char 24)
+    (insert "")
+    (goto-char 9)
+    (delete-char 2)
+    (goto-char 20)
+    (insert "..")
+    (goto-char 27)
+    (delete-char 8)
+    (goto-char 25)
+    (delete-char 6)
+    (goto-char 8)
+    (delete-char 21)
+    (goto-char 9)
+    (insert "..............")
+    (goto-char 3)
+    (insert "....")
+    (goto-char 8)
+    (delete-char 18)
+    (goto-char 6)
+    (widen)
+    (narrow-to-region 5 8)
+    (goto-char 5)
+    (delete-char 3)
+    (goto-char 5)
+    (insert "...")
+    (goto-char 8)
+    (insert "..........")
+    (goto-char 5)
+    (insert "")
+    (goto-char 7)
+    (delete-char 8)
+    (goto-char 8)
+    (widen)
+    (narrow-to-region 2 2)
+    (goto-char 2)
+    (delete-char 0)
+    (goto-char 2)
+    (delete-char 0)
+    (goto-char 2)
+    (delete-char 0)
+    (goto-char 2)
+    (delete-char 0)
+    (goto-char 2)
+    (widen)
+    (narrow-to-region 10 3)
+    (goto-char 8)
+    (delete-char 2)
+    (goto-char 7)
+    (insert ".......")
+    (goto-char 8)
+    (delete-char 3)
+    (goto-char 12)
+    (insert "..")
+    (goto-char 9)
+    (delete-char 2)
+    (goto-char 7)
+    (insert "......")
+    (goto-char 15)
+    (insert "..........")
+    (goto-char 4)
+    (insert "........")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((2 . 13)
+        (13 . 13)
+        (13 . 13)
+        (13 . 13)
+        (13 . 13)
+        (13 . 13)
+        (13 . 13)
+        (13 . 36)
+        (13 . 36)
+        (13 . 36)
+        (13 . 36))))))
+
+(ert-deftest overlay-autogenerated-test-17 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 15 37 nil t nil)
+    (make-overlay 40 3 nil t t)
+    (make-overlay 61 19 nil t t)
+    (make-overlay 46 9 nil nil t)
+    (make-overlay 64 39 nil nil t)
+    (make-overlay 50 58 nil nil t)
+    (make-overlay 21 30 nil t nil)
+    (make-overlay 44 54 nil t nil)
+    (make-overlay 32 2 nil t nil)
+    (make-overlay 14 9 nil t t)
+    (make-overlay 41 40 nil t nil)
+    (make-overlay 17 26 nil t nil)
+    (make-overlay 57 50 nil t t)
+    (make-overlay 16 65 nil nil t)
+    (make-overlay 13 61 nil t t)
+    (make-overlay 39 64 nil nil t)
+    (goto-char 37)
+    (widen)
+    (narrow-to-region 12 1)
+    (goto-char 12)
+    (insert "......")
+    (goto-char 8)
+    (delete-char 4)
+    (goto-char 11)
+    (delete-char 3)
+    (goto-char 6)
+    (insert ".....")
+    (goto-char 6)
+    (widen)
+    (narrow-to-region 53 48)
+    (goto-char 48)
+    (delete-char 5)
+    (goto-char 48)
+    (widen)
+    (narrow-to-region 59 58)
+    (goto-char 59)
+    (delete-char 0)
+    (goto-char 58)
+    (insert "...")
+    (goto-char 60)
+    (insert "...............")
+    (goto-char 58)
+    (insert ".............")
+    (goto-char 67)
+    (insert ".....")
+    (goto-char 73)
+    (insert "")
+    (goto-char 68)
+    (insert ".....")
+    (goto-char 64)
+    (insert "....")
+    (goto-char 62)
+    (insert "..")
+    (goto-char 91)
+    (insert "..........")
+    (goto-char 80)
+    (insert "............")
+    (goto-char 100)
+    (delete-char 21)
+    (goto-char 74)
+    (insert "...")
+    (goto-char 60)
+    (delete-char 30)
+    (goto-char 64)
+    (widen)
+    (narrow-to-region 71 23)
+    (goto-char 53)
+    (delete-char 11)
+    (goto-char 23)
+    (delete-char 21)
+    (goto-char 39)
+    (delete-char 0)
+    (goto-char 35)
+    (insert "")
+    (goto-char 35)
+    (insert ".........")
+    (goto-char 30)
+    (insert "...........")
+    (goto-char 35)
+    (insert "..")
+    (goto-char 37)
+    (delete-char 1)
+    (goto-char 28)
+    (delete-char 3)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((13 . 27)
+        (17 . 67)
+        (20 . 71)
+        (23 . 23)
+        (23 . 24)
+        (23 . 67)
+        (23 . 70)
+        (23 . 70)
+        (27 . 41)
+        (28 . 41)
+        (28 . 41))))))
+
+(ert-deftest overlay-autogenerated-test-18 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 43 52 nil nil t)
+    (make-overlay 27 29 nil nil t)
+    (make-overlay 24 18 nil nil nil)
+    (make-overlay 39 52 nil nil nil)
+    (make-overlay 33 62 nil t t)
+    (make-overlay 16 7 nil t nil)
+    (make-overlay 47 39 nil nil t)
+    (make-overlay 59 41 nil nil nil)
+    (make-overlay 22 55 nil nil t)
+    (make-overlay 60 16 nil t t)
+    (make-overlay 55 20 nil nil t)
+    (make-overlay 25 12 nil nil t)
+    (make-overlay 26 2 nil nil t)
+    (make-overlay 17 35 nil nil t)
+    (make-overlay 46 41 nil t nil)
+    (make-overlay 57 53 nil t t)
+    (goto-char 52)
+    (insert "")
+    (goto-char 4)
+    (delete-char 21)
+    (goto-char 17)
+    (insert "")
+    (goto-char 35)
+    (insert "...............")
+    (goto-char 8)
+    (insert "...............")
+    (goto-char 9)
+    (insert "........")
+    (goto-char 73)
+    (delete-char 9)
+    (goto-char 62)
+    (insert "...............")
+    (goto-char 27)
+    (widen)
+    (narrow-to-region 34 84)
+    (goto-char 81)
+    (insert "...........")
+    (goto-char 48)
+    (insert "...")
+    (goto-char 74)
+    (insert ".......")
+    (goto-char 41)
+    (widen)
+    (narrow-to-region 37 105)
+    (goto-char 75)
+    (insert "...............")
+    (goto-char 47)
+    (insert "..........")
+    (goto-char 99)
+    (delete-char 13)
+    (goto-char 105)
+    (delete-char 4)
+    (goto-char 94)
+    (delete-char 5)
+    (goto-char 96)
+    (insert "..............")
+    (goto-char 74)
+    (insert "")
+    (goto-char 121)
+    (insert "...")
+    (goto-char 102)
+    (insert "...")
+    (goto-char 64)
+    (insert "......")
+    (goto-char 67)
+    (insert "...")
+    (goto-char 95)
+    (delete-char 19)
+    (goto-char 37)
+    (insert "..........")
+    (goto-char 50)
+    (widen)
+    (narrow-to-region 67 96)
+    (goto-char 88)
+    (insert "..........")
+    (goto-char 91)
+    (insert ".............")
+    (goto-char 70)
+    (delete-char 8)
+    (goto-char 111)
+    (widen)
+    (narrow-to-region 72 103)
+    (goto-char 101)
+    (insert "...............")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((4 . 119)
+        (4 . 119)
+        (4 . 162)
+        (35 . 162)
+        (51 . 78)
+        (53 . 162)
+        (55 . 78)
+        (79 . 162))))))
+
+(ert-deftest overlay-autogenerated-test-19 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 19 31 nil t t)
+    (make-overlay 40 5 nil nil nil)
+    (make-overlay 13 41 nil t t)
+    (make-overlay 41 43 nil nil t)
+    (make-overlay 7 60 nil t nil)
+    (make-overlay 40 23 nil t nil)
+    (make-overlay 32 15 nil t t)
+    (make-overlay 12 45 nil nil nil)
+    (make-overlay 18 1 nil nil nil)
+    (make-overlay 58 32 nil t t)
+    (make-overlay 30 3 nil t t)
+    (make-overlay 43 61 nil t nil)
+    (make-overlay 54 57 nil nil t)
+    (make-overlay 34 14 nil t t)
+    (make-overlay 26 49 nil nil t)
+    (make-overlay 54 49 nil nil t)
+    (goto-char 28)
+    (insert "........")
+    (goto-char 32)
+    (insert "...........")
+    (goto-char 78)
+    (delete-char 6)
+    (goto-char 37)
+    (delete-char 0)
+    (goto-char 49)
+    (insert ".........")
+    (goto-char 40)
+    (widen)
+    (narrow-to-region 8 30)
+    (goto-char 20)
+    (delete-char 4)
+    (goto-char 23)
+    (delete-char 1)
+    (goto-char 10)
+    (insert ".")
+    (goto-char 22)
+    (delete-char 2)
+    (goto-char 22)
+    (insert "......")
+    (goto-char 17)
+    (insert "..........")
+    (goto-char 34)
+    (delete-char 0)
+    (goto-char 21)
+    (insert "............")
+    (goto-char 45)
+    (delete-char 7)
+    (goto-char 39)
+    (insert "...............")
+    (goto-char 29)
+    (insert "........")
+    (goto-char 9)
+    (delete-char 3)
+    (goto-char 63)
+    (delete-char 1)
+    (goto-char 33)
+    (insert "........")
+    (goto-char 16)
+    (delete-char 36)
+    (goto-char 20)
+    (delete-char 2)
+    (goto-char 28)
+    (delete-char 0)
+    (goto-char 24)
+    (insert "...........")
+    (goto-char 43)
+    (insert "..........")
+    (goto-char 30)
+    (delete-char 1)
+    (goto-char 40)
+    (delete-char 13)
+    (goto-char 22)
+    (delete-char 19)
+    (goto-char 10)
+    (delete-char 8)
+    (goto-char 14)
+    (delete-char 0)
+    (goto-char 12)
+    (delete-char 2)
+    (goto-char 11)
+    (delete-char 0)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((1 . 12)
+        (3 . 40)
+        (5 . 50)
+        (7 . 69)
+        (10 . 42)
+        (10 . 44)
+        (10 . 51)
+        (10 . 55))))))
+
+(ert-deftest overlay-autogenerated-test-20 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 44 42 nil t t)
+    (make-overlay 47 1 nil nil nil)
+    (make-overlay 24 48 nil nil nil)
+    (make-overlay 62 50 nil nil t)
+    (make-overlay 54 38 nil nil nil)
+    (make-overlay 3 9 nil nil nil)
+    (make-overlay 61 28 nil t nil)
+    (make-overlay 33 33 nil nil t)
+    (make-overlay 37 37 nil t nil)
+    (make-overlay 20 13 nil nil t)
+    (make-overlay 54 36 nil t nil)
+    (make-overlay 18 58 nil nil t)
+    (make-overlay 55 3 nil nil t)
+    (make-overlay 23 21 nil t t)
+    (make-overlay 47 55 nil t t)
+    (make-overlay 50 12 nil nil nil)
+    (goto-char 11)
+    (delete-char 46)
+    (goto-char 7)
+    (delete-char 3)
+    (goto-char 14)
+    (delete-char 1)
+    (goto-char 14)
+    (insert "......")
+    (goto-char 14)
+    (delete-char 4)
+    (goto-char 12)
+    (widen)
+    (narrow-to-region 11 12)
+    (goto-char 11)
+    (insert "...")
+    (goto-char 13)
+    (delete-char 1)
+    (goto-char 14)
+    (insert ".")
+    (goto-char 13)
+    (delete-char 2)
+    (goto-char 11)
+    (delete-char 2)
+    (goto-char 11)
+    (insert "")
+    (goto-char 11)
+    (delete-char 0)
+    (goto-char 11)
+    (delete-char 0)
+    (goto-char 11)
+    (delete-char 0)
+    (goto-char 11)
+    (insert ".")
+    (goto-char 11)
+    (insert ".")
+    (goto-char 12)
+    (insert "......")
+    (goto-char 14)
+    (delete-char 2)
+    (goto-char 11)
+    (delete-char 2)
+    (goto-char 14)
+    (insert "............")
+    (goto-char 19)
+    (insert "..............")
+    (goto-char 29)
+    (insert ".....")
+    (goto-char 42)
+    (delete-char 1)
+    (goto-char 22)
+    (insert ".....")
+    (goto-char 19)
+    (insert "..............")
+    (goto-char 42)
+    (insert ".....")
+    (goto-char 63)
+    (widen)
+    (narrow-to-region 26 42)
+    (goto-char 36)
+    (insert "..........")
+    (goto-char 40)
+    (delete-char 11)
+    (goto-char 26)
+    (delete-char 13)
+    (goto-char 28)
+    (delete-char 0)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((8 . 56))))))
+
+(ert-deftest overlay-autogenerated-test-21 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 65 15 nil nil nil)
+    (make-overlay 52 31 nil nil nil)
+    (make-overlay 12 51 nil t t)
+    (make-overlay 42 20 nil nil t)
+    (make-overlay 51 48 nil nil nil)
+    (make-overlay 59 28 nil t t)
+    (make-overlay 51 53 nil t nil)
+    (make-overlay 50 59 nil nil t)
+    (make-overlay 24 40 nil t nil)
+    (make-overlay 51 61 nil nil nil)
+    (make-overlay 12 58 nil nil t)
+    (make-overlay 64 17 nil t t)
+    (make-overlay 26 38 nil t t)
+    (make-overlay 23 36 nil nil nil)
+    (make-overlay 57 50 nil nil nil)
+    (make-overlay 42 15 nil nil t)
+    (goto-char 14)
+    (insert "............")
+    (goto-char 37)
+    (insert ".")
+    (goto-char 73)
+    (insert "..........")
+    (goto-char 17)
+    (delete-char 31)
+    (goto-char 21)
+    (delete-char 35)
+    (goto-char 9)
+    (delete-char 0)
+    (goto-char 7)
+    (delete-char 2)
+    (goto-char 1)
+    (insert "")
+    (goto-char 5)
+    (insert ".......")
+    (goto-char 8)
+    (insert "....")
+    (goto-char 27)
+    (delete-char 0)
+    (goto-char 10)
+    (insert ".............")
+    (goto-char 24)
+    (delete-char 16)
+    (goto-char 14)
+    (insert ".............")
+    (goto-char 25)
+    (delete-char 11)
+    (goto-char 3)
+    (insert "........")
+    (goto-char 38)
+    (insert "............")
+    (goto-char 41)
+    (insert "..............")
+    (goto-char 56)
+    (delete-char 3)
+    (goto-char 15)
+    (widen)
+    (narrow-to-region 16 53)
+    (goto-char 19)
+    (widen)
+    (narrow-to-region 18 33)
+    (goto-char 32)
+    (insert "......")
+    (goto-char 38)
+    (delete-char 1)
+    (goto-char 19)
+    (widen)
+    (narrow-to-region 11 11)
+    (goto-char 11)
+    (insert ".........")
+    (goto-char 11)
+    (insert ".........")
+    (goto-char 20)
+    (widen)
+    (narrow-to-region 22 69)
+    (goto-char 49)
+    (insert ".........")
+    (goto-char 54)
+    (delete-char 22)
+    (goto-char 44)
+    (insert "........")
+    (goto-char 40)
+    (delete-char 7)
+    (goto-char 29)
+    (delete-char 22)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((33 . 33)
+        (33 . 33)
+        (33 . 33)
+        (33 . 33)
+        (33 . 33)
+        (33 . 33)
+        (33 . 33)
+        (33 . 33)
+        (33 . 33)
+        (33 . 33)
+        (33 . 33)
+        (33 . 33)
+        (33 . 33)
+        (33 . 33)
+        (33 . 33)
+        (33 . 33))))))
+
+(ert-deftest overlay-autogenerated-test-22 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 12 14 nil nil t)
+    (make-overlay 54 7 nil nil t)
+    (make-overlay 8 3 nil nil nil)
+    (make-overlay 42 32 nil nil nil)
+    (make-overlay 10 27 nil t t)
+    (make-overlay 50 28 nil t t)
+    (make-overlay 39 35 nil nil nil)
+    (make-overlay 12 4 nil t t)
+    (make-overlay 29 54 nil nil nil)
+    (make-overlay 14 52 nil t t)
+    (make-overlay 9 15 nil t nil)
+    (make-overlay 44 11 nil nil nil)
+    (make-overlay 46 29 nil t t)
+    (make-overlay 40 58 nil t t)
+    (make-overlay 40 61 nil t nil)
+    (make-overlay 13 59 nil nil t)
+    (goto-char 32)
+    (insert ".............")
+    (goto-char 25)
+    (delete-char 10)
+    (goto-char 3)
+    (insert ".............")
+    (goto-char 33)
+    (delete-char 32)
+    (goto-char 39)
+    (widen)
+    (narrow-to-region 41 46)
+    (goto-char 43)
+    (delete-char 2)
+    (goto-char 42)
+    (delete-char 2)
+    (goto-char 42)
+    (insert "...")
+    (goto-char 43)
+    (delete-char 1)
+    (goto-char 42)
+    (widen)
+    (narrow-to-region 8 46)
+    (goto-char 25)
+    (delete-char 7)
+    (goto-char 12)
+    (delete-char 10)
+    (goto-char 23)
+    (insert "...............")
+    (goto-char 41)
+    (delete-char 3)
+    (goto-char 17)
+    (insert ".........")
+    (goto-char 37)
+    (insert "...............")
+    (goto-char 53)
+    (delete-char 7)
+    (goto-char 53)
+    (delete-char 0)
+    (goto-char 42)
+    (widen)
+    (narrow-to-region 20 54)
+    (goto-char 20)
+    (delete-char 28)
+    (goto-char 23)
+    (insert "..........")
+    (goto-char 30)
+    (insert "......")
+    (goto-char 26)
+    (delete-char 1)
+    (goto-char 27)
+    (widen)
+    (narrow-to-region 40 37)
+    (goto-char 37)
+    (insert ".....")
+    (goto-char 41)
+    (widen)
+    (narrow-to-region 13 37)
+    (goto-char 29)
+    (insert "...........")
+    (goto-char 33)
+    (delete-char 7)
+    (goto-char 33)
+    (delete-char 8)
+    (goto-char 20)
+    (insert "")
+    (goto-char 23)
+    (delete-char 7)
+    (goto-char 14)
+    (widen)
+    (narrow-to-region 33 33)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((15 . 39)
+        (16 . 38)
+        (16 . 39))))))
+
+(ert-deftest overlay-autogenerated-test-23 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 51 32 nil t t)
+    (make-overlay 13 61 nil t nil)
+    (make-overlay 47 19 nil nil t)
+    (make-overlay 11 30 nil nil nil)
+    (make-overlay 50 26 nil t t)
+    (make-overlay 64 13 nil t t)
+    (make-overlay 29 8 nil t t)
+    (make-overlay 25 42 nil t t)
+    (make-overlay 33 28 nil t t)
+    (make-overlay 54 7 nil nil nil)
+    (make-overlay 30 59 nil nil nil)
+    (make-overlay 65 50 nil t t)
+    (make-overlay 64 15 nil t nil)
+    (make-overlay 16 35 nil nil nil)
+    (make-overlay 40 36 nil nil t)
+    (make-overlay 31 35 nil t nil)
+    (goto-char 61)
+    (insert "......")
+    (goto-char 55)
+    (delete-char 2)
+    (goto-char 20)
+    (insert "..............")
+    (goto-char 56)
+    (insert "............")
+    (goto-char 48)
+    (delete-char 6)
+    (goto-char 9)
+    (delete-char 54)
+    (goto-char 20)
+    (delete-char 2)
+    (goto-char 16)
+    (delete-char 12)
+    (goto-char 18)
+    (insert ".............")
+    (goto-char 24)
+    (delete-char 7)
+    (goto-char 5)
+    (delete-char 2)
+    (goto-char 1)
+    (insert ".......")
+    (goto-char 1)
+    (insert ".......")
+    (goto-char 33)
+    (insert "")
+    (goto-char 4)
+    (insert "..")
+    (goto-char 5)
+    (widen)
+    (narrow-to-region 17 4)
+    (goto-char 13)
+    (insert ".")
+    (goto-char 8)
+    (insert "............")
+    (goto-char 9)
+    (delete-char 3)
+    (goto-char 4)
+    (widen)
+    (narrow-to-region 32 32)
+    (goto-char 32)
+    (delete-char 0)
+    (goto-char 32)
+    (delete-char 0)
+    (goto-char 32)
+    (delete-char 0)
+    (goto-char 32)
+    (insert "...............")
+    (goto-char 43)
+    (delete-char 4)
+    (goto-char 32)
+    (delete-char 1)
+    (goto-char 40)
+    (widen)
+    (narrow-to-region 33 19)
+    (goto-char 27)
+    (insert "........")
+    (goto-char 38)
+    (delete-char 2)
+    (goto-char 26)
+    (insert "")
+    (goto-char 33)
+    (delete-char 1)
+    (goto-char 27)
+    (insert ".")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((38 . 56))))))
+
+(ert-deftest overlay-autogenerated-test-24 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 63 8 nil t t)
+    (make-overlay 10 13 nil nil t)
+    (make-overlay 40 38 nil nil nil)
+    (make-overlay 21 34 nil t t)
+    (make-overlay 55 29 nil nil nil)
+    (make-overlay 36 65 nil t t)
+    (make-overlay 29 12 nil t nil)
+    (make-overlay 41 3 nil nil t)
+    (make-overlay 20 9 nil t t)
+    (make-overlay 52 42 nil t t)
+    (make-overlay 21 56 nil nil t)
+    (make-overlay 25 65 nil nil nil)
+    (make-overlay 38 4 nil t t)
+    (make-overlay 48 23 nil t t)
+    (make-overlay 52 9 nil nil t)
+    (make-overlay 48 19 nil nil nil)
+    (goto-char 43)
+    (delete-char 8)
+    (goto-char 30)
+    (delete-char 16)
+    (goto-char 7)
+    (insert "...")
+    (goto-char 14)
+    (delete-char 5)
+    (goto-char 36)
+    (delete-char 0)
+    (goto-char 9)
+    (insert "...............")
+    (goto-char 13)
+    (delete-char 17)
+    (goto-char 16)
+    (delete-char 2)
+    (goto-char 9)
+    (insert "")
+    (goto-char 11)
+    (delete-char 5)
+    (goto-char 18)
+    (insert "........")
+    (goto-char 15)
+    (insert "....")
+    (goto-char 16)
+    (delete-char 14)
+    (goto-char 20)
+    (insert ".")
+    (goto-char 25)
+    (delete-char 1)
+    (goto-char 14)
+    (delete-char 14)
+    (goto-char 3)
+    (delete-char 7)
+    (goto-char 3)
+    (delete-char 4)
+    (goto-char 1)
+    (insert "...........")
+    (goto-char 9)
+    (insert ".......")
+    (goto-char 5)
+    (delete-char 7)
+    (goto-char 12)
+    (insert ".........")
+    (goto-char 2)
+    (delete-char 4)
+    (goto-char 3)
+    (widen)
+    (narrow-to-region 14 6)
+    (goto-char 9)
+    (insert "..........")
+    (goto-char 13)
+    (delete-char 8)
+    (goto-char 7)
+    (delete-char 7)
+    (goto-char 7)
+    (insert "..")
+    (goto-char 9)
+    (insert ".............")
+    (goto-char 9)
+    (insert "..........")
+    (goto-char 21)
+    (insert "...............")
+    (goto-char 42)
+    (insert ".........")
+    (should
+     (equal
+      (test-overlay-regions)
+      'nil))))
+
+(ert-deftest overlay-autogenerated-test-25 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 24 8 nil nil t)
+    (make-overlay 41 16 nil t nil)
+    (make-overlay 3 16 nil nil nil)
+    (make-overlay 26 42 nil nil nil)
+    (make-overlay 32 45 nil nil t)
+    (make-overlay 34 19 nil nil nil)
+    (make-overlay 37 54 nil nil t)
+    (make-overlay 44 34 nil t nil)
+    (make-overlay 49 40 nil t t)
+    (make-overlay 29 34 nil t nil)
+    (make-overlay 54 16 nil t t)
+    (make-overlay 29 4 nil t nil)
+    (make-overlay 44 57 nil nil nil)
+    (make-overlay 5 32 nil nil nil)
+    (make-overlay 12 33 nil nil t)
+    (make-overlay 38 29 nil t nil)
+    (goto-char 12)
+    (delete-char 53)
+    (goto-char 1)
+    (delete-char 6)
+    (goto-char 5)
+    (widen)
+    (narrow-to-region 6 1)
+    (goto-char 6)
+    (insert "......")
+    (goto-char 10)
+    (insert "...............")
+    (goto-char 17)
+    (delete-char 5)
+    (goto-char 7)
+    (insert ".....")
+    (goto-char 8)
+    (insert "...............")
+    (goto-char 4)
+    (insert ".....")
+    (goto-char 44)
+    (widen)
+    (narrow-to-region 18 11)
+    (goto-char 15)
+    (delete-char 1)
+    (goto-char 17)
+    (delete-char 0)
+    (goto-char 13)
+    (delete-char 3)
+    (goto-char 14)
+    (insert "..")
+    (goto-char 16)
+    (insert "..")
+    (goto-char 15)
+    (delete-char 3)
+    (goto-char 13)
+    (delete-char 0)
+    (goto-char 14)
+    (insert "..........")
+    (goto-char 19)
+    (insert ".")
+    (goto-char 23)
+    (delete-char 1)
+    (goto-char 12)
+    (widen)
+    (narrow-to-region 23 40)
+    (goto-char 35)
+    (insert "....")
+    (goto-char 33)
+    (insert "..........")
+    (goto-char 37)
+    (delete-char 16)
+    (goto-char 37)
+    (delete-char 0)
+    (goto-char 23)
+    (widen)
+    (narrow-to-region 30 8)
+    (goto-char 29)
+    (delete-char 0)
+    (goto-char 15)
+    (delete-char 15)
+    (goto-char 9)
+    (insert "...........")
+    (goto-char 9)
+    (delete-char 1)
+    (goto-char 22)
+    (delete-char 3)
+    (goto-char 10)
+    (insert ".........")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((1 . 30)
+        (1 . 30)
+        (1 . 30)
+        (2 . 53)
+        (30 . 30)
+        (30 . 30)
+        (30 . 30)
+        (30 . 30)
+        (30 . 30)
+        (30 . 30)
+        (30 . 30)
+        (30 . 53)
+        (30 . 53)
+        (30 . 53))))))
+
+(ert-deftest overlay-autogenerated-test-26 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 60 59 nil t nil)
+    (make-overlay 18 11 nil nil t)
+    (make-overlay 4 44 nil nil nil)
+    (make-overlay 7 22 nil nil nil)
+    (make-overlay 54 50 nil t nil)
+    (make-overlay 59 28 nil nil nil)
+    (make-overlay 49 23 nil nil t)
+    (make-overlay 21 5 nil t nil)
+    (make-overlay 17 39 nil t nil)
+    (make-overlay 16 14 nil nil nil)
+    (make-overlay 50 26 nil nil nil)
+    (make-overlay 37 14 nil nil nil)
+    (make-overlay 6 59 nil nil t)
+    (make-overlay 30 17 nil nil t)
+    (make-overlay 17 34 nil nil t)
+    (make-overlay 7 22 nil t nil)
+    (goto-char 35)
+    (delete-char 25)
+    (goto-char 30)
+    (delete-char 7)
+    (goto-char 25)
+    (widen)
+    (narrow-to-region 3 19)
+    (goto-char 6)
+    (insert ".........")
+    (goto-char 21)
+    (insert "...............")
+    (goto-char 12)
+    (insert ".............")
+    (goto-char 34)
+    (widen)
+    (narrow-to-region 64 37)
+    (goto-char 62)
+    (insert ".............")
+    (goto-char 50)
+    (widen)
+    (narrow-to-region 72 38)
+    (goto-char 66)
+    (insert "")
+    (goto-char 54)
+    (insert "...")
+    (goto-char 70)
+    (delete-char 4)
+    (goto-char 49)
+    (delete-char 13)
+    (goto-char 38)
+    (insert "....")
+    (goto-char 46)
+    (insert ".")
+    (goto-char 43)
+    (widen)
+    (narrow-to-region 74 53)
+    (goto-char 60)
+    (delete-char 10)
+    (goto-char 53)
+    (insert "..............")
+    (goto-char 72)
+    (insert "............")
+    (goto-char 87)
+    (delete-char 2)
+    (goto-char 73)
+    (insert "............")
+    (goto-char 81)
+    (insert "........")
+    (goto-char 106)
+    (insert "...")
+    (goto-char 95)
+    (widen)
+    (narrow-to-region 77 39)
+    (goto-char 43)
+    (insert "..........")
+    (goto-char 40)
+    (insert "...............")
+    (goto-char 101)
+    (insert "")
+    (goto-char 53)
+    (insert "....")
+    (goto-char 79)
+    (delete-char 21)
+    (goto-char 85)
+    (insert "........")
+    (goto-char 52)
+    (delete-char 41)
+    (goto-char 43)
+    (insert ".....")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((4 . 90)
+        (5 . 57)
+        (6 . 90)
+        (29 . 57)
+        (29 . 57)
+        (33 . 57))))))
+
+(ert-deftest overlay-autogenerated-test-27 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 20 12 nil t nil)
+    (make-overlay 3 10 nil t t)
+    (make-overlay 11 53 nil t nil)
+    (make-overlay 59 3 nil t nil)
+    (make-overlay 28 19 nil t t)
+    (make-overlay 16 30 nil t t)
+    (make-overlay 39 19 nil t t)
+    (make-overlay 33 50 nil t nil)
+    (make-overlay 36 54 nil nil nil)
+    (make-overlay 42 59 nil nil nil)
+    (make-overlay 30 48 nil t nil)
+    (make-overlay 20 13 nil nil t)
+    (make-overlay 63 48 nil t nil)
+    (make-overlay 48 12 nil t t)
+    (make-overlay 64 50 nil nil nil)
+    (make-overlay 7 7 nil nil nil)
+    (goto-char 20)
+    (widen)
+    (narrow-to-region 21 54)
+    (goto-char 40)
+    (insert "..........")
+    (goto-char 21)
+    (delete-char 2)
+    (goto-char 35)
+    (widen)
+    (narrow-to-region 70 11)
+    (goto-char 45)
+    (insert "...............")
+    (goto-char 74)
+    (insert ".")
+    (goto-char 28)
+    (widen)
+    (narrow-to-region 77 67)
+    (goto-char 72)
+    (insert "..........")
+    (goto-char 85)
+    (delete-char 1)
+    (goto-char 82)
+    (widen)
+    (narrow-to-region 83 86)
+    (goto-char 83)
+    (delete-char 0)
+    (goto-char 86)
+    (delete-char 0)
+    (goto-char 86)
+    (insert "...........")
+    (goto-char 97)
+    (insert ".......")
+    (goto-char 103)
+    (widen)
+    (narrow-to-region 44 68)
+    (goto-char 49)
+    (insert "..")
+    (goto-char 65)
+    (insert ".............")
+    (goto-char 59)
+    (delete-char 0)
+    (goto-char 57)
+    (insert "........")
+    (goto-char 55)
+    (delete-char 30)
+    (goto-char 45)
+    (insert "...............")
+    (goto-char 44)
+    (insert "")
+    (goto-char 62)
+    (insert "............")
+    (goto-char 63)
+    (widen)
+    (narrow-to-region 12 5)
+    (goto-char 8)
+    (delete-char 4)
+    (goto-char 6)
+    (delete-char 0)
+    (goto-char 7)
+    (insert "..........")
+    (goto-char 15)
+    (delete-char 0)
+    (goto-char 16)
+    (insert "............")
+    (goto-char 20)
+    (insert ".........")
+    (goto-char 13)
+    (insert "..")
+    (goto-char 32)
+    (insert "..............")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((3 . 55)
+        (3 . 173)
+        (7 . 7))))))
+
+(ert-deftest overlay-autogenerated-test-28 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 59 48 nil t nil)
+    (make-overlay 59 4 nil nil t)
+    (make-overlay 45 35 nil t nil)
+    (make-overlay 13 18 nil t t)
+    (make-overlay 10 7 nil t t)
+    (make-overlay 9 8 nil nil nil)
+    (make-overlay 33 47 nil nil t)
+    (make-overlay 1 57 nil t nil)
+    (make-overlay 16 59 nil nil t)
+    (make-overlay 43 58 nil nil t)
+    (make-overlay 6 11 nil nil nil)
+    (make-overlay 59 7 nil t nil)
+    (make-overlay 3 57 nil t t)
+    (make-overlay 61 35 nil nil nil)
+    (make-overlay 57 8 nil nil nil)
+    (make-overlay 5 32 nil t nil)
+    (goto-char 18)
+    (insert "............")
+    (goto-char 43)
+    (delete-char 2)
+    (goto-char 38)
+    (delete-char 26)
+    (goto-char 42)
+    (insert ".....")
+    (goto-char 52)
+    (insert "..........")
+    (goto-char 45)
+    (delete-char 11)
+    (goto-char 33)
+    (insert "....")
+    (goto-char 23)
+    (delete-char 14)
+    (goto-char 33)
+    (widen)
+    (narrow-to-region 30 33)
+    (goto-char 30)
+    (delete-char 0)
+    (goto-char 30)
+    (insert "...........")
+    (goto-char 30)
+    (delete-char 7)
+    (goto-char 30)
+    (insert ".")
+    (goto-char 32)
+    (delete-char 4)
+    (goto-char 34)
+    (delete-char 0)
+    (goto-char 34)
+    (delete-char 0)
+    (goto-char 32)
+    (insert "...............")
+    (goto-char 46)
+    (insert ".........")
+    (goto-char 45)
+    (delete-char 3)
+    (goto-char 49)
+    (delete-char 2)
+    (goto-char 42)
+    (delete-char 2)
+    (goto-char 32)
+    (insert "..........")
+    (goto-char 47)
+    (insert "....")
+    (goto-char 59)
+    (insert ".......")
+    (goto-char 35)
+    (insert ".")
+    (goto-char 45)
+    (insert "..............")
+    (goto-char 37)
+    (insert "..")
+    (goto-char 80)
+    (insert ".....")
+    (goto-char 30)
+    (insert ".............")
+    (goto-char 102)
+    (insert "............")
+    (goto-char 113)
+    (insert "")
+    (goto-char 66)
+    (widen)
+    (narrow-to-region 47 38)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((1 . 45)
+        (3 . 117)
+        (4 . 121)
+        (7 . 121)
+        (8 . 45)
+        (16 . 121)
+        (28 . 121)
+        (28 . 121)
+        (28 . 121))))))
+
+(ert-deftest overlay-autogenerated-test-29 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 5 63 nil nil t)
+    (make-overlay 20 28 nil t t)
+    (make-overlay 58 53 nil t nil)
+    (make-overlay 4 57 nil t t)
+    (make-overlay 4 16 nil nil nil)
+    (make-overlay 33 26 nil t nil)
+    (make-overlay 9 32 nil t t)
+    (make-overlay 11 8 nil nil nil)
+    (make-overlay 59 35 nil nil t)
+    (make-overlay 15 25 nil t t)
+    (make-overlay 36 16 nil nil nil)
+    (make-overlay 8 37 nil nil nil)
+    (make-overlay 65 63 nil nil t)
+    (make-overlay 3 20 nil nil t)
+    (make-overlay 44 55 nil t t)
+    (make-overlay 45 25 nil t nil)
+    (goto-char 39)
+    (insert "...")
+    (goto-char 22)
+    (insert "........")
+    (goto-char 60)
+    (insert ".........")
+    (goto-char 17)
+    (insert "............")
+    (goto-char 13)
+    (widen)
+    (narrow-to-region 79 16)
+    (goto-char 19)
+    (delete-char 11)
+    (goto-char 25)
+    (insert "........")
+    (goto-char 61)
+    (insert "....")
+    (goto-char 45)
+    (widen)
+    (narrow-to-region 73 66)
+    (goto-char 71)
+    (insert "............")
+    (goto-char 81)
+    (delete-char 2)
+    (goto-char 73)
+    (insert "..........")
+    (goto-char 74)
+    (insert "............")
+    (goto-char 82)
+    (delete-char 7)
+    (goto-char 78)
+    (delete-char 18)
+    (goto-char 75)
+    (insert ".........")
+    (goto-char 66)
+    (insert ".........")
+    (goto-char 86)
+    (delete-char 12)
+    (goto-char 77)
+    (widen)
+    (narrow-to-region 23 55)
+    (goto-char 43)
+    (insert ".")
+    (goto-char 50)
+    (insert "..")
+    (goto-char 25)
+    (delete-char 18)
+    (goto-char 33)
+    (delete-char 7)
+    (goto-char 26)
+    (insert "........")
+    (goto-char 29)
+    (insert "...........")
+    (goto-char 33)
+    (insert "...")
+    (goto-char 40)
+    (insert "..........")
+    (goto-char 26)
+    (insert "")
+    (goto-char 35)
+    (insert ".")
+    (goto-char 59)
+    (insert ".")
+    (goto-char 51)
+    (insert "..")
+    (goto-char 59)
+    (insert ".............")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((4 . 130)
+        (5 . 136)
+        (8 . 82)
+        (9 . 82)
+        (15 . 25)
+        (16 . 82)
+        (21 . 77)
+        (25 . 105)
+        (75 . 82))))))
+
+(ert-deftest overlay-autogenerated-test-30 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 27 65 nil t t)
+    (make-overlay 39 51 nil t t)
+    (make-overlay 53 2 nil nil nil)
+    (make-overlay 3 17 nil nil t)
+    (make-overlay 35 4 nil nil t)
+    (make-overlay 65 53 nil t nil)
+    (make-overlay 8 21 nil t t)
+    (make-overlay 18 62 nil t t)
+    (make-overlay 42 59 nil nil t)
+    (make-overlay 12 37 nil t t)
+    (make-overlay 64 31 nil t nil)
+    (make-overlay 39 54 nil nil t)
+    (make-overlay 41 24 nil t nil)
+    (make-overlay 10 21 nil nil t)
+    (make-overlay 49 15 nil t nil)
+    (make-overlay 49 63 nil nil t)
+    (goto-char 43)
+    (insert "..........")
+    (goto-char 44)
+    (delete-char 29)
+    (goto-char 32)
+    (insert "..")
+    (goto-char 13)
+    (insert ".")
+    (goto-char 42)
+    (insert ".........")
+    (goto-char 39)
+    (insert "..........")
+    (goto-char 15)
+    (insert "............")
+    (goto-char 58)
+    (delete-char 9)
+    (goto-char 63)
+    (insert ".........")
+    (goto-char 49)
+    (insert ".")
+    (goto-char 28)
+    (delete-char 51)
+    (goto-char 12)
+    (delete-char 6)
+    (goto-char 20)
+    (delete-char 2)
+    (goto-char 7)
+    (widen)
+    (narrow-to-region 2 9)
+    (goto-char 5)
+    (insert "...............")
+    (goto-char 18)
+    (delete-char 1)
+    (goto-char 4)
+    (insert ".............")
+    (goto-char 13)
+    (delete-char 22)
+    (goto-char 12)
+    (insert "")
+    (goto-char 3)
+    (insert ".............")
+    (goto-char 22)
+    (insert "...............")
+    (goto-char 9)
+    (insert "....")
+    (goto-char 8)
+    (insert "...........")
+    (goto-char 6)
+    (delete-char 34)
+    (goto-char 21)
+    (insert "....")
+    (goto-char 14)
+    (insert ".....")
+    (goto-char 20)
+    (insert ".......")
+    (goto-char 34)
+    (widen)
+    (narrow-to-region 3 2)
+    (goto-char 3)
+    (delete-char 0)
+    (goto-char 2)
+    (insert "..............")
+    (goto-char 15)
+    (delete-char 2)
+    (goto-char 11)
+    (insert "......")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((2 . 68))))))
+
+(ert-deftest overlay-autogenerated-test-31 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 54 64 nil nil nil)
+    (make-overlay 49 12 nil nil t)
+    (make-overlay 40 12 nil t nil)
+    (make-overlay 17 38 nil nil nil)
+    (make-overlay 21 36 nil t t)
+    (make-overlay 8 38 nil t nil)
+    (make-overlay 50 22 nil t nil)
+    (make-overlay 65 15 nil nil t)
+    (make-overlay 57 60 nil t t)
+    (make-overlay 35 11 nil nil t)
+    (make-overlay 49 44 nil nil t)
+    (make-overlay 45 31 nil nil t)
+    (make-overlay 51 24 nil t t)
+    (make-overlay 20 14 nil nil nil)
+    (make-overlay 6 18 nil t t)
+    (make-overlay 25 3 nil nil nil)
+    (goto-char 18)
+    (delete-char 10)
+    (goto-char 36)
+    (delete-char 13)
+    (goto-char 8)
+    (delete-char 4)
+    (goto-char 2)
+    (delete-char 8)
+    (goto-char 12)
+    (delete-char 10)
+    (goto-char 15)
+    (delete-char 4)
+    (goto-char 16)
+    (insert ".........")
+    (goto-char 17)
+    (insert "...............")
+    (goto-char 33)
+    (delete-char 0)
+    (goto-char 38)
+    (delete-char 0)
+    (goto-char 11)
+    (insert "...........")
+    (goto-char 8)
+    (delete-char 14)
+    (goto-char 32)
+    (insert "........")
+    (goto-char 40)
+    (widen)
+    (narrow-to-region 14 6)
+    (goto-char 10)
+    (delete-char 1)
+    (goto-char 7)
+    (widen)
+    (narrow-to-region 18 39)
+    (goto-char 36)
+    (delete-char 1)
+    (goto-char 34)
+    (widen)
+    (narrow-to-region 39 14)
+    (goto-char 22)
+    (widen)
+    (narrow-to-region 25 21)
+    (goto-char 23)
+    (delete-char 2)
+    (goto-char 23)
+    (delete-char 0)
+    (goto-char 23)
+    (insert ".........")
+    (goto-char 32)
+    (delete-char 0)
+    (goto-char 31)
+    (insert ".........")
+    (goto-char 32)
+    (insert "...")
+    (goto-char 30)
+    (widen)
+    (narrow-to-region 10 56)
+    (goto-char 10)
+    (insert ".........")
+    (goto-char 38)
+    (insert ".........")
+    (goto-char 19)
+    (insert "..")
+    (goto-char 11)
+    (insert "..............")
+    (goto-char 66)
+    (insert "...............")
+    (goto-char 13)
+    (insert "......")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((2 . 41)
+        (3 . 117)
+        (6 . 41)
+        (8 . 41)
+        (9 . 41)
+        (10 . 42)
+        (41 . 42))))))
+
+(ert-deftest overlay-autogenerated-test-32 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 35 60 nil nil t)
+    (make-overlay 45 46 nil nil nil)
+    (make-overlay 47 11 nil nil t)
+    (make-overlay 12 51 nil t nil)
+    (make-overlay 61 17 nil t nil)
+    (make-overlay 7 24 nil t nil)
+    (make-overlay 36 37 nil nil t)
+    (make-overlay 5 39 nil t t)
+    (make-overlay 5 40 nil nil t)
+    (make-overlay 38 40 nil t t)
+    (make-overlay 47 45 nil t nil)
+    (make-overlay 61 48 nil nil nil)
+    (make-overlay 23 39 nil t t)
+    (make-overlay 11 52 nil nil nil)
+    (make-overlay 37 35 nil nil nil)
+    (make-overlay 19 20 nil t nil)
+    (goto-char 43)
+    (insert "........")
+    (goto-char 7)
+    (insert "")
+    (goto-char 28)
+    (delete-char 41)
+    (goto-char 3)
+    (delete-char 17)
+    (goto-char 2)
+    (insert ".")
+    (goto-char 7)
+    (insert ".........")
+    (goto-char 21)
+    (delete-char 4)
+    (goto-char 13)
+    (delete-char 1)
+    (goto-char 2)
+    (insert "...............")
+    (goto-char 7)
+    (insert "")
+    (goto-char 14)
+    (insert ".....")
+    (goto-char 16)
+    (insert ".")
+    (goto-char 10)
+    (insert "..............")
+    (goto-char 16)
+    (delete-char 18)
+    (goto-char 1)
+    (delete-char 36)
+    (goto-char 1)
+    (delete-char 0)
+    (goto-char 1)
+    (delete-char 0)
+    (goto-char 1)
+    (insert ".............")
+    (goto-char 9)
+    (insert ".")
+    (goto-char 14)
+    (insert ".....")
+    (goto-char 9)
+    (delete-char 0)
+    (goto-char 15)
+    (delete-char 0)
+    (goto-char 6)
+    (delete-char 4)
+    (goto-char 11)
+    (delete-char 5)
+    (goto-char 5)
+    (insert "....")
+    (goto-char 5)
+    (insert ".....")
+    (goto-char 12)
+    (insert "")
+    (goto-char 13)
+    (insert ".......")
+    (goto-char 14)
+    (insert "......")
+    (goto-char 9)
+    (delete-char 3)
+    (goto-char 17)
+    (delete-char 0)
+    (goto-char 7)
+    (delete-char 12)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((1 . 1)
+        (1 . 1)
+        (1 . 1)
+        (1 . 1)
+        (1 . 1)
+        (1 . 1)
+        (1 . 1)
+        (1 . 1)
+        (1 . 1)
+        (1 . 18)
+        (1 . 18)
+        (1 . 18)
+        (1 . 18)
+        (18 . 18)
+        (18 . 18)
+        (18 . 18))))))
+
+(ert-deftest overlay-autogenerated-test-33 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 65 33 nil t nil)
+    (make-overlay 45 54 nil t t)
+    (make-overlay 17 38 nil t nil)
+    (make-overlay 58 46 nil nil t)
+    (make-overlay 21 36 nil t t)
+    (make-overlay 31 63 nil nil t)
+    (make-overlay 37 64 nil t t)
+    (make-overlay 42 19 nil nil nil)
+    (make-overlay 51 60 nil t nil)
+    (make-overlay 47 15 nil t t)
+    (make-overlay 57 47 nil nil nil)
+    (make-overlay 40 45 nil nil nil)
+    (make-overlay 44 47 nil t nil)
+    (make-overlay 42 35 nil t nil)
+    (make-overlay 1 65 nil nil t)
+    (make-overlay 29 63 nil t nil)
+    (goto-char 33)
+    (insert "...........")
+    (goto-char 56)
+    (insert ".........")
+    (goto-char 67)
+    (insert "....")
+    (goto-char 28)
+    (delete-char 35)
+    (goto-char 9)
+    (insert "......")
+    (goto-char 43)
+    (delete-char 17)
+    (goto-char 29)
+    (insert ".......")
+    (goto-char 20)
+    (insert "....")
+    (goto-char 53)
+    (insert ".......")
+    (goto-char 14)
+    (widen)
+    (narrow-to-region 38 57)
+    (goto-char 51)
+    (insert "")
+    (goto-char 57)
+    (insert ".......")
+    (goto-char 64)
+    (insert ".....")
+    (goto-char 59)
+    (delete-char 3)
+    (goto-char 45)
+    (delete-char 12)
+    (goto-char 43)
+    (insert "......")
+    (goto-char 48)
+    (insert "......")
+    (goto-char 52)
+    (insert "........")
+    (goto-char 57)
+    (delete-char 16)
+    (goto-char 43)
+    (delete-char 9)
+    (goto-char 40)
+    (insert "")
+    (goto-char 39)
+    (insert "..........")
+    (goto-char 50)
+    (widen)
+    (narrow-to-region 31 27)
+    (goto-char 27)
+    (insert "..........")
+    (goto-char 33)
+    (delete-char 0)
+    (goto-char 37)
+    (insert "..")
+    (goto-char 38)
+    (delete-char 4)
+    (goto-char 38)
+    (insert "..........")
+    (goto-char 45)
+    (insert ".....")
+    (goto-char 53)
+    (insert "...")
+    (goto-char 51)
+    (insert ".")
+    (goto-char 28)
+    (insert "...")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((1 . 93)
+        (25 . 92)
+        (41 . 88)
+        (60 . 88))))))
+
+(ert-deftest overlay-autogenerated-test-34 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 2 63 nil nil t)
+    (make-overlay 54 30 nil t nil)
+    (make-overlay 21 57 nil t nil)
+    (make-overlay 61 19 nil nil nil)
+    (make-overlay 55 8 nil nil t)
+    (make-overlay 14 51 nil nil nil)
+    (make-overlay 33 13 nil t t)
+    (make-overlay 36 25 nil t t)
+    (make-overlay 22 21 nil nil t)
+    (make-overlay 21 48 nil nil t)
+    (make-overlay 36 7 nil nil t)
+    (make-overlay 2 40 nil nil nil)
+    (make-overlay 21 27 nil nil t)
+    (make-overlay 26 2 nil nil nil)
+    (make-overlay 60 43 nil nil nil)
+    (make-overlay 12 50 nil t t)
+    (goto-char 44)
+    (delete-char 6)
+    (goto-char 5)
+    (insert "..")
+    (goto-char 17)
+    (insert "........")
+    (goto-char 48)
+    (insert "..")
+    (goto-char 27)
+    (delete-char 29)
+    (goto-char 10)
+    (delete-char 2)
+    (goto-char 35)
+    (insert ".............")
+    (goto-char 20)
+    (delete-char 0)
+    (goto-char 6)
+    (insert ".")
+    (goto-char 9)
+    (delete-char 6)
+    (goto-char 38)
+    (insert ".........")
+    (goto-char 5)
+    (insert ".........")
+    (goto-char 10)
+    (delete-char 20)
+    (goto-char 6)
+    (delete-char 6)
+    (goto-char 14)
+    (insert ".............")
+    (goto-char 31)
+    (delete-char 10)
+    (goto-char 20)
+    (widen)
+    (narrow-to-region 27 39)
+    (goto-char 34)
+    (delete-char 5)
+    (goto-char 32)
+    (delete-char 1)
+    (goto-char 27)
+    (insert "..")
+    (goto-char 28)
+    (insert "........")
+    (goto-char 39)
+    (insert "........")
+    (goto-char 38)
+    (delete-char 7)
+    (goto-char 44)
+    (delete-char 0)
+    (goto-char 30)
+    (insert "...............")
+    (goto-char 43)
+    (insert "............")
+    (goto-char 56)
+    (delete-char 1)
+    (goto-char 65)
+    (delete-char 3)
+    (goto-char 36)
+    (insert ".........")
+    (goto-char 74)
+    (insert ".....")
+    (goto-char 67)
+    (delete-char 5)
+    (goto-char 38)
+    (insert "..")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((2 . 80)
+        (6 . 78))))))
+
+(ert-deftest overlay-autogenerated-test-35 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 38 16 nil nil nil)
+    (make-overlay 19 22 nil t nil)
+    (make-overlay 16 43 nil nil t)
+    (make-overlay 27 5 nil nil nil)
+    (make-overlay 43 34 nil t nil)
+    (make-overlay 47 4 nil nil t)
+    (make-overlay 1 47 nil nil t)
+    (make-overlay 27 35 nil t nil)
+    (make-overlay 41 41 nil nil t)
+    (make-overlay 21 19 nil nil nil)
+    (make-overlay 16 38 nil nil t)
+    (make-overlay 33 39 nil t nil)
+    (make-overlay 34 51 nil nil t)
+    (make-overlay 45 36 nil t nil)
+    (make-overlay 42 18 nil t t)
+    (make-overlay 12 30 nil nil nil)
+    (goto-char 18)
+    (insert "")
+    (goto-char 58)
+    (delete-char 3)
+    (goto-char 58)
+    (delete-char 0)
+    (goto-char 1)
+    (insert ".......")
+    (goto-char 48)
+    (delete-char 17)
+    (goto-char 39)
+    (delete-char 6)
+    (goto-char 33)
+    (widen)
+    (narrow-to-region 45 46)
+    (goto-char 46)
+    (insert "")
+    (goto-char 46)
+    (delete-char 0)
+    (goto-char 46)
+    (insert ".....")
+    (goto-char 51)
+    (widen)
+    (narrow-to-region 17 26)
+    (goto-char 25)
+    (widen)
+    (narrow-to-region 50 41)
+    (goto-char 45)
+    (insert "..............")
+    (goto-char 59)
+    (insert "...........")
+    (goto-char 47)
+    (delete-char 9)
+    (goto-char 59)
+    (insert "")
+    (goto-char 46)
+    (insert "")
+    (goto-char 54)
+    (delete-char 5)
+    (goto-char 57)
+    (widen)
+    (narrow-to-region 57 31)
+    (goto-char 42)
+    (delete-char 2)
+    (goto-char 52)
+    (insert "....")
+    (goto-char 44)
+    (insert "..")
+    (goto-char 44)
+    (insert "...............")
+    (goto-char 72)
+    (delete-char 1)
+    (goto-char 66)
+    (delete-char 6)
+    (goto-char 64)
+    (delete-char 5)
+    (goto-char 49)
+    (delete-char 12)
+    (goto-char 32)
+    (insert "......")
+    (goto-char 44)
+    (delete-char 2)
+    (goto-char 39)
+    (delete-char 12)
+    (goto-char 42)
+    (insert "......")
+    (goto-char 36)
+    (widen)
+    (narrow-to-region 14 47)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((1 . 39)
+        (11 . 39)
+        (12 . 39)
+        (19 . 39)
+        (23 . 39)
+        (23 . 39)
+        (23 . 39)
+        (25 . 39)
+        (26 . 28)
+        (26 . 29)
+        (39 . 39)
+        (39 . 39)
+        (39 . 39)
+        (39 . 39)
+        (39 . 39)
+        (39 . 39))))))
+
+(ert-deftest overlay-autogenerated-test-36 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 1 38 nil t t)
+    (make-overlay 58 34 nil t nil)
+    (make-overlay 6 33 nil nil t)
+    (make-overlay 63 54 nil nil t)
+    (make-overlay 54 54 nil t t)
+    (make-overlay 21 61 nil nil nil)
+    (make-overlay 64 55 nil nil t)
+    (make-overlay 28 65 nil nil t)
+    (make-overlay 32 51 nil t nil)
+    (make-overlay 36 38 nil nil nil)
+    (make-overlay 35 21 nil nil nil)
+    (make-overlay 65 48 nil nil nil)
+    (make-overlay 32 27 nil nil t)
+    (make-overlay 27 55 nil t t)
+    (make-overlay 30 22 nil t nil)
+    (make-overlay 14 58 nil t nil)
+    (goto-char 40)
+    (delete-char 7)
+    (goto-char 42)
+    (insert "......")
+    (goto-char 11)
+    (widen)
+    (narrow-to-region 64 9)
+    (goto-char 21)
+    (delete-char 23)
+    (goto-char 24)
+    (insert "...")
+    (goto-char 13)
+    (insert "..........")
+    (goto-char 12)
+    (delete-char 5)
+    (goto-char 10)
+    (delete-char 0)
+    (goto-char 21)
+    (widen)
+    (narrow-to-region 9 5)
+    (goto-char 6)
+    (delete-char 0)
+    (goto-char 9)
+    (delete-char 0)
+    (goto-char 9)
+    (delete-char 0)
+    (goto-char 7)
+    (insert "............")
+    (goto-char 9)
+    (insert "...")
+    (goto-char 18)
+    (insert ".")
+    (goto-char 23)
+    (delete-char 1)
+    (goto-char 9)
+    (insert "....")
+    (goto-char 6)
+    (insert ".....")
+    (goto-char 23)
+    (widen)
+    (narrow-to-region 28 1)
+    (goto-char 6)
+    (insert "...........")
+    (goto-char 30)
+    (delete-char 8)
+    (goto-char 2)
+    (insert ".")
+    (goto-char 18)
+    (insert "......")
+    (goto-char 5)
+    (delete-char 9)
+    (goto-char 5)
+    (delete-char 20)
+    (goto-char 4)
+    (delete-char 3)
+    (goto-char 3)
+    (delete-char 2)
+    (goto-char 3)
+    (delete-char 0)
+    (goto-char 1)
+    (insert "......")
+    (goto-char 8)
+    (widen)
+    (narrow-to-region 39 2)
+    (goto-char 13)
+    (delete-char 12)
+    (goto-char 24)
+    (delete-char 0)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((7 . 20)
+        (9 . 20)
+        (13 . 36)
+        (20 . 20)
+        (20 . 20)
+        (20 . 20)
+        (20 . 20)
+        (20 . 29)
+        (20 . 33)
+        (20 . 36)
+        (20 . 39)
+        (20 . 43)
+        (20 . 43))))))
+
+(ert-deftest overlay-autogenerated-test-37 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 26 30 nil nil nil)
+    (make-overlay 55 50 nil nil t)
+    (make-overlay 43 54 nil nil t)
+    (make-overlay 53 48 nil nil nil)
+    (make-overlay 37 51 nil nil t)
+    (make-overlay 15 30 nil nil nil)
+    (make-overlay 2 24 nil t t)
+    (make-overlay 56 61 nil t nil)
+    (make-overlay 65 46 nil t nil)
+    (make-overlay 28 47 nil t nil)
+    (make-overlay 21 24 nil t t)
+    (make-overlay 17 13 nil t t)
+    (make-overlay 7 44 nil t nil)
+    (make-overlay 28 63 nil nil nil)
+    (make-overlay 22 16 nil t t)
+    (make-overlay 26 44 nil t t)
+    (goto-char 57)
+    (delete-char 6)
+    (goto-char 42)
+    (insert ".....")
+    (goto-char 63)
+    (insert ".............")
+    (goto-char 17)
+    (insert "")
+    (goto-char 57)
+    (insert "...........")
+    (goto-char 3)
+    (delete-char 47)
+    (goto-char 15)
+    (insert ".............")
+    (goto-char 28)
+    (insert "")
+    (goto-char 17)
+    (delete-char 31)
+    (goto-char 7)
+    (delete-char 16)
+    (goto-char 2)
+    (insert "...........")
+    (goto-char 2)
+    (insert "..")
+    (goto-char 18)
+    (widen)
+    (narrow-to-region 20 8)
+    (goto-char 13)
+    (widen)
+    (narrow-to-region 12 10)
+    (goto-char 10)
+    (delete-char 1)
+    (goto-char 11)
+    (delete-char 0)
+    (goto-char 10)
+    (insert "...")
+    (goto-char 11)
+    (delete-char 0)
+    (goto-char 13)
+    (insert "..")
+    (goto-char 16)
+    (delete-char 0)
+    (goto-char 10)
+    (delete-char 2)
+    (goto-char 11)
+    (insert ".....")
+    (goto-char 16)
+    (widen)
+    (narrow-to-region 6 13)
+    (goto-char 10)
+    (insert "..")
+    (goto-char 6)
+    (delete-char 6)
+    (goto-char 8)
+    (insert "...............")
+    (goto-char 21)
+    (delete-char 0)
+    (goto-char 21)
+    (widen)
+    (narrow-to-region 36 11)
+    (goto-char 12)
+    (insert "...............")
+    (goto-char 19)
+    (insert ".......")
+    (goto-char 56)
+    (delete-char 2)
+    (goto-char 42)
+    (delete-char 11)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((44 . 45))))))
+
+(ert-deftest overlay-autogenerated-test-38 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 29 13 nil t t)
+    (make-overlay 19 28 nil nil t)
+    (make-overlay 47 33 nil nil nil)
+    (make-overlay 8 44 nil t nil)
+    (make-overlay 48 4 nil t nil)
+    (make-overlay 8 20 nil t t)
+    (make-overlay 38 31 nil nil t)
+    (make-overlay 17 65 nil nil t)
+    (make-overlay 49 31 nil nil nil)
+    (make-overlay 39 19 nil nil t)
+    (make-overlay 40 49 nil t t)
+    (make-overlay 24 16 nil t t)
+    (make-overlay 4 41 nil t nil)
+    (make-overlay 61 42 nil t nil)
+    (make-overlay 46 11 nil nil nil)
+    (make-overlay 1 43 nil nil t)
+    (goto-char 62)
+    (delete-char 2)
+    (goto-char 25)
+    (widen)
+    (narrow-to-region 30 38)
+    (goto-char 37)
+    (delete-char 1)
+    (goto-char 37)
+    (insert "...........")
+    (goto-char 41)
+    (delete-char 3)
+    (goto-char 39)
+    (delete-char 5)
+    (goto-char 39)
+    (widen)
+    (narrow-to-region 31 9)
+    (goto-char 11)
+    (insert "..............")
+    (goto-char 9)
+    (widen)
+    (narrow-to-region 62 30)
+    (goto-char 32)
+    (widen)
+    (narrow-to-region 17 48)
+    (goto-char 39)
+    (delete-char 7)
+    (goto-char 24)
+    (delete-char 8)
+    (goto-char 19)
+    (insert "")
+    (goto-char 25)
+    (delete-char 5)
+    (goto-char 28)
+    (delete-char 0)
+    (goto-char 22)
+    (widen)
+    (narrow-to-region 52 35)
+    (goto-char 49)
+    (delete-char 0)
+    (goto-char 49)
+    (delete-char 3)
+    (goto-char 48)
+    (insert "...........")
+    (goto-char 37)
+    (delete-char 23)
+    (goto-char 36)
+    (delete-char 0)
+    (goto-char 35)
+    (insert "....")
+    (goto-char 35)
+    (insert "..")
+    (goto-char 39)
+    (delete-char 4)
+    (goto-char 39)
+    (delete-char 0)
+    (goto-char 36)
+    (delete-char 3)
+    (goto-char 36)
+    (delete-char 0)
+    (goto-char 36)
+    (delete-char 0)
+    (goto-char 36)
+    (delete-char 0)
+    (goto-char 36)
+    (insert ".....")
+    (goto-char 38)
+    (delete-char 1)
+    (goto-char 35)
+    (delete-char 3)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((1 . 37)
+        (24 . 44)
+        (25 . 37))))))
+
+(ert-deftest overlay-autogenerated-test-39 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 15 49 nil t t)
+    (make-overlay 27 20 nil t nil)
+    (make-overlay 55 50 nil t nil)
+    (make-overlay 17 5 nil t t)
+    (make-overlay 26 56 nil nil t)
+    (make-overlay 42 11 nil t t)
+    (make-overlay 24 35 nil nil t)
+    (make-overlay 47 45 nil t t)
+    (make-overlay 37 12 nil nil t)
+    (make-overlay 17 25 nil t nil)
+    (make-overlay 32 53 nil nil nil)
+    (make-overlay 20 34 nil nil t)
+    (make-overlay 56 58 nil nil t)
+    (make-overlay 42 31 nil nil t)
+    (make-overlay 22 55 nil t t)
+    (make-overlay 55 11 nil t nil)
+    (goto-char 16)
+    (insert ".............")
+    (goto-char 30)
+    (insert ".")
+    (goto-char 12)
+    (delete-char 56)
+    (goto-char 9)
+    (insert ".............")
+    (goto-char 6)
+    (insert "....")
+    (goto-char 19)
+    (delete-char 19)
+    (goto-char 19)
+    (insert "...............")
+    (goto-char 13)
+    (delete-char 21)
+    (goto-char 7)
+    (delete-char 0)
+    (goto-char 14)
+    (widen)
+    (narrow-to-region 5 6)
+    (goto-char 5)
+    (delete-char 0)
+    (goto-char 6)
+    (insert "......")
+    (goto-char 10)
+    (delete-char 0)
+    (goto-char 7)
+    (widen)
+    (narrow-to-region 2 6)
+    (goto-char 2)
+    (insert "..........")
+    (goto-char 2)
+    (delete-char 9)
+    (goto-char 7)
+    (insert "...")
+    (goto-char 9)
+    (insert "...")
+    (goto-char 10)
+    (insert "......")
+    (goto-char 4)
+    (delete-char 14)
+    (goto-char 4)
+    (insert ".")
+    (goto-char 5)
+    (insert "..............")
+    (goto-char 13)
+    (insert "......")
+    (goto-char 10)
+    (insert "......")
+    (goto-char 20)
+    (insert "............")
+    (goto-char 16)
+    (widen)
+    (narrow-to-region 3 32)
+    (goto-char 18)
+    (insert "..")
+    (goto-char 6)
+    (insert "......")
+    (goto-char 38)
+    (delete-char 0)
+    (goto-char 31)
+    (insert "............")
+    (goto-char 28)
+    (insert "")
+    (goto-char 9)
+    (delete-char 23)
+    (should
+     (equal
+      (test-overlay-regions)
+      'nil))))
+
+(ert-deftest overlay-autogenerated-test-40 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 52 3 nil t nil)
+    (make-overlay 35 41 nil t t)
+    (make-overlay 4 2 nil t nil)
+    (make-overlay 51 48 nil nil t)
+    (make-overlay 44 57 nil t t)
+    (make-overlay 13 32 nil nil nil)
+    (make-overlay 46 29 nil t nil)
+    (make-overlay 28 13 nil t nil)
+    (make-overlay 10 65 nil t t)
+    (make-overlay 41 48 nil nil t)
+    (make-overlay 36 44 nil nil t)
+    (make-overlay 29 61 nil t nil)
+    (make-overlay 25 24 nil nil t)
+    (make-overlay 22 45 nil nil t)
+    (make-overlay 37 55 nil nil t)
+    (make-overlay 36 39 nil nil nil)
+    (goto-char 16)
+    (delete-char 48)
+    (goto-char 17)
+    (delete-char 0)
+    (goto-char 7)
+    (insert "..............")
+    (goto-char 30)
+    (insert "........")
+    (goto-char 11)
+    (insert "..........")
+    (goto-char 5)
+    (delete-char 14)
+    (goto-char 19)
+    (insert ".")
+    (goto-char 27)
+    (insert "..")
+    (goto-char 35)
+    (delete-char 1)
+    (goto-char 29)
+    (delete-char 0)
+    (goto-char 33)
+    (delete-char 2)
+    (goto-char 33)
+    (insert "..")
+    (goto-char 28)
+    (insert ".........")
+    (goto-char 30)
+    (delete-char 4)
+    (goto-char 40)
+    (delete-char 1)
+    (goto-char 15)
+    (widen)
+    (narrow-to-region 40 8)
+    (goto-char 10)
+    (delete-char 13)
+    (goto-char 11)
+    (delete-char 5)
+    (goto-char 15)
+    (insert "........")
+    (goto-char 26)
+    (delete-char 4)
+    (goto-char 11)
+    (delete-char 1)
+    (goto-char 14)
+    (insert "............")
+    (goto-char 33)
+    (insert ".")
+    (goto-char 10)
+    (insert "...")
+    (goto-char 30)
+    (widen)
+    (narrow-to-region 28 9)
+    (goto-char 27)
+    (delete-char 0)
+    (goto-char 27)
+    (delete-char 1)
+    (goto-char 26)
+    (insert "..")
+    (goto-char 27)
+    (insert "..")
+    (goto-char 20)
+    (delete-char 5)
+    (goto-char 12)
+    (widen)
+    (narrow-to-region 40 30)
+    (goto-char 37)
+    (delete-char 3)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((13 . 37)
+        (14 . 37)
+        (14 . 37)
+        (14 . 37)
+        (14 . 37)
+        (14 . 37)
+        (14 . 37)
+        (37 . 37)
+        (37 . 37))))))
+
+(ert-deftest overlay-autogenerated-test-41 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 28 48 nil nil t)
+    (make-overlay 30 11 nil nil t)
+    (make-overlay 7 12 nil t nil)
+    (make-overlay 65 35 nil t nil)
+    (make-overlay 22 61 nil t nil)
+    (make-overlay 37 42 nil nil nil)
+    (make-overlay 33 38 nil nil t)
+    (make-overlay 48 45 nil t t)
+    (make-overlay 45 62 nil t nil)
+    (make-overlay 63 7 nil nil t)
+    (make-overlay 23 42 nil t nil)
+    (make-overlay 21 4 nil t nil)
+    (make-overlay 64 41 nil t nil)
+    (make-overlay 20 33 nil t t)
+    (make-overlay 41 26 nil t nil)
+    (make-overlay 43 31 nil t t)
+    (goto-char 55)
+    (delete-char 3)
+    (goto-char 12)
+    (insert "..")
+    (goto-char 62)
+    (insert "")
+    (goto-char 24)
+    (delete-char 2)
+    (goto-char 41)
+    (insert "............")
+    (goto-char 2)
+    (insert ".")
+    (goto-char 55)
+    (insert "........")
+    (goto-char 67)
+    (delete-char 6)
+    (goto-char 58)
+    (delete-char 10)
+    (goto-char 29)
+    (insert "")
+    (goto-char 6)
+    (widen)
+    (narrow-to-region 44 45)
+    (goto-char 44)
+    (delete-char 1)
+    (goto-char 44)
+    (widen)
+    (narrow-to-region 24 37)
+    (goto-char 30)
+    (delete-char 7)
+    (goto-char 27)
+    (insert "......")
+    (goto-char 35)
+    (delete-char 0)
+    (goto-char 32)
+    (insert "...............")
+    (goto-char 37)
+    (delete-char 9)
+    (goto-char 40)
+    (insert "..........")
+    (goto-char 35)
+    (insert "......")
+    (goto-char 25)
+    (delete-char 7)
+    (goto-char 40)
+    (delete-char 4)
+    (goto-char 25)
+    (delete-char 14)
+    (goto-char 28)
+    (insert "")
+    (goto-char 28)
+    (widen)
+    (narrow-to-region 17 43)
+    (goto-char 20)
+    (insert "..........")
+    (goto-char 22)
+    (delete-char 2)
+    (goto-char 48)
+    (insert "............")
+    (goto-char 47)
+    (insert ".........")
+    (goto-char 69)
+    (widen)
+    (narrow-to-region 52 25)
+    (goto-char 26)
+    (insert "......")
+    (goto-char 53)
+    (insert "..")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((5 . 38)
+        (8 . 97)
+        (12 . 47)
+        (37 . 47)
+        (39 . 52)
+        (39 . 87)
+        (39 . 95)
+        (46 . 90)
+        (47 . 49)
+        (47 . 90)
+        (47 . 99)
+        (48 . 87))))))
+
+(ert-deftest overlay-autogenerated-test-42 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 20 23 nil nil nil)
+    (make-overlay 45 51 nil t nil)
+    (make-overlay 34 58 nil t nil)
+    (make-overlay 27 11 nil nil nil)
+    (make-overlay 14 8 nil t t)
+    (make-overlay 64 43 nil t nil)
+    (make-overlay 61 56 nil nil t)
+    (make-overlay 28 14 nil t nil)
+    (make-overlay 21 46 nil t t)
+    (make-overlay 30 34 nil t t)
+    (make-overlay 47 40 nil nil nil)
+    (make-overlay 5 44 nil t t)
+    (make-overlay 11 45 nil nil nil)
+    (make-overlay 65 8 nil nil t)
+    (make-overlay 47 54 nil t t)
+    (make-overlay 37 57 nil t nil)
+    (goto-char 11)
+    (insert "....")
+    (goto-char 65)
+    (delete-char 0)
+    (goto-char 56)
+    (delete-char 4)
+    (goto-char 11)
+    (delete-char 2)
+    (goto-char 23)
+    (insert ".............")
+    (goto-char 2)
+    (insert "............")
+    (goto-char 84)
+    (delete-char 1)
+    (goto-char 10)
+    (insert "..............")
+    (goto-char 19)
+    (insert "............")
+    (goto-char 69)
+    (delete-char 6)
+    (goto-char 15)
+    (insert "........")
+    (goto-char 104)
+    (insert "")
+    (goto-char 94)
+    (delete-char 11)
+    (goto-char 66)
+    (insert ".....")
+    (goto-char 67)
+    (insert "")
+    (goto-char 53)
+    (delete-char 22)
+    (goto-char 42)
+    (insert ".")
+    (goto-char 38)
+    (delete-char 13)
+    (goto-char 27)
+    (insert "......")
+    (goto-char 16)
+    (insert "............")
+    (goto-char 71)
+    (widen)
+    (narrow-to-region 59 15)
+    (goto-char 46)
+    (insert "..")
+    (goto-char 20)
+    (widen)
+    (narrow-to-region 95 93)
+    (goto-char 94)
+    (insert ".............")
+    (goto-char 103)
+    (widen)
+    (narrow-to-region 97 7)
+    (goto-char 93)
+    (insert "....")
+    (goto-char 85)
+    (insert "...........")
+    (goto-char 69)
+    (delete-char 24)
+    (goto-char 87)
+    (insert ".............")
+    (goto-char 7)
+    (delete-char 28)
+    (goto-char 65)
+    (delete-char 8)
+    (goto-char 48)
+    (insert "......")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((31 . 44)
+        (33 . 33)
+        (33 . 41)
+        (33 . 41)
+        (33 . 41)
+        (33 . 41)
+        (33 . 82)
+        (40 . 44)
+        (41 . 41)
+        (41 . 41)
+        (41 . 47)
+        (41 . 48)
+        (44 . 45)
+        (44 . 46)
+        (44 . 63)
+        (46 . 57))))))
+
+(ert-deftest overlay-autogenerated-test-43 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 8 53 nil t nil)
+    (make-overlay 11 50 nil t nil)
+    (make-overlay 1 30 nil nil nil)
+    (make-overlay 54 15 nil t t)
+    (make-overlay 22 30 nil nil nil)
+    (make-overlay 1 33 nil nil nil)
+    (make-overlay 18 15 nil t nil)
+    (make-overlay 43 39 nil nil t)
+    (make-overlay 43 17 nil t nil)
+    (make-overlay 2 29 nil t nil)
+    (make-overlay 57 42 nil t nil)
+    (make-overlay 40 1 nil nil nil)
+    (make-overlay 8 64 nil nil nil)
+    (make-overlay 64 15 nil nil nil)
+    (make-overlay 9 11 nil nil t)
+    (make-overlay 40 21 nil t nil)
+    (goto-char 5)
+    (delete-char 37)
+    (goto-char 25)
+    (delete-char 2)
+    (goto-char 17)
+    (insert "...........")
+    (goto-char 19)
+    (widen)
+    (narrow-to-region 20 20)
+    (goto-char 20)
+    (delete-char 0)
+    (goto-char 20)
+    (insert "..........")
+    (goto-char 24)
+    (delete-char 5)
+    (goto-char 24)
+    (insert "...")
+    (goto-char 28)
+    (widen)
+    (narrow-to-region 20 36)
+    (goto-char 26)
+    (delete-char 2)
+    (goto-char 31)
+    (insert ".............")
+    (goto-char 22)
+    (insert ".....")
+    (goto-char 38)
+    (delete-char 0)
+    (goto-char 31)
+    (delete-char 4)
+    (goto-char 27)
+    (insert "...")
+    (goto-char 23)
+    (widen)
+    (narrow-to-region 37 20)
+    (goto-char 22)
+    (insert ".............")
+    (goto-char 33)
+    (insert "......")
+    (goto-char 43)
+    (insert "............")
+    (goto-char 59)
+    (insert ".......")
+    (goto-char 25)
+    (delete-char 26)
+    (goto-char 49)
+    (insert ".........")
+    (goto-char 50)
+    (insert ".......")
+    (goto-char 39)
+    (widen)
+    (narrow-to-region 54 86)
+    (goto-char 64)
+    (insert "...............")
+    (goto-char 83)
+    (insert "............")
+    (goto-char 70)
+    (insert "........")
+    (goto-char 58)
+    (insert "..............")
+    (goto-char 83)
+    (insert "............")
+    (goto-char 83)
+    (insert "..........")
+    (goto-char 69)
+    (delete-char 75)
+    (goto-char 75)
+    (delete-char 3)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((5 . 75)
+        (5 . 75)
+        (5 . 80)
+        (5 . 80))))))
+
+(ert-deftest overlay-autogenerated-test-44 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 8 48 nil nil t)
+    (make-overlay 52 38 nil nil nil)
+    (make-overlay 3 63 nil nil nil)
+    (make-overlay 44 15 nil nil t)
+    (make-overlay 27 44 nil nil t)
+    (make-overlay 43 9 nil nil t)
+    (make-overlay 11 27 nil t nil)
+    (make-overlay 36 41 nil nil t)
+    (make-overlay 23 25 nil t t)
+    (make-overlay 19 60 nil t t)
+    (make-overlay 11 55 nil t nil)
+    (make-overlay 59 2 nil t nil)
+    (make-overlay 32 64 nil t nil)
+    (make-overlay 15 8 nil nil nil)
+    (make-overlay 61 15 nil nil nil)
+    (make-overlay 64 30 nil t t)
+    (goto-char 42)
+    (delete-char 20)
+    (goto-char 44)
+    (delete-char 1)
+    (goto-char 43)
+    (insert "...........")
+    (goto-char 43)
+    (delete-char 1)
+    (goto-char 28)
+    (delete-char 8)
+    (goto-char 37)
+    (delete-char 9)
+    (goto-char 4)
+    (delete-char 30)
+    (goto-char 6)
+    (delete-char 0)
+    (goto-char 7)
+    (delete-char 0)
+    (goto-char 2)
+    (delete-char 2)
+    (goto-char 5)
+    (delete-char 0)
+    (goto-char 5)
+    (delete-char 0)
+    (goto-char 2)
+    (insert ".....")
+    (goto-char 10)
+    (insert "...........")
+    (goto-char 21)
+    (insert "...")
+    (goto-char 10)
+    (delete-char 13)
+    (goto-char 9)
+    (insert "..........")
+    (goto-char 16)
+    (delete-char 1)
+    (goto-char 16)
+    (delete-char 4)
+    (goto-char 16)
+    (delete-char 0)
+    (goto-char 14)
+    (delete-char 1)
+    (goto-char 3)
+    (widen)
+    (narrow-to-region 2 9)
+    (goto-char 2)
+    (insert "")
+    (goto-char 2)
+    (insert ".............")
+    (goto-char 17)
+    (insert "....")
+    (goto-char 12)
+    (insert "........")
+    (goto-char 8)
+    (widen)
+    (narrow-to-region 32 23)
+    (goto-char 29)
+    (insert ".....")
+    (goto-char 35)
+    (delete-char 2)
+    (goto-char 27)
+    (delete-char 7)
+    (goto-char 23)
+    (widen)
+    (narrow-to-region 4 14)
+    (goto-char 8)
+    (insert "...............")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((2 . 43)
+        (2 . 43)
+        (2 . 43)
+        (2 . 43)
+        (2 . 43)
+        (2 . 44))))))
+
+(ert-deftest overlay-autogenerated-test-45 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 15 48 nil nil nil)
+    (make-overlay 1 47 nil t nil)
+    (make-overlay 43 4 nil t t)
+    (make-overlay 9 45 nil t t)
+    (make-overlay 1 25 nil t t)
+    (make-overlay 5 46 nil t t)
+    (make-overlay 7 14 nil t nil)
+    (make-overlay 1 53 nil nil t)
+    (make-overlay 13 41 nil t nil)
+    (make-overlay 5 31 nil t t)
+    (make-overlay 26 10 nil nil nil)
+    (make-overlay 56 37 nil nil nil)
+    (make-overlay 23 15 nil t nil)
+    (make-overlay 62 30 nil t t)
+    (make-overlay 2 35 nil t t)
+    (make-overlay 46 41 nil nil nil)
+    (goto-char 65)
+    (delete-char 0)
+    (goto-char 55)
+    (insert "...........")
+    (goto-char 22)
+    (insert "")
+    (goto-char 73)
+    (delete-char 3)
+    (goto-char 43)
+    (widen)
+    (narrow-to-region 54 63)
+    (goto-char 56)
+    (insert "......")
+    (goto-char 61)
+    (delete-char 3)
+    (goto-char 65)
+    (insert "......")
+    (goto-char 66)
+    (insert ".....")
+    (goto-char 62)
+    (insert ".")
+    (goto-char 74)
+    (insert ".........")
+    (goto-char 76)
+    (delete-char 4)
+    (goto-char 56)
+    (widen)
+    (narrow-to-region 2 46)
+    (goto-char 43)
+    (insert "...........")
+    (goto-char 20)
+    (delete-char 4)
+    (goto-char 38)
+    (delete-char 7)
+    (goto-char 25)
+    (delete-char 21)
+    (goto-char 12)
+    (insert ".........")
+    (goto-char 19)
+    (widen)
+    (narrow-to-region 72 61)
+    (goto-char 63)
+    (insert "")
+    (goto-char 65)
+    (delete-char 4)
+    (goto-char 61)
+    (delete-char 5)
+    (goto-char 63)
+    (delete-char 0)
+    (goto-char 63)
+    (delete-char 0)
+    (goto-char 62)
+    (delete-char 0)
+    (goto-char 61)
+    (insert "............")
+    (goto-char 72)
+    (insert "..............")
+    (goto-char 62)
+    (delete-char 7)
+    (goto-char 71)
+    (delete-char 5)
+    (goto-char 75)
+    (widen)
+    (narrow-to-region 29 8)
+    (goto-char 17)
+    (delete-char 2)
+    (goto-char 27)
+    (insert "........")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((1 . 36)
+        (1 . 41)
+        (1 . 47)
+        (2 . 40)
+        (4 . 40)
+        (5 . 40)
+        (5 . 40)
+        (7 . 21)
+        (9 . 40)
+        (10 . 37)
+        (20 . 40)
+        (22 . 27)
+        (22 . 42))))))
+
+(ert-deftest overlay-autogenerated-test-46 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 2 43 nil nil t)
+    (make-overlay 44 40 nil nil t)
+    (make-overlay 49 14 nil nil t)
+    (make-overlay 6 55 nil nil nil)
+    (make-overlay 13 52 nil t t)
+    (make-overlay 40 54 nil t nil)
+    (make-overlay 51 41 nil nil t)
+    (make-overlay 7 28 nil nil t)
+    (make-overlay 10 47 nil nil t)
+    (make-overlay 63 21 nil t nil)
+    (make-overlay 4 55 nil nil nil)
+    (make-overlay 52 58 nil t nil)
+    (make-overlay 62 11 nil t t)
+    (make-overlay 22 49 nil t nil)
+    (make-overlay 23 65 nil nil nil)
+    (make-overlay 50 33 nil nil t)
+    (goto-char 22)
+    (insert "..............")
+    (goto-char 12)
+    (insert "....")
+    (goto-char 25)
+    (delete-char 16)
+    (goto-char 14)
+    (delete-char 53)
+    (goto-char 2)
+    (insert "............")
+    (goto-char 20)
+    (delete-char 5)
+    (goto-char 11)
+    (delete-char 7)
+    (goto-char 9)
+    (widen)
+    (narrow-to-region 11 7)
+    (goto-char 8)
+    (insert "...............")
+    (goto-char 12)
+    (delete-char 4)
+    (goto-char 21)
+    (insert "...")
+    (goto-char 20)
+    (delete-char 5)
+    (goto-char 7)
+    (delete-char 3)
+    (goto-char 16)
+    (delete-char 0)
+    (goto-char 12)
+    (delete-char 1)
+    (goto-char 15)
+    (delete-char 0)
+    (goto-char 7)
+    (insert "..............")
+    (goto-char 17)
+    (insert "...........")
+    (goto-char 15)
+    (insert "............")
+    (goto-char 20)
+    (delete-char 5)
+    (goto-char 7)
+    (insert "....")
+    (goto-char 37)
+    (delete-char 7)
+    (goto-char 8)
+    (insert "..........")
+    (goto-char 47)
+    (insert ".............")
+    (goto-char 65)
+    (insert ".......")
+    (goto-char 39)
+    (delete-char 26)
+    (goto-char 14)
+    (delete-char 2)
+    (goto-char 27)
+    (insert ".............")
+    (goto-char 17)
+    (widen)
+    (narrow-to-region 54 32)
+    (goto-char 40)
+    (widen)
+    (narrow-to-region 10 3)
+    (goto-char 7)
+    (insert "........")
+    (goto-char 13)
+    (insert "..............")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((2 . 85))))))
+
+(ert-deftest overlay-autogenerated-test-47 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 58 62 nil t nil)
+    (make-overlay 14 38 nil nil nil)
+    (make-overlay 63 44 nil t t)
+    (make-overlay 41 41 nil nil t)
+    (make-overlay 19 39 nil nil nil)
+    (make-overlay 10 49 nil t t)
+    (make-overlay 56 38 nil t t)
+    (make-overlay 23 38 nil nil t)
+    (make-overlay 1 64 nil nil t)
+    (make-overlay 21 3 nil t nil)
+    (make-overlay 1 1 nil nil t)
+    (make-overlay 27 61 nil nil nil)
+    (make-overlay 29 59 nil nil nil)
+    (make-overlay 37 30 nil t nil)
+    (make-overlay 47 21 nil nil t)
+    (make-overlay 34 26 nil t nil)
+    (goto-char 6)
+    (delete-char 44)
+    (goto-char 8)
+    (delete-char 0)
+    (goto-char 8)
+    (insert "....")
+    (goto-char 17)
+    (delete-char 2)
+    (goto-char 12)
+    (insert "...")
+    (goto-char 20)
+    (insert "")
+    (goto-char 2)
+    (delete-char 20)
+    (goto-char 1)
+    (insert ".........")
+    (goto-char 7)
+    (insert ".............")
+    (goto-char 27)
+    (delete-char 0)
+    (goto-char 15)
+    (insert "..........")
+    (goto-char 36)
+    (insert "..............")
+    (goto-char 26)
+    (insert "..............")
+    (goto-char 63)
+    (insert "...........")
+    (goto-char 9)
+    (insert "............")
+    (goto-char 71)
+    (delete-char 17)
+    (goto-char 36)
+    (insert "....")
+    (goto-char 45)
+    (delete-char 31)
+    (goto-char 28)
+    (delete-char 8)
+    (goto-char 10)
+    (delete-char 16)
+    (goto-char 14)
+    (delete-char 4)
+    (goto-char 16)
+    (delete-char 0)
+    (goto-char 15)
+    (insert "")
+    (goto-char 14)
+    (delete-char 1)
+    (goto-char 10)
+    (delete-char 2)
+    (goto-char 6)
+    (delete-char 0)
+    (goto-char 1)
+    (insert ".........")
+    (goto-char 23)
+    (insert "......")
+    (goto-char 25)
+    (insert "..........")
+    (goto-char 25)
+    (widen)
+    (narrow-to-region 10 30)
+    (goto-char 21)
+    (delete-char 1)
+    (goto-char 17)
+    (insert "..........")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((1 . 48)
+        (1 . 48)
+        (32 . 32)
+        (32 . 32)
+        (32 . 32)
+        (32 . 32)
+        (32 . 32)
+        (32 . 32)
+        (32 . 32)
+        (32 . 32)
+        (32 . 48)
+        (32 . 48)
+        (32 . 48))))))
+
+(ert-deftest overlay-autogenerated-test-48 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 1 11 nil nil nil)
+    (make-overlay 35 29 nil t t)
+    (make-overlay 24 46 nil nil t)
+    (make-overlay 15 43 nil nil t)
+    (make-overlay 51 49 nil t t)
+    (make-overlay 25 43 nil t nil)
+    (make-overlay 23 59 nil nil nil)
+    (make-overlay 10 4 nil t nil)
+    (make-overlay 40 45 nil nil nil)
+    (make-overlay 42 43 nil nil t)
+    (make-overlay 20 38 nil t nil)
+    (make-overlay 17 49 nil nil nil)
+    (make-overlay 9 25 nil nil t)
+    (make-overlay 13 19 nil nil nil)
+    (make-overlay 44 31 nil t nil)
+    (make-overlay 12 65 nil nil t)
+    (goto-char 59)
+    (widen)
+    (narrow-to-region 28 14)
+    (goto-char 26)
+    (insert "...")
+    (goto-char 30)
+    (delete-char 1)
+    (goto-char 23)
+    (insert "...")
+    (goto-char 27)
+    (widen)
+    (narrow-to-region 45 67)
+    (goto-char 50)
+    (insert "...............")
+    (goto-char 59)
+    (insert "..............")
+    (goto-char 55)
+    (insert ".............")
+    (goto-char 106)
+    (delete-char 0)
+    (goto-char 97)
+    (delete-char 10)
+    (goto-char 67)
+    (delete-char 16)
+    (goto-char 76)
+    (insert "..............")
+    (goto-char 71)
+    (insert ".............")
+    (goto-char 110)
+    (delete-char 0)
+    (goto-char 56)
+    (delete-char 38)
+    (goto-char 61)
+    (delete-char 10)
+    (goto-char 56)
+    (delete-char 5)
+    (goto-char 49)
+    (insert ".......")
+    (goto-char 62)
+    (insert "...")
+    (goto-char 54)
+    (insert "..........")
+    (goto-char 47)
+    (delete-char 10)
+    (goto-char 47)
+    (delete-char 20)
+    (goto-char 46)
+    (insert ".............")
+    (goto-char 56)
+    (insert "...........")
+    (goto-char 70)
+    (delete-char 1)
+    (goto-char 62)
+    (widen)
+    (narrow-to-region 50 64)
+    (goto-char 60)
+    (insert "..")
+    (goto-char 55)
+    (delete-char 6)
+    (goto-char 60)
+    (insert ".............")
+    (goto-char 61)
+    (delete-char 9)
+    (goto-char 64)
+    (delete-char 0)
+    (goto-char 53)
+    (widen)
+    (narrow-to-region 15 62)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((9 . 28)
+        (12 . 73)
+        (13 . 19)
+        (15 . 70)
+        (17 . 70)
+        (20 . 43)
+        (23 . 70)
+        (27 . 70)
+        (28 . 70)
+        (34 . 40)
+        (36 . 70)
+        (45 . 70))))))
+
+(ert-deftest overlay-autogenerated-test-49 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 24 10 nil nil t)
+    (make-overlay 53 23 nil t nil)
+    (make-overlay 53 9 nil nil t)
+    (make-overlay 65 64 nil t t)
+    (make-overlay 48 2 nil nil t)
+    (make-overlay 12 58 nil nil t)
+    (make-overlay 64 64 nil nil nil)
+    (make-overlay 26 13 nil t t)
+    (make-overlay 46 26 nil nil t)
+    (make-overlay 28 59 nil t t)
+    (make-overlay 33 52 nil nil nil)
+    (make-overlay 39 8 nil t t)
+    (make-overlay 9 59 nil t t)
+    (make-overlay 50 45 nil nil t)
+    (make-overlay 41 53 nil nil t)
+    (make-overlay 51 51 nil t nil)
+    (goto-char 61)
+    (insert "..............")
+    (goto-char 19)
+    (widen)
+    (narrow-to-region 10 65)
+    (goto-char 65)
+    (delete-char 0)
+    (goto-char 11)
+    (insert "...............")
+    (goto-char 77)
+    (delete-char 0)
+    (goto-char 51)
+    (insert "...")
+    (goto-char 75)
+    (insert ".....")
+    (goto-char 77)
+    (delete-char 11)
+    (goto-char 45)
+    (delete-char 0)
+    (goto-char 24)
+    (widen)
+    (narrow-to-region 33 52)
+    (goto-char 46)
+    (insert "..............")
+    (goto-char 46)
+    (insert "..........")
+    (goto-char 39)
+    (widen)
+    (narrow-to-region 46 77)
+    (goto-char 77)
+    (insert "..............")
+    (goto-char 54)
+    (insert ".......")
+    (goto-char 87)
+    (insert ".")
+    (goto-char 70)
+    (delete-char 16)
+    (goto-char 79)
+    (delete-char 0)
+    (goto-char 73)
+    (widen)
+    (narrow-to-region 74 100)
+    (goto-char 91)
+    (insert ".............")
+    (goto-char 80)
+    (delete-char 11)
+    (goto-char 82)
+    (insert "......")
+    (goto-char 108)
+    (delete-char 0)
+    (goto-char 104)
+    (insert ".....")
+    (goto-char 100)
+    (delete-char 1)
+    (goto-char 90)
+    (insert ".............")
+    (goto-char 99)
+    (insert ".............")
+    (goto-char 124)
+    (insert "..............")
+    (goto-char 114)
+    (insert "....")
+    (goto-char 134)
+    (delete-char 0)
+    (goto-char 89)
+    (delete-char 65)
+    (goto-char 75)
+    (delete-char 16)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((2 . 75)
+        (8 . 75)
+        (9 . 76)
+        (9 . 82)
+        (27 . 82)
+        (38 . 76)
+        (41 . 75)
+        (43 . 82)
+        (70 . 75))))))
+
+(ert-deftest overlay-autogenerated-test-50 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 29 53 nil t t)
+    (make-overlay 65 64 nil nil nil)
+    (make-overlay 3 31 nil nil t)
+    (make-overlay 45 59 nil t nil)
+    (make-overlay 60 37 nil t t)
+    (make-overlay 7 5 nil t t)
+    (make-overlay 37 24 nil nil nil)
+    (make-overlay 45 20 nil nil nil)
+    (make-overlay 33 42 nil nil t)
+    (make-overlay 47 57 nil t nil)
+    (make-overlay 14 49 nil t t)
+    (make-overlay 14 30 nil t nil)
+    (make-overlay 21 40 nil t t)
+    (make-overlay 5 45 nil t t)
+    (make-overlay 59 40 nil t t)
+    (make-overlay 37 52 nil nil nil)
+    (goto-char 48)
+    (insert "")
+    (goto-char 7)
+    (insert ".........")
+    (goto-char 31)
+    (insert "...........")
+    (goto-char 41)
+    (delete-char 7)
+    (goto-char 21)
+    (delete-char 11)
+    (goto-char 41)
+    (widen)
+    (narrow-to-region 51 53)
+    (goto-char 52)
+    (insert ".....")
+    (goto-char 55)
+    (widen)
+    (narrow-to-region 18 24)
+    (goto-char 23)
+    (widen)
+    (narrow-to-region 39 38)
+    (goto-char 38)
+    (insert ".............")
+    (goto-char 41)
+    (insert "......")
+    (goto-char 38)
+    (insert "..............")
+    (goto-char 52)
+    (insert "...............")
+    (goto-char 78)
+    (delete-char 5)
+    (goto-char 50)
+    (insert "..........")
+    (goto-char 50)
+    (delete-char 3)
+    (goto-char 85)
+    (widen)
+    (narrow-to-region 86 1)
+    (goto-char 5)
+    (insert "....")
+    (goto-char 69)
+    (insert "...........")
+    (goto-char 94)
+    (insert "......")
+    (goto-char 98)
+    (delete-char 7)
+    (goto-char 46)
+    (insert "...............")
+    (goto-char 79)
+    (insert "............")
+    (goto-char 89)
+    (insert "")
+    (goto-char 14)
+    (delete-char 63)
+    (goto-char 20)
+    (insert ".........")
+    (goto-char 34)
+    (insert "...")
+    (goto-char 53)
+    (delete-char 14)
+    (goto-char 6)
+    (widen)
+    (narrow-to-region 6 52)
+    (goto-char 42)
+    (insert "...........")
+    (goto-char 40)
+    (insert ".......")
+    (goto-char 46)
+    (widen)
+    (narrow-to-region 1 68)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((3 . 14)
+        (9 . 14)
+        (9 . 91)
+        (14 . 14)
+        (14 . 83)
+        (14 . 86)
+        (14 . 88)
+        (14 . 91)
+        (14 . 95)
+        (14 . 104))))))
+
+(ert-deftest overlay-autogenerated-test-51 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 14 5 nil t nil)
+    (make-overlay 62 34 nil nil t)
+    (make-overlay 7 62 nil nil t)
+    (make-overlay 23 12 nil t t)
+    (make-overlay 16 4 nil nil nil)
+    (make-overlay 24 15 nil nil nil)
+    (make-overlay 6 6 nil t t)
+    (make-overlay 25 64 nil t t)
+    (make-overlay 23 6 nil t t)
+    (make-overlay 55 64 nil nil nil)
+    (make-overlay 8 62 nil nil t)
+    (make-overlay 65 65 nil nil nil)
+    (make-overlay 57 51 nil t t)
+    (make-overlay 35 8 nil t nil)
+    (make-overlay 55 13 nil nil t)
+    (make-overlay 60 62 nil nil t)
+    (goto-char 12)
+    (insert "..")
+    (goto-char 66)
+    (insert "............")
+    (goto-char 32)
+    (insert "..")
+    (goto-char 27)
+    (insert ".........")
+    (goto-char 8)
+    (insert ".............")
+    (goto-char 79)
+    (insert ".")
+    (goto-char 47)
+    (insert "....")
+    (goto-char 49)
+    (insert "...")
+    (goto-char 81)
+    (insert "....")
+    (goto-char 112)
+    (delete-char 0)
+    (goto-char 97)
+    (insert ".....")
+    (goto-char 109)
+    (delete-char 5)
+    (goto-char 20)
+    (insert ".....")
+    (goto-char 59)
+    (delete-char 33)
+    (goto-char 87)
+    (insert ".............")
+    (goto-char 98)
+    (insert "....")
+    (goto-char 22)
+    (delete-char 36)
+    (goto-char 45)
+    (insert "..............")
+    (goto-char 42)
+    (delete-char 29)
+    (goto-char 51)
+    (widen)
+    (narrow-to-region 39 41)
+    (goto-char 39)
+    (delete-char 2)
+    (goto-char 39)
+    (insert ".............")
+    (goto-char 51)
+    (insert "......")
+    (goto-char 52)
+    (insert "...............")
+    (goto-char 56)
+    (widen)
+    (narrow-to-region 59 20)
+    (goto-char 56)
+    (insert "............")
+    (goto-char 57)
+    (insert ".")
+    (goto-char 37)
+    (delete-char 12)
+    (goto-char 39)
+    (delete-char 11)
+    (goto-char 38)
+    (delete-char 8)
+    (goto-char 36)
+    (widen)
+    (narrow-to-region 65 26)
+    (goto-char 40)
+    (widen)
+    (narrow-to-region 27 55)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((7 . 55)
+        (8 . 55)
+        (22 . 29)
+        (23 . 55)
+        (23 . 56)
+        (24 . 31)
+        (29 . 56)
+        (37 . 55))))))
+
+(ert-deftest overlay-autogenerated-test-52 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 58 32 nil nil nil)
+    (make-overlay 44 54 nil nil t)
+    (make-overlay 27 50 nil nil nil)
+    (make-overlay 55 35 nil nil t)
+    (make-overlay 40 46 nil nil t)
+    (make-overlay 56 63 nil t nil)
+    (make-overlay 29 48 nil nil nil)
+    (make-overlay 45 24 nil t nil)
+    (make-overlay 60 25 nil t nil)
+    (make-overlay 55 41 nil t nil)
+    (make-overlay 55 1 nil nil t)
+    (make-overlay 30 45 nil t t)
+    (make-overlay 26 19 nil nil t)
+    (make-overlay 61 5 nil nil nil)
+    (make-overlay 33 5 nil nil nil)
+    (make-overlay 42 18 nil t nil)
+    (goto-char 55)
+    (insert ".")
+    (goto-char 49)
+    (delete-char 12)
+    (goto-char 41)
+    (insert "..........")
+    (goto-char 27)
+    (insert ".....")
+    (goto-char 58)
+    (insert "...........")
+    (goto-char 24)
+    (delete-char 23)
+    (goto-char 47)
+    (delete-char 9)
+    (goto-char 4)
+    (insert "...")
+    (goto-char 10)
+    (delete-char 32)
+    (goto-char 4)
+    (insert "..............")
+    (goto-char 29)
+    (insert "....")
+    (goto-char 28)
+    (delete-char 2)
+    (goto-char 34)
+    (insert "...........")
+    (goto-char 9)
+    (insert "......")
+    (goto-char 5)
+    (insert "")
+    (goto-char 45)
+    (delete-char 1)
+    (goto-char 18)
+    (insert ".........")
+    (goto-char 36)
+    (delete-char 5)
+    (goto-char 15)
+    (delete-char 27)
+    (goto-char 15)
+    (delete-char 10)
+    (goto-char 16)
+    (delete-char 2)
+    (goto-char 16)
+    (widen)
+    (narrow-to-region 10 2)
+    (goto-char 9)
+    (delete-char 1)
+    (goto-char 3)
+    (delete-char 2)
+    (goto-char 2)
+    (widen)
+    (narrow-to-region 9 10)
+    (goto-char 9)
+    (insert "...........")
+    (goto-char 19)
+    (delete-char 0)
+    (goto-char 14)
+    (delete-char 3)
+    (goto-char 11)
+    (delete-char 2)
+    (goto-char 9)
+    (delete-char 6)
+    (goto-char 9)
+    (delete-char 0)
+    (goto-char 10)
+    (insert "....")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((1 . 17))))))
+
+(ert-deftest overlay-autogenerated-test-53 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 10 30 nil nil nil)
+    (make-overlay 11 57 nil t nil)
+    (make-overlay 59 56 nil nil t)
+    (make-overlay 20 37 nil nil t)
+    (make-overlay 41 29 nil nil nil)
+    (make-overlay 31 10 nil nil t)
+    (make-overlay 6 36 nil nil nil)
+    (make-overlay 12 54 nil nil nil)
+    (make-overlay 25 26 nil t t)
+    (make-overlay 21 19 nil nil t)
+    (make-overlay 1 21 nil nil t)
+    (make-overlay 48 51 nil nil nil)
+    (make-overlay 54 55 nil t nil)
+    (make-overlay 64 48 nil t t)
+    (make-overlay 56 25 nil nil t)
+    (make-overlay 12 60 nil t nil)
+    (goto-char 41)
+    (delete-char 1)
+    (goto-char 63)
+    (insert "")
+    (goto-char 14)
+    (delete-char 5)
+    (goto-char 11)
+    (insert "..............")
+    (goto-char 41)
+    (widen)
+    (narrow-to-region 12 1)
+    (goto-char 1)
+    (delete-char 3)
+    (goto-char 9)
+    (delete-char 0)
+    (goto-char 5)
+    (insert "..............")
+    (goto-char 1)
+    (insert "..........")
+    (goto-char 29)
+    (insert "...............")
+    (goto-char 4)
+    (insert "..")
+    (goto-char 31)
+    (delete-char 15)
+    (goto-char 31)
+    (insert "")
+    (goto-char 27)
+    (insert "......")
+    (goto-char 6)
+    (insert "...")
+    (goto-char 23)
+    (widen)
+    (narrow-to-region 23 47)
+    (goto-char 37)
+    (delete-char 2)
+    (goto-char 35)
+    (delete-char 5)
+    (goto-char 38)
+    (delete-char 2)
+    (goto-char 30)
+    (insert ".......")
+    (goto-char 45)
+    (widen)
+    (narrow-to-region 13 2)
+    (goto-char 9)
+    (delete-char 1)
+    (goto-char 3)
+    (insert ".....")
+    (goto-char 2)
+    (insert "...............")
+    (goto-char 16)
+    (delete-char 5)
+    (goto-char 20)
+    (insert ".....")
+    (goto-char 26)
+    (delete-char 0)
+    (goto-char 26)
+    (widen)
+    (narrow-to-region 76 98)
+    (goto-char 88)
+    (insert ".........")
+    (goto-char 92)
+    (insert ".")
+    (goto-char 108)
+    (delete-char 0)
+    (goto-char 103)
+    (delete-char 3)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((1 . 79)
+        (37 . 103)
+        (61 . 88)
+        (61 . 99)
+        (74 . 121)
+        (75 . 118)
+        (75 . 124)
+        (77 . 79)
+        (78 . 103)
+        (83 . 84)
+        (83 . 120)
+        (87 . 106))))))
+
+(ert-deftest overlay-autogenerated-test-54 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 58 36 nil t t)
+    (make-overlay 55 49 nil nil t)
+    (make-overlay 12 25 nil nil t)
+    (make-overlay 16 37 nil t t)
+    (make-overlay 42 25 nil t t)
+    (make-overlay 8 41 nil t t)
+    (make-overlay 13 27 nil nil t)
+    (make-overlay 52 22 nil t nil)
+    (make-overlay 36 17 nil t nil)
+    (make-overlay 1 52 nil t nil)
+    (make-overlay 55 5 nil nil t)
+    (make-overlay 50 50 nil t nil)
+    (make-overlay 32 15 nil t nil)
+    (make-overlay 39 26 nil t nil)
+    (make-overlay 26 4 nil nil nil)
+    (make-overlay 38 47 nil t t)
+    (goto-char 23)
+    (insert ".")
+    (goto-char 57)
+    (delete-char 6)
+    (goto-char 54)
+    (insert "..............")
+    (goto-char 46)
+    (insert "...............")
+    (goto-char 29)
+    (insert ".......")
+    (goto-char 58)
+    (delete-char 21)
+    (goto-char 45)
+    (delete-char 4)
+    (goto-char 50)
+    (delete-char 4)
+    (goto-char 20)
+    (insert ".........")
+    (goto-char 16)
+    (insert "......")
+    (goto-char 17)
+    (insert ".....")
+    (goto-char 63)
+    (insert "........")
+    (goto-char 83)
+    (insert "....")
+    (goto-char 73)
+    (delete-char 8)
+    (goto-char 69)
+    (insert "...........")
+    (goto-char 48)
+    (widen)
+    (narrow-to-region 19 31)
+    (goto-char 22)
+    (delete-char 3)
+    (goto-char 23)
+    (delete-char 5)
+    (goto-char 20)
+    (insert "............")
+    (goto-char 23)
+    (delete-char 11)
+    (goto-char 19)
+    (insert "..........")
+    (goto-char 23)
+    (insert "........")
+    (goto-char 38)
+    (delete-char 1)
+    (goto-char 33)
+    (delete-char 5)
+    (goto-char 27)
+    (insert "..........")
+    (goto-char 35)
+    (delete-char 8)
+    (goto-char 35)
+    (insert ".")
+    (goto-char 20)
+    (insert "......")
+    (goto-char 22)
+    (delete-char 22)
+    (goto-char 23)
+    (delete-char 0)
+    (goto-char 22)
+    (widen)
+    (narrow-to-region 1 41)
+    (goto-char 13)
+    (insert ".......")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((1 . 83)
+        (4 . 46)
+        (5 . 97)
+        (8 . 83)
+        (12 . 45)
+        (13 . 47)
+        (22 . 59)
+        (30 . 82)
+        (30 . 83)
+        (41 . 83)
+        (45 . 83)
+        (46 . 83))))))
+
+(ert-deftest overlay-autogenerated-test-55 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 58 20 nil nil nil)
+    (make-overlay 60 33 nil t nil)
+    (make-overlay 6 27 nil nil nil)
+    (make-overlay 53 31 nil nil t)
+    (make-overlay 30 55 nil t t)
+    (make-overlay 4 64 nil t t)
+    (make-overlay 51 31 nil nil t)
+    (make-overlay 4 65 nil t t)
+    (make-overlay 57 62 nil t t)
+    (make-overlay 28 7 nil nil t)
+    (make-overlay 61 48 nil t nil)
+    (make-overlay 23 54 nil nil t)
+    (make-overlay 47 49 nil nil nil)
+    (make-overlay 12 52 nil t nil)
+    (make-overlay 39 57 nil t t)
+    (make-overlay 28 61 nil nil t)
+    (goto-char 8)
+    (insert "..............")
+    (goto-char 63)
+    (delete-char 3)
+    (goto-char 67)
+    (delete-char 6)
+    (goto-char 3)
+    (widen)
+    (narrow-to-region 10 67)
+    (goto-char 43)
+    (insert ".............")
+    (goto-char 20)
+    (insert "...............")
+    (goto-char 18)
+    (insert "..")
+    (goto-char 37)
+    (delete-char 47)
+    (goto-char 34)
+    (insert "..............")
+    (goto-char 31)
+    (delete-char 2)
+    (goto-char 16)
+    (widen)
+    (narrow-to-region 29 36)
+    (goto-char 31)
+    (delete-char 2)
+    (goto-char 31)
+    (insert ".......")
+    (goto-char 40)
+    (delete-char 0)
+    (goto-char 32)
+    (widen)
+    (narrow-to-region 40 19)
+    (goto-char 40)
+    (insert "..")
+    (goto-char 37)
+    (delete-char 0)
+    (goto-char 40)
+    (delete-char 1)
+    (goto-char 34)
+    (delete-char 4)
+    (goto-char 33)
+    (insert "..............")
+    (goto-char 19)
+    (widen)
+    (narrow-to-region 78 70)
+    (goto-char 77)
+    (insert ".........")
+    (goto-char 80)
+    (delete-char 1)
+    (goto-char 73)
+    (delete-char 3)
+    (goto-char 70)
+    (insert ".........")
+    (goto-char 75)
+    (delete-char 10)
+    (goto-char 74)
+    (delete-char 3)
+    (goto-char 73)
+    (insert "...............")
+    (goto-char 90)
+    (insert "......")
+    (goto-char 94)
+    (insert "..............")
+    (goto-char 101)
+    (insert "........")
+    (goto-char 111)
+    (insert "........")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((4 . 132)
+        (4 . 133)
+        (65 . 89)
+        (65 . 89)
+        (65 . 89)
+        (65 . 89)
+        (65 . 129)
+        (65 . 130)
+        (65 . 130)
+        (65 . 130)
+        (65 . 130)
+        (89 . 89)
+        (89 . 130))))))
+
+(ert-deftest overlay-autogenerated-test-56 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 7 14 nil nil t)
+    (make-overlay 10 10 nil nil t)
+    (make-overlay 21 23 nil nil t)
+    (make-overlay 4 44 nil t nil)
+    (make-overlay 42 16 nil t t)
+    (make-overlay 1 57 nil t nil)
+    (make-overlay 15 27 nil nil nil)
+    (make-overlay 31 1 nil t nil)
+    (make-overlay 56 45 nil t t)
+    (make-overlay 46 19 nil t nil)
+    (make-overlay 15 6 nil nil nil)
+    (make-overlay 31 26 nil nil t)
+    (make-overlay 39 41 nil t t)
+    (make-overlay 52 48 nil nil t)
+    (make-overlay 44 2 nil t nil)
+    (make-overlay 60 7 nil nil t)
+    (goto-char 49)
+    (delete-char 11)
+    (goto-char 43)
+    (delete-char 9)
+    (goto-char 42)
+    (delete-char 2)
+    (goto-char 12)
+    (insert "...........")
+    (goto-char 36)
+    (insert ".........")
+    (goto-char 1)
+    (insert "......")
+    (goto-char 67)
+    (delete-char 0)
+    (goto-char 47)
+    (insert ".............")
+    (goto-char 57)
+    (insert "........")
+    (goto-char 22)
+    (widen)
+    (narrow-to-region 75 33)
+    (goto-char 41)
+    (delete-char 28)
+    (goto-char 43)
+    (delete-char 0)
+    (goto-char 33)
+    (delete-char 5)
+    (goto-char 38)
+    (insert "..")
+    (goto-char 42)
+    (delete-char 0)
+    (goto-char 38)
+    (delete-char 0)
+    (goto-char 38)
+    (insert "............")
+    (goto-char 51)
+    (insert ".......")
+    (goto-char 48)
+    (insert "..")
+    (goto-char 55)
+    (insert ".")
+    (goto-char 33)
+    (delete-char 8)
+    (goto-char 42)
+    (insert "..")
+    (goto-char 45)
+    (insert "..")
+    (goto-char 59)
+    (insert ".............")
+    (goto-char 53)
+    (insert ".......")
+    (goto-char 81)
+    (delete-char 0)
+    (goto-char 44)
+    (delete-char 36)
+    (goto-char 38)
+    (delete-char 8)
+    (goto-char 33)
+    (insert ".............")
+    (goto-char 41)
+    (insert "..............")
+    (goto-char 65)
+    (insert "...............")
+    (goto-char 61)
+    (insert "...")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((7 . 86)
+        (7 . 97)
+        (8 . 97)
+        (10 . 97)
+        (13 . 97)
+        (32 . 68)
+        (33 . 60)
+        (60 . 97)
+        (60 . 97)
+        (68 . 86))))))
+
+(ert-deftest overlay-autogenerated-test-57 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 52 31 nil t nil)
+    (make-overlay 39 17 nil t nil)
+    (make-overlay 19 20 nil t t)
+    (make-overlay 18 3 nil nil t)
+    (make-overlay 19 47 nil nil t)
+    (make-overlay 38 54 nil nil nil)
+    (make-overlay 30 51 nil nil t)
+    (make-overlay 29 60 nil t t)
+    (make-overlay 57 38 nil nil nil)
+    (make-overlay 13 41 nil t nil)
+    (make-overlay 9 44 nil t nil)
+    (make-overlay 30 55 nil t nil)
+    (make-overlay 33 10 nil nil nil)
+    (make-overlay 14 35 nil nil t)
+    (make-overlay 53 50 nil t nil)
+    (make-overlay 25 28 nil nil t)
+    (goto-char 40)
+    (insert "..")
+    (goto-char 64)
+    (insert "........")
+    (goto-char 47)
+    (insert "............")
+    (goto-char 65)
+    (delete-char 0)
+    (goto-char 86)
+    (delete-char 1)
+    (goto-char 59)
+    (delete-char 11)
+    (goto-char 64)
+    (delete-char 8)
+    (goto-char 53)
+    (delete-char 0)
+    (goto-char 28)
+    (delete-char 8)
+    (goto-char 6)
+    (delete-char 33)
+    (goto-char 14)
+    (delete-char 2)
+    (goto-char 2)
+    (delete-char 10)
+    (goto-char 3)
+    (insert "..")
+    (goto-char 5)
+    (insert ".........")
+    (goto-char 1)
+    (insert "........")
+    (goto-char 10)
+    (delete-char 4)
+    (goto-char 26)
+    (insert "........")
+    (goto-char 23)
+    (insert "....")
+    (goto-char 1)
+    (widen)
+    (narrow-to-region 15 23)
+    (goto-char 19)
+    (insert "...")
+    (goto-char 24)
+    (delete-char 0)
+    (goto-char 19)
+    (insert ".......")
+    (goto-char 18)
+    (insert "..")
+    (goto-char 33)
+    (insert "...")
+    (goto-char 32)
+    (insert "...............")
+    (goto-char 29)
+    (delete-char 10)
+    (goto-char 29)
+    (insert "..........")
+    (goto-char 50)
+    (insert "")
+    (goto-char 16)
+    (insert ".........")
+    (goto-char 52)
+    (widen)
+    (narrow-to-region 59 15)
+    (goto-char 35)
+    (delete-char 4)
+    (goto-char 18)
+    (insert "....")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((10 . 57)
+        (10 . 57)
+        (10 . 57)
+        (10 . 60)
+        (10 . 60)
+        (10 . 61)
+        (10 . 68)
+        (57 . 57))))))
+
+(ert-deftest overlay-autogenerated-test-58 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 8 16 nil t nil)
+    (make-overlay 57 27 nil nil nil)
+    (make-overlay 15 62 nil nil nil)
+    (make-overlay 32 33 nil nil t)
+    (make-overlay 47 27 nil nil t)
+    (make-overlay 41 4 nil nil t)
+    (make-overlay 57 61 nil t nil)
+    (make-overlay 18 43 nil nil t)
+    (make-overlay 64 51 nil t t)
+    (make-overlay 44 26 nil nil nil)
+    (make-overlay 9 13 nil nil t)
+    (make-overlay 41 65 nil nil t)
+    (make-overlay 23 13 nil t t)
+    (make-overlay 26 59 nil t t)
+    (make-overlay 65 65 nil t t)
+    (make-overlay 15 7 nil nil nil)
+    (goto-char 41)
+    (insert "........")
+    (goto-char 35)
+    (delete-char 14)
+    (goto-char 32)
+    (widen)
+    (narrow-to-region 23 46)
+    (goto-char 41)
+    (delete-char 5)
+    (goto-char 29)
+    (delete-char 10)
+    (goto-char 31)
+    (insert ".")
+    (goto-char 29)
+    (insert "........")
+    (goto-char 27)
+    (delete-char 7)
+    (goto-char 29)
+    (insert "")
+    (goto-char 24)
+    (insert "............")
+    (goto-char 43)
+    (delete-char 1)
+    (goto-char 31)
+    (delete-char 9)
+    (goto-char 34)
+    (widen)
+    (narrow-to-region 20 14)
+    (goto-char 20)
+    (delete-char 0)
+    (goto-char 17)
+    (insert "...........")
+    (goto-char 31)
+    (delete-char 0)
+    (goto-char 16)
+    (insert "...........")
+    (goto-char 17)
+    (delete-char 8)
+    (goto-char 23)
+    (delete-char 5)
+    (goto-char 20)
+    (insert "..........")
+    (goto-char 33)
+    (widen)
+    (narrow-to-region 16 29)
+    (goto-char 24)
+    (insert "...............")
+    (goto-char 44)
+    (delete-char 0)
+    (goto-char 30)
+    (insert "....")
+    (goto-char 27)
+    (widen)
+    (narrow-to-region 4 22)
+    (goto-char 10)
+    (insert "..............")
+    (goto-char 36)
+    (insert "..")
+    (goto-char 10)
+    (delete-char 21)
+    (goto-char 14)
+    (delete-char 1)
+    (goto-char 14)
+    (insert "...........")
+    (goto-char 12)
+    (insert "........")
+    (goto-char 32)
+    (insert "........")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((4 . 92)
+        (7 . 10)
+        (8 . 10)
+        (9 . 10)
+        (10 . 82)
+        (10 . 104))))))
+
+(ert-deftest overlay-autogenerated-test-59 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 46 30 nil t t)
+    (make-overlay 3 26 nil nil nil)
+    (make-overlay 36 28 nil t t)
+    (make-overlay 49 49 nil t t)
+    (make-overlay 27 61 nil t nil)
+    (make-overlay 14 16 nil nil nil)
+    (make-overlay 50 61 nil t nil)
+    (make-overlay 59 63 nil nil nil)
+    (make-overlay 36 34 nil t nil)
+    (make-overlay 35 29 nil nil nil)
+    (make-overlay 5 65 nil nil nil)
+    (make-overlay 20 61 nil nil t)
+    (make-overlay 10 42 nil nil nil)
+    (make-overlay 47 49 nil nil t)
+    (make-overlay 12 4 nil nil nil)
+    (make-overlay 32 24 nil t t)
+    (goto-char 11)
+    (insert ".")
+    (goto-char 32)
+    (delete-char 2)
+    (goto-char 61)
+    (insert ".........")
+    (goto-char 36)
+    (insert "........")
+    (goto-char 55)
+    (widen)
+    (narrow-to-region 8 55)
+    (goto-char 21)
+    (insert "....")
+    (goto-char 32)
+    (delete-char 15)
+    (goto-char 30)
+    (delete-char 5)
+    (goto-char 31)
+    (insert "......")
+    (goto-char 18)
+    (insert "..")
+    (goto-char 14)
+    (insert ".............")
+    (goto-char 34)
+    (insert "............")
+    (goto-char 51)
+    (widen)
+    (narrow-to-region 58 31)
+    (goto-char 50)
+    (delete-char 5)
+    (goto-char 53)
+    (insert ".........")
+    (goto-char 56)
+    (insert "...............")
+    (goto-char 45)
+    (delete-char 1)
+    (goto-char 67)
+    (insert "............")
+    (goto-char 84)
+    (insert "")
+    (goto-char 39)
+    (delete-char 27)
+    (goto-char 39)
+    (delete-char 21)
+    (goto-char 32)
+    (insert "............")
+    (goto-char 36)
+    (widen)
+    (narrow-to-region 7 37)
+    (goto-char 11)
+    (insert ".......")
+    (goto-char 21)
+    (delete-char 13)
+    (goto-char 15)
+    (insert "....")
+    (goto-char 9)
+    (insert ".............")
+    (goto-char 13)
+    (delete-char 21)
+    (goto-char 21)
+    (delete-char 6)
+    (goto-char 16)
+    (insert ".......")
+    (goto-char 22)
+    (insert "")
+    (goto-char 27)
+    (delete-char 0)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((3 . 42)
+        (4 . 16)
+        (5 . 83)
+        (13 . 51)
+        (25 . 27))))))
+
+(ert-deftest overlay-autogenerated-test-60 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 38 32 nil t nil)
+    (make-overlay 32 42 nil t nil)
+    (make-overlay 29 11 nil nil t)
+    (make-overlay 52 22 nil t t)
+    (make-overlay 39 59 nil t nil)
+    (make-overlay 41 30 nil t t)
+    (make-overlay 29 61 nil nil t)
+    (make-overlay 11 45 nil nil nil)
+    (make-overlay 46 17 nil nil t)
+    (make-overlay 35 51 nil t t)
+    (make-overlay 22 13 nil nil t)
+    (make-overlay 52 34 nil nil t)
+    (make-overlay 59 4 nil nil t)
+    (make-overlay 8 22 nil nil nil)
+    (make-overlay 4 49 nil nil nil)
+    (make-overlay 52 45 nil t t)
+    (goto-char 48)
+    (delete-char 16)
+    (goto-char 37)
+    (delete-char 8)
+    (goto-char 14)
+    (insert "...............")
+    (goto-char 40)
+    (delete-char 16)
+    (goto-char 19)
+    (insert ".........")
+    (goto-char 16)
+    (insert "......")
+    (goto-char 10)
+    (insert "........")
+    (goto-char 11)
+    (insert "...............")
+    (goto-char 22)
+    (insert ".")
+    (goto-char 62)
+    (delete-char 16)
+    (goto-char 14)
+    (delete-char 11)
+    (goto-char 47)
+    (insert "....")
+    (goto-char 33)
+    (insert ".............")
+    (goto-char 49)
+    (delete-char 13)
+    (goto-char 28)
+    (insert "..")
+    (goto-char 35)
+    (delete-char 13)
+    (goto-char 44)
+    (insert "....")
+    (goto-char 34)
+    (delete-char 14)
+    (goto-char 23)
+    (insert ".....")
+    (goto-char 25)
+    (delete-char 4)
+    (goto-char 33)
+    (insert ".....")
+    (goto-char 27)
+    (delete-char 3)
+    (goto-char 16)
+    (widen)
+    (narrow-to-region 36 37)
+    (goto-char 36)
+    (delete-char 1)
+    (goto-char 36)
+    (insert ".......")
+    (goto-char 37)
+    (widen)
+    (narrow-to-region 35 31)
+    (goto-char 34)
+    (delete-char 0)
+    (goto-char 31)
+    (delete-char 2)
+    (goto-char 31)
+    (widen)
+    (narrow-to-region 24 3)
+    (goto-char 22)
+    (delete-char 2)
+    (goto-char 22)
+    (insert ".............")
+    (goto-char 4)
+    (insert ".")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((4 . 54)
+        (4 . 54)
+        (9 . 46))))))
+
+(ert-deftest overlay-autogenerated-test-61 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 45 56 nil t nil)
+    (make-overlay 60 45 nil nil nil)
+    (make-overlay 26 8 nil t t)
+    (make-overlay 63 39 nil nil nil)
+    (make-overlay 18 11 nil t nil)
+    (make-overlay 22 64 nil nil t)
+    (make-overlay 8 41 nil nil t)
+    (make-overlay 6 51 nil t t)
+    (make-overlay 38 26 nil t t)
+    (make-overlay 7 46 nil t nil)
+    (make-overlay 2 42 nil nil t)
+    (make-overlay 44 64 nil nil nil)
+    (make-overlay 7 62 nil t nil)
+    (make-overlay 8 40 nil nil t)
+    (make-overlay 62 36 nil t t)
+    (make-overlay 61 27 nil nil nil)
+    (goto-char 21)
+    (delete-char 0)
+    (goto-char 8)
+    (insert "")
+    (goto-char 55)
+    (insert "......")
+    (goto-char 38)
+    (delete-char 25)
+    (goto-char 37)
+    (delete-char 4)
+    (goto-char 12)
+    (delete-char 4)
+    (goto-char 3)
+    (delete-char 26)
+    (goto-char 10)
+    (insert ".......")
+    (goto-char 18)
+    (delete-char 0)
+    (goto-char 16)
+    (insert ".............")
+    (goto-char 18)
+    (delete-char 3)
+    (goto-char 7)
+    (insert "...")
+    (goto-char 20)
+    (insert "........")
+    (goto-char 38)
+    (delete-char 0)
+    (goto-char 1)
+    (delete-char 36)
+    (goto-char 3)
+    (delete-char 1)
+    (goto-char 2)
+    (insert "......")
+    (goto-char 4)
+    (insert ".......")
+    (goto-char 2)
+    (insert "...........")
+    (goto-char 27)
+    (insert ".....")
+    (goto-char 15)
+    (insert "...............")
+    (goto-char 2)
+    (insert "......")
+    (goto-char 17)
+    (delete-char 8)
+    (goto-char 15)
+    (delete-char 7)
+    (goto-char 33)
+    (delete-char 5)
+    (goto-char 13)
+    (insert "...........")
+    (goto-char 34)
+    (insert "...............")
+    (goto-char 33)
+    (insert "")
+    (goto-char 51)
+    (insert "....")
+    (goto-char 14)
+    (delete-char 36)
+    (goto-char 16)
+    (delete-char 1)
+    (goto-char 14)
+    (delete-char 8)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((1 . 1)
+        (1 . 1)
+        (1 . 1)
+        (1 . 1)
+        (1 . 1)
+        (1 . 1)
+        (1 . 1)
+        (1 . 1)
+        (1 . 1)
+        (1 . 1)
+        (1 . 1)
+        (1 . 1)
+        (1 . 1)
+        (1 . 1)
+        (1 . 18)
+        (1 . 18))))))
+
+(ert-deftest overlay-autogenerated-test-62 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 25 36 nil t nil)
+    (make-overlay 38 6 nil t nil)
+    (make-overlay 40 63 nil nil t)
+    (make-overlay 34 23 nil nil nil)
+    (make-overlay 48 46 nil nil nil)
+    (make-overlay 43 57 nil t t)
+    (make-overlay 6 53 nil t t)
+    (make-overlay 37 27 nil t t)
+    (make-overlay 8 39 nil t nil)
+    (make-overlay 62 6 nil nil nil)
+    (make-overlay 51 6 nil t t)
+    (make-overlay 58 11 nil nil t)
+    (make-overlay 19 25 nil t nil)
+    (make-overlay 13 8 nil nil nil)
+    (make-overlay 19 8 nil nil t)
+    (make-overlay 39 5 nil t t)
+    (goto-char 51)
+    (delete-char 5)
+    (goto-char 16)
+    (delete-char 9)
+    (goto-char 18)
+    (insert "")
+    (goto-char 47)
+    (delete-char 4)
+    (goto-char 24)
+    (insert ".........")
+    (goto-char 24)
+    (insert ".....")
+    (goto-char 18)
+    (insert "...........")
+    (goto-char 5)
+    (delete-char 6)
+    (goto-char 30)
+    (insert "...........")
+    (goto-char 8)
+    (insert ".............")
+    (goto-char 78)
+    (insert "............")
+    (goto-char 67)
+    (insert "")
+    (goto-char 58)
+    (insert "")
+    (goto-char 5)
+    (insert ".")
+    (goto-char 79)
+    (widen)
+    (narrow-to-region 51 55)
+    (goto-char 51)
+    (insert "....")
+    (goto-char 58)
+    (widen)
+    (narrow-to-region 36 37)
+    (goto-char 37)
+    (insert "....")
+    (goto-char 40)
+    (insert ".......")
+    (goto-char 47)
+    (delete-char 1)
+    (goto-char 43)
+    (delete-char 4)
+    (goto-char 37)
+    (insert "........")
+    (goto-char 49)
+    (insert "............")
+    (goto-char 42)
+    (widen)
+    (narrow-to-region 75 111)
+    (goto-char 104)
+    (widen)
+    (narrow-to-region 21 95)
+    (goto-char 22)
+    (widen)
+    (narrow-to-region 64 79)
+    (goto-char 64)
+    (delete-char 0)
+    (goto-char 68)
+    (insert "........")
+    (goto-char 82)
+    (insert "")
+    (goto-char 81)
+    (insert "........")
+    (goto-char 92)
+    (delete-char 2)
+    (goto-char 87)
+    (insert ".")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((5 . 145)
+        (5 . 148)
+        (6 . 118)
+        (6 . 119)
+        (6 . 119)
+        (6 . 143)
+        (6 . 143)
+        (24 . 114)
+        (24 . 116)
+        (63 . 117))))))
+
+(ert-deftest overlay-autogenerated-test-63 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 9 49 nil t nil)
+    (make-overlay 9 16 nil nil nil)
+    (make-overlay 64 2 nil t t)
+    (make-overlay 17 31 nil nil t)
+    (make-overlay 24 51 nil nil nil)
+    (make-overlay 27 56 nil t t)
+    (make-overlay 21 4 nil nil nil)
+    (make-overlay 24 29 nil t t)
+    (make-overlay 4 63 nil nil t)
+    (make-overlay 34 49 nil t nil)
+    (make-overlay 19 47 nil nil t)
+    (make-overlay 8 50 nil t nil)
+    (make-overlay 49 61 nil t nil)
+    (make-overlay 52 10 nil t t)
+    (make-overlay 64 30 nil t nil)
+    (make-overlay 5 13 nil t nil)
+    (goto-char 27)
+    (insert "........")
+    (goto-char 42)
+    (insert "......")
+    (goto-char 48)
+    (insert "....")
+    (goto-char 55)
+    (widen)
+    (narrow-to-region 10 5)
+    (goto-char 8)
+    (insert ".............")
+    (goto-char 19)
+    (insert "......")
+    (goto-char 19)
+    (delete-char 3)
+    (goto-char 8)
+    (delete-char 3)
+    (goto-char 9)
+    (insert ".......")
+    (goto-char 29)
+    (insert "...............")
+    (goto-char 38)
+    (insert ".......")
+    (goto-char 34)
+    (insert "......")
+    (goto-char 28)
+    (delete-char 20)
+    (goto-char 22)
+    (insert "............")
+    (goto-char 21)
+    (delete-char 23)
+    (goto-char 25)
+    (delete-char 2)
+    (goto-char 19)
+    (delete-char 2)
+    (goto-char 12)
+    (delete-char 6)
+    (goto-char 12)
+    (delete-char 0)
+    (goto-char 13)
+    (delete-char 0)
+    (goto-char 12)
+    (insert "........")
+    (goto-char 23)
+    (delete-char 2)
+    (goto-char 5)
+    (insert "...............")
+    (goto-char 28)
+    (delete-char 0)
+    (goto-char 16)
+    (insert "..........")
+    (goto-char 8)
+    (delete-char 17)
+    (goto-char 27)
+    (delete-char 0)
+    (goto-char 12)
+    (insert ".")
+    (goto-char 14)
+    (delete-char 12)
+    (goto-char 11)
+    (insert "..............")
+    (goto-char 34)
+    (insert "")
+    (goto-char 25)
+    (delete-char 8)
+    (should
+     (equal
+      (test-overlay-regions)
+      '((2 . 98)
+        (4 . 37)
+        (4 . 97)
+        (25 . 29)
+        (25 . 32)
+        (25 . 84))))))
+
+(ert-deftest overlay-autogenerated-test-64 nil
+  (with-temp-buffer
+    (insert "................................................................")
+    (make-overlay 31 10 nil nil nil)
+    (make-overlay 17 58 nil nil t)
+    (make-overlay 20 21 nil t nil)
+    (make-overlay 3 47 nil t t)
+    (make-overlay 47 43 nil t t)
+    (make-overlay 54 8 nil nil t)
+    (make-overlay 51 26 nil t nil)
+    (make-overlay 60 14 nil t nil)
+    (make-overlay 38 6 nil nil t)
+    (make-overlay 41 9 nil nil nil)
+    (make-overlay 44 38 nil nil t)
+    (make-overlay 55 48 nil nil t)
+    (make-overlay 10 41 nil nil t)
+    (make-overlay 35 49 nil t nil)
+    (make-overlay 50 46 nil nil nil)
+    (make-overlay 28 28 nil t nil)
+    (goto-char 59)
+    (delete-char 3)
+    (goto-char 28)
+    (widen)
+    (narrow-to-region 13 7)
+    (goto-char 11)
+    (insert ".")
+    (goto-char 9)
+    (delete-char 3)
+    (goto-char 8)
+    (delete-char 0)
+    (goto-char 7)
+    (insert ".............")
+    (goto-char 9)
+    (insert "..........")
+    (goto-char 22)
+    (delete-char 1)
+    (goto-char 31)
+    (delete-char 2)
+    (goto-char 22)
+    (insert ".........")
+    (goto-char 33)
+    (delete-char 1)
+    (goto-char 29)
+    (widen)
+    (narrow-to-region 59 51)
+    (goto-char 52)
+    (insert ".........")
+    (goto-char 53)
+    (insert "........")
+    (goto-char 53)
+    (delete-char 4)
+    (goto-char 54)
+    (insert "........")
+    (goto-char 53)
+    (insert "....")
+    (goto-char 75)
+    (widen)
+    (goto-char 70)
+    (delete-char 2)
+    (goto-char 108)
+    (delete-char 1)
+    (goto-char 80)
+    (widen)
+    (goto-char 70)
+    (widen)
+    (narrow-to-region 49 63)
+    (goto-char 49)
+    (insert "...")
+    (goto-char 66)
+    (delete-char 0)
+    (goto-char 63)
+    (delete-char 3)
+    (goto-char 59)
+    (insert "..........")
+    (goto-char 56)
+    (delete-char 6)
+    (goto-char 60)
+    (insert ".........")
+    (goto-char 62)
+    (widen)
+    (goto-char 58)
+    (insert ".............")
+    (goto-char 105)
+    (widen)
+    (narrow-to-region 94 109)
+    (goto-char 103)
+    (insert "............")
+    (should
+     (equal
+      (test-overlay-regions)
+      '((3 . 134)
+        (6 . 125)
+        (38 . 141)
+        (39 . 118)
+        (39 . 128)
+        (39 . 128)
+        (40 . 146)
+        (43 . 145)
+        (101 . 138)
+        (103 . 103))))))
+
+) ;; End of `when nil' for autogenerated insert/delete/narrow tests.
+
+(ert-deftest buffer-multibyte-overlong-sequences ()
+  (dolist (uni '("\xE0\x80\x80"
+                 "\xF0\x80\x80\x80"
+                 "\xF8\x8F\xBF\xBF\x80"))
+    (let ((multi (string-to-multibyte uni)))
+      (should
+       (string-equal
+        multi
+        (with-temp-buffer
+          (set-buffer-multibyte nil)
+          (insert uni)
+          (set-buffer-multibyte t)
+          (buffer-string)))))))
+
+;; https://debbugs.gnu.org/33492
+(ert-deftest buffer-tests-buffer-local-variables-undo ()
+  "Test that `buffer-undo-list' appears in `buffer-local-variables'."
+  (with-temp-buffer
+    (should (assq 'buffer-undo-list (buffer-local-variables)))))
+
+(ert-deftest buffer-tests-inhibit-buffer-hooks ()
+  "Test `get-buffer-create' argument INHIBIT-BUFFER-HOOKS."
+  (let* (run-bluh (bluh (lambda () (setq run-bluh t))))
+    (unwind-protect
+        (let* ( run-kbh  (kbh  (lambda () (setq run-kbh  t)))
+                run-kbqf (kbqf (lambda () (setq run-kbqf t))) )
+
+          ;; Inhibited.
+          (add-hook 'buffer-list-update-hook bluh)
+          (with-current-buffer (generate-new-buffer " foo" t)
+            (add-hook 'kill-buffer-hook kbh nil t)
+            (add-hook 'kill-buffer-query-functions kbqf nil t)
+            (kill-buffer))
+          (with-temp-buffer (ignore))
+          (with-output-to-string (ignore))
+          (should-not run-bluh)
+          (should-not run-kbh)
+          (should-not run-kbqf)
+
+          ;; Not inhibited.
+          (with-current-buffer (generate-new-buffer " foo")
+            (should run-bluh)
+            (add-hook 'kill-buffer-hook kbh nil t)
+            (add-hook 'kill-buffer-query-functions kbqf nil t)
+            (kill-buffer))
+          (should run-kbh)
+          (should run-kbqf))
+      (remove-hook 'buffer-list-update-hook bluh))))
+
+(ert-deftest buffer-tests-inhibit-buffer-hooks-indirect ()
+  "Indirect buffers do not call `get-buffer-create'."
+  (dolist (inhibit '(nil t))
+    (let ((base (get-buffer-create "foo" inhibit)))
+      (unwind-protect
+          (dotimes (_i 11)
+            (let* (flag*
+                   (flag (lambda () (prog1 t (setq flag* t))))
+                   (indirect (make-indirect-buffer base "foo[indirect]" nil
+                                                   inhibit)))
+              (unwind-protect
+                  (progn
+                    (with-current-buffer indirect
+                      (add-hook 'kill-buffer-query-functions flag nil t))
+                    (kill-buffer indirect)
+                    (if inhibit
+                        (should-not flag*)
+                      (should flag*)))
+                (let (kill-buffer-query-functions)
+                  (when (buffer-live-p indirect)
+                    (kill-buffer indirect))))))
+        (let (kill-buffer-query-functions)
+          (when (buffer-live-p base)
+            (kill-buffer base)))))))
+
+(ert-deftest zero-length-overlays-and-not ()
+  (with-temp-buffer
+    (insert "hello")
+    (let ((long-overlay (make-overlay 2 4))
+          (zero-overlay (make-overlay 3 3)))
+      ;; Exclude.
+      (should (= (length (overlays-at 3)) 1))
+      (should (eq (car (overlays-at 3)) long-overlay))
+      ;; Include.
+      (should (= (length (overlays-in 3 3)) 2))
+      (should (memq long-overlay (overlays-in 3 3)))
+      (should (memq zero-overlay (overlays-in 3 3))))))
+
+(ert-deftest test-remove-overlays ()
+  (with-temp-buffer
+    (insert "foo")
+    (make-overlay (point) (point))
+    (should (= (length (overlays-in (point-min) (point-max))) 1))
+    (remove-overlays)
+    (should (= (length (overlays-in (point-min) (point-max))) 0)))
+
+  (with-temp-buffer
+    (insert "foo")
+    (goto-char 2)
+    (make-overlay (point) (point))
+    ;; We only count zero-length overlays at the end of the buffer.
+    (should (= (length (overlays-in 1 2)) 0))
+    (narrow-to-region 1 2)
+    ;; We've now narrowed, so the zero-length overlay is at the end of
+    ;; the (accessible part of the) buffer.
+    (should (= (length (overlays-in 1 2)) 1))
+    (remove-overlays)
+    (should (= (length (overlays-in (point-min) (point-max))) 0))))
+
+(defun test-kill-buffer-auto-save (auto-save-answer body-func)
+  "Test helper for `kill-buffer-delete-auto-save' tests.
+
+Call BODY-FUNC with the current buffer set to a buffer visiting a
+temporary file.  Around the call, mock the \"Buffer modified;
+kill anyway?\" and \"Delete auto-save file?\" prompts, answering
+\"yes\" for the former and AUTO-SAVE-ANSWER for the latter.  The
+expectation should be the characters `?y' or `?n', or `nil' if no
+prompt is expected.  The test fails if the \"Delete auto-save
+file?\" prompt does not either prompt is not issued as expected.
+Finally, kill the buffer and its temporary file."
+  (ert-with-temp-file file
+    (should (file-exists-p file))
+    (save-excursion
+      (find-file file)
+      (should (equal file (buffer-file-name)))
+      (let ((buffer (current-buffer))
+            (auto-save-prompt-happened nil))
+        (cl-letf (((symbol-function #'read-multiple-choice)
+                   (lambda (prompt choices &rest _)
+                     (should (string-search "modified; kill anyway?" prompt))
+                     (let ((answer (assq ?y choices)))
+                       (should answer)
+                       answer)))
+                  ((symbol-function #'yes-or-no-p)
+                   (lambda (prompt)
+                     (should (string-search "Delete auto-save file?" prompt))
+                     (setq auto-save-prompt-happened t)
+                     (pcase-exhaustive auto-save-answer
+                       (?y t)
+                       (?n nil)))))
+          (funcall body-func)
+          (should (equal (null auto-save-prompt-happened)
+                         (null auto-save-answer))))
+        (when (buffer-live-p buffer)
+          (with-current-buffer buffer
+            (set-buffer-modified-p nil)
+            (kill-buffer)))))))
+
+(ert-deftest test-kill-buffer-auto-save-default ()
+  (let ((kill-buffer-delete-auto-save-files nil))
+    (test-kill-buffer-auto-save
+     nil
+     (lambda ()
+       (let (auto-save)
+         (auto-save-mode t)
+         (insert "foo\n")
+         (should buffer-auto-save-file-name)
+         (setq auto-save buffer-auto-save-file-name)
+         (do-auto-save)
+         (should (file-exists-p auto-save))
+         (kill-buffer (current-buffer))
+         (should (file-exists-p auto-save)))))))
+
+(ert-deftest test-kill-buffer-auto-save-delete-yes ()
+  (let ((kill-buffer-delete-auto-save-files t))
+    (test-kill-buffer-auto-save
+     ?y
+     (lambda ()
+       (let (auto-save)
+         (auto-save-mode t)
+         (insert "foo\n")
+         (should buffer-auto-save-file-name)
+         (setq auto-save buffer-auto-save-file-name)
+         (do-auto-save)
+         (should (file-exists-p auto-save))
+         ;; This should delete the auto-save file.
+         (kill-buffer (current-buffer))
+         (should-not (file-exists-p auto-save)))))))
+
+(ert-deftest test-kill-buffer-auto-save-delete-no ()
+  (let ((kill-buffer-delete-auto-save-files t))
+    (test-kill-buffer-auto-save
+     ?n
+     (lambda ()
+       (let (auto-save)
+         (auto-save-mode t)
+         (insert "foo\n")
+         (should buffer-auto-save-file-name)
+         (setq auto-save buffer-auto-save-file-name)
+         (do-auto-save)
+         (should (file-exists-p auto-save))
+         ;; This should not delete the auto-save file.
+         (kill-buffer (current-buffer))
+         (should (file-exists-p auto-save))
+         (delete-file auto-save))))))
 
 (ert-deftest test-buffer-modifications ()
   (ert-with-temp-file file
diff --git a/test/src/comp-tests.el b/test/src/comp-tests.el
index 1edbd1777c..734b4a0d22 100644
--- a/test/src/comp-tests.el
+++ b/test/src/comp-tests.el
@@ -823,7 +823,7 @@ Return a list of results."
     (should (= (comp-tests-tco-f 1 0 10) 55))))
 
 (defun comp-tests-fw-prop-checker-1 (_)
-  "Check that inside `comp-tests-fw-prop-f' `concat' and `length' are folded."
+  "Check that inside `comp-tests-fw-prop-1-f' `concat' and `length' are 
folded."
   (should
    (cl-notany
     #'identity
diff --git a/test/src/emacs-module-tests.el b/test/src/emacs-module-tests.el
index 1099fd0467..a9a45d5463 100644
--- a/test/src/emacs-module-tests.el
+++ b/test/src/emacs-module-tests.el
@@ -263,7 +263,7 @@ must evaluate to a regular expression string."
 
 (ert-deftest module--test-assertions--load-non-live-object-with-global-copy ()
   "Check that -module-assertions verify that non-live objects aren't accessed.
-This differs from `module--test-assertions-load-non-live-object'
+This differs from `module--test-assertions--load-non-live-object'
 in that it stows away a global reference.  The module assertions
 should nevertheless detect the invalid load."
   :tags (if (getenv "EMACS_EMBA_CI") '(:unstable))
diff --git a/test/src/fns-tests.el b/test/src/fns-tests.el
index 5d5d497c99..7568d941d0 100644
--- a/test/src/fns-tests.el
+++ b/test/src/fns-tests.el
@@ -22,6 +22,7 @@
 ;;; Code:
 
 (require 'cl-lib)
+(require 'ert)
 
 (ert-deftest fns-tests-identity ()
   (let ((num 12345)) (should (eq (identity num) num)))
@@ -29,9 +30,22 @@
   (let ((lst '(11))) (should (eq (identity lst) lst))))
 
 (ert-deftest fns-tests-random ()
-  (should (integerp (random)))
-  (should (>= (random 10) 0))
-  (should (< (random 10) 10)))
+  (unwind-protect
+      (progn
+        (should-error (random -1) :type 'args-out-of-range)
+        (should-error (random 0) :type 'args-out-of-range)
+        (should (integerp (random)))
+        (should (= (random 1) 0))
+        (should (>= (random 10) 0))
+        (should (< (random 10) 10))
+        (should (equal (random "seed") (random "seed")))
+        ;; The probability of four calls being the same is low.
+        ;; This makes sure that the value isn't constant.
+        (should (not (= (random t) (random t) (random t) (random t))))
+        ;; Handle bignums.
+        (should (integerp (random (1+ most-positive-fixnum)))))
+    ;; Reset the PRNG seed after testing.
+    (random t)))
 
 (ert-deftest fns-tests-length ()
   (should (= (length nil) 0))
@@ -843,6 +857,14 @@
   (should-error (reverse (dot1 1)) :type 'wrong-type-argument)
   (should-error (reverse (dot2 1 2)) :type 'wrong-type-argument))
 
+(ert-deftest test-cycle-equal ()
+  (should-error (equal (cyc1 1) (cyc1 1)))
+  (should-error (equal (cyc2 1 2) (cyc2 1 2))))
+
+(ert-deftest test-cycle-nconc ()
+  (should-error (nconc (cyc1 1) 'tail) :type 'circular-list)
+  (should-error (nconc (cyc2 1 2) 'tail) :type 'circular-list))
+
 (ert-deftest test-cycle-plist-get ()
   (let ((c1 (cyc1 1))
         (c2 (cyc2 1 2))
@@ -897,30 +919,47 @@
     (should-error (plist-put d1 3 3) :type 'wrong-type-argument)
     (should-error (plist-put d2 3 3) :type 'wrong-type-argument)))
 
-(ert-deftest test-cycle-equal ()
-  (should-error (equal (cyc1 1) (cyc1 1)))
-  (should-error (equal (cyc2 1 2) (cyc2 1 2))))
-
-(ert-deftest test-cycle-nconc ()
-  (should-error (nconc (cyc1 1) 'tail) :type 'circular-list)
-  (should-error (nconc (cyc2 1 2) 'tail) :type 'circular-list))
-
 (ert-deftest plist-get/odd-number-of-elements ()
   "Test that `plist-get' doesn't signal an error on degenerate plists."
   (should-not (plist-get '(:foo 1 :bar) :bar)))
 
 (ert-deftest plist-put/odd-number-of-elements ()
-  "Check for https://debbugs.gnu.org/cgi/bugreport.cgi?bug=27726.";
-  (should (equal (should-error (plist-put '(:foo 1 :bar) :zot 2)
-                               :type 'wrong-type-argument)
+  "Check for bug#27726."
+  (should (equal (should-error (plist-put (list :foo 1 :bar) :zot 2))
                  '(wrong-type-argument plistp (:foo 1 :bar)))))
 
 (ert-deftest plist-member/improper-list ()
-  "Check for https://debbugs.gnu.org/cgi/bugreport.cgi?bug=27726.";
-  (should (equal (should-error (plist-member '(:foo 1 . :bar) :qux)
-                               :type 'wrong-type-argument)
+  "Check for bug#27726."
+  (should (equal (should-error (plist-member '(:foo 1 . :bar) :qux))
                  '(wrong-type-argument plistp (:foo 1 . :bar)))))
 
+(ert-deftest test-plist ()
+  (let ((plist (list :a "b")))
+    (setq plist (plist-put plist :b "c"))
+    (should (equal (plist-get plist :b) "c"))
+    (should (equal (plist-member plist :b) '(:b "c"))))
+
+  (let ((plist (list "1" "2" "a" "b")))
+    (setq plist (plist-put plist (string ?a) "c"))
+    (should (equal plist '("1" "2" "a" "b" "a" "c")))
+    (should-not (plist-get plist (string ?a)))
+    (should-not (plist-member plist (string ?a))))
+
+  (let ((plist (list "1" "2" "a" "b")))
+    (setq plist (plist-put plist (string ?a) "c" #'equal))
+    (should (equal plist '("1" "2" "a" "c")))
+    (should (equal (plist-get plist (string ?a) #'equal) "c"))
+    (should (equal (plist-member plist (string ?a) #'equal) '("a" "c"))))
+
+  (let ((plist (list :a 1 :b 2 :c 3)))
+    (setq plist (plist-put plist ":a" 4 #'string>))
+    (should (equal plist '(:a 1 :b 4 :c 3)))
+    (should (equal (plist-get plist ":b" #'string>) 3))
+    (should (equal (plist-member plist ":c" #'string<) plist))
+    (dolist (fn '(plist-get plist-member))
+      (should-not (funcall fn plist ":a" #'string<))
+      (should-not (funcall fn plist ":c" #'string>)))))
+
 (ert-deftest test-string-distance ()
   "Test `string-distance' behavior."
   ;; ASCII characters are always fine
@@ -1336,23 +1375,6 @@
     (should-error (append loop '(end))
                   :type 'circular-list)))
 
-(ert-deftest test-plist ()
-  (let ((plist '(:a "b")))
-    (setq plist (plist-put plist :b "c"))
-    (should (equal (plist-get plist :b) "c"))
-    (should (equal (plist-member plist :b) '(:b "c"))))
-
-  (let ((plist '("1" "2" "a" "b")))
-    (setq plist (plist-put plist (copy-sequence "a") "c"))
-    (should-not (equal (plist-get plist (copy-sequence "a")) "c"))
-    (should-not (equal (plist-member plist (copy-sequence "a")) '("a" "c"))))
-
-  (let ((plist '("1" "2" "a" "b")))
-    (setq plist (plist-put plist (copy-sequence "a") "c" #'equal))
-    (should (equal (plist-get plist (copy-sequence "a") #'equal) "c"))
-    (should (equal (plist-member plist (copy-sequence "a") #'equal)
-                   '("a" "c")))))
-
 (ert-deftest fns--string-to-unibyte-multibyte ()
   (dolist (str (list "" "a" "abc" "a\x00\x7fz" "a\xaa\xbbz" "\x80\xdd\xff"
                      (apply #'unibyte-string (number-sequence 0 255))))
diff --git a/test/src/lcms-tests.el b/test/src/lcms-tests.el
index 1829a7ea1f..7f0f660d13 100644
--- a/test/src/lcms-tests.el
+++ b/test/src/lcms-tests.el
@@ -28,7 +28,7 @@
 ;; https://github.com/njsmith/colorspacious
 
 ;; Other references:
-;; 
http://www.babelcolor.com/index_htm_files/A%20review%20of%20RGB%20color%20spaces.pdf
+;; 
https://www.babelcolor.com/index_htm_files/A%20review%20of%20RGB%20color%20spaces.pdf
 
 ;;; Code:
 
diff --git a/test/src/regex-emacs-tests.el b/test/src/regex-emacs-tests.el
index ff0d6be3f5..b323f592dc 100644
--- a/test/src/regex-emacs-tests.el
+++ b/test/src/regex-emacs-tests.el
@@ -867,4 +867,9 @@ This evaluates the TESTS test cases from glibc."
     (should (equal (string-match "[[:lower:]]" "ẞ") 0))
     (should (equal (string-match "[[:upper:]]" "ẞ") 0))))
 
+(ert-deftest regexp-atomic-failure ()
+  "Bug#58726."
+  (should (equal (string-match "\\`\\(?:ab\\)*\\'" "a") nil))
+  (should (equal (string-match "\\`a\\{2\\}*\\'" "a") nil)))
+
 ;;; regex-emacs-tests.el ends here



reply via email to

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