emacs-diffs
[Top][All Lists]
Advanced

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

feature/pkg e148d8c49e: Merge remote-tracking branch 'origin/master' int


From: Gerd Moellmann
Subject: feature/pkg e148d8c49e: Merge remote-tracking branch 'origin/master' into feature/pkg
Date: Tue, 22 Nov 2022 03:24:28 -0500 (EST)

branch: feature/pkg
commit e148d8c49e4e554f536fb022962247eb5397e9b8
Merge: 545cf39307 b32f50c6d8
Author: Gerd Möllmann <gerd@gnu.org>
Commit: Gerd Möllmann <gerd@gnu.org>

    Merge remote-tracking branch 'origin/master' into feature/pkg
---
 .clang-format                                      |   16 +-
 .dir-locals.el                                     |    3 +-
 .gitignore                                         |    1 +
 ChangeLog.2                                        |    4 +-
 ChangeLog.3                                        |  206 +-
 Makefile.in                                        |   10 +-
 admin/cus-test.el                                  |    2 +-
 admin/grammars/srecode-template.wy                 |    2 +-
 admin/last-chance.el                               |    2 +-
 admin/notes/bug-triage                             |    2 +-
 admin/notes/tree-sitter/build-module/README        |   17 +
 admin/notes/tree-sitter/build-module/batch.sh      |   20 +
 admin/notes/tree-sitter/build-module/build.sh      |   62 +
 .../tree-sitter/html-manual/Accessing-Node.html    |  206 ++
 .../html-manual/Language-Definitions.html          |  402 +++
 .../html-manual/Multiple-Languages.html            |  328 +++
 .../html-manual/Parser_002dbased-Font-Lock.html    |  248 ++
 .../html-manual/Parser_002dbased-Indentation.html  |  281 ++
 .../html-manual/Parsing-Program-Source.html        |  126 +
 .../tree-sitter/html-manual/Pattern-Matching.html  |  451 +++
 .../tree-sitter/html-manual/Retrieving-Node.html   |  421 +++
 .../html-manual/Tree_002dsitter-C-API.html         |  212 ++
 .../tree-sitter/html-manual/Using-Parser.html      |  231 ++
 .../notes/tree-sitter/html-manual/build-manual.sh  |   23 +
 admin/notes/tree-sitter/html-manual/manual.css     |  374 +++
 admin/notes/tree-sitter/starter-guide              |  455 +++
 configure.ac                                       |   83 +-
 doc/emacs/ChangeLog.1                              |    2 +-
 doc/emacs/custom.texi                              |    6 +-
 doc/emacs/dired.texi                               |    2 +-
 doc/emacs/maintaining.texi                         |    7 +
 doc/emacs/mark.texi                                |   17 +-
 doc/emacs/package.texi                             |    8 +-
 doc/lispref/Makefile.in                            |    1 +
 doc/lispref/display.texi                           |    2 +-
 doc/lispref/elisp.texi                             |   15 +
 doc/lispref/errors.texi                            |    2 +-
 doc/lispref/functions.texi                         |    2 +-
 doc/lispref/internals.texi                         |   20 +-
 doc/lispref/minibuf.texi                           |    2 +-
 doc/lispref/modes.texi                             |  459 ++-
 doc/lispref/parsing.texi                           | 1886 ++++++++++++
 doc/lispref/positions.texi                         |   16 +
 doc/misc/ChangeLog.1                               |   13 +-
 doc/misc/Makefile.in                               |   14 +-
 doc/misc/auth.texi                                 |   18 +
 doc/misc/cc-mode.texi                              |    2 +-
 doc/misc/ede.texi                                  |    4 +-
 doc/misc/edt.texi                                  |   12 +-
 doc/misc/efaq-w32.texi                             |    6 +-
 doc/misc/eglot.texi                                |    6 +-
 doc/misc/emacs-gnutls.texi                         |    2 +-
 doc/misc/erc.texi                                  |   31 +-
 doc/misc/eshell.texi                               |    2 +-
 doc/misc/eudc.texi                                 |    2 +-
 doc/misc/flymake.texi                              |    6 +-
 doc/misc/gnus-faq.texi                             |  293 +-
 doc/misc/gnus.texi                                 |  153 +-
 doc/misc/idlwave.texi                              |    2 +-
 doc/misc/mairix-el.texi                            |    2 +-
 doc/misc/message.texi                              |    4 +-
 doc/misc/modus-themes.org                          |    2 +-
 doc/misc/org.org                                   |    2 +-
 doc/misc/reftex.texi                               |    2 +-
 doc/misc/ses.texi                                  |    4 +-
 doc/misc/srecode.texi                              |    2 +-
 doc/misc/tramp.texi                                |    7 +-
 doc/misc/vip.texi                                  |    4 +-
 etc/ERC-NEWS                                       |    7 +
 etc/NEWS                                           |  109 +-
 etc/NEWS.24                                        |    2 +-
 etc/NEWS.28                                        |    2 +-
 etc/NEXTSTEP                                       |    2 +-
 etc/ORG-NEWS                                       |    8 +-
 etc/PROBLEMS                                       |   56 +-
 etc/themes/dichromacy-theme.el                     |   38 +-
 lib-src/ChangeLog.1                                |    2 +-
 lib-src/ebrowse.c                                  |   63 +-
 lib-src/emacsclient.c                              |    2 +-
 lisp/ChangeLog.10                                  |    4 +-
 lisp/ChangeLog.12                                  |    8 +-
 lisp/ChangeLog.13                                  |    6 +-
 lisp/ChangeLog.14                                  |    2 +-
 lisp/ChangeLog.15                                  |    2 +-
 lisp/ChangeLog.16                                  |   21 +-
 lisp/ChangeLog.3                                   |    3 +-
 lisp/ChangeLog.4                                   |    2 +-
 lisp/ChangeLog.6                                   |    2 +-
 lisp/ChangeLog.7                                   |    2 +-
 lisp/ChangeLog.8                                   |    4 +-
 lisp/ChangeLog.9                                   |    4 +-
 lisp/allout.el                                     |    3 -
 lisp/apropos.el                                    |    8 +-
 lisp/auth-source-pass.el                           |  112 +-
 lisp/bookmark.el                                   |   35 +-
 lisp/bs.el                                         |   38 +-
 lisp/buff-menu.el                                  |   54 +-
 lisp/calc/calc-graph.el                            |    2 +-
 lisp/calendar/diary-lib.el                         |    2 +-
 lisp/cedet/ChangeLog.1                             |   11 +-
 lisp/cedet/ede/makefile-edit.el                    |    2 +-
 lisp/cedet/ede/proj.el                             |    6 +-
 lisp/cedet/ede/project-am.el                       |    4 +-
 lisp/cedet/semantic.el                             |   10 +-
 lisp/cedet/semantic/analyze/fcn.el                 |    4 +-
 lisp/cedet/semantic/bovine/c.el                    |    4 +-
 lisp/cedet/semantic/complete.el                    |    2 +-
 lisp/cedet/semantic/db-ebrowse.el                  |    2 +-
 lisp/cedet/semantic/db-find.el                     |    2 +-
 lisp/cedet/semantic/decorate/include.el            |    2 +-
 lisp/cedet/semantic/edit.el                        |    2 +-
 lisp/cedet/semantic/grm-wy-boot.el                 |    4 +-
 lisp/cedet/semantic/idle.el                        |    2 +-
 lisp/cedet/semantic/scope.el                       |    4 +-
 lisp/cedet/semantic/symref/list.el                 |    5 +-
 lisp/cedet/semantic/tag.el                         |    2 +-
 lisp/cedet/semantic/util-modes.el                  |    2 +-
 lisp/cedet/srecode/document.el                     |    3 +-
 lisp/cedet/srecode/extract.el                      |    2 +-
 lisp/cedet/srecode/semantic.el                     |    4 +-
 lisp/comint.el                                     |    8 +
 lisp/cus-theme.el                                  |   16 +-
 lisp/dired.el                                      |    2 +-
 lisp/dnd.el                                        |    4 +-
 lisp/dynamic-setting.el                            |   18 +-
 lisp/emacs-lisp/bytecomp.el                        |    9 +-
 lisp/emacs-lisp/checkdoc.el                        |    4 +-
 lisp/emacs-lisp/cl-preloaded.el                    |    3 +
 lisp/emacs-lisp/comp.el                            |    4 +-
 lisp/emacs-lisp/ert-x.el                           |    1 +
 lisp/emacs-lisp/ert.el                             |    2 +-
 lisp/emacs-lisp/hierarchy.el                       |    2 +-
 lisp/emacs-lisp/lisp.el                            |   27 +-
 lisp/emacs-lisp/package-vc.el                      |  409 +--
 lisp/emacs-lisp/package.el                         |   58 +-
 lisp/emacs-lisp/rmc.el                             |    2 +-
 lisp/emacs-lisp/seq.el                             |  126 +-
 lisp/emacs-lisp/shortdoc.el                        |   26 +-
 lisp/emacs-lisp/subr-x.el                          |    4 +
 lisp/emacs-lisp/tcover-ses.el                      |    2 +-
 lisp/erc/ChangeLog.1                               |    5 +-
 lisp/erc/erc-backend.el                            |  140 +-
 lisp/erc/erc-common.el                             |    3 +
 lisp/erc/erc-compat.el                             |  145 +
 lisp/erc/erc-networks.el                           |    9 +-
 lisp/erc/erc-pcomplete.el                          |    4 +
 lisp/erc/erc.el                                    |  241 +-
 lisp/eshell/em-prompt.el                           |    8 +
 lisp/eshell/esh-mode.el                            |    8 +
 lisp/eshell/esh-proc.el                            |    2 +-
 lisp/face-remap.el                                 |   27 +-
 lisp/faces.el                                      |   23 +-
 lisp/files.el                                      |    2 +-
 lisp/filesets.el                                   |    4 +-
 lisp/font-lock.el                                  |   67 +-
 lisp/forms.el                                      |   28 +-
 lisp/gnus/ChangeLog.3                              |    6 +-
 lisp/gnus/gnus-group.el                            |    8 +-
 lisp/gnus/gnus-search.el                           |    2 +-
 lisp/gnus/gnus-start.el                            |    2 +-
 lisp/gnus/message.el                               |    4 +-
 lisp/gnus/mml.el                                   |    2 +-
 lisp/gnus/nnrss.el                                 |    6 +-
 lisp/gnus/nnvirtual.el                             |    2 +-
 lisp/help.el                                       |    5 +-
 lisp/htmlfontify.el                                |    2 +-
 lisp/info.el                                       |    2 +-
 lisp/international/titdic-cnv.el                   |    7 +
 lisp/jsonrpc.el                                    |    2 +-
 lisp/keymap.el                                     |   60 +-
 lisp/language/ethio-util.el                        |   92 +-
 lisp/language/ethiopic.el                          |    7 +
 lisp/language/ind-util.el                          |    7 +
 lisp/language/tibet-util.el                        |    7 +
 lisp/language/tibetan.el                           |    7 +
 lisp/ldefs-boot.el                                 |  156 +-
 lisp/leim/quail/ethiopic.el                        |    7 +
 lisp/leim/quail/indian.el                          |    2 +-
 lisp/leim/quail/japanese.el                        |    2 +-
 lisp/leim/quail/tibetan.el                         |    7 +
 lisp/mail/feedmail.el                              |    6 +-
 lisp/mail/ietf-drums-date.el                       |    4 +-
 lisp/mail/reporter.el                              |    2 +-
 lisp/mail/rfc6068.el                               |    4 +-
 lisp/mail/rmail.el                                 |    2 +-
 lisp/mail/rmailsum.el                              |  245 +-
 lisp/mail/supercite.el                             |    2 +-
 lisp/man.el                                        |   16 +-
 lisp/mh-e/ChangeLog.1                              |    2 +-
 lisp/mh-e/ChangeLog.2                              |    4 +-
 lisp/mh-e/mh-mime.el                               |    5 +-
 lisp/mh-e/mh-scan.el                               |    6 +-
 lisp/mpc.el                                        |    2 +-
 lisp/net/browse-url.el                             |   24 +
 lisp/net/eudc-capf.el                              |   11 +-
 lisp/net/eudc-vars.el                              |    3 +-
 lisp/net/eudcb-mailabbrev.el                       |    5 +-
 lisp/net/rcirc.el                                  |   20 +-
 lisp/net/tramp-archive.el                          |   24 +-
 lisp/net/tramp-compat.el                           |    2 +-
 lisp/net/tramp-container.el                        |    3 +
 lisp/net/tramp-crypt.el                            |    2 +-
 lisp/net/tramp.el                                  |   14 +-
 lisp/net/trampver.el                               |    2 +-
 lisp/nxml/nxml-mode.el                             |    2 +-
 lisp/obsolete/vi.el                                |    4 +-
 lisp/org/ChangeLog.1                               |   22 +-
 lisp/org/ob-R.el                                   |    2 +-
 lisp/org/ob-tangle.el                              |    4 +-
 lisp/org/ol.el                                     |    2 +-
 lisp/org/org-clock.el                              |    3 +-
 lisp/org/org-element.el                            |    2 +-
 lisp/org/org-faces.el                              |    2 +-
 lisp/org/org-id.el                                 |    2 +-
 lisp/org/org-protocol.el                           |    2 +-
 lisp/org/ox-koma-letter.el                         |    4 +-
 lisp/org/ox-odt.el                                 |    6 +-
 lisp/org/ox.el                                     |    2 +-
 lisp/outline.el                                    |  162 +-
 lisp/pcomplete.el                                  |    4 +-
 lisp/proced.el                                     |   26 +-
 lisp/progmodes/c-ts-mode.el                        |  553 ++++
 lisp/progmodes/cc-cmds.el                          |    2 +-
 lisp/progmodes/cc-engine.el                        |   53 +-
 lisp/progmodes/cc-langs.el                         |   10 +-
 lisp/progmodes/cc-mode.el                          |    2 +-
 lisp/progmodes/ebnf2ps.el                          |    6 +-
 lisp/progmodes/eglot.el                            |   54 +-
 lisp/progmodes/elisp-mode.el                       |   67 +-
 lisp/progmodes/flymake-cc.el                       |    2 +-
 lisp/progmodes/flymake.el                          |    2 +-
 lisp/progmodes/hideshow.el                         |    4 +-
 lisp/progmodes/idlw-shell.el                       |    2 +-
 lisp/progmodes/java-ts-mode.el                     |  321 ++
 lisp/progmodes/js.el                               |  372 ++-
 lisp/progmodes/json-ts-mode.el                     |  167 ++
 lisp/progmodes/prog-mode.el                        |    2 +-
 lisp/progmodes/project.el                          |   23 +
 lisp/progmodes/python.el                           |  455 ++-
 lisp/progmodes/sh-script.el                        |  147 +-
 lisp/progmodes/sql.el                              |    2 +-
 lisp/progmodes/ts-mode.el                          |  316 ++
 lisp/progmodes/verilog-mode.el                     |    2 +-
 lisp/progmodes/xref.el                             |    7 +-
 lisp/repeat.el                                     |   65 +-
 lisp/scroll-bar.el                                 |    2 +-
 lisp/server.el                                     |   17 +-
 lisp/shell.el                                      |    8 +
 lisp/simple.el                                     |    7 +-
 lisp/so-long.el                                    |    4 +-
 lisp/startup.el                                    |   37 +-
 lisp/subr.el                                       |    6 +-
 lisp/term/pgtk-win.el                              |    2 +-
 lisp/term/w32-win.el                               |    5 +-
 lisp/textmodes/css-mode.el                         |  177 +-
 lisp/textmodes/reftex-cite.el                      |    2 +-
 lisp/textmodes/reftex-index.el                     |    2 +-
 lisp/textmodes/tex-mode.el                         |    4 +-
 lisp/treesit.el                                    | 2259 ++++++++++++++
 lisp/url/ChangeLog.1                               |    2 +-
 lisp/url/url-irc.el                                |   32 +-
 lisp/vc/add-log.el                                 |    2 +-
 lisp/vc/diff-mode.el                               |    2 +-
 lisp/vc/ediff-diff.el                              |   35 +-
 lisp/vc/ediff-util.el                              |    2 +-
 lisp/vc/vc-dav.el                                  |    2 +-
 lisp/vc/vc-rcs.el                                  |    2 +-
 lisp/vc/vc.el                                      |   56 +-
 lisp/whitespace.el                                 |    2 +-
 lisp/window.el                                     |   41 +-
 lisp/woman.el                                      |   26 +-
 lisp/x-dnd.el                                      |    4 +-
 msdos/autogen/config.in                            |    2 +-
 msdos/sed1v2.inp                                   |    2 +
 nt/icons/README                                    |    2 +-
 src/ChangeLog.10                                   |    2 +-
 src/ChangeLog.11                                   |    6 +-
 src/ChangeLog.12                                   |    8 +-
 src/ChangeLog.13                                   |   12 +-
 src/ChangeLog.5                                    |    2 +-
 src/ChangeLog.6                                    |    2 +-
 src/ChangeLog.8                                    |    6 +-
 src/Makefile.in                                    |    9 +-
 src/alloc.c                                        |   27 +-
 src/buffer.c                                       |   45 +-
 src/buffer.h                                       |    4 +
 src/casefiddle.c                                   |   12 +
 src/comp.c                                         |    4 +-
 src/data.c                                         |    9 +
 src/dbusbind.c                                     |    4 +-
 src/dispextern.h                                   |    3 +-
 src/emacs.c                                        |   11 +-
 src/eval.c                                         |   18 +-
 src/font.h                                         |    6 +-
 src/fontset.c                                      |   12 +-
 src/frame.c                                        |  115 +-
 src/frame.h                                        |    1 +
 src/ftcrfont.c                                     |   38 +-
 src/ftfont.h                                       |    7 +
 src/haiku_support.h                                |    2 +-
 src/haikufns.c                                     |   17 +-
 src/haikuselect.c                                  |    2 +-
 src/haikuterm.c                                    |   15 +-
 src/insdel.c                                       |   47 +-
 src/itree.c                                        |  619 ++--
 src/itree.h                                        |   55 +-
 src/json.c                                         |   16 -
 src/keyboard.c                                     |   53 +-
 src/lisp.h                                         |   29 +-
 src/lread.c                                        |   23 +-
 src/nsfns.m                                        |   17 +-
 src/pgtkfns.c                                      |   24 +-
 src/pgtkterm.c                                     |  131 +-
 src/pgtkterm.h                                     |   11 +-
 src/print.c                                        |   43 +
 src/search.c                                       |   20 +-
 src/treesit.c                                      | 3076 ++++++++++++++++++++
 src/treesit.h                                      |  200 ++
 src/w32fns.c                                       |   25 +-
 src/w32inevt.c                                     |    2 +-
 src/xdisp.c                                        |   32 +-
 src/xfaces.c                                       |   20 +-
 src/xfns.c                                         |   42 +-
 src/xselect.c                                      |    2 +-
 src/xsettings.c                                    |   12 +-
 src/xterm.c                                        |  313 +-
 src/xterm.h                                        |   23 +-
 test/lisp/auth-source-pass-tests.el                |  275 +-
 test/lisp/cedet/srecode/fields-tests.el            |    2 +-
 .../emacs-lisp/package-resources/elpa-packages.eld |    3 +
 .../newer-versions/elpa-packages.eld               |    3 +
 .../package-resources/signed/elpa-packages.eld     |    3 +
 .../package-resources/signed/elpa-packages.eld.sig |  Bin 0 -> 119 bytes
 .../package-resources/signed/update-signatures.sh  |    1 +
 .../with-nil-entry/elpa-packages.eld               |    3 +
 test/lisp/emacs-lisp/syntax-tests.el               |    2 +-
 test/lisp/erc/erc-dcc-tests.el                     |    3 +-
 test/lisp/erc/erc-networks-tests.el                |   17 +
 .../erc-scenarios-base-compat-rename-bouncer.el    |    2 +-
 test/lisp/erc/erc-scenarios-base-reconnect.el      |   46 +
 test/lisp/erc/erc-scenarios-misc.el                |   28 +
 test/lisp/erc/erc-services-tests.el                |    3 -
 test/lisp/erc/erc-tests.el                         |  225 ++
 test/lisp/erc/resources/erc-d/erc-d-tests.el       |    1 +
 test/lisp/erc/resources/erc-scenarios-common.el    |    5 +-
 test/lisp/erc/resources/join/legacy/foonet.eld     |    2 +-
 test/lisp/net/browse-url-tests.el                  |    9 +
 test/lisp/net/dbus-tests.el                        |    6 +-
 test/lisp/net/eudc-resources/bbdb                  |    3 +
 test/lisp/net/eudc-resources/dc=gnu,dc=org.ldif    |   15 +
 .../dc=gnu,dc=org/cn=emacs-ert-test-1.ldif         |   17 +
 .../dc=gnu,dc=org/cn=emacs-ert-test-2.ldif         |   17 +
 test/lisp/net/eudc-resources/slapd.conf            |    7 +
 test/lisp/net/eudc-tests.el                        |   40 +
 test/lisp/net/mailcap-tests.el                     |    2 +-
 test/lisp/net/tramp-tests.el                       |    7 +-
 test/lisp/server-tests.el                          |   41 +
 test/lisp/simple-tests.el                          |   19 +-
 test/lisp/subr-tests.el                            |    2 +-
 test/lisp/vc/vc-tests.el                           |    2 +-
 test/manual/noverlay/overlay-perf.el               |    2 +-
 test/src/buffer-tests.el                           |   87 +-
 test/src/comp-resources/comp-test-funcs.el         |    8 +-
 test/src/comp-tests.el                             |    4 +-
 test/src/eval-tests.el                             |    2 +-
 test/src/font-tests.el                             |    2 +-
 test/src/thread-tests.el                           |    2 +-
 test/src/treesit-tests.el                          |  535 ++++
 368 files changed, 20167 insertions(+), 2324 deletions(-)

diff --git a/.clang-format b/.clang-format
index 464375bd41..2208240a66 100644
--- a/.clang-format
+++ b/.clang-format
@@ -1,15 +1,18 @@
 Language: Cpp
 BasedOnStyle: GNU
 AlignEscapedNewlinesLeft: true
+AlignOperands: Align
 AlwaysBreakAfterReturnType: TopLevelDefinitions
 BreakBeforeBinaryOperators: All
 BreakBeforeBraces: GNU
 ColumnLimit: 70
 ContinuationIndentWidth: 2
-ForEachMacros: [FOR_EACH_TAIL,
-                FOR_EACH_TAIL_SAFE,
-                FOR_EACH_LIVE_BUFFER,
-                ITREE_FOREACH]
+ForEachMacros:
+  - FOR_EACH_TAIL
+  - FOR_EACH_TAIL_SAFE
+  - FOR_EACH_LIVE_BUFFER
+  - ITREE_FOREACH
+  - FOR_EACH_ALIST_VALUE
 IncludeCategories:
   - Regex: '^<config\.h>$'
     Priority: -1
@@ -19,6 +22,11 @@ IncludeCategories:
     Priority: 2
   - Regex: '.*'
     Priority: 3
+WhitespaceSensitiveMacros:
+  - STR
+  - CALL1I
+  - CALL2I
+  - STR_VALUE
 KeepEmptyLinesAtTheStartOfBlocks: false
 MaxEmptyLinesToKeep: 1
 PenaltyBreakBeforeFirstCallParameter: 2000
diff --git a/.dir-locals.el b/.dir-locals.el
index a85769b534..f0ab46236f 100644
--- a/.dir-locals.el
+++ b/.dir-locals.el
@@ -7,7 +7,8 @@
         (emacs-lisp-docstring-fill-column . 65)
          (vc-git-annotate-switches . "-w")
          (bug-reference-url-format . "https://debbugs.gnu.org/%s";)
-        (diff-add-log-use-relative-names . t)))
+        (diff-add-log-use-relative-names . t)
+         (vc-prepare-patches-separately . nil)))
  (c-mode . ((c-file-style . "GNU")
             (c-noise-macro-names . ("INLINE" "NO_INLINE" 
"ATTRIBUTE_NO_SANITIZE_UNDEFINED"
                                     "UNINIT" "CALLBACK" "ALIGN_STACK"))
diff --git a/.gitignore b/.gitignore
index a7828d3386..e6310b644a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,6 +25,7 @@
 
 # Personal customization.
 .dir-locals-2.el
+.no-advice-on-failure
 
 # Built by 'autogen.sh'.
 /aclocal.m4
diff --git a/ChangeLog.2 b/ChangeLog.2
index fc038033ec..483dec2542 100644
--- a/ChangeLog.2
+++ b/ChangeLog.2
@@ -9317,7 +9317,7 @@
        optional.
 
        * src/buffer.c (Fbarf_if_buffer_read_only): Rename argument POS
-       to POSITION to keep consisteny with doc-string.
+       to POSITION to keep consistent with doc-string.
 
 2016-02-01  Paul Eggert  <eggert@cs.ucla.edu>
 
@@ -34464,7 +34464,7 @@
 
        * lisp/emacs-lisp/package.el: Make archive and status pseudo-keywords
        (package--has-keyword-p): Understand "arc:xxxx" and "status:xxxx"
-       as special keywords which match agains package archive and status
+       as special keywords which match against package archive and status
        respectively.
        * etc/NEWS: Document it.
 
diff --git a/ChangeLog.3 b/ChangeLog.3
index d90b261da7..d27a14d427 100644
--- a/ChangeLog.3
+++ b/ChangeLog.3
@@ -4043,7 +4043,7 @@
 
        * doc/emacs/maintaining.texi (Basic VC Editing): Mention Dired buffer.
 
-       * doc/emacs/text.texi (Outline Mode): Replace S-TAB with with S-<TAB>.
+       * doc/emacs/text.texi (Outline Mode): Replace S-TAB with S-<TAB>.
 
        * etc/NEWS: Add some missing +++/--- and move some related items closer.
 
@@ -5557,7 +5557,7 @@
        Fix ert errors when there's a test that binds `debug-on-error'
 
        * lisp/emacs-lisp/ert.el (ert--run-test-internal): Don't infloop
-       on errors when signalling errors (bug#51131).
+       on errors when signaling errors (bug#51131).
 
 2021-10-10  Paul Eggert  <eggert@cs.ucla.edu>
 
@@ -12670,7 +12670,7 @@
 
 2021-09-03  Lars Ingebrigtsen  <larsi@gnus.org>
 
-       Fix `describe-function' for autoloaded adviced functions
+       Fix `describe-function' for autoloaded advised functions
 
        * lisp/emacs-lisp/nadvice.el (advice--make-single-doc): Factor
        out.
@@ -20026,7 +20026,7 @@
 
        Add query command removed in 4ff1f66b12
 
-       * lisp/net/rcirc.el (query): Readd accidentally removed command
+       * lisp/net/rcirc.el (query): Re-add accidentally removed command.
 
 2021-07-06  Philip Kaludercic  <philipk@posteo.net>
 
@@ -21063,7 +21063,7 @@
        Fix prompting for large files when loading literally
 
        * lisp/files.el (find-file-noselect): Don't include "literally" in
-       the "large file" prompt if we're gonna load literally anyway
+       the "large file" prompt if we're going to load literally anyway
        (bug#49144).
 
 2021-06-21  Lars Ingebrigtsen  <larsi@gnus.org>
@@ -21772,7 +21772,7 @@
 
        EIEIO: Promote the CLOS behavior over the EIEIO-specific behavior
 
-       Change docs to advertize `slot-value` rather than `oref`.
+       Change docs to advertise `slot-value` rather than `oref`.
        Change the implementation of `:initform` to better match the CLOS 
semantics,
        while preserving the EIEIO semantics, but warn when encountering cases
        where the two diverge.
@@ -21790,8 +21790,8 @@
 
        * lisp/emacs-lisp/eieio.el (defclass): Warn about inapplicable
        `:initarg` and about uses of init forms that are ambiguous.
-       (oref): Don't advertize the deprecated use of initargs as slot names.
-       (oref-default): Don't advertize the deprecated case where it returns the
+       (oref): Don't advertise the deprecated use of initargs as slot names.
+       (oref-default): Don't advertise the deprecated case where it returns the
        initform's value.
        (initialize-instance): Use `macroexp-const-p`.
        * lisp/emacs-lisp/eieio-core.el (eieio--unbound): Rename from
@@ -27204,7 +27204,7 @@
 
 2021-04-25  Andrea Corallo  <akrl@sdf.org>
 
-       Merge branch 'feature/native-comp' into into trunk
+       Merge branch 'feature/native-comp' into trunk
 
 2021-04-25  Lars Ingebrigtsen  <larsi@gnus.org>
 
@@ -34265,7 +34265,7 @@
        Use `length=' and family where possible in native comp code
 
        * lisp/emacs-lisp/comp-cstr.el (comp-intersect-typesets)
-       (comp-cstr-imm): Use Use `length=' and family where possible.
+       (comp-cstr-imm): Use `length=' and family where possible.
        * lisp/emacs-lisp/comp.el (comp-add-cond-cstrs-target-block)
        (comp-compute-dominator-frontiers)
        (batch-byte-native-compile-for-bootstrap): Likewise.
@@ -36674,7 +36674,7 @@
 
 2021-02-24  Andrea Corallo  <akrl@sdf.org>
 
-       Fix async compilation and paramenter naming
+       Fix async compilation and parameter naming
 
        * lisp/emacs-lisp/comp.el (native--compile-async)
        (native-compile-async): Fix broken parameter renaming.
@@ -40177,7 +40177,7 @@
 
        * lisp/simple.el (recenter-current-error): New command.
        * lisp/progmodes/grep.el (grep-mode-map):
-       Delete bidings for n and p.
+       Delete bindings for n and p.
 
        * lisp/progmodes/compile.el (compilation-minor-mode-map):
        Move here the n and p bindings.
@@ -41213,7 +41213,7 @@
 
 2021-02-02  Lars Ingebrigtsen  <larsi@gnus.org>
 
-       Fix up invalid_syntax error signalling
+       Fix up invalid_syntax error signaling
 
        * src/lread.c (invalid_syntax_lisp): Instead of putting the
        line/column in a string, signal an error containing the numbers as
@@ -41694,7 +41694,7 @@
        in a global minor mode.  This commit fixes bug #45792.
 
        * lisp/international/quail.el (quail-show-guidance): Test the major 
mode is
-       not minibuffer-inactive-mode before proceding with the function.
+       not minibuffer-inactive-mode before proceeding with the function.
 
 2021-01-31  Lars Ingebrigtsen  <larsi@gnus.org>
 
@@ -43613,7 +43613,7 @@
        RFC2047-encode invalid Subject/From headers (bug#45925).  This
        will make them be displayed more consistently in the Summary
        buffer (but still "wrong" sometimes, since there's not that much
-       we can guess at at this stage, charset wise).
+       we can guess at this stage, charset wise).
        (nnml-parse-head): Use it.
 
 2021-01-22  Michael Albinus  <michael.albinus@gmx.de>
@@ -48572,7 +48572,7 @@
        c-laomib-loop.  Insert code which calls c-laomib-loop minimally, with 
the help
        of the new cache.
 
-       * lisp/progmodes/cc-mode.el (c-basic-common-init): Initialize the new 
cach
+       * lisp/progmodes/cc-mode.el (c-basic-common-init): Initialize the new 
cache
        (at mode start).
        (c-before-change): Invalidate the new cache.
        (c-fl-decl-start): Add an extra check (> (point) bod-lim) to prevent 
looping.
@@ -50078,7 +50078,7 @@
        Optimize c-parse-state for large buffers with few (if any) braces.
 
        * lisp/progmodes/cc-engine.el (c-get-fallback-scan-pos): Search a 
maximum of
-       50,000 characters back for the two BODs.  Return nil if we dont' find 
them.
+       50,000 characters back for the two BODs.  Return nil if we don't find 
them.
        (c-parse-state-get-strategy): For strategy `forward', always use the 
position
        `good-pos' for `start-point', even when there's a change of current 
macro.
        Deal with a possible return value of nil from c-get-fallback-scan-pos 
(as
@@ -54142,7 +54142,7 @@
        Tweak the face of unknown backend indicators in flymake
 
        * lisp/progmodes/flymake.el (flymake--mode-line-format): Don't put
-       a face on the the "?" unknown backend indicator, because that
+       a face on the "?" unknown backend indicator, because that
        looks odd in inactive windows (bug#44689).
 
 2020-11-24  Paul W. Rankin  <pwr@skeletons.cc>
@@ -55996,7 +55996,7 @@
 
 2020-11-12  Andrea Corallo  <akrl@sdf.org>
 
-       Unline some functions to optimize bootstrap time
+       Uninline some functions to optimize bootstrap time
 
        * lisp/emacs-lisp/comp.el (comp-mvar-value-vld-p)
        (comp-mvar-value, comp-mvar-fixnum-p, comp-set-op-p)
@@ -56612,7 +56612,7 @@
 
 2020-11-07  Andrea Corallo  <akrl@sdf.org>
 
-       Allow for manually bumbing new native compiler ABI versions
+       Allow for manually bumping new native compiler ABI versions
 
        * src/comp.c (ABI_VERSION): Define macro.
        (hash_native_abi): Include ABI_VERSION in the hashing.
@@ -61229,7 +61229,7 @@
 
 2020-10-14  Andrea Corallo  <akrl@sdf.org>
 
-       Have `native-elisp-load' return the last registerd function
+       Have `native-elisp-load' return the last registered function
 
        * lisp/emacs-lisp/comp.el (comp-emit-for-top-level): Synthesize
        'top_level_run' so it returns the last value returned by
@@ -63478,7 +63478,7 @@
        user-error when there's a wrong password (bug#43704).
        (epa--wrong-password-p): New function.
        (epa-file-insert-file-contents): Use it, and stash the error away
-       for later signalling.
+       for later signaling.
 
        * lisp/emacs-lisp/subr-x.el (if-let): Autoload.
 
@@ -65831,7 +65831,7 @@
 
        Better error handling after calling 'gcc_jit_context_compile_to_file'
 
-       Typically errors are catched in 'compile_function' but in case
+       Typically errors are caught in 'compile_function' but in case
        libgccjit throw an error only afterwards while compiling the whole
        compilation unit we have to report it correctly.
 
@@ -67774,7 +67774,7 @@
 
 2020-09-05  Lars Ingebrigtsen  <larsi@gnus.org>
 
-       Revert "Display name with with spaces, but keep symbol name underneath"
+       Revert "Display name with spaces, but keep symbol name underneath"
 
        This reverts commit e0c77bb62c1c950a82ea0517646d989dc5c1fe27.
 
@@ -67854,7 +67854,7 @@
 
 2020-09-05  ej-32u  <ej32u@protonmail.com>  (tiny change)
 
-       Display name with with spaces, but keep symbol name underneath
+       Display name with spaces, but keep symbol name underneath
 
        * lisp/cus-edit.el (custom-unlispify-menu-entry): Display the
        pretty name, but keep the real symbol name as the value (bug#41905).
@@ -73089,7 +73089,7 @@
        * lisp/textmodes/tex-mode.el (tex-font-lock-keywords-2): End the
        expression before the terminating $ in constructions like $\it
        identifiername$
-       (bug#28277). This avoids italicising the final $ character.
+       (bug#28277). This avoids italicizing the final $ character.
 
        This fixes the final $ of the final test case here:
 
@@ -73421,7 +73421,7 @@
 
 2020-08-08  Lars Ingebrigtsen  <larsi@gnus.org>
 
-       Modernise a code example in os.texi
+       Modernize a code example in os.texi
 
        * doc/lispref/os.texi (Session Management): Use
        with-current-buffer in the example instead of save+switch (bug#40341).
@@ -75687,7 +75687,7 @@
 
        * lisp/net/eww.el (eww-list-bookmarks): Don't show buffer if there
        are no bookmarks.  (Bug#41385)
-       (eww-bookmark-prepare): Move signalling an error if there are no
+       (eww-bookmark-prepare): Move signaling an error if there are no
        bookmarks from here...
        (eww-read-bookmarks): ...to here.  Add new argument `error-out' to
        control this.
@@ -75941,7 +75941,7 @@
 
 2020-07-13  Andrea Corallo  <akrl@sdf.org>
 
-       Rework the backend to allocate arument arrays for call by references
+       Rework the backend to allocate argument arrays for call by references
 
        * src/comp.c (comp_t): Add 'zero' field.
        (emit_limple_call_ref): Allocate an array to host the parameters
@@ -76328,7 +76328,7 @@
 
        Add a simple pass to infer pure functions not explicitly declared as
        such.  Use this information only during compilation (speed 3) to
-       optimize out function calls whe possible.
+       optimize out function calls when possible.
 
 2020-07-09  Andrea Corallo  <akrl@sdf.org>
 
@@ -78012,7 +78012,7 @@
        cd4f75bb86 Rename default function to next-error-buffer-unnavigated-c...
        1dff0a8949 * lisp/image-mode.el (image-toggle-display-image): Fix fit...
        a71d1787f1 * doc/misc/tramp.texi (Predefined connection information):...
-       079b0dc430 Delete, don't kill, dir dir fragments in icomplete-fido-ba...
+       079b0dc430 Delete, don't kill, dir fragments in icomplete-fido-ba...
        6cdecc2659 Revert markup change in with-coding-priority docs
        22f4fba8a9 * lisp/emulation/cua-rect.el (cua--rectangle-region-insert...
        6b9eac6759 * lisp/simple.el (shell-command-on-region): Fix docstring.
@@ -78249,7 +78249,7 @@
        (comp-latch-make-fill): New function.
        (comp-emit-uncond-jump, comp-emit-cond-jump): Update to emit
        latches.
-       (comp-new-block-sym): Add a postfix paramenter.
+       (comp-new-block-sym): Add a postfix parameter.
 
 2020-06-13  Andrea Corallo  <akrl@sdf.org>
 
@@ -78602,7 +78602,7 @@
        Fix comp-call-optim-form-call for null `callee'
 
        * lisp/emacs-lisp/comp.el (comp-call-optim-form-call): Guard
-       agains null `calle'.
+       against null `calle'.
 
 2020-06-07  Glenn Morris  <rgm@gnu.org>
 
@@ -79675,7 +79675,7 @@
        Simply return the directory selected by the user.
        (project-switch-project-find-file): Remove.
        (project-switch-project-dired): Rename to project-dired and make
-       it follow the convention of existing projec tcommands.
+       it follow the convention of existing project tcommands.
        (project-switch-project-eshell): Ditto.
        (project-switch-project): Instead of passing the project instance
        to the command, just bind default-directory.
@@ -80910,7 +80910,7 @@
        'lambda_gc_guard' 'lambda_c_name_idx_h' 'data_imp_relocs'
        'loaded_once' fields.
 
-       * src/comp.c (load_comp_unit): Use compilaiton unit 'loaded_once'
+       * src/comp.c (load_comp_unit): Use compilation unit 'loaded_once'
        field.
        (make_subr, Fcomp__register_lambda): New functions.
        (Fcomp__register_subr): Make use of 'make_subr'.
@@ -81264,7 +81264,7 @@
        Fix bug #40992 whilst still allowing breakpoint highlights in edebug
 
        Strategy: when an instrumented function gets re-evaluated, save the 
former
-       value of its symbol's `edebug' property in the new propery 
`ghost-edebug'.  If
+       value of its symbol's `edebug' property in the new property 
`ghost-edebug'.  If
        this function is still being edebugged, edebug will then access its 
info from
        this new property.
 
@@ -83843,7 +83843,7 @@
 
        Implement position independent dump.
 
-       Set the filename for every compilation unit as realtive to obtain a
+       Set the filename for every compilation unit as relative to obtain a
        position independent dump.
 
        * lisp/loadup.el: Modify filename for every compilation unit as
@@ -84397,7 +84397,7 @@
 
        * lisp/arc-mode.el: Rewrite displaying the summaries
 
-       Completely rewrite the code that displayes the summaries, so all
+       Completely rewrite the code that displays the summaries, so all
        backends share the same code.
 
        (archive--summarize-descs): New function.
@@ -86075,7 +86075,7 @@
 
        Merge remote-tracking branch 'savannah/master' into HEAD
 
-2020-03-10  AndreaCorallo  <akrl@sdf.org>
+2020-03-10  Andrea Corallo  <akrl@sdf.org>
 
        * Improve load_comp_unit
 
@@ -86084,7 +86084,7 @@
 
        Guard also data_ephemeral_vec against compiler optimizations.
 
-2020-03-10  AndreaCorallo  <akrl@sdf.org>
+2020-03-10  Andrea Corallo  <akrl@sdf.org>
 
        * Fix store_function_docstring for native functions
 
@@ -86098,7 +86098,7 @@
        native_intspec and native_doc fields has to be reached by the subr
        cause are not anymore in the CU.
 
-2020-03-10  AndreaCorallo  <akrl@sdf.org>
+2020-03-10  Andrea Corallo  <akrl@sdf.org>
 
        * Set relocation class as ephemeral in `comp-limplify-top-level'
 
@@ -86547,17 +86547,17 @@
 
        * lisp/replace.el (occur-1): Update default-directory in occur buffer.
 
-2020-03-04  AndreaCorallo  <akrl@sdf.org>
+2020-03-04  Andrea Corallo  <akrl@sdf.org>
 
        * Do not crash if the output directory is created in the meanwhile
 
-2020-03-03  AndreaCorallo  <akrl@sdf.org>
+2020-03-03  Andrea Corallo  <akrl@sdf.org>
 
        Hash eln ABI once and add it to the output compilation path
 
        Fix org for eln new compilation folder layout
 
-2020-03-03  AndreaCorallo  <akrl@sdf.org>
+2020-03-03  Andrea Corallo  <akrl@sdf.org>
 
        Rework `find-lisp-object-file-name'
 
@@ -86668,7 +86668,7 @@
        and 'fill-column' values.  (Bug#36837)
        (whitespace-lines-regexp): New function.
 
-2020-03-01  AndreaCorallo  <akrl@sdf.org>
+2020-03-01  Andrea Corallo  <akrl@sdf.org>
 
        * ; Clean-up out of date comment
 
@@ -86718,11 +86718,11 @@
        * src/pdumper.c (dump_object): Fix hash for Lisp_Type after commit
        202c3319a28c029d6971dccea92f92425c5e8067.
 
-2020-02-29  AndreaCorallo  <akrl@sdf.org>
+2020-02-29  Andrea Corallo  <akrl@sdf.org>
 
        Introduce 'effective_load_path'
 
-2020-02-29  AndreaCorallo  <akrl@sdf.org>
+2020-02-29  Andrea Corallo  <akrl@sdf.org>
 
        * Keep comp-subr-list into pure space
 
@@ -87689,7 +87689,7 @@
 
        (lispref/modes.texi): Explain avoiding lambdas on hooks.
 
-2020-02-06  AndreaCorallo  <akrl@sdf.org>
+2020-02-06  Andrea Corallo  <akrl@sdf.org>
 
        Add system-configuration in the compilation output path
 
@@ -87713,7 +87713,7 @@
        Add function ssa-status as `comp-func' slot and have `comp-clean-ssa'
        to run when necessary.
 
-2020-03-01  AndreaCorallo  <akrl@sdf.org>
+2020-03-01  Andrea Corallo  <akrl@sdf.org>
 
        Remove relocation index form LIMPLE setimm
 
@@ -87739,21 +87739,21 @@
 
        Merge remote-tracking branch 'savannah/master' into HEAD
 
-2020-02-26  AndreaCorallo  <akrl@sdf.org>
+2020-02-26  Andrea Corallo  <akrl@sdf.org>
 
        * ; Add a TODO for a future optimization
 
-2020-02-26  AndreaCorallo  <akrl@sdf.org>
+2020-02-26  Andrea Corallo  <akrl@sdf.org>
 
        Store optimize qualities into .eln files
 
        For now just comp-speed and comp-debug are stored.
 
-2020-02-26  AndreaCorallo  <akrl@sdf.org>
+2020-02-26  Andrea Corallo  <akrl@sdf.org>
 
-       Rename d-base allocation classe into d-default
+       Rename d-base allocation class into d-default
 
-2020-02-26  AndreaCorallo  <akrl@sdf.org>
+2020-02-26  Andrea Corallo  <akrl@sdf.org>
 
        Add ephemeral relocation data class
 
@@ -87793,7 +87793,7 @@
 
        Test 'comp-eq' should not assume any string hashing policy
 
-2020-02-21  AndreaCorallo  <akrl@sdf.org>
+2020-02-21  Andrea Corallo  <akrl@sdf.org>
 
        Emit 'top_level_run' objects as impure
 
@@ -87801,7 +87801,7 @@
 
        Verify '--with-nativecomp' has also '--with-dumping=pdumper'
 
-2020-02-21  AndreaCorallo  <akrl@sdf.org>
+2020-02-21  Andrea Corallo  <akrl@sdf.org>
 
        Reorder m-var slots
 
@@ -87819,7 +87819,7 @@
 
        Use `sxhash-eq' to generate mvar SSA ids
 
-2020-02-15  AndreaCorallo  <akrl@sdf.org>
+2020-02-15  Andrea Corallo  <akrl@sdf.org>
 
        Speed 2 goes default
 
@@ -87832,7 +87832,7 @@
        Every function call by reference gets use one unique array of
        arguments.
 
-2020-02-14  AndreaCorallo  <akrl@sdf.org>
+2020-02-14  Andrea Corallo  <akrl@sdf.org>
 
        Clean-up old gc disable refuse in comp-tests-non-locals
 
@@ -88071,7 +88071,7 @@
 
        Always define subr-native-elisp-p also without native compiler
 
-2020-02-03  AndreaCorallo  <akrl@sdf.com>
+2020-02-03  Andrea Corallo  <akrl@sdf.com>
 
        Fix load_comp_unit for non zero speeds
 
@@ -90740,7 +90740,7 @@
 
        compute dominator tree
 
-       ssa and endge number generation with generator
+       ssa and edge number generation with generator
 
        add edge computation
 
@@ -91228,7 +91228,7 @@
 
        optimize primitive native call
 
-       propagate contant types and optimize self calls
+       propagate constant types and optimize self calls
 
        introduce stack_el_t
 
@@ -91466,7 +91466,7 @@
 
        introduce CASE_CALL_NARGS macro and add various ops
 
-       symbol_function set fset fget fget Bsubstring
+       symbol_function set fset fget Bsubstring
 
 2020-01-01  Andrea Corallo  <andrea_corallo@yahoo.it>
 
@@ -93578,7 +93578,7 @@
 
 2021-02-03  Michael Albinus  <michael.albinus@gmx.de>
 
-       Fix an error in tramp-sh-handle-make-process.  Dont' merge with master
+       Fix an error in tramp-sh-handle-make-process.  Don't merge with master
 
        * lisp/net/tramp-sh.el (tramp-sh-handle-make-process): Don't use heredoc
        script whent the argument contains a string.
@@ -95039,7 +95039,7 @@
 
 2020-06-13  João Távora  <joaotavora@gmail.com>
 
-       Delete, don't kill, dir dir fragments in icomplete-fido-backward-updir
+       Delete, don't kill, dir fragments in icomplete-fido-backward-updir
 
        Reported by: Andrew Schwartzmeyer <andrew@schwartzmeyer.com>
 
@@ -101413,7 +101413,7 @@
        vc-hg: prompt for branch to merge
 
        * lisp/vc/vc-hg.el (vc-hg-merge-branch): Prompt for revision to merge.
-       (vc-hg-revision-table): Use branches, tags and bookmarks as competion
+       (vc-hg-revision-table): Use branches, tags and bookmarks as completion
        candidates.
 
        * etc/NEWS: Mention changes of vc-hg.el
@@ -101483,7 +101483,7 @@
        (Create_Pixmap_From_Bitmap_Data):
        (xpm_load): Use new function.
        * src/xterm.c (x_composite_image): Use PictOpOver when there is a mask
-       so the transparency is honoured.
+       so the transparency is honored.
        (x_draw_image_foreground_1): Use x_composite_image.
 
 2019-11-29  Stefan Monnier  <monnier@iro.umontreal.ca>
@@ -103042,7 +103042,7 @@
        open-paren-in-column-0-is-defun-start to nil) fixes bug #37910.  It may 
also
        have fixed bug #5490 and bug #18072.
 
-       * lisp/progmodes/cc-engine.el (c-state-cache-non-literal-place): Remove 
thi
+       * lisp/progmodes/cc-engine.el (c-state-cache-non-literal-place): Remove 
this
        non-sensical function, replacing it with ....
        (c-state-cache-lower-good-pos): New function.
        (c-renarrow-state-cache, c-append-lower-brace-pair-to-state-cache)
@@ -110631,7 +110631,7 @@
        Fix reversed check in mm-possibly-verify-or-decrypt
 
        * lisp/gnus/mm-decode.el (mm-possibly-verify-or-decrypt): Fix
-       reverse check thinko that made unverified singed messages not
+       reverse check thinko that made unverified signed messages not
        display correctly.
 
 2019-09-27  Wilson Snyder  <wsnyder@wsnyder.org>
@@ -113664,7 +113664,7 @@
        Make the NSM not pop up an X dialogue on non-mouse actions
 
        * lisp/emacs-lisp/rmc.el (read-multiple-choice): Don't pop up X
-       dialogues on (url-retrieve "https://expired.badssl.com/"; #'ignore)
+       dialogs on (url-retrieve "https://expired.badssl.com/"; #'ignore)
        and the like.
 
 2019-09-04  Lars Ingebrigtsen  <larsi@gnus.org>
@@ -115157,7 +115157,7 @@
 
 2019-08-21  Lars Ingebrigtsen  <larsi@gnus.org>
 
-       Make hide-ifdef-mode-prefix-key customisable
+       Make hide-ifdef-mode-prefix-key customizable
 
        * lisp/progmodes/hideif.el (hide-ifdef-mode-prefix-key): Make into
        a defcustom since it seems like this is something that should be
@@ -117900,7 +117900,7 @@
        (readbyte_from_file): Assert that `infile` is set.
        (close_infile_unwind): Reset `infile` to its previous value rather than
        to NULL.
-       (Fload): Remember the previous value of `infile` before chaning it.
+       (Fload): Remember the previous value of `infile` before changing it.
        (readevalloop): Don't set `infile` any more.
 
 2019-07-31  Paul Eggert  <eggert@cs.ucla.edu>
@@ -130261,7 +130261,7 @@
        8192 bytes is a reasonable number nowadays given typical file
        system design.
        * test/lisp/image-tests.el (image-tests--emacs-images-directory):
-       New contant.
+       New constant.
        (image-type-from-file-header-test): New test.
 
 2019-05-18  Michael Albinus  <michael.albinus@gmx.de>
@@ -130710,7 +130710,7 @@
        Fix diff-mode face problem when used in terminals (Bug#35695)
 
        In a terminal supporting 256 colors, both diff-added and diff-removed
-       was mapped to the same greyish color.
+       was mapped to the same grayish color.
 
        * lisp/vc/diff-mode.el: Modify the colors of diff-removed,
          diff-added, diff-refine-removed, and diff-refine-added when
@@ -136480,7 +136480,7 @@
        Check gnus-newsgroup-dependencies is hash table in gnus-id-to-thread
 
        * lisp/gnus/gnus-sum.el (gnus-id-to-thread): If dependencies haven't
-         been initialized yet, don't blow up. Mimicks previous (non hasht
+         been initialized yet, don't blow up. Mimics previous (non hash
          table) behavior.
 
 2019-03-31  Mattias Engdegård  <mattiase@acm.org>
@@ -140170,7 +140170,7 @@
 
        * doc/misc/eshell.texi (Built-ins): Fix alias description
 
-       Dear eamcs developers, eshells current documentation first states
+       Dear emacs developers, eshell's current documentation first states
        that alias definitions are not saved to an alias file, later that
        they are saved to an alias file.  I tested it and the latter is
        correct.
@@ -158744,7 +158744,7 @@
 
 2018-04-17  Basil L. Contovounesios  <contovob@tcd.ie>
 
-       Modernise face specs and set version tags in eww/shr
+       Modernize face specs and set version tags in eww/shr
 
        * lisp/net/shr.el (shr-strike-through, shr-link, shr-selected-link):
        Set :version tag (bug#31200).
@@ -159218,10 +159218,10 @@
 
 2018-04-14  Lars Ingebrigtsen  <larsi@gnus.org>
 
-       Modernise a Gnus function a bit
+       Modernize a Gnus function a bit
 
        * lisp/gnus/gnus-start.el (gnus-update-active-hashtb-from-killed):
-       Modernise code a bit.
+       Modernize code a bit.
 
 2018-04-14  Lars Ingebrigtsen  <larsi@gnus.org>
 
@@ -165960,7 +165960,7 @@
        cl-loop: Calculate the array length just once
 
        * lisp/emacs-lisp/cl-macs.el (cl--parse-loop-clause):
-       Dont calculate the array length on each iteration (Bug#29866).
+       Don't calculate the array length on each iteration (Bug#29866).
 
 2018-01-07  Philipp Stephani  <phst@google.com>
 
@@ -166738,7 +166738,7 @@
        5a7d009 * lisp/vc/smerge-mode.el (smerge-refine): Replace obsolete al...
        e019c35 FOR_EACH_FRAME no longer assumes frame-list
        d64b88d * src/font.c (Ffont_info): Doc fix.  (Bug#29682)
-       92b2604 Modernise message.el face spec syntax
+       92b2604 Modernize message.el face spec syntax
        b1efbe6 Update message.el obsolete face aliases
        2494c14 ; * lisp/comint.el (comint-terminfo-terminal): Add a :version...
        12ad276 Improve documentation of TERM environment variable
@@ -167034,7 +167034,7 @@
        5a7d0095a4 * lisp/vc/smerge-mode.el (smerge-refine): Replace obsolete...
        e019c35df6 FOR_EACH_FRAME no longer assumes frame-list
        d64b88da2f * src/font.c (Ffont_info): Doc fix.  (Bug#29682)
-       92b2604a7f Modernise message.el face spec syntax
+       92b2604a7f Modernize message.el face spec syntax
        b1efbe6564 Update message.el obsolete face aliases
        2494c14e76 ; * lisp/comint.el (comint-terminfo-terminal): Add a :vers...
        12ad276d15 Improve documentation of TERM environment variable
@@ -169297,7 +169297,7 @@
        2e1b3522b8 Improve documentation of 'line-number-display-width'
        5b6e59cfdb Implement vc-default-dir-extra-headers for vc-rcs
        22adeca42a In NEWS give advice on use of `switch-to-buffer' (Bug#28645)
-       2c3e6f1ddc Dont update primary selection with winner-undo
+       2c3e6f1ddc Don't update primary selection with winner-undo
        b38724ab67 Work around ImageMagick bug 825
        20cc68e871 Document rectangle-preview option more (Bug#27974)
        a0b7b301dd Do not reject https://gnu.org in commit messages
@@ -172340,7 +172340,7 @@
 
        * doc/misc/eshell.texi (Built-ins): Fix alias description
 
-       Dear eamcs developers, eshells current documentation first states
+       Dear emacs developers, eshell's current documentation first states
        that alias definitions are not saved to an alias file, later that
        they are saved to an alias file.  I tested it and the latter is
        correct.
@@ -181827,7 +181827,7 @@
 
 2017-12-15  Basil L. Contovounesios  <contovob@tcd.ie>
 
-       Modernise message.el face spec syntax
+       Modernize message.el face spec syntax
 
        * lisp/gnus/message.el (message-header-to, message-header-cc)
        (message-header-subject, message-header-newsgroups)
@@ -184352,7 +184352,7 @@
 
        * src/lisp.h (COMMON_MULTIPLE): Move here from alloc.c.
        * src/thread.c (THREAD_ALIGNMENT): New macro.
-       (main_thread): Use THREAD_ALIGNMENT to align propertly.  (Bug#29040)
+       (main_thread): Use THREAD_ALIGNMENT to align properly.  (Bug#29040)
 
 2017-10-28  Eli Zaretskii  <eliz@gnu.org>
 
@@ -185296,10 +185296,10 @@
 2017-10-17  Tino Calancha  <tino.calancha@gmail.com>
            Noam Postavsky  <npostavs@gmail.com>
 
-       Dont update primary selection with winner-undo
+       Don't update primary selection with winner-undo
 
        * lisp/winner.el (winner-set):
-       Dont update primary selection when select-enable-primary
+       Don't update primary selection when select-enable-primary
        is non-nil (Bug#28631).
 
 2017-10-17  Paul Eggert  <eggert@cs.ucla.edu>
@@ -185648,7 +185648,7 @@
        Add mode map to Flymake diagnostic button
 
        * lisp/progmodes/flymake.el (flymake--diagnostics-buffer-entries): Add
-       keymap propery.
+       keymap property.
 
 2017-10-10  João Távora  <joaotavora@gmail.com>
 
@@ -192051,7 +192051,7 @@
 
 2017-08-09  Tino Calancha  <tino.calancha@gmail.com>
 
-       Ask files for deletion in buffer order: top first, botton later
+       Ask files for deletion in buffer order: top first, bottom later
 
        * lisp/dired.el (dired-do-flagged-delete, dired-do-delete):
        Call `nreverse' t invert the output of `dired-map-over-marks'.
@@ -194580,7 +194580,7 @@
 
 2017-07-09  R. Bernstein  <rocky@gnu.org>
 
-       Add realgud faces faces to whiteboard...
+       Add realgud faces to whiteboard...
 
        Adjust wheatgrass to use underline for enabled/disabled breakpoints
 
@@ -195520,7 +195520,7 @@
 
        Ignore mouse-movement for describe-key-briefly (Bug#12204)
 
-       * lisp/help.el (help-read-key-sequence): Add optional argument ot
+       * lisp/help.el (help-read-key-sequence): Add optional argument to
        ignore `mouse-movement' events.
        (describe-key-briefly): Use it.
        * doc/emacs/help.texi (Key Help):
@@ -198690,7 +198690,7 @@
        (tramp-adb-ls-toolbox-regexp):
        Ignore addition links column on Android 7.
        (tramp-adb-get-ls-command):
-       Dont use --color=none when using toybox (Android 7).  It's not
+       Don't use --color=none when using toybox (Android 7).  It's not
        possible to disable coloring explicitly for toybox ls.
 
 2017-05-27  Svante Carl v. Erichsen  <Svante.v.Erichsen@web.de>  (tiny change)
@@ -199186,7 +199186,7 @@
 
 2017-05-23  Paul Eggert  <eggert@cs.ucla.edu>
 
-       Don't warn about missing brances on macOS
+       Don't warn about missing branches on macOS
 
        On macOS, removing -Wmissing-braces is not enough; the warning has to
        be disabled explicitly.
@@ -208945,7 +208945,7 @@
 
        Revamp quitting and fix infloops
 
-       This fixes some infinite loops that cannot be quitted out of,
+       This fixes some infinite loops that cannot be quit out of,
        e.g., (defun foo () (nth most-positive-fixnum '#1=(1 . #1#)))
        when byte-compiled and when run under X.  See:
        https://lists.gnu.org/r/emacs-devel/2017-01/msg00577.html
@@ -214158,7 +214158,7 @@
        refactor systhread.h
 
        This refactors systhread.h to move the notion of a "lisp mutex"
-       into thread.c.  This lets us make make the global lock and
+       into thread.c.  This lets us make the global lock and
        post_acquire_global_lock static.
 
 2012-08-17  Tom Tromey  <tromey@redhat.com>
@@ -218796,7 +218796,7 @@
        * lib/stdint.in.h, m4/extensions.m4, m4/stdint.m4, m4/stdio_h.m4:
        * m4/sys_types_h.m4: Copy from gnulib.
        * lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate.
-       * lib/limits.in.h, m4/limits-h.m4: New files, copie from gnulib.
+       * lib/limits.in.h, m4/limits-h.m4: New files, copied from gnulib.
        * nt/gnulib.mk: Merge changes from lib/gnulib.mk.
 
 2016-09-15  Michael Albinus  <michael.albinus@gmx.de>
@@ -219462,7 +219462,7 @@
 
 2016-09-02  Stefan Monnier  <monnier@iro.umontreal.ca>
 
-       Check actual contents before promting about changed file
+       Check actual contents before prompting about changed file
 
        * lisp/userlock.el (userlock--check-content-unchanged)
        (userlock--ask-user-about-supersession-threat): New functions.
@@ -220696,7 +220696,7 @@
 
        * lisp/progmodes/cc-mode.el (c-after-change): When we detect a missing
        invocation of c-before-change-functions, we assume the changed region 
is the
-       entire buffer, and call c-before-change explicitly before proceding.
+       entire buffer, and call c-before-change explicitly before proceeding.
 
 2016-08-09  Alan Mackenzie  <acm@muc.de>
 
@@ -223962,10 +223962,10 @@
 
 2016-06-15  Stefan Monnier  <monnier@iro.umontreal.ca>
 
-       Advertize set-keymap-parent as replacement for copy-keymap
+       Advertise set-keymap-parent as replacement for copy-keymap
 
        * doc/lispref/keymaps.texi (Creating Keymaps):
-       * src/keymap.c (Fcopy_keymap): Advertize set-keymap-parent as 
replacement.
+       * src/keymap.c (Fcopy_keymap): Advertise set-keymap-parent as 
replacement.
 
 2016-06-15  Ted Zlatanov  <tzz@lifelogs.com>
 
@@ -226537,7 +226537,7 @@
        Fix Bug#10085
 
        * lisp/net/tramp.el (tramp-find-foreign-file-name-handler):
-       Add optional arguments OPERATION and COMPETION.  Handle
+       Add optional arguments OPERATION and COMPLETION.  Handle
        `file-name-as-directory', `file-name-directory' and
        `file-name-nondirectory' also in completion mode.
        (tramp-file-name-handler): Use it.  (Bug#10085)
@@ -226854,7 +226854,7 @@
 
 2016-04-30  Lars Ingebrigtsen  <larsi@gnus.org>
 
-       Document mode mode line variables
+       Document mode line variables
 
        * doc/lispref/modes.texi (Mode Line Variables): Document
        `mode-line-front-space, `mode-line-misc-info',
@@ -227546,7 +227546,7 @@
        of the data end marker from here... (bug#23020).
        (smtpmail-send-data): ... to here, so that we don't get a
        "Sending done" before we've sent the final "." (which can make
-       the SMPT server reject the email.
+       the SMTP server reject the email.
 
 2016-04-25  Lars Magne Ingebrigtsen  <larsi@gnus.org>
 
diff --git a/Makefile.in b/Makefile.in
index 45b4a59e3d..93609a4e16 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -405,16 +405,18 @@ actual-all: ${SUBDIR} info 
$(gsettings_SCHEMAS:.xml=.valid) src-depending-on-lis
 # ADVICE-ON-FAILURE-END:bootstrap
 
 advice-on-failure:
+       @[ -f .no-advice-on-failure ] && exit ${exit-status}; true
        @echo >&2 '***'
        @echo >&2 '*** '"\"make ${make-target}\" failed with exit status 
${exit-status}."
        @echo >&2 '***'
        @cat Makefile | \
-         sed -n '/^# ADVICE-ON-FAILURE-BEGIN:${make-target}/,$${p;/^# 
ADVICE-ON-FAILURE-END:${make-target}/q};' | \
-         sed 's/^# /*** /' | grep -v '^*** ADVICE-ON-FAILURE-' >&2
+         sed -n '/^# ADVICE-ON-FAILURE-BEGIN:${make-target}/,$${p;/^# 
ADVICE-ON-FAILURE-END:${make-target}/q;};' | \
+         sed 's/^# /*** /' | grep -v '^\*\*\* ADVICE-ON-FAILURE-' >&2
        @echo >&2 '***'
        @exit ${exit-status}
 
 sanity-check:
+       @[ -f .no-advice-on-failure ] && exit 0; true
        @v=$$(src/emacs${EXEEXT} --batch --eval \
          '(progn (defun f (n) (if (= 0 n) 1 (* n (f (- n 1))))) (princ (f 
10)))' \
          2> /dev/null); \
@@ -423,8 +425,8 @@ sanity-check:
        echo >&2 '*** '"\"make ${make-target}\" succeeded, but Emacs is not 
functional."; \
        echo >&2 '***'; \
        cat Makefile | \
-         sed -n '/^# ADVICE-ON-FAILURE-BEGIN:${make-target}/,$${p;/^# 
ADVICE-ON-FAILURE-END:${make-target}/q};' | \
-         sed 's/^# /*** /' | grep -v '^*** ADVICE-ON-FAILURE-' >&2; \
+         sed -n '/^# ADVICE-ON-FAILURE-BEGIN:${make-target}/,$${p;/^# 
ADVICE-ON-FAILURE-END:${make-target}/q;};' | \
+         sed 's/^# /*** /' | grep -v '^\*\*\* ADVICE-ON-FAILURE-' >&2; \
        echo >&2 '***'; \
        exit 1
 
diff --git a/admin/cus-test.el b/admin/cus-test.el
index 22d5a3a151..7e73f2e44a 100644
--- a/admin/cus-test.el
+++ b/admin/cus-test.el
@@ -131,7 +131,7 @@ Names should be as they appear in loaddefs.el.")
 ;; Don't create a file `abbrev-file-name'.
 (setq save-abbrevs nil)
 
-;; Avoid compile logs from adviced functions.
+;; Avoid compile logs from advised functions.
 (eval-after-load "bytecomp"
   '(setq ad-default-compilation-action 'never))
 
diff --git a/admin/grammars/srecode-template.wy 
b/admin/grammars/srecode-template.wy
index c3531ebd54..7ba73d2921 100644
--- a/admin/grammars/srecode-template.wy
+++ b/admin/grammars/srecode-template.wy
@@ -126,7 +126,7 @@ variable
   : SET symbol insertable-string-list newline
     (VARIABLE-TAG $2 nil $3)
   | SET symbol number newline
-    ;; This so a common error w/ priority works.
+    ;; This so a common error with priority works.
     ;; Note that "number" still has a string value in the lexer.
     (VARIABLE-TAG $2 nil (list $3))
   | SHOW symbol newline
diff --git a/admin/last-chance.el b/admin/last-chance.el
index 30d6a25a28..45d470cacd 100644
--- a/admin/last-chance.el
+++ b/admin/last-chance.el
@@ -41,7 +41,7 @@
 ;;
 ;; will show you any references to `change-log-date-face' in the
 ;; *.el files in a new buffer (in Grep mode).  Hopefully you see
-;; only the obsolete declaration and can proceed w/ its removal.
+;; only the obsolete declaration and can proceed with its removal.
 ;; If not, please DTRT and refrain from the removal until those
 ;; references are properly transitioned.
 ;;
diff --git a/admin/notes/bug-triage b/admin/notes/bug-triage
index 3d9a275c9d..bee7242337 100644
--- a/admin/notes/bug-triage
+++ b/admin/notes/bug-triage
@@ -73,7 +73,7 @@ the ones that are not reproducible on the current release.
          know if you are able to?  If I don't hear back in a few
          weeks, I'll just close this bug as unreproducible."
      [ ] Check that the priority is reasonable.  Most bugs should be
-         marked as normal, but crashers and security issues can be
+         marked as normal, but crashes and security issues can be
          marked as serious.
   3. Your changes will take some time to take effect.  After a period of 
minutes
      to hours, you will get a mail telling you the control message has been
diff --git a/admin/notes/tree-sitter/build-module/README 
b/admin/notes/tree-sitter/build-module/README
new file mode 100644
index 0000000000..2fcb9778da
--- /dev/null
+++ b/admin/notes/tree-sitter/build-module/README
@@ -0,0 +1,17 @@
+To build the language definition for a particular language, run
+
+    ./build.sh <language>
+
+eg,
+
+    ./build.sh html
+
+The dynamic module will be in /dist directory
+
+To build all modules at once, run
+
+    ./batch.sh
+
+This gives you C, JSON, Go, HTML, Javascript, CSS, Python, Typescript
+(tsx), C# (csharp), C++ (cpp), Rust. More can be added to batch.sh
+unless it's directory structure is not standard.
\ No newline at end of file
diff --git a/admin/notes/tree-sitter/build-module/batch.sh 
b/admin/notes/tree-sitter/build-module/batch.sh
new file mode 100755
index 0000000000..deed18978a
--- /dev/null
+++ b/admin/notes/tree-sitter/build-module/batch.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+languages=(
+    'c'
+    'cpp'
+    'css'
+    'c-sharp'
+    'go'
+    'html'
+    'javascript'
+    'json'
+    'python'
+    'rust'
+    'typescript'
+)
+
+for language in "${languages[@]}"
+do
+    ./build.sh $language
+done
diff --git a/admin/notes/tree-sitter/build-module/build.sh 
b/admin/notes/tree-sitter/build-module/build.sh
new file mode 100755
index 0000000000..102ab310fa
--- /dev/null
+++ b/admin/notes/tree-sitter/build-module/build.sh
@@ -0,0 +1,62 @@
+#!/bin/bash
+
+lang=$1
+
+if [ $(uname) == "Darwin" ]
+then
+    soext="dylib"
+else
+    soext="so"
+fi
+
+echo "Building ${lang}"
+
+# Retrieve sources.
+git clone "https://github.com/tree-sitter/tree-sitter-${lang}.git"; \
+    --depth 1 --quiet
+if [ "${lang}" == "typescript" ]
+then
+    lang="typescript/tsx"
+fi
+cp tree-sitter-lang.in "tree-sitter-${lang}/src"
+cp emacs-module.h "tree-sitter-${lang}/src"
+cp "tree-sitter-${lang}/grammar.js" "tree-sitter-${lang}/src"
+cd "tree-sitter-${lang}/src"
+
+if [ "${lang}" == "typescript/tsx" ]
+then
+    lang="tsx"
+fi
+
+# Build.
+cc -c -I. parser.c
+# Compile scanner.c.
+if test -f scanner.c
+then
+    cc -fPIC -c -I. scanner.c
+fi
+# Compile scanner.cc.
+if test -f scanner.cc
+then
+    c++ -fPIC -I. -c scanner.cc
+fi
+# Link.
+if test -f scanner.cc
+then
+    c++ -fPIC -shared *.o -o "libtree-sitter-${lang}.${soext}"
+else
+    cc -fPIC -shared *.o -o "libtree-sitter-${lang}.${soext}"
+fi
+
+# Copy out.
+
+if [ "${lang}" == "typescript" ]
+then
+    cp "libtree-sitter-${lang}.${soext}" ..
+    cd ..
+fi
+
+mkdir -p ../../dist
+cp "libtree-sitter-${lang}.${soext}" ../../dist
+cd ../../
+rm -rf "tree-sitter-${lang}"
diff --git a/admin/notes/tree-sitter/html-manual/Accessing-Node.html 
b/admin/notes/tree-sitter/html-manual/Accessing-Node.html
new file mode 100644
index 0000000000..00ac63b833
--- /dev/null
+++ b/admin/notes/tree-sitter/html-manual/Accessing-Node.html
@@ -0,0 +1,206 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd";>
+<html>
+<!-- Created by GNU Texinfo 6.8, https://www.gnu.org/software/texinfo/ -->
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<!-- This is the GNU Emacs Lisp Reference Manual
+corresponding to Emacs version 29.0.50.
+
+Copyright © 1990-1996, 1998-2022 Free Software Foundation,
+Inc.
+
+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 the
+Invariant Sections being "GNU General Public License," 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.  Buying copies from the FSF supports it in
+developing GNU and promoting software freedom." -->
+<title>Accessing Node (GNU Emacs Lisp Reference Manual)</title>
+
+<meta name="description" content="Accessing Node (GNU Emacs Lisp Reference 
Manual)">
+<meta name="keywords" content="Accessing Node (GNU Emacs Lisp Reference 
Manual)">
+<meta name="resource-type" content="document">
+<meta name="distribution" content="global">
+<meta name="Generator" content="makeinfo">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+
+<link href="index.html" rel="start" title="Top">
+<link href="Index.html" rel="index" title="Index">
+<link href="index.html#SEC_Contents" rel="contents" title="Table of Contents">
+<link href="Parsing-Program-Source.html" rel="up" title="Parsing Program 
Source">
+<link href="Pattern-Matching.html" rel="next" title="Pattern Matching">
+<link href="Retrieving-Node.html" rel="prev" title="Retrieving Node">
+<style type="text/css">
+<!--
+a.copiable-anchor {visibility: hidden; text-decoration: none; line-height: 0em}
+a.summary-letter {text-decoration: none}
+blockquote.indentedblock {margin-right: 0em}
+div.display {margin-left: 3.2em}
+div.example {margin-left: 3.2em}
+kbd {font-style: oblique}
+pre.display {font-family: inherit}
+pre.format {font-family: inherit}
+pre.menu-comment {font-family: serif}
+pre.menu-preformatted {font-family: serif}
+span.nolinebreak {white-space: nowrap}
+span.roman {font-family: initial; font-weight: normal}
+span.sansserif {font-family: sans-serif; font-weight: normal}
+span:hover a.copiable-anchor {visibility: visible}
+ul.no-bullet {list-style: none}
+-->
+</style>
+<link rel="stylesheet" type="text/css" href="./manual.css">
+
+
+</head>
+
+<body lang="en">
+<div class="section" id="Accessing-Node">
+<div class="header">
+<p>
+Next: <a href="Pattern-Matching.html" accesskey="n" rel="next">Pattern 
Matching Tree-sitter Nodes</a>, Previous: <a href="Retrieving-Node.html" 
accesskey="p" rel="prev">Retrieving Node</a>, Up: <a 
href="Parsing-Program-Source.html" accesskey="u" rel="up">Parsing Program 
Source</a> &nbsp; [<a href="index.html#SEC_Contents" title="Table of contents" 
rel="contents">Contents</a>][<a href="Index.html" title="Index" 
rel="index">Index</a>]</p>
+</div>
+<hr>
+<span id="Accessing-Node-Information"></span><h3 class="section">37.4 
Accessing Node Information</h3>
+
+<p>Before going further, make sure you have read the basic conventions
+about tree-sitter nodes in the previous node.
+</p>
+<span id="Basic-information"></span><h3 class="heading">Basic information</h3>
+
+<p>Every node is associated with a parser, and that parser is associated
+with a buffer.  The following functions let you retrieve them.
+</p>
+<dl class="def">
+<dt id="index-treesit_002dnode_002dparser"><span class="category">Function: 
</span><span><strong>treesit-node-parser</strong> <em>node</em><a 
href='#index-treesit_002dnode_002dparser' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function returns <var>node</var>&rsquo;s associated parser.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dnode_002dbuffer"><span class="category">Function: 
</span><span><strong>treesit-node-buffer</strong> <em>node</em><a 
href='#index-treesit_002dnode_002dbuffer' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function returns <var>node</var>&rsquo;s parser&rsquo;s associated 
buffer.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dnode_002dlanguage"><span class="category">Function: 
</span><span><strong>treesit-node-language</strong> <em>node</em><a 
href='#index-treesit_002dnode_002dlanguage' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function returns <var>node</var>&rsquo;s parser&rsquo;s associated 
language.
+</p></dd></dl>
+
+<p>Each node represents a piece of text in the buffer.  Functions below
+finds relevant information about that text.
+</p>
+<dl class="def">
+<dt id="index-treesit_002dnode_002dstart"><span class="category">Function: 
</span><span><strong>treesit-node-start</strong> <em>node</em><a 
href='#index-treesit_002dnode_002dstart' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>Return the start position of <var>node</var>.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dnode_002dend"><span class="category">Function: 
</span><span><strong>treesit-node-end</strong> <em>node</em><a 
href='#index-treesit_002dnode_002dend' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>Return the end position of <var>node</var>.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dnode_002dtext"><span class="category">Function: 
</span><span><strong>treesit-node-text</strong> <em>node &amp;optional 
object</em><a href='#index-treesit_002dnode_002dtext' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>Returns the buffer text that <var>node</var> represents.  (If 
<var>node</var> is
+retrieved from parsing a string, it will be text from that string.)
+</p></dd></dl>
+
+<p>Here are some basic checks on tree-sitter nodes.
+</p>
+<dl class="def">
+<dt id="index-treesit_002dnode_002dp"><span class="category">Function: 
</span><span><strong>treesit-node-p</strong> <em>object</em><a 
href='#index-treesit_002dnode_002dp' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>Checks if <var>object</var> is a tree-sitter syntax node.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dnode_002deq"><span class="category">Function: 
</span><span><strong>treesit-node-eq</strong> <em>node1 node2</em><a 
href='#index-treesit_002dnode_002deq' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>Checks if <var>node1</var> and <var>node2</var> are the same node in a 
syntax
+tree.
+</p></dd></dl>
+
+<span id="Property-information"></span><h3 class="heading">Property 
information</h3>
+
+<p>In general, nodes in a concrete syntax tree fall into two categories:
+<em>named nodes</em> and <em>anonymous nodes</em>.  Whether a node is named
+or anonymous is determined by the language definition
+(see <a href="Language-Definitions.html#tree_002dsitter-named-node">named 
node</a>).
+</p>
+<span id="index-tree_002dsitter-missing-node"></span>
+<p>Apart from being named/anonymous, a node can have other properties.  A
+node can be &ldquo;missing&rdquo;: missing nodes are inserted by the parser in
+order to recover from certain kinds of syntax errors, i.e., something
+should probably be there according to the grammar, but not there.
+</p>
+<span id="index-tree_002dsitter-extra-node"></span>
+<p>A node can be &ldquo;extra&rdquo;: extra nodes represent things like 
comments,
+which can appear anywhere in the text.
+</p>
+<span id="index-tree_002dsitter-node-that-has-changes"></span>
+<p>A node &ldquo;has changes&rdquo; if the buffer changed since when the node 
is
+retrieved, i.e., outdated.
+</p>
+<span id="index-tree_002dsitter-node-that-has-error"></span>
+<p>A node &ldquo;has error&rdquo; if the text it spans contains a syntax 
error.  It
+can be the node itself has an error, or one of its
+children/grandchildren... has an error.
+</p>
+<dl class="def">
+<dt id="index-treesit_002dnode_002dcheck"><span class="category">Function: 
</span><span><strong>treesit-node-check</strong> <em>node property</em><a 
href='#index-treesit_002dnode_002dcheck' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function checks if <var>node</var> has <var>property</var>.  
<var>property</var>
+can be <code>'named</code>, <code>'missing</code>, <code>'extra</code>,
+<code>'has-changes</code>, or <code>'has-error</code>.
+</p></dd></dl>
+
+
+<dl class="def">
+<dt id="index-treesit_002dnode_002dtype"><span class="category">Function: 
</span><span><strong>treesit-node-type</strong> <em>node</em><a 
href='#index-treesit_002dnode_002dtype' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>Named nodes have &ldquo;types&rdquo; (see <a 
href="Language-Definitions.html#tree_002dsitter-node-type">node type</a>).
+For example, a named node can be a <code>string_literal</code> node, where
+<code>string_literal</code> is its type.
+</p>
+<p>This function returns <var>node</var>&rsquo;s type as a string.
+</p></dd></dl>
+
+<span id="Information-as-a-child-or-parent"></span><h3 
class="heading">Information as a child or parent</h3>
+
+<dl class="def">
+<dt id="index-treesit_002dnode_002dindex"><span class="category">Function: 
</span><span><strong>treesit-node-index</strong> <em>node &amp;optional 
named</em><a href='#index-treesit_002dnode_002dindex' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function returns the index of <var>node</var> as a child node of 
its
+parent.  If <var>named</var> is non-nil, it only count named nodes
+(see <a href="Language-Definitions.html#tree_002dsitter-named-node">named 
node</a>).
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dnode_002dfield_002dname"><span 
class="category">Function: 
</span><span><strong>treesit-node-field-name</strong> <em>node</em><a 
href='#index-treesit_002dnode_002dfield_002dname' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>A child of a parent node could have a field name (see <a 
href="Language-Definitions.html#tree_002dsitter-node-field-name">field 
name</a>).  This function returns the field name
+of <var>node</var> as a child of its parent.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dnode_002dfield_002dname_002dfor_002dchild"><span 
class="category">Function: 
</span><span><strong>treesit-node-field-name-for-child</strong> <em>node 
n</em><a href='#index-treesit_002dnode_002dfield_002dname_002dfor_002dchild' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This function returns the field name of the <var>n</var>&rsquo;th child 
of
+<var>node</var>.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dchild_002dcount"><span class="category">Function: 
</span><span><strong>treesit-child-count</strong> <em>node &amp;optional 
named</em><a href='#index-treesit_002dchild_002dcount' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function finds the number of children of <var>node</var>.  If
+<var>named</var> is non-nil, it only counts named child (see <a 
href="Language-Definitions.html#tree_002dsitter-named-node">named node</a>).
+</p></dd></dl>
+
+</div>
+<hr>
+<div class="header">
+<p>
+Next: <a href="Pattern-Matching.html">Pattern Matching Tree-sitter Nodes</a>, 
Previous: <a href="Retrieving-Node.html">Retrieving Node</a>, Up: <a 
href="Parsing-Program-Source.html">Parsing Program Source</a> &nbsp; [<a 
href="index.html#SEC_Contents" title="Table of contents" 
rel="contents">Contents</a>][<a href="Index.html" title="Index" 
rel="index">Index</a>]</p>
+</div>
+
+
+
+</body>
+</html>
diff --git a/admin/notes/tree-sitter/html-manual/Language-Definitions.html 
b/admin/notes/tree-sitter/html-manual/Language-Definitions.html
new file mode 100644
index 0000000000..6dd589f825
--- /dev/null
+++ b/admin/notes/tree-sitter/html-manual/Language-Definitions.html
@@ -0,0 +1,402 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd";>
+<html>
+<!-- Created by GNU Texinfo 6.8, https://www.gnu.org/software/texinfo/ -->
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<!-- This is the GNU Emacs Lisp Reference Manual
+corresponding to Emacs version 29.0.50.
+
+Copyright © 1990-1996, 1998-2022 Free Software Foundation,
+Inc.
+
+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 the
+Invariant Sections being "GNU General Public License," 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.  Buying copies from the FSF supports it in
+developing GNU and promoting software freedom." -->
+<title>Language Definitions (GNU Emacs Lisp Reference Manual)</title>
+
+<meta name="description" content="Language Definitions (GNU Emacs Lisp 
Reference Manual)">
+<meta name="keywords" content="Language Definitions (GNU Emacs Lisp Reference 
Manual)">
+<meta name="resource-type" content="document">
+<meta name="distribution" content="global">
+<meta name="Generator" content="makeinfo">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+
+<link href="index.html" rel="start" title="Top">
+<link href="Index.html" rel="index" title="Index">
+<link href="index.html#SEC_Contents" rel="contents" title="Table of Contents">
+<link href="Parsing-Program-Source.html" rel="up" title="Parsing Program 
Source">
+<link href="Using-Parser.html" rel="next" title="Using Parser">
+<style type="text/css">
+<!--
+a.copiable-anchor {visibility: hidden; text-decoration: none; line-height: 0em}
+a.summary-letter {text-decoration: none}
+blockquote.indentedblock {margin-right: 0em}
+div.display {margin-left: 3.2em}
+div.example {margin-left: 3.2em}
+kbd {font-style: oblique}
+pre.display {font-family: inherit}
+pre.format {font-family: inherit}
+pre.menu-comment {font-family: serif}
+pre.menu-preformatted {font-family: serif}
+span.nolinebreak {white-space: nowrap}
+span.roman {font-family: initial; font-weight: normal}
+span.sansserif {font-family: sans-serif; font-weight: normal}
+span:hover a.copiable-anchor {visibility: visible}
+ul.no-bullet {list-style: none}
+-->
+</style>
+<link rel="stylesheet" type="text/css" href="./manual.css">
+
+
+</head>
+
+<body lang="en">
+<div class="section" id="Language-Definitions">
+<div class="header">
+<p>
+Next: <a href="Using-Parser.html" accesskey="n" rel="next">Using Tree-sitter 
Parser</a>, Up: <a href="Parsing-Program-Source.html" accesskey="u" 
rel="up">Parsing Program Source</a> &nbsp; [<a href="index.html#SEC_Contents" 
title="Table of contents" rel="contents">Contents</a>][<a href="Index.html" 
title="Index" rel="index">Index</a>]</p>
+</div>
+<hr>
+<span id="Tree_002dsitter-Language-Definitions"></span><h3 
class="section">37.1 Tree-sitter Language Definitions</h3>
+<span id="index-language-definitions_002c-for-tree_002dsitter"></span>
+
+<span id="Loading-a-language-definition"></span><h3 class="heading">Loading a 
language definition</h3>
+<span id="index-loading-language-definition-for-tree_002dsitter"></span>
+
+<span id="index-language-argument_002c-for-tree_002dsitter"></span>
+<p>Tree-sitter relies on language definitions to parse text in that
+language.  In Emacs, a language definition is represented by a symbol.
+For example, the C language definition is represented as the symbol
+<code>c</code>, and <code>c</code> can be passed to tree-sitter functions as 
the
+<var>language</var> argument.
+</p>
+<span id="index-treesit_002dextra_002dload_002dpath"></span>
+<span id="index-treesit_002dload_002dlanguage_002derror"></span>
+<span id="index-treesit_002dload_002dsuffixes"></span>
+<p>Tree-sitter language definitions are distributed as dynamic libraries.
+In order to use a language definition in Emacs, you need to make sure
+that the dynamic library is installed on the system.  Emacs looks for
+language definitions in several places, in the following order:
+</p>
+<ul>
+<li> first, in the list of directories specified by the variable
+<code>treesit-extra-load-path</code>;
+</li><li> then, in the <samp>tree-sitter</samp> subdirectory of the directory
+specified by <code>user-emacs-directory</code> (see <a 
href="Init-File.html">The Init File</a>);
+</li><li> and finally, in the system&rsquo;s default locations for dynamic 
libraries.
+</li></ul>
+
+<p>In each of these directories, Emacs looks for a file with file-name
+extensions specified by the variable <code>treesit-load-suffixes</code>.
+</p>
+<p>If Emacs cannot find the library or has problems loading it, Emacs
+signals the <code>treesit-load-language-error</code> error.  The data of
+that signal could be one of the following:
+</p>
+<dl compact="compact">
+<dt><span><code>(not-found <var>error-msg</var> &hellip;)</code></span></dt>
+<dd><p>This means that Emacs could not find the language definition library.
+</p></dd>
+<dt><span><code>(symbol-error <var>error-msg</var>)</code></span></dt>
+<dd><p>This means that Emacs could not find in the library the expected 
function
+that every language definition library should export.
+</p></dd>
+<dt><span><code>(version-mismatch <var>error-msg</var>)</code></span></dt>
+<dd><p>This means that the version of language definition library is 
incompatible
+with that of the tree-sitter library.
+</p></dd>
+</dl>
+
+<p>In all of these cases, <var>error-msg</var> might provide additional
+details about the failure.
+</p>
+<dl class="def">
+<dt id="index-treesit_002dlanguage_002davailable_002dp"><span 
class="category">Function: 
</span><span><strong>treesit-language-available-p</strong> <em>language 
&amp;optional detail</em><a 
href='#index-treesit_002dlanguage_002davailable_002dp' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function returns non-<code>nil</code> if the language definitions 
for
+<var>language</var> exist and can be loaded.
+</p>
+<p>If <var>detail</var> is non-<code>nil</code>, return <code>(t . nil)</code> 
when
+<var>language</var> is available, and <code>(nil . <var>data</var>)</code> 
when it&rsquo;s
+unavailable.  <var>data</var> is the signal data of
+<code>treesit-load-language-error</code>.
+</p></dd></dl>
+
+<span id="index-treesit_002dload_002dname_002doverride_002dlist"></span>
+<p>By convention, the file name of the dynamic library for <var>language</var> 
is
+<samp>libtree-sitter-<var>language</var>.<var>ext</var></samp>, where 
<var>ext</var> is the
+system-specific extension for dynamic libraries.  Also by convention,
+the function provided by that library is named
+<code>tree_sitter_<var>language</var></code>.  If a language definition library
+doesn&rsquo;t follow this convention, you should add an entry
+</p>
+<div class="example">
+<pre class="example">(<var>language</var> <var>library-base-name</var> 
<var>function-name</var>)
+</pre></div>
+
+<p>to the list in the variable <code>treesit-load-name-override-list</code>, 
where
+<var>library-base-name</var> is the basename of the dynamic library&rsquo;s 
file name,
+(usually, <samp>libtree-sitter-<var>language</var></samp>), and
+<var>function-name</var> is the function provided by the library
+(usually, <code>tree_sitter_<var>language</var></code>).  For example,
+</p>
+<div class="example">
+<pre class="example">(cool-lang &quot;libtree-sitter-coool&quot; 
&quot;tree_sitter_cooool&quot;)
+</pre></div>
+
+<p>for a language that considers itself too &ldquo;cool&rdquo; to abide by
+conventions.
+</p>
+<span id="index-language_002ddefinition-version_002c-compatibility"></span>
+<dl class="def">
+<dt id="index-treesit_002dlanguage_002dversion"><span 
class="category">Function: 
</span><span><strong>treesit-language-version</strong> <em>&amp;optional 
min-compatible</em><a href='#index-treesit_002dlanguage_002dversion' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This function returns the version of the language-definition
+Application Binary Interface (<acronym>ABI</acronym>) supported by the
+tree-sitter library.  By default, it returns the latest ABI version
+supported by the library, but if <var>min-compatible</var> is
+non-<code>nil</code>, it returns the oldest ABI version which the library
+still can support.  Language definition libraries must be built for
+ABI versions between the oldest and the latest versions supported by
+the tree-sitter library, otherwise the library will be unable to load
+them.
+</p></dd></dl>
+
+<span id="Concrete-syntax-tree"></span><h3 class="heading">Concrete syntax 
tree</h3>
+<span id="index-syntax-tree_002c-concrete"></span>
+
+<p>A syntax tree is what a parser generates.  In a syntax tree, each node
+represents a piece of text, and is connected to each other by a
+parent-child relationship.  For example, if the source text is
+</p>
+<div class="example">
+<pre class="example">1 + 2
+</pre></div>
+
+<p>its syntax tree could be
+</p>
+<div class="example">
+<pre class="example">                  +--------------+
+                  | root &quot;1 + 2&quot; |
+                  +--------------+
+                         |
+        +--------------------------------+
+        |       expression &quot;1 + 2&quot;       |
+        +--------------------------------+
+           |             |            |
++------------+   +--------------+   +------------+
+| number &quot;1&quot; |   | operator &quot;+&quot; |   | number &quot;2&quot; 
|
++------------+   +--------------+   +------------+
+</pre></div>
+
+<p>We can also represent it as an s-expression:
+</p>
+<div class="example">
+<pre class="example">(root (expression (number) (operator) (number)))
+</pre></div>
+
+<span id="Node-types"></span><h4 class="subheading">Node types</h4>
+<span id="index-node-types_002c-in-a-syntax-tree"></span>
+
+<span id="index-type-of-node_002c-tree_002dsitter"></span>
+<span id="tree_002dsitter-node-type"></span><span 
id="index-named-node_002c-tree_002dsitter"></span>
+<span id="tree_002dsitter-named-node"></span><span 
id="index-anonymous-node_002c-tree_002dsitter"></span>
+<p>Names like <code>root</code>, <code>expression</code>, <code>number</code>, 
and
+<code>operator</code> specify the <em>type</em> of the nodes.  However, not all
+nodes in a syntax tree have a type.  Nodes that don&rsquo;t have a type are
+known as <em>anonymous nodes</em>, and nodes with a type are <em>named
+nodes</em>.  Anonymous nodes are tokens with fixed spellings, including
+punctuation characters like bracket &lsquo;<samp>]</samp>&rsquo;, and keywords 
like
+<code>return</code>.
+</p>
+<span id="Field-names"></span><h4 class="subheading">Field names</h4>
+
+<span id="index-field-name_002c-tree_002dsitter"></span>
+<span id="index-tree_002dsitter-node-field-name"></span>
+<span id="tree_002dsitter-node-field-name"></span><p>To make the syntax tree 
easier to analyze, many language definitions
+assign <em>field names</em> to child nodes.  For example, a
+<code>function_definition</code> node could have a <code>declarator</code> and 
a
+<code>body</code>:
+</p>
+<div class="example">
+<pre class="example">(function_definition
+ declarator: (declaration)
+ body: (compound_statement))
+</pre></div>
+
+<span id="Exploring-the-syntax-tree"></span><h3 class="heading">Exploring the 
syntax tree</h3>
+<span id="index-explore-tree_002dsitter-syntax-tree"></span>
+<span id="index-inspection-of-tree_002dsitter-parse-tree-nodes"></span>
+
+<p>To aid in understanding the syntax of a language and in debugging of
+Lisp program that use the syntax tree, Emacs provides an &ldquo;explore&rdquo;
+mode, which displays the syntax tree of the source in the current
+buffer in real time.  Emacs also comes with an &ldquo;inspect mode&rdquo;, 
which
+displays information of the nodes at point in the mode-line.
+</p>
+<dl class="def">
+<dt id="index-treesit_002dexplore_002dmode"><span class="category">Command: 
</span><span><strong>treesit-explore-mode</strong><a 
href='#index-treesit_002dexplore_002dmode' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This mode pops up a window displaying the syntax tree of the source in
+the current buffer.  Selecting text in the source buffer highlights
+the corresponding nodes in the syntax tree display.  Clicking
+on nodes in the syntax tree highlights the corresponding text in the
+source buffer.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dinspect_002dmode"><span class="category">Command: 
</span><span><strong>treesit-inspect-mode</strong><a 
href='#index-treesit_002dinspect_002dmode' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This minor mode displays on the mode-line the node that <em>starts</em>
+at point.  For example, the mode-line can display
+</p>
+<div class="example">
+<pre class="example"><var>parent</var> <var>field</var>: (<var>node</var> 
(<var>child</var> (&hellip;)))
+</pre></div>
+
+<p>where <var>node</var>, <var>child</var>, etc., are nodes which begin at 
point.
+<var>parent</var> is the parent of <var>node</var>.  <var>node</var> is 
displayed in
+a bold typeface.  <var>field-name</var>s are field names of <var>node</var> and
+of <var>child</var>, etc.
+</p>
+<p>If no node starts at point, i.e., point is in the middle of a node,
+then the mode line displays the earliest node that spans point, and
+its immediate parent.
+</p>
+<p>This minor mode doesn&rsquo;t create parsers on its own.  It uses the first
+parser in <code>(treesit-parser-list)</code> (see <a 
href="Using-Parser.html">Using Tree-sitter Parser</a>).
+</p></dd></dl>
+
+<span id="Reading-the-grammar-definition"></span><h3 class="heading">Reading 
the grammar definition</h3>
+<span id="index-reading-grammar-definition_002c-tree_002dsitter"></span>
+
+<p>Authors of language definitions define the <em>grammar</em> of a
+programming language, which determines how a parser constructs a
+concrete syntax tree out of the program text.  In order to use the
+syntax tree effectively, you need to consult the <em>grammar file</em>.
+</p>
+<p>The grammar file is usually <samp>grammar.js</samp> in a language
+definition&rsquo;s project repository.  The link to a language 
definition&rsquo;s
+home page can be found on
+<a href="https://tree-sitter.github.io/tree-sitter";>tree-sitter&rsquo;s
+homepage</a>.
+</p>
+<p>The grammar definition is written in JavaScript.  For example, the
+rule matching a <code>function_definition</code> node looks like
+</p>
+<div class="example">
+<pre class="example">function_definition: $ =&gt; seq(
+  $.declaration_specifiers,
+  field('declarator', $.declaration),
+  field('body', $.compound_statement)
+)
+</pre></div>
+
+<p>The rules are represented by functions that take a single argument
+<var>$</var>, representing the whole grammar.  The function itself is
+constructed by other functions: the <code>seq</code> function puts together
+a sequence of children; the <code>field</code> function annotates a child
+with a field name.  If we write the above definition in the so-called
+<em>Backus-Naur Form</em> (<acronym>BNF</acronym>) syntax, it would look like
+</p>
+<div class="example">
+<pre class="example">function_definition :=
+  &lt;declaration_specifiers&gt; &lt;declaration&gt; &lt;compound_statement&gt;
+</pre></div>
+
+<p>and the node returned by the parser would look like
+</p>
+<div class="example">
+<pre class="example">(function_definition
+  (declaration_specifier)
+  declarator: (declaration)
+  body: (compound_statement))
+</pre></div>
+
+<p>Below is a list of functions that one can see in a grammar definition.
+Each function takes other rules as arguments and returns a new rule.
+</p>
+<dl compact="compact">
+<dt><span><code>seq(<var>rule1</var>, <var>rule2</var>, 
&hellip;)</code></span></dt>
+<dd><p>matches each rule one after another.
+</p></dd>
+<dt><span><code>choice(<var>rule1</var>, <var>rule2</var>, 
&hellip;)</code></span></dt>
+<dd><p>matches one of the rules in its arguments.
+</p></dd>
+<dt><span><code>repeat(<var>rule</var>)</code></span></dt>
+<dd><p>matches <var>rule</var> for <em>zero or more</em> times.
+This is like the &lsquo;<samp>*</samp>&rsquo; operator in regular expressions.
+</p></dd>
+<dt><span><code>repeat1(<var>rule</var>)</code></span></dt>
+<dd><p>matches <var>rule</var> for <em>one or more</em> times.
+This is like the &lsquo;<samp>+</samp>&rsquo; operator in regular expressions.
+</p></dd>
+<dt><span><code>optional(<var>rule</var>)</code></span></dt>
+<dd><p>matches <var>rule</var> for <em>zero or one</em> time.
+This is like the &lsquo;<samp>?</samp>&rsquo; operator in regular expressions.
+</p></dd>
+<dt><span><code>field(<var>name</var>, <var>rule</var>)</code></span></dt>
+<dd><p>assigns field name <var>name</var> to the child node matched by 
<var>rule</var>.
+</p></dd>
+<dt><span><code>alias(<var>rule</var>, <var>alias</var>)</code></span></dt>
+<dd><p>makes nodes matched by <var>rule</var> appear as <var>alias</var> in 
the syntax
+tree generated by the parser.  For example,
+</p>
+<div class="example">
+<pre class="example">alias(preprocessor_call_exp, call_expression)
+</pre></div>
+
+<p>makes any node matched by <code>preprocessor_call_exp</code> appear as
+<code>call_expression</code>.
+</p></dd>
+</dl>
+
+<p>Below are grammar functions of lesser importance for reading a
+language definition.
+</p>
+<dl compact="compact">
+<dt><span><code>token(<var>rule</var>)</code></span></dt>
+<dd><p>marks <var>rule</var> to produce a single leaf node.  That is, instead 
of
+generating a parent node with individual child nodes under it,
+everything is combined into a single leaf node.  See <a 
href="Retrieving-Nodes.html">Retrieving Nodes</a>.
+</p></dd>
+<dt><span><code>token.immediate(<var>rule</var>)</code></span></dt>
+<dd><p>Normally, grammar rules ignore preceding whitespace; this
+changes <var>rule</var> to match only when there is no preceding
+whitespaces.
+</p></dd>
+<dt><span><code>prec(<var>n</var>, <var>rule</var>)</code></span></dt>
+<dd><p>gives <var>rule</var> the level-<var>n</var> precedence.
+</p></dd>
+<dt><span><code>prec.left([<var>n</var>,] <var>rule</var>)</code></span></dt>
+<dd><p>marks <var>rule</var> as left-associative, optionally with level 
<var>n</var>.
+</p></dd>
+<dt><span><code>prec.right([<var>n</var>,] <var>rule</var>)</code></span></dt>
+<dd><p>marks <var>rule</var> as right-associative, optionally with level 
<var>n</var>.
+</p></dd>
+<dt><span><code>prec.dynamic(<var>n</var>, <var>rule</var>)</code></span></dt>
+<dd><p>this is like <code>prec</code>, but the precedence is applied at runtime
+instead.
+</p></dd>
+</dl>
+
+<p>The documentation of the tree-sitter project has
+<a href="https://tree-sitter.github.io/tree-sitter/creating-parsers";>more
+about writing a grammar</a>.  Read especially &ldquo;The Grammar DSL&rdquo;
+section.
+</p>
+</div>
+<hr>
+<div class="header">
+<p>
+Next: <a href="Using-Parser.html">Using Tree-sitter Parser</a>, Up: <a 
href="Parsing-Program-Source.html">Parsing Program Source</a> &nbsp; [<a 
href="index.html#SEC_Contents" title="Table of contents" 
rel="contents">Contents</a>][<a href="Index.html" title="Index" 
rel="index">Index</a>]</p>
+</div>
+
+
+
+</body>
+</html>
diff --git a/admin/notes/tree-sitter/html-manual/Multiple-Languages.html 
b/admin/notes/tree-sitter/html-manual/Multiple-Languages.html
new file mode 100644
index 0000000000..46985649a8
--- /dev/null
+++ b/admin/notes/tree-sitter/html-manual/Multiple-Languages.html
@@ -0,0 +1,328 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd";>
+<html>
+<!-- Created by GNU Texinfo 6.8, https://www.gnu.org/software/texinfo/ -->
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<!-- This is the GNU Emacs Lisp Reference Manual
+corresponding to Emacs version 29.0.50.
+
+Copyright © 1990-1996, 1998-2022 Free Software Foundation,
+Inc.
+
+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 the
+Invariant Sections being "GNU General Public License," 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.  Buying copies from the FSF supports it in
+developing GNU and promoting software freedom." -->
+<title>Multiple Languages (GNU Emacs Lisp Reference Manual)</title>
+
+<meta name="description" content="Multiple Languages (GNU Emacs Lisp Reference 
Manual)">
+<meta name="keywords" content="Multiple Languages (GNU Emacs Lisp Reference 
Manual)">
+<meta name="resource-type" content="document">
+<meta name="distribution" content="global">
+<meta name="Generator" content="makeinfo">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+
+<link href="index.html" rel="start" title="Top">
+<link href="Index.html" rel="index" title="Index">
+<link href="index.html#SEC_Contents" rel="contents" title="Table of Contents">
+<link href="Parsing-Program-Source.html" rel="up" title="Parsing Program 
Source">
+<link href="Tree_002dsitter-major-modes.html" rel="next" title="Tree-sitter 
major modes">
+<link href="Pattern-Matching.html" rel="prev" title="Pattern Matching">
+<style type="text/css">
+<!--
+a.copiable-anchor {visibility: hidden; text-decoration: none; line-height: 0em}
+a.summary-letter {text-decoration: none}
+blockquote.indentedblock {margin-right: 0em}
+div.display {margin-left: 3.2em}
+div.example {margin-left: 3.2em}
+kbd {font-style: oblique}
+pre.display {font-family: inherit}
+pre.format {font-family: inherit}
+pre.menu-comment {font-family: serif}
+pre.menu-preformatted {font-family: serif}
+span.nolinebreak {white-space: nowrap}
+span.roman {font-family: initial; font-weight: normal}
+span.sansserif {font-family: sans-serif; font-weight: normal}
+span:hover a.copiable-anchor {visibility: visible}
+ul.no-bullet {list-style: none}
+-->
+</style>
+<link rel="stylesheet" type="text/css" href="./manual.css">
+
+
+</head>
+
+<body lang="en">
+<div class="section" id="Multiple-Languages">
+<div class="header">
+<p>
+Next: <a href="Tree_002dsitter-major-modes.html" accesskey="n" 
rel="next">Developing major modes with tree-sitter</a>, Previous: <a 
href="Pattern-Matching.html" accesskey="p" rel="prev">Pattern Matching 
Tree-sitter Nodes</a>, Up: <a href="Parsing-Program-Source.html" accesskey="u" 
rel="up">Parsing Program Source</a> &nbsp; [<a href="index.html#SEC_Contents" 
title="Table of contents" rel="contents">Contents</a>][<a href="Index.html" 
title="Index" rel="index">Index</a>]</p>
+</div>
+<hr>
+<span id="Parsing-Text-in-Multiple-Languages"></span><h3 class="section">37.6 
Parsing Text in Multiple Languages</h3>
+<span id="index-multiple-languages_002c-parsing-with-tree_002dsitter"></span>
+<span id="index-parsing-multiple-languages-with-tree_002dsitter"></span>
+<p>Sometimes, the source of a programming language could contain snippets
+of other languages; <acronym>HTML</acronym> + <acronym>CSS</acronym> + 
JavaScript is one
+example.  In that case, text segments written in different languages
+need to be assigned different parsers.  Traditionally, this is
+achieved by using narrowing.  While tree-sitter works with narrowing
+(see <a href="Using-Parser.html#tree_002dsitter-narrowing">narrowing</a>), the 
recommended way is
+instead to set regions of buffer text (i.e., ranges) in which a parser
+will operate.  This section describes functions for setting and
+getting ranges for a parser.
+</p>
+<p>Lisp programs should call <code>treesit-update-ranges</code> to make sure
+the ranges for each parser are correct before using parsers in a
+buffer, and call <code>treesit-language-at</code> to figure out the language
+responsible for the text at some position.  These two functions don&rsquo;t
+work by themselves, they need major modes to set
+<code>treesit-range-settings</code> and
+<code>treesit-language-at-point-function</code>, which do the actual work.
+These functions and variables are explained in more detail towards the
+end of the section.
+</p>
+<span id="Getting-and-setting-ranges"></span><h3 class="heading">Getting and 
setting ranges</h3>
+
+<dl class="def">
+<dt id="index-treesit_002dparser_002dset_002dincluded_002dranges"><span 
class="category">Function: 
</span><span><strong>treesit-parser-set-included-ranges</strong> <em>parser 
ranges</em><a href='#index-treesit_002dparser_002dset_002dincluded_002dranges' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This function sets up <var>parser</var> to operate on 
<var>ranges</var>.  The
+<var>parser</var> will only read the text of the specified ranges.  Each
+range in <var>ranges</var> is a list of the form 
<code>(<var>beg</var>&nbsp;.&nbsp;<var>end</var>)</code><!-- /@w -->.
+</p>
+<p>The ranges in <var>ranges</var> must come in order and must not overlap.
+That is, in pseudo code:
+</p>
+<div class="example">
+<pre class="example">(cl-loop for idx from 1 to (1- (length ranges))
+         for prev = (nth (1- idx) ranges)
+         for next = (nth idx ranges)
+         should (&lt;= (car prev) (cdr prev)
+                    (car next) (cdr next)))
+</pre></div>
+
+<span id="index-treesit_002drange_002dinvalid"></span>
+<p>If <var>ranges</var> violates this constraint, or something else went
+wrong, this function signals the <code>treesit-range-invalid</code> error.
+The signal data contains a specific error message and the ranges we
+are trying to set.
+</p>
+<p>This function can also be used for disabling ranges.  If <var>ranges</var>
+is <code>nil</code>, the parser is set to parse the whole buffer.
+</p>
+<p>Example:
+</p>
+<div class="example">
+<pre class="example">(treesit-parser-set-included-ranges
+ parser '((1 . 9) (16 . 24) (24 . 25)))
+</pre></div>
+</dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dparser_002dincluded_002dranges"><span 
class="category">Function: 
</span><span><strong>treesit-parser-included-ranges</strong> <em>parser</em><a 
href='#index-treesit_002dparser_002dincluded_002dranges' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This function returns the ranges set for <var>parser</var>.  The return
+value is the same as the <var>ranges</var> argument of
+<code>treesit-parser-included-ranges</code>: a list of cons cells of the form
+<code>(<var>beg</var>&nbsp;.&nbsp;<var>end</var>)</code><!-- /@w -->.  If 
<var>parser</var> doesn&rsquo;t have any
+ranges, the return value is <code>nil</code>.
+</p>
+<div class="example">
+<pre class="example">(treesit-parser-included-ranges parser)
+    &rArr; ((1 . 9) (16 . 24) (24 . 25))
+</pre></div>
+</dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dquery_002drange"><span class="category">Function: 
</span><span><strong>treesit-query-range</strong> <em>source query 
&amp;optional beg end</em><a href='#index-treesit_002dquery_002drange' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This function matches <var>source</var> with <var>query</var> and 
returns the
+ranges of captured nodes.  The return value is a list of cons cells of
+the form <code>(<var>beg</var>&nbsp;.&nbsp;<var>end</var>)</code><!-- /@w -->, 
where <var>beg</var> and
+<var>end</var> specify the beginning and the end of a region of text.
+</p>
+<p>For convenience, <var>source</var> can be a language symbol, a parser, or a
+node.  If it&rsquo;s a language symbol, this function matches in the root
+node of the first parser using that language; if a parser, this
+function matches in the root node of that parser; if a node, this
+function matches in that node.
+</p>
+<p>The argument <var>query</var> is the query used to capture nodes
+(see <a href="Pattern-Matching.html">Pattern Matching Tree-sitter Nodes</a>).  
The capture names don&rsquo;t matter.  The
+arguments <var>beg</var> and <var>end</var>, if both non-<code>nil</code>, 
limit the
+range in which this function queries.
+</p>
+<p>Like other query functions, this function raises the
+<code>treesit-query-error</code> error if <var>query</var> is malformed.
+</p></dd></dl>
+
+<span id="Supporting-multiple-languages-in-Lisp-programs"></span><h3 
class="heading">Supporting multiple languages in Lisp programs</h3>
+
+<p>It should suffice for general Lisp programs to call the following two
+functions in order to support program sources that mixes multiple
+languages.
+</p>
+<dl class="def">
+<dt id="index-treesit_002dupdate_002dranges"><span class="category">Function: 
</span><span><strong>treesit-update-ranges</strong> <em>&amp;optional beg 
end</em><a href='#index-treesit_002dupdate_002dranges' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function updates ranges for parsers in the buffer.  It makes sure
+the parsers&rsquo; ranges are set correctly between <var>beg</var> and 
<var>end</var>,
+according to <code>treesit-range-settings</code>.  If omitted, <var>beg</var>
+defaults to the beginning of the buffer, and <var>end</var> defaults to the
+end of the buffer.
+</p>
+<p>For example, fontification functions use this function before querying
+for nodes in a region.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dlanguage_002dat"><span class="category">Function: 
</span><span><strong>treesit-language-at</strong> <em>pos</em><a 
href='#index-treesit_002dlanguage_002dat' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function returns the language of the text at buffer position
+<var>pos</var>.  Under the hood it calls
+<code>treesit-language-at-point-function</code> and returns its return
+value.  If <code>treesit-language-at-point-function</code> is <code>nil</code>,
+this function returns the language of the first parser in the returned
+value of <code>treesit-parser-list</code>.  If there is no parser in the
+buffer, it returns <code>nil</code>.
+</p></dd></dl>
+
+<span id="Supporting-multiple-languages-in-major-modes"></span><h3 
class="heading">Supporting multiple languages in major modes</h3>
+
+<span id="index-host-language_002c-tree_002dsitter"></span>
+<span id="index-tree_002dsitter-host-and-embedded-languages"></span>
+<span id="index-embedded-language_002c-tree_002dsitter"></span>
+<p>Normally, in a set of languages that can be mixed together, there is a
+<em>host language</em> and one or more <em>embedded languages</em>.  A Lisp
+program usually first parses the whole document with the host
+language&rsquo;s parser, retrieves some information, sets ranges for the
+embedded languages with that information, and then parses the embedded
+languages.
+</p>
+<p>Take a buffer containing <acronym>HTML</acronym>, <acronym>CSS</acronym> 
and JavaScript
+as an example.  A Lisp program will first parse the whole buffer with
+an <acronym>HTML</acronym> parser, then query the parser for
+<code>style_element</code> and <code>script_element</code> nodes, which
+correspond to <acronym>CSS</acronym> and JavaScript text, respectively.  Then
+it sets the range of the <acronym>CSS</acronym> and JavaScript parser to the
+ranges in which their corresponding nodes span.
+</p>
+<p>Given a simple <acronym>HTML</acronym> document:
+</p>
+<div class="example">
+<pre class="example">&lt;html&gt;
+  &lt;script&gt;1 + 2&lt;/script&gt;
+  &lt;style&gt;body { color: &quot;blue&quot;; }&lt;/style&gt;
+&lt;/html&gt;
+</pre></div>
+
+<p>a Lisp program will first parse with a <acronym>HTML</acronym> parser, then 
set
+ranges for <acronym>CSS</acronym> and JavaScript parsers:
+</p>
+<div class="example">
+<pre class="example">;; Create parsers.
+(setq html (treesit-get-parser-create 'html))
+(setq css (treesit-get-parser-create 'css))
+(setq js (treesit-get-parser-create 'javascript))
+</pre><pre class="example">
+
+</pre><pre class="example">;; Set CSS ranges.
+(setq css-range
+      (treesit-query-range
+       'html
+       &quot;(style_element (raw_text) @capture)&quot;))
+(treesit-parser-set-included-ranges css css-range)
+</pre><pre class="example">
+
+</pre><pre class="example">;; Set JavaScript ranges.
+(setq js-range
+      (treesit-query-range
+       'html
+       &quot;(script_element (raw_text) @capture)&quot;))
+(treesit-parser-set-included-ranges js js-range)
+</pre></div>
+
+<p>Emacs automates this process in <code>treesit-update-ranges</code>.  A
+multi-language major mode should set <code>treesit-range-settings</code> so
+that <code>treesit-update-ranges</code> knows how to perform this process
+automatically.  Major modes should use the helper function
+<code>treesit-range-rules</code> to generate a value that can be assigned to
+<code>treesit-range-settings</code>.  The settings in the following example
+directly translate into operations shown above.
+</p>
+<div class="example">
+<pre class="example">(setq-local treesit-range-settings
+            (treesit-range-rules
+             :embed 'javascript
+             :host 'html
+             '((script_element (raw_text) @capture))
+</pre><pre class="example">
+
+</pre><pre class="example">             :embed 'css
+             :host 'html
+             '((style_element (raw_text) @capture))))
+</pre></div>
+
+<dl class="def">
+<dt id="index-treesit_002drange_002drules"><span class="category">Function: 
</span><span><strong>treesit-range-rules</strong> <em>&amp;rest 
query-specs</em><a href='#index-treesit_002drange_002drules' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This function is used to set <var>treesit-range-settings</var>.  It
+takes care of compiling queries and other post-processing, and outputs
+a value that <var>treesit-range-settings</var> can have.
+</p>
+<p>It takes a series of <var>query-spec</var>s, where each 
<var>query-spec</var> is
+a <var>query</var> preceded by zero or more <var>keyword</var>/<var>value</var>
+pairs.  Each <var>query</var> is a tree-sitter query in either the
+string, s-expression or compiled form, or a function.
+</p>
+<p>If <var>query</var> is a tree-sitter query, it should be preceded by two
+<var>:keyword</var>/<var>value</var> pairs, where the <code>:embed</code> 
keyword
+specifies the embedded language, and the <code>:host</code> keyword
+specified the host language.
+</p>
+<p><code>treesit-update-ranges</code> uses <var>query</var> to figure out how 
to set
+the ranges for parsers for the embedded language.  It queries
+<var>query</var> in a host language parser, computes the ranges in which
+the captured nodes span, and applies these ranges to embedded
+language parsers.
+</p>
+<p>If <var>query</var> is a function, it doesn&rsquo;t need any 
<var>:keyword</var> and
+<var>value</var> pair.  It should be a function that takes 2 arguments,
+<var>start</var> and <var>end</var>, and sets the ranges for parsers in the
+current buffer in the region between <var>start</var> and <var>end</var>.  It 
is
+fine for this function to set ranges in a larger region that
+encompasses the region between <var>start</var> and <var>end</var>.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002drange_002dsettings"><span class="category">Variable: 
</span><span><strong>treesit-range-settings</strong><a 
href='#index-treesit_002drange_002dsettings' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This variable helps <code>treesit-update-ranges</code> in updating the
+ranges for parsers in the buffer.  It is a list of <var>setting</var>s
+where the exact format of a <var>setting</var> is considered internal.  You
+should use <code>treesit-range-rules</code> to generate a value that this
+variable can have.
+</p>
+</dd></dl>
+
+
+<dl class="def">
+<dt id="index-treesit_002dlanguage_002dat_002dpoint_002dfunction"><span 
class="category">Variable: 
</span><span><strong>treesit-language-at-point-function</strong><a 
href='#index-treesit_002dlanguage_002dat_002dpoint_002dfunction' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This variable&rsquo;s value should be a function that takes a single
+argument, <var>pos</var>, which is a buffer position, and returns the
+language of the buffer text at <var>pos</var>.  This variable is used by
+<code>treesit-language-at</code>.
+</p></dd></dl>
+
+</div>
+<hr>
+<div class="header">
+<p>
+Next: <a href="Tree_002dsitter-major-modes.html">Developing major modes with 
tree-sitter</a>, Previous: <a href="Pattern-Matching.html">Pattern Matching 
Tree-sitter Nodes</a>, Up: <a href="Parsing-Program-Source.html">Parsing 
Program Source</a> &nbsp; [<a href="index.html#SEC_Contents" title="Table of 
contents" rel="contents">Contents</a>][<a href="Index.html" title="Index" 
rel="index">Index</a>]</p>
+</div>
+
+
+
+</body>
+</html>
diff --git 
a/admin/notes/tree-sitter/html-manual/Parser_002dbased-Font-Lock.html 
b/admin/notes/tree-sitter/html-manual/Parser_002dbased-Font-Lock.html
new file mode 100644
index 0000000000..e04a730b05
--- /dev/null
+++ b/admin/notes/tree-sitter/html-manual/Parser_002dbased-Font-Lock.html
@@ -0,0 +1,248 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd";>
+<html>
+<!-- Created by GNU Texinfo 6.8, https://www.gnu.org/software/texinfo/ -->
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<!-- This is the GNU Emacs Lisp Reference Manual
+corresponding to Emacs version 29.0.50.
+
+Copyright © 1990-1996, 1998-2022 Free Software Foundation,
+Inc.
+
+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 the
+Invariant Sections being "GNU General Public License," 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.  Buying copies from the FSF supports it in
+developing GNU and promoting software freedom." -->
+<title>Parser-based Font Lock (GNU Emacs Lisp Reference Manual)</title>
+
+<meta name="description" content="Parser-based Font Lock (GNU Emacs Lisp 
Reference Manual)">
+<meta name="keywords" content="Parser-based Font Lock (GNU Emacs Lisp 
Reference Manual)">
+<meta name="resource-type" content="document">
+<meta name="distribution" content="global">
+<meta name="Generator" content="makeinfo">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+
+<link href="index.html" rel="start" title="Top">
+<link href="Index.html" rel="index" title="Index">
+<link href="index.html#SEC_Contents" rel="contents" title="Table of Contents">
+<link href="Font-Lock-Mode.html" rel="up" title="Font Lock Mode">
+<link href="Multiline-Font-Lock.html" rel="prev" title="Multiline Font Lock">
+<style type="text/css">
+<!--
+a.copiable-anchor {visibility: hidden; text-decoration: none; line-height: 0em}
+a.summary-letter {text-decoration: none}
+blockquote.indentedblock {margin-right: 0em}
+div.display {margin-left: 3.2em}
+div.example {margin-left: 3.2em}
+kbd {font-style: oblique}
+pre.display {font-family: inherit}
+pre.format {font-family: inherit}
+pre.menu-comment {font-family: serif}
+pre.menu-preformatted {font-family: serif}
+span.nolinebreak {white-space: nowrap}
+span.roman {font-family: initial; font-weight: normal}
+span.sansserif {font-family: sans-serif; font-weight: normal}
+span:hover a.copiable-anchor {visibility: visible}
+ul.no-bullet {list-style: none}
+-->
+</style>
+<link rel="stylesheet" type="text/css" href="./manual.css">
+
+
+</head>
+
+<body lang="en">
+<div class="subsection" id="Parser_002dbased-Font-Lock">
+<div class="header">
+<p>
+Previous: <a href="Multiline-Font-Lock.html" accesskey="p" 
rel="prev">Multiline Font Lock Constructs</a>, Up: <a 
href="Font-Lock-Mode.html" accesskey="u" rel="up">Font Lock Mode</a> &nbsp; [<a 
href="index.html#SEC_Contents" title="Table of contents" 
rel="contents">Contents</a>][<a href="Index.html" title="Index" 
rel="index">Index</a>]</p>
+</div>
+<hr>
+<span id="Parser_002dbased-Font-Lock-1"></span><h4 class="subsection">24.6.10 
Parser-based Font Lock</h4>
+<span id="index-parser_002dbased-font_002dlock"></span>
+
+
+<p>Besides simple syntactic font lock and regexp-based font lock, Emacs
+also provides complete syntactic font lock with the help of a parser.
+Currently, Emacs uses the tree-sitter library (see <a 
href="Parsing-Program-Source.html">Parsing Program Source</a>) for this purpose.
+</p>
+<p>Parser-based font lock and other font lock mechanisms are not mutually
+exclusive.  By default, if enabled, parser-based font lock runs first,
+replacing syntactic font lock, then the regexp-based font lock.
+</p>
+<p>Although parser-based font lock doesn&rsquo;t share the same customization
+variables with regexp-based font lock, it uses similar customization
+schemes.  The tree-sitter counterpart of <var>font-lock-keywords</var> is
+<var>treesit-font-lock-settings</var>.
+</p>
+<span id="index-tree_002dsitter-fontifications_002c-overview"></span>
+<span id="index-fontifications-with-tree_002dsitter_002c-overview"></span>
+<p>In general, tree-sitter fontification works as follows:
+</p>
+<ul>
+<li> A Lisp program (usually, part of a major mode) provides a <em>query</em>
+consisting of <em>patterns</em>, each pattern associated with a
+<em>capture name</em>.
+
+</li><li> The tree-sitter library finds the nodes in the parse tree
+that match these patterns, tags the nodes with the corresponding
+capture names, and returns them to the Lisp program.
+
+</li><li> The Lisp program uses the returned nodes to highlight the portions of
+buffer text corresponding to each node as appropriate, using the
+tagged capture names of the nodes to determine the correct
+fontification.  For example, a node tagged <code>font-lock-keyword</code>
+would be highlighted in <code>font-lock-keyword</code> face.
+</li></ul>
+
+<p>For more information about queries, patterns, and capture names, see
+<a href="Pattern-Matching.html">Pattern Matching Tree-sitter Nodes</a>.
+</p>
+<p>To setup tree-sitter fontification, a major mode should first set
+<code>treesit-font-lock-settings</code> with the output of
+<code>treesit-font-lock-rules</code>, then call
+<code>treesit-major-mode-setup</code>.
+</p>
+<dl class="def">
+<dt id="index-treesit_002dfont_002dlock_002drules"><span 
class="category">Function: 
</span><span><strong>treesit-font-lock-rules</strong> <em>&amp;rest 
query-specs</em><a href='#index-treesit_002dfont_002dlock_002drules' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This function is used to set <var>treesit-font-lock-settings</var>.  It
+takes care of compiling queries and other post-processing, and outputs
+a value that <var>treesit-font-lock-settings</var> accepts.  Here&rsquo;s an
+example:
+</p>
+<div class="example">
+<pre class="example">(treesit-font-lock-rules
+ :language 'javascript
+ :feature 'constant
+ :override t
+ '((true) @font-lock-constant-face
+   (false) @font-lock-constant-face)
+ :language 'html
+ :feature 'script
+ &quot;(script_element) @font-lock-builtin-face&quot;)
+</pre></div>
+
+<p>This function takes a series of <var>query-spec</var>s, where each
+<var>query-spec</var> is a <var>query</var> preceded by one or more
+<var>:keyword</var>/<var>value</var> pairs.  Each <var>query</var> is a
+tree-sitter query in either the string, s-expression or compiled form.
+</p>
+<p>For each <var>query</var>, the <var>:keyword</var>/<var>value</var> pairs 
that
+precede it add meta information to it.  The <code>:lang</code> keyword
+declares <var>query</var>&rsquo;s language.  The <code>:feature</code> keyword 
sets the
+feature name of <var>query</var>.  Users can control which features are
+enabled with <code>font-lock-maximum-decoration</code> and
+<code>treesit-font-lock-feature-list</code> (described below).  These two
+keywords are mandatory.
+</p>
+<p>Other keywords are optional:
+</p>
+<table>
+<thead><tr><th width="15%">Keyword</th><th width="15%">Value</th><th 
width="60%">Description</th></tr></thead>
+<tr><td width="15%"><code>:override</code></td><td width="15%">nil</td><td 
width="60%">If the region already has a face, discard the new face</td></tr>
+<tr><td width="15%"></td><td width="15%">t</td><td width="60%">Always apply 
the new face</td></tr>
+<tr><td width="15%"></td><td width="15%"><code>append</code></td><td 
width="60%">Append the new face to existing ones</td></tr>
+<tr><td width="15%"></td><td width="15%"><code>prepend</code></td><td 
width="60%">Prepend the new face to existing ones</td></tr>
+<tr><td width="15%"></td><td width="15%"><code>keep</code></td><td 
width="60%">Fill-in regions without an existing face</td></tr>
+</table>
+
+<p>Lisp programs mark patterns in <var>query</var> with capture names (names
+that starts with <code>@</code>), and tree-sitter will return matched nodes
+tagged with those same capture names.  For the purpose of
+fontification, capture names in <var>query</var> should be face names like
+<code>font-lock-keyword-face</code>.  The captured node will be fontified
+with that face.
+</p>
+<span id="index-treesit_002dfontify_002dwith_002doverride"></span>
+<p>Capture names can also be function names, in which case the function
+is called with 4 arguments: <var>node</var> and <var>override</var>, 
<var>start</var>
+and <var>end</var>, where <var>node</var> is the node itself, 
<var>override</var> is
+the override property of the rule which captured this node, and
+<var>start</var> and <var>end</var> limits the region in which this function
+should fontify.  (If this function wants to respect the <var>override</var>
+argument, it can use <code>treesit-fontify-with-override</code>.)
+</p>
+<p>Beyond the 4 arguments presented, this function should accept more
+arguments as optional arguments for future extensibility.
+</p>
+<p>If a capture name is both a face and a function, the face takes
+priority.  If a capture name is neither a face nor a function, it is
+ignored.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dfont_002dlock_002dfeature_002dlist"><span 
class="category">Variable: 
</span><span><strong>treesit-font-lock-feature-list</strong><a 
href='#index-treesit_002dfont_002dlock_002dfeature_002dlist' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This is a list of lists of feature symbols.  Each element of the list
+is a list that represents a decoration level.
+<code>font-lock-maximum-decoration</code> controls which levels are
+activated.
+</p>
+<p>Each element of the list is a list of the form 
<code>(<var>feature</var>&nbsp;&hellip;)</code><!-- /@w -->, where each 
<var>feature</var> corresponds to the
+<code>:feature</code> value of a query defined in
+<code>treesit-font-lock-rules</code>.  Removing a feature symbol from this
+list disables the corresponding query during font-lock.
+</p>
+<p>Common feature names, for many programming languages, include
+<code>definition</code>, <code>type</code>, <code>assignment</code>, 
<code>builtin</code>,
+<code>constant</code>, <code>keyword</code>, <code>string-interpolation</code>,
+<code>comment</code>, <code>doc</code>, <code>string</code>, 
<code>operator</code>,
+<code>preprocessor</code>, <code>escape-sequence</code>, and <code>key</code>. 
 Major
+modes are free to subdivide or extend these common features.
+</p>
+<p>Some of these features warrant some explanation: <code>definition</code>
+highlights whatever is being defined, e.g., the function name in a
+function definition, the struct name in a struct definition, the
+variable name in a variable definition; <code>assignment</code> highlights
+the whatever is being assigned to, e.g., the variable or field in an
+assignment statement; <code>key</code> highlights keys in key-value pairs,
+e.g., keys in a JSON object, or a Python dictionary; <code>doc</code>
+highlights docstrings or doc-comments.
+</p>
+<p>For example, the value of this variable could be:
+</p><div class="example">
+<pre class="example">((comment string doc) ; level 1
+ (function-name keyword type builtin constant) ; level 2
+ (variable-name string-interpolation key)) ; level 3
+</pre></div>
+
+<p>Major modes should set this variable before calling
+<code>treesit-major-mode-setup</code>.
+</p>
+<span id="index-treesit_002dfont_002dlock_002drecompute_002dfeatures"></span>
+<p>For this variable to take effect, a Lisp program should call
+<code>treesit-font-lock-recompute-features</code> (which resets
+<code>treesit-font-lock-settings</code> accordingly), or
+<code>treesit-major-mode-setup</code> (which calls
+<code>treesit-font-lock-recompute-features</code>).
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dfont_002dlock_002dsettings"><span 
class="category">Variable: 
</span><span><strong>treesit-font-lock-settings</strong><a 
href='#index-treesit_002dfont_002dlock_002dsettings' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>A list of settings for tree-sitter based font lock.  The exact format
+of each setting is considered internal.  One should always use
+<code>treesit-font-lock-rules</code> to set this variable.
+</p>
+</dd></dl>
+
+<p>Multi-language major modes should provide range functions in
+<code>treesit-range-functions</code>, and Emacs will set the ranges
+accordingly before fontifing a region (see <a 
href="Multiple-Languages.html">Parsing Text in Multiple Languages</a>).
+</p>
+</div>
+<hr>
+<div class="header">
+<p>
+Previous: <a href="Multiline-Font-Lock.html">Multiline Font Lock 
Constructs</a>, Up: <a href="Font-Lock-Mode.html">Font Lock Mode</a> &nbsp; [<a 
href="index.html#SEC_Contents" title="Table of contents" 
rel="contents">Contents</a>][<a href="Index.html" title="Index" 
rel="index">Index</a>]</p>
+</div>
+
+
+
+</body>
+</html>
diff --git 
a/admin/notes/tree-sitter/html-manual/Parser_002dbased-Indentation.html 
b/admin/notes/tree-sitter/html-manual/Parser_002dbased-Indentation.html
new file mode 100644
index 0000000000..95005de6d1
--- /dev/null
+++ b/admin/notes/tree-sitter/html-manual/Parser_002dbased-Indentation.html
@@ -0,0 +1,281 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd";>
+<html>
+<!-- Created by GNU Texinfo 6.8, https://www.gnu.org/software/texinfo/ -->
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<!-- This is the GNU Emacs Lisp Reference Manual
+corresponding to Emacs version 29.0.50.
+
+Copyright © 1990-1996, 1998-2022 Free Software Foundation,
+Inc.
+
+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 the
+Invariant Sections being "GNU General Public License," 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.  Buying copies from the FSF supports it in
+developing GNU and promoting software freedom." -->
+<title>Parser-based Indentation (GNU Emacs Lisp Reference Manual)</title>
+
+<meta name="description" content="Parser-based Indentation (GNU Emacs Lisp 
Reference Manual)">
+<meta name="keywords" content="Parser-based Indentation (GNU Emacs Lisp 
Reference Manual)">
+<meta name="resource-type" content="document">
+<meta name="distribution" content="global">
+<meta name="Generator" content="makeinfo">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+
+<link href="index.html" rel="start" title="Top">
+<link href="Index.html" rel="index" title="Index">
+<link href="index.html#SEC_Contents" rel="contents" title="Table of Contents">
+<link href="Auto_002dIndentation.html" rel="up" title="Auto-Indentation">
+<link href="SMIE.html" rel="prev" title="SMIE">
+<style type="text/css">
+<!--
+a.copiable-anchor {visibility: hidden; text-decoration: none; line-height: 0em}
+a.summary-letter {text-decoration: none}
+blockquote.indentedblock {margin-right: 0em}
+div.display {margin-left: 3.2em}
+div.example {margin-left: 3.2em}
+kbd {font-style: oblique}
+pre.display {font-family: inherit}
+pre.format {font-family: inherit}
+pre.menu-comment {font-family: serif}
+pre.menu-preformatted {font-family: serif}
+span.nolinebreak {white-space: nowrap}
+span.roman {font-family: initial; font-weight: normal}
+span.sansserif {font-family: sans-serif; font-weight: normal}
+span:hover a.copiable-anchor {visibility: visible}
+ul.no-bullet {list-style: none}
+-->
+</style>
+<link rel="stylesheet" type="text/css" href="./manual.css">
+
+
+</head>
+
+<body lang="en">
+<div class="subsection" id="Parser_002dbased-Indentation">
+<div class="header">
+<p>
+Previous: <a href="SMIE.html" accesskey="p" rel="prev">Simple Minded 
Indentation Engine</a>, Up: <a href="Auto_002dIndentation.html" accesskey="u" 
rel="up">Automatic Indentation of code</a> &nbsp; [<a 
href="index.html#SEC_Contents" title="Table of contents" 
rel="contents">Contents</a>][<a href="Index.html" title="Index" 
rel="index">Index</a>]</p>
+</div>
+<hr>
+<span id="Parser_002dbased-Indentation-1"></span><h4 class="subsection">24.7.2 
Parser-based Indentation</h4>
+<span id="index-parser_002dbased-indentation"></span>
+
+
+<p>When built with the tree-sitter library (see <a 
href="Parsing-Program-Source.html">Parsing Program Source</a>), Emacs is 
capable of parsing the program source and producing
+a syntax tree.  This syntax tree can be used for guiding the program
+source indentation commands.  For maximum flexibility, it is possible
+to write a custom indentation function that queries the syntax tree
+and indents accordingly for each language, but that is a lot of work.
+It is more convenient to use the simple indentation engine described
+below: then the major mode needs only to write some indentation rules
+and the engine takes care of the rest.
+</p>
+<p>To enable the parser-based indentation engine, either set
+<var>treesit-simple-indent-rules</var> and call
+<code>treesit-major-mode-setup</code>, or equivalently, set the value of
+<code>indent-line-function</code> to <code>treesit-indent</code>.
+</p>
+<dl class="def">
+<dt id="index-treesit_002dindent_002dfunction"><span 
class="category">Variable: 
</span><span><strong>treesit-indent-function</strong><a 
href='#index-treesit_002dindent_002dfunction' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This variable stores the actual function called by
+<code>treesit-indent</code>.  By default, its value is
+<code>treesit-simple-indent</code>.  In the future we might add other,
+more complex indentation engines.
+</p></dd></dl>
+
+<span id="Writing-indentation-rules"></span><h3 class="heading">Writing 
indentation rules</h3>
+<span 
id="index-indentation-rules_002c-for-parser_002dbased-indentation"></span>
+
+<dl class="def">
+<dt id="index-treesit_002dsimple_002dindent_002drules"><span 
class="category">Variable: 
</span><span><strong>treesit-simple-indent-rules</strong><a 
href='#index-treesit_002dsimple_002dindent_002drules' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This local variable stores indentation rules for every language.  It is
+a list of the form: 
<code>(<var>language</var>&nbsp;.&nbsp;<var>rules</var>)</code><!-- /@w -->, 
where
+<var>language</var> is a language symbol, and <var>rules</var> is a list of the
+form 
<code>(<var>matcher</var>&nbsp;<var>anchor</var>&nbsp;<var>offset</var>)</code><!--
 /@w -->.
+</p>
+<p>First, Emacs passes the smallest tree-sitter node at the beginning of
+the current line to <var>matcher</var>; if it returns non-<code>nil</code>, 
this
+rule is applicable.  Then Emacs passes the node to <var>anchor</var>, which
+returns a buffer position.  Emacs takes the column number of that
+position, adds <var>offset</var> to it, and the result is the indentation
+column for the current line.  <var>offset</var> can be an integer or a
+variable whose value is an integer.
+</p>
+<p>The <var>matcher</var> and <var>anchor</var> are functions, and Emacs 
provides
+convenient defaults for them.
+</p>
+<p>Each <var>matcher</var> or <var>anchor</var> is a function that takes three
+arguments: <var>node</var>, <var>parent</var>, and <var>bol</var>.  The 
argument
+<var>bol</var> is the buffer position whose indentation is required: the
+position of the first non-whitespace character after the beginning of
+the line.  The argument <var>node</var> is the largest (highest-in-tree)
+node that starts at that position; and <var>parent</var> is the parent of
+<var>node</var>.  However, when that position is in a whitespace or inside
+a multi-line string, no node can start at that position, so
+<var>node</var> is <code>nil</code>.  In that case, <var>parent</var> would be 
the
+smallest node that spans that position.
+</p>
+<p>Emacs finds <var>bol</var>, <var>node</var> and <var>parent</var> and
+passes them to each <var>matcher</var> and <var>anchor</var>.  
<var>matcher</var>
+should return non-<code>nil</code> if the rule is applicable, and
+<var>anchor</var> should return a buffer position.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dsimple_002dindent_002dpresets"><span 
class="category">Variable: 
</span><span><strong>treesit-simple-indent-presets</strong><a 
href='#index-treesit_002dsimple_002dindent_002dpresets' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This is a list of defaults for <var>matcher</var>s and 
<var>anchor</var>s in
+<code>treesit-simple-indent-rules</code>.  Each of them represents a function
+that takes 3 arguments: <var>node</var>, <var>parent</var> and <var>bol</var>. 
 The
+available default functions are:
+</p>
+<dl compact="compact">
+<dt id='index-no_002dnode'><span><code>no-node</code><a 
href='#index-no_002dnode' class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This matcher is a function that is called with 3 arguments:
+<var>node</var>, <var>parent</var>, and <var>bol</var>, and returns 
non-<code>nil</code>,
+indicating a match, if <var>node</var> is <code>nil</code>, i.e., there is no
+node that starts at <var>bol</var>.  This is the case when <var>bol</var> is on
+an empty line or inside a multi-line string, etc.
+</p>
+</dd>
+<dt id='index-parent_002dis'><span><code>parent-is</code><a 
href='#index-parent_002dis' class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This matcher is a function of one argument, <var>type</var>; it returns 
a
+function that is called with 3 arguments: <var>node</var>, <var>parent</var>,
+and <var>bol</var>, and returns non-<code>nil</code> (i.e., a match) if
+<var>parent</var>&rsquo;s type matches regexp <var>type</var>.
+</p>
+</dd>
+<dt id='index-node_002dis'><span><code>node-is</code><a 
href='#index-node_002dis' class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This matcher is a function of one argument, <var>type</var>; it returns 
a
+function that is called with 3 arguments: <var>node</var>, <var>parent</var>,
+and <var>bol</var>, and returns non-<code>nil</code> if 
<var>node</var>&rsquo;s type matches
+regexp <var>type</var>.
+</p>
+</dd>
+<dt id='index-query'><span><code>query</code><a href='#index-query' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This matcher is a function of one argument, <var>query</var>; it 
returns a
+function that is called with 3 arguments: <var>node</var>, <var>parent</var>,
+and <var>bol</var>, and returns non-<code>nil</code> if querying 
<var>parent</var>
+with <var>query</var> captures <var>node</var> (see <a 
href="Pattern-Matching.html">Pattern Matching Tree-sitter Nodes</a>).
+</p>
+</dd>
+<dt id='index-match'><span><code>match</code><a href='#index-match' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This matcher is a function of 5 arguments: <var>node-type</var>,
+<var>parent-type</var>, <var>node-field</var>, <var>node-index-min</var>, and
+<var>node-index-max</var>).  It returns a function that is called with 3
+arguments: <var>node</var>, <var>parent</var>, and <var>bol</var>, and returns
+non-<code>nil</code> if <var>node</var>&rsquo;s type matches regexp 
<var>node-type</var>,
+<var>parent</var>&rsquo;s type matches regexp <var>parent-type</var>, 
<var>node</var>&rsquo;s
+field name in <var>parent</var> matches regexp <var>node-field</var>, and
+<var>node</var>&rsquo;s index among its siblings is between 
<var>node-index-min</var>
+and <var>node-index-max</var>.  If the value of an argument is 
<code>nil</code>,
+this matcher doesn&rsquo;t check that argument.  For example, to match the
+first child where parent is <code>argument_list</code>, use
+</p>
+<div class="example">
+<pre class="example">(match nil &quot;argument_list&quot; nil nil 0 0)
+</pre></div>
+
+</dd>
+<dt id='index-comment_002dend'><span><code>comment-end</code><a 
href='#index-comment_002dend' class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This matcher is a function that is called with 3 arguments:
+<var>node</var>, <var>parent</var>, and <var>bol</var>, and returns 
non-<code>nil</code> if
+point is before a comment ending token.  Comment ending tokens are
+defined by regular expression <code>treesit-comment-end</code>
+(see <a href="Tree_002dsitter-major-modes.html">treesit-comment-end</a>).
+</p>
+</dd>
+<dt id='index-first_002dsibling'><span><code>first-sibling</code><a 
href='#index-first_002dsibling' class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This anchor is a function that is called with 3 arguments: 
<var>node</var>,
+<var>parent</var>, and <var>bol</var>, and returns the start of the first child
+of <var>parent</var>.
+</p>
+</dd>
+<dt id='index-parent'><span><code>parent</code><a href='#index-parent' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This anchor is a function that is called with 3 arguments: 
<var>node</var>,
+<var>parent</var>, and <var>bol</var>, and returns the start of 
<var>parent</var>.
+</p>
+</dd>
+<dt id='index-parent_002dbol'><span><code>parent-bol</code><a 
href='#index-parent_002dbol' class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This anchor is a function that is called with 3 arguments: 
<var>node</var>,
+<var>parent</var>, and <var>bol</var>, and returns the first non-space 
character
+on the line of <var>parent</var>.
+</p>
+</dd>
+<dt id='index-prev_002dsibling'><span><code>prev-sibling</code><a 
href='#index-prev_002dsibling' class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This anchor is a function that is called with 3 arguments: 
<var>node</var>,
+<var>parent</var>, and <var>bol</var>, and returns the start of the previous
+sibling of <var>node</var>.
+</p>
+</dd>
+<dt id='index-no_002dindent'><span><code>no-indent</code><a 
href='#index-no_002dindent' class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This anchor is a function that is called with 3 arguments: 
<var>node</var>,
+<var>parent</var>, and <var>bol</var>, and returns the start of 
<var>node</var>.
+</p>
+</dd>
+<dt id='index-prev_002dline'><span><code>prev-line</code><a 
href='#index-prev_002dline' class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This anchor is a function that is called with 3 arguments: 
<var>node</var>,
+<var>parent</var>, and <var>bol</var>, and returns the first non-whitespace
+character on the previous line.
+</p>
+</dd>
+<dt id='index-point_002dmin'><span><code>point-min</code><a 
href='#index-point_002dmin' class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This anchor is a function that is called with 3 arguments: 
<var>node</var>,
+<var>parent</var>, and <var>bol</var>, and returns the beginning of the buffer.
+This is useful as the beginning of the buffer is always at column 0.
+</p>
+</dd>
+<dt id='index-comment_002dstart'><span><code>comment-start</code><a 
href='#index-comment_002dstart' class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This anchor is a function that is called with 3 arguments: 
<var>node</var>,
+<var>parent</var>, and <var>bol</var>, and returns the position right after the
+comment-start token.  Comment-start tokens are defined by regular
+expression <code>treesit-comment-start</code> (see <a 
href="Tree_002dsitter-major-modes.html">treesit-comment-start</a>).  This 
function assumes <var>parent</var> is
+the comment node.
+</p>
+</dd>
+<dt 
id='index-coment_002dstart_002dskip'><span><code>coment-start-skip</code><a 
href='#index-coment_002dstart_002dskip' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This anchor is a function that is called with 3 arguments: 
<var>node</var>,
+<var>parent</var>, and <var>bol</var>, and returns the position after the
+comment-start token and any whitespace characters following that
+token.  Comment-start tokens are defined by regular expression
+<code>treesit-comment-start</code>.  This function assumes <var>parent</var> is
+the comment node.
+</p></dd>
+</dl>
+</dd></dl>
+
+<span id="Indentation-utilities"></span><h3 class="heading">Indentation 
utilities</h3>
+<span id="index-utility-functions-for-parser_002dbased-indentation"></span>
+
+<p>Here are some utility functions that can help writing parser-based
+indentation rules.
+</p>
+<dl class="def">
+<dt id="index-treesit_002dcheck_002dindent"><span class="category">Function: 
</span><span><strong>treesit-check-indent</strong> <em>mode</em><a 
href='#index-treesit_002dcheck_002dindent' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function checks the current buffer&rsquo;s indentation against 
major
+mode <var>mode</var>.  It indents the current buffer according to
+<var>mode</var> and compares the results with the current indentation.
+Then it pops up a buffer showing the differences.  Correct
+indentation (target) is shown in green color, current indentation is
+shown in red color.  </p></dd></dl>
+
+<p>It is also helpful to use <code>treesit-inspect-mode</code> (see <a 
href="Language-Definitions.html">Tree-sitter Language Definitions</a>) when 
writing indentation rules.
+</p>
+</div>
+<hr>
+<div class="header">
+<p>
+Previous: <a href="SMIE.html">Simple Minded Indentation Engine</a>, Up: <a 
href="Auto_002dIndentation.html">Automatic Indentation of code</a> &nbsp; [<a 
href="index.html#SEC_Contents" title="Table of contents" 
rel="contents">Contents</a>][<a href="Index.html" title="Index" 
rel="index">Index</a>]</p>
+</div>
+
+
+
+</body>
+</html>
diff --git a/admin/notes/tree-sitter/html-manual/Parsing-Program-Source.html 
b/admin/notes/tree-sitter/html-manual/Parsing-Program-Source.html
new file mode 100644
index 0000000000..a0b5775f11
--- /dev/null
+++ b/admin/notes/tree-sitter/html-manual/Parsing-Program-Source.html
@@ -0,0 +1,126 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd";>
+<html>
+<!-- Created by GNU Texinfo 6.8, https://www.gnu.org/software/texinfo/ -->
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<!-- This is the GNU Emacs Lisp Reference Manual
+corresponding to Emacs version 29.0.50.
+
+Copyright © 1990-1996, 1998-2022 Free Software Foundation,
+Inc.
+
+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 the
+Invariant Sections being "GNU General Public License," 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.  Buying copies from the FSF supports it in
+developing GNU and promoting software freedom." -->
+<title>Parsing Program Source (GNU Emacs Lisp Reference Manual)</title>
+
+<meta name="description" content="Parsing Program Source (GNU Emacs Lisp 
Reference Manual)">
+<meta name="keywords" content="Parsing Program Source (GNU Emacs Lisp 
Reference Manual)">
+<meta name="resource-type" content="document">
+<meta name="distribution" content="global">
+<meta name="Generator" content="makeinfo">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+
+<link href="index.html" rel="start" title="Top">
+<link href="Index.html" rel="index" title="Index">
+<link href="index.html#SEC_Contents" rel="contents" title="Table of Contents">
+<link href="index.html" rel="up" title="Top">
+<link href="Abbrevs.html" rel="next" title="Abbrevs">
+<link href="Syntax-Tables.html" rel="prev" title="Syntax Tables">
+<style type="text/css">
+<!--
+a.copiable-anchor {visibility: hidden; text-decoration: none; line-height: 0em}
+a.summary-letter {text-decoration: none}
+blockquote.indentedblock {margin-right: 0em}
+div.display {margin-left: 3.2em}
+div.example {margin-left: 3.2em}
+kbd {font-style: oblique}
+pre.display {font-family: inherit}
+pre.format {font-family: inherit}
+pre.menu-comment {font-family: serif}
+pre.menu-preformatted {font-family: serif}
+span.nolinebreak {white-space: nowrap}
+span.roman {font-family: initial; font-weight: normal}
+span.sansserif {font-family: sans-serif; font-weight: normal}
+span:hover a.copiable-anchor {visibility: visible}
+ul.no-bullet {list-style: none}
+-->
+</style>
+<link rel="stylesheet" type="text/css" href="./manual.css">
+
+
+</head>
+
+<body lang="en">
+<div class="chapter" id="Parsing-Program-Source">
+<div class="header">
+<p>
+Next: <a href="Abbrevs.html" accesskey="n" rel="next">Abbrevs and Abbrev 
Expansion</a>, Previous: <a href="Syntax-Tables.html" accesskey="p" 
rel="prev">Syntax Tables</a>, Up: <a href="index.html" accesskey="u" 
rel="up">Emacs Lisp</a> &nbsp; [<a href="index.html#SEC_Contents" title="Table 
of contents" rel="contents">Contents</a>][<a href="Index.html" title="Index" 
rel="index">Index</a>]</p>
+</div>
+<hr>
+<span id="Parsing-Program-Source-1"></span><h2 class="chapter">37 Parsing 
Program Source</h2>
+
+<span id="index-syntax-tree_002c-from-parsing-program-source"></span>
+<p>Emacs provides various ways to parse program source text and produce a
+<em>syntax tree</em>.  In a syntax tree, text is no longer considered a
+one-dimensional stream of characters, but a structured tree of nodes,
+where each node representing a piece of text.  Thus, a syntax tree can
+enable interesting features like precise fontification, indentation,
+navigation, structured editing, etc.
+</p>
+<p>Emacs has a simple facility for parsing balanced expressions
+(see <a href="Parsing-Expressions.html">Parsing Expressions</a>).  There is 
also the SMIE library for
+generic navigation and indentation (see <a href="SMIE.html">Simple Minded 
Indentation Engine</a>).
+</p>
+<p>In addition to those, Emacs also provides integration with
+<a href="https://tree-sitter.github.io/tree-sitter";>the tree-sitter
+library</a>) if support for it was compiled in.  The tree-sitter library
+implements an incremental parser and has support from a wide range of
+programming languages.
+</p>
+<dl class="def">
+<dt id="index-treesit_002davailable_002dp"><span class="category">Function: 
</span><span><strong>treesit-available-p</strong><a 
href='#index-treesit_002davailable_002dp' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function returns non-<code>nil</code> if tree-sitter features are
+available for the current Emacs session.
+</p></dd></dl>
+
+<p>To be able to parse the program source using the tree-sitter library
+and access the syntax tree of the program, a Lisp program needs to
+load a language definition library, and create a parser for that
+language and the current buffer.  After that, the Lisp program can
+query the parser about specific nodes of the syntax tree.  Then, it
+can access various kinds of information about each node, and search
+for nodes using a powerful pattern-matching syntax.  This chapter
+explains how to do all this, and also how a Lisp program can work with
+source files that mix multiple programming languages.
+</p>
+
+<ul class="section-toc">
+<li><a href="Language-Definitions.html" accesskey="1">Tree-sitter Language 
Definitions</a></li>
+<li><a href="Using-Parser.html" accesskey="2">Using Tree-sitter Parser</a></li>
+<li><a href="Retrieving-Nodes.html" accesskey="3">Retrieving Nodes</a></li>
+<li><a href="Accessing-Node-Information.html" accesskey="4">Accessing Node 
Information</a></li>
+<li><a href="Pattern-Matching.html" accesskey="5">Pattern Matching Tree-sitter 
Nodes</a></li>
+<li><a href="Multiple-Languages.html" accesskey="6">Parsing Text in Multiple 
Languages</a></li>
+<li><a href="Tree_002dsitter-major-modes.html" accesskey="7">Developing major 
modes with tree-sitter</a></li>
+<li><a href="Tree_002dsitter-C-API.html" accesskey="8">Tree-sitter C API 
Correspondence</a></li>
+</ul>
+</div>
+<hr>
+<div class="header">
+<p>
+Next: <a href="Abbrevs.html">Abbrevs and Abbrev Expansion</a>, Previous: <a 
href="Syntax-Tables.html">Syntax Tables</a>, Up: <a href="index.html">Emacs 
Lisp</a> &nbsp; [<a href="index.html#SEC_Contents" title="Table of contents" 
rel="contents">Contents</a>][<a href="Index.html" title="Index" 
rel="index">Index</a>]</p>
+</div>
+
+
+
+</body>
+</html>
diff --git a/admin/notes/tree-sitter/html-manual/Pattern-Matching.html 
b/admin/notes/tree-sitter/html-manual/Pattern-Matching.html
new file mode 100644
index 0000000000..21eb4702b1
--- /dev/null
+++ b/admin/notes/tree-sitter/html-manual/Pattern-Matching.html
@@ -0,0 +1,451 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd";>
+<html>
+<!-- Created by GNU Texinfo 6.8, https://www.gnu.org/software/texinfo/ -->
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<!-- This is the GNU Emacs Lisp Reference Manual
+corresponding to Emacs version 29.0.50.
+
+Copyright © 1990-1996, 1998-2022 Free Software Foundation,
+Inc.
+
+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 the
+Invariant Sections being "GNU General Public License," 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.  Buying copies from the FSF supports it in
+developing GNU and promoting software freedom." -->
+<title>Pattern Matching (GNU Emacs Lisp Reference Manual)</title>
+
+<meta name="description" content="Pattern Matching (GNU Emacs Lisp Reference 
Manual)">
+<meta name="keywords" content="Pattern Matching (GNU Emacs Lisp Reference 
Manual)">
+<meta name="resource-type" content="document">
+<meta name="distribution" content="global">
+<meta name="Generator" content="makeinfo">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+
+<link href="index.html" rel="start" title="Top">
+<link href="Index.html" rel="index" title="Index">
+<link href="index.html#SEC_Contents" rel="contents" title="Table of Contents">
+<link href="Parsing-Program-Source.html" rel="up" title="Parsing Program 
Source">
+<link href="Multiple-Languages.html" rel="next" title="Multiple Languages">
+<link href="Accessing-Node-Information.html" rel="prev" title="Accessing Node 
Information">
+<style type="text/css">
+<!--
+a.copiable-anchor {visibility: hidden; text-decoration: none; line-height: 0em}
+a.summary-letter {text-decoration: none}
+blockquote.indentedblock {margin-right: 0em}
+div.display {margin-left: 3.2em}
+div.example {margin-left: 3.2em}
+kbd {font-style: oblique}
+pre.display {font-family: inherit}
+pre.format {font-family: inherit}
+pre.menu-comment {font-family: serif}
+pre.menu-preformatted {font-family: serif}
+span.nolinebreak {white-space: nowrap}
+span.roman {font-family: initial; font-weight: normal}
+span.sansserif {font-family: sans-serif; font-weight: normal}
+span:hover a.copiable-anchor {visibility: visible}
+ul.no-bullet {list-style: none}
+-->
+</style>
+<link rel="stylesheet" type="text/css" href="./manual.css">
+
+
+</head>
+
+<body lang="en">
+<div class="section" id="Pattern-Matching">
+<div class="header">
+<p>
+Next: <a href="Multiple-Languages.html" accesskey="n" rel="next">Parsing Text 
in Multiple Languages</a>, Previous: <a href="Accessing-Node-Information.html" 
accesskey="p" rel="prev">Accessing Node Information</a>, Up: <a 
href="Parsing-Program-Source.html" accesskey="u" rel="up">Parsing Program 
Source</a> &nbsp; [<a href="index.html#SEC_Contents" title="Table of contents" 
rel="contents">Contents</a>][<a href="Index.html" title="Index" 
rel="index">Index</a>]</p>
+</div>
+<hr>
+<span id="Pattern-Matching-Tree_002dsitter-Nodes"></span><h3 
class="section">37.5 Pattern Matching Tree-sitter Nodes</h3>
+<span id="index-pattern-matching-with-tree_002dsitter-nodes"></span>
+
+<span id="index-capturing_002c-tree_002dsitter-node"></span>
+<p>Tree-sitter lets Lisp programs match patterns using a small
+declarative language.  This pattern matching consists of two steps:
+first tree-sitter matches a <em>pattern</em> against nodes in the syntax
+tree, then it <em>captures</em> specific nodes that matched the pattern
+and returns the captured nodes.
+</p>
+<p>We describe first how to write the most basic query pattern and how to
+capture nodes in a pattern, then the pattern-matching function, and
+finally the more advanced pattern syntax.
+</p>
+<span id="Basic-query-syntax"></span><h3 class="heading">Basic query 
syntax</h3>
+
+<span id="index-tree_002dsitter-query-pattern-syntax"></span>
+<span id="index-pattern-syntax_002c-tree_002dsitter-query"></span>
+<span id="index-query_002c-tree_002dsitter"></span>
+<p>A <em>query</em> consists of multiple <em>patterns</em>.  Each pattern is an
+s-expression that matches a certain node in the syntax node.  A
+pattern has the form 
<code>(<var>type</var>&nbsp;(<var>child</var>&hellip;))</code><!-- /@w -->
+</p>
+<p>For example, a pattern that matches a <code>binary_expression</code> node 
that
+contains <code>number_literal</code> child nodes would look like
+</p>
+<div class="example">
+<pre class="example">(binary_expression (number_literal))
+</pre></div>
+
+<p>To <em>capture</em> a node using the query pattern above, append
+<code>@<var>capture-name</var></code> after the node pattern you want to
+capture.  For example,
+</p>
+<div class="example">
+<pre class="example">(binary_expression (number_literal) @number-in-exp)
+</pre></div>
+
+<p>captures <code>number_literal</code> nodes that are inside a
+<code>binary_expression</code> node with the capture name
+<code>number-in-exp</code>.
+</p>
+<p>We can capture the <code>binary_expression</code> node as well, with, for
+example, the capture name <code>biexp</code>:
+</p>
+<div class="example">
+<pre class="example">(binary_expression
+ (number_literal) @number-in-exp) @biexp
+</pre></div>
+
+<span id="Query-function"></span><h3 class="heading">Query function</h3>
+
+<span id="index-query-functions_002c-tree_002dsitter"></span>
+<p>Now we can introduce the <em>query functions</em>.
+</p>
+<dl class="def">
+<dt id="index-treesit_002dquery_002dcapture"><span class="category">Function: 
</span><span><strong>treesit-query-capture</strong> <em>node query 
&amp;optional beg end node-only</em><a 
href='#index-treesit_002dquery_002dcapture' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function matches patterns in <var>query</var> within 
<var>node</var>.
+The argument <var>query</var> can be either a string, a s-expression, or a
+compiled query object.  For now, we focus on the string syntax;
+s-expression syntax and compiled query are described at the end of the
+section.
+</p>
+<p>The argument <var>node</var> can also be a parser or a language symbol.  A
+parser means using its root node, a language symbol means find or
+create a parser for that language in the current buffer, and use the
+root node.
+</p>
+<p>The function returns all the captured nodes in a list of the form
+<code>(<var><span 
class="nolinebreak">capture_name</span></var>&nbsp;.&nbsp;<var>node</var>)</code><!--
 /@w -->.  If <var>node-only</var> is
+non-<code>nil</code>, it returns the list of nodes instead.  By default the
+entire text of <var>node</var> is searched, but if <var>beg</var> and 
<var>end</var>
+are both non-<code>nil</code>, they specify the region of buffer text where
+this function should match nodes.  Any matching node whose span
+overlaps with the region between <var>beg</var> and <var>end</var> are 
captured,
+it doesn&rsquo;t have to be completely in the region.
+</p>
+<span id="index-treesit_002dquery_002derror"></span>
+<span id="index-treesit_002dquery_002dvalidate"></span>
+<p>This function raises the <code>treesit-query-error</code> error if
+<var>query</var> is malformed.  The signal data contains a description of
+the specific error.  You can use <code>treesit-query-validate</code> to
+validate and debug the query.
+</p></dd></dl>
+
+<p>For example, suppose <var>node</var>&rsquo;s text is <code>1 + 2</code>, and
+<var>query</var> is
+</p>
+<div class="example">
+<pre class="example">(setq query
+      &quot;(binary_expression
+        (number_literal) @number-in-exp) @biexp&quot;)
+</pre></div>
+
+<p>Matching that query would return
+</p>
+<div class="example">
+<pre class="example">(treesit-query-capture node query)
+    &rArr; ((biexp . <var>&lt;node for &quot;1 + 2&quot;&gt;</var>)
+       (number-in-exp . <var>&lt;node for &quot;1&quot;&gt;</var>)
+       (number-in-exp . <var>&lt;node for &quot;2&quot;&gt;</var>))
+</pre></div>
+
+<p>As mentioned earlier, <var>query</var> could contain multiple patterns.
+For example, it could have two top-level patterns:
+</p>
+<div class="example">
+<pre class="example">(setq query
+      &quot;(binary_expression) @biexp
+       (number_literal)  @number @biexp&quot;)
+</pre></div>
+
+<dl class="def">
+<dt id="index-treesit_002dquery_002dstring"><span class="category">Function: 
</span><span><strong>treesit-query-string</strong> <em>string query 
language</em><a href='#index-treesit_002dquery_002dstring' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This function parses <var>string</var> with <var>language</var>, 
matches its
+root node with <var>query</var>, and returns the result.
+</p></dd></dl>
+
+<span id="More-query-syntax"></span><h3 class="heading">More query syntax</h3>
+
+<p>Besides node type and capture, tree-sitter&rsquo;s pattern syntax can
+express anonymous node, field name, wildcard, quantification,
+grouping, alternation, anchor, and predicate.
+</p>
+<span id="Anonymous-node"></span><h4 class="subheading">Anonymous node</h4>
+
+<p>An anonymous node is written verbatim, surrounded by quotes.  A
+pattern matching (and capturing) keyword <code>return</code> would be
+</p>
+<div class="example">
+<pre class="example">&quot;return&quot; @keyword
+</pre></div>
+
+<span id="Wild-card"></span><h4 class="subheading">Wild card</h4>
+
+<p>In a pattern, &lsquo;<samp>(_)</samp>&rsquo; matches any named node, and 
&lsquo;<samp>_</samp>&rsquo; matches
+any named and anonymous node.  For example, to capture any named child
+of a <code>binary_expression</code> node, the pattern would be
+</p>
+<div class="example">
+<pre class="example">(binary_expression (_) @in_biexp)
+</pre></div>
+
+<span id="Field-name"></span><h4 class="subheading">Field name</h4>
+
+<p>It is possible to capture child nodes that have specific field names.
+In the pattern below, <code>declarator</code> and <code>body</code> are field
+names, indicated by the colon following them.
+</p>
+<div class="example">
+<pre class="example">(function_definition
+  declarator: (_) @func-declarator
+  body: (_) @func-body)
+</pre></div>
+
+<p>It is also possible to capture a node that doesn&rsquo;t have a certain
+field, say, a <code>function_definition</code> without a <code>body</code> 
field.
+</p>
+<div class="example">
+<pre class="example">(function_definition !body) @func-no-body
+</pre></div>
+
+<span id="Quantify-node"></span><h4 class="subheading">Quantify node</h4>
+
+<span id="index-quantify-node_002c-tree_002dsitter"></span>
+<p>Tree-sitter recognizes quantification operators 
&lsquo;<samp>*</samp>&rsquo;, &lsquo;<samp>+</samp>&rsquo; and
+&lsquo;<samp>?</samp>&rsquo;.  Their meanings are the same as in regular 
expressions:
+&lsquo;<samp>*</samp>&rsquo; matches the preceding pattern zero or more times, 
&lsquo;<samp>+</samp>&rsquo;
+matches one or more times, and &lsquo;<samp>?</samp>&rsquo; matches zero or 
one time.
+</p>
+<p>For example, the following pattern matches <code>type_declaration</code>
+nodes that has <em>zero or more</em> <code>long</code> keyword.
+</p>
+<div class="example">
+<pre class="example">(type_declaration &quot;long&quot;*) @long-type
+</pre></div>
+
+<p>The following pattern matches a type declaration that has zero or one
+<code>long</code> keyword:
+</p>
+<div class="example">
+<pre class="example">(type_declaration &quot;long&quot;?) @long-type
+</pre></div>
+
+<span id="Grouping"></span><h4 class="subheading">Grouping</h4>
+
+<p>Similar to groups in regular expression, we can bundle patterns into
+groups and apply quantification operators to them.  For example, to
+express a comma separated list of identifiers, one could write
+</p>
+<div class="example">
+<pre class="example">(identifier) (&quot;,&quot; (identifier))*
+</pre></div>
+
+<span id="Alternation"></span><h4 class="subheading">Alternation</h4>
+
+<p>Again, similar to regular expressions, we can express &ldquo;match anyone
+from this group of patterns&rdquo; in a pattern.  The syntax is a list of
+patterns enclosed in square brackets.  For example, to capture some
+keywords in C, the pattern would be
+</p>
+<div class="example">
+<pre class="example">[
+  &quot;return&quot;
+  &quot;break&quot;
+  &quot;if&quot;
+  &quot;else&quot;
+] @keyword
+</pre></div>
+
+<span id="Anchor"></span><h4 class="subheading">Anchor</h4>
+
+<p>The anchor operator &lsquo;<samp>.</samp>&rsquo; can be used to enforce 
juxtaposition,
+i.e., to enforce two things to be directly next to each other.  The
+two &ldquo;things&rdquo; can be two nodes, or a child and the end of its 
parent.
+For example, to capture the first child, the last child, or two
+adjacent children:
+</p>
+<div class="example">
+<pre class="example">;; Anchor the child with the end of its parent.
+(compound_expression (_) @last-child .)
+</pre><pre class="example">
+
+</pre><pre class="example">;; Anchor the child with the beginning of its 
parent.
+(compound_expression . (_) @first-child)
+</pre><pre class="example">
+
+</pre><pre class="example">;; Anchor two adjacent children.
+(compound_expression
+ (_) @prev-child
+ .
+ (_) @next-child)
+</pre></div>
+
+<p>Note that the enforcement of juxtaposition ignores any anonymous
+nodes.
+</p>
+<span id="Predicate"></span><h4 class="subheading">Predicate</h4>
+
+<p>It is possible to add predicate constraints to a pattern.  For
+example, with the following pattern:
+</p>
+<div class="example">
+<pre class="example">(
+ (array . (_) @first (_) @last .)
+ (#equal @first @last)
+)
+</pre></div>
+
+<p>tree-sitter only matches arrays where the first element equals to
+the last element.  To attach a predicate to a pattern, we need to
+group them together.  A predicate always starts with a 
&lsquo;<samp>#</samp>&rsquo;.
+Currently there are two predicates, <code>#equal</code> and 
<code>#match</code>.
+</p>
+<dl class="def">
+<dt id="index-equal-1"><span class="category">Predicate: 
</span><span><strong>equal</strong> <em>arg1 arg2</em><a href='#index-equal-1' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>Matches if <var>arg1</var> equals to <var>arg2</var>.  Arguments can be 
either
+strings or capture names.  Capture names represent the text that the
+captured node spans in the buffer.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-match-1"><span class="category">Predicate: 
</span><span><strong>match</strong> <em>regexp capture-name</em><a 
href='#index-match-1' class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>Matches if the text that <var>capture-name</var>&rsquo;s node spans in 
the buffer
+matches regular expression <var>regexp</var>.  Matching is case-sensitive.
+</p></dd></dl>
+
+<p>Note that a predicate can only refer to capture names that appear in
+the same pattern.  Indeed, it makes little sense to refer to capture
+names in other patterns.
+</p>
+<span id="S_002dexpression-patterns"></span><h3 class="heading">S-expression 
patterns</h3>
+
+<span id="index-tree_002dsitter-patterns-as-sexps"></span>
+<span id="index-patterns_002c-tree_002dsitter_002c-in-sexp-form"></span>
+<p>Besides strings, Emacs provides a s-expression based syntax for
+tree-sitter patterns.  It largely resembles the string-based syntax.
+For example, the following query
+</p>
+<div class="example">
+<pre class="example">(treesit-query-capture
+ node &quot;(addition_expression
+        left: (_) @left
+        \&quot;+\&quot; @plus-sign
+        right: (_) @right) @addition
+
+        [\&quot;return\&quot; \&quot;break\&quot;] @keyword&quot;)
+</pre></div>
+
+<p>is equivalent to
+</p>
+<div class="example">
+<pre class="example">(treesit-query-capture
+ node '((addition_expression
+         left: (_) @left
+         &quot;+&quot; @plus-sign
+         right: (_) @right) @addition
+
+         [&quot;return&quot; &quot;break&quot;] @keyword))
+</pre></div>
+
+<p>Most patterns can be written directly as strange but nevertheless
+valid s-expressions.  Only a few of them needs modification:
+</p>
+<ul>
+<li> Anchor &lsquo;<samp>.</samp>&rsquo; is written as <code>:anchor</code>.
+</li><li> &lsquo;<samp>?</samp>&rsquo; is written as 
&lsquo;<samp>:?</samp>&rsquo;.
+</li><li> &lsquo;<samp>*</samp>&rsquo; is written as 
&lsquo;<samp>:*</samp>&rsquo;.
+</li><li> &lsquo;<samp>+</samp>&rsquo; is written as 
&lsquo;<samp>:+</samp>&rsquo;.
+</li><li> <code>#equal</code> is written as <code>:equal</code>.  In general, 
predicates
+change their &lsquo;<samp>#</samp>&rsquo; to &lsquo;<samp>:</samp>&rsquo;.
+</li></ul>
+
+<p>For example,
+</p>
+<div class="example">
+<pre class="example">&quot;(
+  (compound_expression . (_) @first (_)* @rest)
+  (#match \&quot;love\&quot; @first)
+  )&quot;
+</pre></div>
+
+<p>is written in s-expression as
+</p>
+<div class="example">
+<pre class="example">'((
+   (compound_expression :anchor (_) @first (_) :* @rest)
+   (:match &quot;love&quot; @first)
+   ))
+</pre></div>
+
+<span id="Compiling-queries"></span><h3 class="heading">Compiling queries</h3>
+
+<span id="index-compiling-tree_002dsitter-queries"></span>
+<span id="index-queries_002c-compiling"></span>
+<p>If a query is intended to be used repeatedly, especially in tight
+loops, it is important to compile that query, because a compiled query
+is much faster than an uncompiled one.  A compiled query can be used
+anywhere a query is accepted.
+</p>
+<dl class="def">
+<dt id="index-treesit_002dquery_002dcompile"><span class="category">Function: 
</span><span><strong>treesit-query-compile</strong> <em>language query</em><a 
href='#index-treesit_002dquery_002dcompile' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function compiles <var>query</var> for <var>language</var> into a 
compiled
+query object and returns it.
+</p>
+<p>This function raises the <code>treesit-query-error</code> error if
+<var>query</var> is malformed.  The signal data contains a description of
+the specific error.  You can use <code>treesit-query-validate</code> to
+validate and debug the query.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dquery_002dlanguage"><span class="category">Function: 
</span><span><strong>treesit-query-language</strong> <em>query</em><a 
href='#index-treesit_002dquery_002dlanguage' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function return the language of <var>query</var>.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dquery_002dexpand"><span class="category">Function: 
</span><span><strong>treesit-query-expand</strong> <em>query</em><a 
href='#index-treesit_002dquery_002dexpand' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function converts the s-expression <var>query</var> into the string
+format.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dpattern_002dexpand"><span class="category">Function: 
</span><span><strong>treesit-pattern-expand</strong> <em>pattern</em><a 
href='#index-treesit_002dpattern_002dexpand' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function converts the s-expression <var>pattern</var> into the 
string
+format.
+</p></dd></dl>
+
+<p>For more details, read the tree-sitter project&rsquo;s documentation about
+pattern-matching, which can be found at
+<a 
href="https://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries";>https://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries</a>.
+</p>
+</div>
+<hr>
+<div class="header">
+<p>
+Next: <a href="Multiple-Languages.html">Parsing Text in Multiple 
Languages</a>, Previous: <a href="Accessing-Node-Information.html">Accessing 
Node Information</a>, Up: <a href="Parsing-Program-Source.html">Parsing Program 
Source</a> &nbsp; [<a href="index.html#SEC_Contents" title="Table of contents" 
rel="contents">Contents</a>][<a href="Index.html" title="Index" 
rel="index">Index</a>]</p>
+</div>
+
+
+
+</body>
+</html>
diff --git a/admin/notes/tree-sitter/html-manual/Retrieving-Node.html 
b/admin/notes/tree-sitter/html-manual/Retrieving-Node.html
new file mode 100644
index 0000000000..0c086dab91
--- /dev/null
+++ b/admin/notes/tree-sitter/html-manual/Retrieving-Node.html
@@ -0,0 +1,421 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd";>
+<html>
+<!-- Created by GNU Texinfo 6.8, https://www.gnu.org/software/texinfo/ -->
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<!-- This is the GNU Emacs Lisp Reference Manual
+corresponding to Emacs version 29.0.50.
+
+Copyright © 1990-1996, 1998-2022 Free Software Foundation,
+Inc.
+
+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 the
+Invariant Sections being "GNU General Public License," 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.  Buying copies from the FSF supports it in
+developing GNU and promoting software freedom." -->
+<title>Retrieving Node (GNU Emacs Lisp Reference Manual)</title>
+
+<meta name="description" content="Retrieving Node (GNU Emacs Lisp Reference 
Manual)">
+<meta name="keywords" content="Retrieving Node (GNU Emacs Lisp Reference 
Manual)">
+<meta name="resource-type" content="document">
+<meta name="distribution" content="global">
+<meta name="Generator" content="makeinfo">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+
+<link href="index.html" rel="start" title="Top">
+<link href="Index.html" rel="index" title="Index">
+<link href="index.html#SEC_Contents" rel="contents" title="Table of Contents">
+<link href="Parsing-Program-Source.html" rel="up" title="Parsing Program 
Source">
+<link href="Accessing-Node-Information.html" rel="next" title="Accessing Node 
Information">
+<link href="Using-Parser.html" rel="prev" title="Using Parser">
+<style type="text/css">
+<!--
+a.copiable-anchor {visibility: hidden; text-decoration: none; line-height: 0em}
+a.summary-letter {text-decoration: none}
+blockquote.indentedblock {margin-right: 0em}
+div.display {margin-left: 3.2em}
+div.example {margin-left: 3.2em}
+kbd {font-style: oblique}
+pre.display {font-family: inherit}
+pre.format {font-family: inherit}
+pre.menu-comment {font-family: serif}
+pre.menu-preformatted {font-family: serif}
+span.nolinebreak {white-space: nowrap}
+span.roman {font-family: initial; font-weight: normal}
+span.sansserif {font-family: sans-serif; font-weight: normal}
+span:hover a.copiable-anchor {visibility: visible}
+ul.no-bullet {list-style: none}
+-->
+</style>
+<link rel="stylesheet" type="text/css" href="./manual.css">
+
+
+</head>
+
+<body lang="en">
+<div class="section" id="Retrieving-Node">
+<div class="header">
+<p>
+Next: <a href="Accessing-Node-Information.html" accesskey="n" 
rel="next">Accessing Node Information</a>, Previous: <a 
href="Using-Parser.html" accesskey="p" rel="prev">Using Tree-sitter Parser</a>, 
Up: <a href="Parsing-Program-Source.html" accesskey="u" rel="up">Parsing 
Program Source</a> &nbsp; [<a href="index.html#SEC_Contents" title="Table of 
contents" rel="contents">Contents</a>][<a href="Index.html" title="Index" 
rel="index">Index</a>]</p>
+</div>
+<hr>
+<span id="Retrieving-Node-1"></span><h3 class="section">37.3 Retrieving 
Node</h3>
+<span id="index-retrieve-node_002c-tree_002dsitter"></span>
+<span id="index-tree_002dsitter_002c-find-node"></span>
+<span id="index-get-node_002c-tree_002dsitter"></span>
+
+<span id="index-terminology_002c-for-tree_002dsitter-functions"></span>
+<p>Here&rsquo;s some terminology and conventions we use when documenting
+tree-sitter functions.
+</p>
+<p>We talk about a node being &ldquo;smaller&rdquo; or &ldquo;larger&rdquo;, 
and &ldquo;lower&rdquo; or
+&ldquo;higher&rdquo;.  A smaller and lower node is lower in the syntax tree and
+therefore spans a smaller portion of buffer text; a larger and higher
+node is higher up in the syntax tree, it contains many smaller nodes
+as its children, and therefore spans a larger portion of text.
+</p>
+<p>When a function cannot find a node, it returns <code>nil</code>.  For
+convenience, all functions that take a node as argument and return
+a node, also accept the node argument of <code>nil</code> and in that case
+just return <code>nil</code>.
+</p>
+<span id="index-treesit_002dnode_002doutdated"></span>
+<p>Nodes are not automatically updated when the associated buffer is
+modified, and there is no way to update a node once it is retrieved.
+Using an outdated node signals the <code>treesit-node-outdated</code> error.
+</p>
+<span id="Retrieving-node-from-syntax-tree"></span><h3 
class="heading">Retrieving node from syntax tree</h3>
+<span id="index-retrieving-tree_002dsitter-nodes"></span>
+<span id="index-syntax-tree_002c-retrieving-nodes"></span>
+
+<dl class="def">
+<dt id="index-treesit_002dnode_002dat"><span class="category">Function: 
</span><span><strong>treesit-node-at</strong> <em>pos &amp;optional 
parser-or-lang named</em><a href='#index-treesit_002dnode_002dat' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This function returns the <em>smallest</em> node that starts at or after
+the buffer position <var>pos</var>.  In other words, the start of the node
+is greater or equal to <var>pos</var>.
+</p>
+<p>When <var>parser-or-lang</var> is <code>nil</code> or omitted, this 
function uses
+the first parser in <code>(treesit-parser-list)</code> of the current
+buffer.  If <var>parser-or-lang</var> is a parser object, it uses that
+parser; if <var>parser-or-lang</var> is a language, it finds the first
+parser using that language in <code>(treesit-parser-list)</code>, and uses
+that.
+</p>
+<p>If <var>named</var> is non-<code>nil</code>, this function looks for a 
named node
+only (see <a href="Language-Definitions.html#tree_002dsitter-named-node">named 
node</a>).
+</p>
+<p>When <var>pos</var> is after all the text in the buffer, technically there
+is no node after <var>pos</var>.  But for convenience, this function will
+return the last leaf node in the parse tree.  If <var>strict</var> is
+non-<code>nil</code>, this function will strictly comply to the semantics and
+return <var>nil</var>.
+</p>
+<p>Example:
+</p>
+<div class="example">
+<pre class="example">;; Find the node at point in a C parser's syntax tree.
+(treesit-node-at (point) 'c)
+  &rArr; #&lt;treesit-node (primitive_type) in 23-27&gt;
+</pre></div>
+</dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dnode_002don"><span class="category">Function: 
</span><span><strong>treesit-node-on</strong> <em>beg end &amp;optional 
parser-or-lang named</em><a href='#index-treesit_002dnode_002don' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This function returns the <em>smallest</em> node that covers the region
+of buffer text between <var>beg</var> and <var>end</var>.  In other words, the
+start of the node is before or at <var>beg</var>, and the end of the node
+is at or after <var>end</var>.
+</p>
+<p><em>Beware:</em> calling this function on an empty line that is not
+inside any top-level construct (function definition, etc.) most
+probably will give you the root node, because the root node is the
+smallest node that covers that empty line.  Most of the time, you want
+to use <code>treesit-node-at</code>, described above, instead.
+</p>
+<p>When <var>parser-or-lang</var> is <code>nil</code>, this function uses the 
first
+parser in <code>(treesit-parser-list)</code> of the current buffer.  If
+<var>parser-or-lang</var> is a parser object, it uses that parser; if
+<var>parser-or-lang</var> is a language, it finds the first parser using
+that language in <code>(treesit-parser-list)</code>, and uses that.
+</p>
+<p>If <var>named</var> is non-<code>nil</code>, this function looks for a 
named node
+only (see <a href="Language-Definitions.html#tree_002dsitter-named-node">named 
node</a>).
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dparser_002droot_002dnode"><span 
class="category">Function: 
</span><span><strong>treesit-parser-root-node</strong> <em>parser</em><a 
href='#index-treesit_002dparser_002droot_002dnode' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function returns the root node of the syntax tree generated by
+<var>parser</var>.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dbuffer_002droot_002dnode"><span 
class="category">Function: 
</span><span><strong>treesit-buffer-root-node</strong> <em>&amp;optional 
language</em><a href='#index-treesit_002dbuffer_002droot_002dnode' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This function finds the first parser that uses <var>language</var> in
+<code>(treesit-parser-list)</code> of the current buffer, and returns the
+root node generated by that parser.  If it cannot find an appropriate
+parser, it returns <code>nil</code>.
+</p></dd></dl>
+
+<p>Given a node, a Lisp program can retrieve other nodes starting from
+it, or query for information about this node.
+</p>
+<span id="Retrieving-node-from-other-nodes"></span><h3 
class="heading">Retrieving node from other nodes</h3>
+<span id="index-syntax-tree-nodes_002c-retrieving-from-other-nodes"></span>
+
+<span id="By-kinship"></span><h4 class="subheading">By kinship</h4>
+<span id="index-kinship_002c-syntax-tree-nodes"></span>
+<span id="index-nodes_002c-by-kinship"></span>
+<span id="index-syntax-tree-nodes_002c-by-kinship"></span>
+
+<dl class="def">
+<dt id="index-treesit_002dnode_002dparent"><span class="category">Function: 
</span><span><strong>treesit-node-parent</strong> <em>node</em><a 
href='#index-treesit_002dnode_002dparent' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function returns the immediate parent of <var>node</var>.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dnode_002dchild"><span class="category">Function: 
</span><span><strong>treesit-node-child</strong> <em>node n &amp;optional 
named</em><a href='#index-treesit_002dnode_002dchild' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function returns the <var>n</var>&rsquo;th child of 
<var>node</var>.  If
+<var>named</var> is non-<code>nil</code>, it counts only named nodes
+(see <a href="Language-Definitions.html#tree_002dsitter-named-node">named 
node</a>).
+</p>
+<p>For example, in a node that represents a string 
<code>&quot;text&quot;</code>, there
+are three children nodes: the opening quote <code>&quot;</code>, the string 
text
+<code>text</code>, and the closing quote <code>&quot;</code>.  Among these 
nodes, the
+first child is the opening quote <code>&quot;</code>, and the first named child
+is the string text.
+</p>
+<p>This function returns <code>nil</code> if there is no <var>n</var>&rsquo;th 
child.
+<var>n</var> could be negative, e.g., <code>-1</code> represents the last 
child.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dnode_002dchildren"><span class="category">Function: 
</span><span><strong>treesit-node-children</strong> <em>node &amp;optional 
named</em><a href='#index-treesit_002dnode_002dchildren' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This function returns all of <var>node</var>&rsquo;s children as a 
list.  If
+<var>named</var> is non-<code>nil</code>, it retrieves only named nodes.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dnext_002dsibling"><span class="category">Function: 
</span><span><strong>treesit-next-sibling</strong> <em>node &amp;optional 
named</em><a href='#index-treesit_002dnext_002dsibling' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This function finds the next sibling of <var>node</var>.  If 
<var>named</var> is
+non-<code>nil</code>, it finds the next named sibling.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dprev_002dsibling"><span class="category">Function: 
</span><span><strong>treesit-prev-sibling</strong> <em>node &amp;optional 
named</em><a href='#index-treesit_002dprev_002dsibling' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This function finds the previous sibling of <var>node</var>.  If
+<var>named</var> is non-<code>nil</code>, it finds the previous named sibling.
+</p></dd></dl>
+
+<span id="By-field-name"></span><h4 class="subheading">By field name</h4>
+<span id="index-nodes_002c-by-field-name"></span>
+<span id="index-syntax-tree-nodes_002c-by-field-name"></span>
+
+<p>To make the syntax tree easier to analyze, many language definitions
+assign <em>field names</em> to child nodes (see <a 
href="Language-Definitions.html#tree_002dsitter-node-field-name">field 
name</a>).  For example, a <code>function_definition</code> node
+could have a <code>declarator</code> node and a <code>body</code> node.
+</p>
+<dl class="def">
+<dt id="index-treesit_002dchild_002dby_002dfield_002dname"><span 
class="category">Function: 
</span><span><strong>treesit-child-by-field-name</strong> <em>node 
field-name</em><a href='#index-treesit_002dchild_002dby_002dfield_002dname' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This function finds the child of <var>node</var> whose field name is
+<var>field-name</var>, a string.
+</p>
+<div class="example">
+<pre class="example">;; Get the child that has &quot;body&quot; as its field 
name.
+(treesit-child-by-field-name node &quot;body&quot;)
+  &rArr; #&lt;treesit-node (compound_statement) in 45-89&gt;
+</pre></div>
+</dd></dl>
+
+<span id="By-position"></span><h4 class="subheading">By position</h4>
+<span id="index-nodes_002c-by-position"></span>
+<span id="index-syntax-tree-nodes_002c-by-position"></span>
+
+<dl class="def">
+<dt id="index-treesit_002dfirst_002dchild_002dfor_002dpos"><span 
class="category">Function: 
</span><span><strong>treesit-first-child-for-pos</strong> <em>node pos 
&amp;optional named</em><a 
href='#index-treesit_002dfirst_002dchild_002dfor_002dpos' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This function finds the first child of <var>node</var> that extends 
beyond
+buffer position <var>pos</var>.  &ldquo;Extends beyond&rdquo; means the end of 
the
+child node is greater or equal to <var>pos</var>.  This function only looks
+for immediate children of <var>node</var>, and doesn&rsquo;t look in its
+grandchildren.  If <var>named</var> is non-<code>nil</code>, it looks for the
+first named child (see <a 
href="Language-Definitions.html#tree_002dsitter-named-node">named node</a>).
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dnode_002ddescendant_002dfor_002drange"><span 
class="category">Function: 
</span><span><strong>treesit-node-descendant-for-range</strong> <em>node beg 
end &amp;optional named</em><a 
href='#index-treesit_002dnode_002ddescendant_002dfor_002drange' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This function finds the <em>smallest</em> descendant node of 
<var>node</var>
+that spans the region of text between positions <var>beg</var> and
+<var>end</var>.  It is similar to <code>treesit-node-at</code>.  If 
<var>named</var>
+is non-<code>nil</code>, it looks for smallest named child.
+</p></dd></dl>
+
+<span id="Searching-for-node"></span><h3 class="heading">Searching for 
node</h3>
+
+<dl class="def">
+<dt id="index-treesit_002dsearch_002dsubtree"><span class="category">Function: 
</span><span><strong>treesit-search-subtree</strong> <em>node predicate 
&amp;optional backward all limit</em><a 
href='#index-treesit_002dsearch_002dsubtree' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function traverses the subtree of <var>node</var> (including
+<var>node</var> itself), looking for a node for which <var>predicate</var>
+returns non-<code>nil</code>.  <var>predicate</var> is a regexp that is matched
+against each node&rsquo;s type, or a predicate function that takes a node
+and returns non-<code>nil</code> if the node matches.  The function returns
+the first node that matches, or <code>nil</code> if none does.
+</p>
+<p>By default, this function only traverses named nodes, but if <var>all</var>
+is non-<code>nil</code>, it traverses all the nodes.  If <var>backward</var> is
+non-<code>nil</code>, it traverses backwards (i.e., it visits the last child 
first
+when traversing down the tree).  If <var>limit</var> is non-<code>nil</code>, 
it
+must be a number that limits the tree traversal to that many levels
+down the tree.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dsearch_002dforward"><span class="category">Function: 
</span><span><strong>treesit-search-forward</strong> <em>start predicate 
&amp;optional backward all</em><a href='#index-treesit_002dsearch_002dforward' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>Like <code>treesit-search-subtree</code>, this function also traverses 
the
+parse tree and matches each node with <var>predicate</var> (except for
+<var>start</var>), where <var>predicate</var> can be a regexp or a function.
+For a tree like the below where <var>start</var> is marked S, this function
+traverses as numbered from 1 to 12:
+</p>
+<div class="example">
+<pre class="example">              12
+              |
+     S--------3----------11
+     |        |          |
+o--o-+--o  1--+--2    6--+-----10
+|  |                  |        |
+o  o                +-+-+   +--+--+
+                    |   |   |  |  |
+                    4   5   7  8  9
+</pre></div>
+
+<p>Note that this function doesn&rsquo;t traverse the subtree of 
<var>start</var>,
+and it always traverse leaf nodes first, then upwards.
+</p>
+<p>Like <code>treesit-search-subtree</code>, this function only searches for
+named nodes by default, but if <var>all</var> is non-<code>nil</code>, it
+searches for all nodes.  If <var>backward</var> is non-<code>nil</code>, it
+searches backwards.
+</p>
+<p>While <code>treesit-search-subtree</code> traverses the subtree of a node,
+this function starts with node <var>start</var> and traverses every node
+that comes after it in the buffer position order, i.e., nodes with
+start positions greater than the end position of <var>start</var>.
+</p>
+<p>In the tree shown above, <code>treesit-search-subtree</code> traverses node
+S (<var>start</var>) and nodes marked with <code>o</code>, where this function
+traverses the nodes marked with numbers.  This function is useful for
+answering questions like &ldquo;what is the first node after <var>start</var> 
in
+the buffer that satisfies some condition?&rdquo;
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dsearch_002dforward_002dgoto"><span 
class="category">Function: 
</span><span><strong>treesit-search-forward-goto</strong> <em>node predicate 
&amp;optional start backward all</em><a 
href='#index-treesit_002dsearch_002dforward_002dgoto' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function moves point to the start or end of the next node after
+<var>node</var> in the buffer that matches <var>predicate</var>.  If 
<var>start</var>
+is non-<code>nil</code>, stop at the beginning rather than the end of a node.
+</p>
+<p>This function guarantees that the matched node it returns makes
+progress in terms of buffer position: the start/end position of the
+returned node is always greater than that of <var>node</var>.
+</p>
+<p>Arguments <var>predicate</var>, <var>backward</var> and <var>all</var> are 
the same
+as in <code>treesit-search-forward</code>.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dinduce_002dsparse_002dtree"><span 
class="category">Function: 
</span><span><strong>treesit-induce-sparse-tree</strong> <em>root predicate 
&amp;optional process-fn limit</em><a 
href='#index-treesit_002dinduce_002dsparse_002dtree' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function creates a sparse tree from <var>root</var>&rsquo;s 
subtree.
+</p>
+<p>It takes the subtree under <var>root</var>, and combs it so only the nodes
+that match <var>predicate</var> are left.  Like previous functions, the
+<var>predicate</var> can be a regexp string that matches against each
+node&rsquo;s type, or a function that takes a node and return 
non-<code>nil</code>
+if it matches.
+</p>
+<p>For example, for a subtree on the left that consist of both numbers
+and letters, if <var>predicate</var> is &ldquo;letter only&rdquo;, the 
returned tree
+is the one on the right.
+</p>
+<div class="example">
+<pre class="example">    a                 a              a
+    |                 |              |
++---+---+         +---+---+      +---+---+
+|   |   |         |   |   |      |   |   |
+b   1   2         b   |   |      b   c   d
+    |   |     =&gt;      |   |  =&gt;      |
+    c   +--+          c   +          e
+    |   |  |          |   |
+ +--+   d  4       +--+   d
+ |  |              |
+ e  5              e
+</pre></div>
+
+<p>If <var>process-fn</var> is non-<code>nil</code>, instead of returning the 
matched
+nodes, this function passes each node to <var>process-fn</var> and uses the
+returned value instead.  If non-<code>nil</code>, <var>limit</var> is the 
number of
+levels to go down from <var>root</var>.
+</p>
+<p>Each node in the returned tree looks like
+<code>(<var><span 
class="nolinebreak">tree-sitter-node</span></var>&nbsp;.&nbsp;(<var>child</var>&nbsp;&hellip;))</code><!--
 /@w -->.  The
+<var>tree-sitter-node</var> of the root of this tree will be nil if
+<var>root</var> doesn&rsquo;t match <var>predicate</var>.  If no node matches
+<var>predicate</var>, the function returns <code>nil</code>.
+</p></dd></dl>
+
+<span id="More-convenience-functions"></span><h3 class="heading">More 
convenience functions</h3>
+
+<dl class="def">
+<dt id="index-treesit_002dfilter_002dchild"><span class="category">Function: 
</span><span><strong>treesit-filter-child</strong> <em>node predicate 
&amp;optional named</em><a href='#index-treesit_002dfilter_002dchild' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This function finds immediate children of <var>node</var> that satisfy
+<var>predicate</var>.
+</p>
+<p>The <var>predicate</var> function takes a node as the argument and should
+return non-<code>nil</code> to indicate that the node should be kept.  If
+<var>named</var> is non-<code>nil</code>, this function only examines the named
+nodes.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dparent_002duntil"><span class="category">Function: 
</span><span><strong>treesit-parent-until</strong> <em>node predicate</em><a 
href='#index-treesit_002dparent_002duntil' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function repeatedly finds the parents of <var>node</var>, and 
returns
+the parent that satisfies <var>predicate</var>, a function that takes a
+node as the argument.  If no parent satisfies <var>predicate</var>, this
+function returns <code>nil</code>.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dparent_002dwhile"><span class="category">Function: 
</span><span><strong>treesit-parent-while</strong> <em>node predicate</em><a 
href='#index-treesit_002dparent_002dwhile' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function repeatedly finds the parent of <var>node</var>, and keeps
+doing so as long as the nodes satisfy <var>predicate</var>, a function that
+takes a node as the argument.  That is, this function returns the
+farthest parent that still satisfies <var>predicate</var>.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dnode_002dtop_002dlevel"><span 
class="category">Function: </span><span><strong>treesit-node-top-level</strong> 
<em>node &amp;optional type</em><a 
href='#index-treesit_002dnode_002dtop_002dlevel' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function returns the highest parent of <var>node</var> that has the
+same type as <var>node</var>.  If no such parent exists, it returns
+<code>nil</code>.  Therefore this function is also useful for testing
+whether <var>node</var> is top-level.
+</p>
+<p>If <var>type</var> is non-<code>nil</code>, this function matches each 
parent&rsquo;s
+type with <var>type</var> as a regexp, rather than using 
<var>node</var>&rsquo;s type.
+</p></dd></dl>
+
+</div>
+<hr>
+<div class="header">
+<p>
+Next: <a href="Accessing-Node-Information.html">Accessing Node 
Information</a>, Previous: <a href="Using-Parser.html">Using Tree-sitter 
Parser</a>, Up: <a href="Parsing-Program-Source.html">Parsing Program 
Source</a> &nbsp; [<a href="index.html#SEC_Contents" title="Table of contents" 
rel="contents">Contents</a>][<a href="Index.html" title="Index" 
rel="index">Index</a>]</p>
+</div>
+
+
+
+</body>
+</html>
diff --git a/admin/notes/tree-sitter/html-manual/Tree_002dsitter-C-API.html 
b/admin/notes/tree-sitter/html-manual/Tree_002dsitter-C-API.html
new file mode 100644
index 0000000000..29d51eecf7
--- /dev/null
+++ b/admin/notes/tree-sitter/html-manual/Tree_002dsitter-C-API.html
@@ -0,0 +1,212 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd";>
+<html>
+<!-- Created by GNU Texinfo 6.8, https://www.gnu.org/software/texinfo/ -->
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<!-- This is the GNU Emacs Lisp Reference Manual
+corresponding to Emacs version 29.0.50.
+
+Copyright © 1990-1996, 1998-2022 Free Software Foundation,
+Inc.
+
+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 the
+Invariant Sections being "GNU General Public License," 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.  Buying copies from the FSF supports it in
+developing GNU and promoting software freedom." -->
+<title>Tree-sitter C API (GNU Emacs Lisp Reference Manual)</title>
+
+<meta name="description" content="Tree-sitter C API (GNU Emacs Lisp Reference 
Manual)">
+<meta name="keywords" content="Tree-sitter C API (GNU Emacs Lisp Reference 
Manual)">
+<meta name="resource-type" content="document">
+<meta name="distribution" content="global">
+<meta name="Generator" content="makeinfo">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+
+<link href="index.html" rel="start" title="Top">
+<link href="Index.html" rel="index" title="Index">
+<link href="index.html#SEC_Contents" rel="contents" title="Table of Contents">
+<link href="Parsing-Program-Source.html" rel="up" title="Parsing Program 
Source">
+<link href="Tree_002dsitter-major-modes.html" rel="prev" title="Tree-sitter 
major modes">
+<style type="text/css">
+<!--
+a.copiable-anchor {visibility: hidden; text-decoration: none; line-height: 0em}
+a.summary-letter {text-decoration: none}
+blockquote.indentedblock {margin-right: 0em}
+div.display {margin-left: 3.2em}
+div.example {margin-left: 3.2em}
+kbd {font-style: oblique}
+pre.display {font-family: inherit}
+pre.format {font-family: inherit}
+pre.menu-comment {font-family: serif}
+pre.menu-preformatted {font-family: serif}
+span.nolinebreak {white-space: nowrap}
+span.roman {font-family: initial; font-weight: normal}
+span.sansserif {font-family: sans-serif; font-weight: normal}
+span:hover a.copiable-anchor {visibility: visible}
+ul.no-bullet {list-style: none}
+-->
+</style>
+<link rel="stylesheet" type="text/css" href="./manual.css">
+
+
+</head>
+
+<body lang="en">
+<div class="section" id="Tree_002dsitter-C-API">
+<div class="header">
+<p>
+Previous: <a href="Tree_002dsitter-major-modes.html" accesskey="p" 
rel="prev">Developing major modes with tree-sitter</a>, Up: <a 
href="Parsing-Program-Source.html" accesskey="u" rel="up">Parsing Program 
Source</a> &nbsp; [<a href="index.html#SEC_Contents" title="Table of contents" 
rel="contents">Contents</a>][<a href="Index.html" title="Index" 
rel="index">Index</a>]</p>
+</div>
+<hr>
+<span id="Tree_002dsitter-C-API-Correspondence"></span><h3 
class="section">37.8 Tree-sitter C API Correspondence</h3>
+
+<p>Emacs&rsquo; tree-sitter integration doesn&rsquo;t expose every feature
+provided by tree-sitter&rsquo;s C API.  Missing features include:
+</p>
+<ul>
+<li> Creating a tree cursor and navigating the syntax tree with it.
+</li><li> Setting timeout and cancellation flag for a parser.
+</li><li> Setting the logger for a parser.
+</li><li> Printing a <acronym>DOT</acronym> graph of the syntax tree to a file.
+</li><li> Copying and modifying a syntax tree.  (Emacs doesn&rsquo;t expose a 
tree
+object.)
+</li><li> Using (row, column) coordinates as position.
+</li><li> Updating a node with changes.  (In Emacs, retrieve a new node instead
+of updating the existing one.)
+</li><li> Querying statics of a language definition.
+</li></ul>
+
+<p>In addition, Emacs makes some changes to the C API to make the API more
+convenient and idiomatic:
+</p>
+<ul>
+<li> Instead of using byte positions, the Emacs Lisp API uses character
+positions.
+</li><li> Null nodes are converted to nil.
+</li></ul>
+
+<p>Below is the correspondence between all C API functions and their
+ELisp counterparts.  Sometimes one ELisp function corresponds to
+multiple C functions, and many C functions don&rsquo;t have an ELisp
+counterpart.
+</p>
+<div class="example">
+<pre class="example">ts_parser_new                           
treesit-parser-create
+ts_parser_delete
+ts_parser_set_language
+ts_parser_language                      treesit-parser-language
+ts_parser_set_included_ranges           treesit-parser-set-included-ranges
+ts_parser_included_ranges               treesit-parser-included-ranges
+ts_parser_parse
+ts_parser_parse_string                  treesit-parse-string
+ts_parser_parse_string_encoding
+ts_parser_reset
+ts_parser_set_timeout_micros
+ts_parser_timeout_micros
+ts_parser_set_cancellation_flag
+ts_parser_cancellation_flag
+ts_parser_set_logger
+ts_parser_logger
+ts_parser_print_dot_graphs
+ts_tree_copy
+ts_tree_delete
+ts_tree_root_node
+ts_tree_language
+ts_tree_edit
+ts_tree_get_changed_ranges
+ts_tree_print_dot_graph
+ts_node_type                            treesit-node-type
+ts_node_symbol
+ts_node_start_byte                      treesit-node-start
+ts_node_start_point
+ts_node_end_byte                        treesit-node-end
+ts_node_end_point
+ts_node_string                          treesit-node-string
+ts_node_is_null
+ts_node_is_named                        treesit-node-check
+ts_node_is_missing                      treesit-node-check
+ts_node_is_extra                        treesit-node-check
+ts_node_has_changes
+ts_node_has_error                       treesit-node-check
+ts_node_parent                          treesit-node-parent
+ts_node_child                           treesit-node-child
+ts_node_field_name_for_child            treesit-node-field-name-for-child
+ts_node_child_count                     treesit-node-child-count
+ts_node_named_child                     treesit-node-child
+ts_node_named_child_count               treesit-node-child-count
+ts_node_child_by_field_name             treesit-node-by-field-name
+ts_node_child_by_field_id
+ts_node_next_sibling                    treesit-next-sibling
+ts_node_prev_sibling                    treesit-prev-sibling
+ts_node_next_named_sibling              treesit-next-sibling
+ts_node_prev_named_sibling              treesit-prev-sibling
+ts_node_first_child_for_byte            treesit-first-child-for-pos
+ts_node_first_named_child_for_byte      treesit-first-child-for-pos
+ts_node_descendant_for_byte_range       treesit-descendant-for-range
+ts_node_descendant_for_point_range
+ts_node_named_descendant_for_byte_range treesit-descendant-for-range
+ts_node_named_descendant_for_point_range
+ts_node_edit
+ts_node_eq                              treesit-node-eq
+ts_tree_cursor_new
+ts_tree_cursor_delete
+ts_tree_cursor_reset
+ts_tree_cursor_current_node
+ts_tree_cursor_current_field_name
+ts_tree_cursor_current_field_id
+ts_tree_cursor_goto_parent
+ts_tree_cursor_goto_next_sibling
+ts_tree_cursor_goto_first_child
+ts_tree_cursor_goto_first_child_for_byte
+ts_tree_cursor_goto_first_child_for_point
+ts_tree_cursor_copy
+ts_query_new
+ts_query_delete
+ts_query_pattern_count
+ts_query_capture_count
+ts_query_string_count
+ts_query_start_byte_for_pattern
+ts_query_predicates_for_pattern
+ts_query_step_is_definite
+ts_query_capture_name_for_id
+ts_query_string_value_for_id
+ts_query_disable_capture
+ts_query_disable_pattern
+ts_query_cursor_new
+ts_query_cursor_delete
+ts_query_cursor_exec                    treesit-query-capture
+ts_query_cursor_did_exceed_match_limit
+ts_query_cursor_match_limit
+ts_query_cursor_set_match_limit
+ts_query_cursor_set_byte_range
+ts_query_cursor_set_point_range
+ts_query_cursor_next_match
+ts_query_cursor_remove_match
+ts_query_cursor_next_capture
+ts_language_symbol_count
+ts_language_symbol_name
+ts_language_symbol_for_name
+ts_language_field_count
+ts_language_field_name_for_id
+ts_language_field_id_for_name
+ts_language_symbol_type
+ts_language_version
+</pre></div>
+</div>
+<hr>
+<div class="header">
+<p>
+Previous: <a href="Tree_002dsitter-major-modes.html">Developing major modes 
with tree-sitter</a>, Up: <a href="Parsing-Program-Source.html">Parsing Program 
Source</a> &nbsp; [<a href="index.html#SEC_Contents" title="Table of contents" 
rel="contents">Contents</a>][<a href="Index.html" title="Index" 
rel="index">Index</a>]</p>
+</div>
+
+
+
+</body>
+</html>
diff --git a/admin/notes/tree-sitter/html-manual/Using-Parser.html 
b/admin/notes/tree-sitter/html-manual/Using-Parser.html
new file mode 100644
index 0000000000..a4f31f9089
--- /dev/null
+++ b/admin/notes/tree-sitter/html-manual/Using-Parser.html
@@ -0,0 +1,231 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd";>
+<html>
+<!-- Created by GNU Texinfo 6.8, https://www.gnu.org/software/texinfo/ -->
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<!-- This is the GNU Emacs Lisp Reference Manual
+corresponding to Emacs version 29.0.50.
+
+Copyright © 1990-1996, 1998-2022 Free Software Foundation,
+Inc.
+
+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 the
+Invariant Sections being "GNU General Public License," 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.  Buying copies from the FSF supports it in
+developing GNU and promoting software freedom." -->
+<title>Using Parser (GNU Emacs Lisp Reference Manual)</title>
+
+<meta name="description" content="Using Parser (GNU Emacs Lisp Reference 
Manual)">
+<meta name="keywords" content="Using Parser (GNU Emacs Lisp Reference Manual)">
+<meta name="resource-type" content="document">
+<meta name="distribution" content="global">
+<meta name="Generator" content="makeinfo">
+<meta name="viewport" content="width=device-width,initial-scale=1">
+
+<link href="index.html" rel="start" title="Top">
+<link href="Index.html" rel="index" title="Index">
+<link href="index.html#SEC_Contents" rel="contents" title="Table of Contents">
+<link href="Parsing-Program-Source.html" rel="up" title="Parsing Program 
Source">
+<link href="Retrieving-Nodes.html" rel="next" title="Retrieving Nodes">
+<link href="Language-Definitions.html" rel="prev" title="Language Definitions">
+<style type="text/css">
+<!--
+a.copiable-anchor {visibility: hidden; text-decoration: none; line-height: 0em}
+a.summary-letter {text-decoration: none}
+blockquote.indentedblock {margin-right: 0em}
+div.display {margin-left: 3.2em}
+div.example {margin-left: 3.2em}
+kbd {font-style: oblique}
+pre.display {font-family: inherit}
+pre.format {font-family: inherit}
+pre.menu-comment {font-family: serif}
+pre.menu-preformatted {font-family: serif}
+span.nolinebreak {white-space: nowrap}
+span.roman {font-family: initial; font-weight: normal}
+span.sansserif {font-family: sans-serif; font-weight: normal}
+span:hover a.copiable-anchor {visibility: visible}
+ul.no-bullet {list-style: none}
+-->
+</style>
+<link rel="stylesheet" type="text/css" href="./manual.css">
+
+
+</head>
+
+<body lang="en">
+<div class="section" id="Using-Parser">
+<div class="header">
+<p>
+Next: <a href="Retrieving-Nodes.html" accesskey="n" rel="next">Retrieving 
Nodes</a>, Previous: <a href="Language-Definitions.html" accesskey="p" 
rel="prev">Tree-sitter Language Definitions</a>, Up: <a 
href="Parsing-Program-Source.html" accesskey="u" rel="up">Parsing Program 
Source</a> &nbsp; [<a href="index.html#SEC_Contents" title="Table of contents" 
rel="contents">Contents</a>][<a href="Index.html" title="Index" 
rel="index">Index</a>]</p>
+</div>
+<hr>
+<span id="Using-Tree_002dsitter-Parser"></span><h3 class="section">37.2 Using 
Tree-sitter Parser</h3>
+<span id="index-tree_002dsitter-parser_002c-using"></span>
+
+<p>This section describes how to create and configure a tree-sitter
+parser.  In Emacs, each tree-sitter parser is associated with a
+buffer.  As the user edits the buffer, the associated parser and
+syntax tree are automatically kept up-to-date.
+</p>
+<dl class="def">
+<dt id="index-treesit_002dmax_002dbuffer_002dsize"><span 
class="category">Variable: 
</span><span><strong>treesit-max-buffer-size</strong><a 
href='#index-treesit_002dmax_002dbuffer_002dsize' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This variable contains the maximum size of buffers in which
+tree-sitter can be activated.  Major modes should check this value
+when deciding whether to enable tree-sitter features.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dcan_002denable_002dp"><span 
class="category">Function: </span><span><strong>treesit-can-enable-p</strong><a 
href='#index-treesit_002dcan_002denable_002dp' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function checks whether the current buffer is suitable for
+activating tree-sitter features.  It basically checks
+<code>treesit-available-p</code> and <code>treesit-max-buffer-size</code>.
+</p></dd></dl>
+
+<span id="index-creating-tree_002dsitter-parsers"></span>
+<span id="index-tree_002dsitter-parser_002c-creating"></span>
+<dl class="def">
+<dt id="index-treesit_002dparser_002dcreate"><span class="category">Function: 
</span><span><strong>treesit-parser-create</strong> <em>language &amp;optional 
buffer no-reuse</em><a href='#index-treesit_002dparser_002dcreate' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>Create a parser for the specified <var>buffer</var> and 
<var>language</var>
+(see <a href="Language-Definitions.html">Tree-sitter Language 
Definitions</a>).  If <var>buffer</var> is omitted or
+<code>nil</code>, it stands for the current buffer.
+</p>
+<p>By default, this function reuses a parser if one already exists for
+<var>language</var> in <var>buffer</var>, but if <var>no-reuse</var> is
+non-<code>nil</code>, this function always creates a new parser.
+</p></dd></dl>
+
+<p>Given a parser, we can query information about it.
+</p>
+<dl class="def">
+<dt id="index-treesit_002dparser_002dbuffer"><span class="category">Function: 
</span><span><strong>treesit-parser-buffer</strong> <em>parser</em><a 
href='#index-treesit_002dparser_002dbuffer' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function returns the buffer associated with <var>parser</var>.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dparser_002dlanguage"><span 
class="category">Function: 
</span><span><strong>treesit-parser-language</strong> <em>parser</em><a 
href='#index-treesit_002dparser_002dlanguage' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function returns the language used by <var>parser</var>.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dparser_002dp"><span class="category">Function: 
</span><span><strong>treesit-parser-p</strong> <em>object</em><a 
href='#index-treesit_002dparser_002dp' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function checks if <var>object</var> is a tree-sitter parser, and
+returns non-<code>nil</code> if it is, and <code>nil</code> otherwise.
+</p></dd></dl>
+
+<p>There is no need to explicitly parse a buffer, because parsing is done
+automatically and lazily.  A parser only parses when a Lisp program
+queries for a node in its syntax tree.  Therefore, when a parser is
+first created, it doesn&rsquo;t parse the buffer; it waits until the Lisp
+program queries for a node for the first time.  Similarly, when some
+change is made in the buffer, a parser doesn&rsquo;t re-parse immediately.
+</p>
+<span id="index-treesit_002dbuffer_002dtoo_002dlarge"></span>
+<p>When a parser does parse, it checks for the size of the buffer.
+Tree-sitter can only handle buffer no larger than about 4GB.  If the
+size exceeds that, Emacs signals the <code>treesit-buffer-too-large</code>
+error with signal data being the buffer size.
+</p>
+<p>Once a parser is created, Emacs automatically adds it to the
+internal parser list.  Every time a change is made to the buffer,
+Emacs updates parsers in this list so they can update their syntax
+tree incrementally.
+</p>
+<dl class="def">
+<dt id="index-treesit_002dparser_002dlist"><span class="category">Function: 
</span><span><strong>treesit-parser-list</strong> <em>&amp;optional 
buffer</em><a href='#index-treesit_002dparser_002dlist' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This function returns the parser list of <var>buffer</var>.  If
+<var>buffer</var> is <code>nil</code> or omitted, it defaults to the current
+buffer.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dparser_002ddelete"><span class="category">Function: 
</span><span><strong>treesit-parser-delete</strong> <em>parser</em><a 
href='#index-treesit_002dparser_002ddelete' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function deletes <var>parser</var>.
+</p></dd></dl>
+
+<span id="index-tree_002dsitter-narrowing"></span>
+<span id="tree_002dsitter-narrowing"></span><p>Normally, a parser 
&ldquo;sees&rdquo; the whole buffer, but when the buffer is
+narrowed (see <a href="Narrowing.html">Narrowing</a>), the parser will only 
see the accessible
+portion of the buffer.  As far as the parser can tell, the hidden
+region was deleted.  When the buffer is later widened, the parser
+thinks text is inserted at the beginning and at the end.  Although
+parsers respect narrowing, modes should not use narrowing as a means
+to handle a multi-language buffer; instead, set the ranges in which the
+parser should operate.  See <a href="Multiple-Languages.html">Parsing Text in 
Multiple Languages</a>.
+</p>
+<p>Because a parser parses lazily, when the user or a Lisp program
+narrows the buffer, the parser is not affected immediately; as long as
+the mode doesn&rsquo;t query for a node while the buffer is narrowed, the
+parser is oblivious of the narrowing.
+</p>
+<span id="index-tree_002dsitter-parse-string"></span>
+<span id="index-parse-string_002c-tree_002dsitter"></span>
+<p>Besides creating a parser for a buffer, a Lisp program can also parse a
+string.  Unlike a buffer, parsing a string is a one-off operation, and
+there is no way to update the result.
+</p>
+<dl class="def">
+<dt id="index-treesit_002dparse_002dstring"><span class="category">Function: 
</span><span><strong>treesit-parse-string</strong> <em>string language</em><a 
href='#index-treesit_002dparse_002dstring' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function parses <var>string</var> using <var>language</var>, and 
returns
+the root node of the generated syntax tree.
+</p></dd></dl>
+
+<span id="Be-notified-by-changes-to-the-parse-tree"></span><h3 
class="heading">Be notified by changes to the parse tree</h3>
+<span 
id="index-update-callback_002c-for-tree_002dsitter-parse_002dtree"></span>
+<span 
id="index-after_002dchange-notifier_002c-for-tree_002dsitter-parse_002dtree"></span>
+<span 
id="index-tree_002dsitter-parse_002dtree_002c-update-and-after_002dchange-callback"></span>
+<span id="index-notifiers_002c-tree_002dsitter"></span>
+
+<p>A Lisp program might want to be notified of text affected by
+incremental parsing.  For example, inserting a comment-closing token
+converts text before that token into a comment.  Even
+though the text is not directly edited, it is deemed to be 
&ldquo;changed&rdquo;
+nevertheless.
+</p>
+<p>Emacs lets a Lisp program to register callback functions
+(a.k.a. <em>notifiers</em>) for this kind of changes.  A notifier
+function takes two arguments: <var>ranges</var> and <var>parser</var>.
+<var>ranges</var> is a list of cons cells of the form 
<code>(<var>start</var>&nbsp;.&nbsp;<var>end</var>)</code><!-- /@w -->, where 
<var>start</var> and <var>end</var> mark the start and the
+end positions of a range.  <var>parser</var> is the parser issuing the
+notification.
+</p>
+<p>Every time a parser reparses a buffer, it compares the old and new
+parse-tree, computes the ranges in which nodes have changed, and
+passes the ranges to notifier functions.
+</p>
+<dl class="def">
+<dt id="index-treesit_002dparser_002dadd_002dnotifier"><span 
class="category">Function: 
</span><span><strong>treesit-parser-add-notifier</strong> <em>parser 
function</em><a href='#index-treesit_002dparser_002dadd_002dnotifier' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This function adds <var>function</var> to <var>parser</var>&rsquo;s 
list of
+after-change notifier functions.  <var>function</var> must be a function
+symbol, not a lambda function (see <a 
href="Anonymous-Functions.html">Anonymous Functions</a>).
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dparser_002dremove_002dnotifier"><span 
class="category">Function: 
</span><span><strong>treesit-parser-remove-notifier</strong> <em>parser 
function</em><a href='#index-treesit_002dparser_002dremove_002dnotifier' 
class='copiable-anchor'> &para;</a></span></dt>
+<dd><p>This function removes <var>function</var> from the list of 
<var>parser</var>&rsquo;s
+after-change notifier functions.  <var>function</var> must be a function
+symbol, rather than a lambda function.
+</p></dd></dl>
+
+<dl class="def">
+<dt id="index-treesit_002dparser_002dnotifiers"><span 
class="category">Function: 
</span><span><strong>treesit-parser-notifiers</strong> <em>parser</em><a 
href='#index-treesit_002dparser_002dnotifiers' class='copiable-anchor'> 
&para;</a></span></dt>
+<dd><p>This function returns the list of <var>parser</var>&rsquo;s notifier 
functions.
+</p></dd></dl>
+
+</div>
+<hr>
+<div class="header">
+<p>
+Next: <a href="Retrieving-Nodes.html">Retrieving Nodes</a>, Previous: <a 
href="Language-Definitions.html">Tree-sitter Language Definitions</a>, Up: <a 
href="Parsing-Program-Source.html">Parsing Program Source</a> &nbsp; [<a 
href="index.html#SEC_Contents" title="Table of contents" 
rel="contents">Contents</a>][<a href="Index.html" title="Index" 
rel="index">Index</a>]</p>
+</div>
+
+
+
+</body>
+</html>
diff --git a/admin/notes/tree-sitter/html-manual/build-manual.sh 
b/admin/notes/tree-sitter/html-manual/build-manual.sh
new file mode 100755
index 0000000000..8d931b143b
--- /dev/null
+++ b/admin/notes/tree-sitter/html-manual/build-manual.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+MANUAL_DIR="../../../../doc/lispref"
+THIS_DIR=$(pwd)
+
+echo "Build manual"
+cd "${MANUAL_DIR}"
+make elisp.html HTML_OPTS="--html --css-ref=./manual.css"
+
+cd "${THIS_DIR}"
+
+echo "Copy manual"
+cp -f "${MANUAL_DIR}/elisp.html/Parsing-Program-Source.html" .
+cp -f "${MANUAL_DIR}/elisp.html/Language-Definitions.html" .
+cp -f "${MANUAL_DIR}/elisp.html/Using-Parser.html" .
+cp -f "${MANUAL_DIR}/elisp.html/Retrieving-Node.html" .
+cp -f "${MANUAL_DIR}/elisp.html/Accessing-Node.html" .
+cp -f "${MANUAL_DIR}/elisp.html/Pattern-Matching.html" .
+cp -f "${MANUAL_DIR}/elisp.html/Multiple-Languages.html" .
+cp -f "${MANUAL_DIR}/elisp.html/Tree_002dsitter-C-API.html" .
+
+cp -f "${MANUAL_DIR}/elisp.html/Parser_002dbased-Font-Lock.html" .
+cp -f "${MANUAL_DIR}/elisp.html/Parser_002dbased-Indentation.html" .
diff --git a/admin/notes/tree-sitter/html-manual/manual.css 
b/admin/notes/tree-sitter/html-manual/manual.css
new file mode 100644
index 0000000000..5a6790a345
--- /dev/null
+++ b/admin/notes/tree-sitter/html-manual/manual.css
@@ -0,0 +1,374 @@
+/* Style-sheet to use for Emacs manuals  */
+
+/* Copyright (C) 2013-2014 Free Software Foundation, Inc.
+
+Copying and distribution of this file, with or without modification,
+are permitted in any medium without royalty provided the copyright
+notice and this notice are preserved.  This file is offered as-is,
+without any warranty.
+*/
+
+/* style.css begins here */
+
+/* This stylesheet is used by manuals and a few older resources. */
+
+/* reset.css begins here */
+
+/*
+Software License Agreement (BSD License)
+
+Copyright (c) 2006, Yahoo! Inc.
+All rights reserved.
+
+Redistribution and use of this software in source and
+binary forms, with or without modification, arepermitted
+provided that the following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of Yahoo! Inc. nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of Yahoo! Inc.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+*/
+
+html {
+    color: #000;
+    background: #FFF;
+}
+
+body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4,
+h5, h6, pre, code, form, fieldset, legend, input,
+button, textarea, p, blockquote, th, td {
+    margin: 0;
+    padding: 0;
+}
+
+table {
+    border-collapse: collapse;
+    border-spacing: 0;
+}
+
+fieldset, img {
+    border: 0;
+}
+
+address, caption, cite, code, dfn, em, strong,
+th, var, optgroup {
+    font-style: inherit;
+    font-weight: inherit;
+}
+
+del, ins {
+    text-decoration: none;
+}
+
+li {
+    list-style:none;
+}
+
+caption, th {
+    text-align: left;
+}
+
+h1, h2, h3, h4, h5, h6 {
+    font-size: 100%;
+    font-weight: normal;
+}
+
+q:before, q:after {
+    content:'';
+}
+
+abbr, acronym {
+    border: 0;
+    font-variant: normal;
+}
+
+sup {
+    vertical-align: baseline;
+}
+sub {
+    vertical-align: baseline;
+}
+
+legend {
+    color: #000;
+}
+
+input, button, textarea, select, optgroup, option {
+    font-family: inherit;
+    font-size: inherit;
+    font-style: inherit;
+    font-weight: inherit;
+}
+
+input, button, textarea, select {
+    *font-size: 100%;
+}
+
+
+/* reset.css ends here */
+
+/***  PAGE LAYOUT  ***/
+
+html, body {
+    font-size: 1em;
+    text-align: left;
+    text-decoration: none;
+}
+html { background-color: #e7e7e7; }
+
+body {
+    max-width: 74.92em;
+    margin: 0 auto;
+    padding: .5em 1em 1em 1em;
+    background-color: white;
+    border: .1em solid #c0c0c0;
+}
+
+
+/*** BASIC ELEMENTS ***/
+
+/* Size and positioning */
+
+p, pre, li, dt, dd, table, code, address { line-height: 1.3em; }
+
+h1 { font-size: 2em; margin: 1em 0 }
+h2 { font-size: 1.50em; margin: 1.0em 0 0.87em 0; }
+h3 { font-size: 1.30em; margin: 1.0em 0 0.87em 0; }
+h4 { font-size: 1.13em; margin: 1.0em 0 0.88em 0; }
+h5 { font-size: 1.00em; margin: 1.0em 0 1.00em 0; }
+
+p, pre { margin: 1em 0; }
+pre { overflow: auto; padding-bottom: .3em; }
+
+ul, ol, blockquote { margin-left: 1.5%; margin-right: 1.5%; }
+hr { margin: 1em 0; }
+/* Lists of underlined links are difficult to read. The top margin
+   gives a little more spacing between entries. */
+ul li { margin: .5em 1em; }
+ol li { margin: 1em; }
+ol ul li { margin: .5em 1em; }
+ul li p, ul ul li { margin-top: .3em; margin-bottom: .3em; }
+ul ul, ol ul { margin-top: 0; margin-bottom: 0; }
+
+/* Separate description lists from preceding text */
+dl { margin: 1em 0 0 0; }
+/* separate the "term" from subsequent "description" */
+dt { margin: .5em 0; }
+/* separate the "description" from subsequent list item
+   when the final <dd> child is an anonymous box */
+dd { margin: .5em 3% 1em 3%; }
+/* separate anonymous box (used to be the first element in <dd>)
+   from subsequent <p> */
+dd p { margin: .5em 0; }
+
+table {
+    display: block; overflow: auto;
+    margin-top: 1.5em; margin-bottom: 1.5em;
+}
+th { padding: .3em .5em; text-align: center; }
+td { padding: .2em .5em; }
+
+address { margin-bottom: 1em; }
+caption { margin-bottom: .5em; text-align: center; }
+sup { vertical-align: super; }
+sub { vertical-align: sub; }
+
+/* Style */
+
+h1, h2, h3, h4, h5, h6, strong, dt, th { font-weight: bold; }
+
+/* The default color (black) is too dark for large text in
+   bold font. */
+h1, h2, h3, h4 { color: #333; }
+h5, h6, dt { color: #222; }
+
+a[href] { color: #005090; }
+a[href]:visited { color: #100070; }
+a[href]:active, a[href]:hover {
+    color: #100070;
+    text-decoration: none;
+}
+
+h1 a[href]:visited, h2 a[href]:visited, h3 a[href]:visited,
+h4 a[href]:visited { color: #005090; }
+h1 a[href]:hover, h2 a[href]:hover, h3 a[href]:hover,
+h4 a[href]:hover { color: #100070; }
+
+ol { list-style: decimal outside;}
+ul { list-style: square outside; }
+ul ul, ol ul { list-style: circle; }
+li { list-style: inherit; }
+
+hr { background-color: #ede6d5; }
+table { border: 0; }
+
+abbr,acronym {
+    border-bottom:1px dotted #000;
+    text-decoration: none;
+    cursor:help;
+}
+del { text-decoration: line-through; }
+em { font-style: italic; }
+small { font-size: .9em; }
+
+img { max-width: 100%}
+
+
+/*** SIMPLE CLASSES ***/
+
+.center, .c { text-align: center; }
+.nocenter{ text-align: left; }
+
+.underline { text-decoration: underline; }
+.nounderline { text-decoration: none; }
+
+.no-bullet { list-style: none; }
+.inline-list li { display: inline }
+
+.netscape4, .no-display { display: none; }
+
+
+/*** MANUAL PAGES ***/
+
+/* This makes the very long tables of contents in Gnulib and other
+   manuals easier to read. */
+.contents ul, .shortcontents ul { font-weight: bold; }
+.contents ul ul, .shortcontents ul ul { font-weight: normal; }
+.contents ul { list-style: none; }
+
+/* For colored navigation bars (Emacs manual): make the bar extend
+   across the whole width of the page and give it a decent height. */
+.header, .node { margin: 0 -1em; padding: 0 1em; }
+.header p, .node p { line-height: 2em; }
+
+/* For navigation links */
+.node a, .header a { display: inline-block; line-height: 2em; }
+.node a:hover, .header a:hover { background: #f2efe4; }
+
+/* Inserts */
+table.cartouche td { padding: 1.5em; }
+
+div.display, div.lisp, div.smalldisplay,
+div.smallexample, div.smalllisp { margin-left: 3%; }
+
+div.example { padding: .8em 1.2em .4em; }
+pre.example { padding: .8em 1.2em; }
+div.example, pre.example {
+    margin: 1em 0 1em 3% ;
+    -webkit-border-radius: .3em;
+    -moz-border-radius: .3em;
+    border-radius: .3em;
+    border: 1px solid #d4cbb6;
+    background-color: #f2efe4;
+}
+div.example > pre.example {
+    padding: 0 0 .4em;
+    margin: 0;
+    border: none;
+}
+
+pre.menu-comment { padding-top: 1.3em; margin: 0; }
+
+
+/*** FOR WIDE SCREENS ***/
+
+@media (min-width: 40em) {
+    body { padding: .5em 3em 1em 3em; }
+    div.header, div.node { margin: 0 -3em; padding: 0 3em; }
+}
+
+/* style.css ends here */
+
+/* makeinfo convert @deffn and similar functions to something inside
+   <blockquote>.  style.css uses italic for blockquote.  This looks poor
+   in the Emacs manuals, which make extensive use of @defun (etc).
+   In particular, references to function arguments appear as <var>
+   inside <blockquote>.  Since <var> is also italic, it makes it
+   impossible to distinguish variables.  We could change <var> to
+   e.g. bold-italic, or normal, or a different color, but that does
+   not look as good IMO.  So we just override blockquote to be non-italic.
+   */
+blockquote { font-style: normal; }
+
+var { font-style: italic; }
+
+div.header {
+    background-color: #DDDDFF;
+    padding-top: 0.2em;
+}
+
+
+/*** Customization ***/
+
+body {
+    font-family: Charter, serif;
+    font-size: 14pt;
+    line-height: 1.4;
+    background-color: #fefefc;
+    color: #202010;
+}
+
+pre.menu-comment {
+    font-family: Charter, serif;
+    font-size: 14pt;
+}
+
+body > *, body > div.display, body > div.lisp, body > div.smalldisplay,
+body > div.example, body > div.smallexample, body > div.smalllisp {
+    width: 700px;
+    margin-left: auto;
+    margin-right: auto;
+}
+
+div.header {
+    width: 100%;
+    min-height: 3em;
+    font-size: 13pt;
+}
+
+/* Documentation block for functions and variables. Make then
+   narrower*/
+dd {
+    margin: .5em 6% 1em 6%
+}
+
+code, pre, kbd, samp, tt {
+    font-size: 12pt;
+    font-family: monospace;
+}
+
+/* In each node we have index table to all sub-nodes. Make more space
+   for the first column, which is the name to each sub-node. */
+table.menu tbody tr td:nth-child(1) {
+    white-space: nowrap;
+}
+
+div.header p {
+    text-align: center;
+    margin: 0.5em auto 0.5em auto;
+}
diff --git a/admin/notes/tree-sitter/starter-guide 
b/admin/notes/tree-sitter/starter-guide
new file mode 100644
index 0000000000..123dabd9f2
--- /dev/null
+++ b/admin/notes/tree-sitter/starter-guide
@@ -0,0 +1,455 @@
+STARTER GUIDE ON WRITING MAJOR MODE WITH TREE-SITTER -*- org -*-
+
+This document guides you on adding tree-sitter support to a major
+mode.
+
+TOC:
+
+- Building Emacs with tree-sitter
+- Install language definitions
+- Setup
+- Naming convention
+- Font-lock
+- Indent
+- Imenu
+- Navigation
+- Which-func
+- More features?
+- Common tasks (code snippets)
+- Manual
+
+* Building Emacs with tree-sitter
+
+You can either install tree-sitter by your package manager, or from
+source:
+
+    git clone https://github.com/tree-sitter/tree-sitter.git
+    cd tree-sitter
+    make
+    make install
+
+Then pull the tree-sitter branch (or the master branch, if it has
+merged) and rebuild Emacs.
+
+* Install language definitions
+
+Tree-sitter by itself doesn’t know how to parse any particular
+language. We need to install language definitions (or “grammars”) for
+a language to be able to parse it. There are a couple of ways to get
+them.
+
+You can use this script that I put together here:
+
+    https://github.com/casouri/tree-sitter-module
+
+You can also find them under this directory in /build-modules.
+
+This script automatically pulls and builds language definitions for C,
+C++, Rust, JSON, Go, HTML, Javascript, CSS, Python, Typescript,
+and C#. Better yet, I pre-built these language definitions for
+GNU/Linux and macOS, they can be downloaded here:
+
+    https://github.com/casouri/tree-sitter-module/releases/tag/v2.1
+
+To build them yourself, run
+
+    git clone git@github.com:casouri/tree-sitter-module.git
+    cd tree-sitter-module
+    ./batch.sh
+
+and language definitions will be in the /dist directory. You can
+either copy them to standard dynamic library locations of your system,
+eg, /usr/local/lib, or leave them in /dist and later tell Emacs where
+to find language definitions by setting ‘treesit-extra-load-path’.
+
+Language definition sources can be found on GitHub under
+tree-sitter/xxx, like tree-sitter/tree-sitter-python. The tree-sitter
+organization has all the "official" language definitions:
+
+   https://github.com/tree-sitter
+
+* Setting up for adding major mode features
+
+Start Emacs and load tree-sitter with
+
+    (require 'treesit)
+
+Now check if Emacs is built with tree-sitter library
+
+    (treesit-available-p)
+
+* Tree-sitter major modes
+
+Tree-sitter modes should be separate major modes, so other modes
+inheriting from the original mode don't break if tree-sitter is
+enabled.  For example js2-mode inherits js-mode, we can't enable
+tree-sitter in js-mode, lest js-mode would not setup things that
+js2-mode expects to inherit from. So it's best to use separate major
+modes.
+
+If the tree-sitter variant and the "native" variant could share some
+setup, you can create a "base mode", which only contains the common
+setup.  For example, there is python-base-mode (shared), python-mode
+(native), and python-ts-mode (tree-sitter).
+
+In the tree-sitter mode, check if we can use tree-sitter with
+treesit-ready-p, it will error out if tree-sitter is not ready.
+
+* Naming convention
+
+Use tree-sitter for text (documentation, comment), use treesit for
+symbol (variable, function).
+
+* Font-lock
+
+Tree-sitter works like this: You provide a query made of patterns and
+capture names, tree-sitter finds the nodes that match these patterns,
+tag the corresponding capture names onto the nodes and return them to
+you. The query function returns a list of (capture-name . node). For
+font-lock, we use face names as capture names. And the captured node
+will be fontified in their capture name.
+
+The capture name could also be a function, in which case (NODE
+OVERRIDE START END) is passed to the function for fontification. START
+and END are the start and end of the region to be fontified.  The
+function should only fontify within that region.  The function should
+also allow more optional arguments with (&rest _), for future
+extensibility.  For OVERRIDE check out the docstring of
+treesit-font-lock-rules.
+
+** Query syntax
+
+There are two types of nodes, named, like (identifier),
+(function_definition), and anonymous, like "return", "def", "(",
+"}". Parent-child relationship is expressed as
+
+   (parent (child) (child) (child (grand_child)))
+
+Eg, an argument list (1, "3", 1) could be:
+
+   (argument_list "(" (number) (string) (number) ")")
+
+Children could have field names in its parent:
+
+   (function_definition name: (identifier) type: (identifier))
+
+Match any of the list:
+
+    ["true" "false" "none"]
+
+Capture names can come after any node in the pattern:
+
+    (parent (child) @child) @parent
+
+The query above captures both parent and child.
+
+   ["return" "continue" "break"] @keyword
+
+The query above captures all the keywords with capture name
+"keyword".
+
+These are the common syntax, see all of them in the manual
+("Parsing Program Source" section).
+
+** Query references
+
+But how do one come up with the queries? Take python for an example,
+open any python source file, type M-x treesit-explore-mode RET.  Now
+you should see the parse-tree in a separate window, automatically
+updated as you select text or edit the buffer.  Besides this, you can
+consult the grammar of the language definition. For example, Python’s
+grammar file is at
+
+    https://github.com/tree-sitter/tree-sitter-python/blob/master/grammar.js
+
+Neovim also has a bunch of queries to reference:
+
+    https://github.com/nvim-treesitter/nvim-treesitter/tree/master/queries
+
+The manual explains how to read grammar files in the bottom of section
+"Tree-sitter Language Definitions".
+
+** Debugging queries
+
+If your query has problems, use ‘treesit-query-validate’ to debug the
+query. It will pop a buffer containing the query (in text format) and
+mark the offending part in red.
+
+** Code
+
+To enable tree-sitter font-lock, set ‘treesit-font-lock-settings’ and
+‘treesit-font-lock-feature-list’ buffer-locally and call
+‘treesit-major-mode-setup’. For example, see
+‘python--treesit-settings’ in python.el. Below I paste a snippet of
+it.
+
+Note that like the current font-lock, if the to-be-fontified region
+already has a face (ie, an earlier match fontified part/all of the
+region), the new face is discarded rather than applied. If you want
+later matches always override earlier matches, use the :override
+keyword.
+
+Each rule should have a :feature, like function-name,
+string-interpolation, builtin, etc. Users can then enable/disable each
+feature individually.
+
+#+begin_src elisp
+(defvar python--treesit-settings
+  (treesit-font-lock-rules
+   :feature 'comment
+   :language 'python
+   '((comment) @font-lock-comment-face)
+
+   :feature 'string
+   :language 'python
+   '((string) @font-lock-string-face
+     (string) @contextual) ; Contextual special treatment.
+
+   :feature 'function-name
+   :language 'python
+   '((function_definition
+      name: (identifier) @font-lock-function-name-face))
+
+   :feature 'class-name
+   :language 'python
+   '((class_definition
+      name: (identifier) @font-lock-type-face))
+
+   ...))
+#+end_src
+
+Then in ‘python-mode’, enable tree-sitter font-lock:
+
+#+begin_src elisp
+(treesit-parser-create 'python)
+(setq-local treesit-font-lock-settings python--treesit-settings)
+(setq-local treesit-font-lock-feature-list
+            '((comment string function-name)
+              (class-name keyword builtin)
+              (string-interpolation decorator)))
+...
+(treesit-major-mode-setup)
+#+end_src
+
+Concretely, something like this:
+
+#+begin_src elisp
+(define-derived-mode python-mode prog-mode "Python"
+  ...
+  (cond
+   ;; Tree-sitter.
+   ((treesit-ready-p 'python-mode 'python)
+    (treesit-parser-create 'python)
+    (setq-local treesit-font-lock-settings python--treesit-settings)
+    (setq-local treesit-font-lock-feature-list
+                '((comment string function-name)
+                  (class-name keyword builtin)
+                  (string-interpolation decorator)))
+    (treesit-major-mode-setup))
+   (t
+    ;; No tree-sitter
+    (setq-local font-lock-defaults ...)
+    ...)))
+#+end_src
+
+* Indent
+
+Indent works like this: We have a bunch of rules that look like
+
+   (MATCHER ANCHOR OFFSET)
+
+When the indentation process starts, point is at the BOL of a line, we
+want to know which column to indent this line to. Let NODE be the node
+at point, we pass this node to the MATCHER of each rule, one of them
+will match the node (eg, "this node is a closing bracket!"). Then we
+pass the node to the ANCHOR, which returns a point, eg, the BOL of the
+previous line. We find the column number of that point (eg, 4), add
+OFFSET to it (eg, 0), and that is the column we want to indent the
+current line to (4 + 0 = 4).
+
+Matchers and anchors are functions that takes (NODE PARENT BOL &rest
+_). Matches return nil/non-nil for no match/match, and anchors return
+the anchor point. Below are some convenient builtin matchers and anchors.
+
+For MATHCER we have
+
+    (parent-is TYPE) => matches if PARENT’s type matches TYPE as regexp
+    (node-is TYPE) => matches NODE’s type
+    (query QUERY) => matches if querying PARENT with QUERY
+                     captures NODE.
+
+    (match NODE-TYPE PARENT-TYPE NODE-FIELD
+           NODE-INDEX-MIN NODE-INDEX-MAX)
+
+    => checks everything. If an argument is nil, don’t match that. Eg,
+    (match nil nil TYPE) is the same as (parent-is TYPE)
+
+For ANCHOR we have
+
+    first-sibling => start of the first sibling
+    parent => start of parent
+    parent-bol => BOL of the line parent is on.
+    prev-sibling => start of previous sibling
+    no-indent => current position (don’t indent)
+    prev-line => start of previous line
+
+There is also a manual section for indent: "Parser-based Indentation".
+
+When writing indent rules, you can use ‘treesit-check-indent’ to
+check if your indentation is correct. To debug what went wrong, set
+‘treesit--indent-verbose’ to non-nil. Then when you indent, Emacs
+tells you which rule is applied in the echo area.
+
+#+begin_src elisp
+(defvar typescript-mode-indent-rules
+  (let ((offset typescript-indent-offset))
+    `((typescript
+       ;; This rule matches if node at point is "}", ANCHOR is the
+       ;; parent node’s BOL, and offset is 0.
+       ((node-is "}") parent-bol 0)
+       ((node-is ")") parent-bol 0)
+       ((node-is "]") parent-bol 0)
+       ((node-is ">") parent-bol 0)
+       ((node-is "\\.") parent-bol ,offset)
+       ((parent-is "ternary_expression") parent-bol ,offset)
+       ((parent-is "named_imports") parent-bol ,offset)
+       ((parent-is "statement_block") parent-bol ,offset)
+       ((parent-is "type_arguments") parent-bol ,offset)
+       ((parent-is "variable_declarator") parent-bol ,offset)
+       ((parent-is "arguments") parent-bol ,offset)
+       ((parent-is "array") parent-bol ,offset)
+       ((parent-is "formal_parameters") parent-bol ,offset)
+       ((parent-is "template_substitution") parent-bol ,offset)
+       ((parent-is "object_pattern") parent-bol ,offset)
+       ((parent-is "object") parent-bol ,offset)
+       ((parent-is "object_type") parent-bol ,offset)
+       ((parent-is "enum_body") parent-bol ,offset)
+       ((parent-is "arrow_function") parent-bol ,offset)
+       ((parent-is "parenthesized_expression") parent-bol ,offset)
+       ...))))
+#+end_src
+
+Then you set ‘treesit-simple-indent-rules’ to your rules, and call
+‘treesit-major-mode-setup’:
+
+#+begin_src elisp
+(setq-local treesit-simple-indent-rules typescript-mode-indent-rules)
+(treesit-major-mode-setup)
+#+end_src
+
+* Imenu
+
+Not much to say except for utilizing ‘treesit-induce-sparse-tree’ (and
+explicitly pass a LIMIT argument: most of the time you don't need more
+than 10).  See ‘js--treesit-imenu-1’ in js.el for an example.
+
+Once you have the index builder, set ‘imenu-create-index-function’ to
+it.
+
+* Navigation
+
+Mainly ‘beginning-of-defun-function’ and ‘end-of-defun-function’.
+You can find the end of a defun with something like
+
+(treesit-search-forward-goto "function_definition" 'end)
+
+where "function_definition" matches the node type of a function
+definition node, and ’end means we want to go to the end of that node.
+
+Tree-sitter has default implementations for
+‘beginning-of-defun-function’ and ‘end-of-defun-function’.  So for
+ordinary languages, it is enough to set ‘treesit-defun-type-regexp’
+to something that matches all the defun struct types in the language,
+and call ‘treesit-major-mode-setup’.  For example,
+
+#+begin_src emacs-lisp
+(setq-local treesit-defun-type-regexp (rx bol
+                                          (or "function" "class")
+                                          "_definition"
+                                          eol))
+(treesit-major-mode-setup)
+#+end_src>
+
+* Which-func
+
+If you have an imenu implementation, set ‘which-func-functions’ to
+nil, and which-func will automatically use imenu’s data.
+
+If you want an independent implementation for which-func, you can
+find the current function by going up the tree and looking for the
+function_definition node. See the function below for an example.
+Since Python allows nested function definitions, that function keeps
+going until it reaches the root node, and records all the function
+names along the way.
+
+#+begin_src elisp
+(defun python-info-treesit-current-defun (&optional include-type)
+  "Identical to `python-info-current-defun' but use tree-sitter.
+For INCLUDE-TYPE see `python-info-current-defun'."
+  (let ((node (treesit-node-at (point)))
+        (name-list ())
+        (type nil))
+    (cl-loop while node
+             if (pcase (treesit-node-type node)
+                  ("function_definition"
+                   (setq type 'def))
+                  ("class_definition"
+                   (setq type 'class))
+                  (_ nil))
+             do (push (treesit-node-text
+                       (treesit-node-child-by-field-name node "name")
+                       t)
+                      name-list)
+             do (setq node (treesit-node-parent node))
+             finally return (concat (if include-type
+                                        (format "%s " type)
+                                      "")
+                                    (string-join name-list ".")))))
+#+end_src
+
+* More features?
+
+Obviously this list is just a starting point, if there are features in
+the major mode that would benefit from a parse tree, adding tree-sitter
+support for that would be great. But in the minimal case, just adding
+font-lock is awesome.
+
+* Common tasks
+
+How to...
+
+** Get the buffer text corresponding to a node?
+
+(treesit-node-text node)
+
+BTW ‘treesit-node-string’ does different things.
+
+** Scan the whole tree for stuff?
+
+(treesit-search-subtree)
+(treesit-search-forward)
+(treesit-induce-sparse-tree)
+
+** Move to next node that...?
+
+(treesit-search-forward-goto)
+
+** Get the root node?
+
+(treesit-buffer-root-node)
+
+** Get the node at point?
+
+(treesit-node-at (point))
+
+* Manual
+
+I suggest you read the manual section for tree-sitter in Info. The
+section is Parsing Program Source. Typing
+
+    C-h i d m elisp RET g Parsing Program Source RET
+
+will bring you to that section. You can also read the HTML version
+under /html-manual in this directory. I find the HTML version easier
+to read. You don’t need to read through every sentence, just read the
+text paragraphs and glance over function names.
diff --git a/configure.ac b/configure.ac
index b656dba4d9..187a43dc3e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -463,6 +463,7 @@ OPTION_DEFAULT_ON([xml2],[don't compile with XML parsing 
support])
 OPTION_DEFAULT_OFF([imagemagick],[compile with ImageMagick image support])
 OPTION_DEFAULT_ON([native-image-api], [don't use native image APIs (GDI+ on 
Windows)])
 OPTION_DEFAULT_IFAVAILABLE([json], [compile with native JSON support])
+OPTION_DEFAULT_IFAVAILABLE([tree-sitter], [compile with tree-sitter])
 
 OPTION_DEFAULT_ON([xft],[don't use XFT for anti aliased fonts])
 OPTION_DEFAULT_ON([harfbuzz],[don't use HarfBuzz for text shaping])
@@ -3217,6 +3218,50 @@ AC_SUBST([JSON_LIBS])
 AC_SUBST([JSON_CFLAGS])
 AC_SUBST([JSON_OBJ])
 
+HAVE_TREE_SITTER=no
+TREE_SITTER_OBJ=
+
+if test "${with_tree_sitter}" != "no"; then
+   dnl Tree-sitter 0.20.2 added support to change the malloc it uses
+   dnl at runtime, we need that feature.  However, tree-sitter's
+   dnl Makefile has problems, until that's fixed, all tree-sitter
+   dnl libraries distributed are versioned 0.6.3.  We try to
+   dnl accept a tree-sitter library that has incorrect version as long
+   dnl as it supports changing malloc.
+  EMACS_CHECK_MODULES([TREE_SITTER], [tree-sitter >= 0.20.2],
+    [HAVE_TREE_SITTER=yes], [HAVE_TREE_SITTER=no])
+  if test "${HAVE_TREE_SITTER}" = yes; then
+    AC_DEFINE(HAVE_TREE_SITTER, 1, [Define if using tree-sitter.])
+  else
+    EMACS_CHECK_MODULES([TREE_SITTER], [tree-sitter >= 0.6.3],
+      [HAVE_TREE_SITTER=yes], [HAVE_TREE_SITTER=no])
+    if test "${HAVE_TREE_SITTER}" = yes; then
+      OLD_CFLAGS=$CFLAGS
+      OLD_LIBS=$LIBS
+      CFLAGS="$CFLAGS $TREE_SITTER_CFLAGS"
+      LIBS="$TREE_SITTER_LIBS $LIBS"
+      AC_CHECK_FUNCS([ts_set_allocator])
+      CFLAGS=$OLD_CFLAGS
+      LIBS=$OLD_LIBS
+      if test "$ac_cv_func_ts_set_allocator" == yes; then
+        AC_DEFINE(HAVE_TREE_SITTER, 1, [Define if using tree-sitter.])
+      else
+        AC_MSG_ERROR([Tree-sitter library exists but its version is too old]);
+        TREE_SITTER_CFLAGS=
+        TREE_SITTER_LIBS=
+      fi
+    fi
+  fi
+
+  # Windows loads tree-sitter dynamically
+  if test "${opsys}" = "mingw32"; then
+    TREE_SITTER_LIBS=
+  fi
+fi
+
+AC_SUBST(TREE_SITTER_LIBS)
+AC_SUBST(TREE_SITTER_CFLAGS)
+
 NOTIFY_OBJ=
 NOTIFY_SUMMARY=no
 
@@ -4087,20 +4132,31 @@ if test "${HAVE_ZLIB}" = "yes"; then
 fi
 AC_SUBST([LIBZ])
 
+### Dynamic library support
+case $opsys in
+  cygwin|mingw32) DYNAMIC_LIB_SUFFIX=".dll" ;;
+  darwin) DYNAMIC_LIB_SUFFIX=".dylib" ;;
+  *) DYNAMIC_LIB_SUFFIX=".so" ;;
+esac
+case "${opsys}" in
+  darwin) DYNAMIC_LIB_SECONDARY_SUFFIX='.so' ;;
+  *) DYNAMIC_LIB_SECONDARY_SUFFIX='' ;;
+esac
+AC_DEFINE_UNQUOTED(DYNAMIC_LIB_SUFFIX, "$DYNAMIC_LIB_SUFFIX",
+  [System extension for dynamic libraries])
+AC_DEFINE_UNQUOTED(DYNAMIC_LIB_SECONDARY_SUFFIX, 
"$DYNAMIC_LIB_SECONDARY_SUFFIX",
+  [Alternative system extension for dynamic libraries.])
+
+AC_SUBST(DYNAMIC_LIB_SUFFIX)
+AC_SUBST(DYNAMIC_LIB_SECONDARY_SUFFIX)
+
 ### Dynamic modules support
 LIBMODULES=
 HAVE_MODULES=no
 MODULES_OBJ=
 NEED_DYNLIB=no
-case $opsys in
-  cygwin|mingw32) MODULES_SUFFIX=".dll" ;;
-  darwin) MODULES_SUFFIX=".dylib" ;;
-  *) MODULES_SUFFIX=".so" ;;
-esac
-case "${opsys}" in
-  darwin) MODULES_SECONDARY_SUFFIX='.so' ;;
-  *) MODULES_SECONDARY_SUFFIX='' ;;
-esac
+MODULES_SUFFIX="${DYNAMIC_LIB_SUFFIX}"
+MODULES_SECONDARY_SUFFIX="${DYNAMIC_LIB_SECONDARY_SUFFIX}"
 
 # pgtkterm.c uses dlsym
 if test $window_system = pgtk; then
@@ -4517,6 +4573,12 @@ case $with_json,$HAVE_JSON in
   *) MISSING="$MISSING json"
      WITH_IFAVAILABLE="$WITH_IFAVAILABLE --with-json=ifavailable";;
 esac
+case $with_tree_sitter,$HAVE_TREE_SITTER in
+  no,* | ifavailable,* | *,yes) ;;
+  *) MISSING="$MISSING tree-sitter"
+     WITH_IFAVAILABLE="$WITH_IFAVAILABLE --with-tree-sitter=ifavailable";;
+esac
+
 if test "X${MISSING}" != X; then
   # If we have a missing library, and we don't have pkg-config installed,
   # the missing pkg-config may be the reason.  Give the user a hint.
@@ -6559,7 +6621,7 @@ emacs_config_features=
 for opt in ACL BE_APP CAIRO DBUS FREETYPE GCONF GIF GLIB GMP GNUTLS GPM 
GSETTINGS \
  HARFBUZZ IMAGEMAGICK JPEG JSON LCMS2 LIBOTF LIBSELINUX LIBSYSTEMD LIBXML2 \
  M17N_FLT MODULES NATIVE_COMP NOTIFY NS OLDXMENU PDUMPER PGTK PNG RSVG SECCOMP 
\
- SOUND SQLITE3 THREADS TIFF TOOLKIT_SCROLL_BARS \
+ SOUND SQLITE3 THREADS TIFF TOOLKIT_SCROLL_BARS TREE_SITTER \
  UNEXEC WEBP X11 XAW3D XDBE XFT XIM XINPUT2 XPM XWIDGETS X_TOOLKIT \
  ZLIB; do
 
@@ -6628,6 +6690,7 @@ AS_ECHO(["  Does Emacs use -lXaw3d?                       
          ${HAVE_XAW3D
   Does Emacs use -lxft?                                   ${HAVE_XFT}
   Does Emacs use -lsystemd?                               ${HAVE_LIBSYSTEMD}
   Does Emacs use -ljansson?                               ${HAVE_JSON}
+  Does Emacs use -ltree-sitter?                           ${HAVE_TREE_SITTER}
   Does Emacs use the GMP library?                         ${HAVE_GMP}
   Does Emacs directly use zlib?                           ${HAVE_ZLIB}
   Does Emacs have dynamic modules support?                ${HAVE_MODULES}
diff --git a/doc/emacs/ChangeLog.1 b/doc/emacs/ChangeLog.1
index 5647538a24..6669e28e6b 100644
--- a/doc/emacs/ChangeLog.1
+++ b/doc/emacs/ChangeLog.1
@@ -1356,7 +1356,7 @@
 
        * dired.texi (Dired Deletion, Marks vs Flags): Document Emacs 24.3
        changes to the mark and unmark commands.
-       (Comparison in Dired): Document chages to dired-diff.  Remove M-=,
+       (Comparison in Dired): Document changes to dired-diff.  Remove M-=,
        which is no longer bound to dired-backup-diff.
 
 2012-10-23  Bastien Guerry  <bzg@gnu.org>
diff --git a/doc/emacs/custom.texi b/doc/emacs/custom.texi
index 08ada2a70b..aaf41d2aef 100644
--- a/doc/emacs/custom.texi
+++ b/doc/emacs/custom.texi
@@ -805,12 +805,12 @@ C-h v fill-column @key{RET}
 displays something like this:
 
 @example
-fill-column is a variable defined in ‘C source code’.
+fill-column is a variable defined in @quoteleft{}C source code@quoteright{}.
 Its value is 70
 
   Automatically becomes buffer-local when set.
   This variable is safe as a file local variable if its value
-  satisfies the predicate ‘integerp’.
+  satisfies the predicate @quoteleft{}integerp@quoteright{}.
   Probably introduced at or before Emacs version 18.
 
 Documentation:
@@ -1659,7 +1659,7 @@ events.
 or lower case; @acronym{ASCII} or non-@acronym{ASCII}) are reserved
 for users.  Emacs itself will never bind those key sequences, and
 Emacs extensions should avoid binding them.  In other words, users can
-bind key sequences like @kbd{C-c a} or @kbd{C-c ç} and rely on these
+bind key sequences like @kbd{C-c a} or @kbd{C-c @,{c}} and rely on these
 never being shadowed by other Emacs bindings.
 
 @node Prefix Keymaps
diff --git a/doc/emacs/dired.texi b/doc/emacs/dired.texi
index a9b4ff783d..7a382c0566 100644
--- a/doc/emacs/dired.texi
+++ b/doc/emacs/dired.texi
@@ -807,7 +807,7 @@ should create non-existent directories in @var{new}.
 
 The option @code{dired-create-destination-dirs-on-trailing-dirsep},
 when set in addition to @code{dired-create-destination-dirs}, controls
-wether a trailing directory separator at the destination is treated
+whether a trailing directory separator at the destination is treated
 specially.  In that case, when renaming a directory @samp{old} to
 @samp{new/} and no directory @samp{new} exists already, it will be
 created and @samp{old} is moved into the newly created directory.
diff --git a/doc/emacs/maintaining.texi b/doc/emacs/maintaining.texi
index 3e03bd817a..44e9e1896f 100644
--- a/doc/emacs/maintaining.texi
+++ b/doc/emacs/maintaining.texi
@@ -1832,6 +1832,8 @@ directory.  @xref{Top,Eshell,Eshell, eshell, Eshell: The 
Emacs Shell}.
 @item C-x p b
 Switch to another buffer belonging to the current project
 (@code{project-switch-to-buffer}).
+@item C-x p C-b
+List the project buffers (@code{project-list-buffers}).
 @item C-x p k
 Kill all live buffers that belong to the current project
 (@code{project-kill-buffers}).
@@ -1847,6 +1849,11 @@ switch between buffers that belong to the current 
project by prompting
 for a buffer to switch and considering only the current project's
 buffers as candidates for completion.
 
+@findex project-list-buffers
+  Like the command @code{list-buffers} (@pxref{List Buffers}), the
+command @kbd{C-x p C-b} (@code{project-list-buffers}) displays a list
+of existing buffers, but only belonging to the current project.
+
 @findex project-kill-buffers
 @vindex project-kill-buffer-conditions
 @vindex project-kill-buffers-display-buffer-list
diff --git a/doc/emacs/mark.texi b/doc/emacs/mark.texi
index db96093a17..5472a288d1 100644
--- a/doc/emacs/mark.texi
+++ b/doc/emacs/mark.texi
@@ -50,10 +50,10 @@ Ring}.  Additionally, some commands will have an effect 
even on an
 inactive region (for example @dfn{upcase-region}).  You can also
 reactivate the region with commands like @kbd{C-x C-x}.
 
-  The above default behavior is known as Transient Mark mode.
-Disabling Transient Mark mode switches Emacs to an alternative
-behavior, in which the region is usually not highlighted.
-@xref{Disabled Transient Mark}.
+  The above behavior, which is the default in interactive sessions, is
+known as Transient Mark mode.  Disabling Transient Mark mode switches
+Emacs to an alternative behavior, in which the region is usually not
+highlighted.  @xref{Disabled Transient Mark}.
 
 @vindex highlight-nonselected-windows
   Setting the mark in one buffer has no effect on the marks in other
@@ -455,10 +455,11 @@ motion keys will extend the region set by shift-selection.
 
   The default behavior of the mark and region, in which setting the
 mark activates it and highlights the region, is called Transient Mark
-mode.  This is a minor mode that is enabled by default.  It can be
-toggled with @kbd{M-x transient-mark-mode}, or with the
-@samp{Highlight Active Region} menu item in the @samp{Options} menu.
-Turning it off switches Emacs to an alternative mode of operation:
+mode.  This is a minor mode that is enabled by default in interactive
+sessions.  It can be toggled with @kbd{M-x transient-mark-mode}, or
+with the @samp{Highlight Active Region} menu item in the
+@samp{Options} menu.  Turning it off switches Emacs to an alternative
+mode of operation:
 
 @itemize @bullet
 @item
diff --git a/doc/emacs/package.texi b/doc/emacs/package.texi
index f9fa28074f..cd4c113ae5 100644
--- a/doc/emacs/package.texi
+++ b/doc/emacs/package.texi
@@ -559,7 +559,7 @@ package, without adding it to the package list, use
 @code{package-vc-checkout}.
 
 @vindex package-vc-selected-packages
-@findex package-vc-ensure-packages
+@findex package-vc-install-selected-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
@@ -567,8 +567,8 @@ packages to install, where each key is a package name and 
the value is
 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:
+@code{package-vc-install-selected-packages}.  Here is an example of
+how the user option:
 
 @example
 @group
@@ -593,7 +593,7 @@ 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
+@findex package-vc-rebuild
   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
diff --git a/doc/lispref/Makefile.in b/doc/lispref/Makefile.in
index 8a61adf232..6999169689 100644
--- a/doc/lispref/Makefile.in
+++ b/doc/lispref/Makefile.in
@@ -111,6 +111,7 @@ srcs = \
   $(srcdir)/objects.texi \
   $(srcdir)/os.texi \
   $(srcdir)/package.texi \
+  $(srcdir)/parsing.texi \
   $(srcdir)/positions.texi \
   $(srcdir)/processes.texi \
   $(srcdir)/records.texi \
diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index c75107fb58..10a1a18dd1 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -2273,7 +2273,7 @@ complex text shaping requires that for some scripts.  
When that
 happens, characters no longer map in a simple way to display columns,
 and display layout decisions with such strings, such as truncating too
 wide strings, can be a complex job.  This function helps in performing
-suvh jobs: it splits up its argument @var{string} into a list of
+such jobs: it splits up its argument @var{string} into a list of
 substrings, where each substring produces a single grapheme cluster
 that should be displayed as a unit.  Lisp programs can then use this
 list to construct visually-valid substrings of @var{string} which will
diff --git a/doc/lispref/elisp.texi b/doc/lispref/elisp.texi
index a3d1d80408..b1bbe5e0a9 100644
--- a/doc/lispref/elisp.texi
+++ b/doc/lispref/elisp.texi
@@ -222,6 +222,7 @@ To view this manual in other formats, click
 * Non-ASCII Characters::    Non-ASCII text in buffers and strings.
 * Searching and Matching::  Searching buffers for strings or regexps.
 * Syntax Tables::           The syntax table controls word and list parsing.
+* Parsing Program Source::  Generate syntax tree for program sources.
 * Abbrevs::                 How Abbrev mode works, and its data structures.
 
 * Threads::                 Concurrency in Emacs Lisp.
@@ -937,6 +938,7 @@ Font Lock Mode
 * Syntactic Font Lock::     Fontification based on syntax tables.
 * Multiline Font Lock::     How to coerce Font Lock into properly
                               highlighting multiline constructs.
+* Parser-based Font Lock::  Use parse data for fontification.
 
 Multiline Font Lock Constructs
 
@@ -947,6 +949,7 @@ Multiline Font Lock Constructs
 Automatic Indentation of code
 
 * SMIE::                    A simple minded indentation engine.
+* Parser-based Indentation:: Parser-based indentation engine.
 
 Simple Minded Indentation Engine
 
@@ -1359,6 +1362,17 @@ Syntax Tables
 * Syntax Table Internals::  How syntax table information is stored.
 * Categories::              Another way of classifying character syntax.
 
+Parsing Program Source
+
+* Language Definitions::     Loading tree-sitter language definitions.
+* Using Parser::             Introduction to parsers.
+* Retrieving Nodes::         Retrieving nodes from a syntax tree.
+* Accessing Node Information:: Accessing node information.
+* Pattern Matching::         Pattern matching with query patterns.
+* Multiple Languages::       Parse text written in multiple languages.
+* Tree-sitter major modes::  Develop major modes using tree-sitter.
+* Tree-sitter C API::        Compare the C API and the ELisp API.
+
 Syntax Descriptors
 
 * Syntax Class Table::      Table of syntax classes.
@@ -1703,6 +1717,7 @@ Object Internals
 
 @include searching.texi
 @include syntax.texi
+@include parsing.texi
 @include abbrevs.texi
 @include threads.texi
 @include processes.texi
diff --git a/doc/lispref/errors.texi b/doc/lispref/errors.texi
index 44a62dcebc..cb05290abd 100644
--- a/doc/lispref/errors.texi
+++ b/doc/lispref/errors.texi
@@ -242,7 +242,7 @@ The message is @samp{Cannot determine image type}.  
@xref{Images}.
 
 @item inhibited-interaction
 The message is @samp{User interaction while inhibited}.  This error is
-signalled when @code{inhibit-interaction} is non-@code{nil} and a user
+signaled when @code{inhibit-interaction} is non-@code{nil} and a user
 interaction function (like @code{read-from-minibuffer}) is called.
 @end table
 
diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi
index 7ffde7d43d..9d5a266191 100644
--- a/doc/lispref/functions.texi
+++ b/doc/lispref/functions.texi
@@ -734,7 +734,7 @@ a list of symbols representing the function alias chain, 
else
     @result{} (b c)
 @end example
 
-If there's a loop in the definitions, an error will be signalled.  If
+If there's a loop in the definitions, an error will be signaled.  If
 @var{noerror} is non-@code{nil}, the non-looping parts of the chain is
 returned instead.
 @end defun
diff --git a/doc/lispref/internals.texi b/doc/lispref/internals.texi
index ea1679f693..4640b6d759 100644
--- a/doc/lispref/internals.texi
+++ b/doc/lispref/internals.texi
@@ -554,12 +554,17 @@ trigger another garbage collection.  You can use the 
result returned by
 object type; space allocated to the contents of buffers does not count.
 
 The initial threshold value is @code{GC_DEFAULT_THRESHOLD}, defined in
-@file{alloc.c}.  Since it's defined in @code{word_size} units, the value
-is 400,000 for the default 32-bit configuration and 800,000 for the 64-bit
-one.  If you specify a larger value, garbage collection will happen less
-often.  This reduces the amount of time spent garbage collecting, but
-increases total memory use.  You may want to do this when running a program
-that creates lots of Lisp data.
+@file{alloc.c}.  Since it's defined in @code{word_size} units, the
+value is 400,000 for the default 32-bit configuration and 800,000 for
+the 64-bit one.  If you specify a larger value, garbage collection
+will happen less often.  This reduces the amount of time spent garbage
+collecting, but increases total memory use.  You may want to do this
+when running a program that creates lots of Lisp data.  However, we
+recommend against increasing the threshold for prolonged periods of
+time, and advise that you never set it higher than needed for the
+program to run in reasonable time.  Using thresholds higher than
+necessary could potentially cause system-wide memory pressure, and
+should therefore be avoided.
 
 You can make collections more frequent by specifying a smaller value, down
 to 1/10th of @code{GC_DEFAULT_THRESHOLD}.  A value less than this minimum
@@ -576,6 +581,9 @@ garbage collection occurs only when both criteria are 
satisfied.
 As the heap size increases, the time to perform a garbage collection
 increases.  Thus, it can be desirable to do them less frequently in
 proportion.
+
+As with @code{gc-cons-threshold}, do not enlarge this more than
+necessary, and never for prolonged periods of time.
 @end defopt
 
   Control over the garbage collector via @code{gc-cons-threshold} and
diff --git a/doc/lispref/minibuf.texi b/doc/lispref/minibuf.texi
index 089ae41f32..332a453619 100644
--- a/doc/lispref/minibuf.texi
+++ b/doc/lispref/minibuf.texi
@@ -2745,7 +2745,7 @@ Here's a typical use case:
 If @code{my-client-handling-function} ends up calling something that
 asks the user for something (via @code{y-or-n-p} or
 @code{read-from-minibuffer} or the like), an
-@code{inhibited-interaction} error is signalled instead.  The server
+@code{inhibited-interaction} error is signaled instead.  The server
 code then catches that error and reports it to the client.
 
 @node Minibuffer Misc
diff --git a/doc/lispref/modes.texi b/doc/lispref/modes.texi
index 9527df33b8..c472f9b441 100644
--- a/doc/lispref/modes.texi
+++ b/doc/lispref/modes.texi
@@ -2853,11 +2853,14 @@ mode; most major modes define syntactic criteria for 
which faces to use
 in which contexts.  This section explains how to customize Font Lock for
 a particular major mode.
 
-  Font Lock mode finds text to highlight in two ways: through
-syntactic parsing based on the syntax table, and through searching
-(usually for regular expressions).  Syntactic fontification happens
-first; it finds comments and string constants and highlights them.
-Search-based fontification happens second.
+  Font Lock mode finds text to highlight in three ways: through
+parsing based on a full-blown parser (usually, via an external library
+or program), through syntactic parsing based on the Emacs's built-in
+syntax table, or through searching (usually for regular expressions).
+If enabled, parser-based fontification happens first
+(@pxref{Parser-based Font Lock}).  Syntactic fontification happens
+next; it finds comments and string constants and highlights them.
+Search-based fontification happens last.
 
 @menu
 * Font Lock Basics::            Overview of customizing Font Lock.
@@ -2872,6 +2875,7 @@ Search-based fontification happens second.
 * Syntactic Font Lock::         Fontification based on syntax tables.
 * Multiline Font Lock::         How to coerce Font Lock into properly
                                   highlighting multiline constructs.
+* Parser-based Font Lock::      Use parse data for fontification.
 @end menu
 
 @node Font Lock Basics
@@ -3652,6 +3656,71 @@ This face inherits, by default, from 
@code{font-lock-constant-face}.
 @item font-lock-negation-char-face
 @vindex font-lock-negation-char-face
 for easily-overlooked negation characters.
+
+@item font-lock-escape-face
+@vindex font-lock-escape-face
+for escape sequences in strings.
+This face inherits, by default, from 
@code{font-lock-regexp-grouping-backslash}.
+
+Here is an example in Python, where the escape sequence @code{\n} is used:
+
+@smallexample
+@group
+print('Hello world!\n')
+@end group
+@end smallexample
+
+@item font-lock-number-face
+@vindex font-lock-number-face
+for numbers.
+
+@item font-lock-operator-face
+@vindex font-lock-operator-face
+for operators.
+
+@item font-lock-property-face
+@vindex font-lock-property-face
+for properties of an object, such as the declaration and use of fields
+in a struct.
+This face inherits, by default, from @code{font-lock-variable-name-face}.
+
+For example,
+
+@smallexample
+@group
+typedef struct
+@{
+  int prop;
+//    ^ property
+@} obj;
+
+int main()
+@{
+  obj o;
+  o.prop = 3;
+//  ^ property
+@}
+@end group
+@end smallexample
+
+@item font-lock-punctuation-face
+@vindex font-lock-punctuation-face
+for punctuation such as brackets and delimiters.
+
+@item font-lock-bracket-face
+@vindex font-lock-bracket-face
+for brackets (e.g., @code{()}, @code{[]}, @code{@{@}}).
+This face inherits, by default, from @code{font-lock-punctuation-face}.
+
+@item font-lock-delimiter-face
+@vindex font-lock-delimiter-face
+for delimiters (e.g., @code{;}, @code{:}, @code{,}).
+This face inherits, by default, from @code{font-lock-punctuation-face}.
+
+@item font-lock-misc-punctuation-face
+@vindex font-lock-misc-punctuation-face
+for punctuation that is not a bracket or delimiter.
+This face inherits, by default, from @code{font-lock-punctuation-face}.
 @end table
 
 @node Syntactic Font Lock
@@ -3876,6 +3945,191 @@ Since this function is called after every buffer 
change, it should be
 reasonably fast.
 @end defvar
 
+@node Parser-based Font Lock
+@subsection Parser-based Font Lock
+@cindex parser-based font-lock
+
+@c This node is written when the only parser Emacs has is tree-sitter;
+@c if in the future more parser are supported, this should be
+@c reorganized and rewritten to describe multiple parsers in parallel.
+
+Besides simple syntactic font lock and regexp-based font lock, Emacs
+also provides complete syntactic font lock with the help of a parser.
+Currently, Emacs uses the tree-sitter library (@pxref{Parsing Program
+Source}) for this purpose.
+
+Parser-based font lock and other font lock mechanisms are not mutually
+exclusive.  By default, if enabled, parser-based font lock runs first,
+replacing syntactic font lock, then the regexp-based font lock.
+
+Although parser-based font lock doesn't share the same customization
+variables with regexp-based font lock, it uses similar customization
+schemes.  The tree-sitter counterpart of @var{font-lock-keywords} is
+@var{treesit-font-lock-settings}.
+
+@cindex tree-sitter fontifications, overview
+@cindex fontifications with tree-sitter, overview
+In general, tree-sitter fontification works as follows:
+
+@itemize @bullet
+@item
+A Lisp program (usually, part of a major mode) provides a @dfn{query}
+consisting of @dfn{patterns}, each pattern associated with a
+@dfn{capture name}.
+
+@item
+The tree-sitter library finds the nodes in the parse tree
+that match these patterns, tags the nodes with the corresponding
+capture names, and returns them to the Lisp program.
+
+@item
+The Lisp program uses the returned nodes to highlight the portions of
+buffer text corresponding to each node as appropriate, using the
+tagged capture names of the nodes to determine the correct
+fontification.  For example, a node tagged @code{font-lock-keyword}
+would be highlighted in @code{font-lock-keyword} face.
+@end itemize
+
+For more information about queries, patterns, and capture names, see
+@ref{Pattern Matching}.
+
+To setup tree-sitter fontification, a major mode should first set
+@code{treesit-font-lock-settings} with the output of
+@code{treesit-font-lock-rules}, then call
+@code{treesit-major-mode-setup}.
+
+@defun treesit-font-lock-rules &rest query-specs
+This function is used to set @var{treesit-font-lock-settings}.  It
+takes care of compiling queries and other post-processing, and outputs
+a value that @var{treesit-font-lock-settings} accepts.  Here's an
+example:
+
+@example
+@group
+(treesit-font-lock-rules
+ :language 'javascript
+ :feature 'constant
+ :override t
+ '((true) @@font-lock-constant-face
+   (false) @@font-lock-constant-face)
+ :language 'html
+ :feature 'script
+ "(script_element) @@font-lock-builtin-face")
+@end group
+@end example
+
+This function takes a series of @var{query-spec}s, where each
+@var{query-spec} is a @var{query} preceded by one or more
+@var{:keyword}/@var{value} pairs.  Each @var{query} is a
+tree-sitter query in either the string, s-expression or compiled form.
+
+For each @var{query}, the @var{:keyword}/@var{value} pairs that
+precede it add meta information to it.  The @code{:lang} keyword
+declares @var{query}'s language.  The @code{:feature} keyword sets the
+feature name of @var{query}.  Users can control which features are
+enabled with @code{font-lock-maximum-decoration} and
+@code{treesit-font-lock-feature-list} (described below).  These two
+keywords are mandatory.
+
+Other keywords are optional:
+
+@multitable @columnfractions .15 .15 .6
+@headitem Keyword @tab Value @tab Description
+@item @code{:override} @tab nil
+@tab If the region already has a face, discard the new face
+@item @tab t @tab Always apply the new face
+@item @tab @code{append} @tab Append the new face to existing ones
+@item @tab @code{prepend} @tab Prepend the new face to existing ones
+@item @tab @code{keep} @tab Fill-in regions without an existing face
+@end multitable
+
+Lisp programs mark patterns in @var{query} with capture names (names
+that starts with @code{@@}), and tree-sitter will return matched nodes
+tagged with those same capture names.  For the purpose of
+fontification, capture names in @var{query} should be face names like
+@code{font-lock-keyword-face}.  The captured node will be fontified
+with that face.
+
+@findex treesit-fontify-with-override
+Capture names can also be function names, in which case the function
+is called with 4 arguments: @var{node} and @var{override}, @var{start}
+and @var{end}, where @var{node} is the node itself, @var{override} is
+the override property of the rule which captured this node, and
+@var{start} and @var{end} limits the region in which this function
+should fontify.  (If this function wants to respect the @var{override}
+argument, it can use @code{treesit-fontify-with-override}.)
+
+Beyond the 4 arguments presented, this function should accept more
+arguments as optional arguments for future extensibility.
+
+If a capture name is both a face and a function, the face takes
+priority.  If a capture name is neither a face nor a function, it is
+ignored.
+@end defun
+
+@defvar treesit-font-lock-feature-list
+This is a list of lists of feature symbols.  Each element of the list
+is a list that represents a decoration level.
+@code{font-lock-maximum-decoration} controls which levels are
+activated.
+
+Each element of the list is a list of the form @w{@code{(@var{feature}
+@dots{})}}, where each @var{feature} corresponds to the
+@code{:feature} value of a query defined in
+@code{treesit-font-lock-rules}.  Removing a feature symbol from this
+list disables the corresponding query during font-lock.
+
+Common feature names, for many programming languages, include
+@code{definition}, @code{type}, @code{assignment}, @code{builtin},
+@code{constant}, @code{keyword}, @code{string-interpolation},
+@code{comment}, @code{doc}, @code{string}, @code{operator},
+@code{preprocessor}, @code{escape-sequence}, and @code{key}.  Major
+modes are free to subdivide or extend these common features.
+
+Some of these features warrant some explanation: @code{definition}
+highlights whatever is being defined, e.g., the function name in a
+function definition, the struct name in a struct definition, the
+variable name in a variable definition; @code{assignment} highlights
+the whatever is being assigned to, e.g., the variable or field in an
+assignment statement; @code{key} highlights keys in key-value pairs,
+e.g., keys in a JSON object, or a Python dictionary; @code{doc}
+highlights docstrings or doc-comments.
+
+For example, the value of this variable could be:
+@example
+@group
+((comment string doc) ; level 1
+ (function-name keyword type builtin constant) ; level 2
+ (variable-name string-interpolation key)) ; level 3
+@end group
+@end example
+
+Major modes should set this variable before calling
+@code{treesit-major-mode-setup}.
+
+@findex treesit-font-lock-recompute-features
+For this variable to take effect, a Lisp program should call
+@code{treesit-font-lock-recompute-features} (which resets
+@code{treesit-font-lock-settings} accordingly), or
+@code{treesit-major-mode-setup} (which calls
+@code{treesit-font-lock-recompute-features}).
+@end defvar
+
+@defvar treesit-font-lock-settings
+A list of settings for tree-sitter based font lock.  The exact format
+of each setting is considered internal.  One should always use
+@code{treesit-font-lock-rules} to set this variable.
+
+@c Because the format is internal, we don't document them here.  Though
+@c we do have it explained in the docstring.  We also expose the fact
+@c that it is a list of settings, so one could combine two of them with
+@c append.
+@end defvar
+
+Multi-language major modes should provide range functions in
+@code{treesit-range-functions}, and Emacs will set the ranges
+accordingly before fontifing a region (@pxref{Multiple Languages}).
+
 @node Auto-Indentation
 @section Automatic Indentation of code
 
@@ -3932,10 +4186,12 @@ and a few other such modes) has been made more generic 
over the years,
 so if your language seems somewhat similar to one of those languages,
 you might try to use that engine.  @c FIXME: documentation?
 Another one is SMIE which takes an approach in the spirit
-of Lisp sexps and adapts it to non-Lisp languages.
+of Lisp sexps and adapts it to non-Lisp languages. Yet another one is
+to rely on a full-blown parser, for example, the tree-sitter library.
 
 @menu
 * SMIE::                        A simple minded indentation engine.
+* Parser-based Indentation::    Parser-based indentation engine.
 @end menu
 
 @node SMIE
@@ -4595,6 +4851,197 @@ to the file's local variables of the form:
 @code{eval: (smie-config-local '(@var{rules}))}.
 @end defun
 
+@node Parser-based Indentation
+@subsection Parser-based Indentation
+@cindex parser-based indentation
+
+@c This node is written when the only parser Emacs has is tree-sitter;
+@c if in the future more parsers are supported, this should be
+@c reorganized and rewritten to describe multiple parsers in parallel.
+
+When built with the tree-sitter library (@pxref{Parsing Program
+Source}), Emacs is capable of parsing the program source and producing
+a syntax tree.  This syntax tree can be used for guiding the program
+source indentation commands.  For maximum flexibility, it is possible
+to write a custom indentation function that queries the syntax tree
+and indents accordingly for each language, but that is a lot of work.
+It is more convenient to use the simple indentation engine described
+below: then the major mode needs only to write some indentation rules
+and the engine takes care of the rest.
+
+To enable the parser-based indentation engine, either set
+@var{treesit-simple-indent-rules} and call
+@code{treesit-major-mode-setup}, or equivalently, set the value of
+@code{indent-line-function} to @code{treesit-indent}.
+
+@defvar treesit-indent-function
+This variable stores the actual function called by
+@code{treesit-indent}.  By default, its value is
+@code{treesit-simple-indent}.  In the future we might add other,
+more complex indentation engines.
+@end defvar
+
+@heading Writing indentation rules
+@cindex indentation rules, for parser-based indentation
+
+@defvar treesit-simple-indent-rules
+This local variable stores indentation rules for every language.  It is
+a list of the form: @w{@code{(@var{language} . @var{rules})}}, where
+@var{language} is a language symbol, and @var{rules} is a list of the
+form @w{@code{(@var{matcher} @var{anchor} @var{offset})}}.
+
+First, Emacs passes the smallest tree-sitter node at the beginning of
+the current line to @var{matcher}; if it returns non-@code{nil}, this
+rule is applicable.  Then Emacs passes the node to @var{anchor}, which
+returns a buffer position.  Emacs takes the column number of that
+position, adds @var{offset} to it, and the result is the indentation
+column for the current line.  @var{offset} can be an integer or a
+variable whose value is an integer.
+
+The @var{matcher} and @var{anchor} are functions, and Emacs provides
+convenient defaults for them.
+
+Each @var{matcher} or @var{anchor} is a function that takes three
+arguments: @var{node}, @var{parent}, and @var{bol}.  The argument
+@var{bol} is the buffer position whose indentation is required: the
+position of the first non-whitespace character after the beginning of
+the line.  The argument @var{node} is the largest (highest-in-tree)
+node that starts at that position; and @var{parent} is the parent of
+@var{node}.  However, when that position is in a whitespace or inside
+a multi-line string, no node can start at that position, so
+@var{node} is @code{nil}.  In that case, @var{parent} would be the
+smallest node that spans that position.
+
+Emacs finds @var{bol}, @var{node} and @var{parent} and
+passes them to each @var{matcher} and @var{anchor}.  @var{matcher}
+should return non-@code{nil} if the rule is applicable, and
+@var{anchor} should return a buffer position.
+@end defvar
+
+@defvar treesit-simple-indent-presets
+This is a list of defaults for @var{matcher}s and @var{anchor}s in
+@code{treesit-simple-indent-rules}.  Each of them represents a function
+that takes 3 arguments: @var{node}, @var{parent} and @var{bol}.  The
+available default functions are:
+
+@ftable @code
+@item no-node
+This matcher is a function that is called with 3 arguments:
+@var{node}, @var{parent}, and @var{bol}, and returns non-@code{nil},
+indicating a match, if @var{node} is @code{nil}, i.e., there is no
+node that starts at @var{bol}.  This is the case when @var{bol} is on
+an empty line or inside a multi-line string, etc.
+
+@item parent-is
+This matcher is a function of one argument, @var{type}; it returns a
+function that is called with 3 arguments: @var{node}, @var{parent},
+and @var{bol}, and returns non-@code{nil} (i.e., a match) if
+@var{parent}'s type matches regexp @var{type}.
+
+@item node-is
+This matcher is a function of one argument, @var{type}; it returns a
+function that is called with 3 arguments: @var{node}, @var{parent},
+and @var{bol}, and returns non-@code{nil} if @var{node}'s type matches
+regexp @var{type}.
+
+@item query
+This matcher is a function of one argument, @var{query}; it returns a
+function that is called with 3 arguments: @var{node}, @var{parent},
+and @var{bol}, and returns non-@code{nil} if querying @var{parent}
+with @var{query} captures @var{node} (@pxref{Pattern Matching}).
+
+@item match
+This matcher is a function of 5 arguments: @var{node-type},
+@var{parent-type}, @var{node-field}, @var{node-index-min}, and
+@var{node-index-max}).  It returns a function that is called with 3
+arguments: @var{node}, @var{parent}, and @var{bol}, and returns
+non-@code{nil} if @var{node}'s type matches regexp @var{node-type},
+@var{parent}'s type matches regexp @var{parent-type}, @var{node}'s
+field name in @var{parent} matches regexp @var{node-field}, and
+@var{node}'s index among its siblings is between @var{node-index-min}
+and @var{node-index-max}.  If the value of an argument is @code{nil},
+this matcher doesn't check that argument.  For example, to match the
+first child where parent is @code{argument_list}, use
+
+@example
+(match nil "argument_list" nil nil 0 0)
+@end example
+
+@item comment-end
+This matcher is a function that is called with 3 arguments:
+@var{node}, @var{parent}, and @var{bol}, and returns non-@code{nil} if
+point is before a comment ending token.  Comment ending tokens are
+defined by regular expression @code{treesit-comment-end}
+(@pxref{Tree-sitter major modes, treesit-comment-end}).
+
+@item first-sibling
+This anchor is a function that is called with 3 arguments: @var{node},
+@var{parent}, and @var{bol}, and returns the start of the first child
+of @var{parent}.
+
+@item parent
+This anchor is a function that is called with 3 arguments: @var{node},
+@var{parent}, and @var{bol}, and returns the start of @var{parent}.
+
+@item parent-bol
+This anchor is a function that is called with 3 arguments: @var{node},
+@var{parent}, and @var{bol}, and returns the first non-space character
+on the line of @var{parent}.
+
+@item prev-sibling
+This anchor is a function that is called with 3 arguments: @var{node},
+@var{parent}, and @var{bol}, and returns the start of the previous
+sibling of @var{node}.
+
+@item no-indent
+This anchor is a function that is called with 3 arguments: @var{node},
+@var{parent}, and @var{bol}, and returns the start of @var{node}.
+
+@item prev-line
+This anchor is a function that is called with 3 arguments: @var{node},
+@var{parent}, and @var{bol}, and returns the first non-whitespace
+character on the previous line.
+
+@item point-min
+This anchor is a function that is called with 3 arguments: @var{node},
+@var{parent}, and @var{bol}, and returns the beginning of the buffer.
+This is useful as the beginning of the buffer is always at column 0.
+
+@item comment-start
+This anchor is a function that is called with 3 arguments: @var{node},
+@var{parent}, and @var{bol}, and returns the position right after the
+comment-start token.  Comment-start tokens are defined by regular
+expression @code{treesit-comment-start} (@pxref{Tree-sitter major
+modes, treesit-comment-start}).  This function assumes @var{parent} is
+the comment node.
+
+@item coment-start-skip
+This anchor is a function that is called with 3 arguments: @var{node},
+@var{parent}, and @var{bol}, and returns the position after the
+comment-start token and any whitespace characters following that
+token.  Comment-start tokens are defined by regular expression
+@code{treesit-comment-start}.  This function assumes @var{parent} is
+the comment node.
+@end ftable
+@end defvar
+
+@heading Indentation utilities
+@cindex utility functions for parser-based indentation
+
+Here are some utility functions that can help writing parser-based
+indentation rules.
+
+@defun treesit-check-indent mode
+This function checks the current buffer's indentation against major
+mode @var{mode}.  It indents the current buffer according to
+@var{mode} and compares the results with the current indentation.
+Then it pops up a buffer showing the differences.  Correct
+indentation (target) is shown in green color, current indentation is
+shown in red color.  @c Are colors customizable? faces?
+@end defun
+
+It is also helpful to use @code{treesit-inspect-mode} (@pxref{Language
+Definitions}) when writing indentation rules.
 
 @node Desktop Save Mode
 @section Desktop Save Mode
diff --git a/doc/lispref/parsing.texi b/doc/lispref/parsing.texi
new file mode 100644
index 0000000000..0f6a99b299
--- /dev/null
+++ b/doc/lispref/parsing.texi
@@ -0,0 +1,1886 @@
+@c -*- mode: texinfo; coding: utf-8 -*-
+@c This is part of the GNU Emacs Lisp Reference Manual.
+@c Copyright (C) 2021-2022 Free Software Foundation, Inc.
+@c See the file elisp.texi for copying conditions.
+@node Parsing Program Source
+@chapter Parsing Program Source
+
+@cindex syntax tree, from parsing program source
+Emacs provides various ways to parse program source text and produce a
+@dfn{syntax tree}.  In a syntax tree, text is no longer considered a
+one-dimensional stream of characters, but a structured tree of nodes,
+where each node representing a piece of text.  Thus, a syntax tree can
+enable interesting features like precise fontification, indentation,
+navigation, structured editing, etc.
+
+Emacs has a simple facility for parsing balanced expressions
+(@pxref{Parsing Expressions}).  There is also the SMIE library for
+generic navigation and indentation (@pxref{SMIE}).
+
+In addition to those, Emacs also provides integration with
+@uref{https://tree-sitter.github.io/tree-sitter, the tree-sitter
+library}) if support for it was compiled in.  The tree-sitter library
+implements an incremental parser and has support from a wide range of
+programming languages.
+
+@defun treesit-available-p
+This function returns non-@code{nil} if tree-sitter features are
+available for the current Emacs session.
+@end defun
+
+To be able to parse the program source using the tree-sitter library
+and access the syntax tree of the program, a Lisp program needs to
+load a language definition library, and create a parser for that
+language and the current buffer.  After that, the Lisp program can
+query the parser about specific nodes of the syntax tree.  Then, it
+can access various kinds of information about each node, and search
+for nodes using a powerful pattern-matching syntax.  This chapter
+explains how to do all this, and also how a Lisp program can work with
+source files that mix multiple programming languages.
+
+@menu
+* Language Definitions::     Loading tree-sitter language definitions.
+* Using Parser::             Introduction to parsers.
+* Retrieving Nodes::         Retrieving nodes from a syntax tree.
+* Accessing Node Information:: Accessing node information.
+* Pattern Matching::         Pattern matching with query patterns.
+* Multiple Languages::       Parse text written in multiple languages.
+* Tree-sitter major modes::  Develop major modes using tree-sitter.
+* Tree-sitter C API::        Compare the C API and the ELisp API.
+@end menu
+
+@node Language Definitions
+@section Tree-sitter Language Definitions
+@cindex language definitions, for tree-sitter
+
+@heading Loading a language definition
+@cindex loading language definition for tree-sitter
+
+@cindex language argument, for tree-sitter
+Tree-sitter relies on language definitions to parse text in that
+language.  In Emacs, a language definition is represented by a symbol.
+For example, the C language definition is represented as the symbol
+@code{c}, and @code{c} can be passed to tree-sitter functions as the
+@var{language} argument.
+
+@vindex treesit-extra-load-path
+@vindex treesit-load-language-error
+@vindex treesit-load-suffixes
+Tree-sitter language definitions are distributed as dynamic libraries.
+In order to use a language definition in Emacs, you need to make sure
+that the dynamic library is installed on the system.  Emacs looks for
+language definitions in several places, in the following order:
+
+@itemize @bullet
+@item
+first, in the list of directories specified by the variable
+@code{treesit-extra-load-path};
+@item
+then, in the @file{tree-sitter} subdirectory of the directory
+specified by @code{user-emacs-directory} (@pxref{Init File});
+@item
+and finally, in the system's default locations for dynamic libraries.
+@end itemize
+
+In each of these directories, Emacs looks for a file with file-name
+extensions specified by the variable @code{treesit-load-suffixes}.
+
+If Emacs cannot find the library or has problems loading it, Emacs
+signals the @code{treesit-load-language-error} error.  The data of
+that signal could be one of the following:
+
+@table @code
+@item (not-found @var{error-msg} @dots{})
+This means that Emacs could not find the language definition library.
+@item (symbol-error @var{error-msg})
+This means that Emacs could not find in the library the expected function
+that every language definition library should export.
+@item (version-mismatch @var{error-msg})
+This means that the version of language definition library is incompatible
+with that of the tree-sitter library.
+@end table
+
+@noindent
+In all of these cases, @var{error-msg} might provide additional
+details about the failure.
+
+@defun treesit-language-available-p language &optional detail
+This function returns non-@code{nil} if the language definitions for
+@var{language} exist and can be loaded.
+
+If @var{detail} is non-@code{nil}, return @code{(t . nil)} when
+@var{language} is available, and @code{(nil . @var{data})} when it's
+unavailable.  @var{data} is the signal data of
+@code{treesit-load-language-error}.
+@end defun
+
+@vindex treesit-load-name-override-list
+By convention, the file name of the dynamic library for @var{language} is
+@file{libtree-sitter-@var{language}.@var{ext}}, where @var{ext} is the
+system-specific extension for dynamic libraries.  Also by convention,
+the function provided by that library is named
+@code{tree_sitter_@var{language}}.  If a language definition library
+doesn't follow this convention, you should add an entry
+
+@example
+(@var{language} @var{library-base-name} @var{function-name})
+@end example
+
+to the list in the variable @code{treesit-load-name-override-list}, where
+@var{library-base-name} is the basename of the dynamic library's file name,
+(usually, @file{libtree-sitter-@var{language}}), and
+@var{function-name} is the function provided by the library
+(usually, @code{tree_sitter_@var{language}}).  For example,
+
+@example
+(cool-lang "libtree-sitter-coool" "tree_sitter_cooool")
+@end example
+
+@noindent
+for a language that considers itself too ``cool'' to abide by
+conventions.
+
+@cindex language-definition version, compatibility
+@defun treesit-language-version &optional min-compatible
+This function returns the version of the language-definition
+Application Binary Interface (@acronym{ABI}) supported by the
+tree-sitter library.  By default, it returns the latest ABI version
+supported by the library, but if @var{min-compatible} is
+non-@code{nil}, it returns the oldest ABI version which the library
+still can support.  Language definition libraries must be built for
+ABI versions between the oldest and the latest versions supported by
+the tree-sitter library, otherwise the library will be unable to load
+them.
+@end defun
+
+@heading Concrete syntax tree
+@cindex syntax tree, concrete
+
+A syntax tree is what a parser generates.  In a syntax tree, each node
+represents a piece of text, and is connected to each other by a
+parent-child relationship.  For example, if the source text is
+
+@example
+1 + 2
+@end example
+
+@noindent
+its syntax tree could be
+
+@example
+@group
+                  +--------------+
+                  | root "1 + 2" |
+                  +--------------+
+                         |
+        +--------------------------------+
+        |       expression "1 + 2"       |
+        +--------------------------------+
+           |             |            |
++------------+   +--------------+   +------------+
+| number "1" |   | operator "+" |   | number "2" |
++------------+   +--------------+   +------------+
+@end group
+@end example
+
+We can also represent it as an s-expression:
+
+@example
+(root (expression (number) (operator) (number)))
+@end example
+
+@subheading Node types
+@cindex node types, in a syntax tree
+
+@cindex type of node, tree-sitter
+@anchor{tree-sitter node type}
+@cindex named node, tree-sitter
+@anchor{tree-sitter named node}
+@cindex anonymous node, tree-sitter
+Names like @code{root}, @code{expression}, @code{number}, and
+@code{operator} specify the @dfn{type} of the nodes.  However, not all
+nodes in a syntax tree have a type.  Nodes that don't have a type are
+known as @dfn{anonymous nodes}, and nodes with a type are @dfn{named
+nodes}.  Anonymous nodes are tokens with fixed spellings, including
+punctuation characters like bracket @samp{]}, and keywords like
+@code{return}.
+
+@subheading Field names
+
+@cindex field name, tree-sitter
+@cindex tree-sitter node field name
+@anchor{tree-sitter node field name}
+To make the syntax tree easier to analyze, many language definitions
+assign @dfn{field names} to child nodes.  For example, a
+@code{function_definition} node could have a @code{declarator} and a
+@code{body}:
+
+@example
+@group
+(function_definition
+ declarator: (declaration)
+ body: (compound_statement))
+@end group
+@end example
+
+@heading Exploring the syntax tree
+@cindex explore tree-sitter syntax tree
+@cindex inspection of tree-sitter parse tree nodes
+
+To aid in understanding the syntax of a language and in debugging of
+Lisp program that use the syntax tree, Emacs provides an ``explore''
+mode, which displays the syntax tree of the source in the current
+buffer in real time.  Emacs also comes with an ``inspect mode'', which
+displays information of the nodes at point in the mode-line.
+
+@deffn Command treesit-explore-mode
+This mode pops up a window displaying the syntax tree of the source in
+the current buffer.  Selecting text in the source buffer highlights
+the corresponding nodes in the syntax tree display.  Clicking
+on nodes in the syntax tree highlights the corresponding text in the
+source buffer.
+@end deffn
+
+@deffn Command treesit-inspect-mode
+This minor mode displays on the mode-line the node that @emph{starts}
+at point.  For example, the mode-line can display
+
+@example
+@var{parent} @var{field}: (@var{node} (@var{child} (@dots{})))
+@end example
+
+@noindent
+where @var{node}, @var{child}, etc., are nodes which begin at point.
+@var{parent} is the parent of @var{node}.  @var{node} is displayed in
+a bold typeface.  @var{field-name}s are field names of @var{node} and
+of @var{child}, etc.
+
+If no node starts at point, i.e., point is in the middle of a node,
+then the mode line displays the earliest node that spans point, and
+its immediate parent.
+
+This minor mode doesn't create parsers on its own.  It uses the first
+parser in @code{(treesit-parser-list)} (@pxref{Using Parser}).
+@end deffn
+
+@heading Reading the grammar definition
+@cindex reading grammar definition, tree-sitter
+
+Authors of language definitions define the @dfn{grammar} of a
+programming language, which determines how a parser constructs a
+concrete syntax tree out of the program text.  In order to use the
+syntax tree effectively, you need to consult the @dfn{grammar file}.
+
+The grammar file is usually @file{grammar.js} in a language
+definition's project repository.  The link to a language definition's
+home page can be found on
+@uref{https://tree-sitter.github.io/tree-sitter, tree-sitter's
+homepage}.
+
+The grammar definition is written in JavaScript.  For example, the
+rule matching a @code{function_definition} node looks like
+
+@example
+@group
+function_definition: $ => seq(
+  $.declaration_specifiers,
+  field('declarator', $.declaration),
+  field('body', $.compound_statement)
+)
+@end group
+@end example
+
+@noindent
+The rules are represented by functions that take a single argument
+@var{$}, representing the whole grammar.  The function itself is
+constructed by other functions: the @code{seq} function puts together
+a sequence of children; the @code{field} function annotates a child
+with a field name.  If we write the above definition in the so-called
+@dfn{Backus-Naur Form} (@acronym{BNF}) syntax, it would look like
+
+@example
+@group
+function_definition :=
+  <declaration_specifiers> <declaration> <compound_statement>
+@end group
+@end example
+
+@noindent
+and the node returned by the parser would look like
+
+@example
+@group
+(function_definition
+  (declaration_specifier)
+  declarator: (declaration)
+  body: (compound_statement))
+@end group
+@end example
+
+Below is a list of functions that one can see in a grammar definition.
+Each function takes other rules as arguments and returns a new rule.
+
+@table @code
+@item seq(@var{rule1}, @var{rule2}, @dots{})
+matches each rule one after another.
+@item choice(@var{rule1}, @var{rule2}, @dots{})
+matches one of the rules in its arguments.
+@item repeat(@var{rule})
+matches @var{rule} for @emph{zero or more} times.
+This is like the @samp{*} operator in regular expressions.
+@item repeat1(@var{rule})
+matches @var{rule} for @emph{one or more} times.
+This is like the @samp{+} operator in regular expressions.
+@item optional(@var{rule})
+matches @var{rule} for @emph{zero or one} time.
+This is like the @samp{?} operator in regular expressions.
+@item field(@var{name}, @var{rule})
+assigns field name @var{name} to the child node matched by @var{rule}.
+@item alias(@var{rule}, @var{alias})
+makes nodes matched by @var{rule} appear as @var{alias} in the syntax
+tree generated by the parser.  For example,
+
+@example
+alias(preprocessor_call_exp, call_expression)
+@end example
+
+@noindent
+makes any node matched by @code{preprocessor_call_exp} appear as
+@code{call_expression}.
+@end table
+
+Below are grammar functions of lesser importance for reading a
+language definition.
+
+@table @code
+@item token(@var{rule})
+marks @var{rule} to produce a single leaf node.  That is, instead of
+generating a parent node with individual child nodes under it,
+everything is combined into a single leaf node.  @xref{Retrieving
+Nodes}.
+@item token.immediate(@var{rule})
+Normally, grammar rules ignore preceding whitespace; this
+changes @var{rule} to match only when there is no preceding
+whitespaces.
+@item prec(@var{n}, @var{rule})
+gives @var{rule} the level-@var{n} precedence.
+@item prec.left([@var{n},] @var{rule})
+marks @var{rule} as left-associative, optionally with level @var{n}.
+@item prec.right([@var{n},] @var{rule})
+marks @var{rule} as right-associative, optionally with level @var{n}.
+@item prec.dynamic(@var{n}, @var{rule})
+this is like @code{prec}, but the precedence is applied at runtime
+instead.
+@end table
+
+The documentation of the tree-sitter project has
+@uref{https://tree-sitter.github.io/tree-sitter/creating-parsers, more
+about writing a grammar}.  Read especially ``The Grammar DSL''
+section.
+
+@node Using Parser
+@section Using Tree-sitter Parser
+@cindex tree-sitter parser, using
+
+This section describes how to create and configure a tree-sitter
+parser.  In Emacs, each tree-sitter parser is associated with a
+buffer.  As the user edits the buffer, the associated parser and
+syntax tree are automatically kept up-to-date.
+
+@defvar treesit-max-buffer-size
+This variable contains the maximum size of buffers in which
+tree-sitter can be activated.  Major modes should check this value
+when deciding whether to enable tree-sitter features.
+@end defvar
+
+@defun treesit-can-enable-p
+This function checks whether the current buffer is suitable for
+activating tree-sitter features.  It basically checks
+@code{treesit-available-p} and @code{treesit-max-buffer-size}.
+@end defun
+
+@cindex creating tree-sitter parsers
+@cindex tree-sitter parser, creating
+@defun treesit-parser-create language &optional buffer no-reuse
+Create a parser for the specified @var{buffer} and @var{language}
+(@pxref{Language Definitions}).  If @var{buffer} is omitted or
+@code{nil}, it stands for the current buffer.
+
+By default, this function reuses a parser if one already exists for
+@var{language} in @var{buffer}, but if @var{no-reuse} is
+non-@code{nil}, this function always creates a new parser.
+@end defun
+
+Given a parser, we can query information about it.
+
+@defun treesit-parser-buffer parser
+This function returns the buffer associated with @var{parser}.
+@end defun
+
+@defun treesit-parser-language parser
+This function returns the language used by @var{parser}.
+@end defun
+
+@defun treesit-parser-p object
+This function checks if @var{object} is a tree-sitter parser, and
+returns non-@code{nil} if it is, and @code{nil} otherwise.
+@end defun
+
+There is no need to explicitly parse a buffer, because parsing is done
+automatically and lazily.  A parser only parses when a Lisp program
+queries for a node in its syntax tree.  Therefore, when a parser is
+first created, it doesn't parse the buffer; it waits until the Lisp
+program queries for a node for the first time.  Similarly, when some
+change is made in the buffer, a parser doesn't re-parse immediately.
+
+@vindex treesit-buffer-too-large
+When a parser does parse, it checks for the size of the buffer.
+Tree-sitter can only handle buffer no larger than about 4GB.  If the
+size exceeds that, Emacs signals the @code{treesit-buffer-too-large}
+error with signal data being the buffer size.
+
+Once a parser is created, Emacs automatically adds it to the
+internal parser list.  Every time a change is made to the buffer,
+Emacs updates parsers in this list so they can update their syntax
+tree incrementally.
+
+@defun treesit-parser-list &optional buffer
+This function returns the parser list of @var{buffer}.  If
+@var{buffer} is @code{nil} or omitted, it defaults to the current
+buffer.
+@end defun
+
+@defun treesit-parser-delete parser
+This function deletes @var{parser}.
+@end defun
+
+@cindex tree-sitter narrowing
+@anchor{tree-sitter narrowing}
+Normally, a parser ``sees'' the whole buffer, but when the buffer is
+narrowed (@pxref{Narrowing}), the parser will only see the accessible
+portion of the buffer.  As far as the parser can tell, the hidden
+region was deleted.  When the buffer is later widened, the parser
+thinks text is inserted at the beginning and at the end.  Although
+parsers respect narrowing, modes should not use narrowing as a means
+to handle a multi-language buffer; instead, set the ranges in which the
+parser should operate.  @xref{Multiple Languages}.
+
+Because a parser parses lazily, when the user or a Lisp program
+narrows the buffer, the parser is not affected immediately; as long as
+the mode doesn't query for a node while the buffer is narrowed, the
+parser is oblivious of the narrowing.
+
+@cindex tree-sitter parse string
+@cindex parse string, tree-sitter
+Besides creating a parser for a buffer, a Lisp program can also parse a
+string.  Unlike a buffer, parsing a string is a one-off operation, and
+there is no way to update the result.
+
+@defun treesit-parse-string string language
+This function parses @var{string} using @var{language}, and returns
+the root node of the generated syntax tree.
+@end defun
+
+@heading Be notified by changes to the parse tree
+@cindex update callback, for tree-sitter parse-tree
+@cindex after-change notifier, for tree-sitter parse-tree
+@cindex tree-sitter parse-tree, update and after-change callback
+@cindex notifiers, tree-sitter
+
+A Lisp program might want to be notified of text affected by
+incremental parsing.  For example, inserting a comment-closing token
+converts text before that token into a comment.  Even
+though the text is not directly edited, it is deemed to be ``changed''
+nevertheless.
+
+Emacs lets a Lisp program to register callback functions
+(a.k.a.@: @dfn{notifiers}) for this kind of changes.  A notifier
+function takes two arguments: @var{ranges} and @var{parser}.
+@var{ranges} is a list of cons cells of the form @w{@code{(@var{start}
+. @var{end})}}, where @var{start} and @var{end} mark the start and the
+end positions of a range.  @var{parser} is the parser issuing the
+notification.
+
+Every time a parser reparses a buffer, it compares the old and new
+parse-tree, computes the ranges in which nodes have changed, and
+passes the ranges to notifier functions.
+
+@defun treesit-parser-add-notifier parser function
+This function adds @var{function} to @var{parser}'s list of
+after-change notifier functions.  @var{function} must be a function
+symbol, not a lambda function (@pxref{Anonymous Functions}).
+@end defun
+
+@defun treesit-parser-remove-notifier parser function
+This function removes @var{function} from the list of @var{parser}'s
+after-change notifier functions.  @var{function} must be a function
+symbol, rather than a lambda function.
+@end defun
+
+@defun treesit-parser-notifiers parser
+This function returns the list of @var{parser}'s notifier functions.
+@end defun
+
+@node Retrieving Nodes
+@section Retrieving Nodes
+@cindex retrieve node, tree-sitter
+@cindex tree-sitter, find node
+@cindex get node, tree-sitter
+
+@cindex terminology, for tree-sitter functions
+Here's some terminology and conventions we use when documenting
+tree-sitter functions.
+
+We talk about a node being ``smaller'' or ``larger'', and ``lower'' or
+``higher''.  A smaller and lower node is lower in the syntax tree and
+therefore spans a smaller portion of buffer text; a larger and higher
+node is higher up in the syntax tree, it contains many smaller nodes
+as its children, and therefore spans a larger portion of text.
+
+When a function cannot find a node, it returns @code{nil}.  For
+convenience, all functions that take a node as argument and return
+a node, also accept the node argument of @code{nil} and in that case
+just return @code{nil}.
+
+@vindex treesit-node-outdated
+Nodes are not automatically updated when the associated buffer is
+modified, and there is no way to update a node once it is retrieved.
+Using an outdated node signals the @code{treesit-node-outdated} error.
+
+@heading Retrieving nodes from syntax tree
+@cindex retrieving tree-sitter nodes
+@cindex syntax tree, retrieving nodes
+
+@cindex leaf node, of tree-sitter parse tree
+@cindex tree-sitter parse tree, leaf node
+@defun treesit-node-at pos &optional parser-or-lang named
+This function returns a @dfn{leaf} node at buffer position @var{pos}.
+A leaf node is a node that doesn't have any child nodes.
+
+This function tries to return a node whose span covers @var{pos}: the
+node's beginning position is less or equal to @var{pos}, and the
+node's end position is greater or equal to @var{pos}.
+
+If no leaf node's span covers @var{pos} (e.g., @var{pos} is in the
+whitespace between two leaf nodes), this function returns the first
+leaf node after @var{pos}.
+
+Finally, if there is no leaf node after @var{pos}, return the first
+leaf node before @var{pos}.
+
+When @var{parser-or-lang} is @code{nil} or omitted, this function uses
+the first parser in @code{(treesit-parser-list)} of the current
+buffer.  If @var{parser-or-lang} is a parser object, it uses that
+parser; if @var{parser-or-lang} is a language, it finds the first
+parser using that language in @code{(treesit-parser-list)}, and uses
+that.
+
+If this function cannot find a suitable node to return, it returns
+@code{nil}.
+
+If @var{named} is non-@code{nil}, this function looks only for named
+nodes (@pxref{tree-sitter named node, named node}).
+
+Example:
+
+@example
+@group
+;; Find the node at point in a C parser's syntax tree.
+(treesit-node-at (point) 'c)
+  @result{} #<treesit-node (primitive_type) in 23-27>
+@end group
+@end example
+@end defun
+
+@defun treesit-node-on beg end &optional parser-or-lang named
+This function returns the @emph{smallest} node that covers the region
+of buffer text between @var{beg} and @var{end}.  In other words, the
+start of the node is before or at @var{beg}, and the end of the node
+is at or after @var{end}.
+
+@emph{Beware:} calling this function on an empty line that is not
+inside any top-level construct (function definition, etc.) most
+probably will give you the root node, because the root node is the
+smallest node that covers that empty line.  Most of the time, you want
+to use @code{treesit-node-at}, described above, instead.
+
+When @var{parser-or-lang} is @code{nil}, this function uses the first
+parser in @code{(treesit-parser-list)} of the current buffer.  If
+@var{parser-or-lang} is a parser object, it uses that parser; if
+@var{parser-or-lang} is a language, it finds the first parser using
+that language in @code{(treesit-parser-list)}, and uses that.
+
+If @var{named} is non-@code{nil}, this function looks for a named node
+only (@pxref{tree-sitter named node, named node}).
+@end defun
+
+@defun treesit-parser-root-node parser
+This function returns the root node of the syntax tree generated by
+@var{parser}.
+@end defun
+
+@defun treesit-buffer-root-node &optional language
+This function finds the first parser that uses @var{language} in
+@code{(treesit-parser-list)} of the current buffer, and returns the
+root node generated by that parser.  If it cannot find an appropriate
+parser, it returns @code{nil}.
+@end defun
+
+Given a node, a Lisp program can retrieve other nodes starting from
+it, or query for information about this node.
+
+@heading Retrieving nodes from other nodes
+@cindex syntax tree nodes, retrieving from other nodes
+
+@subheading By kinship
+@cindex kinship, syntax tree nodes
+@cindex nodes, by kinship
+@cindex syntax tree nodes, by kinship
+
+@defun treesit-node-parent node
+This function returns the immediate parent of @var{node}.
+@end defun
+
+@defun treesit-node-child node n &optional named
+This function returns the @var{n}'th child of @var{node}.  If
+@var{named} is non-@code{nil}, it counts only named nodes
+(@pxref{tree-sitter named node, named node}).
+
+For example, in a node that represents a string @code{"text"}, there
+are three children nodes: the opening quote @code{"}, the string text
+@code{text}, and the closing quote @code{"}.  Among these nodes, the
+first child is the opening quote @code{"}, and the first named child
+is the string text.
+
+This function returns @code{nil} if there is no @var{n}'th child.
+@var{n} could be negative, e.g., @code{-1} represents the last child.
+@end defun
+
+@defun treesit-node-children node &optional named
+This function returns all of @var{node}'s children as a list.  If
+@var{named} is non-@code{nil}, it retrieves only named nodes.
+@end defun
+
+@defun treesit-next-sibling node &optional named
+This function finds the next sibling of @var{node}.  If @var{named} is
+non-@code{nil}, it finds the next named sibling.
+@end defun
+
+@defun treesit-prev-sibling node &optional named
+This function finds the previous sibling of @var{node}.  If
+@var{named} is non-@code{nil}, it finds the previous named sibling.
+@end defun
+
+@subheading By field name
+@cindex nodes, by field name
+@cindex syntax tree nodes, by field name
+
+To make the syntax tree easier to analyze, many language definitions
+assign @dfn{field names} to child nodes (@pxref{tree-sitter node field
+name, field name}).  For example, a @code{function_definition} node
+could have a @code{declarator} node and a @code{body} node.
+
+@defun treesit-child-by-field-name node field-name
+This function finds the child of @var{node} whose field name is
+@var{field-name}, a string.
+
+@example
+@group
+;; Get the child that has "body" as its field name.
+(treesit-child-by-field-name node "body")
+  @result{} #<treesit-node (compound_statement) in 45-89>
+@end group
+@end example
+@end defun
+
+@subheading By position
+@cindex nodes, by position
+@cindex syntax tree nodes, by position
+
+@defun treesit-first-child-for-pos node pos &optional named
+This function finds the first child of @var{node} that extends beyond
+buffer position @var{pos}.  ``Extends beyond'' means the end of the
+child node is greater or equal to @var{pos}.  This function only looks
+for immediate children of @var{node}, and doesn't look in its
+grandchildren.  If @var{named} is non-@code{nil}, it looks for the
+first named child (@pxref{tree-sitter named node, named node}).
+@end defun
+
+@defun treesit-node-descendant-for-range node beg end &optional named
+This function finds the @emph{smallest} descendant node of @var{node}
+that spans the region of text between positions @var{beg} and
+@var{end}.  It is similar to @code{treesit-node-at}.  If @var{named}
+is non-@code{nil}, it looks for smallest named child.
+@end defun
+
+@heading Searching for node
+
+@defun treesit-search-subtree node predicate &optional backward all limit
+This function traverses the subtree of @var{node} (including
+@var{node} itself), looking for a node for which @var{predicate}
+returns non-@code{nil}.  @var{predicate} is a regexp that is matched
+against each node's type, or a predicate function that takes a node
+and returns non-@code{nil} if the node matches.  The function returns
+the first node that matches, or @code{nil} if none does.
+
+By default, this function only traverses named nodes, but if @var{all}
+is non-@code{nil}, it traverses all the nodes.  If @var{backward} is
+non-@code{nil}, it traverses backwards (i.e., it visits the last child first
+when traversing down the tree).  If @var{limit} is non-@code{nil}, it
+must be a number that limits the tree traversal to that many levels
+down the tree.
+@end defun
+
+@defun treesit-search-forward start predicate &optional backward all
+Like @code{treesit-search-subtree}, this function also traverses the
+parse tree and matches each node with @var{predicate} (except for
+@var{start}), where @var{predicate} can be a regexp or a function.
+For a tree like the below where @var{start} is marked S, this function
+traverses as numbered from 1 to 12:
+
+@example
+@group
+              12
+              |
+     S--------3----------11
+     |        |          |
+o--o-+--o  1--+--2    6--+-----10
+|  |                  |        |
+o  o                +-+-+   +--+--+
+                    |   |   |  |  |
+                    4   5   7  8  9
+@end group
+@end example
+
+Note that this function doesn't traverse the subtree of @var{start},
+and it always traverse leaf nodes first, then upwards.
+
+Like @code{treesit-search-subtree}, this function only searches for
+named nodes by default, but if @var{all} is non-@code{nil}, it
+searches for all nodes.  If @var{backward} is non-@code{nil}, it
+searches backwards.
+
+While @code{treesit-search-subtree} traverses the subtree of a node,
+this function starts with node @var{start} and traverses every node
+that comes after it in the buffer position order, i.e., nodes with
+start positions greater than the end position of @var{start}.
+
+In the tree shown above, @code{treesit-search-subtree} traverses node
+S (@var{start}) and nodes marked with @code{o}, where this function
+traverses the nodes marked with numbers.  This function is useful for
+answering questions like ``what is the first node after @var{start} in
+the buffer that satisfies some condition?''
+@end defun
+
+@defun treesit-search-forward-goto node predicate &optional start backward all
+This function moves point to the start or end of the next node after
+@var{node} in the buffer that matches @var{predicate}.  If @var{start}
+is non-@code{nil}, stop at the beginning rather than the end of a node.
+
+This function guarantees that the matched node it returns makes
+progress in terms of buffer position: the start/end position of the
+returned node is always greater than that of @var{node}.
+
+Arguments @var{predicate}, @var{backward} and @var{all} are the same
+as in @code{treesit-search-forward}.
+@end defun
+
+@defun treesit-induce-sparse-tree root predicate &optional process-fn limit
+This function creates a sparse tree from @var{root}'s subtree.
+
+It takes the subtree under @var{root}, and combs it so only the nodes
+that match @var{predicate} are left.  Like previous functions, the
+@var{predicate} can be a regexp string that matches against each
+node's type, or a function that takes a node and return non-@code{nil}
+if it matches.
+
+For example, for a subtree on the left that consist of both numbers
+and letters, if @var{predicate} is ``letter only'', the returned tree
+is the one on the right.
+
+@example
+@group
+    a                 a              a
+    |                 |              |
++---+---+         +---+---+      +---+---+
+|   |   |         |   |   |      |   |   |
+b   1   2         b   |   |      b   c   d
+    |   |     =>      |   |  =>      |
+    c   +--+          c   +          e
+    |   |  |          |   |
+ +--+   d  4       +--+   d
+ |  |              |
+ e  5              e
+@end group
+@end example
+
+If @var{process-fn} is non-@code{nil}, instead of returning the matched
+nodes, this function passes each node to @var{process-fn} and uses the
+returned value instead.  If non-@code{nil}, @var{limit} is the number of
+levels to go down from @var{root}.
+
+Each node in the returned tree looks like
+@w{@code{(@var{tree-sitter-node} . (@var{child} @dots{}))}}.  The
+@var{tree-sitter-node} of the root of this tree will be nil if
+@var{root} doesn't match @var{predicate}.  If no node matches
+@var{predicate}, the function returns @code{nil}.
+@end defun
+
+@heading More convenience functions
+
+@defun treesit-filter-child node predicate &optional named
+This function finds immediate children of @var{node} that satisfy
+@var{predicate}.
+
+The @var{predicate} function takes a node as the argument and should
+return non-@code{nil} to indicate that the node should be kept.  If
+@var{named} is non-@code{nil}, this function only examines the named
+nodes.
+@end defun
+
+@defun treesit-parent-until node predicate
+This function repeatedly finds the parents of @var{node}, and returns
+the parent that satisfies @var{predicate}, a function that takes a
+node as the argument.  If no parent satisfies @var{predicate}, this
+function returns @code{nil}.
+@end defun
+
+@defun treesit-parent-while node predicate
+This function repeatedly finds the parent of @var{node}, and keeps
+doing so as long as the nodes satisfy @var{predicate}, a function that
+takes a node as the argument.  That is, this function returns the
+farthest parent that still satisfies @var{predicate}.
+@end defun
+
+@defun treesit-node-top-level node &optional type
+This function returns the highest parent of @var{node} that has the
+same type as @var{node}.  If no such parent exists, it returns
+@code{nil}.  Therefore this function is also useful for testing
+whether @var{node} is top-level.
+
+If @var{type} is non-@code{nil}, this function matches each parent's
+type with @var{type} as a regexp, rather than using @var{node}'s type.
+@end defun
+
+@node Accessing Node Information
+@section Accessing Node Information
+@cindex information of node, syntax trees
+@cindex syntax trees, node information
+
+@heading Basic information of Node
+
+Every node is associated with a parser, and that parser is associated
+with a buffer.  The following functions retrieve them.
+
+@defun treesit-node-parser node
+This function returns @var{node}'s associated parser.
+@end defun
+
+@defun treesit-node-buffer node
+This function returns @var{node}'s parser's associated buffer.
+@end defun
+
+@defun treesit-node-language node
+This function returns @var{node}'s parser's associated language.
+@end defun
+
+Each node represents a portion of text in the buffer.  Functions below
+find relevant information about that text.
+
+@defun treesit-node-start node
+Return the start position of @var{node}.
+@end defun
+
+@defun treesit-node-end node
+Return the end position of @var{node}.
+@end defun
+
+@defun treesit-node-text node &optional object
+Return the buffer text that @var{node} represents, as a string.  (If
+@var{node} is retrieved from parsing a string, it will be the text
+from that string.)
+@end defun
+
+@cindex predicates for syntax tree nodes
+Here are some predicates on tree-sitter nodes:
+
+@defun treesit-node-p object
+Checks if @var{object} is a tree-sitter syntax node.
+@end defun
+
+@defun treesit-node-eq node1 node2
+Checks if @var{node1} and @var{node2} are the same node in a syntax
+tree.
+@end defun
+
+@heading Property information
+
+In general, nodes in a concrete syntax tree fall into two categories:
+@dfn{named nodes} and @dfn{anonymous nodes}.  Whether a node is named
+or anonymous is determined by the language definition
+(@pxref{tree-sitter named node, named node}).
+
+@cindex tree-sitter missing node
+@cindex missing node, tree-sitter
+Apart from being named or anonymous, a node can have other properties.
+A node can be ``missing'': such nodes are inserted by the parser in
+order to recover from certain kinds of syntax errors, i.e., something
+should probably be there according to the grammar, but is not there.
+This can happen during editing of the program source, when the source
+is not yet in its final form.
+
+@cindex tree-sitter extra node
+@cindex extra node, tree-sitter
+A node can be ``extra'': such nodes represent things like comments,
+which can appear anywhere in the text.
+
+@cindex tree-sitter outdated node
+@cindex outdated node, tree-sitter
+A node can be ``outdated'', if its parser has reparsed at least once
+after the node was created.
+
+@cindex tree-sitter node that has error
+@cindex has error, tree-sitter node
+A node ``has error'' if the text it spans contains a syntax error.  It
+can be that the node itself has an error, or one of its descendants
+has an error.
+
+@defun treesit-node-check node property
+This function checks if @var{node} has the specified @var{property}.
+@var{property} can be @code{named}, @code{missing}, @code{extra},
+@code{outdated}, or @code{has-error}.
+@end defun
+
+@defun treesit-node-type node
+Named nodes have ``types'' (@pxref{tree-sitter node type, node type}).
+For example, a named node can be a @code{string_literal} node, where
+@code{string_literal} is its type.  The type of an anonymous node is
+just the text that the node represents; e.g., the type of a @samp{,}
+node 480is just @samp{,}.
+
+This function returns @var{node}'s type as a string.
+@end defun
+
+@heading Information as a child or parent
+
+@defun treesit-node-index node &optional named
+This function returns the index of @var{node} as a child node of its
+parent.  If @var{named} is non-@code{nil}, it only counts named nodes
+(@pxref{tree-sitter named node, named node}).
+@end defun
+
+@defun treesit-node-field-name node
+A child of a parent node could have a field name (@pxref{tree-sitter
+node field name, field name}).  This function returns the field name
+of @var{node} as a child of its parent.
+@end defun
+
+@defun treesit-node-field-name-for-child node n
+This function returns the field name of the @var{n}'th child of
+@var{node}.  It returns @code{nil} if there is no @var{n}'th child, or
+the @var{n}'th child doesn't have a field name.
+
+Note that @var{n} counts both named and anonymous child.  And @var{n}
+could be negative, e.g., @code{-1} represents the last child.
+@end defun
+
+@defun treesit-child-count node &optional named
+This function finds the number of children of @var{node}.  If
+@var{named} is non-@code{nil}, it only counts named children
+(@pxref{tree-sitter named node, named node}).
+@end defun
+
+@node Pattern Matching
+@section Pattern Matching Tree-sitter Nodes
+@cindex pattern matching with tree-sitter nodes
+
+@cindex capturing, tree-sitter node
+Tree-sitter lets Lisp programs match patterns using a small
+declarative language.  This pattern matching consists of two steps:
+first tree-sitter matches a @dfn{pattern} against nodes in the syntax
+tree, then it @dfn{captures} specific nodes that matched the pattern
+and returns the captured nodes.
+
+We describe first how to write the most basic query pattern and how to
+capture nodes in a pattern, then the pattern-matching function, and
+finally the more advanced pattern syntax.
+
+@heading Basic query syntax
+
+@cindex tree-sitter query pattern syntax
+@cindex pattern syntax, tree-sitter query
+@cindex query, tree-sitter
+A @dfn{query} consists of multiple @dfn{patterns}.  Each pattern is an
+s-expression that matches a certain node in the syntax node.  A
+pattern has the form @w{@code{(@var{type} (@var{child}@dots{}))}}
+
+For example, a pattern that matches a @code{binary_expression} node that
+contains @code{number_literal} child nodes would look like
+
+@example
+(binary_expression (number_literal))
+@end example
+
+To @dfn{capture} a node using the query pattern above, append
+@code{@@@var{capture-name}} after the node pattern you want to
+capture.  For example,
+
+@example
+(binary_expression (number_literal) @@number-in-exp)
+@end example
+
+@noindent
+captures @code{number_literal} nodes that are inside a
+@code{binary_expression} node with the capture name
+@code{number-in-exp}.
+
+We can capture the @code{binary_expression} node as well, with, for
+example, the capture name @code{biexp}:
+
+@example
+(binary_expression
+ (number_literal) @@number-in-exp) @@biexp
+@end example
+
+@heading Query function
+
+@cindex query functions, tree-sitter
+Now we can introduce the @dfn{query functions}.
+
+@defun treesit-query-capture node query &optional beg end node-only
+This function matches patterns in @var{query} within @var{node}.
+The argument @var{query} can be either a string, a s-expression, or a
+compiled query object.  For now, we focus on the string syntax;
+s-expression syntax and compiled query are described at the end of the
+section.
+
+The argument @var{node} can also be a parser or a language symbol.  A
+parser means using its root node, a language symbol means find or
+create a parser for that language in the current buffer, and use the
+root node.
+
+The function returns all the captured nodes in a list of the form
+@w{@code{(@var{capture_name} . @var{node})}}.  If @var{node-only} is
+non-@code{nil}, it returns the list of nodes instead.  By default the
+entire text of @var{node} is searched, but if @var{beg} and @var{end}
+are both non-@code{nil}, they specify the region of buffer text where
+this function should match nodes.  Any matching node whose span
+overlaps with the region between @var{beg} and @var{end} are captured,
+it doesn't have to be completely in the region.
+
+@vindex treesit-query-error
+@findex treesit-query-validate
+This function raises the @code{treesit-query-error} error if
+@var{query} is malformed.  The signal data contains a description of
+the specific error.  You can use @code{treesit-query-validate} to
+validate and debug the query.
+@end defun
+
+For example, suppose @var{node}'s text is @code{1 + 2}, and
+@var{query} is
+
+@example
+@group
+(setq query
+      "(binary_expression
+        (number_literal) @@number-in-exp) @@biexp")
+@end group
+@end example
+
+Matching that query would return
+
+@example
+@group
+(treesit-query-capture node query)
+    @result{} ((biexp . @var{<node for "1 + 2">})
+       (number-in-exp . @var{<node for "1">})
+       (number-in-exp . @var{<node for "2">}))
+@end group
+@end example
+
+As mentioned earlier, @var{query} could contain multiple patterns.
+For example, it could have two top-level patterns:
+
+@example
+@group
+(setq query
+      "(binary_expression) @@biexp
+       (number_literal)  @@number @@biexp")
+@end group
+@end example
+
+@defun treesit-query-string string query language
+This function parses @var{string} with @var{language}, matches its
+root node with @var{query}, and returns the result.
+@end defun
+
+@heading More query syntax
+
+Besides node type and capture, tree-sitter's pattern syntax can
+express anonymous node, field name, wildcard, quantification,
+grouping, alternation, anchor, and predicate.
+
+@subheading Anonymous node
+
+An anonymous node is written verbatim, surrounded by quotes.  A
+pattern matching (and capturing) keyword @code{return} would be
+
+@example
+"return" @@keyword
+@end example
+
+@subheading Wild card
+
+In a pattern, @samp{(_)} matches any named node, and @samp{_} matches
+any named and anonymous node.  For example, to capture any named child
+of a @code{binary_expression} node, the pattern would be
+
+@example
+(binary_expression (_) @@in_biexp)
+@end example
+
+@subheading Field name
+
+It is possible to capture child nodes that have specific field names.
+In the pattern below, @code{declarator} and @code{body} are field
+names, indicated by the colon following them.
+
+@example
+@group
+(function_definition
+  declarator: (_) @@func-declarator
+  body: (_) @@func-body)
+@end group
+@end example
+
+It is also possible to capture a node that doesn't have a certain
+field, say, a @code{function_definition} without a @code{body} field.
+
+@example
+(function_definition !body) @@func-no-body
+@end example
+
+@subheading Quantify node
+
+@cindex quantify node, tree-sitter
+Tree-sitter recognizes quantification operators @samp{*}, @samp{+} and
+@samp{?}.  Their meanings are the same as in regular expressions:
+@samp{*} matches the preceding pattern zero or more times, @samp{+}
+matches one or more times, and @samp{?} matches zero or one time.
+
+For example, the following pattern matches @code{type_declaration}
+nodes that has @emph{zero or more} @code{long} keyword.
+
+@example
+(type_declaration "long"*) @@long-type
+@end example
+
+The following pattern matches a type declaration that has zero or one
+@code{long} keyword:
+
+@example
+(type_declaration "long"?) @@long-type
+@end example
+
+@subheading Grouping
+
+Similar to groups in regular expression, we can bundle patterns into
+groups and apply quantification operators to them.  For example, to
+express a comma separated list of identifiers, one could write
+
+@example
+(identifier) ("," (identifier))*
+@end example
+
+@subheading Alternation
+
+Again, similar to regular expressions, we can express ``match anyone
+from this group of patterns'' in a pattern.  The syntax is a list of
+patterns enclosed in square brackets.  For example, to capture some
+keywords in C, the pattern would be
+
+@example
+@group
+[
+  "return"
+  "break"
+  "if"
+  "else"
+] @@keyword
+@end group
+@end example
+
+@subheading Anchor
+
+The anchor operator @samp{.} can be used to enforce juxtaposition,
+i.e., to enforce two things to be directly next to each other.  The
+two ``things'' can be two nodes, or a child and the end of its parent.
+For example, to capture the first child, the last child, or two
+adjacent children:
+
+@example
+@group
+;; Anchor the child with the end of its parent.
+(compound_expression (_) @@last-child .)
+@end group
+
+@group
+;; Anchor the child with the beginning of its parent.
+(compound_expression . (_) @@first-child)
+@end group
+
+@group
+;; Anchor two adjacent children.
+(compound_expression
+ (_) @@prev-child
+ .
+ (_) @@next-child)
+@end group
+@end example
+
+Note that the enforcement of juxtaposition ignores any anonymous
+nodes.
+
+@subheading Predicate
+
+It is possible to add predicate constraints to a pattern.  For
+example, with the following pattern:
+
+@example
+@group
+(
+ (array . (_) @@first (_) @@last .)
+ (#equal @@first @@last)
+)
+@end group
+@end example
+
+@noindent
+tree-sitter only matches arrays where the first element equals to
+the last element.  To attach a predicate to a pattern, we need to
+group them together.  A predicate always starts with a @samp{#}.
+Currently there are two predicates, @code{#equal} and @code{#match}.
+
+@deffn Predicate equal arg1 arg2
+Matches if @var{arg1} equals to @var{arg2}.  Arguments can be either
+strings or capture names.  Capture names represent the text that the
+captured node spans in the buffer.
+@end deffn
+
+@deffn Predicate match regexp capture-name
+Matches if the text that @var{capture-name}'s node spans in the buffer
+matches regular expression @var{regexp}.  Matching is case-sensitive.
+@end deffn
+
+Note that a predicate can only refer to capture names that appear in
+the same pattern.  Indeed, it makes little sense to refer to capture
+names in other patterns.
+
+@heading S-expression patterns
+
+@cindex tree-sitter patterns as sexps
+@cindex patterns, tree-sitter, in sexp form
+Besides strings, Emacs provides a s-expression based syntax for
+tree-sitter patterns.  It largely resembles the string-based syntax.
+For example, the following query
+
+@example
+@group
+(treesit-query-capture
+ node "(addition_expression
+        left: (_) @@left
+        \"+\" @@plus-sign
+        right: (_) @@right) @@addition
+
+        [\"return\" \"break\"] @@keyword")
+@end group
+@end example
+
+@noindent
+is equivalent to
+
+@example
+@group
+(treesit-query-capture
+ node '((addition_expression
+         left: (_) @@left
+         "+" @@plus-sign
+         right: (_) @@right) @@addition
+
+         ["return" "break"] @@keyword))
+@end group
+@end example
+
+Most patterns can be written directly as strange but nevertheless
+valid s-expressions.  Only a few of them needs modification:
+
+@itemize
+@item
+Anchor @samp{.} is written as @code{:anchor}.
+@item
+@samp{?} is written as @samp{:?}.
+@item
+@samp{*} is written as @samp{:*}.
+@item
+@samp{+} is written as @samp{:+}.
+@item
+@code{#equal} is written as @code{:equal}.  In general, predicates
+change their @samp{#} to @samp{:}.
+@end itemize
+
+For example,
+
+@example
+@group
+"(
+  (compound_expression . (_) @@first (_)* @@rest)
+  (#match \"love\" @@first)
+  )"
+@end group
+@end example
+
+@noindent
+is written in s-expression as
+
+@example
+@group
+'((
+   (compound_expression :anchor (_) @@first (_) :* @@rest)
+   (:match "love" @@first)
+   ))
+@end group
+@end example
+
+@heading Compiling queries
+
+@cindex compiling tree-sitter queries
+@cindex queries, compiling
+If a query is intended to be used repeatedly, especially in tight
+loops, it is important to compile that query, because a compiled query
+is much faster than an uncompiled one.  A compiled query can be used
+anywhere a query is accepted.
+
+@defun treesit-query-compile language query
+This function compiles @var{query} for @var{language} into a compiled
+query object and returns it.
+
+This function raises the @code{treesit-query-error} error if
+@var{query} is malformed.  The signal data contains a description of
+the specific error.  You can use @code{treesit-query-validate} to
+validate and debug the query.
+@end defun
+
+@defun treesit-query-language query
+This function return the language of @var{query}.
+@end defun
+
+@defun treesit-query-expand query
+This function converts the s-expression @var{query} into the string
+format.
+@end defun
+
+@defun treesit-pattern-expand pattern
+This function converts the s-expression @var{pattern} into the string
+format.
+@end defun
+
+For more details, read the tree-sitter project's documentation about
+pattern-matching, which can be found at
+@uref{https://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries}.
+
+@node Multiple Languages
+@section Parsing Text in Multiple Languages
+@cindex multiple languages, parsing with tree-sitter
+@cindex parsing multiple languages with tree-sitter
+Sometimes, the source of a programming language could contain snippets
+of other languages; @acronym{HTML} + @acronym{CSS} + JavaScript is one
+example.  In that case, text segments written in different languages
+need to be assigned different parsers.  Traditionally, this is
+achieved by using narrowing.  While tree-sitter works with narrowing
+(@pxref{tree-sitter narrowing, narrowing}), the recommended way is
+instead to set regions of buffer text (i.e., ranges) in which a parser
+will operate.  This section describes functions for setting and
+getting ranges for a parser.
+
+Lisp programs should call @code{treesit-update-ranges} to make sure
+the ranges for each parser are correct before using parsers in a
+buffer, and call @code{treesit-language-at} to figure out the language
+responsible for the text at some position.  These two functions don't
+work by themselves, they need major modes to set
+@code{treesit-range-settings} and
+@code{treesit-language-at-point-function}, which do the actual work.
+These functions and variables are explained in more detail towards the
+end of the section.
+
+@heading Getting and setting ranges
+
+@defun treesit-parser-set-included-ranges parser ranges
+This function sets up @var{parser} to operate on @var{ranges}.  The
+@var{parser} will only read the text of the specified ranges.  Each
+range in @var{ranges} is a list of the form @w{@code{(@var{beg}
+. @var{end})}}.
+
+The ranges in @var{ranges} must come in order and must not overlap.
+That is, in pseudo code:
+
+@example
+@group
+(cl-loop for idx from 1 to (1- (length ranges))
+         for prev = (nth (1- idx) ranges)
+         for next = (nth idx ranges)
+         should (<= (car prev) (cdr prev)
+                    (car next) (cdr next)))
+@end group
+@end example
+
+@vindex treesit-range-invalid
+If @var{ranges} violates this constraint, or something else went
+wrong, this function signals the @code{treesit-range-invalid} error.
+The signal data contains a specific error message and the ranges we
+are trying to set.
+
+This function can also be used for disabling ranges.  If @var{ranges}
+is @code{nil}, the parser is set to parse the whole buffer.
+
+Example:
+
+@example
+@group
+(treesit-parser-set-included-ranges
+ parser '((1 . 9) (16 . 24) (24 . 25)))
+@end group
+@end example
+@end defun
+
+@defun treesit-parser-included-ranges parser
+This function returns the ranges set for @var{parser}.  The return
+value is the same as the @var{ranges} argument of
+@code{treesit-parser-included-ranges}: a list of cons cells of the form
+@w{@code{(@var{beg} . @var{end})}}.  If @var{parser} doesn't have any
+ranges, the return value is @code{nil}.
+
+@example
+@group
+(treesit-parser-included-ranges parser)
+    @result{} ((1 . 9) (16 . 24) (24 . 25))
+@end group
+@end example
+@end defun
+
+@defun treesit-query-range source query &optional beg end
+This function matches @var{source} with @var{query} and returns the
+ranges of captured nodes.  The return value is a list of cons cells of
+the form @w{@code{(@var{beg} . @var{end})}}, where @var{beg} and
+@var{end} specify the beginning and the end of a region of text.
+
+For convenience, @var{source} can be a language symbol, a parser, or a
+node.  If it's a language symbol, this function matches in the root
+node of the first parser using that language; if a parser, this
+function matches in the root node of that parser; if a node, this
+function matches in that node.
+
+The argument @var{query} is the query used to capture nodes
+(@pxref{Pattern Matching}).  The capture names don't matter.  The
+arguments @var{beg} and @var{end}, if both non-@code{nil}, limit the
+range in which this function queries.
+
+Like other query functions, this function raises the
+@code{treesit-query-error} error if @var{query} is malformed.
+@end defun
+
+@heading Supporting multiple languages in Lisp programs
+
+It should suffice for general Lisp programs to call the following two
+functions in order to support program sources that mixes multiple
+languages.
+
+@defun treesit-update-ranges &optional beg end
+This function updates ranges for parsers in the buffer.  It makes sure
+the parsers' ranges are set correctly between @var{beg} and @var{end},
+according to @code{treesit-range-settings}.  If omitted, @var{beg}
+defaults to the beginning of the buffer, and @var{end} defaults to the
+end of the buffer.
+
+For example, fontification functions use this function before querying
+for nodes in a region.
+@end defun
+
+@defun treesit-language-at pos
+This function returns the language of the text at buffer position
+@var{pos}.  Under the hood it calls
+@code{treesit-language-at-point-function} and returns its return
+value.  If @code{treesit-language-at-point-function} is @code{nil},
+this function returns the language of the first parser in the returned
+value of @code{treesit-parser-list}.  If there is no parser in the
+buffer, it returns @code{nil}.
+@end defun
+
+@heading Supporting multiple languages in major modes
+
+@cindex host language, tree-sitter
+@cindex tree-sitter host and embedded languages
+@cindex embedded language, tree-sitter
+Normally, in a set of languages that can be mixed together, there is a
+@dfn{host language} and one or more @dfn{embedded languages}.  A Lisp
+program usually first parses the whole document with the host
+language's parser, retrieves some information, sets ranges for the
+embedded languages with that information, and then parses the embedded
+languages.
+
+Take a buffer containing @acronym{HTML}, @acronym{CSS} and JavaScript
+as an example.  A Lisp program will first parse the whole buffer with
+an @acronym{HTML} parser, then query the parser for
+@code{style_element} and @code{script_element} nodes, which
+correspond to @acronym{CSS} and JavaScript text, respectively.  Then
+it sets the range of the @acronym{CSS} and JavaScript parser to the
+ranges in which their corresponding nodes span.
+
+Given a simple @acronym{HTML} document:
+
+@example
+@group
+<html>
+  <script>1 + 2</script>
+  <style>body @{ color: "blue"; @}</style>
+</html>
+@end group
+@end example
+
+@noindent
+a Lisp program will first parse with a @acronym{HTML} parser, then set
+ranges for @acronym{CSS} and JavaScript parsers:
+
+@example
+@group
+;; Create parsers.
+(setq html (treesit-get-parser-create 'html))
+(setq css (treesit-get-parser-create 'css))
+(setq js (treesit-get-parser-create 'javascript))
+@end group
+
+@group
+;; Set CSS ranges.
+(setq css-range
+      (treesit-query-range
+       'html
+       "(style_element (raw_text) @@capture)"))
+(treesit-parser-set-included-ranges css css-range)
+@end group
+
+@group
+;; Set JavaScript ranges.
+(setq js-range
+      (treesit-query-range
+       'html
+       "(script_element (raw_text) @@capture)"))
+(treesit-parser-set-included-ranges js js-range)
+@end group
+@end example
+
+Emacs automates this process in @code{treesit-update-ranges}.  A
+multi-language major mode should set @code{treesit-range-settings} so
+that @code{treesit-update-ranges} knows how to perform this process
+automatically.  Major modes should use the helper function
+@code{treesit-range-rules} to generate a value that can be assigned to
+@code{treesit-range-settings}.  The settings in the following example
+directly translate into operations shown above.
+
+@example
+@group
+(setq-local treesit-range-settings
+            (treesit-range-rules
+             :embed 'javascript
+             :host 'html
+             '((script_element (raw_text) @@capture))
+@end group
+
+@group
+             :embed 'css
+             :host 'html
+             '((style_element (raw_text) @@capture))))
+@end group
+@end example
+
+@defun treesit-range-rules &rest query-specs
+This function is used to set @var{treesit-range-settings}.  It
+takes care of compiling queries and other post-processing, and outputs
+a value that @var{treesit-range-settings} can have.
+
+It takes a series of @var{query-spec}s, where each @var{query-spec} is
+a @var{query} preceded by zero or more @var{keyword}/@var{value}
+pairs.  Each @var{query} is a tree-sitter query in either the
+string, s-expression or compiled form, or a function.
+
+If @var{query} is a tree-sitter query, it should be preceded by two
+@var{:keyword}/@var{value} pairs, where the @code{:embed} keyword
+specifies the embedded language, and the @code{:host} keyword
+specified the host language.
+
+@code{treesit-update-ranges} uses @var{query} to figure out how to set
+the ranges for parsers for the embedded language.  It queries
+@var{query} in a host language parser, computes the ranges in which
+the captured nodes span, and applies these ranges to embedded
+language parsers.
+
+If @var{query} is a function, it doesn't need any @var{:keyword} and
+@var{value} pair.  It should be a function that takes 2 arguments,
+@var{start} and @var{end}, and sets the ranges for parsers in the
+current buffer in the region between @var{start} and @var{end}.  It is
+fine for this function to set ranges in a larger region that
+encompasses the region between @var{start} and @var{end}.
+@end defun
+
+@defvar treesit-range-settings
+This variable helps @code{treesit-update-ranges} in updating the
+ranges for parsers in the buffer.  It is a list of @var{setting}s
+where the exact format of a @var{setting} is considered internal.  You
+should use @code{treesit-range-rules} to generate a value that this
+variable can have.
+
+@c Because the format is internal, we don't document them here.  Though
+@c we do have it explained in the docstring.  We also expose the fact
+@c that it is a list of settings, so one could combine two of them with
+@c append.
+@end defvar
+
+
+@defvar treesit-language-at-point-function
+This variable's value should be a function that takes a single
+argument, @var{pos}, which is a buffer position, and returns the
+language of the buffer text at @var{pos}.  This variable is used by
+@code{treesit-language-at}.
+@end defvar
+
+@node Tree-sitter major modes
+@section Developing major modes with tree-sitter
+@cindex major mode, developing with tree-sitter
+
+This section covers some general guidelines on developing tree-sitter
+integration for a major mode.
+
+A major mode supporting tree-sitter features should roughly follow
+this pattern:
+
+@c FIXME: Update this part once we settle on the exact format.
+@example
+@group
+(define-derived-mode woomy-mode prog-mode "Woomy"
+  "A mode for Woomy programming language."
+  ;; Shared setup.
+  ...
+  (cond
+   ;; Tree-sitter setup.
+   ((treesit-ready-p 'woomy-mode 'woomy)
+    (setq-local treesit-variables ...)
+    (treesit-major-mode-setup))
+   ;; Non-tree-sitter setup.
+   (t
+    ...)))
+@end group
+@end example
+
+First, the major mode should use @code{treesit-ready-p} to determine
+whether tree-sitter can be activated in this mode.
+
+@defun treesit-ready-p mode language &optional quiet
+This function checks for conditions for activating tree-sitter.  It
+checks whether Emacs was built with tree-sitter, whether the buffer's
+size is not too large for tree-sitter to handle it, and whether the
+language definition for @var{language} is available on the system
+(@pxref{Language Definitions}).
+
+This function emits a warning if tree-sitter cannot be activated.  If
+@var{quiet} is @code{message}, the warning is turned into a message;
+if @var{quiet} is @code{nil}, no warning or message is displayed.
+
+If all the necessary conditions are met, this function returns
+non-@code{nil}; otherwise it returns @code{nil}.
+@end defun
+
+Next, the major mode should set up tree-sitter variables and call
+@code{treesit-major-mode-setup}.
+
+@defun treesit-major-mode-setup
+This function activates some tree-sitter features for a major mode.
+
+Currently, it sets up the following features:
+@itemize
+@item
+If @code{treesit-font-lock-settings} is non-@code{nil}, it sets up
+fontification.
+@item
+If @code{treesit-simple-indent-rules} is non-@code{nil}, it sets up
+indentation.
+@item
+If @code{treesit-defun-type-regexp} is non-@code{nil}, it sets up
+navigation functions for @code{beginning-of-defun} and
+@code{end-of-defun}.
+@end itemize
+@end defun
+
+For more information of these built-in tree-sitter features,
+@pxref{Parser-based Font Lock}, @pxref{Parser-based Indentation}, and
+@pxref{List Motion}.
+
+For supporting mixing of multiple languages in a major mode,
+@pxref{Multiple Languages}.
+
+Setting the following local variables allows tree-sitter's indentation
+engine to correctly indent multi-line comments:
+
+@defvar treesit-comment-start
+This should be a regular expression matching an opening comment token.
+For example, it should match @samp{//}, @samp{////}, @samp{/*},
+@samp{/****}, etc., in C.
+@end defvar
+
+@defvar treesit-comment-end
+This should be a regular expression matching a closing comment token.
+For example, it should match @samp{*/}, @samp{****/}, etc., in C.
+@end defvar
+
+@node Tree-sitter C API
+@section Tree-sitter C API Correspondence
+
+Emacs' tree-sitter integration doesn't expose every feature
+provided by tree-sitter's C API.  Missing features include:
+
+@itemize
+@item
+Creating a tree cursor and navigating the syntax tree with it.
+@item
+Setting timeout and cancellation flag for a parser.
+@item
+Setting the logger for a parser.
+@item
+Printing a @acronym{DOT} graph of the syntax tree to a file.
+@item
+Copying and modifying a syntax tree.  (Emacs doesn't expose a tree
+object.)
+@item
+Using (row, column) coordinates as position.
+@item
+Updating a node with changes.  (In Emacs, retrieve a new node instead
+of updating the existing one.)
+@item
+Querying statics of a language definition.
+@end itemize
+
+In addition, Emacs makes some changes to the C API to make the API more
+convenient and idiomatic:
+
+@itemize
+@item
+Instead of using byte positions, the Emacs Lisp API uses character
+positions.
+@item
+Null nodes are converted to nil.
+@end itemize
+
+Below is the correspondence between all C API functions and their
+ELisp counterparts.  Sometimes one ELisp function corresponds to
+multiple C functions, and many C functions don't have an ELisp
+counterpart.
+
+@example
+ts_parser_new                           treesit-parser-create
+ts_parser_delete
+ts_parser_set_language
+ts_parser_language                      treesit-parser-language
+ts_parser_set_included_ranges           treesit-parser-set-included-ranges
+ts_parser_included_ranges               treesit-parser-included-ranges
+ts_parser_parse
+ts_parser_parse_string                  treesit-parse-string
+ts_parser_parse_string_encoding
+ts_parser_reset
+ts_parser_set_timeout_micros
+ts_parser_timeout_micros
+ts_parser_set_cancellation_flag
+ts_parser_cancellation_flag
+ts_parser_set_logger
+ts_parser_logger
+ts_parser_print_dot_graphs
+ts_tree_copy
+ts_tree_delete
+ts_tree_root_node
+ts_tree_language
+ts_tree_edit
+ts_tree_get_changed_ranges
+ts_tree_print_dot_graph
+ts_node_type                            treesit-node-type
+ts_node_symbol
+ts_node_start_byte                      treesit-node-start
+ts_node_start_point
+ts_node_end_byte                        treesit-node-end
+ts_node_end_point
+ts_node_string                          treesit-node-string
+ts_node_is_null
+ts_node_is_named                        treesit-node-check
+ts_node_is_missing                      treesit-node-check
+ts_node_is_extra                        treesit-node-check
+ts_node_has_changes
+ts_node_has_error                       treesit-node-check
+ts_node_parent                          treesit-node-parent
+ts_node_child                           treesit-node-child
+ts_node_field_name_for_child            treesit-node-field-name-for-child
+ts_node_child_count                     treesit-node-child-count
+ts_node_named_child                     treesit-node-child
+ts_node_named_child_count               treesit-node-child-count
+ts_node_child_by_field_name             treesit-node-by-field-name
+ts_node_child_by_field_id
+ts_node_next_sibling                    treesit-next-sibling
+ts_node_prev_sibling                    treesit-prev-sibling
+ts_node_next_named_sibling              treesit-next-sibling
+ts_node_prev_named_sibling              treesit-prev-sibling
+ts_node_first_child_for_byte            treesit-first-child-for-pos
+ts_node_first_named_child_for_byte      treesit-first-child-for-pos
+ts_node_descendant_for_byte_range       treesit-descendant-for-range
+ts_node_descendant_for_point_range
+ts_node_named_descendant_for_byte_range treesit-descendant-for-range
+ts_node_named_descendant_for_point_range
+ts_node_edit
+ts_node_eq                              treesit-node-eq
+ts_tree_cursor_new
+ts_tree_cursor_delete
+ts_tree_cursor_reset
+ts_tree_cursor_current_node
+ts_tree_cursor_current_field_name
+ts_tree_cursor_current_field_id
+ts_tree_cursor_goto_parent
+ts_tree_cursor_goto_next_sibling
+ts_tree_cursor_goto_first_child
+ts_tree_cursor_goto_first_child_for_byte
+ts_tree_cursor_goto_first_child_for_point
+ts_tree_cursor_copy
+ts_query_new
+ts_query_delete
+ts_query_pattern_count
+ts_query_capture_count
+ts_query_string_count
+ts_query_start_byte_for_pattern
+ts_query_predicates_for_pattern
+ts_query_step_is_definite
+ts_query_capture_name_for_id
+ts_query_string_value_for_id
+ts_query_disable_capture
+ts_query_disable_pattern
+ts_query_cursor_new
+ts_query_cursor_delete
+ts_query_cursor_exec                    treesit-query-capture
+ts_query_cursor_did_exceed_match_limit
+ts_query_cursor_match_limit
+ts_query_cursor_set_match_limit
+ts_query_cursor_set_byte_range
+ts_query_cursor_set_point_range
+ts_query_cursor_next_match
+ts_query_cursor_remove_match
+ts_query_cursor_next_capture
+ts_language_symbol_count
+ts_language_symbol_name
+ts_language_symbol_for_name
+ts_language_field_count
+ts_language_field_name_for_id
+ts_language_field_id_for_name
+ts_language_symbol_type
+ts_language_version
+@end example
diff --git a/doc/lispref/positions.texi b/doc/lispref/positions.texi
index 7945232bf8..a01e568de0 100644
--- a/doc/lispref/positions.texi
+++ b/doc/lispref/positions.texi
@@ -834,6 +834,22 @@ a defun.  The function @code{end-of-defun} calls this 
function instead
 of using its normal method.
 @end defvar
 
+@findex treesit-beginning-of-defun
+@findex treesit-end-of-defun
+If Emacs is compiled with tree-sitter, it can use the tree-sitter parser
+information to move across syntax constructs.  A major mode can set
+@code{treesit-defun-type-regexp} and get navigation functionality for
+free, by using @code{treesit-beginning-of-defun} and
+@code{treesit-end-of-defun}.
+
+@defvar treesit-defun-type-regexp
+The value of this variable is a regexp matching the node type of defun
+nodes.  (For ``node'', ``node type'', @pxref{Parsing Program Source}.)
+
+For example, @code{python-mode} sets this variable to a regexp that
+matches either @code{function_definition} or @code{class_definition}.
+@end defvar
+
 @node Skipping Characters
 @subsection Skipping Characters
 @cindex skipping characters
diff --git a/doc/misc/ChangeLog.1 b/doc/misc/ChangeLog.1
index 48637ab608..1c5e7c1e2f 100644
--- a/doc/misc/ChangeLog.1
+++ b/doc/misc/ChangeLog.1
@@ -3525,7 +3525,7 @@
 
 2012-01-03  Carsten Dominik  <carsten.dominik@gmail.com>
 
-       * org.texi (The clock table): Mention that ACHIVED trees
+       * org.texi (The clock table): Mention that ARCHIVED trees
        contribute to the clock table.
 
 2012-01-03  Carsten Dominik  <carsten.dominik@gmail.com>  (tiny change)
@@ -4420,12 +4420,7 @@
 
 2011-02-05  Era Eriksson  <era+tramp@iki.fi>  (tiny change)
 
-       * tramp.texi:
-       Replace "delimet" with "delimit" globally.
-       Replace "explicite" with "explicit" globally.
-       Replace "instead of" with "instead" where there was nothing after "of".
-       Audit use of comma before interrogative pronoun, "that", or "which".
-       Minor word order, spelling, wording changes.
+       * tramp.texi: Minor word order, spelling, wording changes.
 
 2011-02-04  Teodor Zlatanov  <tzz@lifelogs.com>
 
@@ -5868,7 +5863,7 @@
 2009-09-02  Carsten Dominik  <carsten.dominik@gmail.com>
 
        * org.texi (Effort estimates): Document new effort setting commands.
-       (Agenda commands): Document the new keys fro agenda time motion.
+       (Agenda commands): Document the new keys for agenda time motion.
        Document entry text mode.  Improve documentation of the keys to include
        inactive time stamps into the agenda view.
        (Feedback): Document the new bug report command.
@@ -10409,7 +10404,7 @@
        * sc.texi (Emacs 18 MUAs):
        * speedbar.texi (Top):
        * url.texi (History):
-       Delete duplicate duplicate words.
+       Delete duplicate words.
 
 2005-07-16  Johan Bockgård  <bojohan@users.sourceforge.net>  (tiny change)
 
diff --git a/doc/misc/Makefile.in b/doc/misc/Makefile.in
index b6eef7ea79..a7dbbbb48f 100644
--- a/doc/misc/Makefile.in
+++ b/doc/misc/Makefile.in
@@ -68,13 +68,13 @@ 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 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 \
-       org pcl-cvs pgg rcirc remember reftex sasl \
-       sc semantic ses sieve smtpmail speedbar srecode todo-mode transient \
-       tramp url vhdl-mode vip viper vtable widget wisent woman
+       dbus dired-x ebrowse ede ediff edt efaq eglot eieio \
+       emacs-gnutls emacs-mime epa erc ert eshell eudc eww \
+       flymake forms gnus htmlfontify idlwave ido info.info \
+       mairix-el message mh-e modus-themes newsticker nxml-mode \
+       octave-mode org pcl-cvs pgg rcirc reftex remember sasl \
+       sc semantic ses sieve smtpmail speedbar srecode todo-mode \
+       tramp transient url vhdl-mode vip viper vtable widget wisent woman
 
 ## Info files to install on current platform.
 INFO_INSTALL = $(INFO_COMMON) $(DOCMISC_W32)
diff --git a/doc/misc/auth.texi b/doc/misc/auth.texi
index 9dc63af6bc..872e5f88f5 100644
--- a/doc/misc/auth.texi
+++ b/doc/misc/auth.texi
@@ -526,6 +526,8 @@ If several entries match, the one matching the most items 
(where an
 while searching for an entry matching the @code{rms} user on host
 @code{gnu.org} and port @code{22}, then the entry
 @file{gnu.org:22/rms.gpg} is preferred over @file{gnu.org.gpg}.
+However, such processing is not applied when the option
+@code{auth-source-pass-extra-parameters} is set to @code{t}.
 
 Users of @code{pass} may also be interested in functionality provided
 by other Emacs packages:
@@ -549,6 +551,22 @@ Set this variable to a string that should separate an host 
name from a
 port in an entry.  Defaults to @samp{:}.
 @end defvar
 
+@defvar auth-source-pass-extra-query-keywords
+This expands the selection of available keywords to include
+@code{:max} and @code{:require} and tells more of them to accept a
+list of query parameters as an argument.  When searching, it also
+favors the @samp{rms@@gnu.org.gpg} form for usernames over the
+@samp{gnu.org/rms.gpg} form, regardless of whether a @code{:user}
+param was provided.
+
+In general, if you prefer idiosyncrasies traditionally exhibited by
+this backend, such as prioritizing field count in a filename, try
+setting this option to @code{nil}.  But, if you experience problems
+predicting the outcome of searches relative to other auth-source
+backends or encounter code expecting to query multiple backends
+uniformly, try flipping it back to @code{t} (the default).
+@end defvar
+
 @node Help for developers
 @chapter Help for developers
 
diff --git a/doc/misc/cc-mode.texi b/doc/misc/cc-mode.texi
index bade04fb95..a8f5248c4c 100644
--- a/doc/misc/cc-mode.texi
+++ b/doc/misc/cc-mode.texi
@@ -898,7 +898,7 @@ lines.
 @vindex defun-tactic @r{(c-)}
 
 Move to the beginning or end of the current or next function.  Other
-constructs (such as a structs or classes) which have a brace block
+constructs (such as structs or classes) which have a brace block
 also count as ``functions'' here.  To move over several functions, you
 can give these commands a repeat count.
 
diff --git a/doc/misc/ede.texi b/doc/misc/ede.texi
index 7a26fe0e57..0463d068c2 100644
--- a/doc/misc/ede.texi
+++ b/doc/misc/ede.texi
@@ -2536,7 +2536,7 @@ Call this when a user finishes customizing @var{TARGET}.
 @end deffn
 
 @deffn Method project-edit-file-target :AFTER ot
-Edit the target @var{OT} associated w/ this file.
+Edit the target @var{OT} associated with this file.
 @end deffn
 
 @deffn Method ede-documentation :AFTER this
@@ -3563,7 +3563,7 @@ Run the current project in the debugger.
 @end deffn
 
 @deffn Method project-edit-file-target :AFTER obj
-Edit the target associated w/ this file.
+Edit the target associated with this file.
 @end deffn
 
 @node project-am-objectcode
diff --git a/doc/misc/edt.texi b/doc/misc/edt.texi
index 8b4ac0da5d..d6f9c9faf9 100644
--- a/doc/misc/edt.texi
+++ b/doc/misc/edt.texi
@@ -317,7 +317,7 @@ emulation.
 Emacs binds keys to @acronym{ASCII} control characters and so does the
 real EDT@.  Where EDT key bindings and Emacs key bindings conflict,
 the default Emacs key bindings are retained by the EDT emulation by
-default.  If you are a diehard EDT user you may not like this.  The
+default.  If you are a die-hard EDT user you may not like this.  The
 @ref{Control keys} section explains how to change this so that the EDT
 bindings to @acronym{ASCII} control characters override the default
 Emacs bindings.
@@ -443,7 +443,7 @@ the PC @key{NumLock} keypad key will be configurable for 
the emulation
 of the @key{PF1} key.  The PC keypad can now emulate an LK-201 keypad
 (less the comma key), the standard keyboard supplied with DEC terminals
 VT-200 and above.  This @file{.xmodmaprc} file switches the role of the
-@key{F12} and @key{NumLock} keys.  It has been tested on RedHat
+@key{F12} and @key{NumLock} keys.  It has been tested on Red Hat
 GNU/Linux 5.2.  Other versions of GNU/Linux may require different
 keycodes.  (@ref{Unix} for further help on how to do this.)
 
@@ -460,7 +460,7 @@ keycode assignments vary from system to system, some 
investigation is
 needed to see how to do this on a particular system.
 
 You will need to look at the output generated by @code{xmodmap} invoked
-with the "-pm" switch.  For example, on RedHat GNU/Linux 5.2 on a PC, we
+with the "-pm" switch.  For example, on Red Hat GNU/Linux 5.2 on a PC, we
 get the following output when running @samp{xmodmap -pm}:
 
 @example
@@ -495,7 +495,7 @@ keycode  96 = F12
    .
 @end example
 
-@noindent So, in RedHat GNU/Linux 5.2 on a PC, Num_Lock generates keycode 77.
+@noindent So, in Red Hat GNU/Linux 5.2 on a PC, Num_Lock generates keycode 77.
 The following steps are taken:
 
 @enumerate
@@ -883,7 +883,7 @@ Here are some examples:
 (setq edt-word-entities '(?\t)        ; specifies TAB, the default
 @end example
 
-@noindent You can also specify characters by their decimal ascii values:
+@noindent You can also specify characters by their decimal ASCII values:
 
 @example
 (setq edt-word-entities '(9 45 47))   ; specifies TAB, - , and /
@@ -893,7 +893,7 @@ Here are some examples:
 @section Enabling EDT Control Key Sequence Bindings
 
 Where EDT key bindings and Emacs key bindings conflict, the default
-Emacs key bindings are retained by default.  Some diehard EDT users
+Emacs key bindings are retained by default.  Some die-hard EDT users
 may not like this.  So, if the variable
 @code{edt-use-EDT-control-key-bindings} is set to true in a user's
 @file{.emacs} file, then the default EDT Emulation mode will enable most
diff --git a/doc/misc/efaq-w32.texi b/doc/misc/efaq-w32.texi
index b58f6be758..bc3f545b2b 100644
--- a/doc/misc/efaq-w32.texi
+++ b/doc/misc/efaq-w32.texi
@@ -457,12 +457,12 @@ to your init file:
 @node Using with Explorer
 @subsection For use with Internet Explorer
 @cindex Internet Explorer, view source in Emacs
-@cindex mailto urls, associating with Emacs
-@cindex news urls, associating with Emacs
+@cindex mailto URLs, associating with Emacs
+@cindex news URLs, associating with Emacs
 @cindex URLs, associating mail and news URLs with Emacs
 
 You can use Emacs as the editor for composing mail for
-@indicateurl{mailto:} links, reading usenet for @indicateurl{news:}
+@indicateurl{mailto:} links, reading Usenet for @indicateurl{news:}
 links, and viewing source.  The following registry entries control
 this:
 
diff --git a/doc/misc/eglot.texi b/doc/misc/eglot.texi
index 04bdcc6161..a815aebf59 100644
--- a/doc/misc/eglot.texi
+++ b/doc/misc/eglot.texi
@@ -280,7 +280,7 @@ 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-alternatives} with the functional form of
 @code{eglot-server-programs}.
 
 @lisp
@@ -994,8 +994,8 @@ this variable should be a property list of the following 
format:
 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.
+object.  @var{plist} may be arbitrarily complex, generally containing
+other keyword-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.
diff --git a/doc/misc/emacs-gnutls.texi b/doc/misc/emacs-gnutls.texi
index 1b9f5e1040..8c8a6f3154 100644
--- a/doc/misc/emacs-gnutls.texi
+++ b/doc/misc/emacs-gnutls.texi
@@ -132,7 +132,7 @@ PEM or DER format and examples can be found in most Unix
 distributions.  By default the following locations are tried in this
 order: @file{/etc/ssl/certs/ca-certificates.crt} for Debian, Ubuntu,
 Gentoo and Arch Linux; @file{/etc/pki/tls/certs/ca-bundle.crt} for
-Fedora and RHEL; @file{/etc/ssl/ca-bundle.pem} for Suse;
+Fedora and RHEL; @file{/etc/ssl/ca-bundle.pem} for SUSE;
 @file{/usr/ssl/certs/ca-bundle.crt} for Cygwin;
 @file{/usr/local/share/certs/ca-root-nss.crt} for FreeBSD@.  You can
 easily customize @code{gnutls-trustfiles} to be something else, but
diff --git a/doc/misc/erc.texi b/doc/misc/erc.texi
index 3db83197f9..0d807e323e 100644
--- a/doc/misc/erc.texi
+++ b/doc/misc/erc.texi
@@ -79,6 +79,7 @@ Advanced Usage
 
 * Connecting::                  Ways of connecting to an IRC server.
 * Sample Configuration::        An example configuration file.
+* Integrations::                Integrations available for ERC.
 * Options::                     Options that are available for ERC.
 
 @end detailmenu
@@ -526,6 +527,7 @@ Translate morse code in messages
 @menu
 * Connecting::                  Ways of connecting to an IRC server.
 * Sample Configuration::        An example configuration file.
+* Integrations::                Integrations available for ERC.
 * Options::                     Options that are available for ERC.
 @end menu
 
@@ -861,7 +863,8 @@ The default value for all three options is the function
 @code{erc-auth-source-search}.  It tries to merge relevant contextual
 parameters with those provided or discovered from the logical connection
 or the underlying transport.  Some auth-source back ends may not be
-compatible; netrc, plstore, json, and secrets are currently supported.
+compatible; netrc, plstore, json, secrets, and pass are currently
+supported.
 @end defopt
 
 @subheading Full name
@@ -990,6 +993,32 @@ stuff, to the current ERC buffer."
 ;; (setq erc-kill-server-buffer-on-quit t)
 @end lisp
 
+@node Integrations
+@section Integrations
+@cindex integrations
+
+@subheading URL
+For anything to work, you'll want to set @code{url-irc-function} to
+@code{url-irc-erc}.  As a rule of thumb, libraries relying directly on
+@code{url-retrieve} should be fine out the box from Emacs 29.1 onward.
+On older versions of Emacs, you may need to @code{(require 'erc)}
+beforehand. @pxref{Retrieving URLs,,, url, URL}.
+
+For other apps and libraries, such as those relying on the
+higher-level @code{browse-url}, you'll oftentimes be asked to specify
+a pattern, sometimes paired with a function that accepts a string URL
+as a first argument.  For example, with EWW, you may need to tack
+something like @code{"\\|\\`irc6?s?:"} onto the end of
+@code{eww-use-browse-url}.  But with @code{gnus-button-alist}, you'll
+need a function as well:
+
+@lisp
+  '("\\birc6?s?://[][a-z0-9.,@@_:+%?&/#-]+" 0 t browse-url-irc 0)
+@end lisp
+
+@noindent
+Users on Emacs 28 and below may need to use @code{browse-url} instead.
+
 @node Options
 @section Options
 @cindex options
diff --git a/doc/misc/eshell.texi b/doc/misc/eshell.texi
index 96873a3f9a..e6ddcf11df 100644
--- a/doc/misc/eshell.texi
+++ b/doc/misc/eshell.texi
@@ -1126,7 +1126,7 @@ Repeatedly evaluate @var{commands} until 
@var{conditional} is
 satisfied.
 
 @item for @var{var} in @var{list}@dots{} @{ @var{commands} @}
-Iterate over each element of of @var{list}, storing the element in
+Iterate over each element of @var{list}, storing the element in
 @var{var} and evaluating @var{commands}.  If @var{list} is not a list,
 treat it as a list of one element.  If you specify multiple
 @var{lists}, this will iterate over each of them in turn.
diff --git a/doc/misc/eudc.texi b/doc/misc/eudc.texi
index 7293f48f41..0e1ff67c1f 100644
--- a/doc/misc/eudc.texi
+++ b/doc/misc/eudc.texi
@@ -224,7 +224,7 @@ 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
+lists, 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.
diff --git a/doc/misc/flymake.texi b/doc/misc/flymake.texi
index c075f0298a..4561b760c0 100644
--- a/doc/misc/flymake.texi
+++ b/doc/misc/flymake.texi
@@ -844,8 +844,8 @@ Binding,,, elisp, The Emacs Lisp Reference Manual}) to be 
active.
 * Interaction with other modes::
 @end menu
 
-@findex flymake-proc-legacy-backend
-The backend @code{flymake-proc-legacy-backend} was originally designed
+@findex flymake-proc-legacy-flymake
+The backend @code{flymake-proc-legacy-flymake} was originally designed
 to be extended for supporting new syntax check tools and error message
 patterns.  It is also controlled by its own set of customization variables
 
@@ -1053,7 +1053,7 @@ check-syntax:
 @cindex syntax check models
 @cindex master file
 
-@code{flymake-proc-legacy-backend} saves a copy of the buffer in a
+@code{flymake-proc-legacy-flymake} saves a copy of the buffer in a
 temporary file in the buffer's directory (or in the system temporary
 directory, for Java files), creates a syntax check command and
 launches a process with this command.  The output is parsed using a
diff --git a/doc/misc/gnus-faq.texi b/doc/misc/gnus-faq.texi
index 7cb5621b69..49022ac341 100644
--- a/doc/misc/gnus-faq.texi
+++ b/doc/misc/gnus-faq.texi
@@ -144,9 +144,8 @@ information to disk (e.g., which messages you read), you
 are now asked if you want to restore that information
 from the auto-save file.
 
-To prevent this message make sure you exit Gnus
-via @samp{q} in group buffer instead of
-just killing Emacs.
+To prevent this message make sure you exit Gnus via @kbd{q} in group
+buffer instead of just killing Emacs.
 
 @node FAQ 2-2
 @subsubheading Question 2.2
@@ -180,25 +179,23 @@ example for this (guess from whose .gnus :-)):
 @node FAQ 2-4
 @subsubheading Question 2.4
 
-My group buffer becomes a bit crowded, is there a way to
-sort my groups into categories so I can easier browse
-through them?
+My group buffer is a bit crowded.  Is there a way to sort groups into
+categories so I can browse them more easily?
 
 @subsubheading Answer
 
-Gnus offers the topic mode, it allows you to sort your
-groups in, well, topics, e.g., all groups dealing with
-Linux under the topic linux, all dealing with music under
-the topic music and all dealing with scottish music under
-the topic scottish which is a subtopic of music.
+Gnus offers the topic mode, it allows you to sort your groups in,
+well, topics.  For example, all groups dealing with Linux under the
+topic @samp{linux}, all dealing with music under the topic
+@samp{music} and all dealing with Scottish music under the topic
+@samp{scottish} which is a subtopic of @samp{music}.
 
-To enter topic mode, just hit t while in Group buffer.  Now
-you can use @samp{T n} to create a topic
-at point and @samp{T m} to move a group to
-a specific topic.  For more commands see the manual or the
-menu.  You might want to include the %P specifier at the
-beginning of your gnus-group-line-format variable to have
-the groups nicely indented.
+To enter topic mode, just hit @kbd{t} while in Group buffer.  Now you
+can use @kbd{T n} to create a topic at point and @kbd{T m} to move a
+group to a specific topic.  For more commands see the manual or the
+menu.  You might want to include the @samp{%P} specifier at the
+beginning of your @var{gnus-group-line-format} variable to have the
+groups nicely indented.
 
 @node FAQ 2-5
 @subsubheading Question 2.5
@@ -208,16 +205,14 @@ sort the groups in a topic?
 
 @subsubheading Answer
 
-Move point over the group you want to move and
-hit @samp{C-k}, now move point to the
-place where you want the group to be and
-hit @samp{C-y}.
+Move point over the group you want to move and hit @kbd{C-k}, now move
+point to the place where you want the group to be and hit @kbd{C-y}.
 
 @node FAQ 3 - Getting Messages
 @subsection Getting Messages
 
 @menu
-* FAQ 3-1::     I just installed Gnus, started it via  @samp{M-x gnus}
+* FAQ 3-1::     I just installed Gnus, started it via  @kbd{M-x gnus}
                 but it only says "nntp (news) open error", what to do?
 * FAQ 3-2::     I'm working under Windows and have no idea what
                 ~/.gnus.el means.
@@ -242,9 +237,8 @@ hit @samp{C-y}.
 @node FAQ 3-1
 @subsubheading Question 3.1
 
-I just installed Gnus, started it via
-@samp{M-x gnus}
-but it only says "nntp (news) open error", what to do?
+I just installed Gnus, started it via @kbd{M-x gnus} but it only says
+"nntp (news) open error", what to do?
 
 @subsubheading Answer
 
@@ -270,7 +264,7 @@ The ~/ means the home directory where Gnus and Emacs look
 for the configuration files.  However, you don't really
 need to know what this means, it suffices that Emacs knows
 what it means :-) You can type
-@samp{C-x C-f ~/.gnus.el @key{RET}}
+@kbd{C-x C-f ~/.gnus.el @key{RET}}
 (yes, with the forward slash, even on Windows), and
 Emacs will open the right file for you.  (It will most
 likely be new, and thus empty.)
@@ -295,7 +289,7 @@ possibility to set environment variables.  Create a new one 
with
 name HOME and value C:\myhome.  Rebooting is not necessary.
 
 Now to create @file{~/.gnus.el}, say
-@samp{C-x C-f ~/.gnus.el @key{RET} C-x C-s}.
+@kbd{C-x C-f ~/.gnus.el @key{RET} C-x C-s}.
 in Emacs.
 
 @node FAQ 3-3
@@ -331,14 +325,12 @@ subscribe to a group.
 
 @subsubheading Answer
 
-If you know the name of the group say @samp{U
-name.of.group @key{RET}} in group buffer (use the
-tab-completion Luke).  Otherwise hit ^ in group buffer,
-this brings you to the server buffer.  Now place point (the
-cursor) over the server which carries the group you want,
-hit @samp{@key{RET}}, move point to the group
-you want to subscribe to and say @samp{u}
-to subscribe to it.
+If you know the name of the group say @kbd{U name.of.group @key{RET}}
+in group buffer (use the tab-completion Luke).  Otherwise hit @kbd{^}
+in group buffer, this brings you to the server buffer.  Now place
+point (the cursor) over the server which carries the group you want,
+hit @kbd{RET}, move point to the group you want to subscribe to and
+say @kbd{u} to subscribe to it.
 
 @node FAQ 3-5
 @subsubheading Question 3.5
@@ -625,12 +617,10 @@ When I enter a group, all read messages are gone.  How to 
view them again?
 
 @subsubheading Answer
 
-If you enter the group by saying
-@samp{@key{RET}}
-in group buffer with point over the group, only unread and ticked messages are 
loaded.  Say
-@samp{C-u @key{RET}}
-instead to load all available messages.  If you want only the 300 newest say
-@samp{C-u 300 @key{RET}}
+If you enter the group by saying @kbd{@key{RET}} in group buffer with
+point over the group, only unread and ticked messages are loaded.  Say
+@kbd{C-u @key{RET}} instead to load all available messages.  If you
+want only the 300 newest say @kbd{C-u 300 @key{RET}}
 
 Loading only unread messages can be annoying if you have threaded view 
enabled, say
 
@@ -643,12 +633,12 @@ in @file{~/.gnus.el} to load enough old articles to 
prevent teared threads, repl
 all articles (Warning: Both settings enlarge the amount of data which is
 fetched when you enter a group and slow down the process of entering a group).
 
-You can say @samp{/o N} in the summary buffer to load the last N
+You can say @kbd{/o N} in the summary buffer to load the last N
 messages.
 
 If you don't want all old messages, but the parent of the message you're just 
reading,
-you can say @samp{^}, if you want to retrieve the whole thread
-the message you're just reading belongs to, @samp{A T} is your friend.
+you can say @kbd{^}, if you want to retrieve the whole thread
+the message you're just reading belongs to, @kbd{A T} is your friend.
 
 @node FAQ 4-2
 @subsubheading Question 4.2
@@ -659,10 +649,10 @@ enter a group, even when it's read?
 @subsubheading Answer
 
 You can tick important messages.  To do this hit
-@samp{u} while point is in summary buffer
+@kbd{u} while point is in summary buffer
 over the message.  When you want to remove the mark, hit
-either @samp{d} (this deletes the tick
-mark and set's unread mark) or @samp{M c}
+either @kbd{d} (this deletes the tick
+mark and set's unread mark) or @kbd{M c}
 (which deletes all marks for the message).
 
 @node FAQ 4-3
@@ -672,10 +662,7 @@ How to view the headers of a message?
 
 @subsubheading Answer
 
-Say @samp{t}
-to show all headers, one more
-@samp{t}
-hides them again.
+Say @kbd{t} to show all headers, one more @kbd{t} hides them again.
 
 @node FAQ 4-4
 @subsubheading Question 4.4
@@ -684,11 +671,8 @@ How to view the raw unformatted message?
 
 @subsubheading Answer
 
-Say
-@samp{C-u g}
-to show the raw message
-@samp{g}
-returns to normal view.
+Type @kbd{C-u g} to show the raw message @kbd{g} returns to normal
+view.
 
 @node FAQ 4-5
 @subsubheading Question 4.5
@@ -765,11 +749,11 @@ more readable?
 Gnus offers you several functions to ``wash'' incoming mail, you can
 find them if you browse through the menu, item
 Article->Washing.  The most interesting ones are probably ``Wrap
-long lines'' (@samp{W w}), ``Decode ROT13''
-(@samp{W r}) and ``Outlook Deuglify'' which repairs
+long lines'' (@kbd{W w}), ``Decode ROT13''
+(@kbd{W r}) and ``Outlook Deuglify'' which repairs
 the dumb quoting used by many users of Microsoft products
-(@samp{W Y f} gives you full deuglify.
-See @samp{W Y C-h} or have a look at the menus for
+(@kbd{W Y f} gives you full deuglify.
+See @kbd{W Y C-h} or have a look at the menus for
 other deuglifications).
 
 @node FAQ 4-9
@@ -792,21 +776,21 @@ the scoring-value to messages.  The first and easiest way 
is to set
 up rules based on the article you are just reading.  Say you're
 reading a message by a guy who always writes nonsense and you want
 to ignore his messages in the future.  Hit
-@samp{L}, to set up a rule which lowers the score.
+@kbd{L}, to set up a rule which lowers the score.
 Now Gnus asks you which the criteria for lowering the Score shall
-be.  Hit @samp{?} twice to see all possibilities,
-we want @samp{a} which means the author (the from
+be.  Hit @kbd{?} twice to see all possibilities,
+we want @kbd{a} which means the author (the from
 header).  Now Gnus wants to know which kind of matching we want.
-Hit either @samp{e} for an exact match or
-@samp{s} for substring-match and delete afterwards
+Hit either @kbd{e} for an exact match or
+@kbd{s} for substring-match and delete afterwards
 everything but the name to score down all authors with the given
 name no matter which email address is used.  Now you need to tell
 Gnus when to apply the rule and how long it should last, hit
-@samp{p} to apply the rule now and let it last
+@kbd{p} to apply the rule now and let it last
 forever.  If you want to raise the score instead of lowering it say
-@samp{I} instead of @samp{L}.
+@kbd{I} instead of @kbd{L}.
 
-You can also set up rules by hand.  To do this say @samp{V
+You can also set up rules by hand.  To do this say @kbd{V
 f} in summary buffer.  Then you are asked for the name
 of the score file, it's name.of.group.SCORE for rules valid in
 only one group or all.Score for rules valid in all groups.  See the
@@ -851,7 +835,7 @@ set other variables specific for some groups?
 @subsubheading Answer
 
 While in group buffer move point over the group and hit
-@samp{G c}, this opens a buffer where you
+@kbd{G c}, this opens a buffer where you
 can set options for the group.  At the bottom of the buffer
 you'll find an item that allows you to set variables
 locally for the group.  To disable threading enter
@@ -889,10 +873,10 @@ back ends.  Gnus thinks ``highest-article-number @minus{}
 lowest-article-number = total-number-of-articles''.  This
 works OK for Usenet groups, but if you delete and move
 many messages in mail groups, this fails.  To cure the
-symptom, enter the group via @samp{C-u @key{RET}}
+symptom, enter the group via @kbd{C-u @key{RET}}
 (this makes Gnus get all messages), then
-hit @samp{M P b} to mark all messages and
-then say @samp{B m name.of.group} to move
+hit @kbd{M P b} to mark all messages and
+then say @kbd{B m name.of.group} to move
 all messages to the group they have been in before, they
 get new message numbers in this process and the count is
 right again (until you delete and move your mail to other
@@ -1110,28 +1094,20 @@ What are the basic commands I need to know for sending 
mail and postings?
 
 @subsubheading Answer
 
-To start composing a new mail hit @samp{m}
-either in Group or Summary buffer, for a posting, it's
-either @samp{a} in Group buffer and
-filling the Newsgroups header manually
-or @samp{a} in the Summary buffer of the
-group where the posting shall be send to.  Replying by mail
-is
-@samp{r} if you don't want to cite the
-author, or import the cited text manually and
-@samp{R} to cite the text of the original
-message.  For a follow up to a newsgroup, it's
-@samp{f} and @samp{F}
-(analogously to @samp{r} and
-@samp{R}).
-
-Enter new headers above the line saying "--text follows
-this line--", enter the text below the line.  When ready
-hit @samp{C-c C-c}, to send the message,
-if you want to finish it later hit @samp{C-c
-C-d} to save it in the drafts group, where you
-can start editing it again by saying @samp{D
-e}.
+To start composing a new mail hit @kbd{m} either in Group or Summary
+buffer, for a posting, it's either @kbd{a} in Group buffer and filling
+the Newsgroups header manually or @kbd{a} in the Summary buffer of the
+group where the posting shall be send to.  Replying by mail is @kbd{r}
+if you don't want to cite the author, or import the cited text
+manually and @kbd{R} to cite the text of the original message.  For a
+follow up to a newsgroup, it's @kbd{f} and @kbd{F} (analogously to
+@kbd{r} and @kbd{R}).
+
+Enter new headers above the line saying "--text follows this line--",
+enter the text below the line.  When ready hit @kbd{C-c C-c}, to send
+the message, if you want to finish it later hit @kbd{C-c C-d} to save
+it in the drafts group, where you can start editing it again by saying
+@kbd{D e}.
 
 @node FAQ 5-2
 @subsubheading Question 5.2
@@ -1156,8 +1132,7 @@ For other versions of Gnus, say
 
 in @file{~/.gnus.el}.
 
-You can reformat a paragraph by hitting @samp{M-q}
-(as usual).
+You can reformat a paragraph by hitting @kbd{M-q} (as usual).
 
 @node FAQ 5-3
 @subsubheading Question 5.3
@@ -1358,16 +1333,13 @@ place them in ~/.emacs:
 @end example
 @noindent
 
-Now you should be ready to go.  Say @samp{M-x bbdb @key{RET}
-@key{RET}} to open a bbdb buffer showing all
-entries.  Say @samp{c} to create a new
-entry, @samp{b} to search your BBDB and
-@samp{C-o} to add a new field to an
-entry.  If you want to add a sender to the BBDB you can
-also just hit @kbd{:} on the posting in the summary buffer and
-you are done.  When you now compose a new mail,
-hit @samp{TAB} to cycle through know
-recipients.
+Now you should be ready to go.  Say @kbd{M-x bbdb @key{RET} @key{RET}}
+to open a bbdb buffer showing all entries.  Say @kbd{c} to create a
+new entry, @kbd{b} to search your BBDB and @kbd{C-o} to add a new
+field to an entry.  If you want to add a sender to the BBDB you can
+also just hit @kbd{:} on the posting in the summary buffer and you are
+done.  When you now compose a new mail, hit @kbd{TAB} to cycle through
+know recipients.
 
 @node FAQ 5-8
 @subsubheading Question 5.8
@@ -1576,17 +1548,17 @@ world, you may find tools at
 
 Now you've got to import this mbox file into Gnus.  To do
 this, create a nndoc group based on the mbox file by
-saying @samp{G f /path/file.mbox @key{RET}} in
+saying @kbd{G f /path/file.mbox @key{RET}} in
 Group buffer.  You now have read-only access to your
 mail.  If you want to import the messages to your normal
 Gnus mail groups hierarchy, enter the nndoc group you've
-just created by saying @samp{C-u @key{RET}}
+just created by saying @kbd{C-u @key{RET}}
 (thus making sure all messages are retrieved), mark all
-messages by saying @samp{M P b} and
+messages by saying @kbd{M P b} and
 either copy them to the desired group by saying
-@samp{B c name.of.group @key{RET}} or send them
+@kbd{B c name.of.group @key{RET}} or send them
 through nnmail-split-methods (respool them) by saying
-@samp{B r}.
+@kbd{B r}.
 
 @node FAQ 6-2
 @subsubheading Question 6.2
@@ -1598,7 +1570,7 @@ How to archive interesting messages?
 If you stumble across an interesting message, say in
 gnu.emacs.gnus and want to archive it there are several
 solutions.  The first and easiest is to save it to a file
-by saying @samp{O f}.  However, wouldn't
+by saying @kbd{O f}.  However, wouldn't
 it be much more convenient to have more direct access to
 the archived message from Gnus? If you say yes, put this
 snippet by Frank Haun <pille3003@@fhaun.de> in
@@ -1621,10 +1593,9 @@ more then one article."
 @end example
 @noindent
 
-You can now say @samp{M-x
-my-archive-article} in summary buffer to
-archive the article under the cursor in a nnml
-group. (Change nnml to your preferred back end.)
+You can now say @kbd{M-x my-archive-article} in summary buffer to
+archive the article under the cursor in a nnml group. (Change nnml to
+your preferred back end.)
 
 Of course you can also make sure the cache is enabled by saying
 
@@ -1644,26 +1615,20 @@ How to search for a specific message?
 
 @subsubheading Answer
 
-There are several ways for this, too.  For a posting from
-a Usenet group the easiest solution is probably to ask
-@uref{https://groups.google.com, groups.google.com},
-if you found the posting there, tell Google to display
-the raw message, look for the message-id, and say
-@samp{M-^ the@@message.id @key{RET}} in a
-summary buffer.
-There's a Gnus interface for
-groups.google.com which you can call with
-@samp{G W}) in group buffer.
-
-Another idea which works for both mail and news groups
-is to enter the group where the message you are
-searching is and use the standard Emacs search
-@samp{C-s}, it's smart enough to look at
-articles in collapsed threads, too.  If you want to
-search bodies, too try @samp{M-s}
-instead.  Further on there are the
-gnus-summary-limit-to-foo functions, which can help you,
-too.
+There are several ways for this, too.  For a posting from a Usenet
+group the easiest solution is probably to ask
+@uref{https://groups.google.com, groups.google.com}, if you found the
+posting there, tell Google to display the raw message, look for the
+message-id, and say @kbd{M-^ the@@message.id @key{RET}} in a summary
+buffer.  There's a Gnus interface for @samp{groups.google.com} which
+you can call with @kbd{G W}) in group buffer.
+
+Another idea which works for both mail and news groups is to enter the
+group where the message you are searching is and use the standard
+Emacs search @kbd{C-s}, it's smart enough to look at articles in
+collapsed threads, too.  If you want to search bodies, too try
+@kbd{M-s} instead.  Further on there are the gnus-summary-limit-to-foo
+functions, which can help you, too.
 
 @node FAQ 6-4
 @subsubheading Question 6.4
@@ -1673,18 +1638,18 @@ How to get rid of old unwanted mail?
 @subsubheading Answer
 
 You can of course just mark the mail you don't need
-anymore by saying @samp{#} with point
-over the mail and then say @samp{B @key{DEL}}
+anymore by saying @kbd{#} with point
+over the mail and then say @kbd{B @key{DEL}}
 to get rid of them forever.  You could also instead of
 actually deleting them, send them to a junk-group by
-saying @samp{B m nnml:trash-bin} which
+saying @kbd{B m nnml:trash-bin} which
 you clear from time to time, but both are not the intended
 way in Gnus.
 
 In Gnus, we let mail expire like news expires on a news
 server.  That means you tell Gnus the message is
 expirable (you tell Gnus "I don't need this mail
-anymore") by saying @samp{E} with point
+anymore") by saying @kbd{E} with point
 over the mail in summary buffer.  Now when you leave the
 group, Gnus looks at all messages which you marked as
 expirable before and if they are old enough (default is
@@ -1703,13 +1668,13 @@ mailing lists where there's an online archive), you've
 got two choices: auto-expire and
 total-expire.  Auto-expire means, that every article
 which has no marks set and is selected for reading is
-marked as expirable, Gnus hits @samp{E}
+marked as expirable, Gnus hits @kbd{E}
 for you every time you read a message.  Total-expire
 follows a slightly different approach, here all article
 where the read mark is set are expirable.
 
 To activate auto-expire, include auto-expire in the
-Group parameters for the group. (Hit @samp{G
+Group parameters for the group. (Hit @kbd{G
 c} in summary buffer with point over the
 group to change group parameters).  For total-expire add
 total-expire to the group-parameters.
@@ -1721,10 +1686,10 @@ you should use total-expire.
 
 If you want a message to be excluded from expiration in
 a group where total or auto expire is active, set either
-tick (hit @samp{u}) or dormant mark (hit
-@samp{u}), when you use auto-expire, you
+tick (hit @kbd{u}) or dormant mark (hit
+@kbd{u}), when you use auto-expire, you
 can also set the read mark (hit
-@samp{d}).
+@kbd{d}).
 
 @node FAQ 6-6
 @subsubheading Question 6.6
@@ -1817,12 +1782,12 @@ newsreaders like Forte Agent.  It is enabled by default.
 
 You've got to select the servers whose groups can be
 stored locally.  To do this, open the server buffer
-(that is press @samp{^} while in the
+(that is press @kbd{^} while in the
 group buffer).  Now select a server by moving point to
 the line naming that server.  Finally, agentize the
-server by typing @samp{J a}.  If you
+server by typing @kbd{J a}.  If you
 make a mistake, or change your mind, you can undo this
-action by typing @samp{J r}.  When
+action by typing @kbd{J r}.  When
 you're done, type 'q' to return to the group buffer.
 Now the next time you enter a group on an agentized
 server, the headers will be stored on disk and read from
@@ -1838,7 +1803,7 @@ I want to store article bodies on disk, too.  How to do 
it?
 You can tell the agent to automatically fetch the bodies
 of articles which fulfill certain predicates, this is
 done in a special buffer which can be reached by
-saying @samp{J c} in group
+saying @kbd{J c} in group
 buffer.  Please refer to the documentation for
 information which predicates are possible and how
 exactly to do it.
@@ -1847,12 +1812,12 @@ Further on you can tell the agent manually which
 articles to store on disk.  There are two ways to do
 this: Number one: In the summary buffer, process mark a
 set of articles that shall be stored in the agent by
-saying @samp{#} with point over the
-article and then type @samp{J s}.  The
+saying @kbd{#} with point over the
+article and then type @kbd{J s}.  The
 other possibility is to set, again in the summary
 buffer, downloadable (%) marks for the articles you
-want by typing @samp{@@} with point over
-the article and then typing @samp{J u}.
+want by typing @kbd{@@} with point over
+the article and then typing @kbd{J u}.
 What's the difference? Well, process marks are erased as
 soon as you exit the summary buffer while downloadable
 marks are permanent.  You can actually set downloadable
@@ -1874,10 +1839,10 @@ while I'm offline?
 All you've got to do is to tell Gnus when you are online
 (plugged) and when you are offline (unplugged), the rest
 works automatically.  You can toggle plugged/unplugged
-state by saying @samp{J j} in group
-buffer.  To start Gnus unplugged say @samp{M-x
+state by saying @kbd{J j} in group
+buffer.  To start Gnus unplugged say @kbd{M-x
 gnus-unplugged} instead of
-@samp{M-x gnus}.  Note that for this to
+@kbd{M-x gnus}.  Note that for this to
 work, the agent must be active.
 
 @node FAQ 8 - Getting help
@@ -1901,14 +1866,14 @@ How to find information and help inside Emacs?
 @subsubheading Answer
 
 The first stop should be the Gnus manual (Say
-@samp{C-h i d m Gnus @key{RET}} to start the
+@kbd{C-h i d m Gnus @key{RET}} to start the
 Gnus manual, then walk through the menus or do a
-full-text search with @samp{s}).  Then
+full-text search with @kbd{s}).  Then
 there are the general Emacs help commands starting with
-C-h, type @samp{C-h ? ?} to get a list
+@kbd{C-h}, type @kbd{C-h ? ?} to get a list
 of all available help commands and their meaning.  Finally
-@samp{M-x apropos-command} lets you
-search through all available functions and @samp{M-x
+@kbd{M-x apropos-command} lets you
+search through all available functions and @kbd{M-x
 apropos} searches the bound variables.
 
 @node FAQ 8-2
@@ -1963,7 +1928,7 @@ Where to report bugs?
 
 @subsubheading Answer
 
-Say @samp{M-x gnus-bug}, this will start
+Say @kbd{M-x gnus-bug}, this will start
 a message to the
 @email{bugs@@gnus.org, gnus bug mailing list}
 including information about your environment which make
@@ -1998,7 +1963,7 @@ The reason for this could be the way Gnus reads its
 active file, see the node "The Active File" in the Gnus
 manual for things you might try to speed the process up.
 An other idea would be to byte compile your @file{~/.gnus.el} (say
-@samp{M-x byte-compile-file @key{RET} ~/.gnus.el
+@kbd{M-x byte-compile-file @key{RET} ~/.gnus.el
 @key{RET}} to do it).  Finally, if you have require
 statements in your .gnus, you could replace them with
 @code{with-eval-after-load}, which loads the stuff not at startup
diff --git a/doc/misc/gnus.texi b/doc/misc/gnus.texi
index c4705928d3..cb0d9c50da 100644
--- a/doc/misc/gnus.texi
+++ b/doc/misc/gnus.texi
@@ -402,21 +402,21 @@ This manual corresponds to Gnus v5.13
 @end iftex
 
 @menu
-* Don't Panic::              Your first 20 minutes with Gnus.
-* Starting Up::              Finding news can be a pain.
-* Group Buffer::             Selecting, subscribing and killing groups.
-* Summary Buffer::           Reading, saving and posting articles.
-* Article Buffer::           Displaying and handling articles.
-* Composing Messages::       Information on sending mail and news.
-* Select Methods::           Gnus reads all messages from various select 
methods.
-* Scoring::                  Assigning values to articles.
-* Searching::                Mail and News search engines.
-* Various::                  General purpose settings.
-* The End::                  Farewell and goodbye.
-* Appendices::               Terminology, Emacs intro, @acronym{FAQ}, History, 
Internals.
-* GNU Free Documentation License:: The license for this documentation.
-* Index::                    Variable, function and concept index.
-* Key Index::                Key Index.
+* Don't Panic::                 Your first 20 minutes with Gnus.
+* Starting Up::                 Finding news can be a pain.
+* Group Buffer::                Selecting, subscribing and killing groups.
+* Summary Buffer::              Reading, saving and posting articles.
+* Article Buffer::              Displaying and handling articles.
+* Composing Messages::          Information on sending mail and news.
+* Select Methods::              Gnus reads all messages from various select 
methods.
+* Scoring::                     Assigning values to articles.
+* Searching::                   Mail and News search engines.
+* Various::                     General purpose settings.
+* The End::                     Farewell and goodbye.
+* Appendices::                  Terminology, Emacs intro, @acronym{FAQ}, 
History, Internals.
+* GNU Free Documentation License::  The license for this documentation.
+* Index::                       Variable, function and concept index.
+* Key Index::                   Key Index.
 
 @c Doesn't work right in html.
 @c FIXME Do this in a more standard way.
@@ -589,14 +589,15 @@ Decoding Variables
 Article Treatment
 
 * Article Highlighting::        You want to make the article look like fruit 
salad.
-* Article Fontisizing::         Making emphasized text look nice.
+* Article Fontifying::          Making emphasized text look nice.
 * Article Hiding::              You also want to make certain info go away.
 * Article Washing::             Lots of way-neat functions to make life better.
 * Article Header::              Doing various header transformations.
 * Article Buttons::             Click on URLs, Message-IDs, addresses and the 
like.
 * Article Button Levels::       Controlling appearance of buttons.
 * Article Date::                Grumble, UT!
-* Article Display::             Display various stuff---X-Face, Picons, 
Smileys, Gravatars
+* Article Display::             Display various stuff:
+                                X-Face, Picons, Gravatars, Smileys.
 * Article Signature::           What is a signature?
 * Article Miscellanea::         Various other stuff.
 
@@ -641,7 +642,7 @@ Select Methods
 * Getting Mail::                Reading your personal mail with Gnus.
 * Browsing the Web::            Getting messages from a plethora of Web 
sources.
 * Other Sources::               Reading directories, files.
-* Virtual Groups::              Combining articles from multiple sources.
+* Virtual Groups::              Combining articles and groups together.
 * Email Based Diary::           Using mails to manage diary events in Gnus.
 * Gnus Unplugged::              Reading news and mail offline.
 
@@ -666,6 +667,13 @@ Getting News
 * Indirect Functions::          Connecting indirectly to the server.
 * Common Variables::            Understood by several connection functions.
 
+Using IMAP
+
+* Connecting to an IMAP Server::  Getting started with @acronym{IMAP}.
+* Customizing the IMAP Connection::  Variables for @acronym{IMAP} connection.
+* Client-Side IMAP Splitting::  Put mail in the correct mail box.
+* Support for IMAP Extensions::  Getting extensions and labels from servers.
+
 Getting Mail
 
 * Mail in a Newsreader::        Important introductory notes.
@@ -685,6 +693,7 @@ Getting Mail
 Mail Sources
 
 * Mail Source Specifiers::      How to specify what a mail source is.
+* Mail Source Functions::
 * Mail Source Customization::   Some variables that influence things.
 * Fetching Mail::               Using the mail source specifiers.
 
@@ -695,6 +704,10 @@ Choosing a Mail Back End
 * Mail Spool::                  Store your mail in a private spool?
 * MH Spool::                    An mhspool-like back end.
 * Maildir::                     Another one-file-per-message format.
+* nnmaildir Group Parameters::
+* Article Identification::
+* NOV Data::
+* Article Marks::
 * Mail Folders::                Having one file for each group.
 * Comparing Mail Back Ends::    An in-depth looks at pros and cons.
 
@@ -734,10 +747,10 @@ The NNDiary Back End
 
 The Gnus Diary Library
 
-* Diary Summary Line Format::           A nicer summary buffer line format.
-* Diary Articles Sorting::              A nicer way to sort messages.
-* Diary Headers Generation::            Not doing it manually.
-* Diary Group Parameters::              Not handling them manually.
+* Diary Summary Line Format::   A nicer summary buffer line format.
+* Diary Articles Sorting::      A nicer way to sort messages.
+* Diary Headers Generation::    Not doing it manually.
+* Diary Group Parameters::      Not handling them manually.
 
 Gnus Unplugged
 
@@ -796,9 +809,22 @@ Advanced Scoring
 Searching
 
 * Search Engines::              Selecting and configuring search engines.
-* Creating Search Groups::      Creating search groups.
+* Creating Search Groups::      How and where.
 * Search Queries::              Gnus' built-in search syntax.
 * nnmairix::                    Searching with Mairix.
+* nnir::
+
+nnmairix
+
+* About mairix::                About the mairix mail search engine
+* nnmairix requirements::       What you will need for using nnmairix
+* What nnmairix does::          What does nnmairix actually do?
+* Setting up mairix::           Set up your mairix installation
+* Configuring nnmairix::        Set up the nnmairix back end
+* nnmairix keyboard shortcuts::  List of available keyboard shortcuts
+* Propagating marks::           How to propagate marks from nnmairix groups
+* nnmairix tips and tricks::    Some tips, tricks and examples
+* nnmairix caveats::            Some more stuff you might want to know
 
 Various
 
@@ -839,8 +865,7 @@ Image Enhancements
 
 * X-Face::                      Display a funky, teensy black-and-white image.
 * Face::                        Display a funkier, teensier colored image.
-* Smileys::                     Show all those happy faces the way they were
-                                  meant to be shown.
+* Smileys::                     Show all those happy faces the way they were 
meant to be shown.
 * Picons::                      How to display pictures of what you're reading.
 * Gravatars::                   Display the avatar of people you read.
 
@@ -862,12 +887,39 @@ Spam Package
 * Extending the Spam package::
 * Spam Statistics Package::
 
+Spam Back Ends
+
+* Blacklists and Whitelists::
+* BBDB Whitelists::
+* Gmane Spam Reporting::
+* Anti-spam Hashcash Payments::
+* Blackholes::
+* Regular Expressions Header Matching::
+* Bogofilter::
+* SpamAssassin back end::
+* ifile spam filtering::
+* Spam Statistics Filtering::
+* SpamOracle::
+
 Spam Statistics Package
 
 * Creating a spam-stat dictionary::
 * Splitting mail using spam-stat::
 * Low-level interface to the spam-stat dictionary::
 
+The Gnus Registry
+
+* Gnus Registry Setup::
+* Registry Article Refer Method::
+* Fancy splitting to parent::
+* Store custom flags and keywords::
+* Store arbitrary data::
+
+The Gnus Cloud
+
+* Gnus Cloud Setup::
+* Gnus Cloud Usage::
+
 Appendices
 
 * History::                     How Gnus got where it is today.
@@ -895,7 +947,7 @@ New Features
 * Quassia Gnus::                Two times two is four, or Gnus 5.6/5.7.
 * Pterodactyl Gnus::            Pentad also starts with P, AKA Gnus 5.8/5.9.
 * Oort Gnus::                   It's big.  It's far out.  Gnus 5.10/5.11.
-* No Gnus::                     Very punny.  Gnus 5.12/5.13
+* No Gnus::                     Very punny.  Gnus 5.12/5.13.
 * Ma Gnus::                     Celebrating 25 years of Gnus.
 
 Customization
@@ -1002,7 +1054,7 @@ The fundamental building blocks of Gnus are @dfn{servers},
 Each server maintains a list of groups, and those groups contain
 articles.  Because Gnus presents a unified interface to a wide variety
 of servers, the vocabulary doesn't always quite line up (@pxref{FAQ
-- Glossary}, for a more complete glossary).  Thus a local maildir is
+- Glossary}, for a more complete glossary).  Thus a local Maildir is
 referred to as a ``server'' (@pxref{Finding the News}) the same as a
 Usenet or IMAP server is; ``groups'' (@pxref{Group Buffer}) might mean
 an NNTP group, IMAP folder, or local mail directory; and an
@@ -1039,7 +1091,7 @@ the News}, for details.
 
 New mail has to come from somewhere.  Some servers, such as NNTP or
 IMAP, are themselves responsible for fetching newly-arrived articles.
-Others, such as maildir or mbox servers, only store articles and don't
+Others, such as Maildir or mbox servers, only store articles and don't
 fetch them from anywhere.
 
 In the latter case, Gnus provides for @code{mail sources}: places
@@ -1099,15 +1151,15 @@ If you puzzle at any terms used in this manual, please 
refer to the
 terminology section (@pxref{Terminology}).
 
 @menu
-* Finding the News::      Choosing a method for getting news.
-* The Server is Down::    How can I read my mail then?
-* Child Gnusae::          You can have more than one Gnus active at a time.
-* New Groups::            What is Gnus supposed to do with new groups?
-* Changing Servers::      You may want to move from one server to another.
-* Startup Files::         Those pesky startup files---@file{.newsrc}.
-* Auto Save::             Recovering from a crash.
-* The Active File::       Reading the active file over a slow line Takes Time.
-* Startup Variables::     Other variables you might change.
+* Finding the News::            Choosing a method for getting news.
+* The Server is Down::          How can I read my mail then?
+* Child Gnusae::                You can have more than one Gnus active at a 
time.
+* New Groups::                  What is Gnus supposed to do with new groups?
+* Changing Servers::            You may want to move from one server to 
another.
+* Startup Files::               Those pesky startup files---@file{.newsrc}.
+* Auto Save::                   Recovering from a crash.
+* The Active File::             Reading the active file over a slow line Takes 
Time.
+* Startup Variables::           Other variables you might change.
 @end menu
 
 
@@ -2907,8 +2959,6 @@ sending the message if @code{gnus-add-to-list} is set to 
@code{t}.
 If this variable is set, @code{gnus-mailing-list-mode} is turned on when
 entering summary buffer.
 
-See also @code{gnus-parameter-to-list-alist}.
-
 @anchor{subscribed}
 @item subscribed
 @cindex subscribed
@@ -8718,7 +8768,7 @@ these articles easier.
 
 @menu
 * Article Highlighting::        You want to make the article look like fruit 
salad.
-* Article Fontisizing::         Making emphasized text look nice.
+* Article Fontifying::          Making emphasized text look nice.
 * Article Hiding::              You also want to make certain info go away.
 * Article Washing::             Lots of way-neat functions to make life better.
 * Article Header::              Doing various header transformations.
@@ -8840,8 +8890,8 @@ default.
 @xref{Customizing Articles}, for how to highlight articles automatically.
 
 
-@node Article Fontisizing
-@subsection Article Fontisizing
+@node Article Fontifying
+@subsection Article Fontifying
 @cindex emphasis
 @cindex article emphasis
 
@@ -18399,10 +18449,10 @@ useful things for you.
 
 
 @menu
-* Diary Summary Line Format::           A nicer summary buffer line format.
-* Diary Articles Sorting::              A nicer way to sort messages.
-* Diary Headers Generation::            Not doing it manually.
-* Diary Group Parameters::              Not handling them manually.
+* Diary Summary Line Format::   A nicer summary buffer line format.
+* Diary Articles Sorting::      A nicer way to sort messages.
+* Diary Headers Generation::    Not doing it manually.
+* Diary Group Parameters::      Not handling them manually.
 @end menu
 
 @node Diary Summary Line Format
@@ -21579,10 +21629,11 @@ relative date parsing and tie-ins into other Emacs 
packages.  For
 details on Gnus' query language, see @ref{Search Queries}.
 
 @menu
-* Search Engines::             Selecting and configuring search engines.
-* Creating Search Groups::     How and where.
-* Search Queries::             Gnus' built-in search syntax.
-* nnmairix::                   Searching with Mairix.
+* Search Engines::              Selecting and configuring search engines.
+* Creating Search Groups::      How and where.
+* Search Queries::              Gnus' built-in search syntax.
+* nnmairix::                    Searching with Mairix.
+* nnir::
 @end menu
 
 @node Search Engines
@@ -21878,7 +21929,7 @@ bound to mairix searches and are automatically updated.
 * What nnmairix does::          What does nnmairix actually do?
 * Setting up mairix::           Set up your mairix installation
 * Configuring nnmairix::        Set up the nnmairix back end
-* nnmairix keyboard shortcuts:: List of available keyboard shortcuts
+* nnmairix keyboard shortcuts::  List of available keyboard shortcuts
 * Propagating marks::           How to propagate marks from nnmairix groups
 * nnmairix tips and tricks::    Some tips, tricks and examples
 * nnmairix caveats::            Some more stuff you might want to know
diff --git a/doc/misc/idlwave.texi b/doc/misc/idlwave.texi
index 4bdbd5c219..0c59fdf738 100644
--- a/doc/misc/idlwave.texi
+++ b/doc/misc/idlwave.texi
@@ -3799,7 +3799,7 @@ configuration is not possible, but choices abound.  The 
default
 @code{idlwave-help-browser-function} inherits the browser configured
 in @code{browse-url-browser-function}.
 
-Note that the HTML files decompiled from the help sources contain
+Note that the HTML files recompiled from the help sources contain
 specific references to the @samp{Symbol} font, which by default is not
 permitted in normal encodings (it's invalid, technically).  Though it
 only impacts a few symbols, you can trick Mozilla-based browsers into
diff --git a/doc/misc/mairix-el.texi b/doc/misc/mairix-el.texi
index 3632c64bd4..28b86c43ac 100644
--- a/doc/misc/mairix-el.texi
+++ b/doc/misc/mairix-el.texi
@@ -70,7 +70,7 @@ database.
 Mairix is a tool for indexing and searching words in locally stored
 mail.  It was written by Richard Curnow and is licensed under the
 GPL@.  Mairix comes with most popular GNU/Linux distributions, but it also
-runs under Windows (with cygwin), macOS and Solaris.  The website can
+runs under Windows (with Cygwin), macOS and Solaris.  The website can
 be found at
 @uref{http://www.rpcurnow.force9.co.uk/mairix/index.html}
 
diff --git a/doc/misc/message.texi b/doc/misc/message.texi
index 6a6beb7a1f..fb6e97f0d3 100644
--- a/doc/misc/message.texi
+++ b/doc/misc/message.texi
@@ -935,7 +935,7 @@ Manual}).
 @section IDNA
 @cindex IDNA
 @cindex internationalized domain names
-@cindex non-ascii domain names
+@cindex non-ASCII domain names
 
 @acronym{IDNA} is a standard way to encode non-@acronym{ASCII} domain
 names into a readable @acronym{ASCII} string.  The details can be
@@ -1005,7 +1005,7 @@ that they can deposit messages and lock the door, while 
your private
 key corresponds to the opening combination for the safe.)
 
 Thus, you need to perform the following steps for e-mail encryption,
-typically outside Emacs.  See, for example, the
+typically outside Emacs.  See, for example,
 @uref{https://www.gnupg.org/gph/en/manual.html, The GNU Privacy
 Handbook} for details covering the standard @acronym{OpenPGP} with
 @acronym{GnuPG}.
diff --git a/doc/misc/modus-themes.org b/doc/misc/modus-themes.org
index 56ba5fd348..db92dfb481 100644
--- a/doc/misc/modus-themes.org
+++ b/doc/misc/modus-themes.org
@@ -3649,7 +3649,7 @@ specification of that variable looks like this:
 
 With the exception of ~org-verbatim~ and ~org-code~ faces, everything else
 uses the corresponding type of emphasis: a bold typographic weight, or
-italicised, underlined, and struck through text.
+italicized, underlined, and struck through text.
 
 The best way for users to add some extra attributes, such as a
 foreground color, is to define their own faces and assign them to the
diff --git a/doc/misc/org.org b/doc/misc/org.org
index ae3ca0b64f..6ad8c462cc 100644
--- a/doc/misc/org.org
+++ b/doc/misc/org.org
@@ -16569,7 +16569,7 @@ identifying a reference in the bibliography.
 
 - Each key can be qualified by a /prefix/ (e.g.\nbsp{}"see ") and/or
   a /suffix/ (e.g.\nbsp{}"p.\nbsp{}123"), giving information useful or 
necessary
-  fo the comprehension of the citation but not included in the
+  for the comprehension of the citation but not included in the
   reference.
 
 - A single citation can cite more than one reference ; the keys are
diff --git a/doc/misc/reftex.texi b/doc/misc/reftex.texi
index e85f096f99..9c543a4c0f 100644
--- a/doc/misc/reftex.texi
+++ b/doc/misc/reftex.texi
@@ -5292,7 +5292,7 @@ Some improvements for XEmacs compatibility.
 @itemize @bullet
 @item
 Fixed bug with @samp{%F} in a label prefix.  Added new escapes
-@samp{%m} and @samp{%M} for mater file name and master directory.
+@samp{%m} and @samp{%M} for master file name and master directory.
 @end itemize
 
 @noindent @b{Version 4.24}
diff --git a/doc/misc/ses.texi b/doc/misc/ses.texi
index 6d0415cdbb..e3ef11ebc0 100644
--- a/doc/misc/ses.texi
+++ b/doc/misc/ses.texi
@@ -750,13 +750,13 @@ when you pass a cell name to the @command{ses-jump} 
command (@kbd{j}),
 it changes the entered cell name to that where to jump. The default
 setting @code{upcase} allows you to enter the cell name in low
 case. Another use of @code{ses-jump-cell-name-function} could be some
-internationalisation to convert non latin characters into latin
+internationalization to convert non latin characters into latin
 equivalents to name the cell. Instead of a cell name, the function may
 return cell coordinates in the form of a cons, for instance @code{(0
 . 0)} for cell @code{A1}, @code{(1 . 0)} for cell @code{A2}, etc.
 
 @vindex ses-jump-prefix-function
-@code{ses-jump-prefix-function} is a customisable variable by default
+@code{ses-jump-prefix-function} is a customizable variable by default
 set to the @code{ses-jump-prefix} function. This function is called
 when you give a prefix argument to the @command{ses-jump} command
 (@kbd{j}). It returns a cell name or cell coordinates corresponding to
diff --git a/doc/misc/srecode.texi b/doc/misc/srecode.texi
index a0cbf7e33f..d7356fef58 100644
--- a/doc/misc/srecode.texi
+++ b/doc/misc/srecode.texi
@@ -1595,7 +1595,7 @@ from a user perspective during basic interactive 
insertion via
 
 NOTES ON THIS CHAPTER:
 
-These conventions are being worked on.  Check w/ CEDET-DEVEL mailing
+These conventions are being worked on.  Check with CEDET-DEVEL mailing
 list if you want to support a language, or write an application and
 provide your opinions on this topic.  Any help is appreciated.
 
diff --git a/doc/misc/tramp.texi b/doc/misc/tramp.texi
index 99a268367b..403c0daa67 100644
--- a/doc/misc/tramp.texi
+++ b/doc/misc/tramp.texi
@@ -4225,7 +4225,7 @@ non-@code{nil}, or invoke that command with a negative 
argument like
 @value{tramp}'s implementation of @code{make-process} and
 @code{start-file-process} requires a serious overhead for
 initialization, every process invocation.  This is needed for handling
-interactive dialogues when connecting the remote host (like providing
+interactive dialogs when connecting the remote host (like providing
 a password), and initial environment setup.
 
 Sometimes, this is not needed.  Instead of starting a remote shell and
@@ -4253,8 +4253,9 @@ Furthermore, this approach has the following limitations:
 
 @itemize
 @item
-It works only for connection methods defined in @file{tramp-adb.el},
-@file{tramp-sh.el} and @file{tramp-sshfs.el}.
+It works only for some connection methods defined in
+@file{tramp-adb.el}, @file{tramp-container.el}, @file{tramp-sh.el} and
+@file{tramp-sshfs.el}.
 
 @item
 It does not support interactive user authentication.  With
diff --git a/doc/misc/vip.texi b/doc/misc/vip.texi
index df65d70cb1..8e192d7e40 100644
--- a/doc/misc/vip.texi
+++ b/doc/misc/vip.texi
@@ -1203,11 +1203,11 @@ Move point backward to the character @var{ch} on the 
line.  Signal error if
 @var{ch} could not be found (@code{vip-find-char-backward}).
 @item t @var{ch}
 @kindex 164 t @r{(}@code{vip-goto-char-forward}@r{)}
-Move point forward upto the character @var{ch} on the line.  Signal error if
+Move point forward up to the character @var{ch} on the line.  Signal error if
 @var{ch} could not be found (@code{vip-goto-char-forward}).
 @item T @var{ch}
 @kindex 124 T @r{(}@code{vip-goto-char-backward}@r{)}
-Move point backward upto the character @var{ch} on the line.  Signal error if
+Move point backward up to the character @var{ch} on the line.  Signal error if
 @var{ch} could not be found (@code{vip-goto-char-backward}).
 @item ;
 @kindex 073 ; @r{(}@code{vip-repeat-find}@r{)}
diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS
index 5cabb9b015..f638d4717a 100644
--- a/etc/ERC-NEWS
+++ b/etc/ERC-NEWS
@@ -77,6 +77,13 @@ blanks when 'erc-send-whitespace-lines' is active.  New 
options have
 also been added for warning when input spans multiple lines.  Although
 off by default, new users are encouraged to enable them.
 
+** URL handling has improved.
+Clicking on 'irc://' and 'ircs://' links elsewhere in Emacs now does
+the right thing most of the time.  However, for security reasons,
+users are now prompted to confirm connection parameters prior to lift
+off.  See the new '(erc) Integrations' section in the Info manual to
+override this.
+
 ** Miscellaneous behavioral changes impacting the user experience.
 A bug has been fixed that saw prompts being mangled, doubled, or
 erased in server buffers upon disconnection.  Instead, input prompts
diff --git a/etc/NEWS b/etc/NEWS
index 7cd192b9d3..5a65896d69 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -439,6 +439,12 @@ The user options 'url-gateway-rlogin-host',
 'url-gateway-rlogin-parameters', and 'url-gateway-rlogin-user-name'
 are also obsolete.
 
+---
+** The user function 'url-irc-function' now takes a 'scheme' argument.
+The user option 'url-irc-function' is now called with a sixth argument
+corresponding to the scheme portion of the target URL.  For example,
+this would be "ircs" for a URL like "ircs://irc.libera.chat".
+
 ---
 ** The linum.el library is now obsolete.
 We recommend using either the built-in 'display-line-numbers-mode', or
@@ -743,6 +749,14 @@ If the current buffer is visiting a file that is 
executable, the
 This determines how long to pause Emacs after a process
 filter/sentinel error has been handled.
 
++++
+** New faces for font-lock.
+These faces are primarily meant for use with tree-sitter.  They are:
+'font-lock-bracket-face', 'font-lock-delimiter-face',
+'font-lock-escape-face', 'font-lock-number-face',
+'font-lock-misc-punctuation-face', 'font-lock-operator-face',
+'font-lock-property-face', and 'font-lock-punctuation-face'.
+
 +++
 ** New face 'variable-pitch-text'.
 This face is like 'variable-pitch' (from which it inherits), but is
@@ -1395,6 +1409,14 @@ If non-nil and there's only one matching option, 
auto-select that.
 If non-nil, this user option describes what entries not to add to the
 database stored on disk.
 
+** Auth-Source
+
++++
+*** New user option 'auth-source-pass-extra-query-keywords'.
+Whether to recognize additional keyword params, like ':max' and
+':require', as well as accept lists of query terms paired with
+applicable keywords.
+
 ** Dired
 
 +++
@@ -1450,8 +1472,8 @@ This command visits the file on the current line with EWW.
 ** Elisp
 
 ---
-*** New command 'elisp-eval-buffer' (bound to 'C-c C-e').
-This command evals the forms in the current buffer.
+*** New command 'elisp-eval-region-or-buffer' (bound to 'C-c C-e').
+This command evals the forms in the active region or in the whole buffer.
 
 ---
 *** New commands 'elisp-byte-compile-file' and 'elisp-byte-compile-buffer'.
@@ -2043,13 +2065,6 @@ 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
 
 +++
@@ -2142,13 +2157,20 @@ The older form still works but is undocumented.
 
 ---
 *** 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.
+You can now narrow the set of messages selected by Rmail 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-progressively-narrow' controls whether the stacking of
+the filters is in effect; customize it to a non-nil value to enable
+this feature.
+
+---
+*** New Rmail summary: by thread.
+The new command 'rmail-summary-by-thread' produces a summary of
+messages that belong to a single thread of discussion.
 
 ** EIEIO
 
@@ -2212,6 +2234,10 @@ it with new 'term-{faint,italic,slow-blink,fast-blink}' 
faces.
 *** 'project-find-file' and 'project-or-external-find-file' now accept
 a prefix argument which is interpreted to mean "include all files".
 
++++
+*** New command 'project-list-buffers' bound to 'C-x p C-b'.
+This command displays a list of buffers from the current project.
+
 +++
 *** 'project-kill-buffers' can display the list of buffers to kill.
 Customize the user option 'project-kill-buffers-display-buffer-list'
@@ -2630,6 +2656,17 @@ This user option decides which URL scheme that 
'browse-url' and
 related functions will use by default.  For example, you could
 customize this to "https" to always prefer HTTPS URLs.
 
+---
+*** New user option 'browse-url-irc-function'.
+This option specifies a function for opening irc:// links.  It
+defaults to the new function 'browse-url-irc'.
+
+---
+*** New function 'browse-url-irc'.
+This multipurpose autoloaded function can be used for opening irc://
+and ircs:// URLS by any caller that passes a URL string as an initial
+arg.
+
 ---
 *** Support for the Netscape web browser has been removed.
 This support has been obsolete since Emacs 25.1.  The final version of
@@ -2856,6 +2893,9 @@ remote host are shown.  Alternatively, the user option
 *** 'outlineify-sticky' command is renamed to 'allout-outlinify-sticky'.
 The old name is still available as an obsolete function alias.
 
+---
+*** The url-irc library now understands ircs:// links.
+
 ---
 *** New command 'world-clock-copy-time-as-kill' for 'M-x world-clock'.
 It copies the current line into the kill ring.
@@ -2920,6 +2960,37 @@ This is a lightweight variant of 'js-mode' that is used 
by default
 when visiting JSON files.
 
 
+** New mode 'ts-mode'.
+A major mode based on the tree-sitter library for editing programs
+in the TypeScript language.  It includes support for font-locking,
+indentation, and navigation.
+
+** New mode 'c-ts-mode'.
+A major mode based on the tree-sitter library for editing programs
+in the C language.  It includes support for font-locking,
+indentation, Imenu, which-func, and navigation.
+
+** New mode 'c++-ts-mode'.
+A major mode based on the tree-sitter library for editing programs
+in the C++ language.  It includes support for font-locking,
+indentation, Imenu, which-func, and navigation.
+
+** New mode 'java-ts-mode'.
+A major mode based on the tree-sitter library for editing programs
+in the Java language.  It includes support for font-locking,
+indentation, Imenu, which-func, and navigation.
+
+** New mode 'css-ts-mode'.
+A major mode based on the tree-sitter library for editing programs
+in the CSS language.  It includes support for font-locking,
+indentation, Imenu, which-func, and navigation.
+
+** New mode 'json-ts-mode'.
+A major mode based on the tree-sitter library for editing programs
+in the JSON language.  It includes support for font-locking,
+indentation, Imenu, which-func, and navigation.
+
+
 * Incompatible Lisp Changes in Emacs 29.1
 
 +++
@@ -4043,6 +4114,12 @@ This function allows defining a number of keystrokes 
with one form.
 ** New macro 'defvar-keymap'.
 This macro allows defining keymap variables more conveniently.
 
+** 'defvar-keymap' can specify 'repeat-mode' behavior for the keymap.
+Use ':repeat t' to have all bindings be repeatable or for more
+advanced usage:
+
+    ':repeat (:enter (commands ...) :exit (commands ...))'
+
 ---
 ** 'kbd' can now be used in built-in, preloaded libraries.
 It no longer depends on edmacro.el and cl-lib.el.
diff --git a/etc/NEWS.24 b/etc/NEWS.24
index 8ef479ac0a..fab8a39b0e 100644
--- a/etc/NEWS.24
+++ b/etc/NEWS.24
@@ -951,7 +951,7 @@ Also the following files used by the now obsolete 
otodo-mode.el:
 
 *** the old version of todo-mode.el (renamed to otodo-mode.el).
 
-*** xesam.el (owing to the cancellation of the XESAM project).
+*** xesam.el (owing to the cancelation of the XESAM project).
 
 *** yow.el; use fortune.el or cookie1.el instead.
 
diff --git a/etc/NEWS.28 b/etc/NEWS.28
index 8eab05a763..9982296aaa 100644
--- a/etc/NEWS.28
+++ b/etc/NEWS.28
@@ -2771,7 +2771,7 @@ If non-nil, it is a regexp that should match a valid 
cover image.
 *** 'shell-script-mode' now supports 'outline-minor-mode'.
 The outline headings have lines that start with "###".
 
-*** fileloop will now skip missing files instead of signalling an error.
+*** fileloop will now skip missing files instead of signaling an error.
 
 *** 'tabulated-list-mode' can now restore original display order.
 Many commands (like 'C-x C-b') are derived from 'tabulated-list-mode',
diff --git a/etc/NEXTSTEP b/etc/NEXTSTEP
index dacbed2045..0570f70795 100644
--- a/etc/NEXTSTEP
+++ b/etc/NEXTSTEP
@@ -206,7 +206,7 @@ Release History
                                keys, fix border and box drawing, remove
                                glitches in modeline drawing, support
                                overstrike for unavailable bold fonts, fix XPM
-                               related crasher bugs.  Incremental font
+                               related crashes.  Incremental font
                                metrics caching and other performance
                                improvements.  Shared-lisp builds now possible.
 
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 3c164b1282..3b6ab2e2ad 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -2847,7 +2847,7 @@ can pass the converted path to the =sqlcmd= tool.
 
 **** Improved support of header arguments for postgresql
 
-The postgresql engine in a sql code block supports now ~:dbport~ nd
+The postgresql engine in a sql code block now supports ~:dbport~ and
 ~:dbpassword~ as header arguments.
 
 **** Support for additional plantuml output formats
@@ -5763,7 +5763,7 @@ that Calc formulas can operate on them.
 **** org-ctags.el (Paul Sexton)
 
      Targets like =<<my target>>= can now be found by Emacs' etag
-     functionality, and Org-mode links can be used to to link to
+     functionality, and Org-mode links can be used to link to
      etags, also in non-Org-mode files.  For details, see the file
      /org-ctags.el/.
 
@@ -6120,7 +6120,7 @@ that Calc formulas can operate on them.
     code that is actually evaluated comprises the code block contents,
     augmented with the extra code which assigns the referenced data to
     variables. It is now possible to preview expanded contents, and
-    also to expand code during during tangling. This expansion takes
+    also to expand code during tangling. This expansion takes
     into account all header arguments, and variables.
 
     A new keybinding `C-c M-b p' bound to `org-babel-expand-src-block'
@@ -6235,7 +6235,7 @@ that Calc formulas can operate on them.
 
 **** Localized clock tables
 
-     Clock tables now support a new new =:lang= parameter, allowing
+     Clock tables now support a new =:lang= parameter, allowing
      the user to customize the localization of the table headers.  See
      the variable =org-clock-clocktable-language-setup= which controls
      available translated strings.
diff --git a/etc/PROBLEMS b/etc/PROBLEMS
index aaecc41f6e..2169ed0f80 100644
--- a/etc/PROBLEMS
+++ b/etc/PROBLEMS
@@ -1640,16 +1640,21 @@ X expects to find it.
 
 *** Improving performance with slow X connections.
 
-There are several ways to improve this performance, any subset of which can
-be carried out at the same time:
+There are several ways to improve this performance, any subset of
+which can be carried out at the same time:
 
-1) If you don't need X Input Methods (XIM) for entering text in some
+1) Use the "--with-x-toolkit=no" build of Emacs.  By not relying on
+   any toolkit (exhibiting potentially slow behavior), it has been
+   made very fast over networks exhibiting high latency, but suitable
+   bandwidth.
+
+2) If you don't need X Input Methods (XIM) for entering text in some
    language you use, you can improve performance on WAN links by using
    the X resource useXIM to turn off use of XIM.  This does not affect
    the use of Emacs's own input methods, which are part of the Leim
    package.
 
-2) If the connection is very slow, you might also want to consider
+3) If the connection is very slow, you might also want to consider
    switching off scroll bars, menu bar, and tool bar.  Adding the
    following forms to your .emacs file will accomplish that, but only
    after the initial frame is displayed:
@@ -1665,26 +1670,45 @@ be carried out at the same time:
     Emacs.menuBar: off
     Emacs.toolBar: off
 
-3) Use ssh to forward the X connection, and enable compression on this
+4) Use ssh to forward the X connection, and enable compression on this
    forwarded X connection (ssh -XC remotehostname emacs ...).
 
-4) Use lbxproxy on the remote end of the connection.  This is an interface
-   to the low bandwidth X extension in most modern X servers, which
-   improves performance dramatically, at the slight expense of correctness
-   of the X protocol.  lbxproxy achieves the performance gain by grouping
-   several X requests in one TCP packet and sending them off together,
-   instead of requiring a round-trip for each X request in a separate
-   packet.  The switches that seem to work best for emacs are:
-    -noatomsfile  -nowinattr  -cheaterrors -cheatevents
-   Note that the -nograbcmap option is known to cause problems.
-   For more about lbxproxy, see:
+   Keep in mind that this does not help with latency problems, only
+   andwidth ones.
+
+5) Use lbxproxy on the remote end of the connection.  This is an
+   interface to the low bandwidth X extension in some outdated X
+   servers, which improves performance dramatically, at the slight
+   expense of correctness of the X protocol.  lbxproxy achieves the
+   performance gain by grouping several X requests in one TCP packet
+   and sending them off together, instead of requiring a round-trip
+   for each X request in a separate packet.  The switches that seem to
+   work best for emacs are: -noatomsfile -nowinattr -cheaterrors
+   -cheatevents Note that the -nograbcmap option is known to cause
+   problems.  For more about lbxproxy, see:
    http://www.x.org/archive/X11R6.8.0/doc/lbxproxy.1.html
 
-5) If copying and killing is slow, try to disable the interaction with the
+   Keep in mind that lbxproxy and the LBX extension are now obsolete.
+
+6) If copying and killing is slow, try to disable the interaction with the
    native system's clipboard by adding these lines to your .emacs file:
+
      (setq interprogram-cut-function nil)
      (setq interprogram-paste-function nil)
 
+7) If selecting text with the mouse is slow, the main culprit is
+   likely `select-active-regions', coupled with a program monitoring
+   the clipboard on the X server you are connected to.  Try turning
+   that off.
+
+   However, over networks with moderate to high latency, with no
+   clipboard monitor running, the bottleneck is likely to be
+   `mouse-position' instead.  Set the variable
+   `x-use-fast-mouse-position' to either any non-nil value, or to the
+   symbol `really-fast' if that is still too slow.  Doing so will also
+   cause Emacs features that relies on accurate mouse position
+   reporting to stop working reliably.
+
 *** Emacs gives the error, Couldn't find per display information.
 
 This can result if the X server runs out of memory because Emacs uses
diff --git a/etc/themes/dichromacy-theme.el b/etc/themes/dichromacy-theme.el
index fe44d520cc..c9d73983b5 100644
--- a/etc/themes/dichromacy-theme.el
+++ b/etc/themes/dichromacy-theme.el
@@ -39,7 +39,7 @@ Ansi-Color faces are included."
       (bluegreen "#009e73")
       (yellow "#f8ec59")
       (blue "#0072b2")
-      (vermillion "#d55e00")
+      (vermilion "#d55e00")
       (redpurple "#cc79a7")
       (bluegray "#848ea9"))
   (custom-theme-set-faces
@@ -51,9 +51,9 @@ Ansi-Color faces are included."
    `(highlight ((,class (:foreground ,blue :background "#e5e5e5"))))
    `(region ((,class (:foreground unspecified :background ,yellow))))
    `(secondary-selection ((,class (:background "#e5e5e5"))))
-   `(isearch ((,class (:foreground "white" :background ,vermillion))))
+   `(isearch ((,class (:foreground "white" :background ,vermilion))))
    `(lazy-highlight ((,class (:foreground "white" :background ,redpurple))))
-   `(trailing-whitespace ((,class (:background ,vermillion))))
+   `(trailing-whitespace ((,class (:background ,vermilion))))
    ;; Mode line faces
    `(mode-line ((,class (:box (:line-width -1 :style released-button)
                              :background "#e5e5e5" :foreground "black"))))
@@ -62,17 +62,17 @@ Ansi-Color faces are included."
                                       :foreground "black"))))
    ;; Escape and prompt faces
    `(minibuffer-prompt ((,class (:weight bold :foreground ,blue))))
-   `(escape-glyph ((,class (:foreground ,vermillion))))
-   `(homoglyph ((,class (:foreground ,vermillion))))
+   `(escape-glyph ((,class (:foreground ,vermilion))))
+   `(homoglyph ((,class (:foreground ,vermilion))))
    `(error ((,class (:weight bold :slant italic
-                            :foreground ,vermillion))))
+                            :foreground ,vermilion))))
    `(warning ((,class (:foreground ,orange))))
    `(success ((,class (:foreground ,bluegreen))))
    ;; Font lock faces
    `(font-lock-builtin-face ((,class (:foreground ,blue))))
    `(font-lock-comment-face ((,class (:slant italic :foreground ,bluegreen))))
-   `(font-lock-constant-face ((,class (:weight bold :foreground ,vermillion))))
-   `(font-lock-function-name-face ((,class (:foreground ,vermillion))))
+   `(font-lock-constant-face ((,class (:weight bold :foreground ,vermilion))))
+   `(font-lock-function-name-face ((,class (:foreground ,vermilion))))
    `(font-lock-keyword-face ((,class (:weight bold :foreground ,skyblue))))
    `(font-lock-string-face ((,class (:foreground ,bluegray))))
    `(font-lock-type-face ((,class (:weight bold :foreground ,blue))))
@@ -81,8 +81,8 @@ Ansi-Color faces are included."
    `(link ((,class (:underline t :foreground ,blue))))
    `(link-visited ((,class (:underline t :foreground ,redpurple))))
    ;; Gnus faces
-   `(gnus-group-news-1 ((,class (:weight bold :foreground ,vermillion))))
-   `(gnus-group-news-1-low ((,class (:foreground ,vermillion))))
+   `(gnus-group-news-1 ((,class (:weight bold :foreground ,vermilion))))
+   `(gnus-group-news-1-low ((,class (:foreground ,vermilion))))
    `(gnus-group-news-2 ((,class (:weight bold :foreground ,orange))))
    `(gnus-group-news-2-low ((,class (:foreground ,orange))))
    `(gnus-group-news-3 ((,class (:weight bold :foreground ,skyblue))))
@@ -92,8 +92,8 @@ Ansi-Color faces are included."
    `(gnus-group-news-5 ((,class (:weight bold :foreground ,blue))))
    `(gnus-group-news-5-low ((,class (:foreground ,blue))))
    `(gnus-group-news-low ((,class (:foreground ,bluegreen))))
-   `(gnus-group-mail-1 ((,class (:weight bold :foreground ,vermillion))))
-   `(gnus-group-mail-1-low ((,class (:foreground ,vermillion))))
+   `(gnus-group-mail-1 ((,class (:weight bold :foreground ,vermilion))))
+   `(gnus-group-mail-1-low ((,class (:foreground ,vermilion))))
    `(gnus-group-mail-2 ((,class (:weight bold :foreground ,orange))))
    `(gnus-group-mail-2-low ((,class (:foreground ,orange))))
    `(gnus-group-mail-3 ((,class (:weight bold :foreground ,skyblue))))
@@ -103,13 +103,13 @@ Ansi-Color faces are included."
    `(gnus-header-from ((,class (:weight bold :foreground ,blue))))
    `(gnus-header-subject ((,class (:foreground ,orange))))
    `(gnus-header-name ((,class (:foreground ,skyblue))))
-   `(gnus-header-newsgroups ((,class (:foreground ,vermillion))))
+   `(gnus-header-newsgroups ((,class (:foreground ,vermilion))))
    ;; Image-Dired
-   `(image-dired-thumb-flagged ((,class (:background ,vermillion))))
+   `(image-dired-thumb-flagged ((,class (:background ,vermilion))))
    `(image-dired-thumb-mark ((,class (:background ,orange))))
    ;; Message faces
    `(message-header-name ((,class (:foreground ,skyblue))))
-   `(message-header-cc ((,class (:foreground ,vermillion))))
+   `(message-header-cc ((,class (:foreground ,vermilion))))
    `(message-header-other ((,class (:foreground ,bluegreen))))
    `(message-header-subject ((,class (:foreground ,orange))))
    `(message-header-to ((,class (:weight bold :foreground ,blue))))
@@ -122,8 +122,8 @@ Ansi-Color faces are included."
                                  :slant unspecified :underline ,redpurple))))
    ;; ANSI color
    `(ansi-color-black ((,class (:background "black" :foreground "black"))))
-   `(ansi-color-red ((,class (:background ,vermillion
-                             :foreground ,vermillion))))
+   `(ansi-color-red ((,class (:background ,vermilion
+                             :foreground ,vermilion))))
    `(ansi-color-green ((,class (:background ,bluegreen
                                :foreground ,bluegreen))))
    `(ansi-color-yellow ((,class (:background ,yellow :foreground ,yellow))))
@@ -134,8 +134,8 @@ Ansi-Color faces are included."
    `(ansi-color-white ((,class (:background "gray90" :foreground "gray90"))))
    `(ansi-color-bright-black ((,class (:background "black"
                                       :foreground "black"))))
-   `(ansi-color-bright-red ((,class (:background ,vermillion
-                                    :foreground ,vermillion))))
+   `(ansi-color-bright-red ((,class (:background ,vermilion
+                                    :foreground ,vermilion))))
    `(ansi-color-bright-green ((,class (:background ,bluegreen
                                       :foreground ,bluegreen))))
    `(ansi-color-bright-yellow ((,class (:background ,yellow
diff --git a/lib-src/ChangeLog.1 b/lib-src/ChangeLog.1
index 0829f50a56..1a9767661a 100644
--- a/lib-src/ChangeLog.1
+++ b/lib-src/ChangeLog.1
@@ -5643,7 +5643,7 @@
 1998-04-06  Andreas Schwab  <schwab@gnu.org>
 
        Silence -Wimplicit:
-       * movemail.c: Move cancellations up.  Include <stdlib.h> if
+       * movemail.c: Move cancelations up.  Include <stdlib.h> if
        available.
        * fakemail.c (_XOPEN_SOURCE): Define for declaration of cuserid.
        (parse_header): Explicitly declare return type.
diff --git a/lib-src/ebrowse.c b/lib-src/ebrowse.c
index 641570da02..d3af926b63 100644
--- a/lib-src/ebrowse.c
+++ b/lib-src/ebrowse.c
@@ -1574,6 +1574,67 @@ yylex (void)
 
         end_string:
           return end_char == '\'' ? CCHAR : CSTRING;
+       case 'R':
+         if (GET (c) == '"')
+           {
+             /* C++11 rstrings.  */
+
+#define RSTRING_EOF_CHECK                                              \
+             do {                                                      \
+               if (c == '\0')                                          \
+                 {                                                     \
+                   yyerror ("unterminated c++11 rstring", NULL);       \
+                   UNGET ();                                           \
+                   return CSTRING;                                     \
+                 }                                                     \
+             } while (0)
+
+           char *rstring_prefix_start = in;
+
+           while (GET (c) != '(')
+             {
+               RSTRING_EOF_CHECK;
+               if (c == '"')
+                 {
+                   yyerror ("malformed c++11 rstring", NULL);
+                   return CSTRING;
+                 }
+             }
+           char *rstring_prefix_end = in - 1;
+           while (TRUE)
+             {
+               switch (GET (c))
+                 {
+                 default:
+                   RSTRING_EOF_CHECK;
+                   break;
+                 case '\n':
+                   INCREMENT_LINENO;
+                   break;
+                 case ')':
+                   {
+                     char *in_saved = in;
+                     char *prefix = rstring_prefix_start;
+                     while (prefix != rstring_prefix_end && GET (c) == *prefix)
+                       {
+                         RSTRING_EOF_CHECK;
+                         prefix++;
+                       }
+                     if (prefix == rstring_prefix_end)
+                       {
+                         if (GET (c) == '"')
+                           return CSTRING;
+                         RSTRING_EOF_CHECK;
+                       }
+                     in = in_saved;
+                   }
+                 }
+             }
+           }
+
+          UNGET ();
+          /* Fall through to identifiers and keywords.  */
+         FALLTHROUGH;
 
         case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
         case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
@@ -1581,7 +1642,7 @@ yylex (void)
         case 'v': case 'w': case 'x': case 'y': case 'z':
         case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
         case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
-        case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+        case 'O': case 'P': case 'Q': case 'S': case 'T': case 'U':
         case 'V': case 'W': case 'X': case 'Y': case 'Z': case '_':
           {
             /* Identifier and keywords.  */
diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c
index 425db8cfac..ee124ea135 100644
--- a/lib-src/emacsclient.c
+++ b/lib-src/emacsclient.c
@@ -2240,7 +2240,7 @@ main (int argc, char **argv)
               char *str = unquote_argument (p + strlen ("-error "));
               if (!skiplf)
                 printf ("\n");
-              fprintf (stderr, "*ERROR*: %s", str);
+             message (true, "*ERROR*: %s", str);
               if (str[0])
                skiplf = str[strlen (str) - 1] == '\n';
               exit_status = EXIT_FAILURE;
diff --git a/lisp/ChangeLog.10 b/lisp/ChangeLog.10
index 6053ffa65a..de73fe7f0a 100644
--- a/lisp/ChangeLog.10
+++ b/lisp/ChangeLog.10
@@ -1737,7 +1737,7 @@
        (bibtex-find-crossref, bibtex-find-entry): New funs.
        (bibtex-find-entry-location): Rename to bibtex-prepare-new-entry, use
        bibtex-lessp, Simplify.
-       (bibtex-validate): Simplify.  Fixe bug of internal variable
+       (bibtex-validate): Simplify.  Fix bug of internal variable
        questionable-month.
        (bibtex-remove-OPT-or-ALT): Use when.
        (bibtex-remove-delimiters, bibtex-kill-field, bibtex-kill-entry)
@@ -9449,7 +9449,7 @@
        Use shy group.
        (outline-level) <var>: Update calling convention.
        (outline-level) <fun>: Take advantage of it.
-       (outline-demote): Don't assume the match-data is still uptodate.
+       (outline-demote): Don't assume the match-data is still up-to-date.
        (outline-up-heading): Simplify and make sure the match data is
        properly set at the end.
 
diff --git a/lisp/ChangeLog.12 b/lisp/ChangeLog.12
index a89e515510..0796965a9b 100644
--- a/lisp/ChangeLog.12
+++ b/lisp/ChangeLog.12
@@ -1556,7 +1556,7 @@
        (org-search-not-self): Rename from `org-search-not-link'.
        (org-open-link-marker): New variable.
        (org-open-at-point): Set `org-open-link-marker'.
-       (org-print-icalendar-entries): Fixe bug with excluding DONE
+       (org-print-icalendar-entries): Fix bug with excluding DONE
        entries from the exported list.
        (org-edit-formula-lisp-indent): New command.
        (orgtbl-to-texinfo, orgtbl-to-html): New functions.
@@ -5243,7 +5243,7 @@
 
 2006-10-11  Ilya Zakharevich  <ilyaz@cpan.org>
 
-       * progmodes/cperl-mode.el: Merge from upstream, upto version 5.22.
+       * progmodes/cperl-mode.el: Merge from upstream, up to version 5.22.
        After 5.0:
        (cperl-add-tags-recurse-noxs-fullpath): New function (for -batch mode).
 
@@ -5551,7 +5551,7 @@
        (cperl-next-interpolated-REx-1): Likewise.
        "\C-c\C-x", "\C-c\C-y", "\C-c\C-v": New keybinding for these functions.
        Perl/Regexp menu: 3 new entries for `cperl-next-interpolated-REx'.
-       (cperl-praise): Mention finded interpolated RExen.
+       (cperl-praise): Mention finding interpolated RExen.
 
        After 5.19:
        (cperl-init-faces): Highlight %$foo, @$foo too.
@@ -11874,7 +11874,7 @@
 
        * files.el (hack-local-variables-confirm): Don't prompt for ! if
        enable-local-variables is set to always query, or there is no
-       savable variable.
+       saveable variable.
 
 2006-03-10  Bill Wohler  <wohler@newt.com>
 
diff --git a/lisp/ChangeLog.13 b/lisp/ChangeLog.13
index 369aec81ef..326003d9d9 100644
--- a/lisp/ChangeLog.13
+++ b/lisp/ChangeLog.13
@@ -1070,7 +1070,7 @@
        * international/mule-cmds.el (char-code-property-alist): New variable.
        (define-char-code-property): New function.
        (get-char-code-property, put-char-code-property): Handle a
-       char-table registerd in char-code-property-alist.
+       char-table registered in char-code-property-alist.
        (set-language-environment): Check :ascii-compatible-p property of
        nonascii charset instead of its dimension.
 
@@ -13901,7 +13901,7 @@
 2007-07-23  Stefan Monnier  <monnier@iro.umontreal.ca>
 
        * ses.el (ses-cleanup): Prevent Emacs from spuriously checking if the
-       underlying file is uptodate.
+       underlying file is up-to-date.
 
 2007-07-23  Christopher J. Madsen  <cjm@cjmweb.net>
 
@@ -15245,7 +15245,7 @@
 2007-06-25  Stefan Monnier  <monnier@iro.umontreal.ca>
 
        * emacs-lisp/autoload.el (autoload-modified-buffers): New var.
-       (autoload-find-destination): Keep it uptodate.
+       (autoload-find-destination): Keep it up-to-date.
        (autoload-save-buffers): New fun.
        (update-file-autoloads): Use it.  Re-add the "up to date" message.
 
diff --git a/lisp/ChangeLog.14 b/lisp/ChangeLog.14
index 686746abe0..8d8c611778 100644
--- a/lisp/ChangeLog.14
+++ b/lisp/ChangeLog.14
@@ -6103,7 +6103,7 @@
        * international/quail.el (quail-show-guidance): Don't create
        a guidance-frame if current buffer is not a minibuffer, since even if
        selected-window is mini-p, the buffer will never be displayed in it, so
-       it wil be usable for guidance.
+       it will be usable for guidance.
 
 2008-10-28  Stefan Monnier  <monnier@iro.umontreal.ca>
 
diff --git a/lisp/ChangeLog.15 b/lisp/ChangeLog.15
index 9aa4caedfe..8a21c291e7 100644
--- a/lisp/ChangeLog.15
+++ b/lisp/ChangeLog.15
@@ -22575,7 +22575,7 @@
 
 2009-06-28  Juri Linkov  <juri@jurta.org>
 
-       * help-fns.el (describe-function-1): Correctly locate adviced
+       * help-fns.el (describe-function-1): Correctly locate advised
        functions in hyperlink (Bug#2438).
 
 2009-06-28  Chong Yidong  <cyd@stupidchicken.com>
diff --git a/lisp/ChangeLog.16 b/lisp/ChangeLog.16
index ef59698317..7b57c014d2 100644
--- a/lisp/ChangeLog.16
+++ b/lisp/ChangeLog.16
@@ -10931,7 +10931,7 @@
 
 2012-05-29  Aaron S. Hawley  <aaron.s.hawley@gmail.com>
 
-       * vc/vc.el (vc-revert, vc-rollback): Dont kill vc-diff buffer on
+       * vc/vc.el (vc-revert, vc-rollback): Don't kill vc-diff buffer on
        revert (Bug#11488).
 
 2012-05-29  Juri Linkov  <juri@jurta.org>
@@ -15340,7 +15340,7 @@
        * subr.el (with-selected-frame): Mention that the selected frame
        is restored (bug#9980).
 
-       * ibuffer.el (ibuffer-mode): List the bindings in the corrent map
+       * ibuffer.el (ibuffer-mode): List the bindings in the correct map
        (bug#9759).
 
        * mail/smtpmail.el (password-cache-add): Remove unused declaration.
@@ -22838,15 +22838,14 @@
 2011-05-10  Jim Meyering  <meyering@redhat.com>
 
        Fix doubled-word typos.
-       * international/quail.el (quail-insert-kbd-layout): and and -> and.
-       * kermit.el: and and -> and.
-       * net/ldap.el (ldap-search-internal): to to -> to.
-       * progmodes/vhdl-mode.el (vhdl-offsets-alist): Likewise.
-       * progmodes/js.el (js-mode): and and -> and.
-       * textmodes/artist.el (artist-move-to-xy): at at -> at.
-       (artist-draw-region-trim-line-endings): if if -> if.
-       And Safetyc -> Safety.
-       * textmodes/reftex-dcr.el (reftex-view-crossref): at at -> at a.
+       * international/quail.el (quail-insert-kbd-layout):
+       * kermit.el:
+       * net/ldap.el (ldap-search-internal):
+       * progmodes/vhdl-mode.el (vhdl-offsets-alist):
+       * progmodes/js.el (js-mode):
+       * textmodes/artist.el (artist-move-to-xy):
+       (artist-draw-region-trim-line-endings):
+       * textmodes/reftex-dcr.el (reftex-view-crossref): Fix typos.
 
 2011-05-10  Glenn Morris  <rgm@gnu.org>
            Stefan Monnier  <monnier@iro.umontreal.ca>
diff --git a/lisp/ChangeLog.3 b/lisp/ChangeLog.3
index 7f5ceb4b85..e23226b844 100644
--- a/lisp/ChangeLog.3
+++ b/lisp/ChangeLog.3
@@ -7326,8 +7326,7 @@
 1991-07-13  Jim Blandy  (jimb@churchy.gnu.ai.mit.edu)
 
        * info.el (Info-find-node): Call buffer-flush-undo with one arg,
-       instead of none.  Change call to get-buffer-c>reate to
-       get-buffer-create.
+       instead of none.  Fix typo in call to get-buffer-create.
 
        * startup.el (command-line): Remove the arguments from
        command-line-args as we process them.
diff --git a/lisp/ChangeLog.4 b/lisp/ChangeLog.4
index e965dbb5ef..ea0502975e 100644
--- a/lisp/ChangeLog.4
+++ b/lisp/ChangeLog.4
@@ -6858,7 +6858,7 @@
 1993-07-26  Richard Stallman  (rms@mole.gnu.ai.mit.edu)
 
        * frame.el (frame-notice-user-settings): Don't reapply a parm
-       whose value is ot changed (as far as we know) since frame-initialize.
+       whose value is not changed (as far as we know) since frame-initialize.
 
        * simple.el (kill-ring-save): Delete spurious `message' call.
        (set-mark): If POS is nil, call deactivate-mark.
diff --git a/lisp/ChangeLog.6 b/lisp/ChangeLog.6
index e2128b94c0..27d522aaf2 100644
--- a/lisp/ChangeLog.6
+++ b/lisp/ChangeLog.6
@@ -7392,7 +7392,7 @@
        (ada-format-paramlist): Simplified a regexp.
        (ada-indent-current): On first line of the buffer, indent to column 0.
        Don't reindent if new position is the same as the old one.  Thus, a
-       correctly indended line is not modified.
+       correctly indented line is not modified.
        (ada-get-indent-subprog): Simplified a regexp.
        (ada-goto-matching-decl-start): Distinguish between normal type
        declaration and protected types, which are more like procedures.
diff --git a/lisp/ChangeLog.7 b/lisp/ChangeLog.7
index c81968e6ee..3220832454 100644
--- a/lisp/ChangeLog.7
+++ b/lisp/ChangeLog.7
@@ -20821,7 +20821,7 @@
        function word-help-find-help-file.
        (word-help-guess-all): New subroutine.
        (word-help-guess): Use word-help-guess-all.
-       May optionally copy only upto the cursor,
+       May optionally copy only up to the cursor,
        instead of the entire keyword.
 
 1997-01-01  Richard Stallman  <rms@ethanol.gnu.ai.mit.edu>
diff --git a/lisp/ChangeLog.8 b/lisp/ChangeLog.8
index 78fc7e2056..a14d682192 100644
--- a/lisp/ChangeLog.8
+++ b/lisp/ChangeLog.8
@@ -4356,7 +4356,7 @@
 1999-07-21  Gerd Moellmann  <gerd@gnu.org>
 
        * scroll-bar.el (scroll-bar-toolkit-scroll): New.
-       (global): Use different key bindings if using tookit scroll bars.
+       (global): Use different key bindings if using toolkit scroll bars.
 
 1999-07-21  Gerd Moellmann  <gerd@gnu.org>
 
@@ -9712,7 +9712,7 @@
        (ps-mule-font-info-database-ps-bdf): New variable.
        (ccl-encode-ethio-unicode): Bug of CCL code fixed.
        (ps-mule-generate-font): Give CHARSET arg to FONT-FUNC function
-       registerd in FONT-SPEC.
+       registered in FONT-SPEC.
        (ps-mule-bitmap-prologue): Fix PostScript code to realize correct
        character width of bitmap fonts.
        (ps-mule-generate-bitmap-font): Give COLUMNS arg to PostScript
diff --git a/lisp/ChangeLog.9 b/lisp/ChangeLog.9
index a8ebe81e7d..469d0970f8 100644
--- a/lisp/ChangeLog.9
+++ b/lisp/ChangeLog.9
@@ -789,7 +789,7 @@
        (command-line-1): If inhibit-startup-buffer-menu is set, don't
        display the buffer menu.  From Simon Josefsson <jas@extundo.com>.
 
-       This allows upto 99999 messages in the summary without screwing up
+       This allows up to 99999 messages in the summary without screwing up
        the summary sorting.  Previously 9999 was the maximum.  Added to NEWS.
 
        * mail/rmailsum.el (rmail-make-summary-line)
@@ -1888,7 +1888,7 @@
        (uniquify-item-greaterp): Substitutes uniquify-item-lessp.
        This is equivalent to what the old code did.
        (uniquify-rationalize-a-list): Never recompute the proposed
-       name.  Sort the conflicting sublist before rationalising it: this
+       name.  Sort the conflicting sublist before rationalizing it: this
        is equivalent to what the old code did, but one directory element
        at a time, and only when necessary.
        (uniquify-rationalize-conflicting-sublist): Recompute here the
diff --git a/lisp/allout.el b/lisp/allout.el
index 5f7087829e..df0181cecb 100644
--- a/lisp/allout.el
+++ b/lisp/allout.el
@@ -5356,9 +5356,6 @@ alternate presentation form:
 
  `flat' -- Present prefix as numeric section.subsection..., starting with
         section indicated by the START-NUM, innermost nesting first.
- X`flat-indented' -- Prefix is like `flat' for first topic at each
- X                level, but subsequent topics have only leaf topic
- X                number, padded with blanks to line up with first.
  `indent' (symbol) --  Convert header prefixes to all white space,
                       except for distinctive bullets.
 
diff --git a/lisp/apropos.el b/lisp/apropos.el
index 62a37df820..a731926f45 100644
--- a/lisp/apropos.el
+++ b/lisp/apropos.el
@@ -492,7 +492,7 @@ Intended as a value for `revert-buffer-function'."
 \\{apropos-mode-map}"
   (make-local-variable 'apropos--current)
   (setq-local revert-buffer-function #'apropos--revert-buffer)
-  (setq-local outline-regexp "^[^ \n]+"
+  (setq-local outline-search-function #'outline-search-level
               outline-level (lambda () 1)
               outline-minor-mode-cycle t
               outline-minor-mode-highlight t
@@ -651,7 +651,8 @@ while a list of strings is used as a word list."
 (defun apropos (pattern &optional do-all)
   "Show all meaningful Lisp symbols whose names match PATTERN.
 Symbols are shown if they are defined as functions, variables, or
-faces, or if they have nonempty property lists.
+faces, or if they have nonempty property lists, or if they are
+known keywords.
 
 PATTERN can be a word, a list of words (separated by spaces),
 or a regexp (using some regexp special characters).  If it is a word,
@@ -1187,7 +1188,8 @@ as a heading."
          (insert-text-button (symbol-name symbol)
                              'type 'apropos-symbol
                              'skip apropos-multi-type
-                             'face 'apropos-symbol)
+                             'face 'apropos-symbol
+                             'outline-level 1)
          (setq button-end (point))
          (if (and (eq apropos-sort-by-scores 'verbose)
                   (cadr apropos-item))
diff --git a/lisp/auth-source-pass.el b/lisp/auth-source-pass.el
index 0955e2ed07..dc274843e1 100644
--- a/lisp/auth-source-pass.el
+++ b/lisp/auth-source-pass.el
@@ -55,13 +55,27 @@
   :type 'string
   :version "27.1")
 
+(defcustom auth-source-pass-extra-query-keywords t
+  "Whether to consider additional keywords when performing a query.
+Specifically, when the value is t, recognize the `:max' and
+`:require' keywords and accept lists of query parameters for
+certain keywords, such as `:host' and `:user'.  Also, wrap all
+returned secrets in a function and forgo any further results
+filtering unless given an applicable `:require' argument.  When
+this option is nil, do none of that, and enact the narrowing
+behavior described toward the bottom of the Info node `(auth) The
+Unix password store'."
+  :type 'boolean
+  :version "29.1")
+
 (cl-defun auth-source-pass-search (&rest spec
                                          &key backend type host user port
+                                         require max
                                          &allow-other-keys)
   "Given some search query, return matching credentials.
 
 See `auth-source-search' for details on the parameters SPEC, BACKEND, TYPE,
-HOST, USER and PORT."
+HOST, USER, PORT, REQUIRE, and MAX."
   (cl-assert (or (null type) (eq type (oref backend type)))
              t "Invalid password-store search: %s %s")
   (cond ((eq host t)
@@ -70,6 +84,8 @@ HOST, USER and PORT."
         ((null host)
          ;; Do not build a result, as none will match when HOST is nil
          nil)
+        (auth-source-pass-extra-query-keywords
+         (auth-source-pass--build-result-many host port user require max))
         (t
          (when-let ((result (auth-source-pass--build-result host port user)))
            (list result)))))
@@ -89,6 +105,39 @@ HOSTS can be a string or a list of strings."
                                     (seq-subseq retval 0 -2)) ;; remove 
password
         retval))))
 
+(defvar auth-source-pass--match-regexp nil)
+
+(defun auth-source-pass--match-regexp (s)
+  (rx-to-string ; autoloaded
+   `(: (or bot "/")
+       (or (: (? (group-n 20 (+ (not (in ?\  ?/ ?@ ,s)))) "@")
+              (group-n 10 (+ (not (in ?\  ?/ ?@ ,s))))
+              (? ,s (group-n 30 (+ (not (in ?\  ?/ ,s))))))
+           (: (group-n 11 (+ (not (in ?\  ?/ ?@ ,s))))
+              (? ,s (group-n 31 (+ (not (in ?\  ?/ ,s)))))
+              (? "/" (group-n 21 (+ (not (in ?\  ?/ ,s)))))))
+       eot)
+   'no-group))
+
+(defun auth-source-pass--build-result-many (hosts ports users require max)
+  "Return multiple `auth-source-pass--build-result' values."
+  (unless (listp hosts) (setq hosts (list hosts)))
+  (unless (listp users) (setq users (list users)))
+  (unless (listp ports) (setq ports (list ports)))
+  (let* ((auth-source-pass--match-regexp (auth-source-pass--match-regexp
+                                          auth-source-pass-port-separator))
+         (rv (auth-source-pass--find-match-many hosts users ports
+                                                require (or max 1))))
+    (when auth-source-debug
+      (auth-source-pass--do-debug "final result: %S" rv))
+    (let (out)
+      (dolist (e rv out)
+        (when-let* ((s (plist-get e :secret)) ; not captured by closure in 29.1
+                    (v (auth-source--obfuscate s)))
+          (setf (plist-get e :secret)
+                (lambda () (auth-source--deobfuscate v))))
+        (push e out)))))
+
 ;;;###autoload
 (defun auth-source-pass-enable ()
   "Enable auth-source-password-store."
@@ -206,6 +255,67 @@ HOSTS can be a string or a list of strings."
                 hosts
               (list hosts))))
 
+(defun auth-source-pass--retrieve-parsed (seen path port-number-p)
+  (when (string-match auth-source-pass--match-regexp path)
+    (puthash path
+             `( :host ,(or (match-string 10 path) (match-string 11 path))
+                ,@(if-let* ((tr (match-string 21 path)))
+                      (list :user tr :suffix t)
+                    (list :user (match-string 20 path)))
+                :port ,(and-let* ((p (or (match-string 30 path)
+                                         (match-string 31 path)))
+                                  (n (string-to-number p)))
+                         (if (or (zerop n) (not port-number-p))
+                             (format "%s" p)
+                           n)))
+             seen)))
+
+(defun auth-source-pass--match-parts (parts key value require)
+  (let ((mv (plist-get parts key)))
+    (if (memq key require)
+        (and value (equal mv value))
+      (or (not value) (not mv) (equal mv value)))))
+
+(defun auth-source-pass--find-match-many (hosts users ports require max)
+  "Return plists for valid combinations of HOSTS, USERS, PORTS."
+  (let ((seen (make-hash-table :test #'equal))
+        (entries (auth-source-pass-entries))
+        out suffixed suffixedp)
+    (catch 'done
+      (dolist (host hosts out)
+        (pcase-let ((`(,_ ,u ,p) (auth-source-pass--disambiguate host)))
+          (unless (or (not (equal "443" p)) (string-prefix-p "https://"; host))
+            (setq p nil))
+          (dolist (user (or users (list u)))
+            (dolist (port (or ports (list p)))
+              (dolist (e entries)
+                (when-let*
+                    ((m (or (gethash e seen) (auth-source-pass--retrieve-parsed
+                                              seen e (integerp port))))
+                     ((equal host (plist-get m :host)))
+                     ((auth-source-pass--match-parts m :port port require))
+                     ((auth-source-pass--match-parts m :user user require))
+                     (parsed (auth-source-pass-parse-entry e))
+                     ;; For now, ignore body-content pairs, if any,
+                     ;; from `auth-source-pass--parse-data'.
+                     (secret (or (auth-source-pass--get-attr 'secret parsed)
+                                 (not (memq :secret require)))))
+                  (push
+                   `( :host ,host ; prefer user-provided :host over h
+                      ,@(and-let* ((u (plist-get m :user))) (list :user u))
+                      ,@(and-let* ((p (plist-get m :port))) (list :port p))
+                      ,@(and secret (not (eq secret t)) (list :secret secret)))
+                   (if (setq suffixedp (plist-get m :suffix)) suffixed out))
+                  (unless suffixedp
+                    (when (or (zerop (cl-decf max))
+                              (null (setq entries (delete e entries))))
+                      (throw 'done out)))))
+              (setq suffixed (nreverse suffixed))
+              (while suffixed
+                (push (pop suffixed) out)
+                (when (zerop (cl-decf max))
+                  (throw 'done out))))))))))
+
 (defun auth-source-pass--disambiguate (host &optional user port)
   "Return (HOST USER PORT) after disambiguation.
 Disambiguate between having user provided inside HOST (e.g.,
diff --git a/lisp/bookmark.el b/lisp/bookmark.el
index b57ad12986..7f3a264f53 100644
--- a/lisp/bookmark.el
+++ b/lisp/bookmark.el
@@ -365,8 +365,8 @@ BOOKMARK-RECORD is, e.g., one element from 
`bookmark-alist'."
   (car bookmark-record))
 
 (defun bookmark-type-from-full-record (bookmark-record)
-  "Return then type of BOOKMARK-RECORD.
-BOOKMARK-RECORD is, e.g., one element from `bookmark-alist'. It's
+  "Return the type of BOOKMARK-RECORD.
+BOOKMARK-RECORD is, e.g., one element from `bookmark-alist'.  Its
 type is read from the symbol property named
 `bookmark-handler-type' read on the record handler function."
   (let ((handler (bookmark-get-handler bookmark-record)))
@@ -1396,20 +1396,25 @@ after a bookmark was set in it."
   (interactive (list (bookmark-completing-read "Bookmark to relocate")))
   (bookmark-maybe-historicize-string bookmark-name)
   (bookmark-maybe-load-default-file)
-  (let* ((bmrk-filename (bookmark-get-filename bookmark-name))
-         (newloc (abbreviate-file-name
-                  (expand-file-name
-                   (read-file-name
-                    (format "Relocate %s to: " bookmark-name)
-                    (file-name-directory bmrk-filename))))))
-    (bookmark-set-filename bookmark-name newloc)
-    (bookmark-update-last-modified bookmark-name)
-    (setq bookmark-alist-modification-count
-          (1+ bookmark-alist-modification-count))
-    (if (bookmark-time-to-save-p)
+  (let ((bmrk-filename (bookmark-get-filename bookmark-name)))
+    ;; FIXME: Make `bookmark-relocate' support bookmark Types
+    ;; besides files and directories.
+    (unless bmrk-filename
+      (user-error "Cannot relocate bookmark of type \"%s\""
+                  (bookmark-type-from-full-record
+                   (bookmark-get-bookmark bookmark-name))))
+    (let ((newloc (abbreviate-file-name
+                   (expand-file-name
+                    (read-file-name
+                     (format "Relocate %s to: " bookmark-name)
+                     (file-name-directory bmrk-filename))))))
+      (bookmark-set-filename bookmark-name newloc)
+      (bookmark-update-last-modified bookmark-name)
+      (setq bookmark-alist-modification-count
+            (1+ bookmark-alist-modification-count))
+      (when (bookmark-time-to-save-p)
         (bookmark-save))
-    (bookmark-bmenu-surreptitiously-rebuild-list)))
-
+      (bookmark-bmenu-surreptitiously-rebuild-list))))
 
 ;;;###autoload
 (defun bookmark-insert-location (bookmark-name &optional no-history)
diff --git a/lisp/bs.el b/lisp/bs.el
index 060bae6fdd..1fd31fb3b8 100644
--- a/lisp/bs.el
+++ b/lisp/bs.el
@@ -25,7 +25,7 @@
 
 ;;; Commentary:
 
-;; The bs-package contains a main function bs-show for popping up a
+;; The bs package contains a main function `bs-show' for popping up a
 ;; buffer in a way similar to `list-buffers' and `electric-buffer-list':
 ;; The new buffer offers a Buffer Selection Menu for manipulating
 ;; the buffer list and buffers.
@@ -42,18 +42,18 @@
 
 ;;; Quick Installation and Customization:
 
-;; To display the bs menu, do
+;; To display the bs menu, type
 ;;   M-x bs-show
-;; To customize its behavior, do
+;; To customize its behavior, type
 ;;   M-x bs-customize
 
 ;;; More Commentary:
 
-;; bs-show will generate a new buffer named *buffer-selection*, which shows
+;; `bs-show' will generate a new buffer named *buffer-selection*, which shows
 ;; all buffers or a subset of them, and has possibilities for deleting,
-;; saving and selecting buffers. For more details see docstring of
-;; function `bs-mode'. A current configuration describes which buffers appear
-;; in *buffer-selection*. See docstring of variable `bs-configurations' for
+;; saving and selecting buffers.  For more details see docstring of
+;; function `bs-mode'.  A current configuration describes which buffers appear
+;; in *buffer-selection*.  See docstring of variable `bs-configurations' for
 ;; more details.
 ;;
 ;; The package bs combines the advantages of the Emacs functions
@@ -70,7 +70,7 @@
 
 ;;; Cycling through buffers
 
-;; This package offers two functions for buffer cycling. If you want to cycle
+;; This package offers two functions for buffer cycling.  If you want to cycle
 ;; through buffer list you can use `bs-cycle-next' or `bs-cycle-previous'.
 ;; Bind these function to a key like
 ;;   (global-set-key [(f9)]   'bs-cycle-previous)
@@ -80,7 +80,7 @@
 ;; to go through internal buffers like *Messages*.
 ;;
 ;; Cycling through buffers ignores sorting because sorting destroys
-;; the logical buffer list. If buffer list is sorted by size you
+;; the logical buffer list.  If buffer list is sorted by size you
 ;; won't be able to cycle to the smallest buffer.
 
 ;;; Customization:
@@ -114,7 +114,7 @@
 ;; When cycling through buffer list the functions for cycling will use
 ;; the current configuration of bs to calculate the buffer list.
 ;; If you want to use a different configuration for cycling you have to set
-;; the variable `bs-cycle-configuration-name'. You can customize this variable.
+;; the variable `bs-cycle-configuration-name'.  You can customize this 
variable.
 ;;
 ;; For example: If you use the configuration called "files-and-scratch" you
 ;; can cycle through all file buffers and *scratch* although your current
@@ -390,9 +390,9 @@ column title to highlight.
 FACE is a face used to fontify the sorted column title.  A value of nil means
 don't highlight.
 The new sort aspect will be inserted into list `bs-sort-functions'."
-  (let ((tupel (assoc name bs-sort-functions)))
-    (if tupel
-       (setcdr tupel (list fun regexp-for-sorting face))
+  (let ((tuple (assoc name bs-sort-functions)))
+    (if tuple
+        (setcdr tuple (list fun regexp-for-sorting face))
       (setq bs-sort-functions
            (cons (list name fun regexp-for-sorting face)
                  bs-sort-functions)))))
@@ -1240,13 +1240,13 @@ by buffer configuration `bs-cycle-configuration-name'."
     (bs-set-configuration (or bs-cycle-configuration-name 
bs-default-configuration))
     (let ((bs-buffer-sort-function nil)
          (bs--current-sort-function nil))
-      (let* ((tupel (bs-next-buffer (if (or (eq last-command
+      (let* ((tuple (bs-next-buffer (if (or (eq last-command
                                                'bs-cycle-next)
                                            (eq last-command
                                                'bs-cycle-previous))
                                        bs--cycle-list)))
-            (next (car tupel))
-            (cycle-list (cdr tupel)))
+             (next (car tuple))
+             (cycle-list (cdr tuple)))
         ;; We don't want the frame iconified if the only window in the frame
         ;; happens to be dedicated.
         (bury-buffer (current-buffer))
@@ -1272,13 +1272,13 @@ by buffer configuration `bs-cycle-configuration-name'."
     (bs-set-configuration (or bs-cycle-configuration-name 
bs-default-configuration))
     (let ((bs-buffer-sort-function nil)
          (bs--current-sort-function nil))
-      (let* ((tupel (bs-previous-buffer (if (or (eq last-command
+      (let* ((tuple (bs-previous-buffer (if (or (eq last-command
                                                    'bs-cycle-next)
                                                (eq last-command
                                                    'bs-cycle-previous))
                                            bs--cycle-list)))
-            (prev-buffer (car tupel))
-            (cycle-list (cdr tupel)))
+             (prev-buffer (car tuple))
+             (cycle-list (cdr tuple)))
        (switch-to-buffer prev-buffer nil t)
        (setq bs--cycle-list (append (last cycle-list)
                                     (reverse (cdr (reverse cycle-list)))))
diff --git a/lisp/buff-menu.el b/lisp/buff-menu.el
index abf152f058..aa5f70edf2 100644
--- a/lisp/buff-menu.el
+++ b/lisp/buff-menu.el
@@ -1,7 +1,6 @@
 ;;; buff-menu.el --- Interface for viewing and manipulating buffers -*- 
lexical-binding: t -*-
 
-;; Copyright (C) 1985-1987, 1993-1995, 2000-2022 Free Software
-;; Foundation, Inc.
+;; Copyright (C) 1985-2022 Free Software Foundation, Inc.
 
 ;; Maintainer: emacs-devel@gnu.org
 ;; Keywords: convenience
@@ -101,6 +100,13 @@ as it is by default."
 This is set by the prefix argument to `buffer-menu' and related
 commands.")
 
+(defvar-local Buffer-menu-filter-predicate nil
+  "Function to filter out buffers in the buffer list.
+Buffers that don't satisfy the predicate will be skipped.
+The value should be a function of one argument; it will be
+called with the buffer.  If this function returns non-nil,
+then the buffer will be displayed in the buffer list.")
+
 (defvar-keymap Buffer-menu-mode-map
   :doc "Local keymap for `Buffer-menu-mode' buffers."
   :parent tabulated-list-mode-map
@@ -133,10 +139,12 @@ commands.")
   "M-s a C-s"   #'Buffer-menu-isearch-buffers
   "M-s a C-M-s" #'Buffer-menu-isearch-buffers-regexp
   "M-s a C-o"   #'Buffer-menu-multi-occur
-
   "<mouse-2>"     #'Buffer-menu-mouse-select
   "<follow-link>" 'mouse-face)
 
+(put 'Buffer-menu-delete :advertised-binding "d")
+(put 'Buffer-menu-this-window :advertised-binding "f")
+
 (easy-menu-define Buffer-menu-mode-menu Buffer-menu-mode-map
   "Menu for `Buffer-menu-mode' buffers."
   '("Buffer-Menu"
@@ -236,6 +244,26 @@ In Buffer Menu mode, the following commands are defined:
               (lambda (&optional _noconfirm) 'fast))
   (add-hook 'tabulated-list-revert-hook 'list-buffers--refresh nil t))
 
+(defun buffer-menu--display-help ()
+  (message "%s"
+           (substitute-command-keys
+            (concat
+             "Commands: "
+             "\\<Buffer-menu-mode-map>"
+             "\\[Buffer-menu-delete], "
+             "\\[Buffer-menu-save], "
+             "\\[Buffer-menu-execute], "
+             "\\[Buffer-menu-unmark]; "
+             "\\[Buffer-menu-this-window], "
+             "\\[Buffer-menu-other-window], "
+             "\\[Buffer-menu-1-window], "
+             "\\[Buffer-menu-2-window], "
+             "\\[Buffer-menu-mark], "
+             "\\[Buffer-menu-select]; "
+             "\\[Buffer-menu-not-modified], "
+             "\\[Buffer-menu-toggle-read-only]; "
+             "\\[quit-window] to quit; \\[describe-mode] for help"))))
+
 (defun buffer-menu (&optional arg)
   "Switch to the Buffer Menu.
 By default, the Buffer Menu lists all buffers except those whose
@@ -261,8 +289,7 @@ the `Buffer-menu-name-width', `Buffer-menu-size-width' and
 `Buffer-menu-mode-width' variables."
   (interactive "P")
   (switch-to-buffer (list-buffers-noselect arg))
-  (message
-   "Commands: d, s, x, u; f, o, 1, 2, m, v; ~, %%; q to quit; ? for help."))
+  (buffer-menu--display-help))
 
 (defun buffer-menu-other-window (&optional arg)
   "Display the Buffer Menu in another window.
@@ -273,8 +300,7 @@ with a space (which are for internal use).  With prefix 
argument
 ARG, show only buffers that are visiting files."
   (interactive "P")
   (switch-to-buffer-other-window (list-buffers-noselect arg))
-  (message
-   "Commands: d, s, x, u; f, o, 1, 2, m, v; ~, %%; q to quit; ? for help."))
+  (buffer-menu--display-help))
 
 ;;;###autoload
 (defun list-buffers (&optional arg)
@@ -597,19 +623,23 @@ This behaves like invoking \\[read-only-mode] in that 
buffer."
 ;;; Functions for populating the Buffer Menu.
 
 ;;;###autoload
-(defun list-buffers-noselect (&optional files-only buffer-list)
+(defun list-buffers-noselect (&optional files-only buffer-list 
filter-predicate)
   "Create and return a Buffer Menu buffer.
 This is called by `buffer-menu' and others as a subroutine.
 
 If FILES-ONLY is non-nil, show only file-visiting buffers.
 If BUFFER-LIST is non-nil, it should be a list of buffers; it
-means list those buffers and no others."
+means list those buffers and no others.
+If FILTER-PREDICATE is non-nil, it should be a function
+that filters out buffers from the list of buffers.
+See more at `Buffer-menu-filter-predicate'."
   (let ((old-buffer (current-buffer))
        (buffer (get-buffer-create "*Buffer List*")))
     (with-current-buffer buffer
       (Buffer-menu-mode)
       (setq Buffer-menu-files-only
            (and files-only (>= (prefix-numeric-value files-only) 0)))
+      (setq Buffer-menu-filter-predicate filter-predicate)
       (list-buffers--refresh buffer-list old-buffer)
       (tabulated-list-print))
     buffer))
@@ -631,6 +661,8 @@ means list those buffers and no others."
         (marked-buffers (Buffer-menu-marked-buffers))
         (buffer-menu-buffer (current-buffer))
        (show-non-file (not Buffer-menu-files-only))
+       (filter-predicate (and (functionp Buffer-menu-filter-predicate)
+                              Buffer-menu-filter-predicate))
        entries name-width)
     ;; Collect info for each buffer we're interested in.
     (dolist (buffer (or buffer-list
@@ -644,7 +676,9 @@ means list those buffers and no others."
                         (and (or (not (string= (substring name 0 1) " "))
                                   file)
                              (not (eq buffer buffer-menu-buffer))
-                             (or file show-non-file))))
+                             (or file show-non-file)
+                             (or (not filter-predicate)
+                                 (funcall filter-predicate buffer)))))
            (push (list buffer
                        (vector (cond
                                  ((eq buffer old-buffer) ".")
diff --git a/lisp/calc/calc-graph.el b/lisp/calc/calc-graph.el
index a95967bef4..5735126bf5 100644
--- a/lisp/calc/calc-graph.el
+++ b/lisp/calc/calc-graph.el
@@ -1414,7 +1414,7 @@ This \"dumb\" driver will be present in Gnuplot 3.0."
 
 (defun calc-gnuplot-command (&rest args)
   "Send ARGS to Gnuplot.
-Returns nil if Gnuplot signalled an error."
+Returns nil if Gnuplot signaled an error."
   (calc-graph-init)
   (let ((cmd (concat (mapconcat 'identity args " ") "\n")))
     (or (calc-graph-w32-p)
diff --git a/lisp/calendar/diary-lib.el b/lisp/calendar/diary-lib.el
index 0ebfc4bb8d..9a2baf1e43 100644
--- a/lisp/calendar/diary-lib.el
+++ b/lisp/calendar/diary-lib.el
@@ -1769,7 +1769,7 @@ These functions give the date in alternative calendrical 
systems:
 `diary-islamic-date', `diary-julian-date', `diary-mayan-date',
 `diary-persian-date'
 
-Theses functions only produce output on certain dates:
+These functions only produce output on certain dates:
 
 `diary-lunar-phases'           - phases of moon (on the appropriate days)
 `diary-hebrew-omer'            - Omer count, within 50 days after Passover
diff --git a/lisp/cedet/ChangeLog.1 b/lisp/cedet/ChangeLog.1
index aa54bdd9b0..78275f4db3 100644
--- a/lisp/cedet/ChangeLog.1
+++ b/lisp/cedet/ChangeLog.1
@@ -459,7 +459,7 @@
        * semantic/scope.el (semantic-analyze-scoped-types-default): If we
        cannot find a type in the typecache, also look into the types
        we already found.  This is necessary since in C++, a 'using
-       namespace' can be dependend on a previous one.
+       namespace' can be dependent on a previous one.
        (semantic-completable-tags-from-type): When creating the list of
        completable types, pull in types which are referenced through
        'using' statements, and also preserve their filenames.
@@ -1546,7 +1546,7 @@
        (ede-proj-makefile-dependencies): Update pattern rule so that
        resulting parsers are also byte-compiled.
        (semantic-ede-grammar-compiler-bovine)
-       (semantic-ede-source-grammar-wisent): Remove .elc from gargage
+       (semantic-ede-source-grammar-wisent): Remove .elc from garbage
        pattern, since this is already covered by the elisp compiler.
        (project-compile-target): Add compatibility code for Emacs 23,
        which does not have `byte-recompile-file'.
@@ -1915,10 +1915,9 @@
 2011-05-10  Jim Meyering  <meyering@redhat.com>
 
        Fix doubled-word typos.
-       * ede/pmake.el (ede-proj-makefile-garbage-patterns): the the -> the
+       * ede/pmake.el (ede-proj-makefile-garbage-patterns):
        * semantic/complete.el (semantic-complete-read-tag-local-members):
-       Likewise.
-       * ede.el (ede-auto-add-method): then then -> then
+       * ede.el (ede-auto-add-method): Fix typos.
 
 2011-04-23  Juanma Barranquero  <lekktu@gmail.com>
 
@@ -3098,7 +3097,7 @@
        * ede/proj-prog.el (project-run-target): New method.
 
        * ede/proj-obj.el (ede-cc-linker): Rename from ede-gcc-linker.
-       (ede-g++-linker): Change Change link lines.
+       (ede-g++-linker): Change link lines.
 
        * ede/pmake.el (ede-pmake-insert-variable-shared):
        When searching for old variables, go to the end of the buffer and
diff --git a/lisp/cedet/ede/makefile-edit.el b/lisp/cedet/ede/makefile-edit.el
index 1b424bc6d0..5aaa5b2687 100644
--- a/lisp/cedet/ede/makefile-edit.el
+++ b/lisp/cedet/ede/makefile-edit.el
@@ -35,7 +35,7 @@
 ;;    SOURCE always keep in the order of .c, .h, the other stuff.
 
 ;;; Things to do
-;; makefile-fill-paragraph -- refill a macro w/ backslashes
+;; makefile-fill-paragraph -- refill a macro with backslashes
 ;; makefile-insert-macro -- insert "foo = "
 
 
diff --git a/lisp/cedet/ede/proj.el b/lisp/cedet/ede/proj.el
index 7a486754a0..398e08a1a9 100644
--- a/lisp/cedet/ede/proj.el
+++ b/lisp/cedet/ede/proj.el
@@ -43,7 +43,7 @@
 (autoload 'ede-proj-target-scheme "ede/proj-scheme"
   "Target class for a group of lisp files." nil nil)
 (autoload 'ede-proj-target-makefile-miscelaneous "ede/proj-misc"
-  "Target class for a group of miscellaneous w/ a special makefile." nil nil)
+  "Target class for a group of miscellaneous with a special makefile." nil nil)
 (autoload 'ede-proj-target-makefile-program "ede/proj-prog"
   "Target class for building a program." nil nil)
 (autoload 'ede-proj-target-makefile-archive "ede/proj-archive"
@@ -67,7 +67,7 @@
   "Target class for a group of lisp files.")
 (eieio-defclass-autoload 'ede-proj-target-makefile-miscelaneous 
'(ede-proj-target-makefile)
   "ede/proj-misc"
-  "Target class for a group of miscellaneous w/ a special makefile.")
+  "Target class for a group of miscellaneous with a special makefile.")
 (eieio-defclass-autoload 'ede-proj-target-makefile-program 
'(ede-proj-target-makefile-objectcode)
   "ede/proj-prog"
   "Target class for building a program.")
@@ -543,7 +543,7 @@ Converts all symbols into the objects to be used."
   (when (slot-exists-p obj 'compiler)
     (let ((comp (oref obj compiler)))
       (if comp
-         ;; Now that we have a pre-set compilers to use, convert tye symbols
+          ;; Now that we have a pre-set compilers to use, convert type symbols
          ;; into objects for ease of use
          (setq comp (if (listp comp)
                         (mapcar #'symbol-value comp)
diff --git a/lisp/cedet/ede/project-am.el b/lisp/cedet/ede/project-am.el
index de6936ad1a..75fde2043c 100644
--- a/lisp/cedet/ede/project-am.el
+++ b/lisp/cedet/ede/project-am.el
@@ -195,7 +195,7 @@ other meta-variable based on this name.")
   "Add the current buffer into a project.
 _FILE is ignored.
 OT is the object target.  DIR is the directory to start in."
-  (let* ((target (if ede-object (error "Already associated w/ a target")
+  (let* ((target (if ede-object (error "Already associated with a target")
                   (let ((amf (project-am-load default-directory)))
                     (if (not amf) (error "No project file"))
                     (completing-read "Target: "
@@ -231,7 +231,7 @@ OT is the object target.  DIR is the directory to start in."
   (setq ede-object nil))
 
 (cl-defmethod project-edit-file-target ((obj project-am-target))
-  "Edit the target associated w/ this file."
+  "Edit the target associated with this file."
   (find-file (concat (oref obj path) "Makefile.am"))
   (goto-char (point-min))
   (makefile-move-to-macro (project-am-macro obj))
diff --git a/lisp/cedet/semantic.el b/lisp/cedet/semantic.el
index 3166279de4..adb4705620 100644
--- a/lisp/cedet/semantic.el
+++ b/lisp/cedet/semantic.el
@@ -186,13 +186,13 @@ during a flush when the cache is given a new value of 
nil.")
   "State of the current parse tree.")
 
 (defmacro semantic-parse-tree-unparseable ()
-  "Indicate that the current buffer is unparseable.
+  "Indicate that the current buffer is unparsable.
 It is also true that the parse tree will need either updating or
 a rebuild.  This state will be changed when the user edits the buffer."
   '(setq semantic-parse-tree-state 'unparseable))
 
 (defmacro semantic-parse-tree-unparseable-p ()
-  "Return non-nil if the current buffer has been marked unparseable."
+  "Return non-nil if the current buffer has been marked unparsable."
   '(eq semantic-parse-tree-state 'unparseable))
 
 (defmacro semantic-parse-tree-set-needs-update ()
@@ -528,14 +528,14 @@ If the buffer cache is out of date, attempt an 
incremental reparse.
 If the buffer has not been parsed before, or if the incremental reparse
 fails, then parse the entire buffer.
 If a lexical error had been previously discovered and the buffer
-was marked unparseable, then do nothing, and return the cache."
+was marked unparsable, then do nothing, and return the cache."
   (and
    ;; Is this a semantic enabled buffer?
    (semantic-active-p)
    ;; Application hooks say the buffer is safe for parsing
    (run-hook-with-args-until-failure
     'semantic--before-fetch-tags-hook)
-   ;; If the buffer was previously marked unparseable,
+   ;; If the buffer was previously marked unparsable,
    ;; then don't waste our time.
    (not (semantic-parse-tree-unparseable-p))
    ;; The parse tree actually needs to be refreshed
@@ -606,7 +606,7 @@ Does nothing if the current buffer doesn't need reparsing."
   ;; do them here, then all the bovination hooks are not run, and
   ;; we save lots of time.
   (cond
-   ;; If the buffer was previously marked unparseable,
+   ;; If the buffer was previously marked unparsable,
    ;; then don't waste our time.
    ((semantic-parse-tree-unparseable-p)
     nil)
diff --git a/lisp/cedet/semantic/analyze/fcn.el 
b/lisp/cedet/semantic/analyze/fcn.el
index 7f60162115..ef372b5d8b 100644
--- a/lisp/cedet/semantic/analyze/fcn.el
+++ b/lisp/cedet/semantic/analyze/fcn.el
@@ -67,12 +67,12 @@ Return the string representing the compound name.")
   "For a SEQUENCE of tags, all with good names, pick the best one.
 If SEQUENCE is made up of namespaces, merge the namespaces together.
 If SEQUENCE has several prototypes, find the non-prototype.
-If SEQUENCE has some items w/ no type information, find the one with a type.
+If SEQUENCE has some items with no type information, find the one with a type.
 If SEQUENCE is all prototypes, or has no prototypes, get the first one.
 Optional TAGCLASS indicates to restrict the return to only
 tags of TAGCLASS."
 
-  ;; If there is a srew up and we get just one tag.. massage over it.
+  ;; If there is a screw up and we get just one tag.. massage over it.
   (when (semantic-tag-p sequence)
     (setq sequence (list sequence)))
 
diff --git a/lisp/cedet/semantic/bovine/c.el b/lisp/cedet/semantic/bovine/c.el
index d4ce20589e..5e08413a96 100644
--- a/lisp/cedet/semantic/bovine/c.el
+++ b/lisp/cedet/semantic/bovine/c.el
@@ -1344,7 +1344,7 @@ Optional argument STAR and REF indicate the number of * 
and & in the typedef."
            :reentrant-flag (if (member "reentrant" (nth 6 tokenpart)) t)
            ;; A function post-const is funky.  Try stuff
            :methodconst-flag (if (member "const" (nth 6 tokenpart)) t)
-           ;; prototypes are functions w/ no body
+            ;; prototypes are functions with no body
            :prototype-flag (if (nth 8 tokenpart) t)
            ;; Pure virtual
            :pure-virtual-flag (if (eq (nth 8 tokenpart) :pure-virtual-flag) t)
@@ -2015,7 +2015,7 @@ have to be wrapped in that namespace."
            (setq txt (concat txt (format "%S" arg)))
            (setq sv (cdr sv)))
 
-         ;; This is optional, and potentially fraught w/ errors.
+          ;; This is optional, and potentially fraught with errors.
          (condition-case nil
              (dolist (lt sv)
                (setq txt (concat txt " " (semantic-lex-token-text lt))))
diff --git a/lisp/cedet/semantic/complete.el b/lisp/cedet/semantic/complete.el
index dc270603a0..00fe081acb 100644
--- a/lisp/cedet/semantic/complete.el
+++ b/lisp/cedet/semantic/complete.el
@@ -667,7 +667,7 @@ Similar to `minibuffer-contents' when completing in the 
minibuffer."
                )
            (delete-overlay semantic-complete-inline-overlay)
            (setq semantic-complete-inline-overlay nil)
-           ;; DONT restore the window configuration if we just
+           ;; DON'T restore the window configuration if we just
            ;; switched windows!
            (when (eq buf (current-buffer))
              (set-window-configuration wc))
diff --git a/lisp/cedet/semantic/db-ebrowse.el 
b/lisp/cedet/semantic/db-ebrowse.el
index f0e1d9f029..fa608c7c46 100644
--- a/lisp/cedet/semantic/db-ebrowse.el
+++ b/lisp/cedet/semantic/db-ebrowse.el
@@ -275,7 +275,7 @@ For instance: 
/home/<username>/.semanticdb/!usr!include!BROWSE"
       (let ((ans nil)
            (efcn (symbol-function 'ebrowse-show-progress)))
         (fset 'ebrowse-show-progress (lambda (&rest _junk) nil))
-       (unwind-protect ;; Protect against errors w/ ebrowse
+        (unwind-protect ; Protect against errors with ebrowse
            (setq ans (list B (ebrowse-read)))
          ;; These items must always happen
          (erase-buffer)
diff --git a/lisp/cedet/semantic/db-find.el b/lisp/cedet/semantic/db-find.el
index 3012af41c5..e9d5aaa177 100644
--- a/lisp/cedet/semantic/db-find.el
+++ b/lisp/cedet/semantic/db-find.el
@@ -1277,7 +1277,7 @@ associated with that tag should be loaded into a buffer."
 ;;; Specialty Search Routines
 (defun semanticdb-find-tags-external-children-of-type
   (type &optional path find-file-match)
-  "Search for all tags defined outside of TYPE w/ TYPE as a parent.
+  "Search for all tags defined outside of TYPE with TYPE as a parent.
 See `semanticdb-find-translate-path' for details on PATH.
 FIND-FILE-MATCH indicates that any time a match is found, the file
 associated with that tag should be loaded into a buffer."
diff --git a/lisp/cedet/semantic/decorate/include.el 
b/lisp/cedet/semantic/decorate/include.el
index 144e2ce018..fe510c371e 100644
--- a/lisp/cedet/semantic/decorate/include.el
+++ b/lisp/cedet/semantic/decorate/include.el
@@ -330,7 +330,7 @@ This mode provides a nice context menu on the include 
statements."
        )
       ))
 
-    ;; @TODO - if not a tag w/ a position, we need to get one.  How?
+    ;; @TODO - if not a tag with a position, we need to get one.  How?
 
     (when (semantic-tag-with-position-p tag)
       (let ((ol (semantic-decorate-tag tag
diff --git a/lisp/cedet/semantic/edit.el b/lisp/cedet/semantic/edit.el
index 4efc283520..d752ecdf38 100644
--- a/lisp/cedet/semantic/edit.el
+++ b/lisp/cedet/semantic/edit.el
@@ -40,7 +40,7 @@
 ;;    of themselves that can be edited w/out affecting the definition of
 ;;    that tag.
 ;;
-;; 2. Tags w/ positioned children could have a property of an
+;; 2. Tags with positioned children could have a property of an
 ;;    overlay marking the region in themselves that contain the
 ;;    children.  This could be used to better improve splicing near
 ;;    the beginning and end of the child lists.
diff --git a/lisp/cedet/semantic/grm-wy-boot.el 
b/lisp/cedet/semantic/grm-wy-boot.el
index 376fab89c2..7fff36a3d0 100644
--- a/lisp/cedet/semantic/grm-wy-boot.el
+++ b/lisp/cedet/semantic/grm-wy-boot.el
@@ -396,12 +396,12 @@
              (let
                  ((s $1))
                (if
-                   (string-match "^{[
\n       ]*" s)
+                    (string-match "^{[\^M\n     ]*" s)
                    (setq s
                          (substring s
                                     (match-end 0))))
                (if
-                   (string-match "[
\n       ]*}$" s)
+                    (string-match "[\^M\n       ]*}$" s)
                    (setq s
                          (substring s 0
                                     (match-beginning 0))))
diff --git a/lisp/cedet/semantic/idle.el b/lisp/cedet/semantic/idle.el
index 2d6f26919d..e53dd9104a 100644
--- a/lisp/cedet/semantic/idle.el
+++ b/lisp/cedet/semantic/idle.el
@@ -396,7 +396,7 @@ Uses `semantic-idle-work-for-on-buffer' to do the work."
              (semanticdb-save-all-db-idle)
              )
 
-           ;; Done w/ processing
+            ;; Done with processing
            nil))))
 
     ;; Done
diff --git a/lisp/cedet/semantic/scope.el b/lisp/cedet/semantic/scope.el
index 83e7ed66c1..45964a1a17 100644
--- a/lisp/cedet/semantic/scope.el
+++ b/lisp/cedet/semantic/scope.el
@@ -577,7 +577,7 @@ such as `public' or `private'."
            (if (semantic-tag-file-name TAG)
                ;; If it has a filename, just go with it...
                (setq copyslots (cons TAG copyslots))
-             ;; Otherwise, copy the tag w/ the guessed filename.
+              ;; Otherwise, copy the tag with the guessed filename.
              (setq copyslots (cons (semantic-tag-copy TAG nil fname)
                                    copyslots)))
            )
@@ -822,7 +822,7 @@ hits in order, with the first tag being in the closest 
scope."
          ans)
       ;; Not a real scope.  Our scope calculation analyze parts of
       ;; what it finds, and needs to pass lists through to do it's work.
-      ;; Tread that list as a singly entry.
+      ;; Treat that list as a singly entry.
       (if class
          (semantic-find-tags-by-class class scope)
        scope)
diff --git a/lisp/cedet/semantic/symref/list.el 
b/lisp/cedet/semantic/symref/list.el
index eacbb6f1f8..1cb5d5b04a 100644
--- a/lisp/cedet/semantic/symref/list.el
+++ b/lisp/cedet/semantic/symref/list.el
@@ -51,7 +51,7 @@ Display the references in `semantic-symref-results-mode'."
   (let ((ct (semantic-current-tag)))
     ;; Must have a tag...
     (when (not ct) (error "Place cursor inside tag to be searched for"))
-    ;; Check w/ user.
+    ;; Check with user.
     (when (not (y-or-n-p (format "Find references for %s? "
                                  (semantic-tag-name ct))))
       (error "Quit"))
@@ -378,7 +378,8 @@ BUTTON is the button that was clicked."
 
 (defun semantic-symref-list-on-hit-p ()
   "Return the line number if the cursor is on a buffer line with a hit.
-Hits are the line of code from the buffer, not the tag summar or file lines."
+Hits are the line of code from the buffer, not the tag summary or
+file lines."
   (save-excursion
     (end-of-line)
     (let* ((ol (car (overlays-at (1- (point)))))) ;; trust this for now
diff --git a/lisp/cedet/semantic/tag.el b/lisp/cedet/semantic/tag.el
index 16695a108a..aa5b17e9bb 100644
--- a/lisp/cedet/semantic/tag.el
+++ b/lisp/cedet/semantic/tag.el
@@ -1176,7 +1176,7 @@ This function is for internal use only."
 (defsubst semantic--tag-expanded-p (tag)
   "Return non-nil if TAG is expanded.
 This function is for internal use only.
-See also the function `semantic--expand-tag'."
+See also the function `semantic--tag-expand'."
   ;; In fact a cooked tag is actually a list of cooked tags
   ;; because a raw tag can be expanded in several cooked ones!
   (when (consp tag)
diff --git a/lisp/cedet/semantic/util-modes.el 
b/lisp/cedet/semantic/util-modes.el
index 96d1de5a26..bcded23aec 100644
--- a/lisp/cedet/semantic/util-modes.el
+++ b/lisp/cedet/semantic/util-modes.el
@@ -133,7 +133,7 @@ symbol whose value is such a string."
                                        semantic-minor-mode-alist))))
   (semantic-mode-line-update)
 
-  ;; Semantic minor modes don't work w/ Desktop restore.
+  ;; Semantic minor modes don't work with Desktop restore.
   ;; This line will disable this minor mode from being restored
   ;; by Desktop.
   (when (boundp 'desktop-minor-mode-handlers)
diff --git a/lisp/cedet/srecode/document.el b/lisp/cedet/srecode/document.el
index a25d1441f1..c264ebaa70 100644
--- a/lisp/cedet/srecode/document.el
+++ b/lisp/cedet/srecode/document.el
@@ -29,7 +29,8 @@
 ;;
 ;;; Origins:
 ;;
-;; Document was first written w/ cparse, a custom regexp based c parser.
+;; Document was first written with cparse, a custom regexp based c
+;; parser.
 ;;
 ;; Document was then ported to cedet/semantic using sformat (super
 ;; format) as the templating engine.
diff --git a/lisp/cedet/srecode/extract.el b/lisp/cedet/srecode/extract.el
index 7d4539dcb4..f218f1c6e9 100644
--- a/lisp/cedet/srecode/extract.el
+++ b/lisp/cedet/srecode/extract.el
@@ -219,7 +219,7 @@ Return nil if nothing was extracted."
       ;; With a name, do the insertion.
       (let ((subdict (srecode-dictionary-add-section-dictionary
                      dict (oref ins object-name))))
-       (error "Need to implement include w/ name extractor")
+        (error "Need to implement include with name extractor")
        ;; Recurse into the new template while no errors.
        (while (condition-case nil
                   (progn
diff --git a/lisp/cedet/srecode/semantic.el b/lisp/cedet/srecode/semantic.el
index ea7fda004e..c5ceb89d2d 100644
--- a/lisp/cedet/srecode/semantic.el
+++ b/lisp/cedet/srecode/semantic.el
@@ -43,8 +43,8 @@
 
 ;;; The SEMANTIC TAG inserter
 ;;
-;; Put a tag into the dictionary that can be used w/ arbitrary
-;; lisp expressions.
+;; Put a tag into the dictionary that can be used with arbitrary
+;; Lisp expressions.
 
 (defclass srecode-semantic-tag (srecode-dictionary-compound-value)
   ((prime :initarg :prime
diff --git a/lisp/comint.el b/lisp/comint.el
index 07ced8d321..93b97cb22b 100644
--- a/lisp/comint.el
+++ b/lisp/comint.el
@@ -604,6 +604,14 @@ via PTYs.")
                                       menu-bar-final-items))
     map))
 
+(defvar-keymap comint-repeat-map
+  :doc "Keymap to repeat comint key sequences.  Used in `repeat-mode'."
+  "C-n" #'comint-next-prompt
+  "C-p" #'comint-previous-prompt)
+
+(put #'comint-next-prompt 'repeat-map 'comint-repeat-map)
+(put #'comint-previous-prompt 'repeat-map 'comint-repeat-map)
+
 ;; Fixme: Is this still relevant?
 (defvar comint-ptyp t
   "Non-nil if communications via pty; false if by pipe.  Buffer local.
diff --git a/lisp/cus-theme.el b/lisp/cus-theme.el
index 0260ad4a50..69ed2087a7 100644
--- a/lisp/cus-theme.el
+++ b/lisp/cus-theme.el
@@ -64,13 +64,17 @@ Do not call this mode function yourself.  It is meant for 
internal use."
   variable-pitch escape-glyph homoglyph
   minibuffer-prompt highlight region
   shadow secondary-selection trailing-whitespace
-  font-lock-builtin-face font-lock-comment-delimiter-face
-  font-lock-comment-face font-lock-constant-face
-  font-lock-doc-face font-lock-doc-markup-face font-lock-function-name-face
+  font-lock-bracket-face font-lock-builtin-face
+  font-lock-comment-delimiter-face font-lock-comment-face
+  font-lock-constant-face font-lock-delimiter-face
+  font-lock-doc-face font-lock-doc-markup-face
+  font-lock-escape-face font-lock-function-name-face
   font-lock-keyword-face font-lock-negation-char-face
-  font-lock-preprocessor-face font-lock-regexp-grouping-backslash
-  font-lock-regexp-grouping-construct font-lock-string-face
-  font-lock-type-face font-lock-variable-name-face
+  font-lock-number-face font-lock-misc-punctuation-face
+  font-lock-operator-face font-lock-preprocessor-face
+  font-lock-property-face font-lock-punctuation-face
+  font-lock-regexp-grouping-backslash font-lock-regexp-grouping-construct
+  font-lock-string-face font-lock-type-face font-lock-variable-name-face
   font-lock-warning-face button link link-visited fringe
   header-line tooltip mode-line mode-line-buffer-id
   mode-line-emphasis mode-line-highlight mode-line-inactive
diff --git a/lisp/dired.el b/lisp/dired.el
index 8995c48df7..81e62f88cf 100644
--- a/lisp/dired.el
+++ b/lisp/dired.el
@@ -1541,7 +1541,7 @@ BEG..END is the line where the file info is located."
            (when (< alt-col other-col)
              (setq other-col alt-col)
              (setq other (point)))))
-       ;; Keep positions uptodate when we insert stuff.
+       ;; Keep positions up-to-date when we insert stuff.
        (if (> other file) (setq other (copy-marker other)))
        (setq file (copy-marker file))
        ;; Main loop.
diff --git a/lisp/dnd.el b/lisp/dnd.el
index b2e93a63de..7708695346 100644
--- a/lisp/dnd.el
+++ b/lisp/dnd.el
@@ -325,7 +325,7 @@ in that list instead."
 
 (defun dnd-begin-text-drag (text &optional frame action allow-same-frame)
   "Begin dragging TEXT from FRAME.
-Initate a drag-and-drop operation allowing the user to drag text
+Initiate a drag-and-drop operation allowing the user to drag text
 from Emacs to another program (the drop target), then block until
 the drop is completed or is canceled.
 
@@ -381,7 +381,7 @@ currently being held down.  It should only be called upon a
 
 (defun dnd-begin-file-drag (file &optional frame action allow-same-frame)
   "Begin dragging FILE from FRAME.
-Initate a drag-and-drop operation allowing the user to drag a file
+Initiate a drag-and-drop operation allowing the user to drag a file
 from Emacs to another program (the drop target), then block until
 the drop happens or is canceled.
 
diff --git a/lisp/dynamic-setting.el b/lisp/dynamic-setting.el
index 8ac9a1e9e6..ee6d1ceb35 100644
--- a/lisp/dynamic-setting.el
+++ b/lisp/dynamic-setting.el
@@ -51,19 +51,11 @@ the current form for the frame (i.e. hinting or somesuch 
changed)."
          ;; Set the font on all current and future frames, as though
          ;; the `default' face had been "set for this session":
          (set-frame-font new-font nil frame-list)
-       ;; Just redraw the existing fonts on all frames:
-       (dolist (f frame-list)
-         (let ((frame-font
-                (or (font-get (face-attribute 'default :font f 'default)
-                              :user-spec)
-                    (frame-parameter f 'font-parameter))))
-           (when frame-font
-             (set-frame-parameter f 'font-parameter frame-font)
-             (set-face-attribute 'default f
-                                 :width 'normal
-                                 :weight 'normal
-                                 :slant 'normal
-                                 :font frame-font))))))))
+       ;; Just reconsider the existing fonts on all frames on each
+       ;; display, by clearing the font and face caches.  This will
+       ;; cause all fonts to be recreated.
+        (dolist (frame frame-list)
+          (reconsider-frame-fonts frame))))))
 
 (defun dynamic-setting-handle-config-changed-event (event)
   "Handle config-changed-event on the display in EVENT.
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index 39a7739dc5..3df983b367 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -1941,11 +1941,10 @@ 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)))
                       ;; Directory is requested to be ignored
-                      (string-match-p
-                       (regexp-opt byte-compile-ignore-files)
-                       source)
+                      (not (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)
@@ -3639,7 +3638,7 @@ lambda-expression."
     (byte-compile-out base-op tmp)))
 
 (defun byte-compile-dynamic-variable-bind (var)
-  "Generate code to bind the lexical variable VAR to the top-of-stack value."
+  "Generate code to bind the dynamic variable VAR to the top-of-stack value."
   (byte-compile-check-variable var 'let-bind)
   (push var byte-compile-bound-variables)
   (byte-compile-dynamic-variable-op 'byte-varbind var))
diff --git a/lisp/emacs-lisp/checkdoc.el b/lisp/emacs-lisp/checkdoc.el
index 3f9bc28e0b..3bddb93b64 100644
--- a/lisp/emacs-lisp/checkdoc.el
+++ b/lisp/emacs-lisp/checkdoc.el
@@ -2265,8 +2265,8 @@ buffer, otherwise stop after the first error."
             (unless (and sym (or (boundp sym) (fboundp sym)))
               ;; Find out how we spell-check this word.
               (unless (or
-                       ;; All caps w/ option th, or s tacked on the end
-                       ;; for pluralization or number.
+                       ;; All caps with option th, or s tacked on the
+                       ;; end for pluralization or number.
                        (string-match "^[A-Z][A-Z]+\\(s\\|th\\)?$" word)
                        (looking-at "}") ; a keymap expression
                        )
diff --git a/lisp/emacs-lisp/cl-preloaded.el b/lisp/emacs-lisp/cl-preloaded.el
index 94f9654b23..dbe20f9202 100644
--- a/lisp/emacs-lisp/cl-preloaded.el
+++ b/lisp/emacs-lisp/cl-preloaded.el
@@ -78,6 +78,9 @@
     (font-spec atom) (font-entity atom) (font-object atom)
     (vector array sequence atom)
     (user-ptr atom)
+    (tree-sitter-parser atom)
+    (tree-sitter-node atom)
+    (tree-sitter-compiled-query atom)
     ;; Plus, really hand made:
     (null symbol list sequence atom))
   "Alist of supertypes.
diff --git a/lisp/emacs-lisp/comp.el b/lisp/emacs-lisp/comp.el
index 863e895efd..0ee094c34d 100644
--- a/lisp/emacs-lisp/comp.el
+++ b/lisp/emacs-lisp/comp.el
@@ -2823,7 +2823,7 @@ blocks."
             (first-processed (l)
               (if-let ((p (cl-find-if (lambda (p) (comp-block-idom p)) l)))
                   p
-                (signal 'native-ice "cant't find first preprocessed"))))
+                (signal 'native-ice "can't find first preprocessed"))))
 
     (when-let ((blocks (comp-func-blocks comp-func))
                (entry (gethash 'entry blocks))
@@ -4358,6 +4358,6 @@ of (commands) to run simultaneously."
 
 (provide 'comp)
 
-;; LocalWords: limplified limplified limplification limplify Limple LIMPLE 
libgccjit elc eln
+;; LocalWords: limplified limplification limplify Limple LIMPLE libgccjit elc 
eln
 
 ;;; comp.el ends here
diff --git a/lisp/emacs-lisp/ert-x.el b/lisp/emacs-lisp/ert-x.el
index a891f068a7..49f2a1d696 100644
--- a/lisp/emacs-lisp/ert-x.el
+++ b/lisp/emacs-lisp/ert-x.el
@@ -560,6 +560,7 @@ The same keyword arguments are supported as in
          '("mock"
           (tramp-login-program      "sh")
           (tramp-login-args         (("-i")))
+           (tramp-direct-async       ("-c"))
           (tramp-remote-shell       "/bin/sh")
           (tramp-remote-shell-args  ("-c"))
           (tramp-connection-timeout 10)))
diff --git a/lisp/emacs-lisp/ert.el b/lisp/emacs-lisp/ert.el
index 047b0069bb..c25ade22d6 100644
--- a/lisp/emacs-lisp/ert.el
+++ b/lisp/emacs-lisp/ert.el
@@ -208,7 +208,7 @@ is run.  If a macro (possibly with side effects) is to be 
tested,
 it has to be wrapped in `(eval (quote ...))'.
 
 If NAME is already defined as a test and Emacs is running
-in batch mode, an error is signalled.
+in batch mode, an error is signaled.
 
 \(fn NAME () [DOCSTRING] [:expected-result RESULT-TYPE] \
 [:tags \\='(TAG...)] BODY...)"
diff --git a/lisp/emacs-lisp/hierarchy.el b/lisp/emacs-lisp/hierarchy.el
index fb5d518b22..d955019a9d 100644
--- a/lisp/emacs-lisp/hierarchy.el
+++ b/lisp/emacs-lisp/hierarchy.el
@@ -191,7 +191,7 @@ PARENTFN, CHILDRENFN, ACCEPTFN, and DELAY-CHILDREN-P have 
the same meaning as in
 (defun hierarchy-add-list (hierarchy list &optional wrap childrenfn)
   "Add to HIERARCHY the sub-lists in LIST.
 
-If WRAP is non-nil, allow duplicate items in LIST by wraping each
+If WRAP is non-nil, allow duplicate items in LIST by wrapping each
 item in a cons (id . item).  The root's id is 1.
 
 CHILDRENFN is a function (defaults to `cdr') taking LIST as a
diff --git a/lisp/emacs-lisp/lisp.el b/lisp/emacs-lisp/lisp.el
index acae1a0b0a..c8d05a084b 100644
--- a/lisp/emacs-lisp/lisp.el
+++ b/lisp/emacs-lisp/lisp.el
@@ -375,7 +375,10 @@ does not move to the beginning of the line when 
`defun-prompt-regexp'
 is non-nil.
 
 If variable `beginning-of-defun-function' is non-nil, its value
-is called as a function to find the defun's beginning."
+is called as a function to find the defun's beginning.
+
+Return non-nil if this function successfully found the beginning
+of a defun, nil if it failed to find one."
   (interactive "^p")   ; change this to "P", maybe, if we ever come to pass ARG
                       ; to beginning-of-defun-function.
   (unless arg (setq arg 1))
@@ -543,6 +546,7 @@ report errors as appropriate for this kind of usage."
         (push-mark))
     (if (or (null arg) (= arg 0)) (setq arg 1))
     (let ((pos (point))
+          (success nil)
           (beg (progn (when end-of-defun-moves-to-eol
                         (end-of-line 1))
                       (beginning-of-defun-raw 1) (point)))
@@ -567,9 +571,12 @@ report errors as appropriate for this kind of usage."
             (setq arg (1- arg))
           ;; We started from after the end of the previous function.
           (goto-char pos))
+        ;; At this point, point either didn't move (because we started
+        ;; in between two defun's), or is at the end of a defun
+        ;; (because we started in the middle of a defun).
         (unless (zerop arg)
-          (beginning-of-defun-raw (- arg))
-          (funcall end-of-defun-function)))
+          (when (setq success (beginning-of-defun-raw (- arg)))
+            (funcall end-of-defun-function))))
        ((< arg 0)
         ;; Moving backward.
         (if (< (point) pos)
@@ -579,16 +586,18 @@ report errors as appropriate for this kind of usage."
           ;; We started from inside a function.
           (goto-char beg))
         (unless (zerop arg)
-          (beginning-of-defun-raw (- arg))
-         (setq beg (point))
-          (funcall end-of-defun-function))))
+          (when (setq success (beginning-of-defun-raw (- arg)))
+            (setq beg (point))
+            (funcall end-of-defun-function)))))
       (funcall skip)
-      (while (and (< arg 0) (>= (point) pos))
+      (while (and (< arg 0) (>= (point) pos) success)
         ;; We intended to move backward, but this ended up not doing so:
         ;; Try harder!
         (goto-char beg)
-        (beginning-of-defun-raw (- arg))
-        (if (>= (point) beg)
+        (setq success (beginning-of-defun-raw (- arg)))
+        ;; If we successfully moved pass point, or there is no further
+        ;; defun beginnings anymore, stop.
+        (if (or (>= (point) beg) (not success))
            (setq arg 0)
          (setq beg (point))
           (funcall end-of-defun-function)
diff --git a/lisp/emacs-lisp/package-vc.el b/lisp/emacs-lisp/package-vc.el
index a0b4b03118..a999596785 100644
--- a/lisp/emacs-lisp/package-vc.el
+++ b/lisp/emacs-lisp/package-vc.el
@@ -39,6 +39,9 @@
 
 ;; - Allow maintaining patches that are ported back onto regular
 ;;   packages and maintained between versions.
+;;
+;; - Add a heuristic for guessing a `:lisp-dir' when cloning directly
+;;  from a URL.
 
 ;;; Code:
 
@@ -49,7 +52,6 @@
 (require 'lisp-mnt)
 (require 'vc)
 (require 'seq)
-(require 'xdg)
 
 (defgroup package-vc nil
   "Manage packages from VC checkouts."
@@ -100,12 +102,6 @@
                                               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
@@ -118,13 +114,16 @@ the `clone' function."
   :version "29.1")
 
 (defvar package-vc-selected-packages) ; pacify byte-compiler
-(defun package-vc-ensure-packages ()
+
+;;;###autoload
+(defun package-vc-install-selected-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))
+  (interactive)
+  (pcase-dolist (`(,name . ,spec) package-vc-selected-packages)
+    (when (stringp name)
+      (setq name (intern name)))
+    (let ((pkg-descs (assoc name package-alist #'string=)))
+      (unless (seq-some #'package-vc-p (cdr pkg-descs))
         (cond
          ((null spec)
           (package-vc-install name))
@@ -132,7 +131,7 @@ the `clone' function."
           (package-vc-install name nil spec))
          ((listp spec)
           (package-vc--archives-initialize)
-          (package-vc--unpack pkg-desc spec)))))))
+          (package-vc--unpack (cadr pkg-descs) spec)))))))
 
 ;;;###autoload
 (defcustom package-vc-selected-packages '()
@@ -142,13 +141,40 @@ 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.
+- a property list, describing a package specification.  Valid
+  key/value pairs 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 keys are ignored.
 
 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."
+it is meant to be specified manually.  If you want to install all
+the packages in the list, you cal also use
+`package-vc-install-selected-packages'.
+
+Note that this option will not override an existing source
+package installation or revert the checked out revision."
   :type '(alist :tag "List of packages you want to be installed"
                 :key-type (symbol :tag "Package")
                 :value-type
@@ -159,39 +185,16 @@ function `package-vc-selected-packages' to apply the 
changes."
                                          (:lisp-dir string)
                                          (:main-file string)
                                          (:vc-backend symbol)))))
+  :initialize #'custom-initialize-default
   :set (lambda (sym val)
          (custom-set-default sym val)
-         (package-vc-ensure-packages))
+         (package-vc-install-selected-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.")
+The list maps each package name, as a string, to a plist as
+specified in `package-vc-selected-packages'.")
 
 (defvar package-vc--archive-data-alist nil
   "List of package specification metadata for archives.
@@ -219,7 +222,7 @@ name for 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)))
+     (apply #'append (mapcar #'cdr package-vc--archive-spec-alist)))
    nil nil #'string=))
 
 (define-inline package-vc--query-spec (pkg-desc prop)
@@ -231,7 +234,7 @@ return nil."
 
 (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'."
+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)))
@@ -268,7 +271,6 @@ asynchronously."
       (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."
@@ -282,7 +284,7 @@ asynchronously."
            finally return "unknown"))
 
 (defun package-vc--version (pkg)
-  "Extract the commit of a development package PKG."
+  "Return the version number for the source package PKG."
   (cl-assert (package-vc-p pkg))
   (if-let ((main-file (package-vc--main-file pkg)))
       (with-temp-buffer
@@ -295,15 +297,14 @@ asynchronously."
 (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)))
+  (let ((pkg-spec (package-vc--desc->spec pkg-desc))
+        (name (symbol-name (package-desc-name pkg-desc))))
     (or (plist-get pkg-spec :main-file)
         (expand-file-name
-         (format "%s.el" (package-desc-name pkg-desc))
+         (concat name ".el")
          (file-name-concat
           (or (package-desc-dir pkg-desc)
-              (expand-file-name
-               (package-desc-name pkg-desc)
-               package-user-dir))
+              (expand-file-name name package-user-dir))
           (plist-get pkg-spec :lisp-dir))))))
 
 (defun package-vc--generate-description-file (pkg-desc pkg-file)
@@ -351,26 +352,51 @@ asynchronously."
 (declare-function org-export-to-file "ox" (backend file))
 
 (defun package-vc--build-documentation (pkg-desc file)
-  "Build documentation FILE for PKG-DESC.
+  "Build documentation for package PKG-DESC from documentation source in FILE.
 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)))
+  (let* ((pkg-name (package-desc-name pkg-desc))
+         (default-directory (package-desc-dir pkg-desc))
+         (output (expand-file-name (format "%s.info" pkg-name)))
+         clean-up)
     (when (string-match-p "\\.org\\'" file)
       (require 'ox)
       (require 'ox-texinfo)
       (with-temp-buffer
         (insert-file-contents file)
         (setq file (make-temp-file "ox-texinfo-"))
-        (org-export-to-file 'texinfo file)))
-    (call-process "install-info" nil nil nil
-                  file pkg-dir)))
+        (org-export-to-file 'texinfo file)
+        (setq clean-up t)))
+    (with-current-buffer (get-buffer-create " *package-vc doc*")
+      (erase-buffer)
+      (cond
+       ((/= 0 (call-process "makeinfo" nil t nil
+                            "--no-split" file "-o" output))
+        (message "Failed to build manual %s, see buffer %S"
+                 file (buffer-name)))
+       ((/= 0 (call-process "install-info" nil t nil
+                            output (expand-file-name "dir")))
+        (message "Failed to install manual %s, see buffer %S"
+                 output (buffer-name)))
+       ((kill-buffer))))
+    (when clean-up
+      (delete-file file))))
 
 (defun package-vc--unpack-1 (pkg-desc pkg-dir)
-  "Install PKG-DESC that is already checked-out in PKG-DIR."
+  "Prepare PKG-DESC that is already checked-out in PKG-DIR.
+This includes downloading missing dependencies, generating
+autoloads, generating a package description file (used to
+identify a package as a source package later on), building
+documentation and marking the package as installed."
+  ;; Remove any previous instance of PKG-DESC from `package-alist'
+  (let ((pkgs (assq (package-desc-name pkg-desc) package-alist)))
+    (when pkgs
+      (setf (cdr pkgs) (seq-remove #'package-vc-p (cdr pkgs)))))
+
   ;; 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)
+  (let ((deps '()))
     (dolist (file (directory-files pkg-dir t "\\.el\\'" t))
       (with-temp-buffer
         (insert-file-contents file)
@@ -387,10 +413,26 @@ otherwise it's assumed to be an Info file."
      (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)
+    (let* ((name (package-desc-name pkg-desc))
+           (auto-name (format "%s-autoloads.el" name))
+           (extras (package-desc-extras pkg-desc))
+           (lisp-dir (alist-get :lisp-dir extras)))
+      (package-generate-autoloads
+       name (file-name-concat pkg-dir lisp-dir))
+      (when lisp-dir
+        (write-region
+         (with-temp-buffer
+           (insert ";; Autoload indirection for package-vc\n\n")
+           (prin1 `(load (expand-file-name
+                          ,(file-name-concat lisp-dir auto-name)
+                          (or (and load-file-name
+                                   (file-name-directory load-file-name))
+                              (car load-path))))
+                  (current-buffer))
+           (buffer-string))
+         nil (expand-file-name auto-name pkg-dir))))
 
     ;; Generate package file
     (package-vc--generate-description-file pkg-desc pkg-file)
@@ -474,48 +516,66 @@ 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)
+  (pcase-let* (((map :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)))))
+               (pkg-dir (expand-file-name dirname package-user-dir)))
     (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)
+          (package--delete-directory pkg-dir)
         (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--clone pkg-desc pkg-spec pkg-dir rev)
 
+    (when lisp-dir
+      (push (cons :lisp-dir lisp-dir)
+            (package-desc-extras pkg-desc)))
     (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--read-package-name (prompt &optional allow-url installed)
+  "Query the user for a source package and return a name with PROMPT.
+If the optional argument ALLOW-URL is non-nil, the user is also
+allowed to specify a non-package name.  If the optional argument
+INSTALLED is non-nil, the selection will be filtered down to
+source packages that have already been installed."
+  (package-vc--archives-initialize)
+  (completing-read prompt (if installed package-alist package-archive-contents)
+                   (if installed
+                       (lambda (pkg) (package-vc-p (cadr pkg)))
+                     (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)))))))
+                   (not allow-url)))
+
+(defun package-vc--read-package-desc (prompt &optional installed)
+  "Query the user for a source package and return a description with PROMPT.
+If the optional argument INSTALLED is non-nil, the selection will
+be filtered down to source packages that have already been
+installed, and the package description will be that of an
+installed package."
+  (cadr (assoc (package-vc--read-package-name prompt nil installed)
+               (if installed package-alist package-archive-contents)
+               #'string=)))
+
+;;;###autoload
+(defun package-vc-update-all ()
+  "Attempt to update all installed VC packages."
+  (interactive)
+  (dolist (package package-alist)
+    (dolist (pkg-desc (cdr package))
+      (when (package-vc-p pkg-desc)
+        (package-vc-update pkg-desc))))
+  (message "Done updating packages."))
 
+;;;###autoload
 (defun package-vc-update (pkg-desc)
   "Attempt to update the package PKG-DESC."
+  (interactive (list (package-vc--read-package-desc "Update source package: " 
t)))
   ;; 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
@@ -528,26 +588,25 @@ checkout.  This overrides the `:branch' attribute in 
PKG-SPEC."
   ;; `package-vc--unpack-1'.  Ugh...
   ;;
   ;; If there is a better way to do this, it should be done.
+  (cl-assert (package-vc-p pkg-desc))
   (letrec ((pkg-dir (package-desc-dir pkg-desc))
-           (empty (make-symbol empty))
-           (args (list empty empty empty))
+           (vc-flags)
            (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)
+              (setq vc-flags 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)))))
+            (lambda (_command _file-or-list flags)
+              (when (and (file-equal-p pkg-dir default-directory)
+                         (eq flags vc-flags))
+                (unwind-protect
+                    (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))))
+      (let ((default-directory pkg-dir))
+        (vc-pull)))))
 
 (defun package-vc--archives-initialize ()
   "Initialize package.el and fetch package specifications."
@@ -577,46 +636,60 @@ If no such revision can be found, return nil."
                              (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.
+(defun package-vc-install (package &optional name rev backend)
+  "Fetch a PACKAGE and set it up for using with Emacs.
+
+If PACKAGE is a string containing an URL, download the package
+from the repository at that URL; the function will try to guess
+the name of the package from the URL.  This can be overridden by
+passing the optional argument NAME.  If PACKAGE is a cons-cell,
+it should have the form (NAME . SPEC), where NAME is a symbol
+indicating the package name and SPEC is a plist as described in
+`package-vc-selected-packages'.  Otherwise PACKAGE 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."
+uses `package-vc-heuristic-alist' to guess the backend.
+Note that by default, a source package will be prioritized over a
+regular package, but it will not remove a source 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))
-            (name (file-name-base input)))
-       (list input (intern (string-remove-prefix "emacs-" name))
+     (let* ((name-or-url (package-vc--read-package-name
+                          "Fetch and install package: " t))
+            (name (file-name-base name-or-url)))
+       (list name-or-url (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))))
+   ((null package)
+    (signal 'wrong-type-argument nil))
+   ((consp package)
+    (package-vc--unpack
+     (package-desc-create :name (car package)
+                          :kind 'vc)
+     (cdr package)
+     rev))
+   ((and-let* (((stringp package))
+               (backend (or backend (package-vc--guess-backend package))))
       (package-vc--unpack
        (package-desc-create
-        :name (or name (intern (file-name-base name-or-url)))
+        :name (or name (intern (file-name-base package)))
         :kind 'vc)
-       (list :vc-backend backend :url name-or-url)
+       (list :vc-backend backend :url package)
        rev)))
-   ((and-let* ((desc (assoc name-or-url package-archive-contents #'string=)))
+   ((and-let* ((desc (assoc package package-archive-contents #'string=)))
       (package-vc--unpack
        (let ((copy (copy-package-desc (cadr desc))))
          (setf (package-desc-kind copy) 'vc)
@@ -626,9 +699,9 @@ uses `package-vc--guess-backend' to guess the backend."
                       (url (alist-get :url extras))
                       (backend (package-vc--guess-backend url)))
              (list :vc-backend backend :url url))
-           (user-error "Package has no VC data"))
+           (user-error "Package `%s' has no VC data" package))
        rev)))
-   ((user-error "Unknown package to fetch: %s" name-or-url))))
+   ((user-error "Unknown package to fetch: %s" package))))
 
 ;;;###autoload
 (defun package-vc-checkout (pkg-desc directory &optional rev)
@@ -642,25 +715,20 @@ 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)))))
+   (let* ((name (package-vc--read-package-name "Fetch package source: ")))
+     (list (cadr (assoc name 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"))))
+                      (user-error "Package `%s' has no VC data"
+                                  (package-desc-name pkg-desc)))))
     (package-vc--clone pkg-desc pkg-spec directory rev)
     (find-file directory)))
 
@@ -682,45 +750,42 @@ name from the base name of 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)))
+    (make-symbolic-link (expand-file-name dir) pkg-dir)
+    (package-vc--unpack-1
+     (package-desc-create
+      :name (intern name)
+      :kind 'vc)
+     (file-name-as-directory 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: "))
+(defun package-vc-rebuild (pkg-desc)
+  "Rebuild the installation for package given by PKG-DESC.
+Rebuilding an installation means scraping for new autoload
+cookies, re-compiling Emacs Lisp files, building and installing
+any documentation, downloading any missing dependencies.  This
+command does not fetch new revisions from a remote server.  That
+is the responsibility of `package-vc-update'.  Interactively,
+prompt for the name of the package to rebuild."
+  (interactive (list (package-vc--read-package-desc "Rebuild package: " t)))
   (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)
+(defun package-vc-prepare-patch (pkg-desc 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'."
+The function uses `vc-prepare-patch', passing SUBJECT and
+REVISIONS directly.  PKG-DESC must be a package description.
+Interactively, prompt for PKG-DESC, SUBJECT, and REVISIONS.  When
+invoked with a numerical prefix argument, use the last N
+revisions.  When invoked interactively in a Log View buffer with
+marked revisions, use those."
   (interactive
-   (list (package-vc--read-pkg "Package to prepare a patch for: ")
+   (list (package-vc--read-package-desc "Package to prepare a patch for: " t)
          (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))
+         (vc-prepare-patch-prompt-revisions)))
+  (let ((default-directory (package-desc-dir pkg-desc)))
+    (vc-prepare-patch (package-maintainers pkg-desc 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 a7bcdd214c..c1545a2870 100644
--- a/lisp/emacs-lisp/package.el
+++ b/lisp/emacs-lisp/package.el
@@ -1090,10 +1090,15 @@ untar into a directory named DIR; otherwise, signal an 
error."
          (backup-inhibited t)
          (version-control 'never))
     (loaddefs-generate
-     pkg-dir output-file
-     nil
-     "(add-to-list 'load-path (directory-file-name
-                         (or (file-name-directory #$) (car load-path))))")
+     pkg-dir output-file nil
+     (prin1-to-string
+      '(add-to-list
+        'load-path
+        ;; Add the directory that will contain the autoload file to
+        ;; the load path.  We don't hard-code `pkg-dir', to avoid
+        ;; issues if the package directory is moved around.
+        (or (and load-file-name (file-name-directory load-file-name))
+            (car load-path)))))
     (let ((buf (find-buffer-visiting output-file)))
       (when buf (kill-buffer buf)))
     auto-name))
@@ -1358,10 +1363,7 @@ is non-nil, don't propagate connection errors (does not 
apply to
 errors signaled by ERROR-FORM or by BODY).
 
 \(fn URL &key ASYNC FILE ERROR-FORM NOERROR &rest BODY)"
-  (declare (indent defun)
-           ;; FIXME: This should be something like
-           ;; `form def-body &rest form', but that doesn't work.
-           (debug (form &rest sexp)))
+  (declare (indent defun) (debug (sexp body)))
   (while (keywordp (car body))
     (setq body (cdr (cdr body))))
   `(package--with-response-buffer-1 ,url (lambda () ,@body)
@@ -1785,7 +1787,7 @@ 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)
+  (when async
     (cl-pushnew (cons archive file) package--downloads-in-progress
                 :test #'equal))
   (package--with-response-buffer (cdr archive) :file file
@@ -2419,7 +2421,7 @@ installed), maybe you need to 
\\[package-refresh-contents]")
 
 (declare-function comp-el-to-eln-filename "comp.c")
 (defvar package-vc-repository-store)
-(defun package--delete-directory (dir pkg-desc)
+(defun package--delete-directory (dir)
   "Delete PKG-DESC directory DIR recursively.
 Clean-up the corresponding .eln files if Emacs is native
 compiled."
@@ -2427,17 +2429,8 @@ compiled."
     (cl-loop
      for file in (directory-files-recursively dir "\\.el\\'")
      do (comp-clean-up-stale-eln (comp-el-to-eln-filename file))))
-  (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)))
+  (if (file-symlink-p (directory-file-name dir))
+      (delete-file (directory-file-name dir))
     (delete-directory dir t)))
 
 
@@ -2493,7 +2486,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 pkg-desc)
+           (package--delete-directory dir)
            ;; Remove NAME-VERSION.signed and NAME-readme.txt files.
            ;;
            ;; NAME-readme.txt files are no longer created, but they
@@ -4539,19 +4532,28 @@ DESC must be a `package-desc' object."
         (funcall browse-url-secondary-browser-function url)
       (browse-url url))))
 
+(declare-function ietf-drums-parse-address "ietf-drums"
+                  (string &optional decode))
+
 (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))
+signaled.  If the optional argument NO-ERROR is non-nil no error
+will be signaled in that case."
+  (unless (package-desc-p pkg-desc)
+    (error "Invalid package description: %S" pkg-desc))
+  (let* ((name (package-desc-name pkg-desc))
+         (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"))
+      (user-error "Package `%s' has no explicit maintainer" name))
+     ((and (not (progn
+                  (require 'ietf-drums)
+                  (ietf-drums-parse-address maint)))
+           (null no-error))
+      (user-error "Package `%s' has no maintainer address" name))
      ((not (null maint))
       (with-temp-buffer
         (package--print-email-button maint)
diff --git a/lisp/emacs-lisp/rmc.el b/lisp/emacs-lisp/rmc.el
index dae6590b9b..1083a6868b 100644
--- a/lisp/emacs-lisp/rmc.el
+++ b/lisp/emacs-lisp/rmc.el
@@ -125,7 +125,7 @@
 ;;;###autoload
 (defun read-multiple-choice (prompt choices &optional help-string show-help
                                     long-form)
-  "Ask user to select an entry from CHOICES, promting with PROMPT.
+  "Ask user to select an entry from CHOICES, prompting with PROMPT.
 This function allows to ask the user a multiple-choice question.
 
 CHOICES should be a list of the form (KEY NAME [DESCRIPTION]).
diff --git a/lisp/emacs-lisp/seq.el b/lisp/emacs-lisp/seq.el
index 82ade0ac0c..1645da2eb0 100644
--- a/lisp/emacs-lisp/seq.el
+++ b/lisp/emacs-lisp/seq.el
@@ -63,8 +63,7 @@
 ;; preloaded.  See also Bug#39761#26.
 
 (defmacro seq-doseq (spec &rest body)
-  "Loop over a sequence.
-Evaluate BODY with VAR bound to each element of SEQUENCE, in turn.
+  "Loop over a SEQUENCE, evaluating BODY with VAR bound to each of its 
elements.
 
 Similar to `dolist' but can be applied to lists, strings, and vectors.
 
@@ -95,7 +94,7 @@ name to be bound to the rest of SEQUENCE."
      ,@body))
 
 (defmacro seq-setq (args sequence)
-  "Assign to the variables in ARGS the elements of SEQUENCE.
+  "Assign the elements of SEQUENCE to the variables in ARGS.
 
 ARGS can also include the `&rest' marker followed by a variable
 name to be bound to the rest of SEQUENCE."
@@ -105,7 +104,7 @@ name to be bound to the rest of SEQUENCE."
 
 ;;; Basic seq functions that have to be implemented by new sequence types
 (cl-defgeneric seq-elt (sequence n)
-  "Return Nth element of SEQUENCE."
+  "Return the Nth element of SEQUENCE."
   (elt sequence n))
 
 ;; Default gv setters for `seq-elt'.
@@ -118,7 +117,7 @@ name to be bound to the rest of SEQUENCE."
   (setcar (nthcdr n sequence) store))
 
 (cl-defgeneric seq-length (sequence)
-  "Return the number of elements of SEQUENCE."
+  "Return the number of elements in SEQUENCE."
   (length sequence))
 
 (defun seq-first (sequence)
@@ -126,11 +125,12 @@ name to be bound to the rest of SEQUENCE."
   (seq-elt sequence 0))
 
 (defun seq-rest (sequence)
-  "Return a sequence of the elements of SEQUENCE except the first one."
+  "Return SEQUENCE with its first element removed."
   (seq-drop sequence 1))
 
 (cl-defgeneric seq-do (function sequence)
-  "Apply FUNCTION to each element of SEQUENCE, presumably for side effects.
+  "Apply FUNCTION to each element of SEQUENCE.
+Presumably, FUNCTION has useful side effects.
 Return SEQUENCE."
   (mapc function sequence))
 
@@ -216,8 +216,9 @@ the sequence, and its index within the sequence."
   (mapcar function sequence))
 
 (cl-defgeneric seq-mapn (function sequence &rest sequences)
-  "Like `seq-map' but FUNCTION is mapped over all SEQUENCES.
-The arity of FUNCTION must match the number of SEQUENCES, and the
+  "Return the result of applying FUNCTION to each element of SEQUENCEs.
+Like `seq-map', but FUNCTION is mapped over all SEQUENCEs.
+The arity of FUNCTION must match the number of SEQUENCEs, and the
 mapping stops on the shortest sequence.
 Return a list of the results.
 
@@ -232,7 +233,7 @@ Return a list of the results.
     (nreverse result)))
 
 (cl-defgeneric seq-drop (sequence n)
-  "Remove the first N elements of SEQUENCE and return the result.
+  "Remove the first N elements of SEQUENCE and return the resulting sequence.
 The result is a sequence of the same type as SEQUENCE.
 
 If N is a negative integer or zero, SEQUENCE is returned."
@@ -243,7 +244,7 @@ If N is a negative integer or zero, SEQUENCE is returned."
 
 ;;;###autoload
 (cl-defgeneric seq-take (sequence n)
-  "Take the first N elements of SEQUENCE and return the result.
+  "Return the sequence made of the first N elements of SEQUENCE.
 The result is a sequence of the same type as SEQUENCE.
 
 If N is a negative integer or zero, an empty sequence is
@@ -252,14 +253,17 @@ returned."
 
 (cl-defgeneric seq-drop-while (pred sequence)
   "Remove the successive elements of SEQUENCE for which PRED returns non-nil.
-PRED is a function of one argument.  The result is a sequence of
-the same type as SEQUENCE."
+PRED is a function of one argument.  The function keeps removing
+elements from SEQUENCE until PRED returns nil for an element.
+Value is a sequence of the same type as SEQUENCE."
   (seq-drop sequence (seq--count-successive pred sequence)))
 
 (cl-defgeneric seq-take-while (pred sequence)
   "Take the successive elements of SEQUENCE for which PRED returns non-nil.
-PRED is a function of one argument.  The result is a sequence of
-the same type as SEQUENCE."
+PRED is a function of one argument.  The function keeps collecting
+elements from SEQUENCE and adding them to the result until PRED
+returns nil for an element.
+Value is a sequence of the same type as SEQUENCE."
   (seq-take sequence (seq--count-successive pred sequence)))
 
 (cl-defgeneric seq-empty-p (sequence)
@@ -267,7 +271,7 @@ the same type as SEQUENCE."
   (= 0 (seq-length sequence)))
 
 (cl-defgeneric seq-sort (pred sequence)
-  "Sort SEQUENCE using PRED as comparison function.
+  "Sort SEQUENCE using PRED as the sorting comparison function.
 The result is a sequence of the same type as SEQUENCE."
   (let ((result (seq-sort pred (append sequence nil))))
     (seq-into result (type-of sequence))))
@@ -277,7 +281,7 @@ The result is a sequence of the same type as SEQUENCE."
 
 ;;;###autoload
 (defun seq-sort-by (function pred sequence)
-  "Sort SEQUENCE using PRED as a comparison function.
+  "Sort SEQUENCE transformed by FUNCTION using PRED as the comparison function.
 Elements of SEQUENCE are transformed by FUNCTION before being
 sorted.  FUNCTION must be a function of one argument."
   (seq-sort (lambda (a b)
@@ -300,7 +304,7 @@ sorted.  FUNCTION must be a function of one argument."
 
 (cl-defgeneric seq-concatenate (type &rest sequences)
   "Concatenate SEQUENCES into a single sequence of type TYPE.
-TYPE must be one of following symbols: vector, string or list.
+TYPE must be one of following symbols: `vector', `string' or `list'.
 
 \n(fn TYPE SEQUENCE...)"
   (setq sequences (mapcar #'seq-into-sequence sequences))
@@ -322,8 +326,8 @@ of sequence."
 
 (cl-defgeneric seq-into (sequence type)
   "Concatenate the elements of SEQUENCE into a sequence of type TYPE.
-TYPE can be one of the following symbols: vector, string or
-list."
+TYPE can be one of the following symbols: `vector', `string' or
+`list'."
   (pcase type
     (`vector (seq--into-vector sequence))
     (`string (seq--into-string sequence))
@@ -332,7 +336,7 @@ list."
 
 ;;;###autoload
 (cl-defgeneric seq-filter (pred sequence)
-  "Return a list of all elements for which (PRED element) is non-nil in 
SEQUENCE."
+  "Return a list of all the elements in SEQUENCE for which PRED returns 
non-nil."
   (let ((exclude (make-symbol "exclude")))
     (delq exclude (seq-map (lambda (elt)
                              (if (funcall pred elt)
@@ -342,13 +346,13 @@ list."
 
 ;;;###autoload
 (cl-defgeneric seq-remove (pred sequence)
-  "Return a list of all the elements for which (PRED element) is nil in 
SEQUENCE."
+  "Return a list of all the elements in SEQUENCE for which PRED returns nil."
   (seq-filter (lambda (elt) (not (funcall pred elt)))
               sequence))
 
 ;;;###autoload
 (cl-defgeneric seq-remove-at-position (sequence n)
-  "Return a copy of SEQUENCE where the element at N got removed.
+  "Return a copy of SEQUENCE with the element at index N removed.
 
 N is the (zero-based) index of the element that should not be in
 the result.
@@ -381,7 +385,7 @@ If SEQUENCE is empty, return INITIAL-VALUE and FUNCTION is 
not called."
 
 ;;;###autoload
 (cl-defgeneric seq-every-p (pred sequence)
-  "Return non-nil if (PRED element) is non-nil for all elements of SEQUENCE."
+  "Return non-nil if PRED returns non-nil for all the elements of SEQUENCE."
   (catch 'seq--break
     (seq-doseq (elt sequence)
       (or (funcall pred elt)
@@ -390,8 +394,8 @@ If SEQUENCE is empty, return INITIAL-VALUE and FUNCTION is 
not called."
 
 ;;;###autoload
 (cl-defgeneric seq-some (pred sequence)
-  "Return non-nil if PRED is satisfied for at least one element of SEQUENCE.
-If so, return the first non-nil value returned by PRED."
+  "Return non-nil if PRED returns non-nil for at least one element of SEQUENCE.
+If the value is non-nil, it is the first non-nil value returned by PRED."
   (catch 'seq--break
     (seq-doseq (elt sequence)
       (let ((result (funcall pred elt)))
@@ -401,12 +405,12 @@ If so, return the first non-nil value returned by PRED."
 
 ;;;###autoload
 (cl-defgeneric seq-find (pred sequence &optional default)
-  "Return the first element for which (PRED element) is non-nil in SEQUENCE.
-If no element is found, return DEFAULT.
+  "Return the first element in SEQUENCE for which PRED returns non-nil.
+If no such element is found, return DEFAULT.
 
 Note that `seq-find' has an ambiguity if the found element is
-identical to DEFAULT, as it cannot be known if an element was
-found or not."
+identical to DEFAULT, as in that case it is impossible to know
+whether an element was found or not."
   (catch 'seq--break
     (seq-doseq (elt sequence)
       (when (funcall pred elt)
@@ -414,7 +418,7 @@ found or not."
     default))
 
 (cl-defgeneric seq-count (pred sequence)
-  "Return the number of elements for which (PRED element) is non-nil in 
SEQUENCE."
+  "Return the number of elements in SEQUENCE for which PRED returns non-nil."
   (let ((count 0))
     (seq-doseq (elt sequence)
       (when (funcall pred elt)
@@ -422,8 +426,8 @@ found or not."
     count))
 
 (cl-defgeneric seq-contains (sequence elt &optional testfn)
-  "Return the first element in SEQUENCE that is equal to ELT.
-Equality is defined by the function TESTFN, which defaults to `equal'."
+  "Return the first element in SEQUENCE that is \"equal\" to ELT.
+\"Equality\" is defined by the function TESTFN, which defaults to `equal'."
   (declare (obsolete seq-contains-p "27.1"))
   (seq-some (lambda (e)
               (when (funcall (or testfn #'equal) elt e)
@@ -431,8 +435,8 @@ Equality is defined by the function TESTFN, which defaults 
to `equal'."
             sequence))
 
 (cl-defgeneric seq-contains-p (sequence elt &optional testfn)
-  "Return non-nil if SEQUENCE contains an element equal to ELT.
-Equality is defined by the function TESTFN, which defaults to `equal'."
+  "Return non-nil if SEQUENCE contains an element \"equal\" to ELT.
+\"Equality\" is defined by the function TESTFN, which defaults to `equal'."
     (catch 'seq--break
       (seq-doseq (e sequence)
         (let ((r (funcall (or testfn #'equal) e elt)))
@@ -442,15 +446,16 @@ Equality is defined by the function TESTFN, which 
defaults to `equal'."
 
 (cl-defgeneric seq-set-equal-p (sequence1 sequence2 &optional testfn)
   "Return non-nil if SEQUENCE1 and SEQUENCE2 contain the same elements.
-This does not depend on the order of the elements.
-Equality is defined by the function TESTFN, which defaults to `equal'."
+The order of the elements in the sequences is not important.
+\"Equality\" of elements is defined by the function TESTFN, which
+defaults to `equal'."
   (and (seq-every-p (lambda (item1) (seq-contains-p sequence2 item1 testfn)) 
sequence1)
        (seq-every-p (lambda (item2) (seq-contains-p sequence1 item2 testfn)) 
sequence2)))
 
 ;;;###autoload
 (cl-defgeneric seq-position (sequence elt &optional testfn)
-  "Return the (zero-based) index of the first element in SEQUENCE equal to ELT.
-Equality is defined by the function TESTFN, which defaults to `equal'."
+  "Return the (zero-based) index of the first element in SEQUENCE \"equal\" to 
ELT.
+\"Equality\" is defined by the function TESTFN, which defaults to `equal'."
   (let ((index 0))
     (catch 'seq--break
       (seq-doseq (e sequence)
@@ -461,11 +466,11 @@ Equality is defined by the function TESTFN, which 
defaults to `equal'."
 
 ;;;###autoload
 (cl-defgeneric seq-positions (sequence elt &optional testfn)
-  "Return indices for which (TESTFN (seq-elt SEQUENCE index) ELT) is non-nil.
+  "Return list of indices of SEQUENCE elements for which TESTFN returns 
non-nil.
 
-TESTFN is a two-argument function which is passed each element of
-SEQUENCE as first argument and ELT as second. TESTFN defaults to
-`equal'.
+TESTFN is a two-argument function which is called with each element of
+SEQUENCE as the first argument and ELT as the second.
+TESTFN defaults to `equal'.
 
 The result is a list of (zero-based) indices."
   (let ((result '()))
@@ -479,7 +484,7 @@ The result is a list of (zero-based) indices."
 ;;;###autoload
 (cl-defgeneric seq-uniq (sequence &optional testfn)
   "Return a list of the elements of SEQUENCE with duplicates removed.
-TESTFN is used to compare elements, or `equal' if TESTFN is nil."
+TESTFN is used to compare elements, and defaults to `equal'."
   (let ((result '()))
     (seq-doseq (elt sequence)
       (unless (seq-contains-p result elt testfn)
@@ -514,15 +519,15 @@ TESTFN is used to compare elements, or `equal' if TESTFN 
is nil."
     (nreverse result)))
 
 (cl-defgeneric seq-mapcat (function sequence &optional type)
-  "Concatenate the result of applying FUNCTION to each element of SEQUENCE.
-The result is a sequence of type TYPE, or a list if TYPE is nil."
+  "Concatenate the results of applying FUNCTION to each element of SEQUENCE.
+The result is a sequence of type TYPE; TYPE defaults to `list'."
   (apply #'seq-concatenate (or type 'list)
          (seq-map function sequence)))
 
 (cl-defgeneric seq-partition (sequence n)
   "Return list of elements of SEQUENCE grouped into sub-sequences of length N.
 The last sequence may contain less than N elements.  If N is a
-negative integer or 0, nil is returned."
+negative integer or 0, the function returns nil."
   (unless (< n 1)
     (let ((result '()))
       (while (not (seq-empty-p sequence))
@@ -532,8 +537,9 @@ negative integer or 0, nil is returned."
 
 ;;;###autoload
 (cl-defgeneric seq-union (sequence1 sequence2 &optional testfn)
-  "Return a list of all elements that appear in either SEQUENCE1 or SEQUENCE2.
-Equality is defined by the function TESTFN, which defaults to `equal'."
+  "Return a list of all the elements that appear in either SEQUENCE1 or 
SEQUENCE2.
+\"Equality\" of elements is defined by the function TESTFN, which
+defaults to `equal'."
   (let* ((accum (lambda (acc elt)
                   (if (seq-contains-p acc elt testfn)
                       acc
@@ -544,8 +550,9 @@ Equality is defined by the function TESTFN, which defaults 
to `equal'."
 
 ;;;###autoload
 (cl-defgeneric seq-intersection (sequence1 sequence2 &optional testfn)
-  "Return a list of the elements that appear in both SEQUENCE1 and SEQUENCE2.
-Equality is defined by the function TESTFN, which defaults to `equal'."
+  "Return a list of all the elements that appear in both SEQUENCE1 and 
SEQUENCE2.
+\"Equality\" of elements is defined by the function TESTFN, which
+defaults to `equal'."
   (seq-reduce (lambda (acc elt)
                 (if (seq-contains-p sequence2 elt testfn)
                     (cons elt acc)
@@ -554,8 +561,9 @@ Equality is defined by the function TESTFN, which defaults 
to `equal'."
               '()))
 
 (cl-defgeneric seq-difference (sequence1 sequence2 &optional testfn)
-  "Return a list of the elements that appear in SEQUENCE1 but not in SEQUENCE2.
-Equality is defined by the function TESTFN, which defaults to `equal'."
+  "Return list of all the elements that appear in SEQUENCE1 but not in 
SEQUENCE2.
+\"Equality\" of elements is defined by the function TESTFN, which
+defaults to `equal'."
   (seq-reduce (lambda (acc elt)
                 (if (seq-contains-p sequence2 elt testfn)
                     acc
@@ -591,7 +599,7 @@ SEQUENCE must be a sequence of numbers or markers."
   (apply #'max (seq-into sequence 'list)))
 
 (defun seq--count-successive (pred sequence)
-  "Count successive elements for which (PRED element) is non-nil in SEQUENCE."
+  "Count successive elements in SEQUENCE for which PRED returns non-nil."
   (let ((n 0)
         (len (seq-length sequence)))
     (while (and (< n len)
@@ -628,13 +636,13 @@ SEQUENCE must be a sequence of numbers or markers."
 
 ;; TODO: make public?
 (defun seq--elt-safe (sequence n)
-  "Return element of SEQUENCE at the index N.
+  "Return the element of SEQUENCE whose zero-based index is N.
 If no element is found, return nil."
   (ignore-errors (seq-elt sequence n)))
 
 ;;;###autoload
 (cl-defgeneric seq-random-elt (sequence)
-  "Return a random element from SEQUENCE.
+  "Return a randomly chosen element from SEQUENCE.
 Signal an error if SEQUENCE is empty."
   (if (seq-empty-p sequence)
       (error "Sequence cannot be empty")
@@ -681,8 +689,8 @@ Signal an error if SEQUENCE is empty."
     (concat sequence)))
 
 (defun seq-split (sequence length)
-  "Split SEQUENCE into a list of sub-sequences of at most LENGTH.
-All the sub-sequences will be of LENGTH, except the last one,
+  "Split SEQUENCE into a list of sub-sequences of at most LENGTH elements.
+All the sub-sequences will be LENGTH long, except the last one,
 which may be shorter."
   (when (< length 1)
     (error "Sub-sequence length must be larger than zero"))
@@ -696,7 +704,7 @@ which may be shorter."
     (nreverse result)))
 
 (defun seq-keep (function sequence)
-  "Apply FUNCTION to SEQUENCE and return all non-nil results."
+  "Apply FUNCTION to SEQUENCE and return the list of all the non-nil results."
   (delq nil (seq-map function sequence)))
 
 (provide 'seq)
diff --git a/lisp/emacs-lisp/shortdoc.el b/lisp/emacs-lisp/shortdoc.el
index dbac03432c..8328324715 100644
--- a/lisp/emacs-lisp/shortdoc.el
+++ b/lisp/emacs-lisp/shortdoc.el
@@ -833,7 +833,7 @@ A FUNC form can have any number of `:no-eval' (or 
`:no-value'),
   (seq-set-equal-p
    :eval (seq-set-equal-p '(1 2 3) '(3 1 2)))
   (seq-some
-   :eval (seq-some #'cl-evenp '(1 2 3)))
+   :eval (seq-some #'floatp '(1 2.0 3)))
   "Building Sequences"
   (seq-concatenate
    :eval (seq-concatenate 'vector '(1 2) '(c d)))
@@ -898,14 +898,14 @@ A FUNC form can have any number of `:no-eval' (or 
`:no-value'),
   (seq-filter
    :eval (seq-filter #'numberp '(a b 3 4 f 6)))
   (seq-keep
-   :eval (seq-keep #'cl-digit-char-p '(?6 ?a ?7)))
+   :eval (seq-keep #'car-safe '((1 2) 3 t (a . b))))
   (seq-remove
    :eval (seq-remove #'numberp '(1 2 c d 5)))
   (seq-remove-at-position
    :eval (seq-remove-at-position '(a b c d e) 3)
    :eval (seq-remove-at-position [a b c d e] 0))
   (seq-group-by
-   :eval (seq-group-by #'cl-plusp '(-1 2 3 -4 -5 6)))
+   :eval (seq-group-by #'natnump '(-1 2 3 -4 -5 6)))
   (seq-union
    :eval (seq-union '(1 2 3) '(3 5)))
   (seq-difference
@@ -921,7 +921,7 @@ A FUNC form can have any number of `:no-eval' (or 
`:no-value'),
   (seq-split
    :eval (seq-split [0 1 2 3 5] 2))
   (seq-take-while
-   :eval (seq-take-while #'cl-evenp [2 4 9 6 5]))
+   :eval (seq-take-while #'integerp [1 2 3.0 4]))
   (seq-uniq
    :eval (seq-uniq '(a b d b a c))))
 
@@ -1374,13 +1374,20 @@ If SAME-WINDOW, don't pop to a new window."
          (unless (bobp)
            (insert "\n"))
          (insert (propertize
-                  (concat (substitute-command-keys data) "\n\n")
+                  (substitute-command-keys data)
+                  'face 'shortdoc-heading
+                  'shortdoc-section t
+                  'outline-level 1))
+         (insert (propertize
+                  "\n\n"
                   'face 'shortdoc-heading
                   'shortdoc-section t)))
         ;; There may be functions not yet defined in the data.
         ((fboundp (car data))
          (when prev
-           (insert (make-separator-line)))
+           (insert (make-separator-line)
+                   ;; This helps with hidden outlines (bug#53981)
+                   (propertize "\n" 'face '(:height 0))))
          (setq prev t)
          (shortdoc--display-function data))))
      (cdr (assq group shortdoc--groups))))
@@ -1397,7 +1404,7 @@ If SAME-WINDOW, don't pop to a new window."
         (start-section (point))
         arglist-start)
     ;; Function calling convention.
-    (insert (propertize "(" 'shortdoc-function function))
+    (insert (propertize "(" 'shortdoc-function function 'outline-level 2))
     (if (plist-get data :no-manual)
         (insert-text-button
          (symbol-name function)
@@ -1531,7 +1538,10 @@ Example:
 
 (define-derived-mode shortdoc-mode special-mode "shortdoc"
   "Mode for shortdoc."
-  :interactive nil)
+  :interactive nil
+  (setq-local outline-search-function #'outline-search-level
+              outline-level (lambda ()
+                              (get-text-property (point) 'outline-level))))
 
 (defun shortdoc--goto-section (arg sym &optional reverse)
   (unless (natnump arg)
diff --git a/lisp/emacs-lisp/subr-x.el b/lisp/emacs-lisp/subr-x.el
index 6e4d88b4df..18087bc937 100644
--- a/lisp/emacs-lisp/subr-x.el
+++ b/lisp/emacs-lisp/subr-x.el
@@ -322,6 +322,10 @@ as the new values of the bound variables in the recursive 
invocation."
     ;; Keeping a work buffer around is more efficient than creating a
     ;; new temporary buffer.
     (with-current-buffer (get-buffer-create " *string-pixel-width*")
+      ;; `display-line-numbers-mode' is enabled in internal buffers
+      ;; that breaks width calculation, so need to disable (bug#59311)
+      (when (bound-and-true-p display-line-numbers-mode)
+        (display-line-numbers-mode -1))
       (delete-region (point-min) (point-max))
       (insert string)
       (car (buffer-text-pixel-size nil nil t)))))
diff --git a/lisp/emacs-lisp/tcover-ses.el b/lisp/emacs-lisp/tcover-ses.el
index 2b1672ffd6..645b1a328f 100644
--- a/lisp/emacs-lisp/tcover-ses.el
+++ b/lisp/emacs-lisp/tcover-ses.el
@@ -569,7 +569,7 @@ spreadsheet files with invalid formatting."
          (signal 'singularity-error nil)) ;Shouldn't get here
       (singularity-error (error "No error from %s?" x))
       (error nil)))
-  ;;Test quit-handling in ses-update-cells.  Cant' use `eval' here.
+  ;; Test quit-handling in ses-update-cells.  Can't use `eval' here.
   (let ((inhibit-quit t))
     (setq quit-flag t)
     (condition-case nil
diff --git a/lisp/erc/ChangeLog.1 b/lisp/erc/ChangeLog.1
index 89c24758cb..64231f365e 100644
--- a/lisp/erc/ChangeLog.1
+++ b/lisp/erc/ChangeLog.1
@@ -11075,7 +11075,8 @@
        stay at your current version. It seems fairly stable though.
        That changed? erc-buffer-name handling was completely rewritten,
        and erc-buffer-list local variable handling removed.
-       Simplifies alot of code. Poke at it. read the diff. report bug/send 
patches!
+       Simplifies a lot of code.  Poke at it.  Read the diff.  Report
+       bug/send patches!
 
        * erc.el: * Added variable listing when /set is used without args
 
@@ -11448,7 +11449,7 @@
 2001-10-03  Mario Lang  <mlang@delysid.org>
 
        * erc.el:
-       * Removed alot of (progn ...) where they were not necessary
+       * Removed a lot of (progn ...) where they were not necessary
        * Changed some (if ...) without else part to (when ...)
        * Some (while ...) to use (dolist ...)
        * Fix for completion popup generating tracebacks.
diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el
index 026b34849a..15fd6ac50f 100644
--- a/lisp/erc/erc-backend.el
+++ b/lisp/erc/erc-backend.el
@@ -299,6 +299,9 @@ function `erc-server-process-alive' instead.")
 (defvar-local erc--server-last-reconnect-count 0
   "Snapshot of reconnect count when the connection was established.")
 
+(defvar-local erc--server-reconnect-timer nil
+  "Auto-reconnect timer for a network context.")
+
 (defvar-local erc-server-quitting nil
   "Non-nil if the user requests a quit.")
 
@@ -401,6 +404,16 @@ This only has an effect if `erc-server-auto-reconnect' is 
non-nil."
 If a key is pressed while ERC is waiting, it will stop waiting."
   :type 'number)
 
+(defcustom erc-server-reconnect-function 'erc-server-delayed-reconnect
+  "Function called by the reconnect timer to create a new connection.
+Called with a server buffer as its only argument.  Potential uses
+include exponential backoff and probing for connectivity prior to
+dialing.  Use `erc-schedule-reconnect' to instead try again later
+and optionally alter the attempts tally."
+  :package-version '(ERC . "5.4.1") ; FIXME on next release
+  :type '(choice (function-item erc-server-delayed-reconnect)
+                 function))
+
 (defcustom erc-split-line-length 440
   "The maximum length of a single message.
 If a message exceeds this size, it is broken into multiple ones.
@@ -625,12 +638,18 @@ The current buffer is given by BUFFER."
   (let ((p (plist-put parameters :nowait t)))
     (apply #'open-network-stream name buffer host service p)))
 
+(defvar erc--server-connect-dumb-ipv6-regexp
+  ;; Not for validation (gives false positives).
+  (rx bot "[" (group (+ (any xdigit digit ":.")) (? "%" (+ alnum))) "]" eot))
+
 (defun erc-server-connect (server port buffer &optional client-certificate)
   "Perform the connection and login using the specified SERVER and PORT.
 We will store server variables in the buffer given by BUFFER.
 CLIENT-CERTIFICATE may optionally be used to specify a TLS client
 certificate to use for authentication when connecting over
 TLS (see `erc-session-client-certificate' for more details)."
+  (when (string-match erc--server-connect-dumb-ipv6-regexp server)
+    (setq server (match-string 1 server)))
   (let ((msg (erc-format-message 'connect ?S server ?p port)) process
         (args `(,(format "erc-%s-%s" server port) nil ,server ,port)))
     (when client-certificate
@@ -645,7 +664,8 @@ TLS (see `erc-session-client-certificate' for more 
details)."
       (setq erc-server-process process)
       (setq erc-server-quitting nil)
       (setq erc-server-reconnecting nil
-            erc--server-reconnecting nil)
+            erc--server-reconnecting nil
+            erc--server-reconnect-timer nil)
       (setq erc-server-timed-out nil)
       (setq erc-server-banned nil)
       (setq erc-server-error-occurred nil)
@@ -686,6 +706,7 @@ Make sure you are in an ERC buffer when running this."
     (with-current-buffer buffer
       (erc-update-mode-line)
       (erc-set-active-buffer (current-buffer))
+      (setq erc--server-reconnecting t)
       (setq erc-server-last-sent-time 0)
       (setq erc-server-lines-sent 0)
       (let ((erc-server-connect-function (or erc-session-connector
@@ -758,37 +779,59 @@ EVENT is the message received from the closed connection 
process."
         erc-server-reconnecting)
       (erc--server-reconnect-p event)))
 
+(defconst erc--mode-line-process-reconnecting
+  '(:eval (erc-with-server-buffer
+            (and erc--server-reconnect-timer
+                 (format ": reconnecting in %.1fs"
+                         (- (timer-until erc--server-reconnect-timer
+                                         (current-time)))))))
+  "Mode-line construct showing seconds until next reconnect attempt.
+Move point around to refresh.")
+
+(defun erc--cancel-auto-reconnect-timer ()
+  (when erc--server-reconnect-timer
+    (cancel-timer erc--server-reconnect-timer)
+    (erc-display-message nil 'notice nil 'reconnect-canceled
+                         ?u (buffer-name)
+                         ?c (- (timer-until erc--server-reconnect-timer
+                                            (current-time))))
+    (setq erc--server-reconnect-timer nil)
+    (erc-update-mode-line)))
+
+(defun erc-schedule-reconnect (buffer &optional incr)
+  "Create and return a reconnect timer for BUFFER.
+When `erc-server-reconnect-attempts' is a number, increment
+`erc-server-reconnect-count' by INCR unconditionally."
+  (let ((count (and (integerp erc-server-reconnect-attempts)
+                    (- erc-server-reconnect-attempts
+                       (cl-incf erc-server-reconnect-count (or incr 1))))))
+    (erc-display-message nil 'error (current-buffer) 'reconnecting
+                         ?m erc-server-reconnect-timeout
+                         ?i (if count erc-server-reconnect-count "N")
+                         ?n (if count erc-server-reconnect-attempts "A"))
+    (setq erc-server-reconnecting nil
+          erc--server-reconnect-timer
+          (run-at-time erc-server-reconnect-timeout nil
+                       erc-server-reconnect-function buffer))))
+
 (defun erc-process-sentinel-2 (event buffer)
   "Called when `erc-process-sentinel-1' has detected an unexpected disconnect."
-  (if (not (buffer-live-p buffer))
-      (erc-update-mode-line)
+  (when (buffer-live-p buffer)
     (with-current-buffer buffer
-      (let ((reconnect-p (erc--server-reconnect-p event)) message delay)
+      (let ((reconnect-p (erc--server-reconnect-p event)) message)
         (setq message (if reconnect-p 'disconnected 'disconnected-noreconnect))
         (erc-display-message nil 'error (current-buffer) message)
         (if (not reconnect-p)
             ;; terminate, do not reconnect
             (progn
-              (setq erc--server-reconnecting nil)
+              (setq erc--server-reconnecting nil
+                    erc--server-reconnect-timer nil)
               (erc-display-message nil 'error (current-buffer)
                                    'terminated ?e event)
-              ;; Update mode line indicators
-              (erc-update-mode-line)
               (set-buffer-modified-p nil))
           ;; reconnect
-          (condition-case nil
-              (progn
-                (setq erc-server-reconnecting nil
-                      erc--server-reconnecting t
-                      erc-server-reconnect-count (1+ 
erc-server-reconnect-count))
-                (setq delay erc-server-reconnect-timeout)
-                (run-at-time delay nil
-                             #'erc-server-delayed-reconnect buffer))
-            (error (unless (integerp erc-server-reconnect-attempts)
-                     (message "%s ... %s"
-                              "Reconnecting until we succeed"
-                              "kill the ERC server buffer to stop"))
-                   (erc-server-delayed-reconnect buffer))))))))
+          (erc-schedule-reconnect buffer))))
+    (erc-update-mode-line)))
 
 (defun erc-process-sentinel-1 (event buffer)
   "Called when `erc-process-sentinel' has decided that we're disconnecting.
@@ -1085,8 +1128,37 @@ See also `erc-server-send'."
 
 ;;;; Handling responses
 
+(defcustom erc-tags-format 'overridable
+  "Shape of the `tags' alist in `erc-response' objects.
+When set to `legacy', pre-5.5 parsing behavior takes effect for
+the tags portion of every message.  The resulting alist contains
+conses of the form (STRING . LIST), in which LIST is comprised of
+at most one, possibly empty string.  When set to nil, ERC only
+parses tags if an active module defines an implementation.  It
+otherwise ignores them.  In such cases, each alist element is a
+cons of a symbol and an optional, nonempty string.
+
+With the default value of `overridable', ERC behaves as it does
+with `legacy' except that it emits a warning whenever first
+encountering a message containing tags in a given Emacs session.
+But it only does so when a module implementing overriding,
+non-legacy behavior isn't already active in the current network
+context.
+
+Note that future bundled modules providing IRCv3 functionality
+will not be compatible with the legacy format.  User code should
+eventually transition to expecting this \"5.5+ variant\" and set
+this option to nil."
+  :package-version '(ERC . "5.4.1") ; FIXME increment on next release
+  :type '(choice (const nil)
+                 (const legacy)
+                 (const overridable)))
+
 (defun erc-parse-tags (string)
   "Parse IRCv3 tags list in STRING to a (tag . value) alist."
+  (erc--parse-message-tags string))
+
+(defun erc--parse-tags (string)
   (let ((tags)
         (tag-strings (split-string string ";")))
     (dolist (tag-string tag-strings tags)
@@ -1096,6 +1168,28 @@ See also `erc-server-send'."
                 `(,pair))
               tags)))))
 
+;; A benefit of this function being internal is not having to define a
+;; separate method just to ensure an `erc-tags-format' value of
+;; `legacy' always wins.  A downside is that module code must take
+;; care to preserve that promise manually.
+
+(cl-defgeneric erc--parse-message-tags (string)
+  "Parse STRING into an alist of (TAG . VALUE) conses.
+Expect TAG to be a symbol and VALUE nil or a nonempty string.
+Don't split composite raw-input values containing commas;
+instead, leave them as a single string."
+  (when erc-tags-format
+    (unless (or (eq erc-tags-format 'legacy)
+                (get 'erc-parse-tags 'erc-v3-warned-p))
+      (put 'erc-parse-tags 'erc-v3-warned-p t)
+      (display-warning
+       'ERC
+       (concat
+        "Legacy ERC tags behavior is currently in effect, but other modules,"
+        " including those bundled with ERC, may override this in future"
+        " releases.  See `erc-tags-format' for more info.")))
+    (erc--parse-tags string)))
+
 (defun erc-parse-server-response (proc string)
   "Parse and act upon a complete line from an IRC server.
 PROC is the process (connection) from which STRING was received.
@@ -1105,9 +1199,9 @@ PROCs `process-buffer' is `current-buffer' when this 
function is called."
       (let* ((tag-list (when (eq (aref string 0) ?@)
                          (substring string 1
                                     (string-search " " string))))
-             (msg (make-erc-response :unparsed string :tags (when tag-list
-                                                              (erc-parse-tags
-                                                               tag-list))))
+             (msg (make-erc-response :unparsed string :tags
+                                     (when tag-list
+                                       (erc--parse-message-tags tag-list))))
              (string (if tag-list
                          (substring string (+ 1 (string-search " " string)))
                        string))
diff --git a/lisp/erc/erc-common.el b/lisp/erc/erc-common.el
index d8aac36eab..23a1933798 100644
--- a/lisp/erc/erc-common.el
+++ b/lisp/erc/erc-common.el
@@ -77,6 +77,9 @@
 (cl-defstruct (erc--target-channel (:include erc--target)))
 (cl-defstruct (erc--target-channel-local (:include erc--target-channel)))
 
+;; Beginning in 5.5/29.1, the `tags' field may take on one of two
+;; differing types.  See `erc-tags-format' for details.
+
 (cl-defstruct (erc-response (:conc-name erc-response.))
   (unparsed "" :type string)
   (sender "" :type string)
diff --git a/lisp/erc/erc-compat.el b/lisp/erc/erc-compat.el
index 03bd8f1352..d23703394b 100644
--- a/lisp/erc/erc-compat.el
+++ b/lisp/erc/erc-compat.el
@@ -32,6 +32,7 @@
 ;;; Code:
 
 (require 'compat nil 'noerror)
+(eval-when-compile (require 'cl-lib) (require 'url-parse))
 
 ;;;###autoload(autoload 'erc-define-minor-mode "erc-compat")
 (define-obsolete-function-alias 'erc-define-minor-mode
@@ -157,6 +158,121 @@ If START or END is negative, it counts from the end."
               res))))))
 
 
+;;;; Auth Source
+
+(declare-function auth-source-pass--get-attr
+                  "auth-source-pass" (key entry-data))
+(declare-function auth-source-pass--disambiguate
+                  "auth-source-pass" (host &optional user port))
+(declare-function auth-source-backend-parse-parameters
+                  "auth-source-pass" (entry backend))
+(declare-function auth-source-backend "auth-source" (&rest slots))
+(declare-function auth-source-pass-entries "auth-source-pass" nil)
+(declare-function auth-source-pass-parse-entry "auth-source-pass" (entry))
+
+(defvar auth-sources)
+(defvar auth-source-backend-parser-functions)
+
+;; This hard codes `auth-source-pass-port-separator' to ":"
+(defun erc-compat--29-auth-source-pass--retrieve-parsed (seen e port-number-p)
+  (when (string-match (rx (or bot "/")
+                          (or (: (? (group-n 20 (+ (not (in " /@")))) "@")
+                                 (group-n 10 (+ (not (in " /:@"))))
+                                 (? ":" (group-n 30 (+ (not (in " /:"))))))
+                              (: (group-n 11 (+ (not (in " /:@"))))
+                                 (? ":" (group-n 31 (+ (not (in " /:")))))
+                                 (? "/" (group-n 21 (+ (not (in " /:")))))))
+                          eot)
+                      e)
+    (puthash e `( :host ,(or (match-string 10 e) (match-string 11 e))
+                  ,@(if-let* ((tr (match-string 21 e)))
+                        (list :user tr :suffix t)
+                      (list :user (match-string 20 e)))
+                  :port ,(and-let* ((p (or (match-string 30 e)
+                                           (match-string 31 e)))
+                                    (n (string-to-number p)))
+                           (if (or (zerop n) (not port-number-p))
+                               (format "%s" p)
+                             n)))
+             seen)))
+
+;; This looks bad, but it just inlines `auth-source-pass--find-match-many'.
+(defun erc-compat--29-auth-source-pass--build-result-many
+    (hosts users ports require max)
+  "Return a plist of HOSTS, PORTS, USERS, and secret."
+  (unless (listp hosts) (setq hosts (list hosts)))
+  (unless (listp users) (setq users (list users)))
+  (unless (listp ports) (setq ports (list ports)))
+  (unless max (setq max 1))
+  (let ((seen (make-hash-table :test #'equal))
+        (entries (auth-source-pass-entries))
+        (check (lambda (m k v)
+                 (let ((mv (plist-get m k)))
+                   (if (memq k require)
+                       (and v (equal mv v))
+                     (or (not v) (not mv) (equal mv v))))))
+        out suffixed suffixedp)
+    (catch 'done
+      (dolist (host hosts)
+        (pcase-let ((`(,_ ,u ,p) (auth-source-pass--disambiguate host)))
+          (unless (or (not (equal "443" p)) (string-prefix-p "https://"; host))
+            (setq p nil))
+          (dolist (user (or users (list u)))
+            (dolist (port (or ports (list p)))
+              (dolist (e entries)
+                (when-let*
+                    ((m (or (gethash e seen)
+                            (erc-compat--29-auth-source-pass--retrieve-parsed
+                             seen e (integerp port))))
+                     ((equal host (plist-get m :host)))
+                     ((funcall check m :port port))
+                     ((funcall check m :user user))
+                     (parsed (auth-source-pass-parse-entry e))
+                     (secret (or (auth-source-pass--get-attr 'secret parsed)
+                                 (not (memq :secret require)))))
+                  (push
+                   `( :host ,host ; prefer user-provided :host over h
+                      ,@(and-let* ((u (plist-get m :user))) (list :user u))
+                      ,@(and-let* ((p (plist-get m :port))) (list :port p))
+                      ,@(and secret (not (eq secret t)) (list :secret secret)))
+                   (if (setq suffixedp (plist-get m :suffix)) suffixed out))
+                  (unless suffixedp
+                    (when (or (zerop (cl-decf max))
+                              (null (setq entries (delete e entries))))
+                      (throw 'done out)))))
+              (setq suffixed (nreverse suffixed))
+              (while suffixed
+                (push (pop suffixed) out)
+                (when (zerop (cl-decf max))
+                  (throw 'done out))))))))
+    (reverse out)))
+
+(cl-defun erc-compat--29-auth-source-pass-search
+    (&rest spec &key host user port require max &allow-other-keys)
+  ;; From `auth-source-pass-search'
+  (cl-assert (and host (not (eq host t)))
+             t "Invalid password-store search: %s %s")
+  (erc-compat--29-auth-source-pass--build-result-many
+   host user port require max))
+
+(defun erc-compat--29-auth-source-pass-backend-parse (entry)
+  (when (eq entry 'password-store)
+    (auth-source-backend-parse-parameters
+     entry (auth-source-backend
+            :source "."
+            :type 'password-store
+            :search-function #'erc-compat--29-auth-source-pass-search))))
+
+(defun erc-compat--auth-source-backend-parser-functions ()
+  (if (memq 'password-store auth-sources)
+      (progn
+        (require 'auth-source-pass)
+        `(,@(unless (bound-and-true-p auth-source-pass-extra-query-keywords)
+              '(erc-compat--29-auth-source-pass-backend-parse))
+          ,@auth-source-backend-parser-functions))
+    auth-source-backend-parser-functions))
+
+
 ;;;; Misc 29.1
 
 (defmacro erc-compat--with-memoization (table &rest forms)
@@ -168,6 +284,35 @@ If START or END is negative, it counts from the end."
     `(cl--generic-with-memoization ,table ,@forms))
    (t `(progn ,@forms))))
 
+(defvar url-irc-function)
+
+(defun erc-compat--29-browse-url-irc (string &rest _)
+  (require 'url-irc)
+  (let* ((url (url-generic-parse-url string))
+         (url-irc-function
+          (if (function-equal url-irc-function 'url-irc-erc)
+              (lambda (host port chan user pass)
+                (erc-handle-irc-url host port chan user pass (url-type url)))
+            url-irc-function)))
+    (url-irc url)))
+
+(cond ((fboundp 'browse-url-irc)) ; 29
+      ((boundp 'browse-url-default-handlers) ; 28
+       (cl-pushnew '("\\`irc6?s?://" . erc-compat--29-browse-url-irc)
+                   browse-url-default-handlers))
+      ((boundp 'browse-url-browser-function) ; 27
+       (require 'browse-url)
+       (let ((existing browse-url-browser-function))
+         (setq browse-url-browser-function
+               (if (functionp existing)
+                   (lambda (u &rest r)
+                     (apply (if (string-match-p "\\`irc6?s?://" u)
+                                #'erc-compat--29-browse-url-irc
+                              existing)
+                            u r))
+                 (cons '("\\`irc6?s?://" . erc-compat--29-browse-url-irc)
+                       existing))))))
+
 (provide 'erc-compat)
 
 ;;; erc-compat.el ends here
diff --git a/lisp/erc/erc-networks.el b/lisp/erc/erc-networks.el
index dba6ead073..b3e5fcf1a3 100644
--- a/lisp/erc/erc-networks.el
+++ b/lisp/erc/erc-networks.el
@@ -1256,14 +1256,15 @@ server name and search for a match in 
`erc-networks-alist'."
 (defconst erc-networks--name-missing-sentinel (gensym "Unknown ")
   "Value to cover rare case of a literal NETWORK=nil.")
 
-(defun erc-networks--determine ()
+(defun erc-networks--determine (&optional server)
   "Return the name of the network as a symbol.
-Search `erc-networks-alist' for a known entity matching
+Search `erc-networks-alist' for a known entity matching SERVER or
 `erc-server-announced-name'.  If that fails, use the display name
 given by the `RPL_ISUPPORT' NETWORK parameter."
   (or (cl-loop for (name matcher) in erc-networks-alist
-               when (and matcher (string-match (concat matcher "\\'")
-                                               erc-server-announced-name))
+               when (and matcher
+                         (string-match (concat matcher "\\'")
+                                       (or server erc-server-announced-name)))
                return name)
       (and-let* ((vanity (erc--get-isupport-entry 'NETWORK 'single))
                  ((intern vanity))))
diff --git a/lisp/erc/erc-pcomplete.el b/lisp/erc/erc-pcomplete.el
index af8528dbc3..3ba18e835b 100644
--- a/lisp/erc/erc-pcomplete.el
+++ b/lisp/erc/erc-pcomplete.el
@@ -179,6 +179,10 @@ for use on `completion-at-point-function'."
 (defun pcomplete/erc-mode/UNIGNORE ()
   (pcomplete-here (erc-with-server-buffer erc-ignore-list)))
 
+(defun pcomplete/erc-mode/RECONNECT ()
+  (pcomplete-here '("cancel"))
+  (pcomplete-opt "a"))
+
 ;;; Functions that provide possible completions.
 
 (defun pcomplete-erc-commands ()
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 6b14cf87e2..2312246e3e 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -70,7 +70,7 @@
 (require 'auth-source)
 (require 'time-date)
 (require 'iso8601)
-(eval-when-compile (require 'subr-x))
+(eval-when-compile (require 'subr-x) (require 'url-parse))
 
 (defconst erc-version "5.4.1"
   "This version of ERC.")
@@ -1542,6 +1542,11 @@ symbol, it may have these values:
 * ircs        -> 994
 * ircd        -> 6667
 * ircd-dalnet -> 7000"
+  ;; These were updated somewhat in 2022 to reflect modern standards
+  ;; and practices.  See also:
+  ;;
+  ;; https://datatracker.ietf.org/doc/html/rfc7194#section-1
+  ;; https://www.iana.org/assignments/service-names-port-numbers
   (cond
    ((symbolp port)
     (erc-normalize-port (symbol-name port)))
@@ -1554,8 +1559,10 @@ symbol, it may have these values:
         194)
        ((string-equal port "ircs")
         994)
-       ((string-equal port "ircd")
+       ((string-equal port "ircu") 6667) ; 6665-6669
+       ((string-equal port "ircd") ; nonstandard (irc-serv is 529)
         6667)
+       ((string-equal port "ircs-u") 6697)
        ((string-equal port "ircd-dalnet")
         7000)
        (t
@@ -1924,7 +1931,9 @@ removed from the list will be disabled."
 
 If CONNECT is non-nil, connect to the server.  Otherwise assume
 already connected and just create a separate buffer for the new
-target CHANNEL.
+target given by CHANNEL, meaning these parameters are mutually
+exclusive.  Note that CHANNEL may also be a query; its name has
+been retained for historical reasons.
 
 Use PASSWD as user password on the server.  If TGT-LIST is
 non-nil, use it to initialize `erc-default-recipients'.
@@ -2032,12 +2041,12 @@ Returns the buffer for the given server or channel."
     ;; Saving log file on exit
     (run-hook-with-args 'erc-connect-pre-hook buffer)
 
-    (when connect
-      (erc-server-connect erc-session-server
-                          erc-session-port
-                          buffer
-                          erc-session-client-certificate))
-    (erc-update-mode-line)
+    (if connect
+        (erc-server-connect erc-session-server
+                            erc-session-port
+                            buffer
+                            erc-session-client-certificate)
+      (erc-update-mode-line))
 
     ;; Now display the buffer in a window as per user wishes.
     (unless (eq buffer old-buffer)
@@ -2094,52 +2103,51 @@ parameters SERVER and NICK."
   :group 'erc-hooks
   :type '(repeat function))
 
+(defun erc--ensure-url (input)
+  (unless (string-match (rx bot "irc" (? "6") (? "s") "://") input)
+    (when (and (string-match (rx (? (+ any) "@")
+                                 (or (group (* (not "[")) ":" (* any))
+                                     (+ any))
+                                 ":" (+ (not (any ":]"))) eot)
+                             input)
+               (match-beginning 1))
+      (setq input (concat "[" (substring input (match-beginning 1)) "]")))
+    (setq input (concat "irc://" input)))
+  input)
+
 ;;;###autoload
 (defun erc-select-read-args ()
   "Prompt the user for values of nick, server, port, and password."
-  (let (user-input server port nick passwd)
-    (setq user-input (read-string
-                      "IRC server: "
-                      (erc-compute-server) 'erc-server-history-list))
-
-    (if (string-match "\\(.*\\):\\(.*\\)\\'" user-input)
-        (setq port (erc-string-to-port (match-string 2 user-input))
-              user-input (match-string 1 user-input))
-      (setq port
-            (erc-string-to-port (read-string
-                                 "IRC port: " (erc-port-to-string
-                                               (erc-compute-port))))))
-
-    (if (string-match "\\`\\(.*\\)@\\(.*\\)" user-input)
-        (setq nick (match-string 1 user-input)
-              user-input (match-string 2 user-input))
-      (setq nick
-            (if (erc-already-logged-in server port nick)
-                (read-string
-                 (erc-format-message 'nick-in-use ?n nick)
-                 nick 'erc-nick-history-list)
-              (read-string
-               "Nickname: " (erc-compute-nick nick)
-               'erc-nick-history-list))))
-
-    (setq server user-input)
-
-    (setq passwd (if erc-prompt-for-password
-                     (read-passwd "Server password: ")
-                   (with-suppressed-warnings ((obsolete erc-password))
-                     erc-password)))
+  (require 'url-parse)
+  (let* ((input (let ((d (erc-compute-server)))
+                  (read-string (format "Server (default is %S): " d)
+                               nil 'erc-server-history-list d)))
+         ;; For legacy reasons, also accept a URL without a scheme.
+         (url (url-generic-parse-url (erc--ensure-url input)))
+         (server (url-host url))
+         (sp (and (or (string-suffix-p "s" (url-type url))
+                      (and (equal server erc-default-server)
+                           (not (string-prefix-p "irc://" input))))
+                  'ircs-u))
+         (port (or (url-portspec url)
+                   (erc-compute-port
+                    (let ((d (erc-compute-port sp))) ; may be a string
+                      (read-string (format "Port (default is %s): " d)
+                                   nil nil d)))))
+         ;; Trust the user not to connect twice accidentally.  We
+         ;; can't use `erc-already-logged-in' to check for an existing
+         ;; connection without modifying it to consider USER and PASS.
+         (nick (or (url-user url)
+                   (let ((d (erc-compute-nick)))
+                     (read-string (format "Nickname (default is %S): " d)
+                                  nil 'erc-nick-history-list d))))
+         (passwd (or (url-password url)
+                     (if erc-prompt-for-password
+                         (read-passwd "Server password (optional): ")
+                       (with-suppressed-warnings ((obsolete erc-password))
+                         erc-password)))))
     (when (and passwd (string= "" passwd))
       (setq passwd nil))
-
-    (while (erc-already-logged-in server port nick)
-      ;; hmm, this is a problem when using multiple connections to a bnc
-      ;; with the same nick. Currently this code prevents using more than one
-      ;; bnc with the same nick. actually it would be nice to have
-      ;; bncs transparent, so that erc-compute-buffer-name displays
-      ;; the server one is connected to.
-      (setq nick (read-string
-                  (erc-format-message 'nick-in-use ?n nick)
-                  nick 'erc-nick-history-list)))
     (list :server server :port port :nick nick :password passwd)))
 
 ;;;###autoload
@@ -2184,7 +2192,7 @@ interactively."
 
 ;;;###autoload
 (cl-defun erc-tls (&key (server (erc-compute-server))
-                        (port   (erc-compute-port))
+                        (port   (erc-compute-port 'ircs-u))
                         (nick   (erc-compute-nick))
                         (user   (erc-compute-user))
                         password
@@ -3225,7 +3233,9 @@ host but different ports would result in the one with 
port 123 getting
 the nod.  Much the same would happen for entries sharing only a port:
 the one with host foo would win."
   (when-let*
-      ((priority (map-keys defaults))
+      ((auth-source-backend-parser-functions
+        (erc-compat--auth-source-backend-parser-functions))
+       (priority (map-keys defaults))
        (test (lambda (a b)
                (catch 'done
                  (dolist (key priority)
@@ -3802,17 +3812,17 @@ the message given by REASON."
 (put 'erc-cmd-GQUIT 'do-not-parse-args t)
 (put 'erc-cmd-GQUIT 'process-not-needed t)
 
-(defun erc-cmd-RECONNECT ()
-  "Try to reconnect to the current IRC server."
+(defun erc--cmd-reconnect ()
   (let ((buffer (erc-server-buffer))
         (process nil))
     (unless (buffer-live-p buffer)
       (setq buffer (current-buffer)))
     (with-current-buffer buffer
+      (when erc--server-reconnect-timer
+        (erc--cancel-auto-reconnect-timer))
       (setq erc-server-quitting nil)
       (with-suppressed-warnings ((obsolete erc-server-reconnecting))
         (setq erc-server-reconnecting t))
-      (setq erc--server-reconnecting t)
       (setq erc-server-reconnect-count 0)
       (setq process (get-buffer-process (erc-server-buffer)))
       (when process
@@ -3826,6 +3836,18 @@ the message given by REASON."
           (setq erc--server-reconnecting nil
                 erc-server-reconnecting nil)))))
   t)
+
+(defun erc-cmd-RECONNECT (&rest args)
+  "Try reconnecting to the current IRC server.
+Alternatively, CANCEL a scheduled attempt for either the current
+connection or, with -A, all applicable connections.
+
+\(fn [CANCEL [-A]])"
+  (pcase args
+    (`("cancel" "-a") (erc-buffer-filter #'erc--cancel-auto-reconnect-timer))
+    (`("cancel") (erc-with-server-buffer (erc--cancel-auto-reconnect-timer)))
+    (_ (erc--cmd-reconnect))))
+
 (put 'erc-cmd-RECONNECT 'process-not-needed t)
 
 (defun erc-cmd-SERVER (server)
@@ -6391,7 +6413,7 @@ non-nil value is found.
 - PORT (the argument passed to this function)
 - The `erc-port' option
 - The `erc-default-port' variable"
-  (or port erc-port erc-default-port))
+  (erc-normalize-port (or port erc-port erc-default-port)))
 
 ;; time routines
 
@@ -6711,11 +6733,12 @@ shortened server name instead."
                   (?s . ,(erc-format-target-and/or-server))
                   (?S . ,(erc-format-target-and/or-network))
                   (?t . ,(erc-format-target))))
-          (process-status (cond ((and (erc-server-process-alive)
-                                      (not erc-server-connected))
-                                 ":connecting")
-                                ((erc-server-process-alive)
-                                 "")
+          (process-status (cond ((erc-server-process-alive buffer)
+                                 (unless erc-server-connected
+                                   ": connecting"))
+                                ((erc-with-server-buffer
+                                   erc--server-reconnect-timer)
+                                 erc--mode-line-process-reconnecting)
                                 (t
                                  ": CLOSED")))
           (face (cond ((eq erc-header-line-face-method nil)
@@ -6726,7 +6749,7 @@ shortened server name instead."
                        'erc-header-line))))
       (setq mode-line-buffer-identification
             (list (format-spec erc-mode-line-format spec)))
-      (setq mode-line-process (list process-status))
+      (setq mode-line-process process-status)
       (let ((header (if erc-header-line-format
                         (format-spec erc-header-line-format spec)
                       nil)))
@@ -6911,6 +6934,8 @@ All windows are opened in the current frame."
    (disconnected . "\n\nConnection failed!  Re-establishing connection...\n")
    (disconnected-noreconnect
     . "\n\nConnection failed!  Not re-establishing connection.\n")
+   (reconnecting . "Reconnecting in %ms: attempt %i/%n ...")
+   (reconnect-canceled . "Canceled %u reconnect timer with %cs to go...")
    (finished . "\n\n*** ERC finished ***\n")
    (terminated . "\n\n*** ERC terminated: %e\n")
    (login . "Logging in as `%n'...")
@@ -7161,25 +7186,83 @@ This function should be on `erc-kill-channel-hook'."
 ;; Teach url.el how to open irc:// URLs with ERC.
 ;; To activate, customize `url-irc-function' to `url-irc-erc'.
 
-;; FIXME change user to nick, and use API to find server buffer
+(defcustom erc-url-connect-function nil
+  "When non-nil, a function used to connect to an IRC URL.
+Called with a string meant to represent a URL scheme, like
+\"ircs\", followed by any number of keyword arguments recognized
+by `erc' and `erc-tls'."
+  :group 'erc
+  :package-version '(ERC . "5.4.1") ; FIXME increment on release
+  :type '(choice (const nil) function))
+
+(defun erc--url-default-connect-function (scheme &rest plist)
+  (let* ((ircsp (if scheme
+                    (string-suffix-p "s" scheme)
+                  (or (eql 6697 (plist-get plist :port))
+                      (yes-or-no-p "Connect using TLS? "))))
+         (erc-server (plist-get plist :server))
+         (erc-port (or (plist-get plist :port)
+                       (and ircsp (erc-normalize-port 'ircs-u))
+                       erc-port))
+         (erc-nick (or (plist-get plist :nick) erc-nick))
+         (erc-password (plist-get plist :password))
+         (args (erc-select-read-args)))
+    (unless ircsp
+      (setq ircsp (eql 6697 erc-port)))
+    (apply (if ircsp #'erc-tls #'erc) args)))
+
 ;;;###autoload
-(defun erc-handle-irc-url (host port channel user password)
-  "Use ERC to IRC on HOST:PORT in CHANNEL as USER with PASSWORD.
+(defun erc-handle-irc-url (host port channel nick password &optional scheme)
+  "Use ERC to IRC on HOST:PORT in CHANNEL.
 If ERC is already connected to HOST:PORT, simply /join CHANNEL.
-Otherwise, connect to HOST:PORT as USER and /join CHANNEL."
-  (let ((server-buffer
-         (car (erc-buffer-filter
-               (lambda ()
-                 (and (string-equal erc-session-server host)
-                      (= erc-session-port port)
-                      (erc-open-server-buffer-p)))))))
-    (with-current-buffer (or server-buffer (current-buffer))
-      (if (and server-buffer channel)
-          (erc-cmd-JOIN channel)
-        (erc-open host port (or user (erc-compute-nick)) 
(erc-compute-full-name)
-                  (not server-buffer) password nil channel
-                  (when server-buffer
-                    (get-buffer-process server-buffer)))))))
+Otherwise, connect to HOST:PORT as NICK and /join CHANNEL.
+
+Beginning with ERC 5.5, new connections require human intervention.
+Customize `erc-url-connect-function' to override this."
+  (when (eql port 0) (setq port nil))
+  (let* ((net (erc-networks--determine host))
+         (server-buffer
+          ;; Viable matches may slip through the cracks for unknown
+          ;; networks.  Additional passes could likely improve things.
+          (car (erc-buffer-filter
+                (lambda ()
+                  (and (not erc--target)
+                       (erc-server-process-alive)
+                       ;; Always trust a matched network.
+                       (or (and net (eq net (erc-network)))
+                           (and (string-equal erc-session-server host)
+                                ;; Ports only matter when dialed hosts
+                                ;; match and we have sufficient info.
+                                (or (not port)
+                                    (= (erc-normalize-port erc-session-port)
+                                       port)))))))))
+         key deferred)
+    (unless server-buffer
+      (setq deferred t
+            server-buffer (apply (or erc-url-connect-function
+                                     #'erc--url-default-connect-function)
+                                 scheme
+                                 :server host
+                                 `(,@(and port (list :port port))
+                                   ,@(and nick (list :nick nick))
+                                   ,@(and password `(:password ,password))))))
+    (when channel
+      ;; These aren't percent-decoded by default
+      (when (string-prefix-p "%" channel)
+        (setq channel (url-unhex-string channel)))
+      (cl-multiple-value-setq (channel key) (split-string channel "[?]"))
+      (if deferred
+          ;; Alternatively, we could make this a defmethod, so when
+          ;; autojoin is loaded, it can do its own thing.  Also, as
+          ;; with `erc-once-with-server-event', it's fine to set local
+          ;; hooks here because they're killed when reconnecting.
+          (with-current-buffer server-buffer
+            (letrec ((f (lambda (&rest _)
+                          (remove-hook 'erc-after-connect f t)
+                          (erc-cmd-JOIN channel key))))
+              (add-hook 'erc-after-connect f nil t)))
+        (with-current-buffer server-buffer
+          (erc-cmd-JOIN channel key))))))
 
 (provide 'erc)
 
diff --git a/lisp/eshell/em-prompt.el b/lisp/eshell/em-prompt.el
index a1a91e7d63..a8744de1db 100644
--- a/lisp/eshell/em-prompt.el
+++ b/lisp/eshell/em-prompt.el
@@ -100,6 +100,14 @@ arriving, or after."
   "C-c C-n" #'eshell-next-prompt
   "C-c C-p" #'eshell-previous-prompt)
 
+(defvar-keymap eshell-prompt-repeat-map
+  :doc "Keymap to repeat eshell-prompt key sequences.  Used in `repeat-mode'."
+  "C-n" #'eshell-next-prompt
+  "C-p" #'eshell-previous-prompt)
+
+(put #'eshell-next-prompt 'repeat-map 'eshell-prompt-repeat-map)
+(put #'eshell-previous-prompt 'repeat-map 'eshell-prompt-repeat-map)
+
 ;;; Functions:
 
 (define-minor-mode eshell-prompt-mode
diff --git a/lisp/eshell/esh-mode.el b/lisp/eshell/esh-mode.el
index 92523fd99e..4357a0e29a 100644
--- a/lisp/eshell/esh-mode.el
+++ b/lisp/eshell/esh-mode.el
@@ -280,6 +280,14 @@ This is used by `eshell-watch-for-password-prompt'."
   "C-w" #'backward-kill-word
   "C-y" #'eshell-repeat-argument)
 
+(defvar-keymap eshell-command-repeat-map
+  :doc "Keymap to repeat eshell-command key sequences.  Used in `repeat-mode'."
+  "C-f" #'eshell-forward-argument
+  "C-b" #'eshell-backward-argument)
+
+(put #'eshell-forward-argument 'repeat-map 'eshell-command-repeat-map)
+(put #'eshell-backward-argument 'repeat-map 'eshell-command-repeat-map)
+
 ;;; User Functions:
 
 (defun eshell-kill-buffer-function ()
diff --git a/lisp/eshell/esh-proc.el b/lisp/eshell/esh-proc.el
index bb928fc5fb..950922ea7f 100644
--- a/lisp/eshell/esh-proc.el
+++ b/lisp/eshell/esh-proc.el
@@ -552,7 +552,7 @@ See the variable `eshell-kill-processes-on-exit'."
        (setq sigs (cdr sigs))))))
 
 (defun eshell-query-kill-processes ()
-  "Kill processes belonging to the current Eshell buffer, possibly w/ query."
+  "Kill processes belonging to the current Eshell buffer, possibly with query."
   (when (and eshell-kill-processes-on-exit
             eshell-process-list)
     (save-window-excursion
diff --git a/lisp/face-remap.el b/lisp/face-remap.el
index f1530285fb..a692015094 100644
--- a/lisp/face-remap.el
+++ b/lisp/face-remap.el
@@ -397,7 +397,11 @@ that have an explicit `:height' setting.  The two 
exceptions to
 this are the `default' and `header-line' faces: they will both be
 scaled even if they have an explicit `:height' setting.
 
-See also the related command `global-text-scale-adjust'."
+See also the related command `global-text-scale-adjust'.  Unlike
+that command, which scales the font size with a increment,
+`text-scale-adjust' scales the font size with a factor,
+`text-scale-mode-step'.  With a small `text-scale-mode-step'
+factor, the two commands behave similarly."
   (interactive "p")
   (let ((ev last-command-event)
        (echo-keystrokes nil))
@@ -462,6 +466,8 @@ the `cdr' has the maximum font size, in units of 1/10 pt."
 
 (defvar global-text-scale-adjust--default-height nil)
 
+(defvar global-text-scale-adjust--increment-factor 5)
+
 ;;;###autoload (define-key ctl-x-map [(control meta ?+)] 
'global-text-scale-adjust)
 ;;;###autoload (define-key ctl-x-map [(control meta ?=)] 
'global-text-scale-adjust)
 ;;;###autoload (define-key ctl-x-map [(control meta ?-)] 
'global-text-scale-adjust)
@@ -492,7 +498,10 @@ The variable `global-text-scale-adjust-resizes-frames' 
controls
 whether the frames are resized to keep the same number of lines
 and characters per line when the font size is adjusted.
 
-See also the related command `text-scale-adjust'."
+See also the related command `text-scale-adjust'.  Unlike that
+command, which scales the font size with a factor,
+`global-text-scale-adjust' scales the font size with an
+increment."
   (interactive "p")
   (when (display-graphic-p)
     (unless global-text-scale-adjust--default-height
@@ -503,16 +512,24 @@ See also the related command `text-scale-adjust'."
            (cur (face-attribute 'default :height))
            (inc
             (pcase key
-              (?- (* (- increment) 5))
+              (?- (* (- increment)
+                     global-text-scale-adjust--increment-factor))
               (?0 (- global-text-scale-adjust--default-height cur))
-              (_ (* increment 5))))
+              (_ (* increment
+                    global-text-scale-adjust--increment-factor))))
            (new (+ cur inc)))
       (when (< (car global-text-scale-adjust-limits)
                new
                (cdr global-text-scale-adjust-limits))
         (let ((frame-inhibit-implied-resize
                (not global-text-scale-adjust-resizes-frames)))
-          (set-face-attribute 'default nil :height new)))
+          (set-face-attribute 'default nil :height new)
+          (redisplay 'force)
+          (when (and (not (and (characterp key) (= key ?0)))
+                     (= cur (face-attribute 'default :height)))
+            (setq global-text-scale-adjust--increment-factor
+                  (1+ global-text-scale-adjust--increment-factor))
+            (global-text-scale-adjust increment))))
       (when (characterp key)
         (set-transient-map
          (let ((map (make-sparse-keymap)))
diff --git a/lisp/faces.el b/lisp/faces.el
index 09e8110449..c69339e2fd 100644
--- a/lisp/faces.el
+++ b/lisp/faces.el
@@ -787,13 +787,12 @@ specified below.  WIDTH specifies the width of the lines 
to draw; it
 defaults to 1.  If WIDTH is negative, the absolute value is the width
 of the lines, and draw top/bottom lines inside the characters area,
 not around it.  COLOR is the name of the color to draw in, default is
-the foreground color of the face for simple boxes, and the background
-color of the face for 3D boxes.  STYLE specifies whether a 3D box
-should be draw.  If STYLE is `released-button', draw a box looking
-like a released 3D button.  If STYLE is `pressed-button' draw a box
-that appears like a pressed button.  If STYLE is nil, the default if
-the property list doesn't contain a style specification, draw a 2D
-box.
+the background color of the face for 3D boxes and `flat-button', and
+the foreground color of the face for other boxes.  STYLE specifies
+whether a 3D box should be draw.  If STYLE is `released-button', draw
+a box looking like a released 3D button.  If STYLE is `pressed-button'
+draw a box that appears like a pressed button.  If STYLE is nil,
+`flat-button' or omitted, draw a 2D box.
 
 `:inverse-video'
 
@@ -2201,8 +2200,13 @@ the X resource \"reverseVideo\" is present, handle that."
                           border-color cursor-color mouse-color
                           visibility scroll-bar-foreground
                           scroll-bar-background))
+         (delayed-font nil)
         frame success)
     (dolist (param delayed-params)
+      ;; Save the font used here.  Once the frame is created, set the
+      ;; `font-parameter' frame parameter.
+      (when (and (eq param 'font) (assq 'font parameters))
+        (setq delayed-font (cdr (assq 'font parameters))))
       (setq params (assq-delete-all param params)))
     (setq frame (x-create-frame `((visibility . nil) . ,params)))
     (unwind-protect
@@ -2213,6 +2217,11 @@ the X resource \"reverseVideo\" is present, handle that."
          (x-handle-reverse-video frame parameters)
          (frame-set-background-mode frame t)
          (face-set-after-frame-default frame parameters)
+          ;; The code above will not set the `font-parameter' frame
+          ;; property, which is used by dynamic-setting.el to respect
+          ;; fonts specified by the user via frame parameters (as
+          ;; opposed to face attributes).  Set the parameter manually.
+          (set-frame-parameter frame 'font-parameter delayed-font)
           ;; Mark frame as 'was-invisible' when it was created as
           ;; invisible or iconified and PARAMETERS contains either a
           ;; width or height specification.  This should be sufficient
diff --git a/lisp/files.el b/lisp/files.el
index a282532258..b947451369 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -7294,7 +7294,7 @@ by `sh' are supported."
                              (setq i (1+ i))
                              "[]"))
                           (t "["))
-                         (prog1        ; copy everything upto next `]'.
+                         (prog1        ; copy everything up to next `]'.
                              (substring wildcard
                                         i
                                         (setq j (string-search
diff --git a/lisp/filesets.el b/lisp/filesets.el
index aeebd907c3..9f07072e78 100644
--- a/lisp/filesets.el
+++ b/lisp/filesets.el
@@ -2390,6 +2390,7 @@ fileset thinks this is necessary or not."
   (filesets-menu-cache-file-load))
 
 (defun filesets-update-pre010505 ()
+  (declare (obsolete nil "29.1"))
   (let ((msg (format-message
 "Filesets: manual editing of user data required!
 
@@ -2435,7 +2436,8 @@ We apologize for the inconvenience.")))
    ((or (not cached-version)
        (string< cached-version "1.5.5")
        (boundp 'filesets-subdocument-patterns))
-    (filesets-update-pre010505)))
+    (with-suppressed-warnings ((obsolete filesets-update-pre010505))
+      (filesets-update-pre010505))))
   (filesets-update-cleanup))
 
 (defun filesets-menu-cache-file-load ()
diff --git a/lisp/font-lock.el b/lisp/font-lock.el
index d132de3a32..bf9a179d6a 100644
--- a/lisp/font-lock.el
+++ b/lisp/font-lock.el
@@ -614,6 +614,14 @@ If it fontifies a larger region, it should ideally return 
a list of the form
 \(jit-lock-bounds BEG . END) indicating the bounds of the region actually
 fontified.")
 
+(defvar font-lock-fontify-syntactically-function
+  #'font-lock-default-fontify-syntactically
+  "Function to use for syntactically fontifying a region.
+
+It should take two args, the beginning and end of the region, and
+an optional third arg VERBOSE.  If VERBOSE is non-nil, the
+function should print status messages.")
+
 (defvar font-lock-unfontify-region-function 
#'font-lock-default-unfontify-region
   "Function to use for unfontifying a region.
 It should take two args, the beginning and end of the region.
@@ -932,7 +940,7 @@ The value of this variable is used when Font Lock mode is 
turned on.")
 ;; A further reason to use the fontification indirection feature is when the
 ;; default syntactic fontification, or the default fontification in general,
 ;; is not flexible enough for a particular major mode.  For example, perhaps
-;; comments are just too hairy for `font-lock-fontify-syntactically-region' to
+;; comments are just too hairy for `font-lock-default-fontify-syntactically' to
 ;; cope with.  You need to write your own version of that function, e.g.,
 ;; `hairy-fontify-syntactically-region', and make your own version of
 ;; `hairy-fontify-region' call that function before calling
@@ -942,6 +950,10 @@ The value of this variable is used when Font Lock mode is 
turned on.")
 ;; example, TeX modes could fontify {\foo ...} and \bar{...}  etc. multi-line
 ;; directives correctly and cleanly.  (It is the same problem as fontifying
 ;; multi-line strings and comments; regexps are not appropriate for the job.)
+;; (This comment is written before `font-lock-default-fontify-syntactically'
+;; can be replaced.  Now you can obviously replace
+;; `font-lock-default-fontify-syntactically' with a custom function.)
+
 
 (defvar-local font-lock-extend-after-change-region-function nil
   "A function that determines the region to refontify after a change.
@@ -1171,7 +1183,7 @@ This function is the default 
`font-lock-fontify-region-function'."
            (setq font-lock-syntactically-fontified end))
          (font-lock-fontify-syntactic-keywords-region start end)))
      (unless font-lock-keywords-only
-       (font-lock-fontify-syntactically-region beg end loudly))
+       (funcall font-lock-fontify-syntactically-function beg end loudly))
      (font-lock-fontify-keywords-region beg end loudly)
      `(jit-lock-bounds ,beg . ,end))))
 
@@ -1519,7 +1531,7 @@ START should be at the beginning of a line."
 (defvar font-lock-comment-end-skip nil
   "If non-nil, Font Lock mode uses this instead of `comment-end-skip'.")
 
-(defun font-lock-fontify-syntactically-region (start end &optional loudly)
+(defun font-lock-default-fontify-syntactically (start end &optional loudly)
   "Put proper face on each string and comment between START and END.
 START should be at the beginning of a line."
   (syntax-propertize end)  ; Apply any needed syntax-table properties.
@@ -2071,6 +2083,55 @@ as the constructs of Haddock, Javadoc and similar 
systems."
   "Font Lock mode face used to highlight grouping constructs in Lisp regexps."
   :group 'font-lock-faces)
 
+(defface font-lock-escape-face
+  '((t :inherit font-lock-regexp-grouping-backslash))
+  "Font Lock mode face used to highlight escape sequences in strings."
+  :group 'font-lock-faces
+  :version "29.1")
+
+(defface font-lock-number-face
+  '((t nil))
+  "Font Lock mode face used to highlight numbers."
+  :group 'font-lock-faces
+  :version "29.1")
+
+(defface font-lock-operator-face
+  '((t nil))
+  "Font Lock mode face used to highlight operators."
+  :group 'font-lock-faces
+  :version "29.1")
+
+(defface font-lock-property-face
+  '((t :inherit font-lock-variable-name-face))
+  "Font Lock mode face used to highlight properties of an object.
+For example, the declaration and use of fields in a struct."
+  :group 'font-lock-faces
+  :version "29.1")
+
+(defface font-lock-punctuation-face
+  '((t nil))
+  "Font Lock mode face used to highlight punctuation."
+  :group 'font-lock-faces
+  :version "29.1")
+
+(defface font-lock-bracket-face
+  '((t :inherit font-lock-punctuation-face))
+  "Font Lock mode face used to highlight brackets, braces, and parens."
+  :group 'font-lock-faces
+  :version "29.1")
+
+(defface font-lock-delimiter-face
+  '((t :inherit font-lock-punctuation-face))
+  "Font Lock mode face used to highlight delimiters."
+  :group 'font-lock-faces
+  :version "29.1")
+
+(defface font-lock-misc-punctuation-face
+  '((t :inherit font-lock-punctuation-face))
+  "Font Lock mode face used to highlight miscellaneous punctuation."
+  :group 'font-lock-faces
+  :version "29.1")
+
 ;; End of Color etc. support.
 
 ;;; Menu support.
diff --git a/lisp/forms.el b/lisp/forms.el
index b97fdbe04c..97c7cb79d3 100644
--- a/lisp/forms.el
+++ b/lisp/forms.el
@@ -487,17 +487,11 @@ Commands:                        Equivalent keys in 
read-only mode:
        (make-local-variable 'forms-insert-after)
        (make-local-variable 'forms-use-text-properties)
 
-       ;; Filter functions.
-       (make-local-variable 'forms-read-file-filter)
-       (make-local-variable 'forms-write-file-filter)
-       (make-local-variable 'forms-new-record-filter)
-       (make-local-variable 'forms-modified-record-filter)
-
-       ;; Make sure no filters exist.
-       (setq forms-read-file-filter nil)
-       (setq forms-write-file-filter nil)
-       (setq forms-new-record-filter nil)
-       (setq forms-modified-record-filter nil)
+        ;; Make sure no filters exist.
+        (setq-local forms-read-file-filter nil)
+        (setq-local forms-write-file-filter nil)
+        (setq-local forms-new-record-filter nil)
+        (setq-local forms-modified-record-filter nil)
 
        ;; Setup faces to show read-only and read-write fields.
        (make-local-variable 'forms-ro-face)
@@ -615,10 +609,10 @@ Commands:                        Equivalent keys in 
read-only mode:
   (make-local-variable 'forms--the-record-list)
   (make-local-variable 'forms--search-regexp)
 
-  ; The keymaps are global, so multiple forms mode buffers can share them.
-  ;(make-local-variable 'forms-mode-map)
-  ;(make-local-variable 'forms-mode-ro-map)
-  ;(make-local-variable 'forms-mode-edit-map)
+  ;; The keymaps are global, so multiple forms mode buffers can share them.
+  ;;(make-local-variable 'forms-mode-map)
+  ;;(make-local-variable 'forms-mode-ro-map)
+  ;;(make-local-variable 'forms-mode-edit-map)
   (if forms-mode-map                   ; already defined
       nil
     ;;(message "forms: building keymap...")
@@ -715,8 +709,8 @@ Commands:                        Equivalent keys in 
read-only mode:
   ;;(message "forms: setting up... done.")
 
   ;; be helpful
-  (forms--help)
-)
+  (forms--help))
+
 
 (defun forms--process-format-list ()
   ;; Validate `forms-format-list' and set some global variables.
diff --git a/lisp/gnus/ChangeLog.3 b/lisp/gnus/ChangeLog.3
index 8087021a7c..a1ad22fd62 100644
--- a/lisp/gnus/ChangeLog.3
+++ b/lisp/gnus/ChangeLog.3
@@ -1109,7 +1109,7 @@
 
        * gnus-icalendar.el (gnus-icalendar-event:sync-to-org)
        (gnus-icalendar-event:inline-org-buttons): Allow for appointment
-       cancellations to be synced to org if the original appt has an org
+       cancelations to be synced to org if the original appt has an org
        outline.
 
 2013-11-13  Jan Tatarik  <jan.tatarik@gmail.com>
@@ -4243,7 +4243,7 @@
        * gnus-msg.el (gnus-inews-do-gcc):
        * message.el (message-send-mail):
        * mml.el (mml-generate-mime): Share the value of the buffer-local
-       `message-options' variable between a draft buffer and temprary working
+       `message-options' variable between a draft buffer and temporary working
        buffers.
 
 2011-11-30  Stefan Monnier  <monnier@iro.umontreal.ca>
@@ -10876,7 +10876,7 @@
 2010-09-20  Lars Magne Ingebrigtsen  <larsi@gnus.org>
 
        * gnus-group.el (gnus-group-line-format-alist): Have the ?U (unseen)
-       spec inser "*" if the group isn't active instead of 0.
+       spec insert "*" if the group isn't active instead of 0.
 
        * nnimap.el (nnimap-request-group): Don't select the imap buffer before
        opening the server.
diff --git a/lisp/gnus/gnus-group.el b/lisp/gnus/gnus-group.el
index e69f0857e7..c7ec65da79 100644
--- a/lisp/gnus/gnus-group.el
+++ b/lisp/gnus/gnus-group.el
@@ -393,17 +393,15 @@ variables in the Lisp expression:
   "Add Icons to your group buffer."
   :group 'gnus-group-visual)
 
-(defcustom gnus-group-icon-list
-  nil
+(defcustom gnus-group-icon-list nil
   "Controls the insertion of icons into group buffer lines.
 
 Below is a list of `Form'/`File' pairs.  When deciding how a
 particular group line should be displayed, each form is evaluated.
 The icon from the file field after the first true form is used.  You
 can change how those group lines are displayed by editing the file
-field.  The File will either be found in the
-`gnus-group-glyph-directory' or by designating absolute name of the
-file.
+field.  The File will either be found in the `image-load-path'
+or by specifying the absolute name of the file.
 
 It is also possible to change and add form fields, but currently that
 requires an understanding of Lisp expressions.  Hopefully this will
diff --git a/lisp/gnus/gnus-search.el b/lisp/gnus/gnus-search.el
index b8f7e7a08f..7941496be6 100644
--- a/lisp/gnus/gnus-search.el
+++ b/lisp/gnus/gnus-search.el
@@ -1943,7 +1943,7 @@ Assume \"size\" key is equal to \"larger\"."
        (thread (alist-get 'thread query)))
     (with-slots (switches config-directory) engine
       `("find"                         ; command must come first
-       "--nocolor"             ; mu will always give coloured output otherwise
+       "--nocolor"             ; mu will always give colored output otherwise
        ,(format "--muhome=%s" config-directory)
        ,@switches
        ,(if thread "-r" "")
diff --git a/lisp/gnus/gnus-start.el b/lisp/gnus/gnus-start.el
index 4963fd083f..a4962f9d5f 100644
--- a/lisp/gnus/gnus-start.el
+++ b/lisp/gnus/gnus-start.el
@@ -173,7 +173,7 @@ properly with all servers."
 
 Groups with levels less than `gnus-level-subscribed', which
 should be less than this variable, are subscribed.  Groups with
-levels from `gnus-level-subscribed' (exclusive) upto this
+levels from `gnus-level-subscribed' (exclusive) up to this
 variable (inclusive) are unsubscribed.  See also
 `gnus-level-zombie', `gnus-level-killed' and the Info node `(gnus)Group
 Levels' for details.")
diff --git a/lisp/gnus/message.el b/lisp/gnus/message.el
index 3bbd68bdcd..e0677ff6be 100644
--- a/lisp/gnus/message.el
+++ b/lisp/gnus/message.el
@@ -7520,7 +7520,7 @@ to match all of yours addresses."
 ;;;###autoload
 (defun message-cancel-news (&optional arg)
   "Cancel an article you posted.
-If ARG, allow editing of the cancellation message."
+If ARG, allow editing of the cancelation message."
   (interactive "P")
   (unless (message-news-p)
     (error "This is not a news article; canceling is impossible"))
@@ -8811,8 +8811,6 @@ headers.  If FORCE, insert new field even if NEW-VALUE is 
empty."
        "bug-gnu-emacs@gnu.org")
   "Mail addresses that have no full name.
 Used in `message-simplify-recipients'."
-  ;; Maybe the addresses could be extracted from
-  ;; `gnus-parameter-to-list-alist'?
   :type '(choice (const :tag "None" nil)
                 (repeat string))
   :version "23.1" ;; No Gnus
diff --git a/lisp/gnus/mml.el b/lisp/gnus/mml.el
index e8291cfe6f..ebd0adf2e2 100644
--- a/lisp/gnus/mml.el
+++ b/lisp/gnus/mml.el
@@ -1446,7 +1446,7 @@ will be computed and used."
     (mml-insert-empty-tag 'part
                          'type type
                          ;; icicles redefines read-file-name and returns a
-                         ;; string w/ text properties :-/
+                          ;; string with text properties :-/
                          'filename (substring-no-properties file)
                          'disposition (or disposition "attachment")
                          'description description)
diff --git a/lisp/gnus/nnrss.el b/lisp/gnus/nnrss.el
index 99e7b2a6f3..66cee52865 100644
--- a/lisp/gnus/nnrss.el
+++ b/lisp/gnus/nnrss.el
@@ -77,7 +77,7 @@ this variable to the list of fields to be ignored.")
   "List of RSS addresses.")
 
 (defvar nnrss-use-local nil
-  "If non-nil nnrss will read the feeds from local files in nnrss-directory.")
+  "If non-nil nnrss will read the feeds from local files in 
`nnrss-directory'.")
 
 (defvar nnrss-description-field 'X-Gnus-Description
   "Field name used for DESCRIPTION.
@@ -398,7 +398,7 @@ otherwise return nil."
 (declare-function libxml-parse-html-region "xml.c"
                  (start end &optional base-url discard-comments))
 (defun nnrss-fetch (url &optional local)
-  "Fetch URL and put it in a the expected Lisp structure."
+  "Fetch URL and put it in the expected Lisp structure."
   (mm-with-unibyte-buffer
     ;;some versions of url.el need this to close the connection quickly
     (let (cs xmlform htmlform)
@@ -800,7 +800,7 @@ It is useful when `(setq nnrss-use-local t)'."
     node))
 
 (defun nnrss-find-el (tag data &optional found-list)
-  "Find the all matching elements in the data.
+  "Find all the matching elements in the data.
 Careful with this on large documents!"
   (when (consp data)
     (dolist (bit data)
diff --git a/lisp/gnus/nnvirtual.el b/lisp/gnus/nnvirtual.el
index e150cbf2b4..eec0347bcf 100644
--- a/lisp/gnus/nnvirtual.el
+++ b/lisp/gnus/nnvirtual.el
@@ -172,7 +172,7 @@ It is computed from the marks of individual component 
groups.")
              (with-current-buffer nntp-server-buffer
                (erase-buffer)
                (insert-buffer-substring vbuf)
-               ;; FIX FIX FIX, we should be able to sort faster than
+                ;; FIXME: we should be able to sort faster than
                ;; this if needed, since each cgroup is sorted, we just
                ;; need to merge
                (sort-numeric-fields 1 (point-min) (point-max))
diff --git a/lisp/help.el b/lisp/help.el
index b25a8ce299..f956111a52 100644
--- a/lisp/help.el
+++ b/lisp/help.el
@@ -1945,10 +1945,7 @@ of a horizontal combination, restrain its new size by
 `fit-window-to-buffer-horizontally' can inhibit resizing.
 
 If WINDOW is the root window of its frame, resize the frame
-provided `fit-frame-to-buffer' is non-nil.
-
-This function may call `preserve-window-size' to preserve the
-size of WINDOW."
+provided `fit-frame-to-buffer' is non-nil."
   (setq window (window-normalize-window window t))
   (let* ((buffer (window-buffer window))
          (height (if (functionp temp-buffer-max-height)
diff --git a/lisp/htmlfontify.el b/lisp/htmlfontify.el
index 34092b1417..df4c6ab079 100644
--- a/lisp/htmlfontify.el
+++ b/lisp/htmlfontify.el
@@ -1937,7 +1937,7 @@ Otherwise, the link should be to the index file.
 We are not yet concerned with the file extensions/tag line number and so on at
 this point.
 
-If `hfy-split-index' is set, and the href wil be to an index file rather than
+If `hfy-split-index' is set, and the href will be to an index file rather than
 a source file, append a .X to `hfy-index-file', where X is the uppercased
 first character of TAG.
 
diff --git a/lisp/info.el b/lisp/info.el
index 02dde50dd6..8860a664bd 100644
--- a/lisp/info.el
+++ b/lisp/info.el
@@ -2666,7 +2666,7 @@ new buffer."
 (defconst Info-menu-entry-name-re "\\(?:[^:]\\|:[^:,.;() \t\n]\\)*"
   ;; We allow newline because this is also used in Info-follow-reference,
   ;; where the xref name might be wrapped over two lines.
-  "Regexp that matches a menu entry name upto but not including the colon.
+  "Regexp that matches a menu entry name up to but not including the colon.
 Because of ambiguities, this should be concatenated with something like
 `:' and `Info-following-node-name-re'.")
 
diff --git a/lisp/international/titdic-cnv.el b/lisp/international/titdic-cnv.el
index d2a6ee1e9d..b942f5fabc 100644
--- a/lisp/international/titdic-cnv.el
+++ b/lisp/international/titdic-cnv.el
@@ -59,6 +59,13 @@
 ;; Near the end of this file, we also have a few other tools to convert
 ;; miscellaneous dictionaries.
 
+;; Note: This file includes several codepoints outside of the Unicode
+;; 0..#x10FFFF range, which are characters that were not unified into
+;; Unicode.  Therefore, this file is encoded in utf-8-emacs, because
+;; UTF-8 cannot encode such codepoints.  We include these codepoints
+;; literally in the file to have them displayed by suitable fonts,
+;; which makes maintenance easier.
+
 ;;; Code:
 
 (require 'quail)
diff --git a/lisp/jsonrpc.el b/lisp/jsonrpc.el
index 90833e1c1d..1387fa3692 100644
--- a/lisp/jsonrpc.el
+++ b/lisp/jsonrpc.el
@@ -308,7 +308,7 @@ ignored."
                          (setq canceled t))
                        `(canceled ,cancel-on-input-retval))
                       (t (while t (accept-process-output nil 30)))))
-            ;; In normal operation, cancellation is handled by the
+            ;; In normal operation, cancelation is handled by the
             ;; timeout function and response filter, but we still have
             ;; to protect against user-quit (C-g) or the
             ;; `cancel-on-input' case.
diff --git a/lisp/keymap.el b/lisp/keymap.el
index 107565590c..eaeba96644 100644
--- a/lisp/keymap.el
+++ b/lisp/keymap.el
@@ -559,22 +559,45 @@ In addition to the keywords accepted by `define-keymap', 
this
 macro also accepts a `:doc' keyword, which (if present) is used
 as the variable documentation string.
 
-\(fn VARIABLE-NAME &key DOC FULL PARENT SUPPRESS NAME PREFIX KEYMAP &rest [KEY 
DEFINITION]...)"
+The `:repeat' keyword can also be specified; it controls the
+`repeat-mode' behavior of the bindings in the keymap.  When it is
+non-nil, all commands in the map will have the `repeat-map'
+symbol property.
+
+More control is available over which commands are repeatable; the
+value can also be a property list with properties `:enter' and
+`:exit', for example:
+
+     :repeat (:enter (commands ...) :exit (commands ...))
+
+`:enter' specifies the list of additional commands that only
+enter `repeat-mode'.  When the list is empty, then by default all
+commands in the map enter `repeat-mode'.  This is useful when
+there is a command that has the `repeat-map' symbol property, but
+doesn't exist in this specific map.  `:exit' is a list of
+commands that exit `repeat-mode'.  When the list is empty, no
+commands in the map exit `repeat-mode'.  This is useful when a
+command exists in this specific map, but it doesn't have the
+`repeat-map' symbol property on its symbol.
+
+\(fn VARIABLE-NAME &key DOC FULL PARENT SUPPRESS NAME PREFIX KEYMAP REPEAT 
&rest [KEY DEFINITION]...)"
   (declare (indent 1))
   (let ((opts nil)
-        doc)
+        doc repeat props)
     (while (and defs
                 (keywordp (car defs))
                 (not (eq (car defs) :menu)))
       (let ((keyword (pop defs)))
         (unless defs
           (error "Uneven number of keywords"))
-        (if (eq keyword :doc)
-            (setq doc (pop defs))
-          (push keyword opts)
-          (push (pop defs) opts))))
+        (cond
+         ((eq keyword :doc) (setq doc (pop defs)))
+         ((eq keyword :repeat) (setq repeat (pop defs)))
+         (t (push keyword opts)
+            (push (pop defs) opts)))))
     (unless (zerop (% (length defs) 2))
       (error "Uneven number of key/definition pairs: %s" defs))
+
     (let ((defs defs)
           key seen-keys)
       (while defs
@@ -585,9 +608,28 @@ as the variable documentation string.
               (error "Duplicate definition for key '%s' in keymap '%s'"
                      key variable-name)
             (push key seen-keys)))))
-    `(defvar ,variable-name
-       (define-keymap ,@(nreverse opts) ,@defs)
-       ,@(and doc (list doc)))))
+
+    (when repeat
+      (let ((defs defs)
+            def)
+        (dolist (def (plist-get repeat :enter))
+          (push `(put ',def 'repeat-map ',variable-name) props))
+        (while defs
+          (pop defs)
+          (setq def (pop defs))
+          (when (and (memq (car def) '(function quote))
+                     (not (memq (cadr def) (plist-get repeat :exit))))
+            (push `(put ,def 'repeat-map ',variable-name) props)))))
+
+    (let ((defvar-form
+           `(defvar ,variable-name
+              (define-keymap ,@(nreverse opts) ,@defs)
+              ,@(and doc (list doc)))))
+      (if repeat
+          `(progn
+             ,defvar-form
+             ,@(nreverse props))
+        defvar-form))))
 
 (defun make-non-key-event (symbol)
   "Mark SYMBOL as an event that shouldn't be returned from `where-is'."
diff --git a/lisp/language/ethio-util.el b/lisp/language/ethio-util.el
index 2f76acfe7c..5b4252d779 100644
--- a/lisp/language/ethio-util.el
+++ b/lisp/language/ethio-util.el
@@ -30,6 +30,13 @@
 
 ;;; Commentary:
 
+;; Note: This file includes several codepoints outside of the Unicode
+;; 0..#x10FFFF range, which are characters that were not unified into
+;; Unicode.  Therefore, this file is encoded in utf-8-emacs, because
+;; UTF-8 cannot encode such codepoints.  We include these codepoints
+;; literally in the file to have them displayed by suitable fonts,
+;; which makes maintenance easier.
+
 ;;; Code:
 
 (require 'robin)
@@ -105,6 +112,8 @@
 
 (defcustom ethio-primary-language 'tigrigna
   "Symbol that defines the primary language in SERA --> FIDEL conversion.
+FIDEL is the Amharic alphabet; SERA (System for Ethiopic Representation
+in ASCII) is the Latin representation of Ethiopic script.
 The value should be one of: `tigrigna', `amharic' or `english'."
   :version "28.1"
   :type '(choice (const :tag "Tigrigna" tigrigna)
@@ -113,6 +122,8 @@ The value should be one of: `tigrigna', `amharic' or 
`english'."
 
 (defcustom ethio-secondary-language 'english
   "Symbol that defines the secondary language in SERA --> FIDEL conversion.
+FIDEL is the Amharic alphabet; SERA (System for Ethiopic Representation
+in ASCII) is the Latin representation of Ethiopic script.
 The value should be one of: `tigrigna', `amharic' or `english'."
   :version "28.1"
   :type '(choice (const :tag "Tigrigna" tigrigna)
@@ -123,7 +134,9 @@ The value should be one of: `tigrigna', `amharic' or 
`english'."
   "Non-nil means associate ASCII colon with Ethiopic colon.
 If nil, associate ASCII colon with Ethiopic word separator, i.e., two
 vertically stacked dots.  All SERA <--> FIDEL converters refer this
-variable."
+variable.
+FIDEL is the Amharic alphabet; SERA (System for Ethiopic Representation
+in ASCII) is the Latin representation of Ethiopic script."
   :version "28.1"
   :type 'boolean)
 
@@ -131,7 +144,9 @@ variable."
   "If non-nil, associate ASCII question mark with Ethiopic question mark.
 The Ethiopic old style question mark is three vertically stacked dots.
 If nil, associate ASCII question mark with Ethiopic stylized question
-mark.  All SERA <--> FIDEL converters refer this variable."
+mark.  All SERA <--> FIDEL converters refer this variable.
+FIDEL is the Amharic alphabet; SERA (System for Ethiopic Representation
+in ASCII) is the Latin representation of Ethiopic script."
   :version "28.1"
   :type 'boolean)
 
@@ -140,13 +155,17 @@ mark.  All SERA <--> FIDEL converters refer this 
variable."
 This happens in FIDEL --> SERA conversions.  Isolated vowels at
 word beginning do not get an apostrophe put before them.
 If nil, put an apostrophe only between a 6th-form consonant and an
-isolated vowel."
+isolated vowel.
+FIDEL is the Amharic alphabet; SERA (System for Ethiopic Representation
+in ASCII) is the Latin representation of Ethiopic script."
   :version "28.1"
   :type 'boolean)
 
 (defcustom ethio-W-sixth-always nil
   "Non-nil means convert the Wu-form of a 12-form consonant to \"W'\".
-This is instead of \"Wu\" in FIDEL --> SERA conversion."
+This is instead of \"Wu\" in FIDEL --> SERA conversion.
+FIDEL is the Amharic alphabet; SERA (System for Ethiopic Representation
+in ASCII) is the Latin representation of Ethiopic script."
   :version "28.1"
   :type 'boolean)
 
@@ -216,6 +235,8 @@ If nil, use uppercases."
 (defun ethio-sera-to-fidel-buffer (&optional secondary force)
   "Convert the current buffer from SERA to FIDEL.
 
+FIDEL is the Amharic alphabet; SERA (System for Ethiopic Representation
+in ASCII) is the Latin representation of Ethiopic script.
 The variable `ethio-primary-language' specifies the primary
 language and `ethio-secondary-language' specifies the secondary.
 
@@ -241,6 +262,8 @@ See also the descriptions of the variables
 (defun ethio-sera-to-fidel-region (begin end &optional secondary force)
   "Convert the characters in region from SERA to FIDEL.
 
+FIDEL is the Amharic alphabet; SERA (System for Ethiopic Representation
+in ASCII) is the Latin representation of Ethiopic script.
 The variable `ethio-primary-language' specifies the primary
 language and `ethio-secondary-language' specifies the secondary.
 
@@ -496,7 +519,9 @@ changing anything."
 
 ;;;###autoload
 (defun ethio-sera-to-fidel-marker (&optional force)
-  "Convert the regions surrounded by \"<sera>\" and \"</sera>\" from SERA to 
FIDEL.
+  "Convert regions surrounded by \"<sera>\" and \"</sera>\" from SERA to FIDEL.
+FIDEL is the Amharic alphabet; SERA (System for Ethiopic Representation
+in ASCII) is the Latin representation of Ethiopic script.
 Assume that each region begins with `ethio-primary-language'.
 The markers \"<sera>\" and \"</sera>\" themselves are not deleted."
   (interactive "P")
@@ -528,7 +553,9 @@ The markers \"<sera>\" and \"</sera>\" themselves are not 
deleted."
 
 ;;;###autoload
 (defun ethio-fidel-to-sera-buffer (&optional secondary force)
-  "Replace all the FIDEL characters in the current buffer to the SERA format.
+  "Convert all the FIDEL characters in the current buffer to the SERA format.
+FIDEL is the Amharic alphabet; SERA (System for Ethiopic Representation
+in ASCII) is the Latin representation of Ethiopic script.
 The variable `ethio-primary-language' specifies the primary
 language and `ethio-secondary-language' specifies the secondary.
 
@@ -548,8 +575,10 @@ See also the descriptions of the variables
 
 ;;;###autoload
 (defun ethio-fidel-to-sera-region (begin end &optional secondary force)
-  "Replace all the FIDEL characters in the region to the SERA format.
+  "Convert all the FIDEL characters in the region to the SERA format.
 
+FIDEL is the Amharic alphabet; SERA (System for Ethiopic Representation
+in ASCII) is the Latin representation of Ethiopic script.
 The variable `ethio-primary-language' specifies the primary
 language and `ethio-secondary-language' specifies the secondary.
 
@@ -667,6 +696,8 @@ See also the descriptions of the variables
 ;;;###autoload
 (defun ethio-fidel-to-sera-marker (&optional force)
   "Convert the regions surrounded by \"<sera>\" and \"</sera>\" from FIDEL to 
SERA.
+FIDEL is the Amharic alphabet; SERA (System for Ethiopic Representation
+in ASCII) is the Latin representation of Ethiopic script.
 The markers \"<sera>\" and \"</sera>\" themselves are not deleted."
 
   (interactive "P")
@@ -691,7 +722,8 @@ The markers \"<sera>\" and \"</sera>\" themselves are not 
deleted."
 
 ;;;###autoload
 (defun ethio-modify-vowel nil
-  "Modify the vowel of the FIDEL that is under the cursor."
+  "Modify the vowel of the FIDEL that is under the cursor.
+FIDEL is the Amharic/Ethiopic alphabet."
   (interactive)
   (ethio-adjust-robin)
   (let ((consonant (ethio-get-consonant (following-char)))
@@ -708,7 +740,9 @@ The markers \"<sera>\" and \"</sera>\" themselves are not 
deleted."
          (robin-convert-region (point-min) (point-max) "ethiopic-sera"))))))
 
 (defun ethio-get-consonant (ch)
-  "Return the consonant part of CH's SERA spelling in ethiopic-sera."
+  "Return the consonant part of CH's SERA spelling in ethiopic-sera.
+SERA (System for Ethiopic Representation in ASCII) is the Latin
+representation of Ethiopic script."
   (let ((sera (get-char-code-property ch 'ethiopic-sera)))
     (cond
      ((null sera) nil)
@@ -813,7 +847,8 @@ The 2nd and 3rd arguments BEGIN and END specify the region."
 
 ;;;###autoload
 (defun ethio-fidel-to-tex-buffer nil
-  "Convert each fidel characters in the current buffer into a fidel-tex 
command."
+  "Convert each FIDEL characters in the current buffer into a fidel-tex 
command.
+FIDEL is the Amharic/Ethiopic alphabet."
   (interactive)
   (let ((buffer-read-only nil)
        comp)
@@ -860,7 +895,8 @@ The 2nd and 3rd arguments BEGIN and END specify the region."
 
 ;;;###autoload
 (defun ethio-tex-to-fidel-buffer ()
-  "Convert fidel-tex commands in the current buffer into fidel chars."
+  "Convert fidel-tex commands in the current buffer into FIDEL chars.
+FIDEL is the Amharic/Ethiopic alphabet."
   (interactive)
   (let ((inhibit-read-only t)
        ;; (p) (ch)
@@ -888,7 +924,7 @@ The 2nd and 3rd arguments BEGIN and END specify the region."
 
 ;;;###autoload
 (defun ethio-fidel-to-java-buffer nil
-  "Convert Ethiopic characters into the Java escape sequences.
+  "Convert Ethiopic characters in the buffer into the Java escape sequences.
 
 Each escape sequence is of the form \\uXXXX, where XXXX is the
 character's codepoint (in hex) in Unicode.
@@ -907,7 +943,7 @@ Otherwise, [0-9A-F]."
 
 ;;;###autoload
 (defun ethio-java-to-fidel-buffer nil
-  "Convert the Java escape sequences into corresponding Ethiopic characters."
+  "Convert the Java escape sequences in the buffer into Ethiopic characters."
   (let ((case-fold-search t)
        (ucode))
     (goto-char (point-min))
@@ -922,7 +958,17 @@ Otherwise, [0-9A-F]."
 
 ;;;###autoload
 (defun ethio-find-file nil
-  "Transliterate file content into Ethiopic depending on filename suffix."
+  "Transliterate file content into Ethiopic depending on filename suffix.
+If the file-name extension is \".sera\", convert from SERA to FIDEL.
+If the file-name extension is \".html\", convert regions enclosed
+by \"<sera>..</sera>\" from SERA to FIDEL.
+If the file-name extension is \".tex\", convert fidel-tex commands
+to FIDEL characters.
+If the file-name extension is \".java\", convert Java escape sequences
+to FIDEL characters.
+
+FIDEL is the Amharic alphabet; SERA (System for Ethiopic Representation
+in ASCII) is the Latin representation of Ethiopic script."
   (cond
 
    ((string-match "\\.sera$" (buffer-file-name))
@@ -956,7 +1002,17 @@ Otherwise, [0-9A-F]."
 
 ;;;###autoload
 (defun ethio-write-file nil
-  "Transliterate Ethiopic characters in ASCII depending on the file extension."
+  "Transliterate Ethiopic characters to ASCII depending on the file extension.
+If the file-name extension is \".sera\", convert from FIDEL to SERA.
+If the file-name extension is \".html\", convert FIDEL characters to
+SERA regions enclosed by \"<sera>..</sera>\".
+If the file-name extension is \".tex\", convert FIDEL characters
+to fidel-tex commands.
+If the file-name extension is \".java\", convert FIDEL characters to
+Java escape sequences.
+
+FIDEL is the Amharic alphabet; SERA (System for Ethiopic Representation
+in ASCII) is the Latin representation of Ethiopic script."
   (cond
 
    ((string-match "\\.sera$" (buffer-file-name))
@@ -1062,7 +1118,7 @@ With ARG, insert that many delimiters."
 
 ;; This function is not used any more.
 (defun ethio-gemination nil
-  "Compose the character before the point with the Ethiopic gemination mark.
+  "Compose the character before point with the Ethiopic gemination mark.
 If the character is already composed, decompose it and remove the gemination
 mark."
   (interactive "*")
@@ -1081,7 +1137,9 @@ mark."
 ;;;
 
 (robin-define-package "ethiopic-sera"
- "SERA transliteration system for Ethiopic."
+ "SERA transliteration system for Ethiopic.
+SERA (System for Ethiopic Representation in ASCII) is the Latin
+representation of Ethiopic script."
 
  ("he" ?ሀ)
  ("hu" ?ሁ)
diff --git a/lisp/language/ethiopic.el b/lisp/language/ethiopic.el
index 1faba424ba..fb1e272843 100644
--- a/lisp/language/ethiopic.el
+++ b/lisp/language/ethiopic.el
@@ -27,6 +27,13 @@
 
 ;;; Commentary:
 
+;; Note: This file includes several codepoints outside of the Unicode
+;; 0..#x10FFFF range, which are characters that were not unified into
+;; Unicode.  Therefore, this file is encoded in utf-8-emacs, because
+;; UTF-8 cannot encode such codepoints.  We include these codepoints
+;; literally in the file to have them displayed by suitable fonts,
+;; which makes maintenance easier.
+
 ;;; Code:
 
 (define-ccl-program ccl-encode-ethio-font
diff --git a/lisp/language/ind-util.el b/lisp/language/ind-util.el
index e2a21820f4..447ca0c20c 100644
--- a/lisp/language/ind-util.el
+++ b/lisp/language/ind-util.el
@@ -27,6 +27,13 @@
 ;; Finally, this program provides the compatibility support with
 ;; old implementation of Devanagari script.
 
+;; Note: This file includes several codepoints outside of the Unicode
+;; 0..#x10FFFF range, which are characters that were not unified into
+;; Unicode.  Therefore, this file is encoded in utf-8-emacs, because
+;; UTF-8 cannot encode such codepoints.  We include these codepoints
+;; literally in the file to have them displayed by suitable fonts,
+;; which makes maintenance easier.
+
 ;;; Code:
 
 ;;; Transliteration
diff --git a/lisp/language/tibet-util.el b/lisp/language/tibet-util.el
index e7cb289b65..0f09c48d6d 100644
--- a/lisp/language/tibet-util.el
+++ b/lisp/language/tibet-util.el
@@ -32,6 +32,13 @@
 
 ;;; Commentary:
 
+;; Note: This file includes several codepoints outside of the Unicode
+;; 0..#x10FFFF range, which are characters that were not unified into
+;; Unicode.  Therefore, this file is encoded in utf-8-emacs, because
+;; UTF-8 cannot encode such codepoints.  We include these codepoints
+;; literally in the file to have them displayed by suitable fonts,
+;; which makes maintenance easier.
+
 ;;; Code:
 
 (defconst tibetan-obsolete-glyphs
diff --git a/lisp/language/tibetan.el b/lisp/language/tibetan.el
index 0262798bb2..8121045789 100644
--- a/lisp/language/tibetan.el
+++ b/lisp/language/tibetan.el
@@ -34,6 +34,13 @@
 
 ;;; Commentary:
 
+;; Note: This file includes several codepoints outside of the Unicode
+;; 0..#x10FFFF range, which are characters that were not unified into
+;; Unicode.  Therefore, this file is encoded in utf-8-emacs, because
+;; UTF-8 cannot encode such codepoints.  We include these codepoints
+;; literally in the file to have them displayed by suitable fonts,
+;; which makes maintenance easier.
+
 ;;; Code:
 
 ;;; Tibetan Character set.
diff --git a/lisp/ldefs-boot.el b/lisp/ldefs-boot.el
index c754e72354..3f36cfbba5 100644
--- a/lisp/ldefs-boot.el
+++ b/lisp/ldefs-boot.el
@@ -10010,7 +10010,7 @@ If ERC is already connected to HOST:PORT, simply /join 
CHANNEL.
 Otherwise, connect to HOST:PORT as USER and /join CHANNEL.
 
 (fn HOST PORT CHANNEL USER PASSWORD)")
-(register-definition-prefixes "erc" '("define-erc-module" "erc-"))
+(register-definition-prefixes "erc" '("erc-"))
 
 
 ;;; Generated autoloads from erc/erc-autoaway.el
@@ -10033,6 +10033,11 @@ Otherwise, connect to HOST:PORT as USER and /join 
CHANNEL.
 (register-definition-prefixes "erc-capab" '("erc-capab-identify-"))
 
 
+;;; Generated autoloads from erc/erc-common.el
+
+(register-definition-prefixes "erc-common" '("define-erc-module" "erc-"))
+
+
 ;;; Generated autoloads from erc/erc-compat.el
 
 (register-definition-prefixes "erc-compat" '("erc-"))
@@ -10931,6 +10936,23 @@ Edit the hotlist of directory servers in a specialized 
buffer." t)
 (register-definition-prefixes "eudcb-bbdb" '("eudc-bbdb-"))
 
 
+;;; Generated autoloads from net/eudcb-ecomplete.el
+
+(autoload 'eudc-ecomplete-query-internal "eudcb-ecomplete" "\
+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.
+
+(fn QUERY &optional RETURN-ATTRS)")
+(register-definition-prefixes "eudcb-ecomplete" 
'("eudc-ecomplete-attributes-translation-alist"))
+
+
 ;;; Generated autoloads from net/eudcb-ldap.el
 
 (register-definition-prefixes "eudcb-ldap" '("eudc-"))
@@ -10946,6 +10968,26 @@ Edit the hotlist of directory servers in a specialized 
buffer." t)
 (register-definition-prefixes "eudcb-macos-contacts" '("eudc-macos-contacts-"))
 
 
+;;; Generated autoloads from net/eudcb-mailabbrev.el
+
+(autoload 'eudc-mailabbrev-query-internal "eudcb-mailabbrev" "\
+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'.
+
+(fn QUERY &optional RETURN-ATTRS)")
+
+
 ;;; Generated autoloads from emacs-lisp/ewoc.el
 
 (autoload 'ewoc-create "ewoc" "\
@@ -11280,7 +11322,7 @@ See `text-scale-increase' for more details.
  (define-key ctl-x-map [(control ?0)] 'text-scale-adjust)
 (autoload 'text-scale-adjust "face-remap" "\
 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:
@@ -11290,13 +11332,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
@@ -11322,19 +11365,22 @@ Adjust the height of the default face by the scale in 
the pinch event EVENT.
  (define-key ctl-x-map [(control meta ?-)] 'global-text-scale-adjust)
  (define-key ctl-x-map [(control meta ?0)] 'global-text-scale-adjust)
 (autoload 'global-text-scale-adjust "face-remap" "\
-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.
@@ -21217,6 +21263,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\".
@@ -23064,6 +23113,81 @@ Location of the file used to speed up activation of 
packages at startup." :type
 (register-definition-prefixes "package" '("bad-signature" "define-package" 
"describe-package-1" "package-"))
 
 
+;;; Generated autoloads from emacs-lisp/package-vc.el
+
+(defvar package-vc-selected-packages 'nil "\
+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.")
+(custom-autoload 'package-vc-selected-packages "package-vc" nil)
+(autoload 'package-vc-install "package-vc" "\
+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.
+
+(fn NAME-OR-URL &optional NAME REV BACKEND)" t)
+(autoload 'package-vc-checkout "package-vc" "\
+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.
+
+(fn PKG-DESC DIRECTORY &optional REV)" t)
+(autoload 'package-vc-install-from-checkout "package-vc" "\
+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.
+
+(fn DIR NAME)" t)
+(autoload 'package-vc-refresh "package-vc" "\
+Refresh the installation for package given by PKG-DESC.
+Interactively, prompt for the name of the package to refresh.
+
+(fn PKG-DESC)" t)
+(autoload 'package-vc-prepare-patch "package-vc" "\
+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'.
+
+(fn PKG SUBJECT REVISIONS)" t)
+(register-definition-prefixes "package-vc" '("package-vc-"))
+
+
 ;;; Generated autoloads from emacs-lisp/package-x.el
 
 (autoload 'package-upload-file "package-x" "\
@@ -24616,7 +24740,7 @@ Open profile FILENAME.
 
 ;;; Generated autoloads from progmodes/project.el
 
-(push (purecopy '(project 0 8 2)) package--builtin-versions)
+(push (purecopy '(project 0 8 3)) package--builtin-versions)
 (autoload 'project-current "project" "\
 Return the project instance in DIRECTORY, defaulting to `default-directory'.
 
@@ -26744,6 +26868,8 @@ Return ROT13 encryption of STRING.
 (fn STRING)")
 (autoload 'rot13-region "rot13" "\
 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.
 
 (fn START END)" t)
 (autoload 'rot13-other-window "rot13" "\
diff --git a/lisp/leim/quail/ethiopic.el b/lisp/leim/quail/ethiopic.el
index c8753effe0..df4bf596cb 100644
--- a/lisp/leim/quail/ethiopic.el
+++ b/lisp/leim/quail/ethiopic.el
@@ -26,6 +26,13 @@
 
 ;;; Commentary:
 
+;; Note: This file includes several codepoints outside of the Unicode
+;; 0..#x10FFFF range, which are characters that were not unified into
+;; Unicode.  Therefore, this file is encoded in utf-8-emacs, because
+;; UTF-8 cannot encode such codepoints.  We include these codepoints
+;; literally in the file to have them displayed by suitable fonts,
+;; which makes maintenance easier.
+
 ;;; Code:
 
 (require 'quail)
diff --git a/lisp/leim/quail/indian.el b/lisp/leim/quail/indian.el
index deef00b4c2..8fe81a2217 100644
--- a/lisp/leim/quail/indian.el
+++ b/lisp/leim/quail/indian.el
@@ -373,7 +373,7 @@ Full key sequences are listed below:")
 
 ;; Define the input method straight away.
 (quail-define-package "tamil-phonetic" "Tamil" "ழ" t
- "Customisable Tamil phonetic input method.
+ "Customizable Tamil phonetic input method.
 To change the translation rules of the input method, customize
 `tamil-translation-rules'.
 
diff --git a/lisp/leim/quail/japanese.el b/lisp/leim/quail/japanese.el
index df080fc0e8..fb8b9e6166 100644
--- a/lisp/leim/quail/japanese.el
+++ b/lisp/leim/quail/japanese.el
@@ -359,7 +359,7 @@ input method.
 The input method `japanese-zenkaku' is used to enter full width
 JISX0208 characters corresponding to typed ASCII characters.
 
-List of the all key sequences for Roman-Kana transliteration is shown
+List of all the key sequences for Roman-Kana transliteration is shown
 at the tail.
 
 :: Kana-Kanji conversion ::
diff --git a/lisp/leim/quail/tibetan.el b/lisp/leim/quail/tibetan.el
index ca44f7022d..7f0848d04b 100644
--- a/lisp/leim/quail/tibetan.el
+++ b/lisp/leim/quail/tibetan.el
@@ -33,6 +33,13 @@
 
 ;;; Commentary:
 
+;; Note: This file includes several codepoints outside of the Unicode
+;; 0..#x10FFFF range, which are characters that were not unified into
+;; Unicode.  Therefore, this file is encoded in utf-8-emacs, because
+;; UTF-8 cannot encode such codepoints.  We include these codepoints
+;; literally in the file to have them displayed by suitable fonts,
+;; which makes maintenance easier.
+
 ;;; Code:
 
 (require 'quail)
diff --git a/lisp/mail/feedmail.el b/lisp/mail/feedmail.el
index eb6a071bf4..97d20cca15 100644
--- a/lisp/mail/feedmail.el
+++ b/lisp/mail/feedmail.el
@@ -2766,7 +2766,7 @@ return that value."
   (cond
    ;; nil means do nothing
    ((eq nil feedmail-date-generator) nil)
-   ;; t is the same a using the function feedmail-default-date-generator, so 
let it and recurse
+   ;; t is the same as using the function feedmail-default-date-generator, so 
let it and recurse
    ((eq t feedmail-date-generator)
     (let ((feedmail-date-generator (feedmail-default-date-generator 
maybe-file)))
       (feedmail-fiddle-date maybe-file)))
@@ -2822,7 +2822,7 @@ probably not appropriate for you."
   (cond
    ;; nil means do nothing
    ((eq nil feedmail-message-id-generator) nil)
-   ;; t is the same a using the function 
feedmail-default-message-id-generator, so let it and recurse
+   ;; t is the same as using the function 
feedmail-default-message-id-generator, so let it and recurse
    ((eq t feedmail-message-id-generator)
     (let ((feedmail-message-id-generator 
(feedmail-default-message-id-generator maybe-file)))
       (feedmail-fiddle-message-id maybe-file)))
@@ -2864,7 +2864,7 @@ probably not appropriate for you."
   (cond
    ;; nil means do nothing
    ((eq nil feedmail-x-mailer-line) nil)
-   ;; t is the same a using the function feedmail-default-x-mailer-generator, 
so let it and recurse
+   ;; t is the same as using the function feedmail-default-x-mailer-generator, 
so let it and recurse
    ((eq t feedmail-x-mailer-line)
     (let ((feedmail-x-mailer-line (feedmail-default-x-mailer-generator)))
       (feedmail-fiddle-x-mailer)))
diff --git a/lisp/mail/ietf-drums-date.el b/lisp/mail/ietf-drums-date.el
index ddef7f11b6..034854dce5 100644
--- a/lisp/mail/ietf-drums-date.el
+++ b/lisp/mail/ietf-drums-date.el
@@ -126,7 +126,7 @@ treat them as whitespace (per RFC822)."
 (defun ietf-drums-parse-date-string (time-string &optional error no-822)
   "Parse an RFC5322 or RFC822 date, passed as TIME-STRING.
 The optional ERROR parameter causes syntax errors to be flagged
-by signalling an instance of the date-parse-error condition.  The
+by signaling an instance of the date-parse-error condition.  The
 optional NO-822 parameter disables the more lax RFC822 syntax,
 which is permitted by default.
 
@@ -162,7 +162,7 @@ DST is returned as -1)."
         (time (list nil nil nil nil nil nil nil -1 nil)))
     (cl-labels ((set-matched-slot (slot index token)
                   ;; Assign a slot value from match data if index is
-                  ;; non-nil, else from token, signalling an error if
+                  ;; non-nil, else from token, signaling an error if
                   ;; enabled and it's out of range.
                   (let ((value (if index
                                    (cl-parse-integer (match-string index 
token))
diff --git a/lisp/mail/reporter.el b/lisp/mail/reporter.el
index 324165a8ff..231fa69baa 100644
--- a/lisp/mail/reporter.el
+++ b/lisp/mail/reporter.el
@@ -357,7 +357,7 @@ mail-sending package is used for editing and sending the 
message."
          (goto-char final-resting-place))
       (set-marker final-resting-place nil))
 
-    ;; save initial text and set up the `no-empty-submission' hook.
+    ;; save initial text and set up the no empty submission hook.
     ;; This only works for mailers that support a pre-send hook, and
     ;; for which the paradigm has a non-nil value for the `hookvar'
     ;; key in its agent (i.e. sendmail.el's mail-send-hook).
diff --git a/lisp/mail/rfc6068.el b/lisp/mail/rfc6068.el
index 54035b6698..4863f3582c 100644
--- a/lisp/mail/rfc6068.el
+++ b/lisp/mail/rfc6068.el
@@ -43,9 +43,9 @@ string instead of decoding as utf-8."
 
 (defun rfc6068-parse-mailto-url (mailto-url)
   "Parse MAILTO-URL, and return an alist of header-name, header-value pairs.
-MAILTO-URL should be a RFC 6068 (mailto) compliant url.  A cons cell w/ a
+MAILTO-URL should be a RFC 6068 (mailto) compliant url.  A cons cell with a
 key of `Body' is a special case and is considered a header for this purpose.
-The returned alist is intended for use w/ the `compose-mail' interface.
+The returned alist is intended for use with the `compose-mail' interface.
 Note: make sure MAILTO-URL has been \"unhtmlized\" (e.g., &amp; -> &), before
 calling this function."
   (let ((case-fold-search t)
diff --git a/lisp/mail/rmail.el b/lisp/mail/rmail.el
index e3372a6ff4..2421b283e6 100644
--- a/lisp/mail/rmail.el
+++ b/lisp/mail/rmail.el
@@ -2599,7 +2599,7 @@ is greater than zero; otherwise, show it in full."
   "Handle a \"Mail-Followup-To\" header field with an unknown mailing list.
 Ask the user whether to add that list name to `mail-mailing-lists'."
   ;; FIXME s-r not needed?  Use rmail-get-header?
-  ;; We have not narrowed to the headers at ths point?
+  ;; We have not narrowed to the headers at this point?
    (save-restriction
      (let ((mail-followup-to (mail-fetch-field "mail-followup-to" nil t)))
        (when mail-followup-to
diff --git a/lisp/mail/rmailsum.el b/lisp/mail/rmailsum.el
index 0144a34e5e..b30c32aaff 100644
--- a/lisp/mail/rmailsum.el
+++ b/lisp/mail/rmailsum.el
@@ -50,23 +50,40 @@ 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."
+(defcustom rmail-summary-progressively-narrow nil
+  "Non-nil means progressively narrow the set of messages produced by summary.
+This allows to apply the summary criteria on top one another,
+thus progressively narrowing the selection of the messages produced
+by each summary criteria.
+For example, applying `rmail-summary-by-senders' on top
+of `rmail-summary-by-topic' produces a summary of messages
+with the specified Subjects that were sent from specified
+sending addresses.
+This way, the user can apply one summary on top of another,
+and keep narrowing the resulting list of 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'.")
+  "Boolean vector that tells which messages are displayed in the summary.
+First element is ignored.  Used when applying rmail-summary-by-*
+commands consecutively.  Filled by
+`rmail-summary-populate-displayed-messages'.")
 (put 'rmail-summary-currently-displayed-msgs 'permanent-local t)
 
+(defvar rmail-summary-message-ids-hash-table nil
+  "Hash table linking Message IDs of messages with their indices.")
+
+(defvar rmail-summary-subjects-hash-table nil
+  "Hash table linking subjects with index of the first message with that 
subject.")
+
+(defvar rmail-summary-message-parents-vector nil
+  "Vector that holds a list of indices of parents for each message.
+Message A is parent to message B if the id of A appear in the
+References or In-reply-to fields of B, or if A is the first
+message with the same subject as B.  First element is ignored.")
+
 (defvar rmail-summary-font-lock-keywords
   '(("^ *[0-9]+D.*" . font-lock-string-face)                   ; Deleted.
     ("^ *[0-9]+-.*" . font-lock-type-face)                     ; Unread.
@@ -284,33 +301,85 @@ by `rmail-summary-fill-displayed-messages'.")
 (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."
+(defun rmail-summary-populate-displayed-messages ()
+  "Populate the `rmail-summary-currently-displayed-msgs' vector."
   (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."
+    (let ((totmsgs rmail-total-messages))
+      (with-current-buffer rmail-summary-buffer
+       (setq rmail-summary-currently-displayed-msgs
+             (make-bool-vector (1+ totmsgs) nil))
+       (goto-char (point-min))
+       (while (not (eobp))
+         (aset rmail-summary-currently-displayed-msgs
+               (string-to-number (thing-at-point 'line))
+               t)
+         (forward-line 1))))))
+
+(defun rmail-summary-fill-message-ids-hash-table ()
+  "Fill `rmail-summary-message-ids-hash-table'."
+  (with-current-buffer rmail-buffer
+    (setq rmail-summary-message-ids-hash-table (make-hash-table :test 'equal 
:size 1024))
+    (let ((msgnum 1))
+      (while (<= msgnum rmail-total-messages)
+       (let ((id (rmail-get-header "Message-ID" msgnum)))
+         (puthash id (cons (cons id msgnum) (gethash id 
rmail-summary-message-ids-hash-table))
+                  rmail-summary-message-ids-hash-table))
+       (setq msgnum (1+ msgnum))))))
+
+(defun rmail-summary--split-header-field (name &optional msgnum)
+  (let ((header (rmail-get-header name msgnum)))
+    (if header
+       (split-string header "[ \f\t\n\r\v,;]+"))))
+
+(defun rmail-summary-fill-message-parents-vector ()
+  "Fill `rmail-summary-message-parents-vector'."
+  (with-current-buffer rmail-buffer
+    (rmail-summary-fill-message-ids-hash-table)
+    (setq rmail-summary-subjects-hash-table
+          (make-hash-table :test 'equal :size 1024))
+    (setq rmail-summary-message-parents-vector
+          (make-vector (1+ rmail-total-messages) nil))
+    (let ((msgnum 1))
+      (while (<= msgnum rmail-total-messages)
+       (let* ((parents nil)
+              (subject (rmail-simplified-subject msgnum))
+              (subj-cell (gethash subject rmail-summary-subjects-hash-table))
+              (subj-par (assoc subject subj-cell))
+              (refs (rmail-summary--split-header-field "References" msgnum))
+              (reply-to (rmail-summary--split-header-field "In-reply-to"
+                                                            msgnum)))
+         (if subj-par
+             (setq parents (cons (cdr subj-par) parents))
+           (puthash subject (cons (cons subject msgnum) subj-cell)
+                    rmail-summary-subjects-hash-table))
+         (dolist (id (append refs reply-to))
+           (let ((ent
+                   (assoc id
+                          (gethash id rmail-summary-message-ids-hash-table))))
+             (if ent
+                 (setq parents (cons (cdr ent) parents)))))
+         (aset rmail-summary-message-parents-vector msgnum parents)
+         (setq msgnum (1+ msgnum)))))))
+
+(defun rmail-summary-invert ()
+  "Invert the criteria of the current summary.
+That is, show the messages that are not displayed, and hide
+the messages that are displayed."
   (interactive)
-  (rmail-summary-fill-displayed-messages)
-  (rmail-new-summary "Negate"
+  (rmail-summary-populate-displayed-messages)
+  (rmail-new-summary "Invert"
                     '(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)))))
+                          (not (aref rmail-summary-currently-displayed-msgs 
msg))
+                          (aset rmail-summary-currently-displayed-msgs msg t)
+                        (aset rmail-summary-currently-displayed-msgs msg 
nil)))))
+
+(defun rmail-summary--exists-1 ()
+  "Like `rmail-summary-exists', but works in both main and summary buffers."
+  (with-current-buffer rmail-buffer
+    (and rmail-summary-buffer (buffer-name rmail-summary-buffer)
+        rmail-summary-buffer)))
 
 ;;;###autoload
 (defun rmail-summary ()
@@ -318,6 +387,63 @@ by `rmail-summary-fill-displayed-messages'.")
   (interactive)
   (rmail-new-summary "All" '(rmail-summary) nil))
 
+(defun rmail-summary-direct-descendants (msgnum encountered-msgs)
+  "Find all direct descendants of MSGNUM, ignoring ENCOUNTERED-MSGS.
+Assumes `rmail-summary-message-parents-vector' is filled.  Ignores messages
+already ticked in ENCOUNTERED-MSGS."
+  (let (desc
+       (msg 1))
+    (while (<= msg rmail-total-messages)
+      (when (and
+            (not (aref encountered-msgs msg))
+            (memq msgnum (aref rmail-summary-message-parents-vector msg)))
+       (setq desc (cons msg desc)))
+      (setq msg (1+ msg)))
+    desc))
+
+(defun rmail-summary--walk-thread-message-recursively (msgnum encountered-msgs)
+  "Add parents and descendants of message MSGNUM to ENCOUNTERED-MSGS, 
recursively."
+  (unless (aref encountered-msgs msgnum)
+    (aset encountered-msgs msgnum t)
+    (let ((walk-thread-msg
+           (lambda (msg)
+             (rmail-summary--walk-thread-message-recursively
+              msg encountered-msgs))))
+      (mapc walk-thread-msg
+            (aref rmail-summary-message-parents-vector msgnum))
+      (mapc walk-thread-msg
+            (rmail-summary-direct-descendants msgnum encountered-msgs)))))
+
+;;;###autoload
+(defun rmail-summary-by-thread (&optional msgnum)
+  "Display a summary of messages in the same discussion thread as MSGNUM.
+Interactively, prompt for MSGNUM, defaulting to the current message.
+Threads are based on the \"Subject\", \"References\" and \"In-reply-to\"
+headers of the messages."
+  (interactive
+   (let* ((msg rmail-current-message)
+         (prompt (concat "Show thread containing message number")))
+     (list (read-number prompt msg))))
+  (with-current-buffer rmail-buffer
+    (unless msgnum
+      (setq msgnum rmail-current-message))
+    (unless (and rmail-summary-message-parents-vector
+                (= (length rmail-summary-message-parents-vector)
+                   (1+ rmail-total-messages)))
+      (rmail-summary-fill-message-parents-vector))
+    (let ((enc-msgs (make-bool-vector (1+ rmail-total-messages) nil)))
+      (rmail-summary--walk-thread-message-recursively msgnum enc-msgs)
+      (rmail-new-summary (format "thread containing message %d" msgnum)
+                        (list 'rmail-summary-by-thread msgnum)
+                        (if (and rmail-summary-progressively-narrow
+                                 (rmail-summary--exists-1))
+                            (lambda (msg _msgnum)
+                              (and (aref 
rmail-summary-currently-displayed-msgs msg)
+                                   (aref enc-msgs msg)))
+                          (lambda (msg _msgnum)
+                             (aref enc-msgs msg)))
+                        msgnum))))
+
 ;;;###autoload
 (defun rmail-summary-by-labels (labels)
   "Display a summary of all messages with one or more LABELS.
@@ -327,14 +453,15 @@ 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))
+  (if (and rmail-summary-progressively-narrow
+          (rmail-summary--exists-1))
+      (rmail-summary-populate-displayed-messages))
   (rmail-new-summary (concat "labels " labels)
                     (list 'rmail-summary-by-labels labels)
-                    (if rmail-summary-apply-filters-consecutively
+                    (if (and rmail-summary-progressively-narrow
+                             (rmail-summary--exists-1))
                         (lambda (msg l)
-                          (and (= (aref rmail-summary-currently-displayed-msgs 
msg)
-                                  ?y)
+                          (and (aref rmail-summary-currently-displayed-msgs 
msg)
                                (rmail-message-labels-p msg l)))
                       'rmail-message-labels-p)
                     (concat " \\("
@@ -349,15 +476,16 @@ 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))
+  (if (and rmail-summary-progressively-narrow
+          (rmail-summary--exists-1))
+      (rmail-summary-populate-displayed-messages))
   (rmail-new-summary
    (concat "recipients " recipients)
    (list 'rmail-summary-by-recipients recipients primary-only)
-   (if rmail-summary-apply-filters-consecutively
+   (if (and rmail-summary-progressively-narrow
+           (rmail-summary--exists-1))
        (lambda (msg r &optional po)
-        (and (= (aref rmail-summary-currently-displayed-msgs msg)
-                ?y)
+        (and (aref rmail-summary-currently-displayed-msgs msg)
              (rmail-message-recipients-p msg r po)))
      'rmail-message-recipients-p)
    recipients primary-only))
@@ -388,14 +516,15 @@ 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))
+  (if (and rmail-summary-progressively-narrow
+          (rmail-summary--exists-1))
+      (rmail-summary-populate-displayed-messages))
   (rmail-new-summary (concat "regexp " regexp)
                     (list 'rmail-summary-by-regexp regexp)
-                    (if rmail-summary-apply-filters-consecutively
+                    (if (and rmail-summary-progressively-narrow
+                             (rmail-summary--exists-1))
                         (lambda (msg r)
-                          (and (= (aref rmail-summary-currently-displayed-msgs 
msg)
-                                  ?y)
+                          (and (aref rmail-summary-currently-displayed-msgs 
msg)
                                (rmail-message-regexp-p msg r)))
                       'rmail-message-regexp-p)
                      regexp))
@@ -443,15 +572,16 @@ 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))
+  (if (and rmail-summary-progressively-narrow
+          (rmail-summary--exists-1))
+      (rmail-summary-populate-displayed-messages))
   (rmail-new-summary
    (concat "about " subject)
    (list 'rmail-summary-by-topic subject whole-message)
-   (if rmail-summary-apply-filters-consecutively
+   (if (and rmail-summary-progressively-narrow
+           (rmail-summary--exists-1))
        (lambda (msg s &optional wm)
-        (and (= (aref rmail-summary-currently-displayed-msgs msg)
-                ?y)
+        (and (aref rmail-summary-currently-displayed-msgs msg)
              (rmail-message-subject-p msg s wm)))
      'rmail-message-subject-p)
    subject whole-message))
@@ -477,15 +607,16 @@ 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))
+  (if (and rmail-summary-progressively-narrow
+          (rmail-summary--exists-1))
+      (rmail-summary-populate-displayed-messages))
   (rmail-new-summary
    (concat "senders " senders)
    (list 'rmail-summary-by-senders senders)
-   (if rmail-summary-apply-filters-consecutively
+   (if (and rmail-summary-progressively-narrow
+           (rmail-summary--exists-1))
        (lambda (msg s)
-        (and (= (aref rmail-summary-currently-displayed-msgs msg)
-                ?y)
+        (and (aref rmail-summary-currently-displayed-msgs msg)
              (rmail-message-senders-p msg s)))
      'rmail-message-senders-p)
    senders))
diff --git a/lisp/mail/supercite.el b/lisp/mail/supercite.el
index 98f46a3af5..558785de14 100644
--- a/lisp/mail/supercite.el
+++ b/lisp/mail/supercite.el
@@ -1350,7 +1350,7 @@ buffer."
      nesting)))
 
 (defun sc-add-citation-level ()
-  "Add a citation level for nested citation style w/ coercion."
+  "Add a citation level for nested citation style with coercion."
   (let* ((nesting (sc-guess-nesting))
         (citation (make-string (1+ (length nesting))
                                (string-to-char sc-citation-delimiter)))
diff --git a/lisp/man.el b/lisp/man.el
index 6c50f017e3..3802362da0 100644
--- a/lisp/man.el
+++ b/lisp/man.el
@@ -331,7 +331,7 @@ This regexp should not start with a `^' character.")
 ;; This used to have leading space [ \t]*, but was removed because it
 ;; causes false page splits on an occasional NAME with leading space
 ;; inside a manpage.  And `Man-heading-regexp' doesn't have [ \t]* anyway.
-(defvar Man-first-heading-regexp "^NAME$\\|^[ \t]*No manual entry fo.*$"
+(defvar Man-first-heading-regexp "^NAME$\\|^[ \t]*No manual entry for.*$"
   "Regular expression describing first heading on a manpage.
 This regular expression should start with a `^' character.")
 
@@ -1077,13 +1077,13 @@ to auto-complete your input based on the installed 
manual pages."
     ;; unless COLUMNS or MANWIDTH is set.  This isn't a problem on
     ;; a tty.  man(1) says:
     ;;        MANWIDTH
-    ;;               If $MANWIDTH is set, its value is used as the  line
-    ;;               length  for which manual pages should be formatted.
-    ;;               If it is not set, manual pages  will  be  formatted
-    ;;               with  a line length appropriate to the current ter-
-    ;;               minal (using an ioctl(2) if available, the value of
-    ;;               $COLUMNS,  or falling back to 80 characters if nei-
-    ;;               ther is available).
+    ;;               If $MANWIDTH is set, its value is used as the line
+    ;;               length for which manual pages should be formatted.
+    ;;               If it is not set, manual pages will be formatted
+    ;;               with a line length appropriate to the current
+    ;;               terminal (using an ioctl(2) if available, the value
+    ;;               of $COLUMNS, or falling back to 80 characters if
+    ;;               neither is available).
     (when (or window-system
              (not (or (getenv "MANWIDTH") (getenv "COLUMNS"))))
       ;; Since the page buffer is displayed beforehand,
diff --git a/lisp/mh-e/ChangeLog.1 b/lisp/mh-e/ChangeLog.1
index c7f5586140..d6893fb9ec 100644
--- a/lisp/mh-e/ChangeLog.1
+++ b/lisp/mh-e/ChangeLog.1
@@ -2944,7 +2944,7 @@
        change fixes that.
 
        * mh-utils.el (mh-show-mode): Setup mh-show-mode to display
-       elipsis for truncated header fields and to skip over them quickly.
+       ellipsis for truncated header fields and to skip over them quickly.
        (mh-clean-msg-header): Make another pass over the message header
        fields truncating long headers.
 
diff --git a/lisp/mh-e/ChangeLog.2 b/lisp/mh-e/ChangeLog.2
index 5f2dd299f8..fd597f0c00 100644
--- a/lisp/mh-e/ChangeLog.2
+++ b/lisp/mh-e/ChangeLog.2
@@ -360,8 +360,8 @@
 2011-05-10  Jim Meyering  <meyering@redhat.com>
 
        Fix doubled-word typos.
-       * mh-alias.el (mh-alias-minibuffer-confirm-address): if if -> if it
-       * mh-scan.el (mh-scan-destination-width): in in -> in
+       * mh-alias.el (mh-alias-minibuffer-confirm-address):
+       * mh-scan.el (mh-scan-destination-width): Fix typos.
 
 2011-04-28  Stefan Monnier  <monnier@iro.umontreal.ca>
 
diff --git a/lisp/mh-e/mh-mime.el b/lisp/mh-e/mh-mime.el
index 316463b989..c0b42171b9 100644
--- a/lisp/mh-e/mh-mime.el
+++ b/lisp/mh-e/mh-mime.el
@@ -607,8 +607,9 @@ If no part is preferred then all the parts are displayed."
 
 (defun mh-mime-maybe-display-alternatives (alternatives)
   "Show buttons for ALTERNATIVES.
-If `mh-mime-display-alternatives-flag' is non-nil then display
-buttons for alternative parts that are usually suppressed."
+If `mh-display-buttons-for-alternatives-flag' is non-nil then
+display buttons for alternative parts that are usually
+suppressed."
   (when (and mh-display-buttons-for-alternatives-flag alternatives)
     (insert "\n----------------------------------------------------\n")
     (insert "Alternatives:\n")
diff --git a/lisp/mh-e/mh-scan.el b/lisp/mh-e/mh-scan.el
index 4b4c594286..abbf7422b5 100644
--- a/lisp/mh-e/mh-scan.el
+++ b/lisp/mh-e/mh-scan.el
@@ -279,9 +279,9 @@ as in the default of
   ^ *[0-9]+.\\\\([bct]\\\\).....[ ]*\\\\(..................\\\\)
 
 If this regular expression is not correct, the notation hints
-will not be highlighted with the face
-`mh-mh-folder-sent-to-me-hint' and the sender will not be
-highlighted with the face `mh-folder-sent-to-me-sender'.")
+will not be highlighted with the face `mh-folder-sent-to-me-hint'
+and the sender will not be highlighted with the face
+`mh-folder-sent-to-me-sender'.")
 
 (defvar mh-scan-subject-regexp
   "^ *[0-9]+........[ 
]*...................\\([Rr][Ee]\\(\\[[0-9]+\\]\\)?:\\s-*\\)*\\([^<\n]*\\)"
diff --git a/lisp/mpc.el b/lisp/mpc.el
index 1775e7d5e7..f878c6ca29 100644
--- a/lisp/mpc.el
+++ b/lisp/mpc.el
@@ -447,7 +447,7 @@ which will be concatenated with proper quoting before 
passing them to MPD."
 ;;; Support for regularly updated current status information ;;;;;;;;;;;;;;;
 
 ;; Exported elements:
-;; `mpc-status' holds the uptodate data.
+;; `mpc-status' holds the up-to-date data.
 ;; `mpc-status-callbacks' holds the registered callback functions.
 ;; `mpc-status-refresh' forces a refresh of the data.
 ;; `mpc-status-stop' stops the automatic updating.
diff --git a/lisp/net/browse-url.el b/lisp/net/browse-url.el
index 1597f3651a..7ac6396d31 100644
--- a/lisp/net/browse-url.el
+++ b/lisp/net/browse-url.el
@@ -222,6 +222,14 @@ be used instead."
           (function :tag "Other function"))
   :version "26.1")
 
+(defcustom browse-url-irc-function 'browse-url-irc
+  "Function to open an irc:// link."
+  :type '(choice
+          (function-item :tag "Emacs IRC" :value browse-url-irc)
+          (const :tag "None" nil)
+          (function :tag "Other function"))
+  :version "29.1")
+
 (defcustom browse-url-button-regexp
   (concat
    "\\b\\(\\(www\\.\\|\\(s?https?\\|ftp\\|file\\|gopher\\|gemini\\|"
@@ -547,6 +555,11 @@ process), or nil (we don't know)."
 (function-put 'browse-url--man 'browse-url-browser-kind
               #'browse-url--browser-kind-man)
 
+(defun browse-url--irc (url &rest args)
+  "Call `browse-url-irc-function' with URL and ARGS."
+  (funcall browse-url-irc-function url args))
+(function-put 'browse-url--irc 'browse-url-browser-kind 'internal)
+
 (defun browse-url--browser (url &rest args)
   "Call `browse-url-browser-function' with URL and ARGS."
   (funcall browse-url-browser-function url args))
@@ -565,6 +578,7 @@ process), or nil (we don't know)."
 (defvar browse-url-default-handlers
   '(("\\`mailto:"; . browse-url--mailto)
     ("\\`man:" . browse-url--man)
+    ("\\`irc6?s?://" . browse-url--irc)
     (browse-url--non-html-file-url-p . browse-url-emacs))
   "Like `browse-url-handlers' but populated by Emacs and packages.
 
@@ -1510,6 +1524,16 @@ used instead of `browse-url-new-window-flag'."
 
 (function-put 'browse-url-text-emacs 'browse-url-browser-kind 'internal)
 
+;; --- irc ---
+
+;;;###autoload
+(defun browse-url-irc (url &rest _)
+  "Call `url-irc' directly after parsing URL.
+This function is a fit for options like `gnus-button-alist'."
+  (url-irc (url-generic-parse-url url)))
+
+(function-put 'browse-url-irc 'browse-url-browser-kind 'internal)
+
 ;; --- mailto ---
 
 (autoload 'rfc6068-parse-mailto-url "rfc6068")
diff --git a/lisp/net/eudc-capf.el b/lisp/net/eudc-capf.el
index 92f0c80493..e2bbd5b28b 100644
--- a/lisp/net/eudc-capf.el
+++ b/lisp/net/eudc-capf.el
@@ -123,11 +123,12 @@ queried for email addresses, and the results delivered to
                       (match-end 0)))
                (end (point))
                (prefix (save-excursion (buffer-substring-no-properties beg 
end))))
-          (list beg end
-                (completion-table-with-cache
-                 (lambda (_)
-                   (eudc-query-with-words (split-string prefix "[ \t]+") t))
-                 t))))))
+          (let ((result
+                 (eudc-query-with-words (split-string prefix "[ \t]+") t)))
+            (when result
+              (list beg end
+                    (completion-table-with-cache
+                     (lambda (_) result) t))))))))
 
 (provide 'eudc-capf)
 ;;; eudc-capf.el ends here
diff --git a/lisp/net/eudc-vars.el b/lisp/net/eudc-vars.el
index b44989d906..450943a3f0 100644
--- a/lisp/net/eudc-vars.el
+++ b/lisp/net/eudc-vars.el
@@ -53,8 +53,7 @@ instead."
 ;; Not to be mistaken with `eudc-supported-protocols'
 (defvar eudc-known-protocols '(bbdb ldap ecomplete mailabbrev))
 
-(defcustom eudc-server-hotlist '(("localhost" . ecomplete)
-                                 ("localhost" . mailabbrev))
+(defcustom eudc-server-hotlist nil
   "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
diff --git a/lisp/net/eudcb-mailabbrev.el b/lisp/net/eudcb-mailabbrev.el
index 64b50af09b..4a2dd9ad4a 100644
--- a/lisp/net/eudcb-mailabbrev.el
+++ b/lisp/net/eudcb-mailabbrev.el
@@ -78,7 +78,10 @@ RETURN-ATTRS is a list of attributes to return, defaulting to
     (dolist (term query)
       (let* ((attr (car term))
              (value (cdr term))
-             (raw-matches (symbol-value (intern-soft value mail-abbrevs))))
+             (soft (intern-soft value mail-abbrevs))
+             (raw-matches (and
+                           (boundp soft)
+                           (symbol-value soft))))
         (when (and raw-matches
                    (memq attr '(email firstname name)))
           (let* ((matches (split-string raw-matches ", "))
diff --git a/lisp/net/rcirc.el b/lisp/net/rcirc.el
index 370f388b3e..29957a62d0 100644
--- a/lisp/net/rcirc.el
+++ b/lisp/net/rcirc.el
@@ -1353,10 +1353,10 @@ inserting the new one."
     (if (use-region-p)
         (let ((beg (region-beginning)))
           (goto-char (region-end))
-          (insert "")
+          (insert "\^O")
           (goto-char beg)
           (insert pre))
-      (insert pre "")))
+      (insert pre "\^O")))
   (when (or (not (region-active-p)) (< (point) (mark)))
     (forward-char (length pre))))
 
@@ -1364,11 +1364,11 @@ inserting the new one."
   "Remove the closes formatting found closes to the current point."
   (interactive)
   (save-excursion
-    (when (and (search-backward-regexp (rx (or "" "" "" "" ""))
+    (when (and (search-backward-regexp (rx (or "\^B" "\^]" "\^_" "\^^" "\^Q"))
                                        rcirc-prompt-end-marker t)
-               (looking-at (rx (group (or "" "" "" "" ""))
+               (looking-at (rx (group (or "\^B" "\^]" "\^_" "\^^" "\^Q"))
                                (*? nonl)
-                               (group ""))))
+                               (group "\^O"))))
       (replace-match "" nil nil nil 2)
       (replace-match "" nil nil nil 1))))
 
@@ -1378,7 +1378,7 @@ If REPLACE is non-nil or a prefix argument is given, any 
prior
 formatting will be replaced before the bold formatting is
 inserted."
   (interactive "P")
-  (rcirc-format "" replace))
+  (rcirc-format "\^B" replace))
 
 (defun rcirc-format-italic (replace)
   "Insert italic formatting.
@@ -1386,7 +1386,7 @@ If REPLACE is non-nil or a prefix argument is given, any 
prior
 formatting will be replaced before the italic formatting is
 inserted."
   (interactive "P")
-  (rcirc-format "" replace))
+  (rcirc-format "\^]" replace))
 
 (defun rcirc-format-underline (replace)
   "Insert underlining formatting.
@@ -1394,7 +1394,7 @@ If REPLACE is non-nil or a prefix argument is given, any 
prior
 formatting will be replaced before the underline formatting is
 inserted."
   (interactive "P")
-  (rcirc-format "" replace))
+  (rcirc-format "\^_" replace))
 
 (defun rcirc-format-strike-trough (replace)
   "Insert strike-trough formatting.
@@ -1402,7 +1402,7 @@ If REPLACE is non-nil or a prefix argument is given, any 
prior
 formatting will be replaced before the strike-trough formatting
 is inserted."
   (interactive "P")
-  (rcirc-format "" replace))
+  (rcirc-format "\^^" replace))
 
 (defun rcirc-format-fixed-width (replace)
   "Insert fixed-width formatting.
@@ -1410,7 +1410,7 @@ If REPLACE is non-nil or a prefix argument is given, any 
prior
 formatting will be replaced before the fixed width formatting is
 inserted."
   (interactive "P")
-  (rcirc-format "" replace))
+  (rcirc-format "\^Q" replace))
 
 (defvar-keymap rcirc-mode-map
   :doc "Keymap for rcirc mode."
diff --git a/lisp/net/tramp-archive.el b/lisp/net/tramp-archive.el
index 646ae86452..5b2af7c6b2 100644
--- a/lisp/net/tramp-archive.el
+++ b/lisp/net/tramp-archive.el
@@ -183,18 +183,20 @@ It must be supported by libarchive(3).")
 ;; The definition of `tramp-archive-file-name-regexp' contains calls
 ;; to `regexp-opt', which cannot be autoloaded while loading
 ;; loaddefs.el.  So we use a macro, which is evaluated only when needed.
+;; When tramp-archive.el is unloaded and reloaded, it gripes about
+;; missing `tramp-archive{-compression]-suffixes'.  We protect this.
 ;;;###autoload
 (progn (defmacro tramp-archive-autoload-file-name-regexp ()
   "Regular expression matching archive file names."
-  `(rx
+  `(tramp-compat-rx
     bos
     ;; This group is used in `tramp-archive-file-name-archive'.
     (group
      (+ nonl)
      ;; Default suffixes ...
-     "." ,(cons '| tramp-archive-suffixes)
+     "." ,(cons '| (bound-and-true-p tramp-archive-suffixes))
      ;; ... with compression.
-     (? "." ,(cons '| tramp-archive-compression-suffixes)))
+     (? "." ,(cons '| (bound-and-true-p tramp-archive-compression-suffixes))))
     ;; This group is used in `tramp-archive-file-name-localname'.
     (group "/" (* nonl))
     eos)))
@@ -330,10 +332,6 @@ arguments to pass to the OPERATION."
         (inhibit-file-name-operation operation))
     (apply operation args))))
 
-;; Starting with Emacs 29, `tramp-archive-file-name-handler' is
-;; autoloaded.  But it must still be in tramp-loaddefs.el for older
-;; versions of Emacs.
-;;;###autoload(autoload 'tramp-archive-file-name-handler "tramp-archive")
 ;;;###tramp-autoload
 (defun tramp-archive-file-name-handler (operation &rest args)
   "Invoke the file archive related OPERATION.
@@ -396,30 +394,30 @@ arguments to pass to the OPERATION."
 (put #'tramp-archive-autoload-file-name-handler 'tramp-autoload t)
 
 ;;;###autoload
-(progn (defun tramp-register-archive-file-name-handler ()
+(progn (defun tramp-register-archive-autoload-file-name-handler ()
   "Add archive file name handler to `file-name-handler-alist'."
   (when (and tramp-archive-enabled
              (not
-              (rassq #'tramp-archive-file-name-handler 
file-name-handler-alist)))
+              (rassq 'tramp-archive-file-name-handler 
file-name-handler-alist)))
     (add-to-list 'file-name-handler-alist
                 (cons (tramp-archive-autoload-file-name-regexp)
                       #'tramp-archive-autoload-file-name-handler))
     (put #'tramp-archive-autoload-file-name-handler 'safe-magic t))))
 
-(put #'tramp-register-archive-file-name-handler 'tramp-autoload t)
+(put #'tramp-register-archive-autoload-file-name-handler 'tramp-autoload t)
 
 ;;;###autoload
 (progn
-  (add-hook 'after-init-hook #'tramp-register-archive-file-name-handler)
+  (add-hook 'after-init-hook 
#'tramp-register-archive-autoload-file-name-handler)
   (add-hook
    'tramp-archive-unload-hook
    (lambda ()
      (remove-hook
-      'after-init-hook #'tramp-register-archive-file-name-handler))))
+      'after-init-hook #'tramp-register-archive-autoload-file-name-handler))))
 
 ;; In older Emacsen (prior 27.1), the autoload above does not exist.
 ;; So we call it again; it doesn't hurt.
-(tramp-register-archive-file-name-handler)
+(tramp-register-archive-autoload-file-name-handler)
 
 ;; Mark `operations' the handler is responsible for.
 (put #'tramp-archive-file-name-handler 'operations
diff --git a/lisp/net/tramp-compat.el b/lisp/net/tramp-compat.el
index a1d1d284ed..252eab0f3b 100644
--- a/lisp/net/tramp-compat.el
+++ b/lisp/net/tramp-compat.el
@@ -179,7 +179,7 @@ A nil value for either argument stands for the current 
time."
     (lambda (reporter &optional value _suffix)
       (progress-reporter-update reporter value))))
 
-;; `ignore-error' is new in Emacs Emacs 27.1.
+;; `ignore-error' is new in Emacs 27.1.
 (defmacro tramp-compat-ignore-error (condition &rest body)
   "Execute BODY; if the error CONDITION occurs, return nil.
 Otherwise, return result of last form in BODY.
diff --git a/lisp/net/tramp-container.el b/lisp/net/tramp-container.el
index 328625b776..7b94253226 100644
--- a/lisp/net/tramp-container.el
+++ b/lisp/net/tramp-container.el
@@ -163,6 +163,7 @@ see its function help for a description of the format."
                                    ("-u" "%u")
                                    ("%h")
                                   ("%l")))
+               (tramp-direct-async (,tramp-default-remote-shell "-c"))
                 (tramp-remote-shell ,tramp-default-remote-shell)
                 (tramp-remote-shell-login ("-l"))
                 (tramp-remote-shell-args ("-i" "-c"))))
@@ -174,6 +175,7 @@ see its function help for a description of the format."
                                    ("-u" "%u")
                                    ("%h")
                                   ("%l")))
+               (tramp-direct-async (,tramp-default-remote-shell "-c"))
                 (tramp-remote-shell ,tramp-default-remote-shell)
                 (tramp-remote-shell-login ("-l"))
                 (tramp-remote-shell-args ("-i" "-c"))))
@@ -186,6 +188,7 @@ see its function help for a description of the format."
                                    ("--")
                                   ("%l")))
                (tramp-config-check tramp-kubernetes--current-context-data)
+               (tramp-direct-async (,tramp-default-remote-shell "-c"))
                 (tramp-remote-shell ,tramp-default-remote-shell)
                 (tramp-remote-shell-login ("-l"))
                 (tramp-remote-shell-args ("-i" "-c"))))
diff --git a/lisp/net/tramp-crypt.el b/lisp/net/tramp-crypt.el
index 16c4049a68..0973258157 100644
--- a/lisp/net/tramp-crypt.el
+++ b/lisp/net/tramp-crypt.el
@@ -455,7 +455,7 @@ Otherwise, return NAME."
 (defun tramp-crypt-do-encrypt-or-decrypt-file (op root infile outfile)
   "Encrypt / decrypt file INFILE to OUTFILE according to encrypted directory 
ROOT.
 Both files must be local files.  OP must be `encrypt' or `decrypt'.
-If OP ist `decrypt', the basename of INFILE must be an encrypted file name.
+If OP is `decrypt', the basename of INFILE must be an encrypted file name.
 Raise an error if this fails."
   (when-let ((tramp-crypt-enabled t)
             (dir (tramp-crypt-file-name-p root))
diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el
index b08bc63e8a..e9f30bea7b 100644
--- a/lisp/net/tramp.el
+++ b/lisp/net/tramp.el
@@ -278,8 +278,9 @@ pair of the form (KEY VALUE).  The following KEYs are 
defined:
 
   * `tramp-direct-async'
     Whether the method supports direct asynchronous processes.
-    Until now, just \"ssh\"-based, \"sshfs\"-based and
-    \"adb\"-based methods do.
+    Until now, just \"ssh\"-based, \"sshfs\"-based, \"adb\"-based
+    and container methods do.  If it is a list of strings, they
+    are used to construct the remote command.
 
   * `tramp-config-check'
     A function to be called with one argument, VEC.  It should
@@ -4804,7 +4805,14 @@ substitution.  SPEC-LIST is a list of char/value pairs 
used for
               (command
                (append
                 `("cd" ,(tramp-shell-quote-argument localname) "&&" "(" "env")
-                env `(,command ")"))))
+                env `(,command ")")))
+               ;; Add remote shell if needed.
+              (command
+               (if (consp (tramp-get-method-parameter v 'tramp-direct-async))
+                   (append
+                    (tramp-get-method-parameter v 'tramp-direct-async)
+                     `(,(mapconcat #'identity command " ")))
+                 command)))
 
          ;; Check for `tramp-sh-file-name-handler', because something
          ;; is different between tramp-sh.el, and tramp-adb.el or
diff --git a/lisp/net/trampver.el b/lisp/net/trampver.el
index 2b39add20d..caf6750c26 100644
--- a/lisp/net/trampver.el
+++ b/lisp/net/trampver.el
@@ -83,7 +83,7 @@
   (unless (string-equal "ok" x) (error "%s" x)))
 
 (defun tramp-inside-emacs ()
-  "Version string provided by INSIDE_EMACS enmvironment variable."
+  "Version string provided by INSIDE_EMACS environment variable."
   (concat (or (getenv "INSIDE_EMACS") emacs-version)
          ",tramp:" tramp-version))
 
diff --git a/lisp/nxml/nxml-mode.el b/lisp/nxml/nxml-mode.el
index 9cbab29504..fb8d5322c0 100644
--- a/lisp/nxml/nxml-mode.el
+++ b/lisp/nxml/nxml-mode.el
@@ -195,7 +195,7 @@ This is not used directly, but only via inheritance by 
other faces."
 (defface nxml-char-ref-number
   '((t (:inherit nxml-ref)))
   "Face used for the number in character references.
-This includes ths `x' in hex references."
+This includes the `x' in hex references."
   :group 'nxml-faces)
 
 (defface nxml-char-ref-delimiter
diff --git a/lisp/obsolete/vi.el b/lisp/obsolete/vi.el
index 91baa4d28e..afc6284b34 100644
--- a/lisp/obsolete/vi.el
+++ b/lisp/obsolete/vi.el
@@ -927,13 +927,13 @@ it is used instead of the saved one."
   (vi-repeat-last-find-char count))
 
 (defun vi-backward-upto-char (count char)
-  "Find upto the COUNT'th CHAR backward on current line."
+  "Find up to the COUNT'th CHAR backward on current line."
   (interactive "p\nc")
   (setq vi-last-find-char (cons -1 (cons char t)))
   (vi-repeat-last-find-char count))
 
 (defun vi-forward-upto-char (count char)
-  "Find upto the COUNT'th CHAR forward on current line."
+  "Find up to the COUNT'th CHAR forward on current line."
   (interactive "p\nc")
   (setq vi-last-find-char (cons 1 (cons char t)))
   (vi-repeat-last-find-char count))
diff --git a/lisp/org/ChangeLog.1 b/lisp/org/ChangeLog.1
index 4e1c44d2bc..1491a4645a 100644
--- a/lisp/org/ChangeLog.1
+++ b/lisp/org/ChangeLog.1
@@ -10556,7 +10556,7 @@
        * org-element.el (org-element-paragraph-separate): Fix comments in
        paragraph separator regexp.  Optimize it.
 
-       * org-element.el: Update code commets.
+       * org-element.el: Update code comments.
 
        * org.el (org-mark-subtree): Fix bug when marking subtree with
        point on an inlinetask.  Refactor code.
@@ -13798,7 +13798,7 @@
 2012-01-03  Dave Abrahams  <dave@boostpro.com>
 
        * org-agenda.el (org-agenda-follow-indirect): New option.
-       (org-agenda-follow-mode): Call `org-agenda-do-context-action' fro
+       (org-agenda-follow-mode): Call `org-agenda-do-context-action' from
        follow mode.
        (org-agenda-do-context-action): Also do indirect follow mode
        action.
@@ -17948,7 +17948,7 @@
 
        * org.el (org-narrow-to-subtree): Ensure `org-back-to-heading'
        will move point to a real heading and not an inline task by
-       wraping function into a `org-with-limited-levels' macro.
+       wrapping function into a `org-with-limited-levels' macro.
 
 2011-07-28  Bastien Guerry  <bzg@gnu.org>
 
@@ -20112,10 +20112,10 @@
 2011-05-10  Jim Meyering  <meyering@redhat.com>
 
        Fix doubled-word typos.
-       * org-agenda.el (org-agenda-entry-types): the the -> the
-       * org-table.el (org-table-get-remote-range): or or -> or
-       * org-wl.el (org-wl-folder-type): the the -> the
-       * org.el (org-goto, org-inside-LaTeX-fragment-p): Likewise.
+       * org-agenda.el (org-agenda-entry-types):
+       * org-table.el (org-table-get-remote-range):
+       * org-wl.el (org-wl-folder-type):
+       * org.el (org-goto, org-inside-LaTeX-fragment-p): Fix typos.
 
 2011-03-15  Stefan Monnier  <monnier@iro.umontreal.ca>
 
@@ -24341,7 +24341,7 @@
 2010-07-19  Bernt Hansen  <bernt@norang.ca>
 
        * org.el (org-time-string-to-absolute): Ignore cyclic repeater
-       when displaying items on todays agenda date.
+       when displaying items on today's agenda date.
 
 2010-07-19  Carsten Dominik  <carsten.dominik@gmail.com>
 
@@ -28477,7 +28477,7 @@
        (outline-end-of-subtree): Make `outline-end-of-subtree' use the
        org-version of this function in Org-mode.  We use advice to
        implement this change, so that future changes to this function in
-       outline.el wil be handled properly.
+       outline.el will be handled properly.
        (org-forward-same-level, org-backward-same-level): New commands.
 
 2009-08-06  Carsten Dominik  <carsten.dominik@gmail.com>
@@ -28769,7 +28769,7 @@
        attachments.
 
        * org-latex.el (org-export-latex-quotation-marks): Fix export of
-       quotation makrs in parenthesis.
+       quotation marks in parenthesis.
        (org-remove-initial-hash): New function.
        (org-export-latex-preprocess): Fix bug with infinite loop if
        environment is not properly closed.
@@ -30559,7 +30559,7 @@
 2009-01-25  Carsten Dominik  <carsten.dominik@gmail.com>
 
        * org-archive.el (org-extract-archive-heading): Allow %s for file
-       name also in achive location heading.
+       name also in archive location heading.
 
 2009-01-25  Carsten Dominik  <carsten.dominik@gmail.com>
 
diff --git a/lisp/org/ob-R.el b/lisp/org/ob-R.el
index cd822ef837..93d1d34229 100644
--- a/lisp/org/ob-R.el
+++ b/lisp/org/ob-R.el
@@ -545,7 +545,7 @@ by `org-babel-comint-async-filter'."
 
 (defun ob-session-async-R-value-callback (params tmp-file)
   "Callback for async value results.
-Assigned locally to `ob-session-async-file-callback' in R
+Assigned locally to `org-babel-comint-async-file-callback' in R
 comint buffers used for asynchronous Babel evaluation."
   (let* ((graphics-file (and (member "graphics" (assq :result-params params))
                             (org-babel-graphical-output-file params)))
diff --git a/lisp/org/ob-tangle.el b/lisp/org/ob-tangle.el
index 525d27bc07..d9814a7aa6 100644
--- a/lisp/org/ob-tangle.el
+++ b/lisp/org/ob-tangle.el
@@ -433,7 +433,7 @@ non-nil, return the full association list to be used by
                       ;; The created link is transient.  Using ID is
                       ;; not necessary, but could have side-effects if
                       ;; used.  An ID property may be added to
-                      ;; existing entries thus creatin unexpected file
+                      ;; existing entries thus creating unexpected file
                       ;; modifications.
                       (org-id-link-to-org-use-id nil)
                       (l (org-no-properties (org-store-link nil))))
@@ -525,7 +525,7 @@ by `org-babel-get-src-block-info'."
                        ("link" . ,(let (;; The created link is transient.  
Using ID is
                                          ;; not necessary, but could have 
side-effects if
                                          ;; used.  An ID property may be added 
to
-                                         ;; existing entries thus creatin 
unexpected file
+                                         ;; existing entries thus creating 
unexpected file
                                          ;; modifications.
                                          (org-id-link-to-org-use-id nil))
                                      (org-no-properties (org-store-link nil))))
diff --git a/lisp/org/ol.el b/lisp/org/ol.el
index 4ad1f6d345..108f031cde 100644
--- a/lisp/org/ol.el
+++ b/lisp/org/ol.el
@@ -339,7 +339,7 @@ another window."
 (defcustom org-link-search-must-match-exact-headline 'query-to-create
   "Non-nil means internal fuzzy links can only match headlines.
 
-When nil, the a fuzzy link may point to a target or a named
+When nil, the fuzzy link may point to a target or a named
 construct in the document.  When set to the special value
 `query-to-create', offer to create a new headline when none
 matched.
diff --git a/lisp/org/org-clock.el b/lisp/org/org-clock.el
index 38e0826075..42de0a0cf9 100644
--- a/lisp/org/org-clock.el
+++ b/lisp/org/org-clock.el
@@ -428,8 +428,7 @@ When `org-clock-clocked-in-display' is set to `frame-title'
 or `both', clocking in will replace `frame-title-format' with
 this value.  Clocking out will restore `frame-title-format'.
 
-`org-frame-title-string' is a format string using the same
-specifications than `frame-title-format', which see."
+This uses the same format as `frame-title-format', which see."
   :version "24.1"
   :group 'org-clock
   :type 'sexp)
diff --git a/lisp/org/org-element.el b/lisp/org/org-element.el
index 4c018062af..474a93577a 100644
--- a/lisp/org/org-element.el
+++ b/lisp/org/org-element.el
@@ -139,7 +139,7 @@ is not sufficient to know if point is at a paragraph 
ending.  See
 (defvar org-element--object-regexp nil
   "Regexp possibly matching the beginning of an object.
 This regexp allows false positives.  Dedicated parser (e.g.,
-`org-export-bold-parser') will take care of further filtering.
+`org-element-bold-parser') will take care of further filtering.
 Radio links are not matched by this regexp, as they are treated
 specially in `org-element--object-lex'.")
 
diff --git a/lisp/org/org-faces.el b/lisp/org/org-faces.el
index d96898372f..78148a1b6d 100644
--- a/lisp/org/org-faces.el
+++ b/lisp/org/org-faces.el
@@ -137,7 +137,7 @@ The following faces apply, with this priority.
 
 Since column view works by putting overlays with a display property
 over individual characters in the buffer, the face of the underlining
-character (this might for example be the a TODO keyword) might still
+character (this might for example be the TODO keyword) might still
 shine through in some properties.  So when your column view looks
 funny, with \"random\" colors, weight, strike-through, try to explicitly
 set the properties in the `org-column' face.  For example, set
diff --git a/lisp/org/org-id.el b/lisp/org/org-id.el
index 7334050b8b..2fb299d5e8 100644
--- a/lisp/org/org-id.el
+++ b/lisp/org/org-id.el
@@ -591,7 +591,7 @@ If SILENT is non-nil, messages are suppressed."
                        (setf (car item) (expand-file-name (car item) loc))))
                    org-id-locations)))
        (error
-        (message "Could not read `org-id-values' from %s, setting it to nil"
+         (message "Could not read `org-id-locations' from %s, setting it to 
nil"
                  org-id-locations-file))))
     (setq org-id-files (mapcar 'car org-id-locations))
     (setq org-id-locations (org-id-alist-to-hash org-id-locations))))
diff --git a/lisp/org/org-protocol.el b/lisp/org/org-protocol.el
index 137a11f3d9..cd99f30e7b 100644
--- a/lisp/org/org-protocol.el
+++ b/lisp/org/org-protocol.el
@@ -687,7 +687,7 @@ to deal with new-style links.")
       fname)))
 
 (defadvice server-visit-files (before org-protocol-detect-protocol-server 
activate)
-  "Advice server-visit-flist to call 
`org-protocol-modify-filename-for-protocol'."
+  "Advice server-visit-flist to call 
`org-protocol-check-filename-for-protocol'."
   (let ((flist (if org-protocol-reverse-list-of-files
                    (reverse  (ad-get-arg 0))
                  (ad-get-arg 0)))
diff --git a/lisp/org/ox-koma-letter.el b/lisp/org/ox-koma-letter.el
index 5f62cd1c04..6b5edd20f5 100644
--- a/lisp/org/ox-koma-letter.el
+++ b/lisp/org/ox-koma-letter.el
@@ -35,7 +35,7 @@
 ;; `org-koma-letter-export-to-pdf' ("pdf" file).
 ;;
 ;; On top of buffer keywords supported by `latex' back-end (see
-;; `org-latex-options-alist'), this back-end introduces the following
+;; `org-latex-packages-alist'), this back-end introduces the following
 ;; keywords:
 ;;   - CLOSING: see `org-koma-letter-closing',
 ;;   - FROM_ADDRESS: see `org-koma-letter-from-address',
@@ -66,7 +66,7 @@
 ;;   - from-logo (see `org-koma-letter-use-from-logo')
 ;;   - email (see `org-koma-letter-use-email')
 ;;   - place (see `org-koma-letter-use-place')
-;;   - location (see `org-koma-letter-use-location')
+;;   - location (see `org-koma-letter-location')
 ;;   - subject, a list of format options
 ;;     (see `org-koma-letter-subject-format')
 ;;   - after-closing-order, a list of the ordering of headings with
diff --git a/lisp/org/ox-odt.el b/lisp/org/ox-odt.el
index 7f2e8ba47f..8c8c80136a 100644
--- a/lisp/org/ox-odt.el
+++ b/lisp/org/ox-odt.el
@@ -3227,8 +3227,7 @@ Return a cons of (TABLE-CELL-STYLE-NAME . 
PARAGRAPH-STYLE-NAME).
 When STYLE-SPEC is nil, style the table cell the conventional way
 - choose cell borders based on row and column groupings and
 choose paragraph alignment based on `org-col-cookies' text
-property.  See also
-`org-odt-get-paragraph-style-cookie-for-table-cell'.
+property.  See also `org-odt-table-style-spec'.
 
 When STYLE-SPEC is non-nil, ignore the above cookie and return
 styles congruent with the ODF-1.2 specification."
@@ -3573,8 +3572,7 @@ pertaining to indentation here."
     ;;   item, but also within description lists and low-level
     ;;   headlines.
 
-    ;; See `org-odt-translate-description-lists' and
-    ;; `org-odt-translate-low-level-headlines' for how this is
+    ;; See `org-odt--translate-description-lists' for how this is
     ;; tackled.
 
     (concat "\n"
diff --git a/lisp/org/ox.el b/lisp/org/ox.el
index 56bb4b74df..ca6b3f2208 100644
--- a/lisp/org/ox.el
+++ b/lisp/org/ox.el
@@ -6291,7 +6291,7 @@ to `:default' encoding.  If it fails, return S."
 ;;
 ;; Export Stack is viewed through a dedicated major mode
 ;;`org-export-stack-mode' and tools: `org-export-stack-refresh',
-;;`org-export-stack-delete', `org-export-stack-view' and
+;;`org-export-stack-remove', `org-export-stack-view' and
 ;;`org-export-stack-clear'.
 ;;
 ;; For back-ends, `org-export-add-to-stack' add a new source to stack.
diff --git a/lisp/outline.el b/lisp/outline.el
index a646f71db8..2465a4963a 100644
--- a/lisp/outline.el
+++ b/lisp/outline.el
@@ -59,6 +59,18 @@ The recommended way to set this is with a `Local Variables:' 
list
 in the file it applies to.")
 ;;;###autoload(put 'outline-heading-end-regexp 'safe-local-variable 'stringp)
 
+(defvar outline-search-function nil
+  "Function to search the next outline heading.
+The function is called with four optional arguments: BOUND, MOVE, BACKWARD,
+LOOKING-AT.  The first two arguments BOUND and MOVE are almost the same as
+the BOUND and NOERROR arguments of `re-search-forward', with the difference
+that MOVE accepts only a boolean, either nil or non-nil.  When the argument
+BACKWARD is non-nil, the search should search backward like
+`re-search-backward' does.  In case of a successful search, the
+function should return non-nil, move point, and set match-data
+appropriately.  When the argument LOOKING-AT is non-nil, it should
+imitate the function `looking-at'.")
+
 (defvar outline-mode-prefix-map
   (let ((map (make-sparse-keymap)))
     (define-key map "@" 'outline-mark-subtree)
@@ -233,7 +245,8 @@ This option is only in effect when 
`outline-minor-mode-cycle' is non-nil."
 (defvar outline-font-lock-keywords
   '(
     ;; Highlight headings according to the level.
-    (eval . (list (concat "^\\(?:" outline-regexp "\\).*")
+    (eval . (list (or outline-search-function
+                      (concat "^\\(?:" outline-regexp "\\).*"))
                   0 '(if outline-minor-mode
                          (if outline-minor-mode-highlight
                              (list 'face (outline-font-lock-face)))
@@ -366,7 +379,9 @@ data reflects the `outline-regexp'.")
   "Return one of `outline-font-lock-faces' for current level."
   (save-excursion
     (goto-char (match-beginning 0))
-    (looking-at outline-regexp)
+    (if outline-search-function
+        (funcall outline-search-function nil nil nil t)
+      (looking-at outline-regexp))
     (aref outline-font-lock-faces
           (% (1- (funcall outline-level))
              (length outline-font-lock-faces)))))
@@ -454,7 +469,7 @@ bindings, per the current major mode."
 
 (defcustom outline-minor-mode-highlight nil
   "Whether to highlight headings in `outline-minor-mode' using font-lock 
keywords.
-This option controles whether `outline-minor-mode' will use its font-lock
+This option controls whether `outline-minor-mode' will use its font-lock
 keywords to highlight headings, which could potentially conflict with
 font-lock faces defined by the major mode.  Thus, a non-nil value will
 work well only when there's no such conflict.
@@ -474,8 +489,11 @@ outline font-lock faces to those of major mode."
   ;; Fallback to overlays when font-lock is unsupported.
   (save-excursion
     (goto-char (point-min))
-    (let ((regexp (concat "^\\(?:" outline-regexp "\\).*$")))
-      (while (re-search-forward regexp nil t)
+    (let ((regexp (unless outline-search-function
+                    (concat "^\\(?:" outline-regexp "\\).*$"))))
+      (while (if outline-search-function
+                 (funcall outline-search-function)
+               (re-search-forward regexp nil t))
         (let ((overlay (make-overlay (match-beginning 0) (match-end 0))))
           (overlay-put overlay 'outline-highlight t)
           ;; FIXME: Is it possible to override all underlying face attributes?
@@ -592,26 +610,37 @@ or else the number of characters matched by 
`outline-regexp'."
   "Skip forward to just before the next heading line.
 If there's no following heading line, stop before the newline
 at the end of the buffer."
-  (if (re-search-forward (concat "\n\\(?:" outline-regexp "\\)")
-                        nil 'move)
-      (goto-char (match-beginning 0)))
-  (if (and (bolp) (or outline-blank-line (eobp)) (not (bobp)))
-      (forward-char -1)))
+  (when (if outline-search-function
+            (progn
+              ;; Emulate "\n" to force finding the next preface
+              (unless (eobp) (forward-char 1))
+              (funcall outline-search-function nil t))
+          (re-search-forward (concat "\n\\(?:" outline-regexp "\\)")
+                            nil 'move))
+    (goto-char (match-beginning 0))
+    ;; Compensate "\n" from the beginning of regexp
+    (when (and outline-search-function (not (bobp))) (forward-char -1)))
+  (when (and (bolp) (or outline-blank-line (eobp)) (not (bobp)))
+    (forward-char -1)))
 
 (defun outline-next-heading ()
   "Move to the next (possibly invisible) heading line."
   (interactive)
   ;; Make sure we don't match the heading we're at.
-  (if (and (bolp) (not (eobp))) (forward-char 1))
-  (if (re-search-forward (concat "^\\(?:" outline-regexp "\\)")
-                        nil 'move)
-      (goto-char (match-beginning 0))))
+  (when (and (bolp) (not (eobp))) (forward-char 1))
+  (when (if outline-search-function
+            (funcall outline-search-function nil t)
+          (re-search-forward (concat "^\\(?:" outline-regexp "\\)")
+                            nil 'move))
+    (goto-char (match-beginning 0))))
 
 (defun outline-previous-heading ()
   "Move to the previous (possibly invisible) heading line."
   (interactive)
-  (re-search-backward (concat "^\\(?:" outline-regexp "\\)")
-                     nil 'move))
+  (if outline-search-function
+      (funcall outline-search-function nil t t)
+    (re-search-backward (concat "^\\(?:" outline-regexp "\\)")
+                       nil 'move)))
 
 (defsubst outline-invisible-p (&optional pos)
   "Non-nil if the character after POS has outline invisible property.
@@ -628,8 +657,10 @@ Only visible heading lines are considered, unless 
INVISIBLE-OK is non-nil."
       (let (found)
        (save-excursion
          (while (not found)
-           (or (re-search-backward (concat "^\\(?:" outline-regexp "\\)")
-                                   nil t)
+           (or (if outline-search-function
+                    (funcall outline-search-function nil nil t)
+                  (re-search-backward (concat "^\\(?:" outline-regexp "\\)")
+                                     nil t))
                 (signal 'outline-before-first-heading nil))
            (setq found (and (or invisible-ok (not (outline-invisible-p)))
                             (point)))))
@@ -642,7 +673,9 @@ If INVISIBLE-OK is non-nil, an invisible heading line is ok 
too."
   (save-excursion
     (beginning-of-line)
     (and (bolp) (or invisible-ok (not (outline-invisible-p)))
-        (looking-at outline-regexp))))
+        (if outline-search-function
+             (funcall outline-search-function nil nil nil t)
+           (looking-at outline-regexp)))))
 
 (defun outline-insert-heading ()
   "Insert a new heading at same depth at point."
@@ -754,7 +787,9 @@ nil for WHICH, or do not pass any argument)."
                      (while (and (progn (outline-next-heading) (not (eobp)))
                                  (<= (funcall outline-level) level))))
                    (unless (eobp)
-                     (looking-at outline-regexp)
+                     (if outline-search-function
+                          (funcall outline-search-function nil nil nil t)
+                        (looking-at outline-regexp))
                      (match-string-no-properties 0))))
                 ;; Bummer!! There is no higher-level heading in the buffer.
                 (outline-invent-heading head nil))))
@@ -805,7 +840,9 @@ the match data is set appropriately."
   (save-excursion
     (setq end (copy-marker end))
     (goto-char beg)
-    (when (re-search-forward (concat "^\\(?:" outline-regexp "\\)") end t)
+    (when (if outline-search-function
+              (funcall outline-search-function end)
+            (re-search-forward (concat "^\\(?:" outline-regexp "\\)") end t))
       (goto-char (match-beginning 0))
       (funcall fun)
       (while (and (progn
@@ -873,21 +910,23 @@ A heading line is one that starts with a `*' (or that
   (if (< arg 0)
       (beginning-of-line)
     (end-of-line))
-  (let (found-heading-p)
+  (let ((regexp (unless outline-search-function
+                  (concat "^\\(?:" outline-regexp "\\)")))
+        found-heading-p)
     (while (and (not (bobp)) (< arg 0))
       (while (and (not (bobp))
                  (setq found-heading-p
-                       (re-search-backward
-                        (concat "^\\(?:" outline-regexp "\\)")
-                        nil 'move))
+                       (if outline-search-function
+                            (funcall outline-search-function nil t t)
+                          (re-search-backward regexp nil 'move)))
                  (outline-invisible-p)))
       (setq arg (1+ arg)))
     (while (and (not (eobp)) (> arg 0))
       (while (and (not (eobp))
                  (setq found-heading-p
-                       (re-search-forward
-                        (concat "^\\(?:" outline-regexp "\\)")
-                        nil 'move))
+                       (if outline-search-function
+                            (funcall outline-search-function nil t)
+                          (re-search-forward regexp nil 'move)))
                  (outline-invisible-p (match-beginning 0))))
       (setq arg (1- arg)))
     (if found-heading-p (beginning-of-line))))
@@ -1026,6 +1065,8 @@ Note that this does not hide the lines preceding the 
first heading line."
   ;; Nullify the hook to avoid repeated calls to `outline-flag-region'
   ;; wasting lots of time running `lazy-lock-fontify-after-outline'
   ;; and run the hook finally.
+  ;; FIXME: The above comment seems outdated, as lazy-lock has been
+  ;;        removed from Emacs.
   (let (outline-view-change-hook)
     (save-excursion
       (save-restriction
@@ -1107,8 +1148,11 @@ of the current heading, or to 1 if the current line is 
not a heading."
   (interactive (list
                (cond
                 (current-prefix-arg (prefix-numeric-value current-prefix-arg))
-                ((save-excursion (beginning-of-line)
-                                 (looking-at outline-regexp))
+                ((save-excursion
+                    (beginning-of-line)
+                   (if outline-search-function
+                        (funcall outline-search-function nil nil nil t)
+                      (looking-at outline-regexp)))
                  (funcall outline-level))
                 (t 1))))
   (if (< levels 1)
@@ -1255,7 +1299,9 @@ If INVISIBLE-OK is non-nil, also consider invisible 
lines."
          (setq level (funcall outline-level)))
        (setq start-level level))
       (setq arg (- arg 1))))
-  (looking-at outline-regexp))
+  (if outline-search-function
+      (funcall outline-search-function nil nil nil t)
+    (looking-at outline-regexp)))
 
 (defun outline-forward-same-level (arg)
   "Move forward to the ARG'th subheading at same level as this one.
@@ -1313,6 +1359,60 @@ If there is no such heading, return nil."
       (if (< (funcall outline-level) level)
          nil
         (point)))))
+
+
+;;; Search text-property for outline headings
+
+;;;###autoload
+(defun outline-search-level (&optional bound move backward looking-at)
+  "Search for the next text property `outline-level'.
+The arguments are the same as in `outline-search-text-property',
+except the hard-coded property name `outline-level'.
+This function is intended to be used in `outline-search-function'."
+  (outline-search-text-property 'outline-level nil bound move backward 
looking-at))
+
+(autoload 'text-property-search-forward "text-property-search")
+(autoload 'text-property-search-backward "text-property-search")
+
+(defun outline-search-text-property (property &optional value bound move 
backward looking-at)
+  "Search for the next text property PROPERTY with VALUE.
+The rest of arguments are described in `outline-search-function'."
+  (if looking-at
+      (when (if value (eq (get-text-property (point) property) value)
+              (get-text-property (point) property))
+        (set-match-data (list (pos-bol) (pos-eol)))
+        t)
+    ;; Go to the end when in the middle of heading
+    (when (and (not backward)
+               (if value (eq (get-text-property (point) property) value)
+                 (get-text-property (point) property))
+               (not (or (bobp)
+                        (not (if value
+                                 (eq (get-text-property (1- (point)) property) 
value)
+                               (get-text-property (1- (point)) property))))))
+      (goto-char (1+ (pos-eol))))
+    (let ((prop-match (if backward
+                          (text-property-search-backward property value (and 
value t))
+                        (text-property-search-forward property value (and 
value t)))))
+      (if prop-match
+          (let ((beg (prop-match-beginning prop-match))
+                (end (prop-match-end prop-match)))
+            (if (or (null bound) (if backward (>= beg bound) (<= end bound)))
+                (cond (backward
+                       (goto-char beg)
+                       (goto-char (pos-bol))
+                       (set-match-data (list (point) end))
+                       t)
+                      (t
+                       (goto-char end)
+                       (goto-char (if (bolp) (1- (point)) (pos-eol)))
+                       (set-match-data (list beg (point)))
+                       t))
+              (when move (goto-char bound))
+              nil))
+        (when move (goto-char (or bound (if backward (point-min) 
(point-max)))))
+        nil))))
+
 
 (defun outline-headers-as-kill (beg end)
   "Save the visible outline headers between BEG and END to the kill ring.
diff --git a/lisp/pcomplete.el b/lisp/pcomplete.el
index 8cb0aa3b7a..ef286b70fe 100644
--- a/lisp/pcomplete.el
+++ b/lisp/pcomplete.el
@@ -1210,9 +1210,7 @@ Returns nil if no completion was inserted.
 Returns `sole' if completed with the only completion match.
 Returns `shortest' if completed with the shortest of the matches.
 Returns `partial' if completed as far as possible with the matches.
-Returns `listed' if a completion listing was shown.
-
-See also `pcomplete-filename'."
+Returns `listed' if a completion listing was shown."
   (let* ((completion-ignore-case completion-ignore-case)
         (completions (all-completions stub candidates))
          (entry (try-completion stub candidates))
diff --git a/lisp/proced.el b/lisp/proced.el
index a774f2dd1e..ac44ae1513 100644
--- a/lisp/proced.el
+++ b/lisp/proced.el
@@ -140,8 +140,8 @@ the external command (usually \"kill\")."
     (nice    "Ni"      "%3d" 3 proced-< t (nice pid) (t t nil))
     (thcount "THCount" "%d" right proced-< t (thcount pid) (nil t t))
     (start   "Start"   proced-format-start 6 proced-time-lessp nil (start pid) 
(t t nil))
-    (vsize   "VSize"   "%d" right proced-< t (vsize pid) (nil t t))
-    (rss     "RSS"     "%d" right proced-< t (rss pid) (nil t t))
+    (vsize   "VSize"   proced-format-memory right proced-< t (vsize pid) (nil 
t t))
+    (rss     "RSS"     proced-format-memory right proced-< t (rss pid) (nil t 
t))
     (etime   "ETime"   proced-format-time right proced-time-lessp t (etime 
pid) (nil t t))
     (pcpu    "%CPU"    "%.1f" right proced-< t (pcpu pid) (nil t t))
     (pmem    "%Mem"    "%.1f" right proced-< t (pmem pid) (nil t t))
@@ -740,12 +740,18 @@ Proced buffers."
         "Type \\<proced-mode-map>\\[quit-window] to quit, \\[proced-help] for 
help")))))
 
 (defun proced-auto-update-timer ()
-  "Auto-update Proced buffers using `run-at-time'."
-  (dolist (buf (buffer-list))
-    (with-current-buffer buf
-      (if (and (eq major-mode 'proced-mode)
-               proced-auto-update-flag)
-          (proced-update t t)))))
+  "Auto-update Proced buffers using `run-at-time'.
+
+If there are no proced buffers, cancel the timer."
+  (unless (seq-filter (lambda (buf)
+                        (with-current-buffer buf
+                          (when (eq major-mode 'proced-mode)
+                            (if proced-auto-update-flag
+                                (proced-update t t))
+                            t)))
+                      (buffer-list))
+    (cancel-timer proced-auto-update-timer)
+    (setq proced-auto-update-timer nil)))
 
 (defun proced-toggle-auto-update (arg)
   "Change whether this Proced buffer is updated automatically.
@@ -1425,6 +1431,10 @@ The return string is always 6 characters wide."
 Replace newline characters by \"^J\" (two characters)."
   (string-replace "\n" "^J" args))
 
+(defun proced-format-memory (kilobytes)
+  "Format KILOBYTES in a human readable format."
+  (funcall byte-count-to-string-function (* 1024 kilobytes)))
+
 (defun proced-format (process-alist format)
   "Display PROCESS-ALIST using FORMAT."
   (if (symbolp format)
diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el
new file mode 100644
index 0000000000..6eaf200182
--- /dev/null
+++ b/lisp/progmodes/c-ts-mode.el
@@ -0,0 +1,553 @@
+;;; c-ts-mode.el --- tree-sitter support for C and C++  -*- lexical-binding: 
t; -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+
+;; Author     : Theodor Thornhill <theo@thornhill.no>
+;; Maintainer : Theodor Thornhill <theo@thornhill.no>
+;; Created    : November 2022
+;; Keywords   : c c++ cpp languages tree-sitter
+
+;; This file is part of GNU Emacs.
+
+;; 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 <http://www.gnu.org/licenses/>.
+
+
+;;; Commentary:
+;;
+
+;;; Code:
+
+(require 'treesit)
+(require 'rx)
+
+(declare-function treesit-parser-create "treesit.c")
+(declare-function treesit-induce-sparse-tree "treesit.c")
+(declare-function treesit-node-parent "treesit.c")
+(declare-function treesit-node-start "treesit.c")
+(declare-function treesit-node-end "treesit.c")
+(declare-function treesit-node-child "treesit.c")
+(declare-function treesit-node-child-by-field-name "treesit.c")
+(declare-function treesit-node-type "treesit.c")
+
+(defcustom c-ts-mode-indent-offset 2
+  "Number of spaces for each indentation step in `c-ts-mode'."
+  :version "29.1"
+  :type 'integer
+  :safe 'integerp
+  :group 'c)
+
+(defcustom c-ts-mode-indent-style 'gnu
+  "Style used for indentation.
+
+The selected style could be one of GNU, K&R, LINUX or BSD.  If
+one of the supplied styles doesn't suffice a function could be
+set instead.  This function is expected return a list that
+follows the form of `treesit-simple-indent-rules'."
+  :version "29.1"
+  :type '(choice (symbol :tag "Gnu" 'gnu)
+                 (symbol :tag "K&R" 'k&r)
+                 (symbol :tag "Linux" 'linux)
+                 (symbol :tag "BSD" 'bsd)
+                 (function :tag "A function for user customized style" ignore))
+  :group 'c)
+
+(defvar c-ts-mode--syntax-table
+  (let ((table (make-syntax-table)))
+    ;; Taken from the cc-langs version
+    (modify-syntax-entry ?_  "_"     table)
+    (modify-syntax-entry ?\\ "\\"    table)
+    (modify-syntax-entry ?+  "."     table)
+    (modify-syntax-entry ?-  "."     table)
+    (modify-syntax-entry ?=  "."     table)
+    (modify-syntax-entry ?%  "."     table)
+    (modify-syntax-entry ?<  "."     table)
+    (modify-syntax-entry ?>  "."     table)
+    (modify-syntax-entry ?&  "."     table)
+    (modify-syntax-entry ?|  "."     table)
+    (modify-syntax-entry ?\' "\""    table)
+    (modify-syntax-entry ?\240 "."   table)
+    (modify-syntax-entry ?/  ". 124b" table)
+    (modify-syntax-entry ?*  ". 23"   table)
+    table)
+  "Syntax table for `c-ts-mode'.")
+
+(defun c-ts-mode--indent-styles (mode)
+  "Indent rules supported by `c-ts-mode'.
+MODE is either `c' or `cpp'."
+  (let ((common
+         `(((parent-is "translation_unit") parent-bol 0)
+           ((node-is ")") parent 1)
+           ((node-is "]") parent-bol 0)
+           ((node-is "}") (and parent parent-bol) 0)
+           ((node-is "else") parent-bol 0)
+           ((node-is "case") parent-bol 0)
+           ((node-is "preproc_arg") no-indent)
+           ((and (parent-is "comment") comment-end) comment-start -1)
+           ((parent-is "comment") comment-start-skip 0)
+           ((node-is "labeled_statement") parent-bol 0)
+           ((parent-is "labeled_statement") parent-bol c-ts-mode-indent-offset)
+           ((match "preproc_ifdef" "compound_statement") point-min 0)
+           ((match "#endif" "preproc_ifdef") point-min 0)
+           ((match "preproc_if" "compound_statement") point-min 0)
+           ((match "#endif" "preproc_if") point-min 0)
+           ((match "preproc_function_def" "compound_statement") point-min 0)
+           ((match "preproc_call" "compound_statement") point-min 0)
+           ((parent-is "compound_statement") (and parent parent-bol) 
c-ts-mode-indent-offset)
+           ((parent-is "function_definition") parent-bol 0)
+           ((parent-is "conditional_expression") first-sibling 0)
+           ((parent-is "assignment_expression") parent-bol 
c-ts-mode-indent-offset)
+           ((parent-is "comma_expression") first-sibling 0)
+           ((parent-is "init_declarator") parent-bol c-ts-mode-indent-offset)
+           ((parent-is "parenthesized_expression") first-sibling 1)
+           ((parent-is "argument_list") first-sibling 1)
+           ((parent-is "parameter_list") first-sibling 1)
+           ((parent-is "binary_expression") parent 0)
+           ((query "(for_statement initializer: (_) @indent)") parent-bol 5)
+           ((query "(for_statement condition: (_) @indent)") parent-bol 5)
+           ((query "(for_statement update: (_) @indent)") parent-bol 5)
+           ((query "(call_expression arguments: (_) @indent)") parent 
c-ts-mode-indent-offset)
+           ((parent-is "call_expression") parent 0)
+           ((parent-is "enumerator_list") parent-bol c-ts-mode-indent-offset)
+           ((parent-is "field_declaration_list") parent-bol 
c-ts-mode-indent-offset)
+           ((parent-is "initializer_list") parent-bol c-ts-mode-indent-offset)
+           ((parent-is "if_statement") parent-bol c-ts-mode-indent-offset)
+           ((parent-is "for_statement") parent-bol c-ts-mode-indent-offset)
+           ((parent-is "while_statement") parent-bol c-ts-mode-indent-offset)
+           ((parent-is "switch_statement") parent-bol c-ts-mode-indent-offset)
+           ((parent-is "case_statement") parent-bol c-ts-mode-indent-offset)
+           ((parent-is "do_statement") parent-bol c-ts-mode-indent-offset)
+           ,@(when (eq mode 'cpp)
+               `(((node-is "field_initializer_list") parent-bol ,(* 
c-ts-mode-indent-offset 2)))))))
+    `((gnu
+       ;; Prepend rules to set highest priority
+       ((match "while" "do_statement") parent 0)
+       ,@common)
+      (k&r ,@common)
+      (linux ,@common)
+      (bsd
+       ((parent-is "if_statement") parent-bol 0)
+       ((parent-is "for_statement") parent-bol 0)
+       ((parent-is "while_statement") parent-bol 0)
+       ((parent-is "switch_statement") parent-bol 0)
+       ((parent-is "case_statement") parent-bol 0)
+       ((parent-is "do_statement") parent-bol 0)
+       ,@common))))
+
+(defun c-ts-mode--set-indent-style (mode)
+  "Helper function to set indentation style.
+MODE is either `c' or `cpp'."
+  (let ((style
+         (if (functionp c-ts-mode-indent-style)
+             (funcall c-ts-mode-indent-style)
+           (pcase c-ts-mode-indent-style
+             ('gnu   (alist-get 'gnu (c-ts-mode--indent-styles mode)))
+             ('k&r   (alist-get 'k&r (c-ts-mode--indent-styles mode)))
+             ('bsd   (alist-get 'bsd (c-ts-mode--indent-styles mode)))
+             ('linux (alist-get 'linux (c-ts-mode--indent-styles mode)))))))
+    `((,mode ,@style))))
+
+(defvar c-ts-mode--preproc-keywords
+  '("#define" "#if" "#ifdef" "#ifndef"
+    "#else" "#elif" "#endif" "#include")
+  "C/C++ keywords for tree-sitter font-locking.")
+
+(defun c-ts-mode--keywords (mode)
+  "C/C++ keywords for tree-sitter font-locking.
+MODE is either `c' or `cpp'."
+  (let ((c-keywords
+         '("break" "case" "const" "continue"
+           "default" "do" "else" "enum"
+           "extern" "for" "goto" "if"
+           "long" "register" "return" "short"
+           "signed" "sizeof" "static" "struct"
+           "switch" "typedef" "union" "unsigned"
+           "volatile" "while")))
+    (if (eq mode 'cpp)
+        (append c-keywords
+                '("and" "and_eq" "bitand" "bitor"
+                  "catch" "class" "co_await" "co_return"
+                  "co_yield" "compl" "concept" "consteval"
+                  "constexpr" "constinit" "decltype" "delete"
+                  "explicit" "final" "friend" "friend"
+                  "mutable" "namespace" "new" "noexcept"
+                  "not" "not_eq" "operator" "or"
+                  "or_eq" "override" "private" "protected"
+                  "public" "requires" "template" "throw"
+                  "try" "typename" "using" "virtual"
+                  "xor" "xor_eq"))
+      (append '("auto") c-keywords))))
+
+(defvar c-ts-mode--operators
+  '("=" "-" "*" "/" "+" "%" "~" "|" "&" "^" "<<" ">>" "->"
+    "." "<" "<=" ">=" ">" "==" "!=" "!" "&&" "||" "-="
+    "+=" "*=" "/=" "%=" "|=" "&=" "^=" ">>=" "<<=" "--" "++")
+  "C/C++ operators for tree-sitter font-locking.")
+
+(defun c-ts-mode--font-lock-settings (mode)
+  "Tree-sitter font-lock settings.
+MODE is either `c' or `cpp'."
+  (treesit-font-lock-rules
+   :language mode
+   :feature 'comment
+   `((comment) @font-lock-comment-face
+     (comment) @contextual)
+
+   :language mode
+   :feature 'preprocessor
+   `((preproc_directive) @font-lock-preprocessor-face
+
+     (preproc_def
+      name: (identifier) @font-lock-variable-name-face)
+
+     (preproc_ifdef
+      name: (identifier) @font-lock-variable-name-face)
+
+     (preproc_function_def
+      name: (identifier) @font-lock-function-name-face)
+
+     (preproc_params
+      (identifier) @font-lock-variable-name-face)
+
+     (preproc_defined) @font-lock-preprocessor-face
+     (preproc_defined (identifier) @font-lock-variable-name-face)
+     [,@c-ts-mode--preproc-keywords] @font-lock-preprocessor-face)
+
+   :language mode
+   :feature 'constant
+   `((true) @font-lock-constant-face
+     (false) @font-lock-constant-face
+     (null) @font-lock-constant-face
+     ,@(when (eq mode 'cpp)
+         '((this) @font-lock-constant-face)))
+
+   :language mode
+   :feature 'keyword
+   `([,@(c-ts-mode--keywords mode)] @font-lock-keyword-face
+     ,@(when (eq mode 'cpp)
+         '((auto) @font-lock-keyword-face)))
+
+   :language mode
+   :feature 'operator
+   `([,@c-ts-mode--operators] @font-lock-operator-face
+     "!" @font-lock-negation-char-face)
+
+   :language mode
+   :feature 'string
+   `((string_literal) @font-lock-string-face
+     (system_lib_string) @font-lock-string-face)
+
+   :language mode
+   :feature 'literal
+   `((number_literal) @font-lock-number-face
+     (char_literal) @font-lock-constant-face)
+
+   :language mode
+   :feature 'type
+   `((primitive_type) @font-lock-type-face
+     (type_identifier) @font-lock-type-face
+     (sized_type_specifier) @font-lock-type-face
+     ,@(when (eq mode 'cpp)
+         '((type_qualifier) @font-lock-type-face
+
+           (qualified_identifier
+            scope: (namespace_identifier) @font-lock-type-face)
+
+           (operator_cast) type: (type_identifier) @font-lock-type-face)))
+
+   :language mode
+   :feature 'definition
+   ;; Highlights identifiers in declarations.
+   `((declaration
+      declarator: (_) @font-lock-variable-name-face)
+
+     (field_declaration
+      declarator: (_) @c-ts-mode--fontify-declarator)
+
+     (function_definition
+      declarator: (_) @c-ts-mode--fontify-declarator))
+
+   ;; Should we highlight identifiers in the parameter list?
+   ;; (parameter_declaration
+   ;;  declarator: (_) @c-ts-mode--fontify-declarator))
+
+   :language mode
+   :feature 'assignment
+   ;; TODO: Recursively highlight identifiers in parenthesized
+   ;; expressions, see `c-ts-mode--fontify-struct-declarator' for
+   ;; inspiration.
+   '((assignment_expression
+      left: (identifier) @font-lock-variable-name-face)
+     (assignment_expression
+      left: (field_expression field: (_) @font-lock-property-face))
+     (assignment_expression
+      left: (pointer_expression
+             (identifier) @font-lock-variable-name-face))
+     (assignment_expression
+      left: (subscript_expression
+             (identifier) @font-lock-variable-name-face)))
+
+   :language mode
+   :feature 'expression
+   '((call_expression
+      function: (identifier) @font-lock-function-name-face)
+     (field_expression
+      argument: (identifier) @font-lock-variable-name-face)
+     (pointer_expression
+      (identifier) @font-lock-variable-name-face))
+
+   :language mode
+   :feature 'label
+   '((expression_statement (identifier) @font-lock-variable-name-face)
+     (labeled_statement
+      label: (statement_identifier) @font-lock-constant-face))
+
+   :language mode
+   :feature 'error
+   '((ERROR) @font-lock-warning-face)
+
+   :feature 'escape-sequence
+   :language mode
+   :override t
+   '((escape_sequence) @font-lock-escape-face)
+
+   :language mode
+   :feature 'property
+   '((field_identifier) @font-lock-property-face
+     (enumerator
+      name: (identifier) @font-lock-property-face))
+
+   :language mode
+   :feature 'bracket
+   '((["(" ")" "[" "]" "{" "}"]) @font-lock-bracket-face)
+
+   :language mode
+   :feature 'delimiter
+   '((["," ":" ";"]) @font-lock-delimiter-face)
+
+   :language mode
+   :feature 'emacs-devel
+   :override t
+   '(((call_expression
+       (call_expression function: (identifier) @fn)
+       @c-ts-mode--fontify-defun)
+      (:match "^DEFUN$" @fn)))))
+
+(defun c-ts-mode--fontify-declarator (node override start end &rest args)
+  "Fontify a declarator (whatever under the \"declarator\" field).
+For NODE, OVERRIDE, START, END, and ARGS, see
+`treesit-font-lock-rules'."
+  (pcase (treesit-node-type node)
+    ((or "attributed_declarator" "parenthesized_declarator")
+     (apply #'c-ts-mode--fontify-declarator
+            (treesit-node-child node 0 t) override start end args))
+    ("pointer_declarator"
+     (apply #'c-ts-mode--fontify-declarator
+            (treesit-node-child node -1) override start end args))
+    ((or "function_declarator" "array_declarator" "init_declarator")
+     (apply #'c-ts-mode--fontify-declarator
+            (treesit-node-child-by-field-name node "declarator")
+            override start end args))
+    ((or "identifier" "field_identifier")
+     (treesit-fontify-with-override
+      (max (treesit-node-start node) start)
+      (min (treesit-node-end node) end)
+      (pcase (treesit-node-type (treesit-node-parent node))
+        ("function_declarator" 'font-lock-function-name-face)
+        (_ 'font-lock-variable-name-face))
+      override))))
+
+(defun c-ts-mode--fontify-defun (node override start end &rest _)
+  "Correctly fontify the DEFUN macro.
+For NODE, OVERRIDE, START, and END, see
+`treesit-font-lock-rules'.  The captured NODE is a
+call_expression where DEFUN is the function.
+
+This function corrects the fontification on the colon in
+\"doc:\", and the parameter list."
+  (let* ((parent (treesit-node-parent node))
+         ;; ARG-LIST-1 and 2 are like this:
+         ;;
+         ;; DEFUN (ARG-LIST-1)
+         ;; (ARG-LIST-2)
+         (arg-list-1 (treesit-node-children
+                      (treesit-node-child-by-field-name
+                       node "arguments")))
+         ;; ARG-LIST-2 is the
+         (arg-list-2 (treesit-node-children
+                      (treesit-node-child-by-field-name
+                       parent "arguments") t)))
+    ;; Fix the colon.
+    (dolist (node arg-list-1)
+      (when (equal (treesit-node-text node t) ":")
+        (treesit-fontify-with-override
+         (treesit-node-start node) (treesit-node-end node)
+         'default override)))
+    ;; Fix the parameter list.
+    (while arg-list-2
+      (let ((type (and arg-list-2 (pop arg-list-2)))
+            (arg (and arg-list-2 (pop arg-list-2))))
+        (when type
+          (treesit-fontify-with-override
+           (max start (treesit-node-start type))
+           (min end (treesit-node-end type))
+           'font-lock-type-face override))
+        (when arg
+          (treesit-fontify-with-override
+           (max start (treesit-node-start arg))
+           (min end (treesit-node-end arg))
+           'default override))))))
+
+(defun c-ts-mode--imenu-1 (node)
+  "Helper for `c-ts-mode--imenu'.
+Find string representation for NODE and set marker, then recurse
+the subtrees."
+  (let* ((ts-node (car node))
+         (subtrees (mapcan #'c-ts-mode--imenu-1 (cdr node)))
+         (name (when ts-node
+                 (treesit-node-text
+                  (pcase (treesit-node-type ts-node)
+                    ("function_definition"
+                     (treesit-node-child-by-field-name
+                      (treesit-node-child-by-field-name
+                       ts-node "declarator")
+                      "declarator"))
+                    ("declaration"
+                     (let ((child (treesit-node-child ts-node -1 t)))
+                       (pcase (treesit-node-type child)
+                         ("identifier" child)
+                         (_ (treesit-node-child-by-field-name
+                             child "declarator")))))
+                    ("struct_specifier"
+                     (treesit-node-child-by-field-name
+                      ts-node "name"))))))
+         (marker (when ts-node
+                   (set-marker (make-marker)
+                               (treesit-node-start ts-node)))))
+    (cond
+     ;; A struct_specifier could be inside a parameter list, another
+     ;; struct definition, a variable declaration, a function
+     ;; declaration.  In those cases we don't include it.
+     ((string-match-p
+       (rx (or "parameter_declaration" "field_declaration"
+               "declaration" "function_definition"))
+       (or (treesit-node-type (treesit-node-parent ts-node))
+           ""))
+      nil)
+     ;; Ignore function local variable declarations.
+     ((and (equal (treesit-node-type ts-node) "declaration")
+           (not (equal (treesit-node-type (treesit-node-parent ts-node))
+                       "translation_unit")))
+      nil)
+     ((or (null ts-node) (null name)) subtrees)
+     (subtrees
+      `((,name ,(cons name marker) ,@subtrees)))
+     (t
+      `((,name . ,marker))))))
+
+(defun c-ts-mode--imenu ()
+  "Return Imenu alist for the current buffer."
+  (let* ((node (treesit-buffer-root-node))
+         (func-tree (treesit-induce-sparse-tree
+                     node "^function_definition$" nil 1000))
+         (var-tree (treesit-induce-sparse-tree
+                    node "^declaration$" nil 1000))
+         (struct-tree (treesit-induce-sparse-tree
+                       node "^struct_specifier$" nil 1000))
+         (func-index (c-ts-mode--imenu-1 func-tree))
+         (var-index (c-ts-mode--imenu-1 var-tree))
+         (struct-index (c-ts-mode--imenu-1 struct-tree)))
+    (append
+     (when struct-index `(("Struct" . ,struct-index)))
+     (when var-index `(("Variable" . ,var-index)))
+     (when func-index `(("Function" . ,func-index))))))
+
+;;;###autoload
+(define-derived-mode c-ts-mode--base-mode prog-mode "C"
+  "Major mode for editing C, powered by tree-sitter."
+  :syntax-table c-ts-mode--syntax-table
+
+  ;; Navigation.
+  (setq-local treesit-defun-type-regexp
+              (rx (or "specifier"
+                      "definition")))
+
+  ;; Indent.
+  (when (eq c-ts-mode-indent-style 'linux)
+    (setq-local indent-tabs-mode t))
+
+  ;; Electric
+  (setq-local electric-indent-chars
+              (append "{}():;," electric-indent-chars))
+
+  ;; Imenu.
+  (setq-local imenu-create-index-function #'c-ts-mode--imenu)
+  (setq-local which-func-functions nil)
+
+  (setq-local treesit-font-lock-feature-list
+              '(( comment constant keyword literal preprocessor string)
+                ( assignment definition label property type)
+                ( bracket delimiter error escape-sequence expression
+                  operator))))
+
+;;;###autoload
+(define-derived-mode c-ts-mode c-ts-mode--base-mode "C"
+  "Major mode for editing C, powered by tree-sitter."
+  :group 'c
+
+  (unless (treesit-ready-p 'c)
+    (error "Tree-sitter for C isn't available"))
+
+  (treesit-parser-create 'c)
+
+  ;; Comments.
+  (setq-local comment-start "/* ")
+  (setq-local comment-start-skip "\\(?://+\\|/\\*+\\)\\s *")
+  (setq-local comment-end " */")
+  (setq-local treesit-comment-start (rx "/" (or (+ "/") (+ "*"))))
+  (setq-local treesit-comment-end (rx (+ (or "*")) "/"))
+
+  (setq-local treesit-simple-indent-rules
+              (c-ts-mode--set-indent-style 'c))
+
+  ;; Font-lock.
+  (setq-local treesit-font-lock-settings (c-ts-mode--font-lock-settings 'c))
+
+  (treesit-major-mode-setup))
+
+;;;###autoload
+(define-derived-mode c++-ts-mode c-ts-mode--base-mode "C++"
+  "Major mode for editing C++, powered by tree-sitter."
+  :group 'c++
+
+  (unless (treesit-ready-p 'cpp)
+    (error "Tree-sitter for C++ isn't available"))
+
+  ;; Comments.
+  (setq-local comment-start "// ")
+  (setq-local comment-start-skip "\\(?://+\\|/\\*+\\)\\s *")
+  (setq-local comment-end "")
+
+  (treesit-parser-create 'cpp)
+
+  (setq-local treesit-simple-indent-rules
+              (c-ts-mode--set-indent-style 'cpp))
+
+  ;; Font-lock.
+  (setq-local treesit-font-lock-settings (c-ts-mode--font-lock-settings 'cpp))
+
+  (treesit-major-mode-setup))
+
+(provide 'c-ts-mode)
+
+;;; c-ts-mode.el ends here
diff --git a/lisp/progmodes/cc-cmds.el b/lisp/progmodes/cc-cmds.el
index 38e9d6011d..7bfd6bdbd9 100644
--- a/lisp/progmodes/cc-cmds.el
+++ b/lisp/progmodes/cc-cmds.el
@@ -1456,7 +1456,7 @@ keyword on the line, the keyword is not inserted inside a 
literal, and
 
 (defun c-align-cpp-indent-to-body ()
   "Align a \"#pragma\" line under the previous line.
-This function is intented for use as a member of `c-special-indent-hook'."
+This function is intended for use as a member of `c-special-indent-hook'."
   (when (assq 'cpp-macro c-syntactic-context)
     (when
        (save-excursion
diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el
index 5d3d240886..7e6dd43175 100644
--- a/lisp/progmodes/cc-engine.el
+++ b/lisp/progmodes/cc-engine.el
@@ -6963,7 +6963,7 @@ comment at the start of cc-engine.el for more info."
 ;; At each buffer change, the syntax-table properties are removed in a
 ;; before-change function and reapplied, when needed, in an
 ;; after-change function.  It is far more important that the
-;; properties get removed when they they are spurious than that they
+;; properties get removed when they are spurious than that they
 ;; be present when wanted.
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 (defun c-clear-<-pair-props (&optional pos)
@@ -9036,7 +9036,8 @@ multi-line strings (but not C++, for example)."
   ;;   o - 'found if it's a type that matches one in `c-found-types';
   ;;   o - 'maybe if it's an identifier that might be a type;
   ;;   o - 'decltype if it's a decltype(variable) declaration; - or
-  ;;   o - 'no-id if "auto" precluded parsing a type identifier.
+  ;;   o - 'no-id if "auto" precluded parsing a type identifier (C++)
+  ;;      or the type int was implicit (C).
   ;;   o -  nil if it can't be a type (the point isn't moved then).
   ;;
   ;; The point is assumed to be at the beginning of a token.
@@ -9060,10 +9061,11 @@ multi-line strings (but not C++, for example)."
 
     ;; Skip leading type modifiers.  If any are found we know it's a
     ;; prefix of a type.
-    (when c-opt-type-modifier-prefix-key ; e.g. "const" "volatile", but NOT 
"typedef"
-      (while (looking-at c-opt-type-modifier-prefix-key)
-       (when (looking-at c-no-type-key)
-         (setq res 'no-id))
+    (when c-maybe-typeless-specifier-re
+      (while (looking-at c-maybe-typeless-specifier-re)
+       (save-match-data
+         (when (looking-at c-no-type-key)
+           (setq res 'no-id)))
        (goto-char (match-end 1))
        (c-forward-syntactic-ws)
        (or (eq res 'no-id)
@@ -9128,6 +9130,9 @@ multi-line strings (but not C++, for example)."
        (not (eq res 'no-id))
        (progn
         (setq pos nil)
+        (while (and c-opt-cpp-prefix
+                    (looking-at c-noise-macro-with-parens-name-re))
+          (c-forward-noise-clause))
         (if (looking-at c-identifier-start)
             (save-excursion
               (setq id-start (point)
@@ -9187,6 +9192,18 @@ multi-line strings (but not C++, for example)."
            (goto-char (match-end 1))
            (c-forward-syntactic-ws)))))
 
+     ((and (eq name-res t)
+          (eq res 'prefix)
+          (c-major-mode-is 'c-mode)
+          (save-excursion
+            (goto-char id-end)
+            (and (not (looking-at c-symbol-start))
+                 (not (looking-at c-type-decl-prefix-key)))))
+      ;; A C specifier followed by an implicit int, e.g.
+      ;; "register count;"
+      (goto-char id-start)
+      (setq res 'no-id))
+
      (name-res
       (cond ((eq name-res t)
             ;; A normal identifier.
@@ -9224,7 +9241,11 @@ multi-line strings (but not C++, for example)."
            (t
             ;; Otherwise it's an operator identifier, which is not a type.
             (goto-char start)
-            (setq res nil)))))
+            (setq res nil))))
+
+     ((eq res 'prefix)
+      ;; Deal with "extern "C" foo_t my_foo;"
+      (setq res nil)))
 
     (when (not (memq res '(nil no-id)))
       ;; Skip trailing type modifiers.  If any are found we know it's
@@ -10012,9 +10033,11 @@ This function might do hidden buffer changes."
               got-suffix-after-parens id-start
               paren-depth 0))
 
-     (if (setq at-type (if (eq backup-at-type 'prefix)
-                          t
-                        backup-at-type))
+     (if (not (memq
+              (setq at-type (if (eq backup-at-type 'prefix)
+                                t
+                              backup-at-type))
+              '(nil no-id)))
         (setq type-start backup-type-start
               id-start backup-id-start)
        (setq type-start start-pos
@@ -11079,6 +11102,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
@@ -11214,7 +11242,8 @@ This function might do hidden buffer changes."
 
       ;; Record the type's coordinates in `c-record-type-identifiers' for
       ;; later fontification.
-      (when (and c-record-type-identifiers at-type ;; (not (eq at-type t))
+      (when (and c-record-type-identifiers
+                (not (memq at-type '(nil no-id)))
                 ;; There seems no reason to exclude a token from
                 ;; fontification just because it's "a known type that can't
                 ;; be a name or other expression".  2013-09-18.
@@ -12597,7 +12626,7 @@ comment at the start of cc-engine.el for more info."
 
 (defun c-laomib-fix-elt (lwm elt paren-state)
   ;; Correct a c-laomib-cache entry ELT with respect to buffer changes, either
-  ;; doing nothing, signalling it is to be deleted, or replacing its start
+  ;; doing nothing, signaling it is to be deleted, or replacing its start
   ;; point with one lower in the buffer than LWM.  PAREN-STATE is the paren
   ;; state at LWM.  Return the corrected entry, or nil (if it needs deleting).
   ;; Note that corrections are made by `setcar'ing the original structure,
diff --git a/lisp/progmodes/cc-langs.el b/lisp/progmodes/cc-langs.el
index 291af038b7..561aa0f7e5 100644
--- a/lisp/progmodes/cc-langs.el
+++ b/lisp/progmodes/cc-langs.el
@@ -2713,7 +2713,7 @@ before the type, so such things are not necessary to 
mention here.
 Mentioning them here is necessary only if they can occur in other
 places, or if they are followed by a construct that must be skipped
 over (like the parens in the \"__attribute__\" and \"__declspec\"
-examples above).  In the last case, they alse need to be present on
+examples above).  In the last case, they also need to be present on
 one of `c-type-list-kwds', `c-ref-list-kwds',
 `c-colon-type-list-kwds', `c-paren-nontype-kwds', `c-paren-type-kwds',
 `c-<>-type-kwds', or `c-<>-arglist-kwds'."
@@ -3869,6 +3869,14 @@ possible for good performance."
                     t)
         "\\>")))
 
+(c-lang-defconst c-maybe-typeless-specifier-re
+  "Regexp matching keywords which might, but needn't, declare variables with
+no explicit type given, or nil in languages without such specifiers."
+  t (c-lang-const c-opt-type-modifier-prefix-key)
+  c (c-lang-const c-type-decl-prefix-keywords-key))
+(c-lang-defvar c-maybe-typeless-specifier-re
+  (c-lang-const c-maybe-typeless-specifier-re))
+
 (c-lang-defconst c-type-decl-prefix-key
   "Regexp matching any declarator operator that might precede the
 identifier in a declaration, e.g. the \"*\" in \"char *argv\".  This
diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el
index fb5ef69413..5a610253e0 100644
--- a/lisp/progmodes/cc-mode.el
+++ b/lisp/progmodes/cc-mode.el
@@ -1235,7 +1235,7 @@ Note that the style variables are always made local to 
the buffer."
 
 (defun c-multiline-string-check-final-quote ()
   ;; Check that the final quote in the buffer is correctly marked or not with
-  ;; a string-fence syntax-table text propery.  The return value has no
+  ;; a string-fence syntax-table text property.  The return value has no
   ;; significance.
   (let (pos-ll pos-lt)
     (save-excursion
diff --git a/lisp/progmodes/ebnf2ps.el b/lisp/progmodes/ebnf2ps.el
index 1894826fe4..36849492be 100644
--- a/lisp/progmodes/ebnf2ps.el
+++ b/lisp/progmodes/ebnf2ps.el
@@ -4282,7 +4282,7 @@ end
   (ebnf-eps-header-footer ebnf-eps-footer))
 
 
-;; hacked fom `ps-output-string-prim' (ps-print.el)
+;; hacked from `ps-output-string-prim' (ps-print.el)
 (defun ebnf-eps-string (string)
   (let* ((str   string)
         (len   (length str))
@@ -4403,9 +4403,9 @@ end
 (defvar ebnf-nprod 0)
 
 
-(defsubst ebnf-message-info (messag)
+(defsubst ebnf-message-info (msg)
   (message "%s...%3d%%"
-          messag
+           msg
           (round (/ (* (setq ebnf-nprod (1+ ebnf-nprod)) 100.0) ebnf-total))))
 
 
diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index 12808e80c4..bbd902c1c7 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -9,7 +9,7 @@
 ;; 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
+;; This 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.
 
@@ -192,8 +192,9 @@ chosen (interactively or automatically)."
                                  . ("typescript-language-server" "--stdio"))
                                 (sh-mode . ("bash-language-server" "start"))
                                 ((php-mode phps-mode)
-                                 . ("php" "vendor/felixfbecker/\
-language-server/bin/php-language-server.php"))
+                                 . ,(eglot-alternatives
+                                     '(("phpactor" "language-server")
+                                       ("php" 
"vendor/felixfbecker/language-server/bin/php-language-server.php"))))
                                 ((c++-mode c-mode) . ,(eglot-alternatives
                                                        '("clangd" "ccls")))
                                 (((caml-mode :language-id "ocaml")
@@ -820,9 +821,6 @@ treated as in `eglot--dbind'."
    (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."
@@ -1237,7 +1235,7 @@ This docstring appeases checkdoc, that's all."
            :request-dispatcher (funcall spread #'eglot-handle-request)
            :on-shutdown #'eglot--on-shutdown
            initargs))
-         (cancelled nil)
+         (canceled nil)
          (tag (make-symbol "connected-catch-tag")))
     (when server-info
       (jsonrpc--debug server "Running language server: %s"
@@ -1276,7 +1274,7 @@ This docstring appeases checkdoc, that's all."
                             :workspaceFolders (eglot-workspace-folders server))
                       :success-fn
                       (eglot--lambda ((InitializeResult) capabilities 
serverInfo)
-                        (unless cancelled
+                        (unless canceled
                           (push server
                                 (gethash project eglot--servers-by-project))
                           (setf (eglot--capabilities server) capabilities)
@@ -1314,13 +1312,13 @@ in project `%s'."
                           (when tag (throw tag t))))
                       :timeout eglot-connect-timeout
                       :error-fn (eglot--lambda ((ResponseError) code message)
-                                  (unless cancelled
+                                  (unless canceled
                                     (jsonrpc-shutdown server)
                                     (let ((msg (format "%s: %s" code message)))
                                       (if tag (throw tag `(error . ,msg))
                                         (eglot--error msg)))))
                       :timeout-fn (lambda ()
-                                    (unless cancelled
+                                    (unless canceled
                                       (jsonrpc-shutdown server)
                                       (let ((msg (format "Timed out after %s 
seconds"
                                                          
eglot-connect-timeout)))
@@ -1337,7 +1335,7 @@ in project `%s'."
                                       (jsonrpc-name server))
                       nil)
                 (_ server)))
-          (quit (jsonrpc-shutdown server) (setq cancelled 'quit)))
+          (quit (jsonrpc-shutdown server) (setq canceled 'quit)))
       (setq tag nil))))
 
 (defun eglot--inferior-bootstrap (name contact &optional connect-args)
@@ -1688,7 +1686,7 @@ 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."
+  "Tell if Eglot should stay out of SYMBOL."
   (cl-find (symbol-name symbol) eglot-stay-out-of
            :test (lambda (s thing)
                    (let ((re (if (symbolp thing) (symbol-name thing) thing)))
@@ -1923,12 +1921,11 @@ Uses THING, FACE, DEFS and PREPEND."
 
 (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))))
+  (let* ((server (eglot-current-server))
+         (nick (and server (eglot-project-nickname server)))
+         (pending (and server (hash-table-count
+                               (jsonrpc--request-continuations server))))
+         (last-error (and server (jsonrpc-last-error server))))
     (append
      `(,(propertize
          eglot-menu-string
@@ -1954,14 +1951,11 @@ Uses THING, FACE, DEFS and PREPEND."
                      '((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"))
+       ,@(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"))))))))
 
@@ -2298,8 +2292,7 @@ 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.
+info node `(emacs)Directory Variables' for various ways to do that.
 
 Here's an example value that establishes two sections relevant to
 the Pylsp and Gopls LSP servers:
@@ -2414,7 +2407,6 @@ When called interactively, use the currently active 
server"
                    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 ()
@@ -2460,7 +2452,7 @@ When called interactively, use the currently active 
server"
 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')."
+`flymake-diagnostic-functions')."
   (cond (eglot--managed-mode
          (setq eglot--current-flymake-report-fn report-fn)
          (eglot--report-to-flymake eglot--diagnostics))
@@ -3213,7 +3205,7 @@ at point.  With prefix argument, prompt for ACTION-KIND."
       actions)))
 
 (defun eglot--read-execute-code-action (actions server &optional action-kind)
-  "Helper for interactive calls to `eglot-code-actions'"
+  "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))
diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el
index 537b9484bd..7c470de195 100644
--- a/lisp/progmodes/elisp-mode.el
+++ b/lisp/progmodes/elisp-mode.el
@@ -52,7 +52,7 @@ All commands in `lisp-mode-shared-map' are inherited by this 
map."
   :parent lisp-mode-shared-map
   "M-TAB" #'completion-at-point
   "C-M-x" #'eval-defun
-  "C-c C-e" #'elisp-eval-buffer
+  "C-c C-e" #'elisp-eval-region-or-buffer
   "C-c C-f" #'elisp-byte-compile-file
   "C-c C-b" #'elisp-byte-compile-buffer
   "C-M-q" #'indent-pp-sexp)
@@ -280,7 +280,9 @@ Comments in the form will be lost."
   (remove-hook 'electric-pair-mode-hook #'emacs-lisp-set-electric-text-pairs))
 
 (defun elisp-enable-lexical-binding (&optional interactive)
-  "Make the current buffer use `lexical-binding'."
+  "Make the current buffer use `lexical-binding'.
+INTERACTIVE non-nil means ask the user for confirmation; this
+happens in interactive invocations."
   (interactive "p")
   (if lexical-binding
       (when interactive
@@ -360,7 +362,7 @@ be used instead.
 ;;; Completion at point for Elisp
 
 (defun elisp--local-variables-1 (vars sexp)
-  "Return the vars locally bound around the witness, or nil if not found."
+  "Return VARS locally bound around the witness, or nil if not found."
   (let (res)
     (while
         (unless
@@ -463,7 +465,7 @@ be used instead.
          lastvars)))))
 
 (defun elisp--expect-function-p (pos)
-  "Return non-nil if the symbol at point is expected to be a function."
+  "Return non-nil if the symbol at position POS is expected to be a function."
   (or
    (and (eq (char-before pos) ?')
         (eq (char-before (1- pos)) ?#))
@@ -1232,7 +1234,7 @@ All commands in `lisp-mode-shared-map' are inherited by 
this map."
   :parent lisp-mode-shared-map
   "C-M-x" #'eval-defun
   "C-M-q" #'indent-pp-sexp
-  "C-c C-e" #'elisp-eval-buffer
+  "C-c C-e" #'elisp-eval-region-or-buffer
   "C-c C-b" #'elisp-byte-compile-buffer
   "M-TAB" #'completion-at-point
   "C-j"   #'eval-print-last-sexp)
@@ -1331,12 +1333,12 @@ Semicolons start comments.
 (defun eval-print-last-sexp (&optional eval-last-sexp-arg-internal)
   "Evaluate sexp before point; print value into current buffer.
 
+Interactively, EVAL-LAST-SEXP-ARG-INTERNAL is the prefix numeric argument.
 Normally, this function truncates long output according to the value
 of the variables `eval-expression-print-length' and
-`eval-expression-print-level'.  With a prefix argument of zero,
-however, there is no such truncation.  Such a prefix argument
-also causes integers to be printed in several additional formats
-\(octal, hexadecimal, and character).
+`eval-expression-print-level'.  But if EVAL-LAST-SEXP-ARG-INTERNAL is zero,
+there is no such truncation, and integers are printed in several additional
+formats (octal, hexadecimal, and character).
 
 If `eval-expression-debug-on-error' is non-nil, which is the default,
 this command arranges for all errors to enter the debugger."
@@ -1557,8 +1559,8 @@ POS specifies the starting position where EXP was found 
and defaults to point."
 
 (defun eval-last-sexp (eval-last-sexp-arg-internal)
   "Evaluate sexp before point; print value in the echo area.
-Interactively, with a non `-' prefix argument, print output into
-current buffer.
+Interactively, EVAL-LAST-SEXP-ARG-INTERNAL is the prefix argument.
+With a non `-' prefix argument, print output into current buffer.
 
 This commands handles `defvar', `defcustom' and `defface' the
 same way that `eval-defun' does.  See the doc string of that
@@ -1588,7 +1590,7 @@ this command arranges for all errors to enter the 
debugger."
       (car value))))
 
 (defun elisp--eval-defun-1 (form)
-  "Treat some expressions specially.
+  "Treat some expressions in FORM specially.
 Reset the `defvar' and `defcustom' variables to the initial value.
 \(For `defcustom', use the :set function if there is one.)
 Reinitialize the face according to the `defface' specification."
@@ -1688,15 +1690,21 @@ Return the result of evaluation."
     elisp--eval-defun-result))
 
 (defun eval-defun (edebug-it)
-  "Evaluate the top-level form containing point.
+  "Evaluate top-level form around point and instrument it if EDEBUG-IT is 
non-nil.
+Interactively, EDEBUG-IT is the prefix argument.
+If `edebug-all-defs' is non-nil, that inverts the meaning of EDEBUG-IT
+and the prefix argument: this function will instrument the form
+unless EDEBUG-IT is non-nil.  The command `edebug-all-defs' toggles
+the value of the variable `edebug-all-defs'.
+
 If point isn't in a top-level form, evaluate the first top-level
 form after point.  If there is no top-level form after point,
-eval the first preceeding top-level form.
+evaluate the first preceding top-level form.
 
 If the current defun is actually a call to `defvar' or `defcustom',
 evaluating it this way resets the variable using its initial value
 expression (using the defcustom's :set function if there is one), even
-if the variable already has some other value.  \(Normally `defvar' and
+if the variable already has some other value.  (Normally `defvar' and
 `defcustom' do not alter the value if there already is one.)  In an
 analogous way, evaluating a `defface' overrides any customizations of
 the face, so that it becomes defined exactly as the `defface' expression
@@ -1705,8 +1713,6 @@ says.
 If `eval-expression-debug-on-error' is non-nil, which is the default,
 this command arranges for all errors to enter the debugger.
 
-With a prefix argument, instrument the code for Edebug.
-
 If acting on a `defun' for FUNCTION, and the function was
 instrumented, `Edebug: FUNCTION' is printed in the echo area.  If not
 instrumented, just FUNCTION is printed.
@@ -1734,7 +1740,8 @@ which see."
 ;;; ElDoc Support
 
 (defvar elisp--eldoc-last-data (make-vector 3 nil)
-  "Bookkeeping; elements are as follows:
+  "Bookkeeping.
+Elements are as follows:
   0 - contains the last symbol read from the buffer.
   1 - contains the string last displayed in the echo area for variables,
       or argument string for functions.
@@ -1766,7 +1773,7 @@ it is preferable to use ElDoc's interfaces directly.")
                "use ElDoc's interfaces instead." "28.1")
 
 (defun elisp-eldoc-funcall (callback &rest _ignored)
-  "Document function call at point.
+  "Document function call at point by calling CALLBACK.
 Intended for `eldoc-documentation-functions' (which see)."
   (let* ((sym-info (elisp--fnsym-in-current-sexp))
          (fn-sym (car sym-info)))
@@ -1778,7 +1785,7 @@ Intended for `eldoc-documentation-functions' (which see)."
                        'font-lock-keyword-face)))))
 
 (defun elisp-eldoc-var-docstring (callback &rest _ignored)
-  "Document variable at point.
+  "Document variable at point by calling CALLBACK.
 Intended for `eldoc-documentation-functions' (which see).
 Also see `elisp-eldoc-var-docstring-with-value'."
   (let* ((sym (elisp--current-symbol))
@@ -1789,7 +1796,7 @@ Also see `elisp-eldoc-var-docstring-with-value'."
                :face 'font-lock-variable-name-face))))
 
 (defun elisp-eldoc-var-docstring-with-value (callback &rest _)
-  "Document variable at point.
+  "Document variable at point by calling CALLBACK.
 Intended for `eldoc-documentation-functions' (which see).
 Compared to `elisp-eldoc-var-docstring', this also includes the
 current variable value and a bigger chunk of the docstring."
@@ -1817,6 +1824,7 @@ current variable value and a bigger chunk of the 
docstring."
 
 (defun elisp-get-fnsym-args-string (sym &optional index)
   "Return a string containing the parameter list of the function SYM.
+INDEX is the index of the parameter in the returned string to highlight.
 If SYM is a subr and no arglist is obtainable from the docstring
 or elsewhere, return a 1-line docstring."
   (let ((argstring
@@ -1847,7 +1855,8 @@ or elsewhere, return a 1-line docstring."
          sym argstring index))))
 
 (defun elisp--highlight-function-argument (sym args index)
-  "Highlight argument INDEX in ARGS list for function SYM."
+  "Highlight the argument of function SYM whose index is INDEX.
+ARGS is the argument list of function SYM."
   ;; FIXME: This should probably work on the list representation of `args'
   ;; rather than its string representation.
   ;; FIXME: This function is much too long, we need to split it up!
@@ -2203,11 +2212,17 @@ Runs in a batch-mode Emacs.  Interactively use variable
     (terpri)
     (pp collected)))
 
-(defun elisp-eval-buffer ()
-  "Evaluate the forms in the current buffer."
+(defun elisp-eval-region-or-buffer ()
+  "Evaluate the forms in the active region or the whole current buffer.
+In Transient Mark mode when the mark is active, call `eval-region'.
+Otherwise, call `eval-buffer'."
   (interactive)
-  (eval-buffer)
-  (message "Evaluated the %s buffer" (buffer-name)))
+  (if (use-region-p)
+      (eval-region (region-beginning) (region-end))
+    (eval-buffer))
+  (message "Evaluated the %s%s buffer"
+           (if (use-region-p) "region in the " "")
+           (buffer-name)))
 
 (defun elisp-byte-compile-file (&optional load)
   "Byte compile the file the current buffer is visiting.
diff --git a/lisp/progmodes/flymake-cc.el b/lisp/progmodes/flymake-cc.el
index 49e36f4280..b44e625565 100644
--- a/lisp/progmodes/flymake-cc.el
+++ b/lisp/progmodes/flymake-cc.el
@@ -114,7 +114,7 @@ process that is passed the current buffer's contents via 
stdin.
 REPORT-FN is Flymake's callback."
   ;; HACK: XXX: Assuming this backend function is run before it in
   ;; `flymake-diagnostic-functions', very hackingly convince the other
-  ;; backend `flymake-proc-legacy-backend', which is on by default, to
+  ;; backend `flymake-proc-legacy-flymake', which is on by default, to
   ;; disable itself.
   ;;
   (setq-local flymake-proc-allowed-file-name-masks nil)
diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el
index 294cf47087..adb984c3e5 100644
--- a/lisp/progmodes/flymake.el
+++ b/lisp/progmodes/flymake.el
@@ -1133,7 +1133,7 @@ special *Flymake log* buffer."  :group 'flymake :lighter
     (add-hook 'kill-buffer-hook 'flymake-kill-buffer-hook nil t)
     (add-hook 'eldoc-documentation-functions 'flymake-eldoc-function t t)
 
-    ;; If Flymake happened to be already already ON, we must cleanup
+    ;; If Flymake happened to be already ON, we must cleanup
     ;; existing diagnostic overlays, lest we forget them by blindly
     ;; reinitializing `flymake--state' in the next line.
     ;; See https://github.com/joaotavora/eglot/issues/223.
diff --git a/lisp/progmodes/hideshow.el b/lisp/progmodes/hideshow.el
index 00507a3c1a..655dd6a5d9 100644
--- a/lisp/progmodes/hideshow.el
+++ b/lisp/progmodes/hideshow.el
@@ -276,7 +276,7 @@ START, END and COMMENT-START are regular expressions.  A 
block is
 defined as text surrounded by START and END.
 
 As a special case, START may be a list of the form (COMPLEX-START
-MDATA-SELECTOR), where COMPLEX-START is a regexp w/ multiple parts and
+MDATA-SELECTOR), where COMPLEX-START is a regexp with multiple parts and
 MDATA-SELECTOR an integer that specifies which sub-match is the proper
 place to adjust point, before calling `hs-forward-sexp-func'.  Point
 is adjusted to the beginning of the specified match.  For example,
@@ -576,7 +576,7 @@ property of an overlay."
        (save-match-data (not (nth 8 (syntax-ppss))))))
 
 (defun hs-forward-sexp (match-data arg)
-  "Adjust point based on MATCH-DATA and call `hs-forward-sexp-func' w/ ARG.
+  "Adjust point based on MATCH-DATA and call `hs-forward-sexp-func' with ARG.
 Original match data is restored upon return."
   (save-match-data
     (set-match-data match-data)
diff --git a/lisp/progmodes/idlw-shell.el b/lisp/progmodes/idlw-shell.el
index 63f032b7b3..20472a6034 100644
--- a/lisp/progmodes/idlw-shell.el
+++ b/lisp/progmodes/idlw-shell.el
@@ -448,7 +448,7 @@ a face highlighting may be better."
 We use a single character by default, since the main block of IDL procedures
 often has no indentation.  Where possible, IDLWAVE will use overlays to
 display the stop-lines.  The arrow is only used on character-based terminals.
-See also `idlwave-shell-use-overlay-arrow'."
+See also `idlwave-shell-mark-stop-line'."
   :group 'idlwave-shell-highlighting-and-faces
   :type 'string)
 
diff --git a/lisp/progmodes/java-ts-mode.el b/lisp/progmodes/java-ts-mode.el
new file mode 100644
index 0000000000..ee8ac31f67
--- /dev/null
+++ b/lisp/progmodes/java-ts-mode.el
@@ -0,0 +1,321 @@
+;;; java-ts-mode.el --- tree-sitter support for Java  -*- lexical-binding: t; 
-*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+
+;; Author     : Theodor Thornhill <theo@thornhill.no>
+;; Maintainer : Theodor Thornhill <theo@thornhill.no>
+;; Created    : November 2022
+;; Keywords   : java languages tree-sitter
+
+;; This file is part of GNU Emacs.
+
+;; 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 <http://www.gnu.org/licenses/>.
+
+
+;;; Commentary:
+;;
+
+;;; Code:
+
+(require 'treesit)
+
+(declare-function treesit-parser-create "treesit.c")
+(declare-function treesit-induce-sparse-tree "treesit.c")
+(declare-function treesit-node-start "treesit.c")
+(declare-function treesit-node-child-by-field-name "treesit.c")
+
+(defcustom java-ts-mode-indent-offset 4
+  "Number of spaces for each indentation step in `java-ts-mode'."
+  :version "29.1"
+  :type 'integer
+  :safe 'integerp
+  :group 'java)
+
+(defvar java-ts-mode--syntax-table
+  (let ((table (make-syntax-table)))
+    ;; Taken from the cc-langs version
+    (modify-syntax-entry ?_  "_"     table)
+    (modify-syntax-entry ?\\ "\\"    table)
+    (modify-syntax-entry ?+  "."     table)
+    (modify-syntax-entry ?-  "."     table)
+    (modify-syntax-entry ?=  "."     table)
+    (modify-syntax-entry ?%  "."     table)
+    (modify-syntax-entry ?<  "."     table)
+    (modify-syntax-entry ?>  "."     table)
+    (modify-syntax-entry ?&  "."     table)
+    (modify-syntax-entry ?|  "."     table)
+    (modify-syntax-entry ?\' "\""    table)
+    (modify-syntax-entry ?\240 "."   table)
+    table)
+  "Syntax table for `java-ts-mode'.")
+
+(defvar java-ts-mode--indent-rules
+  `((java
+     ((parent-is "program") parent-bol 0)
+     ((node-is "}") (and parent parent-bol) 0)
+     ((node-is ")") parent-bol 0)
+     ((node-is "]") parent-bol 0)
+     ((and (parent-is "comment") comment-end) comment-start -1)
+     ((parent-is "comment") comment-start-skip 0)
+     ((parent-is "class_body") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "interface_body") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "constructor_body") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "enum_body") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "switch_block") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "record_declaration_body") parent-bol 
java-ts-mode-indent-offset)
+     ((query "(method_declaration (block _ @indent))") parent-bol 
java-ts-mode-indent-offset)
+     ((query "(method_declaration (block (_) @indent))") parent-bol 
java-ts-mode-indent-offset)
+     ((parent-is "variable_declarator") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "method_invocation") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "switch_rule") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "ternary_expression") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "element_value_array_initializer") parent-bol 
java-ts-mode-indent-offset)
+     ((parent-is "function_definition") parent-bol 0)
+     ((parent-is "conditional_expression") first-sibling 0)
+     ((parent-is "assignment_expression") parent-bol 2)
+     ((parent-is "binary_expression") parent 0)
+     ((parent-is "parenthesized_expression") first-sibling 1)
+     ((parent-is "argument_list") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "annotation_argument_list") parent-bol 
java-ts-mode-indent-offset)
+     ((parent-is "modifiers") parent-bol 0)
+     ((parent-is "formal_parameters") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "formal_parameter") parent-bol 0)
+     ((parent-is "init_declarator") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "if_statement") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "for_statement") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "while_statement") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "switch_statement") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "case_statement") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "labeled_statement") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "do_statement") parent-bol java-ts-mode-indent-offset)
+     ((parent-is "block") (and parent parent-bol) java-ts-mode-indent-offset)))
+  "Tree-sitter indent rules.")
+
+(defvar java-ts-mode--keywords
+  '("abstract" "assert" "break" "case" "catch"
+    "class" "continue" "default" "do" "else"
+    "enum" "exports" "extends" "final" "finally"
+    "for" "if" "implements" "import" "instanceof"
+    "interface" "module" "native" "new" "non-sealed"
+    "open" "opens" "package" "private" "protected"
+    "provides" "public" "requires" "return" "sealed"
+    "static" "strictfp" "switch" "synchronized"
+    "throw" "throws" "to" "transient" "transitive"
+    "try" "uses" "volatile" "while" "with" "record")
+  "C keywords for tree-sitter font-locking.")
+
+(defvar java-ts-mode--operators
+  '("@" "+" ":" "++" "-" "--" "&" "&&" "|" "||"
+    "!=" "==" "*" "/" "%" "<" "<=" ">" ">=" "="
+    "-=" "+=" "*=" "/=" "%=" "->" "^" "^=" "&="
+    "|=" "~" ">>" ">>>" "<<" "::" "?")
+  "C operators for tree-sitter font-locking.")
+
+(defvar java-ts-mode--font-lock-settings
+  (treesit-font-lock-rules
+   :language 'java
+   :override t
+   :feature 'basic
+   '((identifier) @font-lock-variable-name-face)
+   :language 'java
+   :override t
+   :feature 'comment
+   `((line_comment) @font-lock-comment-face
+     (block_comment) @font-lock-comment-face)
+   :language 'java
+   :override t
+   :feature 'constant
+   `(((identifier) @font-lock-constant-face
+      (:match "^[A-Z_][A-Z_\\d]*$" @font-lock-constant-face))
+     (true) @font-lock-constant-face
+     (false) @font-lock-constant-face)
+   :language 'java
+   :override t
+   :feature 'keyword
+   `([,@java-ts-mode--keywords] @font-lock-keyword-face
+     (labeled_statement
+      (identifier) @font-lock-keyword-face))
+   :language 'java
+   :override t
+   :feature 'operator
+   `([,@java-ts-mode--operators] @font-lock-builtin-face)
+   :language 'java
+   :override t
+   :feature 'annotation
+   `((annotation
+      name: (identifier) @font-lock-constant-face)
+
+     (marker_annotation
+      name: (identifier) @font-lock-constant-face))
+   :language 'java
+   :override t
+   :feature 'string
+   `((string_literal) @font-lock-string-face)
+   :language 'java
+   :override t
+   :feature 'literal
+   `((null_literal) @font-lock-constant-face
+     (decimal_floating_point_literal)  @font-lock-constant-face
+     (hex_floating_point_literal) @font-lock-constant-face)
+   :language 'java
+   :override t
+   :feature 'type
+   '((interface_declaration
+      name: (identifier) @font-lock-type-face)
+
+     (class_declaration
+      name: (identifier) @font-lock-type-face)
+
+     (record_declaration
+      name: (identifier) @font-lock-type-face)
+
+     (enum_declaration
+      name: (identifier) @font-lock-type-face)
+
+     (constructor_declaration
+      name: (identifier) @font-lock-type-face)
+
+     (field_access
+      object: (identifier) @font-lock-type-face)
+
+     (method_reference (identifier) @font-lock-type-face)
+
+     ((scoped_identifier name: (identifier) @font-lock-type-face)
+      (:match "^[A-Z]" @font-lock-type-face))
+
+     (type_identifier) @font-lock-type-face
+
+     [(boolean_type)
+      (integral_type)
+      (floating_point_type)
+      (void_type)] @font-lock-type-face)
+   :language 'java
+   :override t
+   :feature 'definition
+   `((method_declaration
+      name: (identifier) @font-lock-function-name-face)
+
+     (formal_parameter
+      name: (identifier) @font-lock-variable-name-face)
+
+     (catch_formal_parameter
+      name: (identifier) @font-lock-variable-name-face))
+   :language 'java
+   :override t
+   :feature 'expression
+   '((method_invocation
+      object: (identifier) @font-lock-variable-name-face)
+
+     (method_invocation
+      name: (identifier) @font-lock-function-name-face)
+
+     (argument_list (identifier) @font-lock-variable-name-face)))
+  "Tree-sitter font-lock settings.")
+
+(defun java-ts-mode--imenu-1 (node)
+  "Helper for `java-ts-mode--imenu'.
+Find string representation for NODE and set marker, then recurse
+the subtrees."
+  (let* ((ts-node (car node))
+         (subtrees (mapcan #'java-ts-mode--imenu-1 (cdr node)))
+         (name (when ts-node
+                 (or (treesit-node-text
+                      (or (treesit-node-child-by-field-name
+                           ts-node "name"))
+                      t)
+                     "Unnamed node")))
+         (marker (when ts-node
+                   (set-marker (make-marker)
+                               (treesit-node-start ts-node)))))
+    (cond
+     ((null ts-node) subtrees)
+     (subtrees
+      `((,name ,(cons name marker) ,@subtrees)))
+     (t
+      `((,name . ,marker))))))
+
+(defun java-ts-mode--imenu ()
+  "Return Imenu alist for the current buffer."
+  (let* ((node (treesit-buffer-root-node))
+         (class-tree
+          `("Class" . ,(java-ts-mode--imenu-1
+                        (treesit-induce-sparse-tree
+                         node "^class_declaration$" nil 1000))))
+         (interface-tree
+          `("Interface" . ,(java-ts-mode--imenu-1
+                            (treesit-induce-sparse-tree
+                             node "^interface_declaration$"  nil 1000))))
+         (enum-tree
+          `("Enum" . ,(java-ts-mode--imenu-1
+                       (treesit-induce-sparse-tree
+                        node "^enum_declaration$"  nil 1000))))
+         (record-tree
+          `("Record" . ,(java-ts-mode--imenu-1
+                         (treesit-induce-sparse-tree
+                          node "^record_declaration$"  nil 1000))))
+         (method-tree
+          `("Method" . ,(java-ts-mode--imenu-1
+                         (treesit-induce-sparse-tree
+                          node "^method_declaration$"  nil 1000)))))
+    (cl-remove-if
+     #'null
+     `(,(when (cdr class-tree) class-tree)
+       ,(when (cdr interface-tree) interface-tree)
+       ,(when (cdr enum-tree) enum-tree)
+       ,(when (cdr record-tree) record-tree)
+       ,(when (cdr method-tree) method-tree)))))
+
+;;;###autoload
+(define-derived-mode java-ts-mode prog-mode "Java"
+  "Major mode for editing Java, powered by tree-sitter."
+  :group 'java
+  :syntax-table java-ts-mode--syntax-table
+
+  (unless (treesit-ready-p 'java)
+    (error "Tree-sitter for Java isn't available"))
+
+  (treesit-parser-create 'java)
+
+  ;; Comments.
+  (setq-local comment-start "// ")
+  (setq-local comment-start-skip "\\(?://+\\|/\\*+\\)\\s *")
+  (setq-local comment-end "")
+  (setq-local treesit-comment-start (rx "/" (or (+ "/") (+ "*"))))
+  (setq-local treesit-comment-end (rx (+ (or "*")) "/"))
+
+  ;; Indent.
+  (setq-local treesit-simple-indent-rules java-ts-mode--indent-rules)
+
+  ;; Electric
+  (setq-local electric-indent-chars
+              (append "{}():;," electric-indent-chars))
+
+  ;; Navigation.
+  (setq-local treesit-defun-type-regexp "declaration")
+
+  ;; Font-lock.
+  (setq-local treesit-font-lock-settings java-ts-mode--font-lock-settings)
+  (setq-local treesit-font-lock-feature-list
+              '((basic comment keyword constant string operator)
+                (type definition expression literal annotation)
+                ()))
+
+  ;; Imenu.
+  (setq-local imenu-create-index-function #'java-ts-mode--imenu)
+  (setq-local which-func-functions nil) ;; Piggyback on imenu
+  (treesit-major-mode-setup))
+
+(provide 'java-ts-mode)
+
+;;; java-ts-mode.el ends here
diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el
index b920ef6c2c..51d105b9d7 100644
--- a/lisp/progmodes/js.el
+++ b/lisp/progmodes/js.el
@@ -53,14 +53,26 @@
 (require 'imenu)
 (require 'json)
 (require 'prog-mode)
+(require 'treesit)
 
 (eval-when-compile
   (require 'cl-lib)
-  (require 'ido))
+  (require 'ido)
+  (require 'rx))
 
 (defvar ido-cur-list)
 (defvar electric-layout-rules)
 (declare-function ido-mode "ido" (&optional arg))
+(declare-function treesit-parser-create "treesit.c")
+(declare-function treesit-induce-sparse-tree "treesit.c")
+(declare-function treesit-search-subtree "treesit.c")
+(declare-function treesit-node-parent "treesit.c")
+(declare-function treesit-node-child "treesit.c")
+(declare-function treesit-node-child-by-field-name "treesit.c")
+(declare-function treesit-node-next-sibling "treesit.c")
+(declare-function treesit-node-start "treesit.c")
+(declare-function treesit-node-end "treesit.c")
+(declare-function treesit-node-type "treesit.c")
 
 ;;; Constants
 
@@ -663,6 +675,9 @@ This variable is like `sgml-attribute-offset'."
   :doc "Keymap for `js-mode'."
   "M-." #'js-find-symbol)
 
+(defvar js-ts-mode-map (copy-keymap js-mode-map)
+  "Keymap used in `js-ts-mode'.")
+
 ;;; Syntax table and parsing
 
 (defvar js-mode-syntax-table
@@ -3400,10 +3415,315 @@ This function is intended for use in 
`after-change-functions'."
 (c-lang-defconst c-paragraph-start
   js-mode "\\(@[[:alpha:]]+\\>\\|$\\)")
 
+;;; Tree sitter integration
+
+(defvar js--treesit-indent-rules
+  (let ((switch-case (rx "switch_" (or "case" "default"))))
+    `((javascript
+       ((parent-is "program") parent-bol 0)
+       ((node-is "}") parent-bol 0)
+       ((node-is ")") parent-bol 0)
+       ((node-is "]") parent-bol 0)
+       ((node-is ">") parent-bol 0)
+       ((parent-is "comment") comment-start 0)
+       ((and (parent-is "comment") comment-end) comment-start -1)
+       ((parent-is "comment") comment-start-skip 0)
+       ((parent-is "ternary_expression") parent-bol js-indent-level)
+       ((parent-is "member_expression") parent-bol js-indent-level)
+       ((node-is ,switch-case) parent-bol 0)
+       ;; "{" on the newline.
+       ((node-is "statement_block") parent-bol js-indent-level)
+       ((parent-is "named_imports") parent-bol js-indent-level)
+       ((parent-is "statement_block") parent-bol js-indent-level)
+       ((parent-is "variable_declarator") parent-bol js-indent-level)
+       ((parent-is "arguments") parent-bol js-indent-level)
+       ((parent-is "array") parent-bol js-indent-level)
+       ((parent-is "formal_parameters") parent-bol js-indent-level)
+       ((parent-is "template_substitution") parent-bol js-indent-level)
+       ((parent-is "object_pattern") parent-bol js-indent-level)
+       ((parent-is "object") parent-bol js-indent-level)
+       ((parent-is "pair") parent-bol js-indent-level)
+       ((parent-is "arrow_function") parent-bol js-indent-level)
+       ((parent-is "parenthesized_expression") parent-bol js-indent-level)
+       ((parent-is "class_body") parent-bol js-indent-level)
+       ((parent-is ,switch-case) parent-bol js-indent-level)
+       ((parent-is "statement_block") parent-bol js-indent-level)
+
+       ;; JSX
+       ((parent-is "jsx_opening_element") parent js-indent-level)
+       ((node-is "jsx_closing_element") parent 0)
+       ((node-is "jsx_text") parent js-indent-level)
+       ((parent-is "jsx_element") parent js-indent-level)
+       ((node-is "/") parent 0)
+       ((parent-is "jsx_self_closing_element") parent js-indent-level)))))
+
+(defvar js--treesit-keywords
+  '("as" "async" "await" "break" "case" "catch" "class" "const" "continue"
+    "debugger" "default" "delete" "do" "else" "export" "extends" "finally"
+    "for" "from" "function" "get" "if" "import" "in" "instanceof" "let" "new"
+    "of" "return" "set" "static" "switch" "switch" "target" "throw" "try"
+    "typeof" "var" "void" "while" "with" "yield")
+  "JavaScript keywords for tree-sitter font-locking.")
+
+(defvar js--treesit-font-lock-settings
+  (treesit-font-lock-rules
+
+   :language 'javascript
+   :override t
+   :feature 'comment
+   `((comment) @font-lock-comment-face)
+
+   :language 'javascript
+   :override t
+   :feature 'constant
+   `(((identifier) @font-lock-constant-face
+      (:match "^[A-Z_][A-Z_\\d]*$" @font-lock-constant-face))
+
+     [(true) (false) (null)] @font-lock-constant-face
+     (number) @font-lock-constant-face)
+
+   :language 'javascript
+   :override t
+   :feature 'keyword
+   `([,@js--treesit-keywords] @font-lock-keyword-face
+     [(this) (super)] @font-lock-keyword-face)
+
+   :language 'javascript
+   :override t
+   :feature 'string
+   `((regex pattern: (regex_pattern)) @font-lock-string-face
+     (string) @font-lock-string-face
+     (template_string) @js--fontify-template-string
+     (template_substitution ["${" "}"] @font-lock-builtin-face))
+
+   :language 'javascript
+   :override t
+   :feature 'declaration
+   `((function
+      name: (identifier) @font-lock-function-name-face)
+
+     (class_declaration
+      name: (identifier) @font-lock-type-face)
+
+     (function_declaration
+      name: (identifier) @font-lock-function-name-face)
+
+     (method_definition
+      name: (property_identifier) @font-lock-function-name-face)
+
+     (variable_declarator
+      name: (identifier) @font-lock-variable-name-face)
+
+     (variable_declarator
+      name: (identifier) @font-lock-function-name-face
+      value: [(function) (arrow_function)])
+
+     (variable_declarator
+      name: (array_pattern
+             (identifier)
+             (identifier)
+             @font-lock-function-name-face)
+      value: (array (number) (function))))
+
+   :language 'javascript
+   :override t
+   :feature 'identifier
+   `((new_expression
+      constructor: (identifier) @font-lock-type-face)
+
+     (for_in_statement
+      left: (identifier) @font-lock-variable-name-face)
+
+     (arrow_function
+      parameter: (identifier) @font-lock-variable-name-face))
+
+   :language 'javascript
+   :override t
+   :feature 'expression
+   `((assignment_expression
+      left: [(identifier) @font-lock-function-name-face
+             (member_expression property: (property_identifier)
+                                @font-lock-function-name-face)]
+      right: [(function) (arrow_function)])
+
+     (call_expression
+      function: [(identifier) @font-lock-function-name-face
+                 (member_expression
+                  property:
+                  (property_identifier) @font-lock-function-name-face)])
+
+     (assignment_expression
+      left: [(identifier) @font-lock-variable-name-face
+             (member_expression
+              property: (property_identifier) @font-lock-variable-name-face)]))
+
+   :language 'javascript
+   :override t
+   :feature 'property
+   `((pair key: (property_identifier) @font-lock-variable-name-face)
+
+     (pair value: (identifier) @font-lock-variable-name-face)
+
+     (pair
+      key: (property_identifier) @font-lock-function-name-face
+      value: [(function) (arrow_function)])
+
+     ((shorthand_property_identifier) @font-lock-variable-name-face)
+
+     ((shorthand_property_identifier_pattern) @font-lock-variable-name-face))
+
+   :language 'javascript
+   :override t
+   :feature 'pattern
+   `((pair_pattern key: (property_identifier) @font-lock-variable-name-face)
+     (array_pattern (identifier) @font-lock-variable-name-face))
+
+   :language 'javascript
+   :override t
+   :feature 'jsx
+   `(
+     (jsx_opening_element
+      [(nested_identifier (identifier)) (identifier)]
+      @font-lock-function-name-face)
+
+     (jsx_closing_element
+      [(nested_identifier (identifier)) (identifier)]
+      @font-lock-function-name-face)
+
+     (jsx_self_closing_element
+      [(nested_identifier (identifier)) (identifier)]
+      @font-lock-function-name-face)
+
+     (jsx_attribute
+      (property_identifier)
+      @font-lock-constant-face)))
+  "Tree-sitter font-lock settings.")
+
+(defun js--fontify-template-string (node override start end &rest _)
+  "Fontify template string but not substitution inside it.
+NODE is the template_string node.  START and END mark the region
+to be fontified.
+
+OVERRIDE is the override flag described in
+`treesit-font-lock-rules'."
+  ;; You would have thought that the children of the string node spans
+  ;; the whole string.  No, the children of the template_string only
+  ;; includes the starting "`", any template_substitution, and the
+  ;; closing "`".  That's why we have to track BEG instead of just
+  ;; fontifying each child.
+  (let ((child (treesit-node-child node 0))
+        (font-beg (treesit-node-start node)))
+    (while child
+      (let ((font-end (if (equal (treesit-node-type child)
+                                 "template_substitution")
+                          (treesit-node-start child)
+                        (treesit-node-end child))))
+        (setq font-beg (max start font-beg))
+        (when (< font-beg end)
+          (treesit-fontify-with-override
+           font-beg font-end 'font-lock-string-face override)))
+      (setq font-beg (treesit-node-end child)
+            child (treesit-node-next-sibling child)))))
+
+(defun js-treesit-current-defun ()
+  "Return name of surrounding function.
+This function can be used as a value in `which-func-functions'"
+  (let ((node (treesit-node-at (point)))
+        (name-list ()))
+    (cl-loop while node
+             if (pcase (treesit-node-type node)
+                  ("function_declaration" t)
+                  ("method_definition" t)
+                  ("class_declaration" t)
+                  ("variable_declarator" t)
+                  (_ nil))
+             do (push (treesit-node-text
+                       (treesit-node-child-by-field-name node "name")
+                       t)
+                      name-list)
+             do (setq node (treesit-node-parent node))
+             finally return  (string-join name-list "."))))
+
+(defun js--treesit-imenu-1 (node)
+  "Given a sparse tree, create an imenu alist.
+
+NODE is the root node of the tree returned by
+`treesit-induce-sparse-tree' (not a tree-sitter node, its car is
+a tree-sitter node).  Walk that tree and return an imenu alist.
+
+Return a list of ENTRY where
+
+ENTRY := (NAME . MARKER)
+       | (NAME . ((JUMP-LABEL . MARKER)
+                  ENTRY
+                  ...)
+
+NAME is the function/class's name, JUMP-LABEL is like \"*function
+definition*\"."
+  (let* ((ts-node (car node))
+         (children (cdr node))
+         (subtrees (mapcan #'js--treesit-imenu-1
+                           children))
+         (type (pcase (treesit-node-type ts-node)
+                 ("lexical_declaration" 'variable)
+                 ("class_declaration" 'class)
+                 ("method_definition" 'method)
+                 ("function_declaration" 'function)))
+         ;; The root of the tree could have a nil ts-node.
+         (name (when ts-node
+                 (let ((ts-node-1
+                        (if (eq type 'variable)
+                            (treesit-search-subtree
+                             ts-node "variable_declarator" nil nil 1)
+                          ts-node)))
+                   (treesit-node-text
+                    (treesit-node-child-by-field-name
+                     ts-node-1 "name")
+                    t))))
+         (marker (when ts-node
+                   (set-marker (make-marker)
+                               (treesit-node-start ts-node)))))
+    (cond
+     ((null ts-node)
+      subtrees)
+     ;; Don't included non-top-level variable declarations.
+     ((and (eq type 'variable)
+           (treesit-node-top-level ts-node))
+      nil)
+     (subtrees
+      `((,name
+         ,(cons "" marker)
+         ,@subtrees)))
+     (t (list (cons name marker))))))
+
+(defun js--treesit-imenu ()
+  "Return Imenu alist for the current buffer."
+  (let* ((node (treesit-buffer-root-node))
+         (class-tree (treesit-induce-sparse-tree
+                      node (rx (or "class_declaration"
+                                   "method_definition"))
+                      nil 1000))
+         (func-tree (treesit-induce-sparse-tree
+                     node "function_declaration" nil 1000))
+         (var-tree (treesit-induce-sparse-tree
+                    node "lexical_declaration" nil 1000)))
+    `(("Class" . ,(js--treesit-imenu-1 class-tree))
+      ("Varieable" . ,(js--treesit-imenu-1 var-tree))
+      ("Function" . ,(js--treesit-imenu-1 func-tree)))))
+
 ;;; Main Function
 
 ;;;###autoload
-(define-derived-mode js-mode prog-mode "JavaScript"
+(define-derived-mode js-base-mode prog-mode "JavaScript"
+  "Generic major mode for editing JavaScript.
+
+This mode is intended to be inherited by concrete major modes.
+Currently there are `js-mode' and `js-ts-mode'."
+  :group 'js
+  nil)
+
+;;;###autoload
+(define-derived-mode js-mode js-base-mode "JavaScript"
   "Major mode for editing JavaScript."
   :group 'js
   ;; Ensure all CC Mode "lang variables" are set to valid values.
@@ -3488,6 +3808,54 @@ This function is intended for use in 
`after-change-functions'."
   ;;(syntax-propertize (point-max))
   )
 
+;;;###autoload
+(define-derived-mode js-ts-mode js-base-mode "JavaScript"
+  "Major mode for editing JavaScript.
+
+\\<js-ts-mode-map>"
+  :group 'js
+  (when (treesit-ready-p 'javascript)
+    ;; Borrowed from `js-mode'.
+    (setq-local prettify-symbols-alist js--prettify-symbols-alist)
+    (setq-local parse-sexp-ignore-comments t)
+    ;; Which-func.
+    (setq-local which-func-imenu-joiner-function #'js--which-func-joiner)
+    ;; Comment.
+    (setq-local comment-start "// ")
+    (setq-local comment-start-skip "\\(?://+\\|/\\*+\\)\\s *")
+    (setq-local comment-end "")
+    (setq-local comment-multi-line t)
+    (setq-local treesit-comment-start (rx "/" (or (+ "/") (+ "*"))))
+    (setq-local treesit-comment-end (rx (+ (or "*")) "/"))
+    ;; Electric-indent.
+    (setq-local electric-indent-chars
+               (append "{}():;," electric-indent-chars)) ;FIXME: js2-mode adds 
"[]*".
+    (setq-local electric-layout-rules
+               '((?\; . after) (?\{ . after) (?\} . before)))
+
+    ;; Tree-sitter setup.
+    (treesit-parser-create 'javascript)
+    ;; Indent.
+    (setq-local treesit-simple-indent-rules js--treesit-indent-rules)
+    ;; Navigation.
+    (setq-local treesit-defun-type-regexp
+                (rx (or "class_declaration"
+                        "method_definition"
+                        "function_declaration"
+                        "lexical_declaration")))
+    ;; Fontification.
+    (setq-local treesit-font-lock-settings js--treesit-font-lock-settings)
+    (setq-local treesit-font-lock-feature-list
+                '((comment declaration)
+                  (string keyword identifier expression constant)
+                  (property pattern jsx )))
+    ;; Imenu
+    (setq-local imenu-create-index-function
+                #'js--treesit-imenu)
+    ;; Which-func (use imenu).
+    (setq-local which-func-functions nil)
+    (treesit-major-mode-setup)))
+
 ;;;###autoload
 (define-derived-mode js-json-mode js-mode "JSON"
   (setq-local js-enabled-frameworks nil)
diff --git a/lisp/progmodes/json-ts-mode.el b/lisp/progmodes/json-ts-mode.el
new file mode 100644
index 0000000000..4ea285bd43
--- /dev/null
+++ b/lisp/progmodes/json-ts-mode.el
@@ -0,0 +1,167 @@
+;;; json-ts-mode.el --- tree-sitter support for JSON  -*- lexical-binding: t; 
-*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+
+;; Author     : Theodor Thornhill <theo@thornhill.no>
+;; Maintainer : Theodor Thornhill <theo@thornhill.no>
+;; Created    : November 2022
+;; Keywords   : json languages tree-sitter
+
+;; This file is part of GNU Emacs.
+
+;; 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 <http://www.gnu.org/licenses/>.
+
+
+;;; Commentary:
+;;
+
+;;; Code:
+
+(require 'treesit)
+(require 'rx)
+
+(declare-function treesit-parser-create "treesit.c")
+(declare-function treesit-induce-sparse-tree "treesit.c")
+(declare-function treesit-node-start "treesit.c")
+(declare-function treesit-node-child-by-field-name "treesit.c")
+
+
+(defcustom json-ts-mode-indent-offset 2
+  "Number of spaces for each indentation step in `json-ts-mode'."
+  :version "29.1"
+  :type 'integer
+  :safe 'integerp
+  :group 'json)
+
+(defvar json-ts-mode--syntax-table
+  (let ((table (make-syntax-table)))
+    ;; Taken from the cc-langs version
+    (modify-syntax-entry ?_  "_"     table)
+    (modify-syntax-entry ?$ "_"      table)
+    (modify-syntax-entry ?\\ "\\"    table)
+    (modify-syntax-entry ?+  "."     table)
+    (modify-syntax-entry ?-  "."     table)
+    (modify-syntax-entry ?=  "."     table)
+    (modify-syntax-entry ?%  "."     table)
+    (modify-syntax-entry ?<  "."     table)
+    (modify-syntax-entry ?>  "."     table)
+    (modify-syntax-entry ?&  "."     table)
+    (modify-syntax-entry ?|  "."     table)
+    (modify-syntax-entry ?` "\""     table)
+    (modify-syntax-entry ?\240 "."   table)
+    table)
+  "Syntax table for `json-ts-mode'.")
+
+
+(defvar json-ts--indent-rules
+  `((json
+     ((node-is "}") parent-bol 0)
+     ((node-is ")") parent-bol 0)
+     ((node-is "]") parent-bol 0)
+     ((parent-is "object") parent-bol json-ts-mode-indent-offset))))
+
+(defvar json-ts-mode--font-lock-settings
+  (treesit-font-lock-rules
+   :language 'json
+   :feature 'comment
+   :override t
+   '((comment) @font-lock-comment-face)
+   :language 'json
+   :feature 'string
+   :override t
+   '((escape_sequence) @font-lock-constant-face
+     (string) @font-lock-string-face)
+   :language 'json
+   :feature 'number
+   :override t
+   '((number) @font-lock-constant-face)
+   :language 'json
+   :feature 'constant
+   :override t
+   '([(null) (true) (false)] @font-lock-constant-face)
+   :language 'json
+   :feature 'pair
+   :override t
+   `((pair key: (_) @font-lock-variable-name-face)))
+  "Font-lock settings for JSON.")
+
+(defun json-ts-mode--imenu-1 (node)
+  "Helper for `json-ts-mode--imenu'.
+Find string representation for NODE and set marker, then recurse
+the subtrees."
+  (let* ((ts-node (car node))
+         (subtrees (mapcan #'json-ts-mode--imenu-1 (cdr node)))
+         (name (when ts-node
+                 (treesit-node-text
+                  (treesit-node-child-by-field-name
+                   ts-node "key")
+                  t)))
+         (marker (when ts-node
+                   (set-marker (make-marker)
+                               (treesit-node-start ts-node)))))
+    (cond
+     ((null ts-node) subtrees)
+     (subtrees
+      `((,name ,(cons name marker) ,@subtrees)))
+     (t
+      `((,name . ,marker))))))
+
+(defun json-ts-mode--imenu ()
+  "Return Imenu alist for the current buffer."
+  (let* ((node (treesit-buffer-root-node))
+         (tree (treesit-induce-sparse-tree
+                node "pair" nil 1000)))
+    (json-ts-mode--imenu-1 tree)))
+
+;;;###autoload
+(define-derived-mode json-ts-mode prog-mode "JSON"
+  "Major mode for editing JSON, powered by tree-sitter."
+  :group 'json
+  :syntax-table json-ts-mode--syntax-table
+
+  (unless (treesit-ready-p 'json)
+    (error "Tree-sitter for JSON isn't available"))
+
+  (treesit-parser-create 'json)
+
+  ;; Comments.
+  (setq-local comment-start "// ")
+  (setq-local comment-start-skip "\\(?://+\\|/\\*+\\)\\s *")
+  (setq-local comment-end "")
+
+  ;; Electric
+  (setq-local electric-indent-chars
+              (append "{}():;," electric-indent-chars))
+
+  ;; Indent.
+  (setq-local treesit-simple-indent-rules json-ts--indent-rules)
+
+  ;; Navigation.
+  (setq-local treesit-defun-type-regexp
+              (rx (or "pair" "object")))
+
+  ;; Font-lock.
+  (setq-local treesit-font-lock-settings json-ts-mode--font-lock-settings)
+  (setq-local treesit-font-lock-feature-list
+              '((comment string number) (constant pair) ()))
+
+  ;; Imenu.
+  (setq-local imenu-create-index-function #'json-ts-mode--imenu)
+  (setq-local which-func-functions nil) ;; Piggyback on imenu
+
+  (treesit-major-mode-setup))
+
+(provide 'json-ts-mode)
+
+;;; json-ts-mode.el ends here
diff --git a/lisp/progmodes/prog-mode.el b/lisp/progmodes/prog-mode.el
index f87230bd2f..58cb48f182 100644
--- a/lisp/progmodes/prog-mode.el
+++ b/lisp/progmodes/prog-mode.el
@@ -155,7 +155,7 @@ which case it will be used to compose the new symbol as per 
the
 third argument of `compose-region'.")
 
 (defun prettify-symbols-default-compose-p (start end _match)
-  "Return non-nil iff the symbol MATCH should be composed.
+  "Return non-nil if the symbol MATCH should be composed.
 The symbol starts at position START and ends at position END.
 This is the default for `prettify-symbols-compose-predicate'
 which is suitable for most programming languages such as C or Lisp."
diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el
index ed26872ae7..63510e9050 100644
--- a/lisp/progmodes/project.el
+++ b/lisp/progmodes/project.el
@@ -712,6 +712,7 @@ DIRS must contain directory names."
     (define-key map "G" 'project-or-external-find-regexp)
     (define-key map "r" 'project-query-replace-regexp)
     (define-key map "x" 'project-execute-extended-command)
+    (define-key map "\C-b" 'project-list-buffers)
     map)
   "Keymap for project commands.")
 
@@ -1222,6 +1223,28 @@ displayed."
   (interactive (list (project--read-project-buffer)))
   (display-buffer-other-frame buffer-or-name))
 
+;;;###autoload
+(defun project-list-buffers (&optional arg)
+  "Display a list of project buffers.
+The list is displayed in a buffer named \"*Buffer List*\".
+
+By default, all project buffers are listed except those whose names
+start with a space (which are for internal use).  With prefix argument
+ARG, show only buffers that are visiting files."
+  (interactive "P")
+  (let ((pr (project-current t)))
+    (display-buffer
+     (if (version< emacs-version "29.0.50")
+         (let ((buf (list-buffers-noselect arg (project-buffers pr))))
+           (with-current-buffer buf
+             (setq-local revert-buffer-function
+                         (lambda (&rest _ignored)
+                           (list-buffers--refresh (project-buffers pr))
+                           (tabulated-list-print t))))
+           buf)
+       (list-buffers-noselect
+        arg nil (lambda (buf) (memq buf (project-buffers pr))))))))
+
 (defcustom project-kill-buffer-conditions
   '(buffer-file-name    ; All file-visiting buffers are included.
     ;; Most of temp and logging buffers (aside from hidden ones):
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index a734e06149..af59b8e146 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -261,15 +261,26 @@
 (require 'ansi-color)
 (require 'cl-lib)
 (require 'comint)
+(eval-when-compile (require 'subr-x))   ;For `string-empty-p' and 
`string-join'.
+(require 'treesit)
+(require 'pcase)
 (require 'compat nil 'noerror)
 (require 'project nil 'noerror)
 (require 'seq)
-(eval-when-compile (require 'subr-x))   ;For `string-empty-p'.
 
 ;; Avoid compiler warnings
 (defvar compilation-error-regexp-alist)
 (defvar outline-heading-end-regexp)
 
+(declare-function treesit-parser-create "treesit.c")
+(declare-function treesit-induce-sparse-tree "treesit.c")
+(declare-function treesit-node-child-by-field-name "treesit.c")
+(declare-function treesit-node-type "treesit.c")
+(declare-function treesit-node-start "treesit.c")
+(declare-function treesit-node-end "treesit.c")
+(declare-function treesit-node-parent "treesit.c")
+
+
 (autoload 'comint-mode "comint")
 (autoload 'help-function-arglist "help-fns")
 
@@ -291,6 +302,7 @@ instead."
   :version "29.1"
   :type 'string)
 
+
 
 ;;; Bindings
 
@@ -393,6 +405,9 @@ instead."
     map)
   "Keymap for `python-mode'.")
 
+(defvar python-ts-mode-map (copy-keymap python-mode-map)
+  "Keymap for `(copy-keymap python-mode-map)'.")
+
 
 ;;; Python specialized rx
 
@@ -941,6 +956,211 @@ is used to limit the scan."
   "Dotty syntax table for Python files.
 It makes underscores and dots word constituent chars.")
 
+;;; Tree-sitter font-lock
+
+;; NOTE: Tree-sitter and font-lock works differently so this can't
+;; merge with `python-font-lock-keywords-level-2'.
+
+(defvar python--treesit-keywords
+  '("as" "assert" "async" "await" "break" "class" "continue" "def"
+    "del" "elif" "else" "except" "exec" "finally" "for" "from"
+    "global" "if" "import" "lambda" "nonlocal" "pass" "print"
+    "raise" "return" "try" "while" "with" "yield"
+    ;; These are technically operators, but we fontify them as
+    ;; keywords.
+    "and" "in" "is" "not" "or"))
+
+(defvar python--treesit-builtins
+  '("abs" "all" "any" "ascii" "bin" "bool" "breakpoint" "bytearray"
+    "bytes" "callable" "chr" "classmethod" "compile" "complex"
+    "delattr" "dict" "dir" "divmod" "enumerate" "eval" "exec"
+    "filter" "float" "format" "frozenset" "getattr" "globals"
+    "hasattr" "hash" "help" "hex" "id" "input" "int" "isinstance"
+    "issubclass" "iter" "len" "list" "locals" "map" "max"
+    "memoryview" "min" "next" "object" "oct" "open" "ord" "pow"
+    "print" "property" "range" "repr" "reversed" "round" "set"
+    "setattr" "slice" "sorted" "staticmethod" "str" "sum" "super"
+    "tuple" "type" "vars" "zip" "__import__"))
+
+(defvar python--treesit-constants
+  '("Ellipsis" "False" "None" "NotImplemented" "True" "__debug__"
+    "copyright" "credits" "exit" "license" "quit"))
+
+(defvar python--treesit-operators
+  '("-" "-=" "!=" "*" "**" "**=" "*=" "/" "//" "//=" "/=" "&" "%" "%="
+    "^" "+" "+=" "<" "<<" "<=" "<>" "=" "==" ">" ">=" ">>" "|" "~"))
+
+(defvar python--treesit-special-attributes
+  '("__annotations__" "__closure__" "__code__"
+    "__defaults__" "__dict__" "__doc__" "__globals__"
+    "__kwdefaults__" "__name__" "__module__" "__package__"
+    "__qualname__" "__all__"))
+
+(defvar python--treesit-exceptions
+  '(;; Python 2 and 3:
+    "ArithmeticError" "AssertionError" "AttributeError" "BaseException"
+    "BufferError" "BytesWarning" "DeprecationWarning" "EOFError"
+    "EnvironmentError" "Exception" "FloatingPointError" "FutureWarning"
+    "GeneratorExit" "IOError" "ImportError" "ImportWarning"
+    "IndentationError" "IndexError" "KeyError" "KeyboardInterrupt"
+    "LookupError" "MemoryError" "NameError" "NotImplementedError"
+    "OSError" "OverflowError" "PendingDeprecationWarning"
+    "ReferenceError" "RuntimeError" "RuntimeWarning" "StopIteration"
+    "SyntaxError" "SyntaxWarning" "SystemError" "SystemExit" "TabError"
+    "TypeError" "UnboundLocalError" "UnicodeDecodeError"
+    "UnicodeEncodeError" "UnicodeError" "UnicodeTranslateError"
+    "UnicodeWarning" "UserWarning" "ValueError" "Warning"
+    "ZeroDivisionError"
+    ;; Python 2:
+    "StandardError"
+    ;; Python 3:
+    "BlockingIOError" "BrokenPipeError" "ChildProcessError"
+    "ConnectionAbortedError" "ConnectionError" "ConnectionRefusedError"
+    "ConnectionResetError" "FileExistsError" "FileNotFoundError"
+    "InterruptedError" "IsADirectoryError" "NotADirectoryError"
+    "PermissionError" "ProcessLookupError" "RecursionError"
+    "ResourceWarning" "StopAsyncIteration" "TimeoutError"
+    ;; OS specific
+    "VMSError" "WindowsError"
+    ))
+
+(defun python--treesit-fontify-string (node override start end &rest _)
+  "Fontify string.
+NODE is the string node.  Do not fontify the initial f for
+f-strings.  OVERRIDE is the override flag described in
+`treesit-font-lock-rules'.  START and END mark the region to be
+fontified."
+  (let* ((string-beg (treesit-node-start node))
+         (string-end (treesit-node-end node))
+         (maybe-expression (treesit-node-parent node))
+         (maybe-defun (treesit-node-parent
+                       (treesit-node-parent
+                        maybe-expression)))
+         (face (if (and (member (treesit-node-type maybe-defun)
+                                '("function_definition"
+                                  "class_definition"))
+                        ;; This check filters out this case:
+                        ;; def function():
+                        ;;     return "some string"
+                        (equal (treesit-node-type maybe-expression)
+                               "expression_statement"))
+                   'font-lock-doc-face
+                 'font-lock-string-face)))
+    (when (eq (char-after string-beg) ?f)
+      (cl-incf string-beg))
+    (treesit-fontify-with-override
+     (max start string-beg) (min end string-end) face override)))
+
+(defvar python--treesit-settings
+  (treesit-font-lock-rules
+   :feature 'comment
+   :language 'python
+   '((comment) @font-lock-comment-face)
+
+   :feature 'string
+   :language 'python
+   :override t
+   '((string) @python--treesit-fontify-string)
+
+   :feature 'string-interpolation
+   :language 'python
+   :override t
+   '((interpolation (identifier) @font-lock-variable-name-face))
+
+   :feature 'definition
+   :language 'python
+   '((function_definition
+      name: (identifier) @font-lock-function-name-face)
+     (class_definition
+      name: (identifier) @font-lock-type-face))
+
+   :feature 'keyword
+   :language 'python
+   `([,@python--treesit-keywords] @font-lock-keyword-face
+     ((identifier) @font-lock-keyword-face
+      (:match "^self$" @font-lock-keyword-face)))
+
+   :feature 'builtin
+   :language 'python
+   `(((identifier) @font-lock-builtin-face
+      (:match ,(rx-to-string
+                `(seq bol
+                      (or ,@python--treesit-builtins
+                          ,@python--treesit-special-attributes)
+                      eol))
+              @font-lock-builtin-face)))
+
+   :feature 'constant
+   :language 'python
+   '([(true) (false) (none)] @font-lock-constant-face)
+
+   :feature 'assignment
+   :language 'python
+   `(;; Variable names and LHS.
+     (assignment left: (identifier)
+                 @font-lock-variable-name-face)
+     (assignment left: (attribute
+                        attribute: (identifier)
+                        @font-lock-variable-name-face))
+     (pattern_list (identifier)
+                   @font-lock-variable-name-face)
+     (tuple_pattern (identifier)
+                    @font-lock-variable-name-face)
+     (list_pattern (identifier)
+                   @font-lock-variable-name-face)
+     (list_splat_pattern (identifier)
+                         @font-lock-variable-name-face))
+
+   :feature 'decorator
+   :language 'python
+   '((decorator) @font-lock-type-face)
+
+   :feature 'type
+   :language 'python
+   `(((identifier) @font-lock-type-face
+      (:match ,(rx-to-string
+                `(seq bol (or ,@python--treesit-exceptions)
+                      eol))
+              @font-lock-type-face))
+     (type (identifier) @font-lock-type-face))
+
+   :feature 'escape-sequence
+   :language 'python
+   :override t
+   '((escape_sequence) @font-lock-escape-face)
+
+   :feature 'number
+   :language 'python
+   :override t
+   '([(integer) (float)] @font-lock-number-face)
+
+   :feature 'property
+   :language 'python
+   :override t
+   '((attribute
+      attribute: (identifier) @font-lock-property-face)
+     (class_definition
+      body: (block
+             (expression_statement
+              (assignment left:
+                          (identifier) @font-lock-property-face)))))
+
+   :feature 'operator
+   :language 'python
+   :override t
+   `([,@python--treesit-operators] @font-lock-operator-face)
+
+   :feature 'bracket
+   :language 'python
+   :override t
+   '(["(" ")" "[" "]" "{" "}"] @font-lock-bracket-face)
+
+   :feature 'delimiter
+   :language 'python
+   :override t
+   '(["," "." ":" ";" (ellipsis)] @font-lock-delimiter-face))
+  "Tree-sitter font-lock settings.")
+
 
 ;;; Indentation
 
@@ -2174,6 +2394,55 @@ position, else returns nil."
       (ignore (goto-char point)))))
 
 
+;;; Tree-sitter navigation
+
+(defun python-treesit-beginning-of-defun (&optional arg)
+  "Tree-sitter `beginning-of-defun' function.
+ARG is the same as in `beginning-of-defun'."
+  (let ((arg (or arg 1))
+        (node (treesit-node-at (point)))
+        (function-or-class (rx (or "function" "class") "_definition")))
+    (if (> arg 0)
+        ;; Go backward.
+        (while (and (> arg 0)
+                    (setq node (treesit-search-forward-goto
+                                node function-or-class t t)))
+          ;; Here we deviate from `treesit-beginning-of-defun': if
+          ;; NODE is function_definition, find the top-level
+          ;; function_definition, if NODE is class_definition, find
+          ;; the top-level class_definition, don't mix the two like
+          ;; `treesit-beginning-of-defun' would.
+          (setq node (or (treesit-node-top-level node)
+                         node))
+          (setq arg (1- arg)))
+      ;; Go forward.
+      (while (and (< arg 0)
+                  (setq node (treesit-search-forward-goto
+                              node function-or-class)))
+        (setq node (or (treesit-node-top-level node)
+                       node))
+        (setq arg (1+ arg))))
+    (when node
+      (goto-char (treesit-node-start node))
+      t)))
+
+(defun python-treesit-end-of-defun ()
+  "Tree-sitter `end-of-defun' function."
+  ;; Why not simply get the largest node at point: when point is at
+  ;; (point-min), that gives us the root node.
+  (let* ((node (treesit-node-at (point)))
+         (top-func (treesit-node-top-level
+                    node
+                    "function_definition"))
+         (top-class (treesit-node-top-level
+                     node
+                     "class_definition")))
+    ;; Prefer function_definition over class_definition: when we are
+    ;; in a function_definition inside a class_definition, jump to the
+    ;; end of function_definition.
+    (goto-char (or (treesit-node-end (or top-func top-class)) (point)))))
+
+
 ;;; Shell integration
 
 (defcustom python-shell-buffer-name "Python"
@@ -5195,6 +5464,92 @@ To this:
               (python-imenu-format-parent-item-jump-label-function fn))
           (python-imenu-create-index))))))
 
+;;; Tree-sitter imenu
+
+(defun python--imenu-treesit-create-index-1 (node)
+  "Given a sparse tree, create an imenu alist.
+
+NODE is the root node of the tree returned by
+`treesit-induce-sparse-tree' (not a tree-sitter node, its car is
+a tree-sitter node).  Walk that tree and return an imenu alist.
+
+Return a list of ENTRY where
+
+ENTRY := (NAME . MARKER)
+       | (NAME . ((JUMP-LABEL . MARKER)
+                  ENTRY
+                  ...)
+
+NAME is the function/class's name, JUMP-LABEL is like \"*function
+definition*\"."
+  (let* ((ts-node (car node))
+         (children (cdr node))
+         (subtrees (mapcan #'python--imenu-treesit-create-index-1
+                           children))
+         (type (pcase (treesit-node-type ts-node)
+                 ("function_definition" 'def)
+                 ("class_definition" 'class)))
+         ;; The root of the tree could have a nil ts-node.
+         (name (when ts-node
+                 (treesit-node-text
+                  (treesit-node-child-by-field-name
+                   ts-node "name") t)))
+         (marker (when ts-node
+                   (set-marker (make-marker)
+                               (treesit-node-start ts-node)))))
+    (cond
+     ((null ts-node)
+      subtrees)
+     (subtrees
+      (let ((parent-label
+             (funcall python-imenu-format-parent-item-label-function
+                      type name))
+            (jump-label
+             (funcall
+              python-imenu-format-parent-item-jump-label-function
+              type name)))
+        `((,parent-label
+           ,(cons jump-label marker)
+           ,@subtrees))))
+     (t (let ((label
+               (funcall python-imenu-format-item-label-function
+                        type name)))
+          (list (cons label marker)))))))
+
+(defun python-imenu-treesit-create-index (&optional node)
+  "Return tree Imenu alist for the current Python buffer.
+
+Change `python-imenu-format-item-label-function',
+`python-imenu-format-parent-item-label-function',
+`python-imenu-format-parent-item-jump-label-function' to
+customize how labels are formatted.
+
+NODE is the root node of the subtree you want to build an index
+of.  If nil, use the root node of the whole parse tree.
+
+Similar to `python-imenu-create-index' but use tree-sitter."
+  (let* ((node (or node (treesit-buffer-root-node 'python)))
+         (tree (treesit-induce-sparse-tree
+                node
+                (rx (seq bol
+                         (or "function" "class")
+                         "_definition"
+                         eol))
+                nil 1000)))
+    (python--imenu-treesit-create-index-1 tree)))
+
+(defun python-imenu-treesit-create-flat-index ()
+  "Return flat outline of the current Python buffer for Imenu.
+
+Change `python-imenu-format-item-label-function',
+`python-imenu-format-parent-item-label-function',
+`python-imenu-format-parent-item-jump-label-function' to
+customize how labels are formatted.
+
+Similar to `python-imenu-create-flat-index' but use
+tree-sitter."
+  (python-imenu-create-flat-index
+   (python-imenu-treesit-create-index)))
 
 ;;; Misc helpers
 
@@ -5260,6 +5615,29 @@ since it returns nil if point is not inside a defun."
              (concat (and type (format "%s " type))
                      (mapconcat #'identity names ".")))))))
 
+(defun python-info-treesit-current-defun (&optional include-type)
+  "Identical to `python-info-current-defun' but use tree-sitter.
+For INCLUDE-TYPE see `python-info-current-defun'."
+  (let ((node (treesit-node-at (point)))
+        (name-list ())
+        (type 'def))
+    (cl-loop while node
+             if (pcase (treesit-node-type node)
+                  ("function_definition"
+                   (setq type 'def))
+                  ("class_definition"
+                   (setq type 'class))
+                  (_ nil))
+             do (push (treesit-node-text
+                       (treesit-node-child-by-field-name node "name")
+                       t)
+                      name-list)
+             do (setq node (treesit-node-parent node))
+             finally return (concat (if include-type
+                                        (format "%s " type)
+                                      "")
+                                    (string-join name-list ".")))))
+
 (defun python-info-current-symbol (&optional replace-self)
   "Return current symbol using dotty syntax.
 With optional argument REPLACE-SELF convert \"self\" to current
@@ -6152,10 +6530,12 @@ Add import for undefined name `%s' (empty to skip): "
 (defvar prettify-symbols-alist)
 
 ;;;###autoload
-(define-derived-mode python-mode prog-mode "Python"
-  "Major mode for editing Python files.
+(define-derived-mode python-base-mode prog-mode "Python"
+  "Generic major mode for editing Python files.
 
-\\{python-mode-map}"
+This is a generic major mode intended to be inherited by
+concrete implementations.  Currently there are two concrete
+implementations: `python-mode' and `python-ts-mode'."
   (setq-local tab-width 8)
   (setq-local indent-tabs-mode nil)
 
@@ -6167,17 +6547,6 @@ Add import for undefined name `%s' (empty to skip): "
 
   (setq-local forward-sexp-function python-forward-sexp-function)
 
-  (setq-local font-lock-defaults
-              `(,python-font-lock-keywords
-                nil nil nil nil
-                (font-lock-syntactic-face-function
-                 . python-font-lock-syntactic-face-function)
-                (font-lock-extend-after-change-region-function
-                 . python-font-lock-extend-region)))
-
-  (setq-local syntax-propertize-function
-              python-syntax-propertize-function)
-
   (setq-local indent-line-function #'python-indent-line-function)
   (setq-local indent-region-function #'python-indent-region)
   ;; Because indentation is not redundant, we cannot safely reindent code.
@@ -6202,14 +6571,9 @@ Add import for undefined name `%s' (empty to skip): "
   (add-hook 'post-self-insert-hook
             #'python-indent-post-self-insert-function 'append 'local)
 
-  (setq-local imenu-create-index-function
-              #'python-imenu-create-index)
-
   (setq-local add-log-current-defun-function
               #'python-info-current-defun)
 
-  (add-hook 'which-func-functions #'python-info-current-defun nil t)
-
   (setq-local skeleton-further-elements
               '((abbrev-mode nil)
                 (< '(backward-delete-char-untabify (min python-indent-offset
@@ -6218,13 +6582,13 @@ Add import for undefined name `%s' (empty to skip): "
 
   (with-no-warnings
     ;; suppress warnings about eldoc-documentation-function being obsolete
-   (if (null eldoc-documentation-function)
-       ;; Emacs<25
-       (setq-local eldoc-documentation-function #'python-eldoc-function)
-     (if (boundp 'eldoc-documentation-functions)
-         (add-hook 'eldoc-documentation-functions #'python-eldoc-function nil 
t)
-       (add-function :before-until (local 'eldoc-documentation-function)
-                     #'python-eldoc-function))))
+    (if (null eldoc-documentation-function)
+        ;; Emacs<25
+        (setq-local eldoc-documentation-function #'python-eldoc-function)
+      (if (boundp 'eldoc-documentation-functions)
+          (add-hook 'eldoc-documentation-functions #'python-eldoc-function nil 
t)
+        (add-function :before-until (local 'eldoc-documentation-function)
+                      #'python-eldoc-function))))
 
   (add-to-list
    'hs-special-modes-alist
@@ -6257,6 +6621,43 @@ Add import for undefined name `%s' (empty to skip): "
 
   (add-hook 'flymake-diagnostic-functions #'python-flymake nil t))
 
+;;;###autoload
+(define-derived-mode python-mode python-base-mode "Python"
+  "Major mode for editing Python files.
+
+\\{python-mode-map}"
+  (setq-local font-lock-defaults
+              `(,python-font-lock-keywords
+                nil nil nil nil
+                (font-lock-syntactic-face-function
+                 . python-font-lock-syntactic-face-function)))
+  (setq-local syntax-propertize-function
+              python-syntax-propertize-function)
+  (setq-local imenu-create-index-function
+              #'python-imenu-create-index)
+  (add-hook 'which-func-functions #'python-info-current-defun nil t))
+
+;;;###autoload
+(define-derived-mode python-ts-mode python-base-mode "Python"
+  "Major mode for editing Python files, using tree-sitter library.
+
+\\{python-ts-mode-map}"
+  (when (treesit-ready-p 'python)
+    (treesit-parser-create 'python)
+    (setq-local treesit-font-lock-feature-list
+                '(( comment string definition)
+                  ( keyword builtin constant type)
+                  ( assignment decorator escape-sequence
+                    string-interpolation number property
+                    operator bracket delimiter)))
+    (setq-local treesit-font-lock-settings python--treesit-settings)
+    (setq-local imenu-create-index-function
+                #'python-imenu-treesit-create-index)
+    (setq-local beginning-of-defun-function
+                #'python-treesit-beginning-of-defun)
+    (setq-local end-of-defun-function #'python-treesit-end-of-defun)
+    (treesit-major-mode-setup)))
+
 ;;; Completion predicates for M-x
 ;; Commands that only make sense when editing Python code
 (dolist (sym '(python-add-import
diff --git a/lisp/progmodes/sh-script.el b/lisp/progmodes/sh-script.el
index 558b62b20a..54f005508c 100644
--- a/lisp/progmodes/sh-script.el
+++ b/lisp/progmodes/sh-script.el
@@ -148,6 +148,7 @@
   (require 'let-alist)
   (require 'subr-x))
 (require 'executable)
+(require 'treesit)
 
 (autoload 'comint-completion-at-point "comint")
 (autoload 'comint-filename-completion "comint")
@@ -522,7 +523,7 @@ This is buffer-local in every such buffer.")
     (rc . "\\<\\([[:alnum:]_*]+\\)[ \t]*=")
     (sh . "\\<\\([[:alnum:]_]+\\)="))
   "Regexp for the variable name and what may follow in an assignment.
-First grouping matches the variable name.  This is upto and including the `='
+First grouping matches the variable name.  This is up to and including the `='
 sign.  See `sh-feature'."
   :type '(repeat (cons (symbol :tag "Shell")
                       (choice regexp
@@ -1465,6 +1466,8 @@ When the region is active, send the region instead."
                       (symbol-name sh-shell)
                     sh-shell))))
 
+(defvar sh-mode--treesit-settings)
+
 ;;;###autoload
 (define-derived-mode sh-mode prog-mode "Shell-script"
   "Major mode for editing shell scripts.
@@ -1534,13 +1537,6 @@ with your script for an edit-interpret-debug cycle."
   ;; we can't look if previous line ended with `\'
   (setq-local comint-prompt-regexp "^[ \t]*")
   (setq-local imenu-case-fold-search nil)
-  (setq font-lock-defaults
-       `((sh-font-lock-keywords
-          sh-font-lock-keywords-1 sh-font-lock-keywords-2)
-         nil nil
-         ((?/ . "w") (?~ . "w") (?. . "w") (?- . "w") (?_ . "w")) nil
-         (font-lock-syntactic-face-function
-          . ,#'sh-font-lock-syntactic-face-function)))
   (setq-local syntax-propertize-function #'sh-syntax-propertize-function)
   (add-hook 'syntax-propertize-extend-region-functions
             #'syntax-propertize-multiline 'append 'local)
@@ -1587,7 +1583,27 @@ with your script for an edit-interpret-debug cycle."
    nil nil)
   (add-hook 'flymake-diagnostic-functions #'sh-shellcheck-flymake nil t)
   (add-hook 'hack-local-variables-hook
-    #'sh-after-hack-local-variables nil t))
+    #'sh-after-hack-local-variables nil t)
+
+  (cond
+   ;; Tree-sitter.  If the shell is bash, we can enable tree-sitter.
+   ((treesit-ready-p sh-shell)
+    (setq-local treesit-font-lock-feature-list
+                '((comment function string heredoc)
+                  (variable keyword command declaration-command)
+                  (constant operator builtin-variable)))
+    (setq-local treesit-font-lock-settings
+                sh-mode--treesit-settings)
+    (treesit-major-mode-setup))
+   ;; Elisp.
+   (t
+    (setq font-lock-defaults
+          `((sh-font-lock-keywords
+             sh-font-lock-keywords-1 sh-font-lock-keywords-2)
+            nil nil
+            ((?/ . "w") (?~ . "w") (?. . "w") (?- . "w") (?_ . "w")) nil
+            (font-lock-syntactic-face-function
+             . ,#'sh-font-lock-syntactic-face-function))))))
 
 ;;;###autoload
 (defalias 'shell-script-mode 'sh-mode)
@@ -3191,6 +3207,117 @@ member of `flymake-diagnostic-functions'."
       (process-send-region sh--shellcheck-process (point-min) (point-max))
       (process-send-eof sh--shellcheck-process))))
 
-(provide 'sh-script)
+;;; Tree-sitter font-lock
+
+(defvar sh-mode--treesit-operators
+  '("|" "|&" "||" "&&" ">" ">>" "<" "<<" "<<-" "<<<" "==" "!=" ";"
+    ";;" ";&" ";;&")
+  "A list of `sh-mode' operators to fontify.")
+
+(defvar sh-mode--treesit-keywords
+  '("case" "do" "done" "elif" "else" "esac" "export" "fi" "for"
+    "function" "if" "in" "unset" "while" "then")
+  "Minimal list of keywords that belong to tree-sitter-bash's grammar.
+
+Some reserved words are not recognize to keep the grammar
+simpler.  Those are identified with regex-based filtered queries.
+
+\(See `sh-mode--treesit-other-keywords' and
+`sh-mode--treesit-settings').")
+
+(defun sh-mode--treesit-other-keywords ()
+  "Return a list `others' of key/reserved words.
+These words are fontified with regex-based queries as they are
+not part of tree-sitter-bash's grammar.
+
+See `sh-mode--treesit-other-keywords' and
+`sh-mode--treesit-settings')."
+  (let ((minimal sh-mode--treesit-keywords)
+        (all (append (sh-feature sh-leading-keywords)
+                     (sh-feature sh-other-keywords)))
+        (others))
+    (dolist (keyword all others)
+      (if (not (member keyword minimal))
+          (setq others (cons keyword others))))))
+
+(defvar sh-mode--treesit-declaration-commands
+  '("declare" "typeset" "export" "readonly" "local")
+  "Keywords in declaration commands.")
+
+(defvar sh-mode--treesit-settings
+  (treesit-font-lock-rules
+   :feature 'comment
+   :language 'bash
+   '((comment) @font-lock-comment-face)
+
+   :feature 'function
+   :language 'bash
+   '((function_definition name: (word) @font-lock-function-name-face))
+
+   :feature 'string
+   :language 'bash
+   '([(string) (raw_string)] @font-lock-string-face)
+
+   :feature 'heredoc
+   :language 'bash
+   '([(heredoc_start) (heredoc_body)] @sh-heredoc)
+
+   :feature 'variable
+   :language 'bash
+   '((variable_name) @font-lock-variable-name-face)
+
+   :feature 'keyword
+   :language 'bash
+   `(;; keywords
+     [ ,@sh-mode--treesit-keywords ] @font-lock-keyword-face
+     ;; reserved words
+     (command_name
+      ((word) @font-lock-keyword-face
+       (:match
+        ,(rx-to-string
+          `(seq bol
+                (or ,@(sh-mode--treesit-other-keywords))
+                eol))
+        @font-lock-keyword-face))))
+
+   :feature 'command
+   :language 'bash
+   `(;; function/non-builtin command calls
+     (command_name (word) @font-lock-function-name-face)
+     ;; builtin commands
+     (command_name
+      ((word) @font-lock-builtin-face
+       (:match ,(let ((builtins
+                       (sh-feature sh-builtins)))
+                  (rx-to-string
+                   `(seq bol
+                         (or ,@builtins)
+                         eol)))
+               @font-lock-builtin-face))))
+
+   :feature 'declaration-command
+   :language 'bash
+   `([,@sh-mode--treesit-declaration-commands] @font-lock-keyword-face)
+
+   :feature 'constant
+   :language 'bash
+   '((case_item value: (word) @font-lock-constant-face)
+     (file_descriptor) @font-lock-constant-face)
+
+   :feature 'operator
+   :language 'bash
+   `([ ,@sh-mode--treesit-operators ] @font-lock-builtin-face)
+
+   :feature 'builtin-variable
+   :language 'bash
+   `(((special_variable_name) @font-lock-builtin-face
+      (:match ,(let ((builtin-vars (sh-feature sh-variables)))
+                 (rx-to-string
+                  `(seq bol
+                        (or ,@builtin-vars)
+                        eol)))
+              @font-lock-builtin-face))))
+  "Tree-sitter font-lock settings for `sh-mode'.")
 
+(provide 'sh-script)
 ;;; sh-script.el ends here
diff --git a/lisp/progmodes/sql.el b/lisp/progmodes/sql.el
index a1c0aa76de..e585799dc6 100644
--- a/lisp/progmodes/sql.el
+++ b/lisp/progmodes/sql.el
@@ -781,7 +781,7 @@ host key."
       ;; Perform search
       (dolist (s (auth-source-search :max 1000))
         (when (and
-               ;; Is PRODUCT specified, in the enty, and they are equal
+               ;; Is PRODUCT specified, in the entry, and they are equal
                (if product
                    (if (plist-member s :product)
                        (equal (plist-get s :product) product)
diff --git a/lisp/progmodes/ts-mode.el b/lisp/progmodes/ts-mode.el
new file mode 100644
index 0000000000..01719a89ee
--- /dev/null
+++ b/lisp/progmodes/ts-mode.el
@@ -0,0 +1,316 @@
+;;; ts-mode.el --- tree sitter support for TypeScript  -*- lexical-binding: t; 
-*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+
+;; Author     : Theodor Thornhill <theo@thornhill.no>
+;; Maintainer : Theodor Thornhill <theo@thornhill.no>
+;; Created    : October 2022
+;; Keywords   : typescript tsx languages tree-sitter
+
+;; This file is part of GNU Emacs.
+
+;; 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 <http://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(require 'treesit)
+(require 'rx)
+(require 'js)
+
+(defcustom ts-mode-indent-offset 2
+  "Number of spaces for each indentation step in `ts-mode'."
+  :version "29.1"
+  :type 'integer
+  :safe 'integerp
+  :group 'typescript)
+
+(defvar ts-mode--syntax-table
+  (let ((table (make-syntax-table)))
+    ;; Taken from the cc-langs version
+    (modify-syntax-entry ?_  "_"     table)
+    (modify-syntax-entry ?$ "_"      table)
+    (modify-syntax-entry ?\\ "\\"    table)
+    (modify-syntax-entry ?+  "."     table)
+    (modify-syntax-entry ?-  "."     table)
+    (modify-syntax-entry ?=  "."     table)
+    (modify-syntax-entry ?%  "."     table)
+    (modify-syntax-entry ?<  "."     table)
+    (modify-syntax-entry ?>  "."     table)
+    (modify-syntax-entry ?&  "."     table)
+    (modify-syntax-entry ?|  "."     table)
+    (modify-syntax-entry ?` "\""     table)
+    (modify-syntax-entry ?\240 "."   table)
+    table)
+  "Syntax table for `ts-mode'.")
+
+(defvar ts-mode--indent-rules
+  `((tsx
+     ((parent-is "program") parent-bol 0)
+     ((node-is "}") parent-bol 0)
+     ((node-is ")") parent-bol 0)
+     ((node-is "]") parent-bol 0)
+     ((node-is ">") parent-bol 0)
+     ((and (parent-is "comment") comment-end) comment-start -1)
+     ((parent-is "comment") comment-start-skip 0)
+     ((parent-is "ternary_expression") parent-bol ts-mode-indent-offset)
+     ((parent-is "member_expression") parent-bol ts-mode-indent-offset)
+     ((parent-is "named_imports") parent-bol ts-mode-indent-offset)
+     ((parent-is "statement_block") parent-bol ts-mode-indent-offset)
+     ((parent-is "type_arguments") parent-bol ts-mode-indent-offset)
+     ((parent-is "variable_declarator") parent-bol ts-mode-indent-offset)
+     ((parent-is "arguments") parent-bol ts-mode-indent-offset)
+     ((parent-is "array") parent-bol ts-mode-indent-offset)
+     ((parent-is "formal_parameters") parent-bol ts-mode-indent-offset)
+     ((parent-is "template_substitution") parent-bol ts-mode-indent-offset)
+     ((parent-is "object_pattern") parent-bol ts-mode-indent-offset)
+     ((parent-is "object") parent-bol ts-mode-indent-offset)
+     ((parent-is "object_type") parent-bol ts-mode-indent-offset)
+     ((parent-is "enum_body") parent-bol ts-mode-indent-offset)
+     ((parent-is "arrow_function") parent-bol ts-mode-indent-offset)
+     ((parent-is "parenthesized_expression") parent-bol ts-mode-indent-offset)
+
+     ;; TSX
+     ((parent-is "jsx_opening_element") parent ts-mode-indent-offset)
+     ((node-is "jsx_closing_element") parent 0)
+     ((parent-is "jsx_element") parent ts-mode-indent-offset)
+     ((node-is "/") parent 0)
+     ((parent-is "jsx_self_closing_element") parent ts-mode-indent-offset)
+     (no-node parent-bol 0)))
+  "Tree-sitter indent rules.")
+
+(defvar ts-mode--keywords
+  '("!" "abstract" "as" "async" "await" "break"
+    "case" "catch" "class" "const" "continue" "debugger"
+    "declare" "default" "delete" "do" "else" "enum"
+    "export" "extends" "finally" "for" "from" "function"
+    "get" "if" "implements" "import" "in" "instanceof" "interface"
+    "keyof" "let" "namespace" "new" "of" "private" "protected"
+    "public" "readonly" "return" "set" "static" "switch"
+    "target" "throw" "try" "type" "typeof" "var" "void"
+    "while" "with" "yield")
+  "TypeScript keywords for tree-sitter font-locking.")
+
+(defvar ts-mode--font-lock-settings
+  (treesit-font-lock-rules
+   :language 'tsx
+   :override t
+   :feature 'comment
+   `((comment) @font-lock-comment-face)
+
+   :language 'tsx
+   :override t
+   :feature 'constant
+   `(((identifier) @font-lock-constant-face
+      (:match "^[A-Z_][A-Z_\\d]*$" @font-lock-constant-face))
+
+     [(true) (false) (null)] @font-lock-constant-face
+     (number) @font-lock-constant-face)
+
+   :language 'tsx
+   :override t
+   :feature 'keyword
+   `([,@ts-mode--keywords] @font-lock-keyword-face
+     [(this) (super)] @font-lock-keyword-face)
+
+   :language 'tsx
+   :override t
+   :feature 'string
+   `((regex pattern: (regex_pattern)) @font-lock-string-face
+     (string) @font-lock-string-face
+     (template_string) @js--fontify-template-string
+     (template_substitution ["${" "}"] @font-lock-builtin-face))
+
+   :language 'tsx
+   :override t
+   :feature 'declaration
+   `((function
+      name: (identifier) @font-lock-function-name-face)
+
+     (function_declaration
+      name: (identifier) @font-lock-function-name-face)
+
+     (method_definition
+      name: (property_identifier) @font-lock-function-name-face)
+
+     (variable_declarator
+      name: (identifier) @font-lock-variable-name-face)
+
+     (enum_declaration (identifier) @font-lock-type-face)
+
+     (arrow_function
+      parameter: (identifier) @font-lock-variable-name-face)
+
+     (variable_declarator
+      name: (identifier) @font-lock-function-name-face
+      value: [(function) (arrow_function)])
+
+     (variable_declarator
+      name: (array_pattern
+             (identifier)
+             (identifier) @font-lock-function-name-face)
+      value: (array (number) (function))))
+
+   :language 'tsx
+   :override t
+   :feature 'identifier
+   `((nested_type_identifier
+      module: (identifier) @font-lock-type-face)
+
+     (type_identifier) @font-lock-type-face
+
+     (predefined_type) @font-lock-type-face
+
+     (new_expression
+      constructor: (identifier) @font-lock-type-face)
+
+     (enum_body (property_identifier) @font-lock-type-face)
+
+     (enum_assignment name: (property_identifier) @font-lock-type-face)
+
+     (assignment_expression
+      left: [(identifier) @font-lock-variable-name-face
+             (member_expression
+              property: (property_identifier) @font-lock-variable-name-face)])
+
+     (for_in_statement
+      left: (identifier) @font-lock-variable-name-face)
+
+     (arrow_function
+      parameters:
+      [(_ (identifier) @font-lock-variable-name-face)
+       (_ (_ (identifier) @font-lock-variable-name-face))
+       (_ (_ (_ (identifier) @font-lock-variable-name-face)))]))
+
+   :language 'tsx
+   :override t
+   :feature 'expression
+   '((assignment_expression
+      left: [(identifier) @font-lock-function-name-face
+             (member_expression
+              property: (property_identifier) @font-lock-function-name-face)]
+      right: [(function) (arrow_function)])
+
+     (call_expression
+      function:
+      [(identifier) @font-lock-function-name-face
+       (member_expression
+        property: (property_identifier) @font-lock-function-name-face)]))
+
+   :language 'tsx
+   :override t
+   :feature 'property
+   `((pair key: (property_identifier) @font-lock-variable-name-face)
+
+     (pair value: (identifier) @font-lock-variable-name-face)
+
+     (pair
+      key: (property_identifier) @font-lock-function-name-face
+      value: [(function) (arrow_function)])
+
+     (property_signature
+      name: (property_identifier) @font-lock-variable-name-face)
+
+     ((shorthand_property_identifier) @font-lock-variable-name-face)
+
+     ((shorthand_property_identifier_pattern)
+      @font-lock-variable-name-face))
+
+   :language 'tsx
+   :override t
+   :feature 'pattern
+   `((pair_pattern
+      key: (property_identifier) @font-lock-variable-name-face)
+
+     (array_pattern (identifier) @font-lock-variable-name-face))
+
+   :language 'tsx
+   :override t
+   :feature 'jsx
+   `((jsx_opening_element
+      [(nested_identifier (identifier)) (identifier)]
+      @font-lock-function-name-face)
+
+     (jsx_closing_element
+      [(nested_identifier (identifier)) (identifier)]
+      @font-lock-function-name-face)
+
+     (jsx_self_closing_element
+      [(nested_identifier (identifier)) (identifier)]
+      @font-lock-function-name-face)
+
+     (jsx_attribute (property_identifier) @font-lock-constant-face)))
+  "Tree-sitter font-lock settings.")
+
+;;;###autoload
+(add-to-list 'auto-mode-alist '("\\.ts\\'" . ts-mode))
+
+;;;###autoload
+(add-to-list 'auto-mode-alist '("\\.tsx\\'" . ts-mode))
+
+;;;###autoload
+(define-derived-mode ts-mode prog-mode "TypeScript"
+  "Major mode for editing TypeScript."
+  :group 'typescript
+  :syntax-table ts-mode--syntax-table
+
+  (cond
+   ;; `ts-mode' requires tree-sitter to work, so we don't check if
+   ;; user enables tree-sitter for it.
+   ((treesit-ready-p 'tsx)
+    ;; Tree-sitter.
+    (treesit-parser-create 'tsx)
+
+    ;; Comments.
+    (setq-local comment-start "// ")
+    (setq-local comment-start-skip "\\(?://+\\|/\\*+\\)\\s *")
+    (setq-local comment-end "")
+    (setq-local treesit-comment-start (rx "/" (or (+ "/") (+ "*"))))
+    (setq-local treesit-comment-end (rx (+ (or "*")) "/"))
+
+    ;; Electric
+    (setq-local electric-indent-chars
+                (append "{}():;," electric-indent-chars))
+
+    ;; Indent.
+    (setq-local treesit-simple-indent-rules ts-mode--indent-rules)
+
+    ;; Navigation.
+    (setq-local treesit-defun-type-regexp
+                (rx (or "class_declaration"
+                        "method_definition"
+                        "function_declaration"
+                        "lexical_declaration")))
+
+    ;; Font-lock.
+    (setq-local treesit-font-lock-settings ts-mode--font-lock-settings)
+    (setq-local treesit-font-lock-feature-list
+                '((comment declaration)
+                  (string keyword identifier expression constant)
+                  (property pattern jsx)))
+    ;; Imenu.
+    (setq-local imenu-create-index-function #'js--treesit-imenu)
+
+    ;; Which-func (use imenu).
+    (setq-local which-func-functions nil)
+
+    (treesit-major-mode-setup))
+
+   ;; Elisp.
+   (t
+    (js-mode)
+    (message "Tree-sitter for TypeScript isn't available, falling back to 
`js-mode'"))))
+
+(provide 'ts-mode)
+
+;;; ts-mode.el ends here
diff --git a/lisp/progmodes/verilog-mode.el b/lisp/progmodes/verilog-mode.el
index 310a9be4f6..e5458e6a07 100644
--- a/lisp/progmodes/verilog-mode.el
+++ b/lisp/progmodes/verilog-mode.el
@@ -7719,7 +7719,7 @@ nil otherwise."
                           (setq match t)
                           (setq elm nil))
                       (setq elm (cdr elm)))))
-                ;; If this is a test just for exact match, return nil ot t
+                ;; If this is a test just for exact match, return nil or t
                 (if (and (equal flag 'lambda) (not (equal match 't)))
                     nil
                   match))))
diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el
index bb36688ef8..89a090ae93 100644
--- a/lisp/progmodes/xref.el
+++ b/lisp/progmodes/xref.el
@@ -1237,16 +1237,21 @@ local keymap that binds `RET' to 
`xref-quit-and-goto-xref'."
          (max-height (/ (window-height) 2))
          (size-fun (lambda (window)
                      (fit-window-to-buffer window max-height)))
+         xref-alist
          buf)
     (cond
      ((not (cdr xrefs))
       (xref-pop-to-location (car xrefs)
                             (assoc-default 'display-action alist)))
      (t
+      ;; Call it here because it can call (project-current), and that
+      ;; might depend on individual buffer, not just directory.
+      (setq xref-alist (xref--analyze xrefs))
+
       (with-current-buffer (get-buffer-create xref-buffer-name)
         (xref--ensure-default-directory dd (current-buffer))
         (xref--transient-buffer-mode)
-        (xref--show-common-initialize (xref--analyze xrefs) fetcher alist)
+        (xref--show-common-initialize xref-alist fetcher alist)
         (pop-to-buffer (current-buffer)
                        `(display-buffer-in-direction . ((direction . below)
                                                         (window-height . 
,size-fun))))
diff --git a/lisp/repeat.el b/lisp/repeat.el
index 0ae68d6024..33e8d98ce3 100644
--- a/lisp/repeat.el
+++ b/lisp/repeat.el
@@ -582,28 +582,57 @@ Used in `repeat-mode'."
                          (push s (alist-get (get s 'repeat-map) keymaps)))))
       (with-help-window (help-buffer)
         (with-current-buffer standard-output
-          (insert "A list of keymaps used by commands with the symbol property 
`repeat-map'.\n\n")
+          (insert "A list of keymaps used by commands with the symbol property 
`repeat-map'.\n")
 
           (dolist (keymap (sort keymaps (lambda (a b)
                                           (when (and (symbolp (car a))
                                                      (symbolp (car b)))
-                                            (string-lessp (car a) (car b))))))
-            (insert (format-message
-                     "`%s' keymap is repeatable by these commands:\n"
-                     (car keymap)))
-            (dolist (command (sort (cdr keymap) #'string-lessp))
-              (let* ((info (help-fns--analyze-function command))
-                     (map (list (if (symbolp (car keymap))
-                                    (symbol-value (car keymap))
-                                  (car keymap))))
-                     (desc (mapconcat (lambda (key)
-                                        (propertize (key-description key)
-                                                    'face 'help-key-binding))
-                                      (or (where-is-internal command map)
-                                          (where-is-internal (nth 3 info) map))
-                                      ", ")))
-                (insert (format-message " `%s' (bound to %s)\n" command 
desc))))
-            (insert "\n")))))))
+                                            (string< (car a) (car b))))))
+            (insert (format-message "\f\n* `%s'\n" (car keymap)))
+            (when (symbolp (car keymap))
+              (insert (substitute-command-keys (format-message "\\{%s}" (car 
keymap)))))
+
+            (let* ((map (if (symbolp (car keymap))
+                            (symbol-value (car keymap))
+                          (car keymap)))
+                   (repeat-commands (cdr keymap))
+                   map-commands commands-enter commands-exit)
+              (map-keymap (lambda (_key cmd)
+                            (when (symbolp cmd) (push cmd map-commands)))
+                          map)
+              (setq map-commands (seq-uniq map-commands))
+              (setq commands-enter (seq-difference repeat-commands 
map-commands))
+              (setq commands-exit  (seq-difference map-commands 
repeat-commands))
+
+              (when (or commands-enter commands-exit)
+                (when commands-enter
+                  (insert "\n** Entered with:\n\n")
+                  (fill-region-as-paragraph
+                   (point)
+                   (progn
+                     (insert (mapconcat (lambda (cmd)
+                                          (format-message "`%s'" cmd))
+                                        (sort commands-enter #'string<)
+                                        ", "))
+                     (point)))
+                  (insert "\n"))
+                (when commands-exit
+                  (insert "\n** Exited with:\n\n")
+                  (fill-region-as-paragraph
+                   (point)
+                   (progn
+                     (insert (mapconcat (lambda (cmd)
+                                          (format-message "`%s'" cmd))
+                                        (sort commands-exit #'string<)
+                                        ", "))
+                     (point)))
+                  (insert "\n")))))
+
+          ;; Hide ^Ls.
+          (goto-char (point-min))
+          (while (search-forward "\n\f\n" nil t)
+           (put-text-property (1+ (match-beginning 0)) (1- (match-end 0))
+                               'invisible t)))))))
 
 (provide 'repeat)
 
diff --git a/lisp/scroll-bar.el b/lisp/scroll-bar.el
index 5786a21e88..dbe2b6241f 100644
--- a/lisp/scroll-bar.el
+++ b/lisp/scroll-bar.el
@@ -390,7 +390,7 @@ EVENT should be a scroll bar click."
        (setq point-before-scroll before-scroll)))))
 
 
-;;; Tookit scroll bars.
+;;; Toolkit scroll bars.
 
 (defun scroll-bar-toolkit-scroll (event)
   "Handle event EVENT on vertical scroll bar."
diff --git a/lisp/server.el b/lisp/server.el
index 90d97c1538..2973b783e6 100644
--- a/lisp/server.el
+++ b/lisp/server.el
@@ -670,7 +670,6 @@ the `server-process' variable."
                             "/tmp/")
                  (ignore-errors
                    (delete-directory (file-name-directory server-file))))))
-         (setq server-mode nil) ;; already set by the minor mode code
          (display-warning
           'server
           (concat "Unable to start the Emacs server.\n"
@@ -688,7 +687,9 @@ server or call `\\[server-force-delete]' to forcibly 
disconnect it."))
       (if leave-dead
          (progn
            (unless (eq t leave-dead) (server-log (message "Server stopped")))
-           (setq server-process nil))
+            (setq server-mode nil
+                  global-minor-modes (delq 'server-mode global-minor-modes)
+                  server-process nil))
        ;; Make sure there is a safe directory in which to place the socket.
        (server-ensure-safe-dir server-dir)
        (when server-process
@@ -716,7 +717,10 @@ server or call `\\[server-force-delete]' to forcibly 
disconnect it."))
                       ;; Those are decoded by server-process-filter according
                       ;; to file-name-coding-system.  Also don't get
                       ;; confused by CRs since we don't quote them.
-                      :coding 'raw-text-unix
+                       ;; For encoding, we must use the locale's encoding,
+                       ;; since emacsclient shows that verbatim on the
+                       ;; console.
+                      :coding (cons 'raw-text-unix locale-coding-system)
                       ;; The other args depend on the kind of socket used.
                       (if server-use-tcp
                           (list :family 'ipv4  ;; We're not ready for IPv6 yet
@@ -728,6 +732,8 @@ server or call `\\[server-force-delete]' to forcibly 
disconnect it."))
                               :plist '(:authenticated t)))))
          (unless server-process (error "Could not start server process"))
          (process-put server-process :server-file server-file)
+          (setq server-mode t)
+          (push 'server-mode global-minor-modes)
          (when server-use-tcp
            (let ((auth-key (server-get-auth-key)))
              (process-put server-process :auth-key auth-key)
@@ -796,6 +802,10 @@ by the current Emacs process, use the `server-process' 
variable."
        t)
     (file-error nil)))
 
+;; This keymap is empty, but allows users to define keybindings to use
+;; when `server-mode' is active.
+(defvar-keymap server-mode-map)
+
 ;;;###autoload
 (define-minor-mode server-mode
   "Toggle Server mode.
@@ -805,6 +815,7 @@ Server mode runs a process that accepts commands from the
 `server-start' for details."
   :global t
   :version "22.1"
+  :keymap server-mode-map
   ;; Fixme: Should this check for an existing server socket and do
   ;; nothing if there is one (for multiple Emacs sessions)?
   (server-start (not server-mode)))
diff --git a/lisp/shell.el b/lisp/shell.el
index 641f274045..7c3c925ab8 100644
--- a/lisp/shell.el
+++ b/lisp/shell.el
@@ -393,6 +393,14 @@ Useful for shells like zsh that has this feature."
       'complete-expand)
     map))
 
+(defvar-keymap shell-repeat-map
+  :doc "Keymap to repeat shell key sequences.  Used in `repeat-mode'."
+  "C-f" #'shell-forward-command
+  "C-b" #'shell-backward-command)
+
+(put #'shell-forward-command 'repeat-map 'shell-repeat-map)
+(put #'shell-backward-command 'repeat-map 'shell-repeat-map)
+
 (defcustom shell-mode-hook '()
   "Hook for customizing Shell mode."
   :type 'hook
diff --git a/lisp/simple.el b/lisp/simple.el
index a53b7b1d0d..0f44b14948 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -2494,9 +2494,10 @@ Also see `suggest-key-bindings'."
 (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)))))
+                  (propertize (cond (shorter (concat "M-x " shorter))
+                                    ((stringp binding) binding)
+                                    (t (key-description binding)))
+                              'face 'help-key-binding)))
 
 (defun execute-extended-command (prefixarg &optional command-name typed)
   "Read a command name, then read the arguments and call the command.
diff --git a/lisp/so-long.el b/lisp/so-long.el
index 75201fefca..661f5ee57a 100644
--- a/lisp/so-long.el
+++ b/lisp/so-long.el
@@ -839,7 +839,7 @@ was established."
     )
   ;; It's not clear to me whether all of these would be problematic, but they
   ;; seemed like reasonable targets.  Some are certainly excessive in smaller
-  ;; buffers of minified code, but we should be aiming to maximise performance
+  ;; buffers of minified code, but we should be aiming to maximize performance
   ;; by default, so that Emacs is as responsive as we can manage in even very
   ;; large buffers of minified code.
   "List of buffer-local minor modes to explicitly disable.
@@ -880,7 +880,7 @@ If `so-long-revert' is subsequently invoked, then the 
variables are restored
 to their original states.
 
 The combination of `line-move-visual' (enabled) and `truncate-lines' (disabled)
-is important for maximising responsiveness when moving vertically within an
+is important for maximizing responsiveness when moving vertically within an
 extremely long line, as otherwise the full length of the line may need to be
 scanned to find the next position.
 
diff --git a/lisp/startup.el b/lisp/startup.el
index 70267fc857..5e0a47d3f8 100644
--- a/lisp/startup.el
+++ b/lisp/startup.el
@@ -1063,19 +1063,30 @@ init-file, or to a default value if loading is not 
possible."
 
             ;; If we loaded a compiled file, set `user-init-file' to
             ;; the source version if that exists.
-            (when (equal (file-name-extension user-init-file)
-                         "elc")
-              (let* ((source (file-name-sans-extension user-init-file))
-                     (alt (concat source ".el")))
-                (setq source (cond ((file-exists-p alt) alt)
-                                   ((file-exists-p source) source)
-                                   (t nil)))
-                (when source
-                  (when (file-newer-than-file-p source user-init-file)
-                    (message "Warning: %s is newer than %s"
-                             source user-init-file)
-                    (sit-for 1))
-                  (setq user-init-file source))))
+            (if (equal (file-name-extension user-init-file) "elc")
+                (let* ((source (file-name-sans-extension user-init-file))
+                       (alt (concat source ".el")))
+                  (setq source (cond ((file-exists-p alt) alt)
+                                     ((file-exists-p source) source)
+                                     (t nil)))
+                  (when source
+                    (when (file-newer-than-file-p source user-init-file)
+                      (message "Warning: %s is newer than %s"
+                               source user-init-file)
+                      (sit-for 1))
+                    (setq user-init-file source)))
+              ;; Else, perhaps the user init file was compiled
+              (when (and (equal (file-name-extension user-init-file) "eln")
+                         ;; The next test is for builds without native
+                         ;; compilation support or builds with unexec.
+                         (boundp 'comp-eln-to-el-h))
+                (if-let (source (gethash (file-name-nondirectory 
user-init-file)
+                                         comp-eln-to-el-h))
+                    ;; source exists or the .eln file would not load
+                    (setq user-init-file source)
+                  (message "Warning: unknown source file for init file %S"
+                           user-init-file)
+                  (sit-for 1))))
 
             (when (and load-defaults
                        (not inhibit-default-init))
diff --git a/lisp/subr.el b/lisp/subr.el
index 6b83196d05..261ec512d8 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -2584,7 +2584,7 @@ Uses the `derived-mode-parent' property of the symbol to 
trace backwards."
 (defun major-mode-restore (&optional avoided-modes)
   "Restore major mode earlier suspended with `major-mode-suspend'.
 If there was no earlier suspended major mode, then fallback to `normal-mode',
-tho trying to avoid AVOIDED-MODES."
+though trying to avoid AVOIDED-MODES."
   (if major-mode--suspended
       (funcall (prog1 major-mode--suspended
                  (kill-local-variable 'major-mode--suspended)))
@@ -6911,7 +6911,7 @@ string will be displayed only if BODY takes longer than 
TIMEOUT seconds.
 If FUNC is a function alias, return the function alias chain.
 
 If the function alias chain contains loops, an error will be
-signalled.  If NOERROR, the non-loop parts of the chain is returned."
+signaled.  If NOERROR, the non-loop parts of the chain is returned."
   (declare (side-effect-free t))
   (let ((chain nil)
         (orig-func func))
@@ -7061,7 +7061,7 @@ CONDITION is either:
 
 (defun match-buffers (condition &optional buffers arg)
   "Return a list of buffers that match CONDITION.
-See `buffer-match' for details on CONDITION.  By default all
+See `buffer-match-p' for details on CONDITION.  By default all
 buffers are checked, this can be restricted by passing an
 optional argument BUFFERS, set to a list of buffers to check.
 ARG is passed to `buffer-match', for predicate conditions in
diff --git a/lisp/term/pgtk-win.el b/lisp/term/pgtk-win.el
index 20f1573916..a398bb9fb2 100644
--- a/lisp/term/pgtk-win.el
+++ b/lisp/term/pgtk-win.el
@@ -184,7 +184,7 @@ DISPLAY is the name of the display Emacs should connect to."
 (defun pgtk-preedit-text (event)
   "An internal function to display preedit text from input method.
 
-EVENT is a `preedit-text-event'."
+EVENT is a `preedit-text' event."
   (interactive "e")
   (when pgtk-preedit-overlay
     (delete-overlay pgtk-preedit-overlay))
diff --git a/lisp/term/w32-win.el b/lisp/term/w32-win.el
index 993f1d4320..34f899ecaa 100644
--- a/lisp/term/w32-win.el
+++ b/lisp/term/w32-win.el
@@ -287,7 +287,10 @@ See the documentation of 
`create-fontset-from-fontset-spec' for the format.")
        '(zlib "zlib1.dll" "libz-1.dll")
        '(lcms2 "liblcms2-2.dll")
        '(json "libjansson-4.dll")
-       '(gccjit "libgccjit-0.dll")))
+       '(gccjit "libgccjit-0.dll")
+       ;; MSYS2 distributes libtree-sitter.dll, without API version
+       ;; number...
+       '(tree-sitter "libtree-sitter.dll" "libtree-sitter-0.dll")))
 
 ;;; multi-tty support
 (defvar w32-initialized nil
diff --git a/lisp/textmodes/css-mode.el b/lisp/textmodes/css-mode.el
index 55dced96b7..4b46e5a1fb 100644
--- a/lisp/textmodes/css-mode.el
+++ b/lisp/textmodes/css-mode.el
@@ -40,7 +40,16 @@
 (require 'sgml-mode)
 (require 'smie)
 (require 'thingatpt)
-(eval-when-compile (require 'subr-x))
+(eval-when-compile (require 'subr-x)
+                   (require 'rx))
+(require 'treesit)
+
+(declare-function treesit-parser-create "treesit.c")
+(declare-function treesit-induce-sparse-tree "treesit.c")
+(declare-function treesit-node-type "treesit.c")
+(declare-function treesit-node-start "treesit.c")
+(declare-function treesit-node-child "treesit.c")
+
 
 (defgroup css nil
   "Cascading Style Sheets (CSS) editing mode."
@@ -1317,6 +1326,98 @@ for determining whether point is within a selector."
      (when (smie-rule-hanging-p)
        css-indent-offset))))
 
+;;; Tree-sitter
+
+(defvar css-ts-mode-map (copy-keymap css-mode-map)
+  "Keymap used in `css-ts-mode'.")
+
+(defvar css--treesit-indent-rules
+  '((css
+     ((node-is "}") parent-bol 0)
+     ((node-is ")") parent-bol 0)
+     ((node-is "]") parent-bol 0)
+
+     ((parent-is "block") parent-bol css-indent-offset)
+     ((parent-is "arguments") parent-bol css-indent-offset)
+     ((match nil "declaration" nil 0 3) parent-bol css-indent-offset)
+     ((match nil "declaration" nil 3) (nth-sibling 2) 0)))
+  "Tree-sitter indentation rules for `css-ts-mode'.")
+
+(defvar css--treesit-settings
+  (treesit-font-lock-rules
+   :feature 'comment
+   :language 'css
+   '((comment) @font-lock-comment-face)
+
+   :feature 'string
+   :language 'css
+   '((string_value) @font-lock-string-face)
+
+   :feature 'variable
+   :language 'css
+   '((plain_value) @font-lock-variable-name-face)
+
+   :feature 'selector
+   :language 'css
+   '((class_selector) @css-selector
+     (child_selector) @css-selector
+     (id_selector) @css-selector
+     (tag_name) @css-selector
+     (class_name) @css-selector)
+
+   :feature 'property
+   :language 'css
+   `((property_name) @css-property)
+
+   :feature 'function
+   :language 'css
+   '((function_name) @font-lock-function-name-face)
+
+   :feature 'constant
+   :language 'css
+   '((integer_value) @font-lock-number-face
+     (float_value) @font-lock-number-face
+     (unit) @font-lock-constant-face)
+
+   :feature 'error
+   :language 'css
+   '((ERROR) @error))
+  "Tree-sitter font-lock settings for `css-ts-mode'.")
+
+(defun css--treesit-imenu-1 (node)
+  "Helper for `css--treesit-imenu'.
+Find string representation for NODE and set marker, then recurse
+the subtrees."
+  (let* ((ts-node (car node))
+         (subtrees (mapcan #'css--treesit-imenu-1 (cdr node)))
+         (name (when ts-node
+                 (pcase (treesit-node-type ts-node)
+                   ("rule_set" (treesit-node-text
+                                (treesit-node-child ts-node 0) t))
+                   ("media_statement"
+                    (let ((block (treesit-node-child ts-node -1)))
+                      (string-trim
+                       (buffer-substring-no-properties
+                        (treesit-node-start ts-node)
+                        (treesit-node-start block))))))))
+         (marker (when ts-node
+                   (set-marker (make-marker)
+                               (treesit-node-start ts-node)))))
+    (cond
+     ((or (null ts-node) (null name)) subtrees)
+     (subtrees
+      `((,name ,(cons name marker) ,@subtrees)))
+     (t
+      `((,name . ,marker))))))
+
+(defun css--treesit-imenu ()
+  "Return Imenu alist for the current buffer."
+  (let* ((node (treesit-buffer-root-node))
+         (tree (treesit-induce-sparse-tree
+                node (rx (or "rule_set" "media_statement"))
+                nil 1000)))
+    (css--treesit-imenu-1 tree)))
+
 ;;; Completion
 
 (defun css--complete-property ()
@@ -1655,8 +1756,72 @@ rgb()/rgba()."
               (replace-regexp-in-string "[\n ]+" " " s)))
            res)))))))
 
+(define-derived-mode css-base-mode prog-mode "CSS"
+  "Generic mode to edit Cascading Style Sheets (CSS).
+
+This is a generic major mode intended to be inherited by a
+concrete implementation.  Currently there are two concrete
+implementations: `css-mode' and `css-ts-mode'."
+  (setq-local comment-start "/*")
+  (setq-local comment-start-skip "/\\*+[ \t]*")
+  (setq-local comment-end "*/")
+  (setq-local comment-end-skip "[ \t]*\\*+/")
+  (setq-local electric-indent-chars
+              (append css-electric-keys electric-indent-chars))
+  ;; The default "." creates ambiguity with class selectors.
+  (setq-local imenu-space-replacement " "))
+
+;;;###autoload
+(define-derived-mode css-ts-mode css-base-mode "CSS"
+  "Major mode to edit Cascading Style Sheets (CSS).
+\\<css-ts-mode-map>
+
+This mode provides syntax highlighting, indentation, completion,
+and documentation lookup for CSS, based on the tree-sitter
+library.
+
+Use `\\[completion-at-point]' to complete CSS properties,
+property values, pseudo-elements, pseudo-classes, at-rules,
+bang-rules, and HTML tags, classes and IDs.  Completion
+candidates for HTML class names and IDs are found by looking
+through open HTML mode buffers.
+
+Use `\\[info-lookup-symbol]' to look up documentation of CSS
+properties, at-rules, pseudo-classes, and pseudo-elements on the
+Mozilla Developer Network (MDN).
+
+Use `\\[fill-paragraph]' to reformat CSS declaration blocks.  It
+can also be used to fill comments.
+
+\\{css-mode-map}"
+  (when (treesit-ready-p 'css)
+    ;; Borrowed from `css-native-mode'.
+    (add-hook 'completion-at-point-functions
+              #'css-completion-at-point nil 'local)
+    (setq-local fill-paragraph-function #'css-fill-paragraph)
+    (setq-local adaptive-fill-function #'css-adaptive-fill)
+    (setq-local add-log-current-defun-function #'css-current-defun-name)
+
+    ;; Tree-sitter specific setup.
+    (treesit-parser-create 'css)
+    (setq-local treesit-simple-indent-rules css--treesit-indent-rules)
+    (setq-local treesit-defun-type-regexp "rule_set")
+    (setq-local treesit-font-lock-settings css--treesit-settings)
+    (setq-local treesit-font-lock-feature-list
+                '((selector comment)
+                  (property constant string)
+                  (error variable function)))
+    ;; Tree-sitter-css, for whatever reason, cannot reliably return
+    ;; the captured nodes in a given range (it instead returns the
+    ;; nodes preceding range).  Before this is fixed in
+    ;; tree-sitter-css, use this heuristic as a temporary fix.
+    (setq-local treesit--font-lock-query-expand-range (cons 80 80))
+    (setq-local imenu-create-index-function #'css--treesit-imenu)
+    (setq-local which-func-functions nil)
+    (treesit-major-mode-setup)))
+
 ;;;###autoload
-(define-derived-mode css-mode prog-mode "CSS"
+(define-derived-mode css-mode css-base-mode "CSS"
   "Major mode to edit Cascading Style Sheets (CSS).
 \\<css-mode-map>
 This mode provides syntax highlighting, indentation, completion,
@@ -1677,10 +1842,6 @@ be used to fill comments.
 
 \\{css-mode-map}"
   (setq-local font-lock-defaults css-font-lock-defaults)
-  (setq-local comment-start "/*")
-  (setq-local comment-start-skip "/\\*+[ \t]*")
-  (setq-local comment-end "*/")
-  (setq-local comment-end-skip "[ \t]*\\*+/")
   (setq-local syntax-propertize-function
               css-syntax-propertize-function)
   (setq-local fill-paragraph-function #'css-fill-paragraph)
@@ -1689,13 +1850,9 @@ be used to fill comments.
   (smie-setup css-smie-grammar #'css-smie-rules
               :forward-token #'css-smie--forward-token
               :backward-token #'css-smie--backward-token)
-  (setq-local electric-indent-chars
-              (append css-electric-keys electric-indent-chars))
   (setq-local font-lock-fontify-region-function #'css--fontify-region)
   (add-hook 'completion-at-point-functions
             #'css-completion-at-point nil 'local)
-  ;; The default "." creates ambiguity with class selectors.
-  (setq-local imenu-space-replacement " ")
   (setq-local imenu-prev-index-position-function
               #'css--prev-index-position)
   (setq-local imenu-extract-index-name-function
diff --git a/lisp/textmodes/reftex-cite.el b/lisp/textmodes/reftex-cite.el
index f3f95627af..41a5b52f99 100644
--- a/lisp/textmodes/reftex-cite.el
+++ b/lisp/textmodes/reftex-cite.el
@@ -96,7 +96,7 @@ Find the bof of the current file."
   "Return list of bibfiles for current document.
 When using the chapterbib or bibunits package you should either
 use the same database files everywhere, or separate parts using
-different databases into different files (included into the mater file).
+different databases into different files (included into the master file).
 Then this function will return the applicable database files."
 
   ;; Ensure access to scanning info
diff --git a/lisp/textmodes/reftex-index.el b/lisp/textmodes/reftex-index.el
index 075ad666b3..38d6ebe7e6 100644
--- a/lisp/textmodes/reftex-index.el
+++ b/lisp/textmodes/reftex-index.el
@@ -1807,7 +1807,7 @@ With optional arg ALLOW-NEWLINE, allow single newline 
between words."
 (defun reftex-index-phrases-find-dup-re (phrase &optional sub)
   "Return a regexp which matches variations of PHRASE (with additional space).
 When SUB ins non-nil, the regexp will also match when PHRASE is a subphrase
-of another phrase.  The regexp works lonly in the phrase buffer."
+of another phrase.  The regexp works only in the phrase buffer."
   (concat (if sub "^\\S-?\t\\([^\t\n]*" "^\\S-?\t")
           (mapconcat #'regexp-quote (split-string phrase) " +")
           (if sub "[^\t\n]*\\)\\([\t\n]\\|$\\)" " *\\([\t\n]\\|$\\)")))
diff --git a/lisp/textmodes/tex-mode.el b/lisp/textmodes/tex-mode.el
index ca0312d8fb..a1914a8cc8 100644
--- a/lisp/textmodes/tex-mode.el
+++ b/lisp/textmodes/tex-mode.el
@@ -2238,7 +2238,7 @@ of the current buffer."
               "&")))
 
 (defun tex-uptodate-p (file)
-  "Return non-nil if FILE is not uptodate w.r.t the document source files.
+  "Return non-nil if FILE is not up-to-date w.r.t the document source files.
 FILE is typically the output DVI or PDF file."
   ;; We should check all the files included !!!
   (and
@@ -2375,7 +2375,7 @@ Only applies the FSPEC to the args part of FORMAT."
          (push cmd tmp)))
       ;; Only remove if there's something left.
       (if tmp (setq cmds (nreverse tmp))))
-    ;; Remove commands whose input is not uptodate either.
+    ;; Remove commands whose input is not up-to-date either.
     (let ((outs (delq nil (mapcar (lambda (x) (nth 2 x)) cmds)))
          (tmp nil))
       (dolist (cmd cmds)
diff --git a/lisp/treesit.el b/lisp/treesit.el
new file mode 100644
index 0000000000..0dcd16d89a
--- /dev/null
+++ b/lisp/treesit.el
@@ -0,0 +1,2259 @@
+;;; treesit.el --- tree-sitter utilities -*- lexical-binding: t -*-
+
+;; Copyright (C) 2021-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:
+
+;; This file is the Lisp counterpart of treesit.c.  Together they
+;; provide tree-sitter integration for Emacs.  This file contains
+;; convenient functions that are more idiomatic and flexible than the
+;; exposed C API of tree-sitter.  It also contains frameworks for
+;; integrating tree-sitter with font-lock, indentation, activating and
+;; deactivating tree-sitter, debugging tree-sitter, etc.
+
+;;; Code:
+
+(eval-when-compile (require 'cl-lib))
+(eval-when-compile (require 'subr-x)) ; For `string-join'.
+(require 'cl-seq)
+(require 'font-lock)
+
+;;; Function declarations
+
+(declare-function treesit-language-available-p "treesit.c")
+(declare-function treesit-language-version "treesit.c")
+
+(declare-function treesit-parser-p "treesit.c")
+(declare-function treesit-node-p "treesit.c")
+(declare-function treesit-compiled-query-p "treesit.c")
+(declare-function treesit-query-p "treesit.c")
+(declare-function treesit-query-language "treesit.c")
+
+(declare-function treesit-node-parser "treesit.c")
+
+(declare-function treesit-parser-create "treesit.c")
+(declare-function treesit-parser-delete "treesit.c")
+(declare-function treesit-parser-list "treesit.c")
+(declare-function treesit-parser-buffer "treesit.c")
+(declare-function treesit-parser-language "treesit.c")
+
+(declare-function treesit-parser-root-node "treesit.c")
+
+(declare-function treesit-parser-set-included-ranges "treesit.c")
+(declare-function treesit-parser-included-ranges "treesit.c")
+(declare-function treesit-parser-add-notifier "treesit.c")
+
+(declare-function treesit-node-type "treesit.c")
+(declare-function treesit-node-start "treesit.c")
+(declare-function treesit-node-end "treesit.c")
+(declare-function treesit-node-string "treesit.c")
+(declare-function treesit-node-parent "treesit.c")
+(declare-function treesit-node-child "treesit.c")
+(declare-function treesit-node-check "treesit.c")
+(declare-function treesit-node-field-name-for-child "treesit.c")
+(declare-function treesit-node-child-count "treesit.c")
+(declare-function treesit-node-child-by-field-name "treesit.c")
+(declare-function treesit-node-next-sibling "treesit.c")
+(declare-function treesit-node-prev-sibling "treesit.c")
+(declare-function treesit-node-first-child-for-pos "treesit.c")
+(declare-function treesit-node-descendant-for-range "treesit.c")
+(declare-function treesit-node-eq "treesit.c")
+
+(declare-function treesit-pattern-expand "treesit.c")
+(declare-function treesit-query-expand "treesit.c")
+(declare-function treesit-query-compile "treesit.c")
+(declare-function treesit-query-capture "treesit.c")
+
+(declare-function treesit-search-subtree "treesit.c")
+(declare-function treesit-search-forward "treesit.c")
+(declare-function treesit-induce-sparse-tree "treesit.c")
+
+(declare-function treesit-available-p "treesit.c")
+
+;;; Custom options
+
+;; Tree-sitter always appear as treesit in symbols.
+(defgroup treesit nil
+  "Incremental parser.
+It is used to enhance major mode features like font-lock,
+indent, imenu, etc."
+  :group 'tools
+  :version "29.1")
+
+(defcustom treesit-max-buffer-size (* 4 1024 1024)
+  "Maximum buffer size for enabling tree-sitter parsing (in bytes)."
+  :type 'integer
+  :version "29.1")
+
+;;; Parser API supplement
+
+(defun treesit-parse-string (string language)
+  "Parse STRING using a parser for LANGUAGE.
+Return the root node of the syntax tree."
+  (with-temp-buffer
+    (insert string)
+    (treesit-parser-root-node
+     (treesit-parser-create language))))
+
+(defvar-local treesit-language-at-point-function nil
+  "A function that returns the language at point.
+This is used by `treesit-language-at', which is used by various
+functions to determine which parser to use at point.
+
+The function is called with one argument, the position of point.")
+
+(defun treesit-language-at (position)
+  "Return the language at POSITION.
+This function assumes that parser ranges are up-to-date.  It
+returns the return value of `treesit-language-at-point-function'
+if it's non-nil, otherwise it returns the language of the first
+parser in `treesit-parser-list', or nil if there is no parser."
+  (if treesit-language-at-point-function
+      (funcall treesit-language-at-point-function position)
+    (when-let ((parser (car (treesit-parser-list))))
+      (treesit-parser-language parser))))
+
+;;; Node API supplement
+
+(defun treesit-node-buffer (node)
+  "Return the buffer in which NODE belongs."
+  (treesit-parser-buffer
+   (treesit-node-parser node)))
+
+(defun treesit-node-language (node)
+  "Return the language symbol that NODE's parser uses."
+  (treesit-parser-language
+   (treesit-node-parser node)))
+
+(defun treesit-node-at (pos &optional parser-or-lang named)
+  "Return the leaf node at position POS.
+
+A leaf node is a node that doesn't have any child nodes.
+
+The returned node's span covers POS: the node's beginning is before
+or at POS, and the node's end is at or after POS.
+
+If no leaf node's span covers POS (e.g., POS is on whitespace
+between two leaf nodes), return the first leaf node after POS.
+
+If there is no leaf node after POS, return the first leaf node
+before POS.
+
+Return nil if no leaf node can be returned.  If NAMED is non-nil,
+only look for named nodes.
+
+If PARSER-OR-LANG is nil, use the first parser in
+`treesit-parser-list'; if PARSER-OR-LANG is a parser, use
+that parser; if PARSER-OR-LANG is a language, find a parser using
+that language in the current buffer, and use that."
+  (let* ((root (if (treesit-parser-p parser-or-lang)
+                   (treesit-parser-root-node parser-or-lang)
+                 (treesit-buffer-root-node parser-or-lang)))
+         (node root)
+         (node-before root)
+         (pos-1 (max (1- pos) (point-min)))
+         next)
+    (when node
+      ;; This is very fast so no need for C implementation.
+      (while (setq next (treesit-node-first-child-for-pos
+                         node pos named))
+        (setq node next))
+      ;; If POS is at the end of buffer, after all the text, we will
+      ;; end up with NODE = root node.  Instead of returning nil,
+      ;; return the last leaf node in the tree for convenience.
+      (if (treesit-node-eq node root)
+          (progn
+            (while (setq next (treesit-node-child node -1 named))
+              (setq node next))
+            node)
+        ;; Normal case, where we found a node.
+        (if (<= (treesit-node-start node) pos)
+            node
+          ;; So the node we found is completely after POS, try to find
+          ;; a node whose end equals to POS.
+          (while (setq next (treesit-node-first-child-for-pos
+                             node-before pos-1 named))
+            (setq node-before next))
+          (if (eq (treesit-node-end node-before) pos)
+              node-before
+            node))))))
+
+(defun treesit-node-on (beg end &optional parser-or-lang named)
+  "Return the smallest node covering BEG to END.
+
+BEWARE!  Calling this function on an empty line that is not
+inside any top-level construct (function definition, etc.) most
+probably will give you the root node, because the root node is
+the smallest node that covers that empty line.  You probably want
+to use `treesit-node-at' instead.
+
+Return nil if none was found.  If NAMED is non-nil, only look for
+named node.
+
+If PARSER-OR-LANG is nil, use the first parser in
+`treesit-parser-list'; if PARSER-OR-LANG is a parser, use
+that parser; if PARSER-OR-LANG is a language, find a parser using
+that language in the current buffer, and use that."
+  (let ((root (if (treesit-parser-p parser-or-lang)
+                  (treesit-parser-root-node parser-or-lang)
+                (treesit-buffer-root-node parser-or-lang))))
+    (treesit-node-descendant-for-range root beg (or end beg) named)))
+
+(defun treesit-node-top-level (node &optional type)
+  "Return the top-level equivalent of NODE.
+Specifically, return the highest parent of NODE that has the same
+type as it.  If no such parent exists, return nil.
+
+If TYPE is non-nil, match each parent's type with TYPE as a
+regexp, rather than using NODE's type."
+  (let ((type (or type (treesit-node-type node)))
+        (result nil))
+    (cl-loop for cursor = (treesit-node-parent node)
+             then (treesit-node-parent cursor)
+             while cursor
+             if (string-match-p type (treesit-node-type cursor))
+             do (setq result cursor))
+    result))
+
+(defun treesit-buffer-root-node (&optional language)
+  "Return the root node of the current buffer.
+Use the first parser in `treesit-parser-list'.
+
+If optional argument LANGUAGE is non-nil, use the first parser
+for LANGUAGE."
+  (if-let ((parser
+            (or (if language
+                    (treesit-parser-create language)
+                  (or (car (treesit-parser-list))
+                      (signal 'treesit-error
+                              '("Buffer has no parser")))))))
+      (treesit-parser-root-node parser)))
+
+(defun treesit-filter-child (node pred &optional named)
+  "Return children of NODE that satisfies predicate PRED.
+PRED is a function that takes one argument, the child node.
+If optional argument NAMED is non-nil, only search for named
+node."
+  (let ((child (treesit-node-child node 0 named))
+        result)
+    (while child
+      (when (funcall pred child)
+        (push child result))
+      (setq child (treesit-node-next-sibling child named)))
+    (reverse result)))
+
+(defun treesit-node-text (node &optional no-property)
+  "Return the buffer (or string) content corresponding to NODE.
+If optional argument NO-PROPERTY is non-nil, remove text
+properties."
+  (when node
+    (with-current-buffer (treesit-node-buffer node)
+      (if no-property
+          (buffer-substring-no-properties
+           (treesit-node-start node)
+           (treesit-node-end node))
+        (buffer-substring
+         (treesit-node-start node)
+         (treesit-node-end node))))))
+
+(defun treesit-parent-until (node pred)
+  "Return the closest parent of NODE that satisfies PRED.
+Return nil if none was found.  PRED should be a function that
+takes one argument, the parent node."
+  (let ((node (treesit-node-parent node)))
+    (while (and node (not (funcall pred node)))
+      (setq node (treesit-node-parent node)))
+    node))
+
+(defun treesit-parent-while (node pred)
+  "Return the furthest parent of NODE that satisfies PRED.
+Return nil if none was found.  PRED should be a function that
+takes one argument, the parent node."
+  (let ((last nil))
+    (while (and node (funcall pred node))
+      (setq last node
+            node (treesit-node-parent node)))
+    last))
+
+(defalias 'treesit-traverse-parent #'treesit-parent-until)
+
+(defun treesit-node-children (node &optional named)
+  "Return a list of NODE's children.
+If NAMED is non-nil, collect named child only."
+  (mapcar (lambda (idx)
+            (treesit-node-child node idx named))
+          (number-sequence
+           0 (1- (treesit-node-child-count node named)))))
+
+(defun treesit-node-index (node &optional named)
+  "Return the index of NODE in its parent.
+If NAMED is non-nil, count named child only."
+  (let ((count 0))
+    (while (setq node (treesit-node-prev-sibling node named))
+      (cl-incf count))
+    count))
+
+(defun treesit-node-field-name (node)
+  "Return the field name of NODE as a child of its parent."
+  (when-let ((parent (treesit-node-parent node))
+             (idx (treesit-node-index node)))
+    (treesit-node-field-name-for-child parent idx)))
+
+;;; Query API supplement
+
+(defun treesit-query-string (string query language)
+  "Query STRING with QUERY in LANGUAGE.
+See `treesit-query-capture' for QUERY."
+  (with-temp-buffer
+    (insert string)
+    (let ((parser (treesit-parser-create language)))
+      (treesit-query-capture
+       (treesit-parser-root-node parser)
+       query))))
+
+(defun treesit-query-range (node query &optional beg end)
+  "Query the current buffer and return ranges of captured nodes.
+
+QUERY, NODE, BEG, END are the same as in
+`treesit-query-capture'.  This function returns a list
+of (START . END), where START and END specifics the range of each
+captured node.  Capture names don't matter."
+  (cl-loop for capture
+           in (treesit-query-capture node query beg end)
+           for node = (cdr capture)
+           collect (cons (treesit-node-start node)
+                         (treesit-node-end node))))
+
+;;; Range API supplement
+
+(defvar-local treesit-range-settings nil
+  "A list of range settings.
+
+Each element of the list is of the form (QUERY LANGUAGE).
+When updating the range of each parser in the buffer,
+`treesit-update-ranges' queries each QUERY, and sets LANGUAGE's
+range to the range spanned by captured nodes.  QUERY must be a
+compiled query.
+
+QUERY can also be a function, in which case it is called with 2
+arguments, START and END.  It should ensure parsers' ranges are
+correct in the region between START and END.
+
+The exact form of each setting is considered internal and subject
+to change.  Use `treesit-range-rules' to set this variable.")
+
+(defun treesit-range-rules (&rest query-specs)
+  "Produce settings for `treesit-range-settings'.
+
+QUERY-SPECS are a series of QUERY-SPECs, where each QUERY-SPEC is
+a QUERY preceded by zero or more pairs of :KEYWORD and VALUE,
+like this:
+
+    :KEYWORD VALUE... QUERY
+
+Each QUERY is a tree-sitter query in either the string,
+s-expression or compiled form.
+
+For each QUERY, :KEYWORD and VALUE pairs add meta information to
+it.  For example,
+
+    (treesit-range-rules
+     :embed \\='javascript
+     :host \\='html
+     \\='((script_element (raw_text) @cap)))
+
+The `:embed' keyword specifies the embedded language, and the
+`:host' keyword specifies the host language.  They are used in
+this way: Emacs queries QUERY in the host language's parser,
+computes the ranges spanned by the captured nodes, and applies
+these ranges to parsers for the embedded language.
+
+QUERY can also be a function that takes two arguments, START and
+END.  If QUERY is a function, it doesn't need the :KEYWORD VALUE
+pair preceding it.  This function should set the ranges for
+parsers in the current buffer in the region between START and
+END.  It is OK for this function to set ranges in a larger region
+that encompasses the region between START and END."
+  (let (host embed result)
+    (while query-specs
+      (pcase (pop query-specs)
+        (:host (let ((host-lang (pop query-specs)))
+                 (unless (symbolp host-lang)
+                   (signal 'treesit-error (list "Value of :host option should 
be a symbol" host-lang)))
+                 (setq host host-lang)))
+        (:embed (let ((embed-lang (pop query-specs)))
+                  (unless (symbolp embed-lang)
+                    (signal 'treesit-error (list "Value of :embed option 
should be a symbol" embed-lang)))
+                  (setq embed embed-lang)))
+        (query (if (functionp query)
+                   (push (list query nil nil) result)
+                 (when (null embed)
+                   (signal 'treesit-error (list "Value of :embed option cannot 
be omitted")))
+                 (when (null host)
+                   (signal 'treesit-error (list "Value of :host option cannot 
be omitted")))
+                 (push (list (treesit-query-compile host query)
+                             embed host)
+                       result))
+               (setq host nil embed nil))))
+    (nreverse result)))
+
+(defun treesit--merge-ranges (old-ranges new-ranges start end)
+  "Merge OLD-RANGES and NEW-RANGES, discarding ranges between START and END.
+OLD-RANGES and NEW-RANGES are lists of cons of the form (BEG . END).
+When merging the two ranges, if a range in OLD-RANGES intersects with
+another range in NEW-RANGES, discard the one in OLD-RANGES and
+keep the one in NEW-RANGES.  Also discard any range in OLD-RANGES
+that intersects the region marked by START and END.
+
+Return the merged list of ranges."
+  (let ((result nil))
+    (while (and old-ranges new-ranges)
+      (let ((new-beg (caar new-ranges))
+            (new-end (cdar new-ranges))
+            (old-beg (caar old-ranges))
+            (old-end (cdar old-ranges)))
+        (cond
+         ;; Old range intersects with START-END, discard.
+         ((and (< start old-end)
+               (< old-beg end))
+          (setq old-ranges (cdr old-ranges)))
+         ;; New range and old range don't intersect, new comes
+         ;; before, push new.
+         ((<= new-end old-beg)
+          (push (car new-ranges) result)
+          (setq new-ranges (cdr new-ranges)))
+         ;; New range and old range don't intersect, old comes
+         ;; before, push old.
+         ((<= old-end new-beg)
+          (push (car old-ranges) result)
+          (setq old-ranges (cdr old-ranges)))
+         (t ;; New and old range intersect, discard old.
+          (setq old-ranges (cdr old-ranges))))))
+    (let ((left-over (or new-ranges old-ranges)))
+      (dolist (range left-over)
+        (push range result)))
+    (nreverse result)))
+
+(defun treesit-update-ranges (&optional beg end)
+  "Update the ranges for each language in the current buffer.
+If BEG and END are non-nil, only update parser ranges in that
+region."
+  ;; When updating ranges, we want to avoid querying the whole buffer
+  ;; which could be slow in very large buffers.  Instead, we only
+  ;; query for nodes that intersect with the region between BEG and
+  ;; END.  Also, we only update the ranges intersecting BEG and END;
+  ;; outside of that region we inherit old ranges.
+  (dolist (setting treesit-range-settings)
+    (let ((query (nth 0 setting))
+          (language (nth 1 setting))
+          (beg (or beg (point-min)))
+          (end (or end (point-max))))
+      (if (functionp query) (funcall query beg end)
+        (let* ((host-lang (treesit-query-language query))
+               (parser (treesit-parser-create language))
+               (old-ranges (treesit-parser-included-ranges parser))
+               (new-ranges (treesit-query-range
+                            host-lang query beg end))
+               (set-ranges (treesit--merge-ranges
+                            old-ranges new-ranges beg end)))
+          (dolist (parser (treesit-parser-list))
+            (when (eq (treesit-parser-language parser)
+                      language)
+              (treesit-parser-set-included-ranges
+               parser set-ranges))))))))
+
+(defun treesit-parser-range-on (parser beg &optional end)
+  "Check if PARSER's range covers the portion between BEG and END.
+
+If it does, return the range covering that portion in the form
+of (RANGE-BEG . RANGE-END), if not, return nil.  If nil or
+omitted, default END to BEG."
+  (let ((ranges (treesit-parser-included-ranges parser))
+        (end (or end beg)))
+    (if (null ranges)
+        (cons (point-min) (point-max))
+      (cl-loop for rng in ranges
+               if (<= (car rng) beg end (cdr rng))
+               return rng
+               finally return nil))))
+
+;;; Fontification
+
+(define-error 'treesit-font-lock-error
+              "Generic tree-sitter font-lock error"
+              'treesit-error)
+
+(defvar-local treesit--font-lock-query-expand-range (cons 0 0)
+  "The amount to expand the start and end of the region when fontifying.
+This should be a cons cell (START . END).  When fontifying a
+buffer, Emacs will move the start of the query range backward by
+START amount, and the end of the query range by END amount.  Both
+START and END should be positive integers or 0.  This doesn't
+affect the fontified range.
+
+Sometimes, querying on some parser with a restricted range
+returns nodes not in that range but before it, which breaks
+fontification.  Major modes can adjust this variable as a
+temporarily fix.")
+
+(defvar-local treesit-font-lock-feature-list nil
+  "A list of lists of feature symbols.
+
+Use `treesit-font-lock-recompute-features' and
+`font-lock-maximum-decoration' to configure enabled features.
+
+Each sublist represents a decoration level.
+`font-lock-maximum-decoration' controls which levels are
+activated.
+
+Inside each sublist are feature symbols, which correspond to the
+:feature value of a query defined in `treesit-font-lock-rules'.
+Removing a feature symbol from this list disables the
+corresponding query during font-lock.
+
+Common feature names (for general programming languages) include
+definition, type, assignment, builtin, constant, keyword,
+string-interpolation, comment, doc, string, operator, property,
+preprocessor, escape-sequence, key (in key-value pairs).  Major
+modes are free to subdivide or extend on these common features.
+See the manual for more explanations on some of the features.
+
+For changes to this variable to take effect, run
+`treesit-font-lock-recompute-features'.")
+
+(defvar-local treesit-font-lock-settings nil
+  "A list of SETTINGs for treesit-based fontification.
+
+The exact format of each SETTING is considered internal.  Use
+`treesit-font-lock-rules' to set this variable.
+
+Each SETTING has the form:
+
+    (QUERY ENABLE FEATURE OVERRIDE)
+
+QUERY must be a compiled query.  See Info node `(elisp)Pattern
+Matching' for how to write a query and compile it.
+
+For SETTING to be activated for font-lock, ENABLE must be t.  To
+disable this SETTING, set ENABLE to nil.
+
+FEATURE is the \"feature name\" of the query.  Users can control
+which features are enabled with `font-lock-maximum-decoration'
+and `treesit-font-lock-feature-list'.
+
+OVERRIDE is the override flag for this query.  Its value can be
+t, nil, append, prepend, keep.  See more in
+`treesit-font-lock-rules'.")
+
+(defun treesit-font-lock-rules (&rest query-specs)
+  "Return a value suitable for `treesit-font-lock-settings'.
+
+QUERY-SPECS is a series of QUERY-SPECs.  Each QUERY-SPEC is a
+QUERY preceded by multiple pairs of :KEYWORD and VALUE:
+
+   :KEYWORD VALUE... QUERY
+
+QUERY is a tree-sitter query in either the string, s-expression
+or compiled form.  For each query, captured nodes are highlighted
+with the capture name as its face.
+
+:KEYWORD and VALUE pairs preceding a QUERY add meta information
+to QUERY.  For example,
+
+    (treesit-font-lock-rules
+     :language \\='javascript
+     :override t
+     :feature\\='constant
+     \\='((true) @font-lock-constant-face
+       (false) @font-lock-constant-face)
+     :language \\='html
+     :feature \\='script
+     \"(script_element) @font-lock-builtin-face\")
+
+For each QUERY, a :language keyword and a :feature keyword are
+required.  Each query's :feature is a symbol summarizing what the
+query fontifies.  It is used to allow users to enable/disable
+certain features.  See `treesit-font-lock-kind-list' for more.
+Other keywords include:
+
+  KEYWORD    VALUE      DESCRIPTION
+  :override  nil        If the region already has a face,
+                        discard the new face.
+             t          Always apply the new face.
+             `append'   Append the new face to existing ones.
+             `prepend'  Prepend the new face to existing ones.
+             `keep'     Fill-in regions without an existing face.
+
+Capture names in QUERY should be face names like
+`font-lock-keyword-face'.  The captured node will be fontified
+with that face.
+
+Capture names can also be function names, in which case the
+function will be called with the following argument list:
+
+    (NODE OVERRIDE START END &rest _)
+
+where NODE is the tree-sitter node object, OVERRIDE is the
+override option of that rule, and START and END specify the region
+to be fontified.  This function should accept more arguments as
+optional arguments for future extensibility, and it shouldn't
+fontify text outside the region given by START and END.
+
+If a capture name is both a face and a function, the face takes
+priority.  If a capture name is not a face name nor a function
+name, it is ignored."
+  ;; Other tree-sitter function don't tend to be called unless
+  ;; tree-sitter is enabled, which means tree-sitter must be compiled.
+  ;; But this function is usually call in `defvar' which runs
+  ;; regardless whether tree-sitter is enabled.  So we need this
+  ;; guard.
+  (when (treesit-available-p)
+    (let (;; Tracks the current :language/:override/:toggle/:level value
+          ;; that following queries will apply to.
+          current-language current-override
+          current-feature
+          ;; The list this function returns.
+          (result nil))
+      (while query-specs
+        (let ((token (pop query-specs)))
+          (pcase token
+            ;; (1) Process keywords.
+            (:language
+             (let ((lang (pop query-specs)))
+               (when (or (not (symbolp lang)) (null lang))
+                 (signal 'treesit-font-lock-error
+                         `("Value of :language should be a symbol"
+                           ,lang)))
+               (setq current-language lang)))
+            (:override
+             (let ((flag (pop query-specs)))
+               (when (not (memq flag '(t nil append prepend keep)))
+                 (signal 'treesit-font-lock-error
+                         `("Value of :override should be one of t, nil, 
append, prepend, keep"
+                           ,flag))
+                 (signal 'wrong-type-argument
+                         `((or t nil append prepend keep)
+                           ,flag)))
+               (setq current-override flag)))
+            (:feature
+             (let ((var (pop query-specs)))
+               (when (or (not (symbolp var))
+                         (memq var '(t nil)))
+                 (signal 'treesit-font-lock-error
+                         `("Value of :feature should be a symbol"
+                           ,var)))
+               (setq current-feature var)))
+            ;; (2) Process query.
+            ((pred treesit-query-p)
+             (when (null current-language)
+               (signal 'treesit-font-lock-error
+                       `("Language unspecified, use :language keyword to 
specify a language for this query" ,token)))
+             (when (null current-feature)
+               (signal 'treesit-font-lock-error
+                       `("Feature unspecified, use :feature keyword to specify 
the feature name for this query" ,token)))
+             (if (treesit-compiled-query-p token)
+                 (push `(,current-language token) result)
+               (push `(,(treesit-query-compile current-language token)
+                       t
+                       ,current-feature
+                       ,current-override)
+                     result))
+             ;; Clears any configurations set for this query.
+             (setq current-language nil
+                   current-override nil
+                   current-feature nil))
+            (_ (signal 'treesit-font-lock-error
+                       `("Unexpected value" ,token))))))
+      (nreverse result))))
+
+;; `font-lock-fontify-region-function' has the LOUDLY argument, but
+;; `jit-lock-functions' doesn't pass that argument.  So even if we set
+;; `font-lock-verbose' to t, if jit-lock is enabled (and it's almost
+;; always is), we don't get debug messages.  So we add our own.
+(defvar treesit--font-lock-verbose nil
+  "If non-nil, print debug messages when fontifying.")
+
+(defun treesit-font-lock-recompute-features (&optional add-list remove-list)
+  "Enable/disable font-lock settings according to decoration level.
+
+First compute the enabled features according to
+`treesit-font-lock-feature-list' and `font-lock-maximum-decoration',
+then, if ADD-LIST or REMOVE-LIST are not omitted, further add and
+remove features accordingly.
+
+ADD-LIST and REMOVE-LIST are each a list of feature symbols.  The
+same feature symbol cannot appear in both lists; the function
+signals the `treesit-font-lock-error' error if so."
+  (when-let ((intersection (cl-intersection add-list remove-list)))
+    (signal 'treesit-font-lock-error
+            (list "ADD-LIST and REMOVE-LIST contain the same feature"
+                  intersection)))
+  (let* ((level (font-lock-value-in-major-mode
+                 font-lock-maximum-decoration))
+         (base-features (cl-loop
+                         for idx = 0 then (1+ idx)
+                         for features in treesit-font-lock-feature-list
+                         if (or (eq level t)
+                                (>= level (1+ idx)))
+                         append features))
+         (features (cl-set-difference (cl-union base-features add-list)
+                                      remove-list)))
+    (cl-loop for idx = 0 then (1+ idx)
+             for setting in treesit-font-lock-settings
+             for feature = (nth 2 setting)
+             ;; Set the ENABLE flag for the setting.
+             do (setf (nth 1 (nth idx treesit-font-lock-settings))
+                      (if (memq feature features) t nil)))))
+
+(defun treesit-fontify-with-override (start end face override)
+  "Apply FACE to the region between START and END.
+OVERRIDE can be nil, t, `append', `prepend', or `keep'.
+See `treesit-font-lock-rules' for their semantic."
+  (pcase override
+    ('nil (unless (text-property-not-all
+                   start end 'face nil)
+            (put-text-property start end 'face face)))
+    ('t (put-text-property start end 'face face))
+    ('append (font-lock-append-text-property
+              start end 'face face))
+    ('prepend (font-lock-prepend-text-property
+               start end 'face face))
+    ('keep (font-lock-fillin-text-property
+            start end 'face face))
+    (_ (signal 'treesit-font-lock-error
+               (list
+                "Unrecognized value of :override option"
+                override)))))
+
+(defun treesit--set-nonsticky (start end sym &optional remove)
+  "Set `rear-nonsticky' property between START and END.
+Set the property to a list containing SYM.  If there is already a
+list, add SYM to that list.  If REMOVE is non-nil, remove SYM
+instead."
+  (let* ((prop (get-text-property start 'rear-nonsticky))
+         (new-prop
+          (pcase prop
+            ((pred listp) ; PROP is a list or nil.
+             (if remove
+                 (remove sym prop)
+               ;; We should make sure PORP doesn't contain SYM, but
+               ;; whatever.
+               (cons sym prop)))
+            ;; PROP is t.
+            (_ (if remove
+                   nil
+                 (list sym))))))
+    (if (null new-prop)
+        (remove-text-properties start end '(rear-nonsticky nil))
+      (put-text-property start end 'rear-nonsticky new-prop))))
+
+(defun treesit--children-covering-range (node start end)
+  "Return a list of children of NODE covering a range.
+The range is between START and END."
+  (let* ((child (treesit-node-first-child-for-pos node start))
+         (result (list child)))
+    (while (and (< (treesit-node-end child) end)
+                (setq child (treesit-node-next-sibling child)))
+      (push child result))
+    (nreverse result)))
+
+(defun treesit--children-covering-range-recurse (node start end threshold)
+  "Return a list of children of NODE covering a range.
+Recursively go down the parse tree and collect children, until
+all nodes in the returned list are smaller than THRESHOLD.  The
+range is between START and END."
+  (let* ((child (treesit-node-first-child-for-pos node start))
+         result)
+    (while (and child (<= (treesit-node-start child) end))
+      ;; If child still too large, recurse down.  Otherwise collect
+      ;; child.
+      (if (> (- (treesit-node-end child)
+                (treesit-node-start child))
+             threshold)
+          (dolist (r (treesit--children-covering-range-recurse
+                      child start end threshold))
+            (push r result))
+        (push child result))
+      (setq child (treesit-node-next-sibling child)))
+    ;; If NODE has no child, keep NODE.
+    (or result node)))
+
+(defsubst treesit--node-length (node)
+  "Return the length of the text of NODE."
+  (- (treesit-node-end node) (treesit-node-start node)))
+
+(defvar-local treesit--font-lock-fast-mode nil
+  "If this variable is t, change the way we query so it's faster.
+This is not a general optimization and should be RARELY needed!
+See comments in `treesit-font-lock-fontify-region' for more
+detail.")
+
+;; Some details worth explaining:
+;;
+;; 1. When we apply face to a node, we clip the face into the
+;; currently fontifying region, this way we don't overwrite faces
+;; applied by regexp-based font-lock.  The clipped part will be
+;; fontified fine when Emacs fontifies the region containing it.
+;;
+(defun treesit-font-lock-fontify-region (start end &optional loudly)
+  "Fontify the region between START and END.
+If LOUDLY is non-nil, display some debugging information."
+  (when (or loudly treesit--font-lock-verbose)
+    (message "Fontifying region: %s-%s" start end))
+  (treesit-update-ranges start end)
+  (font-lock-unfontify-region start end)
+  (dolist (setting treesit-font-lock-settings)
+    (let* ((query (nth 0 setting))
+           (enable (nth 1 setting))
+           (override (nth 3 setting))
+           (language (treesit-query-language query)))
+      ;; If you insert an ending quote into a buffer, jit-lock only
+      ;; wants to fontify that single quote, and (treesit-node-on
+      ;; start end) will give you that quote node.  We want to capture
+      ;; the string and apply string face to it, but querying on the
+      ;; quote node will not give us the string node.  So we don't use
+      ;; treesit-node-on: using the root node with a restricted range
+      ;; is very fast anyway (even in large files of size ~10MB).
+      ;; Plus, querying the result of `treesit-node-on' could still
+      ;; miss patterns even if we use some heuristic to enlarge the
+      ;; node (how much to enlarge? to which extent?), it's much safer
+      ;; to just use the root node.
+      ;;
+      ;; Sometimes the source file has some errors that cause
+      ;; tree-sitter to parse it into a enormously tall tree (10k
+      ;; levels tall).  In that case querying the root node is very
+      ;; slow.  So we try to get top-level nodes and query them.  This
+      ;; ensures that querying is fast everywhere else, except for the
+      ;; problematic region.
+      (when-let ((nodes (list (treesit-buffer-root-node language)))
+                 ;; Only activate if ENABLE flag is t.
+                 (activate (eq t enable)))
+        (ignore activate)
+
+        ;; If we run into problematic files, use the "fast mode" to
+        ;; try to recover.
+        (when treesit--font-lock-fast-mode
+          (setq nodes (treesit--children-covering-range
+                       (car nodes) start end)))
+
+        (dolist (sub-node nodes)
+          (let* ((delta-start (car treesit--font-lock-query-expand-range))
+                 (delta-end (cdr treesit--font-lock-query-expand-range))
+                 (start-time (current-time))
+                 (captures (treesit-query-capture
+                            sub-node query
+                            (max (- start delta-start) (point-min))
+                            (min (+ end delta-end) (point-max))))
+                 (end-time (current-time))
+                 (inhibit-point-motion-hooks t))
+            ;; If for any query the query time is strangely long,
+            ;; switch to fast mode (see comments above).
+            (when (> (time-to-seconds (time-subtract end-time start-time))
+                     0.01)
+              (setq-local treesit--font-lock-fast-mode t))
+            (with-silent-modifications
+              (dolist (capture captures)
+                (let* ((face (car capture))
+                       (node (cdr capture))
+                       (node-start (treesit-node-start node))
+                       (node-end (treesit-node-end node)))
+                  ;; Turns out it is possible to capture a node that's
+                  ;; completely outside the region between START and
+                  ;; END.  If the node is outside of that region, (max
+                  ;; node-start start) and friends return bad values.
+                  (when (and (< start node-end)
+                             (< node-start end))
+                    (cond
+                     ((facep face)
+                      (treesit-fontify-with-override
+                       (max node-start start) (min node-end end)
+                       face override))
+                     ((functionp face)
+                      (funcall face node override start end)))
+                    ;; Don't raise an error if FACE is neither a face nor
+                    ;; a function.  This is to allow intermediate capture
+                    ;; names used for #match and #eq.
+                    (when (or loudly treesit--font-lock-verbose)
+                      (message "Fontifying text from %d to %d, Face: %s, Node: 
%s"
+                               (max node-start start) (min node-end end)
+                               face (treesit-node-type node))))))))))))
+  `(jit-lock-bounds ,start . ,end))
+
+(defun treesit--font-lock-notifier (ranges parser)
+  "Ensures updated parts of the parse-tree are refontified.
+RANGES is a list of (BEG . END) ranges, PARSER is the tree-sitter
+parser notifying of the change."
+  (with-current-buffer (treesit-parser-buffer parser)
+    (dolist (range ranges)
+      (when treesit--font-lock-verbose
+        (message "Notifier received range: %s-%s"
+                 (car range) (cdr range)))
+      (put-text-property (car range) (cdr range) 'fontified nil))))
+
+;;; Indent
+
+;; `comment-start' and `comment-end' assume there is only one type of
+;; comment, and that the comment spans only one line.  So they are not
+;; sufficient for our purpose.
+
+(defvar-local treesit-comment-start nil
+  "Regular expression matching an opening comment token.")
+
+(defvar-local treesit-comment-end nil
+  "Regular expression matching a closing comment token.")
+
+(define-error 'treesit-indent-error
+              "Generic tree-sitter indentation error"
+              'treesit-error)
+
+(defvar treesit--indent-verbose nil
+  "If non-nil, log progress when indenting.")
+
+(defvar-local treesit-simple-indent-rules nil
+  "A list of indent rule settings.
+Each indent rule setting should be (LANGUAGE . RULES),
+where LANGUAGE is a language symbol, and RULES is a list of
+
+    (MATCHER ANCHOR OFFSET).
+
+MATCHER determines whether this rule applies, ANCHOR and OFFSET
+together determines which column to indent to.
+
+A MATCHER is a function that takes three arguments (NODE PARENT
+BOL).  BOL is the point where we are indenting: the beginning of
+line content, the position of the first non-whitespace character.
+NODE is the largest (highest-in-tree) node starting at that
+point.  PARENT is the parent of NODE.
+
+If MATCHER returns non-nil, meaning the rule matches, Emacs then
+uses ANCHOR to find an anchor, it should be a function that takes
+the same argument (NODE PARENT BOL) and returns a point.
+
+Finally Emacs computes the column of that point returned by
+ANCHOR and adds OFFSET to it, and indents to that column.  OFFSET
+can be an integer or a variable whose value is an integer.
+
+For MATCHER and ANCHOR, Emacs provides some convenient presets.
+See `treesit-simple-indent-presets'.")
+
+(defvar treesit-simple-indent-presets
+  (list (cons 'match
+              (lambda
+                (&optional node-type parent-type node-field
+                           node-index-min node-index-max)
+                (lambda (node parent &rest _)
+                  (and (or (null node-type)
+                           (string-match-p
+                            node-type (or (treesit-node-type node) "")))
+                       (or (null parent-type)
+                           (string-match-p
+                            parent-type (treesit-node-type parent)))
+                       (or (null node-field)
+                           (string-match-p
+                            node-field
+                            (or (treesit-node-field-name node) "")))
+                       (or (null node-index-min)
+                           (>= (treesit-node-index node)
+                               node-index-min))
+                       (or (null node-index-max)
+                           (<= (treesit-node-index node)
+                               node-index-max))))))
+        ;; TODO: Document if genuinely useful.
+        (cons 'n-p-gp
+              (lambda (node-t parent-t grand-parent-t)
+                (lambda (node parent &rest _)
+                  (and (or (null node-t)
+                           (string-match-p
+                            node-t (or (treesit-node-type node) "")))
+                       (or (null parent-t)
+                           (string-match-p
+                            parent-t (treesit-node-type parent)))
+                       (or (null grand-parent-t)
+                           (string-match-p
+                            grand-parent-t
+                            (treesit-node-type
+                             (treesit-node-parent parent))))))))
+        (cons 'no-node (lambda (node &rest _) (null node)))
+        (cons 'parent-is (lambda (type)
+                           (lambda (_n parent &rest _)
+                             (string-match-p
+                              type (treesit-node-type parent)))))
+
+        (cons 'node-is (lambda (type)
+                         (lambda (node &rest _)
+                           (string-match-p
+                            type (or (treesit-node-type node) "")))))
+        (cons 'field-is (lambda (name)
+                          (lambda (node &rest _)
+                            (string-match-p
+                             name (or (treesit-node-field-name node) "")))))
+        (cons 'comment-end (lambda (&rest _)
+                             (looking-at-p treesit-comment-end)))
+        ;; TODO: Document.
+        (cons 'catch-all (lambda (&rest _) t))
+
+        (cons 'query (lambda (pattern)
+                       (lambda (node parent &rest _)
+                         (cl-loop for capture
+                                  in (treesit-query-capture
+                                      parent pattern)
+                                  if (treesit-node-eq node (cdr capture))
+                                  return t
+                                  finally return nil))))
+        (cons 'first-sibling (lambda (_n parent &rest _)
+                               (treesit-node-start
+                                (treesit-node-child parent 0))))
+        ;; TODO: Document.
+        (cons 'nth-sibling (lambda (n &optional named)
+                             (lambda (_n parent &rest _)
+                               (treesit-node-start
+                                (treesit-node-child parent n named)))))
+        (cons 'parent (lambda (_n parent &rest _)
+                        (treesit-node-start parent)))
+        (cons 'comment-start
+              (lambda (_n parent &rest _)
+                (save-excursion
+                  (goto-char (treesit-node-start parent))
+                  (re-search-forward treesit-comment-start)
+                  (point))))
+        (cons 'comment-start-skip
+              (lambda (_n parent &rest _)
+                (save-excursion
+                  (goto-char (treesit-node-start parent))
+                  (re-search-forward treesit-comment-start)
+                  (skip-syntax-forward "-")
+                  (point))))
+        ;; TODO: Document.
+        (cons 'grand-parent
+              (lambda (_n parent &rest _)
+                (treesit-node-start (treesit-node-parent parent))))
+        (cons 'parent-bol (lambda (_n parent &rest _)
+                            (save-excursion
+                              (goto-char (treesit-node-start parent))
+                              (back-to-indentation)
+                              (point))))
+        (cons 'prev-sibling (lambda (node &rest _)
+                              (treesit-node-start
+                               (treesit-node-prev-sibling node))))
+        (cons 'no-indent (lambda (_n _p bol &rest _) bol))
+        (cons 'prev-line (lambda (_n _p bol &rest _)
+                           (save-excursion
+                             (goto-char bol)
+                             (forward-line -1)
+                             (skip-chars-forward " \t"))))
+        (cons 'point-min (lambda (&rest _) (point-min)))
+        ;; TODO: Document.
+        (cons 'and (lambda (&rest fns)
+                     (lambda (node parent bol &rest _)
+                       (cl-reduce (lambda (a b) (and a b))
+                                  (mapcar (lambda (fn)
+                                            (funcall fn node parent bol))
+                                          fns)))))
+        (cons 'or (lambda (&rest fns)
+                    (lambda (node parent bol &rest _)
+                      (cl-reduce (lambda (a b) (or a b))
+                                 (mapcar (lambda (fn)
+                                           (funcall fn node parent bol))
+                                         fns)))))
+        (cons 'not (lambda (fn)
+                     (lambda (node parent bol &rest _)
+                       (debug)
+                       (not (funcall fn node parent bol)))))
+        (cons 'list (lambda (&rest fns)
+                      (lambda (node parent bol &rest _)
+                        (mapcar (lambda (fn)
+                                  (funcall fn node parent bol))
+                                fns)))))
+  "A list of presets.
+These presets that can be used as MATHER and ANCHOR in
+`treesit-simple-indent-rules'.  MACHTERs and ANCHORs are
+functions that take 3 arguments: NODE, PARENT and BOL.
+
+MATCHER:
+
+\(match NODE-TYPE PARENT-TYPE NODE-FIELD NODE-INDEX-MIN NODE-INDEX-MAX)
+
+    NODE-TYPE checks for NODE's type, PARENT-TYPE checks for
+    PARENT's type, NODE-FIELD checks for the field name of NODE
+    in PARENT, NODE-INDEX-MIN and NODE-INDEX-MAX check for
+    NODE's index in PARENT.  Therefore, to match the first child
+    where PARENT is \"argument_list\", use
+
+        (match nil \"argument_list\" nil nil 0 0).
+
+    NODE-TYPE, PARENT-TYPE, and NODE-FIELD are regexps.
+
+no-node
+
+    Matches the case where NODE is nil, i.e., there is no node
+    that starts at point.  This is the case when indenting an
+    empty line.
+
+\(parent-is TYPE)
+
+    Check that PARENT's type matches regexp TYPE.
+
+\(node-is TYPE)
+
+    Checks that NODE's type matches regexp TYPE.
+
+\(query QUERY)
+
+    Queries PARENT with QUERY, and checks if NODE is
+    captured (by any capture name).
+
+comment-end
+
+    Matches if text after point matches `treesit-comment-end'.
+
+ANCHOR:
+
+first-sibling
+
+    Returns the start of the first child of PARENT.
+
+parent
+
+    Returns the start of PARENT.
+
+parent-bol
+
+    Returns the beginning of non-space characters on the line where
+    PARENT is on.
+
+prev-sibling
+
+    Returns the start of NODE's previous sibling.
+
+no-indent
+
+    Returns the start of NODE.
+
+prev-line
+
+    Returns the first non-whitespace character on the previous line.
+
+point-min
+
+    Returns the beginning of buffer, which is always at column 0.
+
+comment-start
+
+    Returns the position after a match for `treesit-comment-start'.
+    Assumes PARENT is a comment node.
+
+comment-start-skip
+
+    Goes to the position that comment-start would return, skips
+    whitespace after that, and returns the resulting position.
+    Assumes PARENT is a comment node.")
+
+(defun treesit--simple-indent-eval (exp)
+  "Evaluate EXP.
+
+If EXP is an application and the function is a key in
+`treesit-simple-indent-presets', use the corresponding value as
+the function."
+  ;; We don't want to match uncompiled lambdas, so make sure this cons
+  ;; is not a function.  We could move the condition functionp
+  ;; forward, but better be explicit.
+  (cond ((and (consp exp) (not (functionp exp)))
+         (apply (treesit--simple-indent-eval (car exp))
+                (mapcar #'treesit--simple-indent-eval
+                        (cdr exp))))
+        ;; Presets override functions, so this condition comes before
+        ;; `functionp'.
+        ((alist-get exp treesit-simple-indent-presets)
+         (alist-get exp treesit-simple-indent-presets))
+        ((functionp exp) exp)
+        ((symbolp exp)
+         (if (null exp)
+             exp
+           ;; Matchers only return lambdas, anchors only return
+           ;; integer, so we should never see a variable.
+           (signal 'treesit-indent-error
+                   (list "Couldn't find the preset corresponding to expression"
+                         exp))))
+        (t exp)))
+
+;; This variable might seem unnecessary: why split
+;; `treesit-indent' and `treesit-simple-indent' into two
+;; functions?  We add this variable in between because later we might
+;; add more powerful indentation engines, and that new engine can
+;; probably share `treesit-indent'.  It is also useful, suggested
+;; by Stefan M, to have a function that figures out how much to indent
+;; but doesn't actually performs the indentation, because we might
+;; want to know where will a node indent to if we put it at some other
+;; location, and use that information to calculate the actual
+;; indentation.  And `treesit-simple-indent' is that function.  I
+;; forgot the example Stefan gave, but it makes a lot of sense.
+(defvar treesit-indent-function #'treesit-simple-indent
+  "Function used by `treesit-indent' to do some of the work.
+
+This function is called with
+
+    (NODE PARENT BOL &rest _)
+
+and returns
+
+    (ANCHOR . OFFSET).
+
+BOL is the position of the beginning of the line; NODE is the
+\"largest\" node that starts at BOL; PARENT is its parent; ANCHOR
+is a point (not a node), and OFFSET is a number.  Emacs finds the
+column of ANCHOR and adds OFFSET to it as the final indentation
+of the current line.")
+
+(defun treesit--indent-1 ()
+  "Indent the current line.
+Return (ANCHOR . OFFSET).  This function is used by
+`treesit-indent' and `treesit-indent-region'."
+  ;; Basically holds the common part between the two indent function.
+  (let* ((bol (save-excursion
+                (forward-line 0)
+                (skip-chars-forward " \t")
+                (point)))
+         (smallest-node
+          (cond ((null (treesit-parser-list)) nil)
+                ((eq 1 (length (treesit-parser-list)))
+                 (treesit-node-at bol))
+                ((treesit-language-at (point))
+                 (treesit-node-at bol (treesit-language-at (point))))
+                (t (treesit-node-at bol))))
+         (node (treesit-parent-while
+                smallest-node
+                (lambda (node)
+                  (eq bol (treesit-node-start node))))))
+    (let*
+        ((parser (if smallest-node
+                     (treesit-node-parser smallest-node)
+                   nil))
+         ;; NODE would be nil if BOL is on a whitespace.  In that case
+         ;; we set PARENT to the "node at point", which would
+         ;; encompass the whitespace.
+         (parent (cond ((and node parser)
+                        (treesit-node-parent node))
+                       (t (treesit-node-on bol bol)))))
+      (funcall treesit-indent-function node parent bol))))
+
+(defun treesit-indent ()
+  "Indent according to the result of `treesit-indent-function'."
+  (treesit-update-ranges (line-beginning-position)
+                         (line-end-position))
+  ;; We don't return 'noindent even if no rules match, because
+  ;; `indent-for-tab-command' tries to indent itself when we return
+  ;; 'noindent, which leads to wrong indentation at times.
+  (pcase-let* ((`(,anchor . ,offset) (treesit--indent-1)))
+    (when (and anchor offset)
+      (let ((col (+ (save-excursion
+                      (goto-char anchor)
+                      (current-column))
+                    offset))
+            (delta (- (point-max) (point))))
+        (indent-line-to col)
+        ;; Now point is at the end of indentation.  If we started
+        ;; from within the line, go back to where we started.
+        (when (> (- (point-max) delta) (point))
+          (goto-char (- (point-max) delta)))))))
+
+;; Batch size can't be too large, because we put markers on each
+;; ANCHOR, so a batch size of 400 lines means 400 markers.
+(defvar treesit--indent-region-batch-size 400
+  "How many lines of indent value do we precompute.
+In `treesit-indent-region' we indent in batches: precompute
+indent for each line, apply them in one go, let parser reparse,
+and do it again.  This way the parser doesn't need to unnecessarily
+reparse after indenting every single line.")
+
+(defun treesit-indent-region (beg end)
+  "Indent the region between BEG and END.
+Similar to `treesit-indent', but indent a region instead."
+  (treesit-update-ranges beg end)
+  ;; We indent `treesit--indent-region-batch-size' lines at a time, to
+  ;; reduce the number of times the parser needs to re-parse.  In each
+  ;; batch, we go through each line and calculate the anchor and
+  ;; offset as usual, but instead of modifying the buffer, we save
+  ;; these information in a vector.  Once we've collected ANCHOR and
+  ;; OFFSET for each line in the batch, we go through each line again
+  ;; and apply the changes.  Now that buffer is modified, we need to
+  ;; reparse the buffer before continuing to indent the next batch.
+  (let* ((meta-len 2)
+         (vector-len (* meta-len treesit--indent-region-batch-size))
+         ;; This vector saves the indent meta for each line in the
+         ;; batch.  It is a vector [ANCHOR OFFSET ANCHOR OFFSET...].
+         ;; ANCHOR is a marker on the anchor position, and OFFSET is
+         ;; an integer.  ANCHOR and OFFSET are either both nil, or
+         ;; both valid.
+         (meta-vec (make-vector vector-len 0))
+         (lines-left-to-move 0)
+         (end (copy-marker end t))
+         (idx 0)
+         (starting-pos 0)
+         (announce-progress (> (- end beg) 80000)))
+    (save-excursion
+      (goto-char beg)
+      ;; First pass.  Go through each line and compute the
+      ;; indentation.
+      (while (and (eq lines-left-to-move 0) (< (point) end))
+        (setq idx 0
+              starting-pos (point))
+        (while (and (eq lines-left-to-move 0)
+                    (< idx treesit--indent-region-batch-size)
+                    (< (point) end))
+          (if (looking-at (rx (* whitespace) eol) t)
+              ;; Unlike in `indent-line' where we sometimes pre-indent
+              ;; an empty line, We don't indent empty lines in
+              ;; `indent-region'.  Set ANCHOR and OFFSET to nil.
+              (setf (aref meta-vec (* idx meta-len)) nil
+                    (aref meta-vec (+ 1 (* idx meta-len))) nil)
+            (pcase-let* ((`(,anchor . ,offset) (treesit--indent-1))
+                         (marker (aref meta-vec (* idx meta-len))))
+              ;; Set ANCHOR.
+              (when anchor
+                (if (markerp marker)
+                    (move-marker marker anchor)
+                  (setf (aref meta-vec (* idx meta-len))
+                        (copy-marker anchor t))))
+              ;; SET OFFSET.
+              (setf (aref meta-vec (+ 1 (* idx meta-len))) offset)))
+          (cl-incf idx)
+          (setq lines-left-to-move (forward-line 1)))
+        ;; Now IDX = last valid IDX + 1.
+        (goto-char starting-pos)
+        ;; Second pass, go to each line and apply the indentation.
+        (dotimes (jdx idx)
+          (let ((anchor (aref meta-vec (* jdx meta-len)))
+                (offset (aref meta-vec (+ 1 (* jdx meta-len)))))
+            (when offset
+              (let ((col (save-excursion
+                           (goto-char anchor)
+                           (+ offset (current-column)))))
+                (indent-line-to col))))
+          (forward-line 1))
+        (when announce-progress
+          (message "Indenting region...%s%%"
+                   (/ (* (- (point) beg) 100) (- end beg)))))
+      ;; Delete markers.
+      (dotimes (idx treesit--indent-region-batch-size)
+        (let ((marker (aref meta-vec (* idx meta-len))))
+          (when (markerp marker)
+            (move-marker marker nil))))
+      (move-marker end nil))))
+
+(defun treesit-simple-indent (node parent bol)
+  "Calculate indentation according to `treesit-simple-indent-rules'.
+
+BOL is the position of the first non-whitespace character on the
+current line.  NODE is the largest node that starts at BOL,
+PARENT is NODE's parent.
+
+Return (ANCHOR . OFFSET) where ANCHOR is a node, OFFSET is the
+indentation offset, meaning indent to align with ANCHOR and add
+OFFSET."
+  (if (null parent)
+      (progn (when treesit--indent-verbose
+               (message "PARENT is nil, not indenting"))
+             (cons nil nil))
+    (let* ((language (treesit-node-language parent))
+           (rules (alist-get language
+                             treesit-simple-indent-rules)))
+      (cl-loop for rule in rules
+               for pred = (nth 0 rule)
+               for anchor = (nth 1 rule)
+               for offset = (nth 2 rule)
+               if (treesit--simple-indent-eval
+                   (list pred node parent bol))
+               do (when treesit--indent-verbose
+                    (message "Matched rule: %S" rule))
+               and
+               return
+               (let ((anchor-pos
+                      (treesit--simple-indent-eval
+                       (list anchor node parent bol))))
+                 (cons anchor-pos (if (symbolp offset)
+                                      (symbol-value offset)
+                                    offset)))
+               finally return
+               (progn (when treesit--indent-verbose
+                        (message "No matched rule"))
+                      (cons nil nil))))))
+
+(defun treesit-check-indent (mode)
+  "Check current buffer's indentation against a major mode MODE.
+
+Pop up a diff buffer showing the difference.  Correct
+indentation (target) is in green, current indentation is in red."
+  (interactive "CTarget major mode: ")
+  (let ((source-buf (current-buffer)))
+    (with-temp-buffer
+      (insert-buffer-substring source-buf)
+      (funcall mode)
+      (indent-region (point-min) (point-max))
+      (diff-buffers source-buf (current-buffer)))))
+
+(defun treesit--indent-rules-optimize (rules)
+  "Optimize simple indent RULES.
+RULES should be a value suitable for
+`treesit-simple-indent-rules'.  Return the optimized version of
+RULES."
+  ;; Right now this function just compiles queries.  It doesn't
+  ;; byte-compile matchers and anchors because it doesn't make much
+  ;; difference.
+  (cl-loop for setting in rules
+           for lang = (car setting)
+           for indent-rules = (cdr setting)
+           collect
+           (cl-labels
+               ;; Optimize a matcher or anchor.
+               ((optimize-func (func)
+                  (pcase func
+                    (`(query ,qry)
+                     (list 'query (treesit-query-compile lang qry)))
+                    (_ func)))
+                ;; Optimize a rule (MATCHER ANCHOR OFFSET).
+                (optimize-rule (rule)
+                  (let ((matcher (nth 0 rule))
+                        (anchor (nth 1 rule))
+                        (offset (nth 2 rule)))
+                    (list (optimize-func matcher)
+                          (optimize-func anchor)
+                          offset))))
+             (cons lang (mapcar #'optimize-rule indent-rules)))))
+
+;;; Search
+
+(defun treesit-search-forward-goto
+    (node predicate &optional start backward all)
+  "Search forward for a node and move to its end position.
+
+Stop at the first node after NODE that matches PREDICATE.
+PREDICATE can be either a regexp that matches against each node's
+type case-insensitively, or a function that takes a node and
+returns nil/non-nil for match/no match.
+
+If a node matches, move to that node and return the node,
+otherwise return nil.  If START is non-nil, stop at the
+beginning rather than the end of a node.
+
+This function guarantees that the matched node it returns makes
+progress in terms of buffer position: the start/end position of
+the returned node is always greater than that of NODE.
+
+BACKWARD and ALL are the same as in `treesit-search-forward'."
+  (when-let* ((start-pos (if start
+                             (treesit-node-start node)
+                           (treesit-node-end node)))
+              (current-pos start-pos))
+    ;; When searching forward and stopping at beginnings, or search
+    ;; backward stopping at ends, it is possible to "roll back" in
+    ;; position.  Take three nodes N1, N2, N3 as an example, if we
+    ;; start at N3, search for forward for beginning, and N1 matches,
+    ;; we would stop at beg of N1, which is backwards!  So we skip N1
+    ;; and keep going.
+    ;;
+    ;;   |<--------N1------->|
+    ;;   |<--N2-->| |<--N3-->|
+    (while (and node (if backward
+                         (>= current-pos start-pos)
+                       (<= current-pos start-pos)))
+      (setq node (treesit-search-forward
+                  node predicate backward all))
+      (setq current-pos (if start
+                            (treesit-node-start node)
+                          (treesit-node-end node))))
+    (cond
+     ;; When there is a match and match made progress, go to the
+     ;; result position.
+     ((and node
+           (if backward
+               (< current-pos (point))
+             (> current-pos (point))))
+      (goto-char current-pos)))
+    node))
+
+;;; Navigation
+
+(defvar-local treesit-defun-type-regexp nil
+  "A regexp that matches the node type of defun nodes.
+For example, \"(function|class)_definition\".
+
+This is used by `treesit-beginning-of-defun' and friends.")
+
+(defun treesit-beginning-of-defun (&optional arg)
+  "Tree-sitter `beginning-of-defun' function.
+ARG is the same as in `beginning-of-defun'."
+  (let ((arg (or arg 1))
+        (node (treesit-node-at (point))))
+    (if (> arg 0)
+        ;; Go backward.
+        (while (and (> arg 0)
+                    (setq node (treesit-search-forward-goto
+                                node treesit-defun-type-regexp t t)))
+          (setq node (or (treesit-node-top-level
+                          node treesit-defun-type-regexp)
+                         node))
+          (setq arg (1- arg)))
+      ;; Go forward.
+      (while (and (< arg 0)
+                  (setq node (treesit-search-forward-goto
+                              node treesit-defun-type-regexp)))
+        (setq node (or (treesit-node-top-level
+                        node treesit-defun-type-regexp)
+                       node))
+        (setq arg (1+ arg))))
+    (when node
+      (goto-char (treesit-node-start node))
+      t)))
+
+(defun treesit-end-of-defun ()
+  "Tree-sitter `end-of-defun' function."
+  ;; Why not simply get the largest node at point: when point is at
+  ;; (point-min), that gives us the root node.
+  (let* ((node (treesit-node-at (point)))
+         (top (or (treesit-node-top-level
+                   node
+                   treesit-defun-type-regexp)
+                  node)))
+    (goto-char (treesit-node-end top))))
+
+;;; Imenu
+
+(defvar-local treesit-imenu-function nil
+  "Tree-sitter version of `imenu-create-index-function'.
+
+Set this variable to a function and `treesit-mode' will bind it
+to `imenu-create-index-function'.")
+
+;;; Activating tree-sitter
+
+(defun treesit-ready-p (language &optional quiet)
+  "Check whether tree-sitter is ready to be used for MODE and LANGUAGE.
+
+LANGUAGE is the language symbol to check for availability.
+It can also be a list of language symbols.
+
+If tree-sitter is not ready, emit a warning and return nil.  If
+the user has chosen to activate tree-sitter for LANGUAGE and
+tree-sitter is ready, return non-nil.  If QUIET is t, don't emit
+a warning in either case; if quiet is `message', display a message
+instead of emitting a warning."
+  (let ((language-list (if (consp language)
+                           language
+                         (list language)))
+        msg)
+    ;; Check for each condition and set MSG.
+    (catch 'term
+      (when (not (treesit-available-p))
+        (setq msg "tree-sitter library is not compiled with Emacs")
+        (throw 'term nil))
+      (when (> (buffer-size) treesit-max-buffer-size)
+        (setq msg "buffer larger than `treesit-max-buffer-size'")
+        (throw 'term nil))
+      (dolist (lang language-list)
+        (pcase-let ((`(,available . ,err)
+                     (treesit-language-available-p lang t)))
+          (when (not available)
+            (setq msg (format "language definition for %s is unavailable (%s): 
%s"
+                              lang (nth 0 err)
+                              (string-join
+                               (mapcar (lambda (x) (format "%s" x))
+                                       (cdr err))
+                               " ")))
+            (throw 'term nil)))))
+    ;; Decide if all conditions met and whether emit a warning.
+    (if (not msg)
+        t
+      (setq msg (concat "Cannot activate tree-sitter, because " msg))
+      (pcase quiet
+        ('nil (display-warning 'treesit msg))
+        ('message (message "%s" msg)))
+      nil)))
+
+(defun treesit-major-mode-setup ()
+  "Activate tree-sitter to power major-mode features.
+
+If `treesit-font-lock-settings' is non-nil, setup fontification and
+enable `font-lock-mode'.
+
+If `treesit-simple-indent-rules' is non-nil, setup indentation.
+
+If `treesit-defun-type-regexp' is non-nil, setup
+`beginning/end-of-defun' functions.
+
+Make sure necessary parsers are created for the current buffer
+before calling this function."
+  ;; Font-lock.
+  (when treesit-font-lock-settings
+    ;; `font-lock-mode' wouldn't setup properly if
+    ;; `font-lock-defaults' is nil, see `font-lock-specified-p'.
+    (setq-local font-lock-defaults
+                '( nil nil nil nil
+                   (font-lock-fontify-syntactically-function
+                    . treesit-font-lock-fontify-region)))
+    (font-lock-mode 1)
+    (treesit-font-lock-recompute-features)
+    (dolist (parser (treesit-parser-list))
+      (treesit-parser-add-notifier
+       parser #'treesit--font-lock-notifier)))
+  ;; Indent.
+  (when treesit-simple-indent-rules
+    (setq-local treesit-simple-indent-rules
+                (treesit--indent-rules-optimize
+                 treesit-simple-indent-rules))
+    (setq-local indent-line-function #'treesit-indent)
+    (setq-local indent-region-function #'treesit-indent-region))
+  ;; Navigation.
+  (when treesit-defun-type-regexp
+    (setq-local beginning-of-defun-function #'treesit-beginning-of-defun)
+    (setq-local end-of-defun-function #'treesit-end-of-defun)))
+
+;;; Debugging
+
+(defvar-local treesit--inspect-name nil
+  "Used by `treesit-inspect-mode' to show node name in mode-line.")
+
+(defun treesit-inspect-node-at-point (&optional arg)
+  "Show information of the node at point.
+If called interactively, show in echo area, otherwise set
+`treesit--inspect-name' (which will appear in the mode-line
+if `treesit-inspect-mode' is enabled).  Uses the first parser
+in `treesit-parser-list'."
+  (interactive "p")
+  ;; NODE-LIST contains all the node that starts at point.
+  (let* ((node-list
+          (cl-loop for node = (treesit-node-at (point))
+                   then (treesit-node-parent node)
+                   while node
+                   if (eq (treesit-node-start node)
+                          (point))
+                   collect node))
+         (largest-node (car (last node-list)))
+         (parent (treesit-node-parent largest-node))
+         ;; node-list-acending contains all the node bottom-up, then
+         ;; the parent.
+         (node-list-acending
+          (if (null largest-node)
+              ;; If there are no nodes that start at point, just show
+              ;; the node at point and its parent.
+              (list (treesit-node-at (point))
+                    (treesit-node-parent
+                     (treesit-node-at (point))))
+            (append node-list (list parent))))
+         (name ""))
+    ;; We draw nodes like (parent field-name: (node)) recursively,
+    ;; so it could be (node1 field-name: (node2 field-name: (node3))).
+    (dolist (node node-list-acending)
+      (setq
+       name
+       (concat
+        (if (treesit-node-field-name node)
+            (format " %s: " (treesit-node-field-name node))
+          " ")
+        (if (treesit-node-check node 'named) "(" "\"")
+        (propertize (or (treesit-node-type node) "N/A")
+                    'face
+                    (if (treesit-node-eq node largest-node)
+                        'bold nil))
+        name
+        (if (treesit-node-check node 'named) ")" "\""))))
+    (setq treesit--inspect-name name)
+    (force-mode-line-update)
+    (when arg
+      (if node-list
+          (message "%s" treesit--inspect-name)
+        (message "No node at point")))))
+
+(define-minor-mode treesit-inspect-mode
+  "Minor mode that displays in the mode-line the node which starts at point.
+
+When this mode is enabled, the mode-line displays
+
+    PARENT FIELD-NAME: (NODE FIELD-NAME: (CHILD (...)))
+
+where NODE, CHILD, etc, are nodes which begin at point.  PARENT
+is the parent of NODE.  NODE is displayed in bold typeface.
+FIELD-NAMEs are field names of NODE and CHILD, etc (see Info
+node `(elisp)Language Definitions', heading \"Field names\").
+
+If no node starts at point, i.e., point is in the middle of a
+node, then the mode line displays the earliest node that spans point,
+and its immediate parent.
+
+This minor mode doesn't create parsers on its own.  It uses the first
+parser in `treesit-parser-list'."
+  :lighter nil
+  (if treesit-inspect-mode
+      (progn
+        (add-hook 'post-command-hook
+                  #'treesit-inspect-node-at-point 0 t)
+        (add-to-list 'mode-line-misc-info
+                     '(:eval treesit--inspect-name)))
+    (remove-hook 'post-command-hook
+                 #'treesit-inspect-node-at-point t)
+    (setq mode-line-misc-info
+          (remove '(:eval treesit--inspect-name)
+                  mode-line-misc-info))))
+
+(defun treesit-query-validate (language query)
+  "Check if QUERY is valid for LANGUAGE.
+If QUERY is invalid, display the query in a popup buffer, jump
+to the offending pattern and highlight the pattern."
+  (cl-assert (or (consp query) (stringp query)))
+  (let ((buf (get-buffer-create "*tree-sitter check query*")))
+    (with-temp-buffer
+      (treesit-parser-create language)
+      (condition-case err
+          (progn (treesit-query-capture language query)
+                 (message "QUERY is valid"))
+        (treesit-query-error
+         (with-current-buffer buf
+           (let* ((data (cdr err))
+                  (message (nth 0 data))
+                  (start (nth 1 data)))
+             (erase-buffer)
+             (insert (treesit-query-expand query))
+             (goto-char start)
+             (search-forward " " nil t)
+             (put-text-property start (point) 'face 'error)
+             (message "%s" (buffer-substring start (point)))
+             (goto-char (point-min))
+             (insert (format "%s: %d\n" message start))
+             (forward-char start)))
+         (pop-to-buffer buf))))))
+
+;;; Explorer
+
+(defvar-local treesit--explorer-buffer nil
+  "Buffer used to display the syntax tree.")
+
+(defvar-local treesit--explorer-source-buffer nil
+  "Source buffer corresponding to the playground buffer.")
+
+(defvar-local treesit--explorer-language nil
+  "The language used in the playground.")
+
+(defvar-local treesit--explorer-refresh-timer nil
+  "Timer for refreshing the syntax tree buffer.")
+
+(defvar-local treesit--explorer-highlight-overlay nil
+  "Overlay used to highlight in syntax tree and source buffer.")
+
+(defvar-local treesit--explorer-last-node nil
+  "Last top-level node used to generate syntax tree.")
+
+(defvar treesit-explore-mode)
+
+(defun treesit--explorer--nodes-to-highlight (language)
+  "Return nodes for LANGUAGE covered in region.
+This function tries to return the largest node possible.  So it
+will return a single large node rather than a bunch of small
+nodes.  If it end up returning multiple small nodes, it only
+returns the first and last node, and omits the ones in between."
+  (let* ((beg (region-beginning))
+         (end (region-end))
+         (node (treesit-node-on beg end language))
+         (node (or (treesit-parent-while
+                    node
+                    (lambda (n)
+                      (<= beg (treesit-node-start n)
+                          (treesit-node-end n) end)))
+                   node)))
+    ;; If NODE is completely contained in the region, return NODE,
+    ;; otherwise return its children that are in the region.
+    (if (<= beg (treesit-node-start node)
+            (treesit-node-end node) end)
+        (list node)
+      (list (treesit-node-at beg)
+            (treesit-search-forward
+             (treesit-node-at end)
+             (lambda (n)
+               (<= (treesit-node-end n) end))
+             t t)))))
+
+(defun treesit--explorer-refresh ()
+  "Update the syntax tree buffer."
+  (when (and treesit-explore-mode
+             (buffer-live-p treesit--explorer-buffer))
+    (let* ((root (treesit-node-on
+                  (window-start) (window-end) treesit--explorer-language))
+           ;; Only highlight the current top-level construct.
+           ;; Highlighting the whole buffer is slow and unnecessary.
+           (top-level (treesit-node-first-child-for-pos
+                       root (if (eolp)
+                                (max (point-min) (1- (point)))
+                              (point))
+                       t))
+           ;; Only highlight node when region is active, if we
+           ;; highlight node at point the syntax tree is too jumpy.
+           (nodes-hl
+            (when (region-active-p)
+              (treesit--explorer--nodes-to-highlight
+               treesit--explorer-language)))
+           ;; If we didn't edit the buffer nor change the top-level
+           ;; node, don't redraw the whole syntax tree.
+           (highlight-only (treesit-node-eq
+                            top-level treesit--explorer-last-node))
+           (source-buffer (current-buffer)))
+      (setq-local treesit--explorer-last-node top-level)
+      (with-current-buffer treesit--explorer-buffer
+        (let ((inhibit-read-only t))
+          (setq-local treesit--explorer-source-buffer source-buffer)
+          ;; Redraw the syntax tree or just rehighlight the focused
+          ;; node.
+          (when (and top-level (not highlight-only))
+            (erase-buffer)
+            (treesit--explorer-draw-node top-level))
+          (when-let ((pos (treesit--explorer-highlight-node nodes-hl))
+                     (window (get-buffer-window
+                              treesit--explorer-buffer)))
+            (if highlight-only
+                (goto-char pos)
+              ;; If HIGHLIGHT-ONLY is nil, we erased the buffer and
+              ;; re-inserted text, scroll down from the very top until
+              ;; we can see the highlighted node.
+              (goto-char (point-min))
+              (while (and (null (pos-visible-in-window-p pos window))
+                          (= (forward-line 4) 0))
+                (set-window-start window (point))))
+            (set-window-point window pos)))))))
+
+(defun treesit--explorer-post-command (&rest _)
+  "Post-command function that runs in the source buffer."
+  (when treesit-explore-mode
+    (when treesit--explorer-highlight-overlay
+      (delete-overlay treesit--explorer-highlight-overlay))
+    (when treesit--explorer-refresh-timer
+      (cancel-timer treesit--explorer-refresh-timer))
+    (setq-local treesit--explorer-refresh-timer
+                (run-with-timer 0.1 nil #'treesit--explorer-refresh))))
+
+(defun treesit--explorer-jump (button)
+  "Mark the original text corresponding to BUTTON."
+  (interactive)
+  (when (and (derived-mode-p 'treesit--explorer-tree-mode)
+             (buffer-live-p treesit--explorer-source-buffer))
+    (with-current-buffer treesit--explorer-source-buffer
+      (let ((start (button-get button 'node-start))
+            (end (button-get button 'node-end)))
+        (when treesit--explorer-highlight-overlay
+          (delete-overlay treesit--explorer-highlight-overlay))
+        (setq-local treesit--explorer-highlight-overlay
+                    (make-overlay start end nil t nil))
+        (overlay-put treesit--explorer-highlight-overlay
+                     'face 'highlight)))))
+
+(defun treesit--explorer-highlight-node (nodes)
+  "Highlight nodes in NODES in the syntax tree buffer.
+Return the start of the syntax tree text corresponding to NODE."
+  (when treesit--explorer-highlight-overlay
+    (delete-overlay treesit--explorer-highlight-overlay))
+  (let ((start-node (car nodes))
+        (end-node (car (last nodes)))
+        start end)
+    (when (and start-node end-node)
+      (cl-loop for ov in (overlays-in (point-min) (point-max))
+               while (or (null start) (null end))
+               if (treesit-node-eq start-node
+                                   (overlay-get ov 'treesit-node))
+               do (setq start (overlay-start ov))
+               if (treesit-node-eq end-node (overlay-get ov 'treesit-node))
+               do (setq end (overlay-end ov)))
+      (when (and start end)
+        (setq-local treesit--explorer-highlight-overlay
+                    (make-overlay start end))
+        (overlay-put treesit--explorer-highlight-overlay
+                     'face 'highlight)
+        start))))
+
+(defun treesit--explorer-draw-node (node)
+  "Draw the syntax tree of NODE.
+If NODE and NODE-HIGHLIGHT are the same node, highlight it.
+
+When this function is called, point should be at an empty line,
+when appropriate indent in front of point.  When this function
+returns, it leaves point at the end of the last line of NODE.
+
+Return the start position of NODE-HIGHLIGHT in the buffer, if any."
+  (let* ((type (treesit-node-type node))
+         (field-name (treesit-node-field-name node))
+         (children (treesit-node-children node))
+         (named (treesit-node-check node 'named))
+         ;; Column number of the start of the field-name, aka start of
+         ;; the whole node.
+         (before-field-column (current-column))
+         ;; Column number after the field-name.
+         after-field-column
+         ;; Column number after the type.
+         after-type-column
+         ;; Are all children suitable for inline?
+         (all-children-inline
+          (eq 0 (apply #'+ (mapcar #'treesit-node-child-count children))))
+         ;; If the child is the first child, we can inline, if the
+         ;; previous child is suitable for inline, this child can
+         ;; inline, if the previous child is not suitable for inline,
+         ;; this child cannot inline.
+         (can-inline t)
+         ;; The beg and end of this node.
+         beg end)
+    (when treesit--explorer-highlight-overlay
+      (delete-overlay treesit--explorer-highlight-overlay))
+
+    (setq beg (point))
+    ;; Draw field name.  If all children are suitable for inline, we
+    ;; draw everything in one line, other wise draw field name and the
+    ;; rest of the node in two lines.
+    (when field-name
+      (insert field-name ": ")
+      (when (and children (not all-children-inline))
+        (insert "\n")
+        (indent-to-column (1+ before-field-column))))
+    (setq after-field-column (current-column))
+
+    ;; Draw type.
+    (if named
+        (progn
+          (insert "(")
+          (insert-text-button
+           type 'action #'treesit--explorer-jump
+           'follow-link t
+           'node-start (treesit-node-start node)
+           'node-end (treesit-node-end node)))
+      (pcase type
+        ("\n" (insert "\\n"))
+        ("\t" (insert "\\t"))
+        (" " (insert "SPC"))
+        (_ (insert type))))
+    (setq after-type-column (current-column))
+
+    ;; Draw children.
+    (dolist (child children)
+      ;; If a child doesn't have children, it is suitable for inline.
+      (let ((draw-inline (eq 0 (treesit-node-child-count child)))
+            (children-indent (1+ after-field-column)))
+        (while
+            ;; This form returns t if it wants to run another
+            ;; iteration, returns nil if it wants to stop.
+            (if (and draw-inline can-inline)
+                ;; Draw children on the same line.
+                (let ((inline-beg (point)))
+                  (insert " ")
+                  (treesit--explorer-draw-node child)
+                  ;; If we exceeds window width, draw on the next line.
+                  (if (< (current-column) (window-width))
+                      nil
+                    (delete-region inline-beg (point))
+                    (setq draw-inline nil
+                          children-indent (1+ after-type-column))
+                    t))
+              ;; Draw children on the new line.
+              (insert "\n")
+              (indent-to-column children-indent)
+              (treesit--explorer-draw-node child)
+              nil))
+        (setq can-inline draw-inline)))
+
+    ;; Done drawing children, draw the ending paren.
+    (when named (insert ")"))
+    (setq end (point))
+
+    ;; Associate the text with NODE, so we can later find a piece of
+    ;; text by a node.
+    (let ((ov (make-overlay beg end)))
+      (overlay-put ov 'treesit-node node)
+      (overlay-put ov 'evaporate t)
+      (when (not named)
+        (overlay-put ov 'face 'shadow)))))
+
+(define-derived-mode treesit--explorer-tree-mode special-mode
+  "TS Explorer"
+  "Mode for displaying syntax trees for `treesit-explore-mode'."
+  nil)
+
+(define-minor-mode treesit-explore-mode
+  "Enable exploring the current buffer's syntax tree.
+Pops up a window showing the syntax tree of the source in the
+current buffer in real time.  The corresponding node enclosing
+the text in the active region is highlighted in the explorer
+window."
+  :lighter " TSplay"
+  (if treesit-explore-mode
+      (progn
+        (unless (buffer-live-p treesit--explorer-buffer)
+          (setq-local treesit--explorer-buffer
+                      (get-buffer-create
+                       (format "*tree-sitter playground for %s*"
+                               (buffer-name))))
+          (setq-local treesit--explorer-language
+                      (intern (completing-read
+                               "Language: "
+                               (mapcar #'treesit-parser-language
+                                       (treesit-parser-list)))))
+          (with-current-buffer treesit--explorer-buffer
+            (treesit--explorer-tree-mode)))
+        (display-buffer treesit--explorer-buffer
+                        (cons nil '((inhibit-same-window . t))))
+        (treesit--explorer-refresh)
+        (add-hook 'post-command-hook
+                  #'treesit--explorer-post-command 0 t)
+        (setq-local treesit--explorer-last-node nil))
+    (remove-hook 'post-command-hook
+                 #'treesit--explorer-post-command t)
+    (kill-buffer treesit--explorer-buffer)))
+
+;;; Etc
+
+(declare-function find-library-name "find-func.el")
+(defun treesit--check-manual-coverage ()
+  "Print tree-sitter functions missing from the manual in message buffer."
+  (interactive)
+  (require 'find-func)
+  (let ((functions-in-source
+         (with-temp-buffer
+           (insert-file-contents (find-library-name "treesit"))
+           (cl-remove-if
+            (lambda (name) (string-match "treesit--" name))
+            (cl-sort
+             (save-excursion
+               (goto-char (point-min))
+               (cl-loop while (re-search-forward
+                               "^(defun \\([^ ]+\\)" nil t)
+                        collect (match-string-no-properties 1)))
+             #'string<))))
+        (functions-in-manual
+         (with-temp-buffer
+           (insert-file-contents (expand-file-name
+                                  "doc/lispref/parsing.texi"
+                                  source-directory))
+           (insert-file-contents (expand-file-name
+                                  "doc/lispref/modes.texi"
+                                  source-directory))
+           (cl-sort
+            (save-excursion
+              (goto-char (point-min))
+              (cl-loop while (re-search-forward
+                              "^@defun \\([^ ]+\\)" nil t)
+                       collect (match-string-no-properties 1)))
+            #'string<))))
+    (message "Missing: %s"
+             (string-join
+              (cl-remove-if
+               (lambda (name) (member name functions-in-manual))
+               functions-in-source)
+              "\n"))))
+
+;;; Shortdocs
+
+(defun treesit--generate-shortdoc-examples ()
+  "Generate examples for shortdoc."
+  (with-temp-buffer
+    (let (node parent)
+      (insert "int c = 0;")
+      (print (treesit-parser-create 'c))
+      (print (treesit-parser-list))
+      (goto-char (point-min))
+      (print (setq node (treesit-node-at (point))))
+      (print (setq parent (treesit-node-parent node)))
+      (print (treesit-node-children parent))
+      (print (treesit-node-next-sibling node))
+      (print (treesit-node-child-by-field-name parent "declarator"))
+      nil)))
+
+(define-short-documentation-group treesit
+
+
+  "Parsers"
+  (treesit-parser-create
+   :no-eval (treesit-parser-create)
+   :eg-result-string "#<treesit-parser for c>")
+  (treesit-parser-delete
+   :no-value (treesit-parser-delete parser))
+  (treesit-parser-list
+   :no-eval (treesit-parser-list)
+   :eg-result-string "(#<treesit-parser for c>)")
+  (treesit-parser-buffer
+   :no-eval (treesit-parser-buffer parser)
+   :eg-result-string "#<buffer xdisp.c>")
+  (treesit-parser-language
+   :no-eval (treesit-parser-language parser)
+   :eg-result c)
+  (treesit-parser-add-notifier)
+  (treesit-parser-remove-notifier)
+  (treesit-parser-notifiers
+   :no-eval (treesit-parser-notifiers parser)
+   :eg-result (function1 function2 function3))
+
+
+  "Parser ranges"
+  (treesit-parser-set-included-ranges
+   :no-value (treesit-parser-set-included-ranges parser '((1 . 4) (5 . 8))))
+  (treesit-parser-included-ranges
+   :no-eval (treesit-parser-included-ranges parser)
+   :eg-result '((1 . 4) (5 . 8)))
+  (treesit-query-range
+   :no-eval (treesit-query-range node '((script_element) @cap))
+   :eg-result-string '((1 . 4) (5 . 8)))
+
+
+  "Retrieving a node"
+  (treesit-node-at
+   :no-eval (treesit-node-at (point))
+   :eg-result-string "#<treesit-node (identifier) in 179-180>")
+  (treesit-node-on
+   :no-eval (treesit-node-on 18 28)
+   :eg-result-string "#<treesit-node (compound_statement) in 143-290>")
+  (treesit-buffer-root-node
+   :no-eval (treesit-buffer-root-node)
+   :eg-result-string "#<treesit-node (translation_unit) in 1-4830>")
+  (treesit-parser-root-node
+   :no-eval (treesit-parser-root-node parser)
+   :eg-result-string "#<treesit-node (translation_unit) in 1-4830>")
+
+
+  "Retrieving a node from another node"
+  (treesit-node-parent
+   :no-eval (treesit-node-parent node)
+   :eg-result-string "#<treesit-node (declaration) in 1-11>")
+  (treesit-node-child
+   :no-eval (treesit-node-child node 0)
+   :eg-result-string "#<treesit-node (primitive_type) in 1-4>")
+  (treesit-node-children
+   :no-eval (treesit-node-children node)
+   :eg-result-string "(#<treesit-node (primitive_type) in 1-4> #<treesit-node 
(init_declarator) in 5-10> #<treesit-node \";\" in 10-11>)")
+  (treesit-node-next-sibling
+   :no-eval (treesit-node-next-sibling node)
+   :eg-result-string "#<treesit-node (init_declarator) in 5-10>")
+  (treesit-node-prev-sibling
+   :no-eval (treesit-node-prev-sibling node)
+   :eg-result-string "#<treesit-node (primitive_type) in 1-4>")
+  (treesit-node-child-by-field-name
+   :no-eval (treesit-node-child-by-field-name node "declarator")
+   :eg-result-string "#<treesit-node (init_declarator) in 5-10>")
+
+
+  (treesit-first-child-for-pos
+   :no-eval (treesit-first-child-for-pos node 1)
+   :eg-result-string "#<treesit-node (primitive_type) in 1-4>")
+  (treesit-node-descendant-for-range
+   :no-eval (treesit-node-descendant-for-range node 2 3)
+   :eg-result-string "#<treesit-node (primitive_type) in 1-4>")
+
+
+  "Searching for node"
+  (treesit-search-subtree
+   :no-eval (treesit-search-subtree node "function_definition")
+   :eg-result-string "#<treesit-node (function_definition) in 57-146>")
+  (treesit-search-forward
+   :no-eval (treesit-search-forward node "function_definition")
+   :eg-result-string "#<treesit-node (function_definition) in 57-146>")
+  (treesit-search-forward-goto
+   :no-eval (treesit-search-forward-goto node "function_definition")
+   :eg-result-string "#<treesit-node (function_definition) in 57-146>")
+  (treesit-induce-sparse-tree
+   :no-eval (treesit-induce-sparse-tree node "function_definition")
+   :eg-result-string "(nil (#<treesit-node (function_definition) in 57-146>) 
(#<treesit-node (function_definition) in 259-296>) (#<treesit-node 
(function_definition) in 303-659>))")
+  (treesit-filter-child
+   :no-eval (treesit-filter-child node (lambda (n) (equal (treesit-node-type) 
"identifier")))
+   :eg-result-string "(#<treesit-node (identifier) in 195-196>)")
+  (treesit-parent-until
+   :no-eval (treesit-parent-until node (lambda (p) (eq (treesit-node-start p) 
(point))))
+   :eg-result-string "#<treesit-node (declaration) in 1-11>")
+  (treesit-parent-while
+   :no-eval (treesit-parent-while node (lambda (p) (eq (treesit-node-start p) 
(point))))
+   :eg-result-string "#<treesit-node (declaration) in 1-11>")
+  (treesit-node-top-level
+   :no-eval (treesit-node-top-level node)
+   :eg-result-string "#<treesit-node (declaration) in 1-11>")
+
+
+  "Retrieving node information"
+  (treesit-node-text
+   :no-eval (treesit-node-text node)
+   :eg-result "int")
+  (treesit-node-start
+   :no-eval (treesit-node-start node)
+   :eg-result 1)
+  (treesit-node-end
+   :no-eval (treesit-node-end node)
+   :eg-result 10)
+  (treesit-node-type
+   :no-eval (treesit-node-type node)
+   :eg-result "function_definition")
+  (treesit-node-field-name
+   :no-eval (treesit-node-field-name node)
+   :eg-result "body")
+
+
+  (treesit-node-parser
+   :no-eval (treesit-node-parser node)
+   :eg-result-string "#<treesit-parser for c>")
+  (treesit-node-language
+   :no-eval (treesit-node-language node)
+   :eg-result c)
+  (treesit-node-buffer
+   :no-eval (treesit-node-buffer node)
+   :eg-result-string "#<buffer xdisp.c>")
+
+
+  (treesit-node-index
+   :no-eval (treesit-node-index node)
+   :eg-result 0)
+  (treesit-node-string
+   :no-eval (treesit-node-string node)
+   :eg-result-string "(init_declarator declarator: (identifier) value: 
(number_literal))")
+  (treesit-node-check
+   :no-eval (treesit-node-check node 'named)
+   :eg-result t)
+
+
+  (treesit-field-name-for-child
+   :no-eval (treesit-field-name-for-child node)
+   :eg-result "body")
+  (treesit-child-count
+   :no-eval (treesit-child-count node)
+   :eg-result 3)
+
+
+  "Pattern matching"
+  (treesit-query-capture
+   :no-eval (treesit-query-capture node '((identifier) @id "return" @ret))
+   :eg-result-string "((id . #<treesit-node (identifier) in 195-196>) (ret . 
#<treesit-node "return" in 338-344>))")
+  (treesit-query-compile
+   :no-eval (treesit-query-compile 'c '((identifier) @id "return" @ret))
+   :eg-result-string "#<treesit-compiled-query>")
+  (treesit-query-language
+   :no-eval (treesit-query-language compiled-query)
+   :eg-result c)
+  (treesit-query-expand
+   :eval (treesit-query-expand '((identifier) @id "return" @ret)))
+  (treesit-pattern-expand
+   :eval (treesit-pattern-expand :anchor)
+   :eval (treesit-pattern-expand '(identifier))
+   :eval (treesit-pattern-expand :equal))
+
+
+  "Parsing a string"
+  (treesit-parse-string
+   :no-eval (treesit-parse-string "int c = 0;" 'c)
+   :eg-result-string "#<treesit-node (translation_unit) in 1-11>")
+  (treesit-query-string
+   :no-eval (treesit-query-string "int c = 0;" '((identifier) @id) 'c)
+   :eg-result-string "((id . #<treesit-node (identifier) in 5-6>))"))
+
+(provide 'treesit)
+
+;;; treesit.el ends here
diff --git a/lisp/url/ChangeLog.1 b/lisp/url/ChangeLog.1
index 2f7813e64c..1b5ddc1e76 100644
--- a/lisp/url/ChangeLog.1
+++ b/lisp/url/ChangeLog.1
@@ -366,7 +366,7 @@
        * url.el, url-queue.el, url-parse.el, url-http.el, url-future.el:
        * url-dav.el, url-cookie.el: Use cl-lib.
        * url-util.el, url-privacy.el, url-nfs.el, url-misc.el, url-methods.el:
-       * url-gw.el, url-file.el, url-expand.el: Dont use CL.
+       * url-gw.el, url-file.el, url-expand.el: Don't use CL.
 
 2012-06-30  Glenn Morris  <rgm@gnu.org>
 
diff --git a/lisp/url/url-irc.el b/lisp/url/url-irc.el
index 9161f7d13e..f97b6de6fe 100644
--- a/lisp/url/url-irc.el
+++ b/lisp/url/url-irc.el
@@ -38,11 +38,13 @@ The function should take the following arguments:
     PORT - the port number of the IRC server to contact
  CHANNEL - What channel on the server to visit right away (can be nil)
     USER - What username to use
-PASSWORD - What password to use"
+PASSWORD - What password to use.
+  SCHEME - a URI scheme, such as \"irc\" or \"ircs\""
   :type '(choice (const :tag "rcirc" :value url-irc-rcirc)
                 (const :tag "ERC" :value url-irc-erc)
                 (const :tag "ZEN IRC" :value url-irc-zenirc)
                 (function :tag "Other"))
+  :version "29.1" ; Added SCHEME
   :group 'url)
 
 ;; External.
@@ -51,7 +53,7 @@ PASSWORD - What password to use"
 (defvar zenirc-server-alist)
 (defvar zenirc-buffer-name)
 
-(defun url-irc-zenirc (host port channel user password)
+(defun url-irc-zenirc (host port channel user password _)
   (let ((zenirc-buffer-name (if (and user host port)
                                (format "%s@%s:%d" user host port)
                              (format "%s:%d" host port)))
@@ -65,14 +67,14 @@ PASSWORD - What password to use"
       (insert "/join " channel)
       (zenirc-send-line))))
 
-(defun url-irc-rcirc (host port channel user password)
+(defun url-irc-rcirc (host port channel user password _)
   (let ((chan (when channel (concat "#" channel))))
     (rcirc-connect host port user nil nil (when chan (list chan)) password)
     (when chan
       (switch-to-buffer (concat chan "@" host)))))
 
-(defun url-irc-erc (host port channel user password)
-  (erc-handle-irc-url host port channel user password))
+(defun url-irc-erc (host port channel user password scheme)
+  (erc-handle-irc-url host port channel user password scheme))
 
 ;;;###autoload
 (defun url-irc (url)
@@ -80,16 +82,32 @@ PASSWORD - What password to use"
         (port (url-port url))
         (pass (url-password url))
         (user (url-user url))
-        (chan (url-filename url)))
+         (chan (url-filename url))
+         (type (url-type url))
+         (compatp (eql 5 (cdr (func-arity url-irc-function)))))
     (if (url-target url)
        (setq chan (concat chan "#" (url-target url))))
     (if (string-match "^/" chan)
        (setq chan (substring chan 1 nil)))
     (if (= (length chan) 0)
        (setq chan nil))
-    (funcall url-irc-function host port chan user pass)
+    (when compatp
+      (lwarn 'url :error "Obsolete value for `url-irc-function'"))
+    (apply url-irc-function
+           host port chan user pass (unless compatp (list type)))
     nil))
 
+;;;; ircs://
+
+;; The function `url-scheme-get-property' tries and fails to load the
+;; nonexistent url-ircs.el but falls back to using the following:
+
+;;;###autoload
+(defconst url-ircs-default-port 6697 "Default port for IRCS connections.")
+
+;;;###autoload
+(defalias 'url-ircs 'url-irc)
+
 (provide 'url-irc)
 
 ;;; url-irc.el ends here
diff --git a/lisp/vc/add-log.el b/lisp/vc/add-log.el
index e3c0e2ca06..a1d6152ee2 100644
--- a/lisp/vc/add-log.el
+++ b/lisp/vc/add-log.el
@@ -1188,7 +1188,7 @@ file were isearch was started."
      ((and wrap (null file))
       (current-buffer))
      ;; When there is no next file, file-exists-p raises the error to be
-     ;; catched by the search function that displays the error message.
+     ;; caught by the search function that displays the error message.
      ((file-exists-p file)
       (find-file-noselect file))
      (t
diff --git a/lisp/vc/diff-mode.el b/lisp/vc/diff-mode.el
index a9591c9d82..357ce001b3 100644
--- a/lisp/vc/diff-mode.el
+++ b/lisp/vc/diff-mode.el
@@ -306,7 +306,7 @@ well."
 ;; terminal supporting 24 bit colors) doesn't render well in terminal
 ;; supporting only 256 colors.  Concretely, both #ffeeee
 ;; (diff-removed) and #eeffee (diff-added) are mapped to the same
-;; greyish color.  "min-colors 257" ensures that those colors are not
+;; grayish color.  "min-colors 257" ensures that those colors are not
 ;; used terminals supporting only 256 colors.  However, any number
 ;; between 257 and 2^24 (16777216) would do.
 
diff --git a/lisp/vc/ediff-diff.el b/lisp/vc/ediff-diff.el
index 07b853817d..24647de576 100644
--- a/lisp/vc/ediff-diff.el
+++ b/lisp/vc/ediff-diff.el
@@ -942,6 +942,9 @@ one optional arguments, diff-number to refine.")
        (c-prev-pt nil)
        (anc-prev 1)
        diff-list shift-A shift-B shift-C
+        (A-idx "1")
+        (B-idx (if three-way-comp "2" "3"))
+        (C-idx (if three-way-comp "3" "2"))
        )
 
     ;; diff list contains word numbers or points, depending on word-mode
@@ -979,23 +982,23 @@ one optional arguments, diff-number to refine.")
        (let ((agreement (buffer-substring (match-beginning 1) (match-end 1))))
         ;; if the files A and B are the same and not 3way-comparison,
         ;; ignore the difference
-        (if (or three-way-comp (not (string-equal agreement "3")))
-            (let* ((a-begin (car (ediff-get-diff3-group "1")))
-                   (a-end  (nth 1 (ediff-get-diff3-group "1")))
-                   (b-begin (car (ediff-get-diff3-group "2")))
-                   (b-end (nth 1 (ediff-get-diff3-group "2")))
-                   (c-or-anc-begin (car (ediff-get-diff3-group "3")))
-                   (c-or-anc-end (nth 1 (ediff-get-diff3-group "3")))
+        (if (or three-way-comp (not (string-equal agreement C-idx)))
+            (let* ((a-begin (car (ediff-get-diff3-group A-idx)))
+                   (a-end  (nth 1 (ediff-get-diff3-group A-idx)))
+                   (b-begin (car (ediff-get-diff3-group B-idx)))
+                   (b-end (nth 1 (ediff-get-diff3-group B-idx)))
+                   (c-or-anc-begin (car (ediff-get-diff3-group C-idx)))
+                   (c-or-anc-end (nth 1 (ediff-get-diff3-group C-idx)))
                    (state-of-merge
-                    (cond ((string-equal agreement "1") 'prefer-A)
-                          ((string-equal agreement "2") 'prefer-B)
+                    (cond ((string-equal agreement A-idx) 'prefer-A)
+                          ((string-equal agreement B-idx) 'prefer-B)
                           (t ediff-default-variant)))
                    (state-of-diff-merge
                     (if (memq state-of-merge '(default-A prefer-A)) 'B 'A))
                    (state-of-diff-comparison
-                    (cond ((string-equal agreement "1") 'A)
-                          ((string-equal agreement "2") 'B)
-                          ((string-equal agreement "3") 'C)))
+                    (cond ((string-equal agreement A-idx) 'A)
+                          ((string-equal agreement B-idx) 'B)
+                          ((string-equal agreement C-idx) 'C)))
                    state-of-ancestor
                    c-begin c-end
                    a-begin-pt a-end-pt
@@ -1108,8 +1111,12 @@ one optional arguments, diff-number to refine.")
            (get-buffer-create (ediff-unique-buffer-name "*ediff-diff" "*"))))
 
   (message "Computing differences ...")
-  (ediff-exec-process ediff-diff3-program ediff-diff-buffer 'synchronize
-                     ediff-actual-diff3-options file-A file-B file-C)
+  (apply #'ediff-exec-process ediff-diff3-program ediff-diff-buffer 
'synchronize
+        ediff-actual-diff3-options
+         (cons file-A (if ediff-merge-with-ancestor-job
+                          ;; Ancestor must be the middle file
+                          (list file-C file-B)
+                          (list file-B file-C))))
 
   (ediff-prepare-error-list ediff-diff3-ok-lines-regexp ediff-diff-buffer)
   ;;(message "Computing differences ... done")
diff --git a/lisp/vc/ediff-util.el b/lisp/vc/ediff-util.el
index 0d96a195ad..f406883221 100644
--- a/lisp/vc/ediff-util.el
+++ b/lisp/vc/ediff-util.el
@@ -1748,7 +1748,7 @@ With a prefix argument ARG, go back that many 
differences."
                     regexp-skip
                     ;; skip clashes, if necessary
                     non-clash-skip
-                    ;; skipp changed regions
+                     ;; skip changed regions
                     skip-changed
                     ;; skip difference regions that differ in white space
                     (and ediff-ignore-similar-regions
diff --git a/lisp/vc/vc-dav.el b/lisp/vc/vc-dav.el
index 94621599e4..1cd91c9f54 100644
--- a/lisp/vc/vc-dav.el
+++ b/lisp/vc/vc-dav.el
@@ -93,7 +93,7 @@ If EDITABLE is non-nil URL should be writable by the user and 
if
 locking is used for URL, a lock should also be set.
 
 If REV is non-nil, that is the revision to check out.  If REV is the
-empty string, that means to check ou tht ehead of the trunk.
+empty string, that means to check out the head of the trunk.
 
 If optional arg DESTFILE is given, it is an alternate filename to
 write the contents to."
diff --git a/lisp/vc/vc-rcs.el b/lisp/vc/vc-rcs.el
index a4345c7d7e..c41835e19f 100644
--- a/lisp/vc/vc-rcs.el
+++ b/lisp/vc/vc-rcs.el
@@ -1192,7 +1192,7 @@ variable `vc-rcs-release' is set to the returned value."
 (defun vc-rcs-parse (&optional buffer)
   "Parse current buffer, presumed to be in RCS-style masterfile format.
 Optional arg BUFFER specifies another buffer to parse.  Return an alist
-of two elements, w/ keys `headers' and `revisions' and values in turn
+of two elements, with keys `headers' and `revisions' and values in turn
 sub-alists.  For `headers', the values unless otherwise specified are
 strings and the keys are:
 
diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el
index 513fbb23fe..fa3d58f770 100644
--- a/lisp/vc/vc.el
+++ b/lisp/vc/vc.el
@@ -3347,6 +3347,8 @@ immediately after this one."
           (lambda (&rest args)
             (apply #'vc-user-edit-command (apply old args))))))
 
+;; This is used in .dir-locals.el in the Emacs source tree.
+;;;###autoload (put 'vc-prepare-patches-separately 'safe-local-variable 
'booleanp)
 (defcustom vc-prepare-patches-separately t
   "Whether `vc-prepare-patch' should generate a separate message for each 
patch.
 If nil, `vc-prepare-patch' creates a single email message by attaching
@@ -3384,25 +3386,43 @@ If nil, no default will be used.  This option may be 
set locally."
                                (vc-root-dir))))
             :buffer (current-buffer)))))
 
+(defun vc-prepare-patch-prompt-revisions ()
+  "Prompt the user for a list revisions.
+Prepare a default value, depending on the current context.  With
+a numerical prefix argument, use the last N revisions as the
+default value.  If the current buffer is a log-view buffer, use
+the marked commits.  Otherwise fall back to the working revision
+of the current file."
+  (vc-read-multiple-revisions
+   "Revisions: " nil nil nil
+   (or (and-let* ((arg current-prefix-arg)
+                  (fs (vc-deduce-fileset t)))
+         (cl-loop with file = (caadr fs)
+                  repeat (prefix-numeric-value arg)
+                  for rev = (vc-working-revision file)
+                  then (vc-call-backend
+                        (car fs) 'previous-revision
+                        file rev)
+                  when rev collect it into revs
+                  finally return (mapconcat #'identity revs ",")))
+       (and-let* ((revs (log-view-get-marked)))
+         (mapconcat #'identity revs ","))
+       (and-let* ((file (buffer-file-name)))
+         (vc-working-revision file)))))
+
 ;;;###autoload
 (defun vc-prepare-patch (addressee subject revisions)
   "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."
+If `vc-prepare-patches-separately' is nil, use SUBJECT as the
+default subject for the message, or prompt a subject when invoked
+interactively.  Otherwise compose a separate message for each
+revision, with SUBJECT derived from each revision subject.
+When invoked with a numerical prefix argument, use the last N
+revisions.
+When invoked interactively in a Log View buffer with
+marked revisions, use those these."
   (interactive
-   (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)
+   (let ((revs (vc-prepare-patch-prompt-revisions)) to)
      (require 'message)
      (while (null (setq to (completing-read-multiple
                             (format-prompt
@@ -3567,7 +3587,7 @@ to provide the `find-revision' operation instead."
 
 (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
+If successful, returns the string with the directory of the
 checkout.  If BACKEND is nil, iterate through every known backend
 in `vc-handled-backends' until one succeeds.  If REV is non-nil,
 it indicates a specific revision to check out."
@@ -3597,9 +3617,7 @@ It returns the last revision that changed LINE number in 
FILE."
                      file (current-buffer))
     (goto-char (point-min))
     (forward-line (1- line))
-    (let ((rev (vc-call-backend
-                (vc-backend file)
-                'annotate-extract-revision-at-line)))
+    (let ((rev (vc-call annotate-extract-revision-at-line file)))
       (if (consp rev) (car rev) rev))))
 
 
diff --git a/lisp/whitespace.el b/lisp/whitespace.el
index 791a0a0b4e..25ea07e9db 100644
--- a/lisp/whitespace.el
+++ b/lisp/whitespace.el
@@ -746,7 +746,7 @@ 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.
 
-The indentation characters are highlighted using the `whitespace-indentationp'
+The indentation characters are highlighted using the `whitespace-indentation'
 face.
 This variable is used when `whitespace-style' includes `indentation',
 `indentation::tab' or  `indentation::space'."
diff --git a/lisp/window.el b/lisp/window.el
index 905803b19e..dd23ab1d39 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -1,7 +1,6 @@
 ;;; window.el --- GNU Emacs window commands aside from those written in C  -*- 
lexical-binding:t -*-
 
-;; Copyright (C) 1985, 1989, 1992-1994, 2000-2022 Free Software
-;; Foundation, Inc.
+;; Copyright (C) 1985-2022 Free Software Foundation, Inc.
 
 ;; Maintainer: emacs-devel@gnu.org
 ;; Keywords: internal
@@ -9173,7 +9172,7 @@ present.  See also `fit-frame-to-buffer-sizes'."
 This list specifies the total maximum and minimum numbers of
 lines and the maximum and minimum numbers of columns of the body
 of the root window of any frame that shall be fit to its buffer.
-Any value specified by ths variable will be overridden by the
+Any value specified by this variable will be overridden by the
 corresponding argument of `fit-frame-to-buffer', if non-nil.
 
 On window systems where the menubar can wrap, fitting a frame to
@@ -10562,27 +10561,25 @@ displaying that processes's buffer."
 (define-key ctl-x-4-map "1" 'same-window-prefix)
 (define-key ctl-x-4-map "4" 'other-window-prefix)
 
-(defvar other-window-repeat-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map "o" 'other-window)
-    (define-key map "O" (lambda ()
-                          (interactive)
-                          (setq repeat-map 'other-window-repeat-map)
-                          (other-window -1)))
-    map)
-  "Keymap to repeat `other-window' key sequences.  Used in `repeat-mode'.")
+(defvar-keymap other-window-repeat-map
+  :doc "Keymap to repeat `other-window' key sequences.
+Used in `repeat-mode'."
+  "o" #'other-window
+  "O" (lambda ()
+        (interactive)
+        (setq repeat-map 'other-window-repeat-map)
+        (other-window -1)))
 (put 'other-window 'repeat-map 'other-window-repeat-map)
 
-(defvar resize-window-repeat-map
-  (let ((map (make-sparse-keymap)))
-    ;; Standard keys:
-    (define-key map "^" 'enlarge-window)
-    (define-key map "}" 'enlarge-window-horizontally)
-    (define-key map "{" 'shrink-window-horizontally)
-    ;; Additional keys:
-    (define-key map "v" 'shrink-window)
-    map)
-  "Keymap to repeat window resizing commands.  Used in `repeat-mode'.")
+(defvar-keymap resize-window-repeat-map
+  :doc "Keymap to repeat window resizing commands.
+Used in `repeat-mode'."
+  ;; Standard keys:
+  "^" #'enlarge-window
+  "}" #'enlarge-window-horizontally
+  "{" #'shrink-window-horizontally
+  ;; Additional keys:
+  "v" #'shrink-window)
 (put 'enlarge-window 'repeat-map 'resize-window-repeat-map)
 (put 'enlarge-window-horizontally 'repeat-map 'resize-window-repeat-map)
 (put 'shrink-window-horizontally 'repeat-map 'resize-window-repeat-map)
diff --git a/lisp/woman.el b/lisp/woman.el
index 7f494a3b68..2b456fed3c 100644
--- a/lisp/woman.el
+++ b/lisp/woman.el
@@ -1751,21 +1751,17 @@ Leave point at end of new text.  Return length of 
inserted text."
 
 ;;; Major mode (Man) interface:
 
-(defvar woman-mode-map
-  (let ((map (make-sparse-keymap)))
-    (set-keymap-parent map Man-mode-map)
-
-    (define-key map "R" #'woman-reformat-last-file)
-    (define-key map "w" #'woman)
-    (define-key map "\en" #'WoMan-next-manpage)
-    (define-key map "\ep" #'WoMan-previous-manpage)
-    (define-key map [M-mouse-2] #'woman-follow-word)
-
-    ;; We don't need to call `man' when we are in `woman-mode'.
-    (define-key map [remap man] #'woman)
-    (define-key map [remap man-follow] #'woman-follow)
-    map)
-  "Keymap for `woman-mode'.")
+(defvar-keymap woman-mode-map
+  :doc "Keymap for `woman-mode'."
+  :parent Man-mode-map
+  "R"   #'woman-reformat-last-file
+  "w"   #'woman
+  "M-n" #'WoMan-next-manpage
+  "M-p" #'WoMan-previous-manpage
+  "M-<mouse-2>"          #'woman-follow-word
+  ;; We don't need to call `man' when we are in `woman-mode'.
+  "<remap> <man>"        #'woman
+  "<remap> <man-follow>" #'woman-follow)
 
 (defun woman-follow (topic)
   "Get a Un*x manual page of the item under point and put it in a buffer."
diff --git a/lisp/x-dnd.el b/lisp/x-dnd.el
index 058ab99f5c..b3465e757a 100644
--- a/lisp/x-dnd.el
+++ b/lisp/x-dnd.el
@@ -818,7 +818,7 @@ has been pressed."
               (let ((inhibit-message t))
                 (funcall function amt))
             ;; Do not error at buffer limits.  Show a message instead.
-            ;; This is especially important here because signalling an
+            ;; This is especially important here because signaling an
             ;; error will mess up the drag-and-drop operation.
             (beginning-of-buffer
              (message (error-message-string '(beginning-of-buffer))))
@@ -1468,7 +1468,7 @@ instead of returning \"E\".")
                           (dnd-get-local-file-name local-file-uri))))
     (if (not local-name)
         '(STRING . "F")
-      ;; We want errors to be signalled immediately during ERT
+      ;; We want errors to be signaled immediately during ERT
       ;; testing, instead of being silently handled.  (bug#56712)
       (if x-dnd-xds-testing
           (prog1 '(STRING . "S")
diff --git a/msdos/autogen/config.in b/msdos/autogen/config.in
index 14782ab4bf..4b1cfb96e8 100644
--- a/msdos/autogen/config.in
+++ b/msdos/autogen/config.in
@@ -217,7 +217,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
    whether the gnulib module scanf shall be considered present. */
 #undef GNULIB_SCANF
 
-/* Define if ths system is compatible with GNU/Linux. */
+/* Define if this system is compatible with GNU/Linux. */
 #undef GNU_LINUX
 
 /* Define to 1 if you want to use the GNU memory allocator. */
diff --git a/msdos/sed1v2.inp b/msdos/sed1v2.inp
index b7818f8b21..9c9d1eded1 100644
--- a/msdos/sed1v2.inp
+++ b/msdos/sed1v2.inp
@@ -179,6 +179,8 @@ s/ *@WEBP_LIBS@//
 /^LIBGCCJIT_OBJ *=/s/@LIBGCCJIT_OBJ@//
 /^LIBGCCJIT_CFLAGS *=/s/@LIBGCCJIT_CFLAGS@//
 /^LIBGCCJIT_LIBS *=/s/@LIBGCCJIT_LIBS@//
+/^TREE_SITTER_LIBS *=/s/@TREE_SITTER_LIBS@//
+/^TREE_SITTER_CFLAGS *=/s/@TREE_SITTER_CFLAGS@//
 /^HARFBUZZ_CFLAGS *=/s/@HARFBUZZ_CFLAGS@//
 /^HARFBUZZ_LIBS *=/s/@HARFBUZZ_LIBS@//
 /^LCMS2_CFLAGS *=/s/@LCMS2_CFLAGS@//
diff --git a/nt/icons/README b/nt/icons/README
index 4d9fb15e52..f84d4635b3 100644
--- a/nt/icons/README
+++ b/nt/icons/README
@@ -23,7 +23,7 @@ License: GNU General Public License version 3 or later (see 
COPYING)
   <http://users.adelphia.net/~rob.davenport/gnuicons.html>
   "These are some images of a 3D stylized gnu head that I created back
   in 1998. I started studying pictures of gnus and wildebeests and
-  worked with a 3D modeller, sPatch, until I came up with these. Then
+  worked with a 3D modeler, sPatch, until I came up with these. Then
   I worked to make them into icons - cropping the horns off the sides
   so the images were big enough to be recognizable (to me anyway)."
 
diff --git a/src/ChangeLog.10 b/src/ChangeLog.10
index ba1cea18d4..1b18ae5ec5 100644
--- a/src/ChangeLog.10
+++ b/src/ChangeLog.10
@@ -7899,7 +7899,7 @@
        * buffer.c (scroll-up-aggressively, scroll-down-aggressively):
        * keymap.c (Fminor_mode_key_binding):
        * macterm.c (mac-emulate-three-button-mouse):
-       Delete duplicate duplicate words.
+       Delete duplicate words.
 
 2005-07-18  Ken Raeburn  <raeburn@gnu.org>
 
diff --git a/src/ChangeLog.11 b/src/ChangeLog.11
index 15ab227171..a00ca453ca 100644
--- a/src/ChangeLog.11
+++ b/src/ChangeLog.11
@@ -7503,7 +7503,7 @@
 2010-05-28  Kenichi Handa  <handa@m17n.org>
 
        * font.c (font_delete_unmatched): Check Vface_ignored_fonts.
-       Don't sheck SPEC if it is nil.
+       Don't check SPEC if it is nil.
        (font_list_entities): Call font_delete_unmatched if
        Vface_ignored_fonts is non-nil.  (Bug#6287)
 
@@ -8639,7 +8639,7 @@
 
        * keyboard.c: QClabel is new.
        (parse_tool_bar_item): Take out QClabel from tool bar items.
-       Try to construct a label if ther is no QClabel.
+       Try to construct a label if there is no QClabel.
        (syms_of_keyboard): Intern :label as QClabel.
 
        * dispextern.h (tool_bar_item_idx): TOOL_BAR_ITEM_LABEL is new.
@@ -11988,7 +11988,7 @@
 
        * cmds.c (nonundocount): New global variable.
        (keys_of_cmds): Initialize it.
-       (Fself_insert_command): Use it to combine upto 20 sequential chars
+       (Fself_insert_command): Use it to combine up to 20 sequential chars
        into a single undo entry, just like the Qself_insert_command code in
        keyboard.c does.
        Call frame_make_pointer_invisible, also like the Qself_insert_command
diff --git a/src/ChangeLog.12 b/src/ChangeLog.12
index 18618bbfb2..7f77c0ca07 100644
--- a/src/ChangeLog.12
+++ b/src/ChangeLog.12
@@ -73,7 +73,7 @@
 
        * lisp.h (adjust_after_replace): Extern it.
 
-       * coding.c (detect_coding): Cound the heading ASCII bytes in the
+       * coding.c (detect_coding): Count the heading ASCII bytes in the
        case of detection for coding_category_utf_8_auto.
        (decode_coding_gap) [not CODING_DISABLE_ASCII_OPTIMIZATION]:
        Skip decoding if all bytes are ASCII.
@@ -1809,7 +1809,7 @@
 
        * nsfns.m (Fns_do_applescript): Run event loop until script has
        been executed (Bug#12969).
-       (ns_run_ascript): Chech as_script for nil, set to nil after
+       (ns_run_ascript): Check as_script for nil, set to nil after
        executing script.
 
 2012-12-22  Martin Rudalics  <rudalics@gmx.at>
@@ -14132,7 +14132,7 @@
        (coding_set_destination): Return how many bytes
        coding->destination was relocated.
        (CODING_DECODE_CHAR, CODING_ENCODE_CHAR, CODING_CHAR_CHARSET)
-       (CODING_CHAR_CHARSET_P): Adjust for the avove changes.
+       (CODING_CHAR_CHARSET_P): Adjust for the above changes.
 
 2011-12-05  Kazuhiro Ito  <kzhr@d1.dion.ne.jp>  (tiny change)
 
@@ -19967,7 +19967,7 @@
 2011-05-05  Eli Zaretskii  <eliz@gnu.org>
 
        * w32heap.c (allocate_heap) [USE_LISP_UNION_TYPE || USE_LSB_TAG]:
-       New version that can reserve upto 2GB of heap space.
+       New version that can reserve up to 2GB of heap space.
 
 2011-05-05  Chong Yidong  <cyd@stupidchicken.com>
 
diff --git a/src/ChangeLog.13 b/src/ChangeLog.13
index 6eb54dfb2c..91f8005ac5 100644
--- a/src/ChangeLog.13
+++ b/src/ChangeLog.13
@@ -1459,11 +1459,11 @@
        (frame_default_tool_bar_height): Extern.
        * gtkutil.c (xg_frame_set_char_size): Pass Qxg_frame_set_char_size
        to adjust_frame_size.
-       * nsfns.m (Fx_create_frame): Pass Pass Qx_create_frame_1 and
+       * nsfns.m (Fx_create_frame): Pass Qx_create_frame_1 and
        Qx_create_frame_2 to adjust_frame_size.
        * w32fns.c (x_change_tool_bar_height): Call adjust_frame_size with
        inhibit 1 when we have not redisplayed the tool bar yet.
-       (Fx_create_frame): Pass Pass Qx_create_frame_1 and
+       (Fx_create_frame): Pass Qx_create_frame_1 and
        Qx_create_frame_2 to adjust_frame_size.
        * w32menu.c (set_frame_menubar): Simplify adjust_frame_size
        call.
@@ -1476,7 +1476,7 @@
        frame size accordingly.
        * xfns.c (x_change_tool_bar_height): Call adjust_frame_size with
        inhibit 1 when we have not redisplayed the tool bar yet.
-       (Fx_create_frame): Pass Pass Qx_create_frame_1 and
+       (Fx_create_frame): Pass Qx_create_frame_1 and
        Qx_create_frame_2 to adjust_frame_size.
 
 2015-01-12  Paul Eggert  <eggert@cs.ucla.edu>
@@ -7498,7 +7498,7 @@
 2014-04-16  Eli Zaretskii  <eliz@gnu.org>
 
        * insdel.c (invalidate_buffer_caches): When deleting or replacing
-       text, invalidate the bidi_paragraph_cache upto and including the
+       text, invalidate the bidi_paragraph_cache up to and including the
        preceding newline.
 
 2014-04-16  Paul Eggert  <eggert@cs.ucla.edu>
@@ -10868,7 +10868,7 @@
 
        * xdisp.c (syms_of_xdisp): New vars redisplay--all-windows-cause and
        redisplay--mode-lines-cause.
-       (redisplay_internal): Keep them uptodate.  Remove redundant check of
+       (redisplay_internal): Keep them up-to-date.  Remove redundant check of
        buffer_shared_and_changed.
        * *.[chm]: Number every assignment to update_mode_lines so we
        can track why it is set.
@@ -12566,7 +12566,7 @@
 2013-09-16  Dmitry Antipov  <dmantipov@yandex.ru>
 
        Do not copy X event in handle_one_xevent except KeyPress case.
-       Wnen XEvent is processed, it is unlikely to be changed except
+       When XEvent is processed, it is unlikely to be changed except
        KeyPress case, so we can avoid copying and use const pointer to
        const data to make sure that an event is not changed elsewhere.
        * xterm.c (handle_one_xevent): Change 2nd arg to 'const XEvent *
diff --git a/src/ChangeLog.5 b/src/ChangeLog.5
index c74e44d7a2..408a934ce2 100644
--- a/src/ChangeLog.5
+++ b/src/ChangeLog.5
@@ -2316,7 +2316,7 @@
 
 1995-02-15  Paul Reilly  <pmr@geech.gnu.ai.mit.edu>
 
-       * s/dgux.h (LIB_MOTIF): Add -lgen to provide provide the symbols
+       * s/dgux.h (LIB_MOTIF): Add -lgen to provide the symbols
        `regcmp' and `regex'.
 
 1995-02-15  Richard Stallman  <rms@pogo.gnu.ai.mit.edu>
diff --git a/src/ChangeLog.6 b/src/ChangeLog.6
index fc7cc5e4d4..f5653efd91 100644
--- a/src/ChangeLog.6
+++ b/src/ChangeLog.6
@@ -2225,7 +2225,7 @@
 
 1996-02-08  Eli Zaretskii  <eliz@is.elta.co.il>
 
-       * fileio.c (Fmake_temp_name) [MS-DOS]: Allow upto 8 characters in
+       * fileio.c (Fmake_temp_name) [MS-DOS]: Allow up to 8 characters in
        the prefix of the temporary file name.
 
 1996-02-07  Richard Stallman  <rms@mole.gnu.ai.mit.edu>
diff --git a/src/ChangeLog.8 b/src/ChangeLog.8
index ef2472a0f3..c0e3523c64 100644
--- a/src/ChangeLog.8
+++ b/src/ChangeLog.8
@@ -1272,7 +1272,7 @@
 
        * xdisp.c (display_line): Set charpos of first glyph in blank
        lines not corresponding to any text to -1, even if no glyphs are
-       filled in in that line.
+       filled in on that line.
 
 1999-11-01  Gerd Moellmann  <gerd@gnu.org>
 
@@ -3155,7 +3155,7 @@
 
        * xdisp.c (resize_mini_window): Don't resize if
        Vmax_mini_window_height is nil.  Otherwise, use a default if
-       Vmax_mini_window_height is not ot a number.
+       Vmax_mini_window_height is not a number.
        (syms_of_xdisp): Extend documentation of Vmax_mini_window_height.
 
 1999-08-25  Alexandre Oliva  <oliva@dcc.unicamp.br>
@@ -5704,7 +5704,7 @@
        (x_scroll_bar_expose): Make no-op for toolkit scroll bars.
        (x_scroll_bar_create): Create and show a scroll bar widget
        if using toolkit scroll bars.
-       (x_scroll_bar_move): Handle tookit scroll bars.
+       (x_scroll_bar_move): Handle toolkit scroll bars.
 
        * Makefile.in (LIBW): Use Xaw3d if present.
 
diff --git a/src/Makefile.in b/src/Makefile.in
index 0ccda63284..5d6fe6ebfd 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -343,6 +343,9 @@ JSON_LIBS = @JSON_LIBS@
 JSON_CFLAGS = @JSON_CFLAGS@
 JSON_OBJ = @JSON_OBJ@
 
+TREE_SITTER_LIBS = @TREE_SITTER_LIBS@
+TREE_SITTER_CFLAGS = @TREE_SITTER_CFLAGS@
+
 INTERVALS_H = dispextern.h intervals.h composite.h
 
 GETLOADAVG_LIBS = @GETLOADAVG_LIBS@
@@ -406,7 +409,7 @@ EMACS_CFLAGS=-Demacs $(MYCPPFLAGS) -I. -I$(srcdir) \
   $(XINPUT_CFLAGS) $(WEBP_CFLAGS) $(WEBKIT_CFLAGS) $(LCMS2_CFLAGS) \
   $(SETTINGS_CFLAGS) $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) \
   $(HARFBUZZ_CFLAGS) $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \
-  $(LIBSYSTEMD_CFLAGS) $(JSON_CFLAGS) $(XSYNC_CFLAGS) \
+  $(LIBSYSTEMD_CFLAGS) $(JSON_CFLAGS) $(XSYNC_CFLAGS) $(TREE_SITTER_CFLAGS) \
   $(LIBGNUTLS_CFLAGS) $(NOTIFY_CFLAGS) $(CAIRO_CFLAGS) \
   $(WERROR_CFLAGS) $(HAIKU_CFLAGS) $(XCOMPOSITE_CFLAGS) $(XSHAPE_CFLAGS)
 ALL_CFLAGS = $(EMACS_CFLAGS) $(WARN_CFLAGS) $(CFLAGS)
@@ -441,8 +444,8 @@ base_obj = dispnew.o frame.o scroll.o xdisp.o menu.o 
$(XMENU_OBJ) window.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                                          \
        pkg.o                                                                  \
+       thread.o systhread.o sqlite.o  treesit.o                               \
        itree.o                                                                \
        $(if $(HYBRID_MALLOC),sheap.o)                                         \
        $(MSDOS_OBJ) $(MSDOS_X_OBJ) $(NS_OBJ) $(CYGWIN_OBJ) $(FONT_OBJ)        \
@@ -567,7 +570,7 @@ LIBES = $(LIBS) $(W32_LIBS) $(LIBS_GNUSTEP) $(PGTK_LIBS) 
$(LIBX_BASE) $(LIBIMAGE
    $(LIBGNUTLS_LIBS) $(LIB_PTHREAD) $(GETADDRINFO_A_LIBS) $(LCMS2_LIBS) \
    $(NOTIFY_LIBS) $(LIB_MATH) $(LIBZ) $(LIBMODULES) $(LIBSYSTEMD_LIBS) \
    $(JSON_LIBS) $(LIBGMP) $(LIBGCCJIT_LIBS) $(XINPUT_LIBS) $(HAIKU_LIBS) \
-   $(SQLITE3_LIBS) $(XCOMPOSITE_LIBS) $(XSHAPE_LIBS)
+   $(TREE_SITTER_LIBS) $(SQLITE3_LIBS) $(XCOMPOSITE_LIBS) $(XSHAPE_LIBS)
 
 ## FORCE it so that admin/unidata can decide whether this file is
 ## up-to-date.  Although since charprop depends on bootstrap-emacs,
diff --git a/src/alloc.c b/src/alloc.c
index 03199f69d3..8dce3996c7 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -50,6 +50,10 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include TERM_HEADER
 #endif /* HAVE_WINDOW_SYSTEM */
 
+#ifdef HAVE_TREE_SITTER
+#include "treesit.h"
+#endif
+
 #include <flexmember.h>
 #include <verify.h>
 #include <execinfo.h>           /* For backtrace.  */
@@ -3215,6 +3219,12 @@ cleanup_vector (struct Lisp_Vector *vector)
       if (uptr->finalizer)
        uptr->finalizer (uptr->p);
     }
+#ifdef HAVE_TREE_SITTER
+  else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_TS_PARSER))
+    treesit_delete_parser (PSEUDOVEC_STRUCT (vector, Lisp_TS_Parser));
+  else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_TS_COMPILED_QUERY))
+    treesit_delete_query (PSEUDOVEC_STRUCT (vector, Lisp_TS_Query));
+#endif
 #ifdef HAVE_MODULES
   else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_MODULE_FUNCTION))
     {
@@ -6075,11 +6085,6 @@ 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 ();
@@ -7574,13 +7579,23 @@ allocated since the last garbage collection.  All data 
types count.
 Garbage collection happens automatically only when `eval' is called.
 
 By binding this temporarily to a large number, you can effectively
-prevent garbage collection during a part of the program.
+prevent garbage collection during a part of the program.  But be
+sure to get back to the normal value soon enough, to avoid system-wide
+memory pressure, and never use a too-high value for prolonged periods
+of time.
 See also `gc-cons-percentage'.  */);
 
   DEFVAR_LISP ("gc-cons-percentage", Vgc_cons_percentage,
               doc: /* Portion of the heap used for allocation.
 Garbage collection can happen automatically once this portion of the heap
 has been allocated since the last garbage collection.
+
+By binding this temporarily to a large number, you can effectively
+prevent garbage collection during a part of the program.  But be
+sure to get back to the normal value soon enough, to avoid system-wide
+memory pressure, and never use a too-high value for prolonged periods
+of time.
+
 If this portion is smaller than `gc-cons-threshold', this is ignored.  */);
   Vgc_cons_percentage = make_float (0.1);
 
diff --git a/src/buffer.c b/src/buffer.c
index 9be2c4a970..ac7f4f8e9d 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -231,6 +231,13 @@ bset_extra_line_spacing (struct buffer *b, Lisp_Object val)
 {
   b->extra_line_spacing_ = val;
 }
+#ifdef HAVE_TREE_SITTER
+static void
+bset_ts_parser_list (struct buffer *b, Lisp_Object val)
+{
+  b->ts_parser_list_ = val;
+}
+#endif
 static void
 bset_file_format (struct buffer *b, Lisp_Object val)
 {
@@ -937,19 +944,16 @@ delete_all_overlays (struct buffer *b)
   if (! b->overlays)
     return;
 
-  /* 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)
+  /* The general rule is that the tree cannot be modified from within
+     ITREE_FOREACH, but here we bend this rule a little because we know
+     that the POST_ORDER iterator will not need to look at `node` again.  */
+  ITREE_FOREACH (node, b->overlays, PTRDIFF_MIN, PTRDIFF_MAX, POST_ORDER)
     {
       modify_overlay (b, node->begin, node->end);
-      /* Where are the nodes freed ? --ap */
       XOVERLAY (node->data)->buffer = NULL;
+      node->parent = NULL;
+      node->left = NULL;
+      node->right = NULL;
     }
   itree_clear (b->overlays);
 }
@@ -1061,6 +1065,9 @@ reset_buffer (register struct buffer *b)
     (b, BVAR (&buffer_defaults, enable_multibyte_characters));
   bset_cursor_type (b, BVAR (&buffer_defaults, cursor_type));
   bset_extra_line_spacing (b, BVAR (&buffer_defaults, extra_line_spacing));
+#ifdef HAVE_TREE_SITTER
+  bset_ts_parser_list (b, Qnil);
+#endif
 
   b->display_error_modiff = 0;
 }
@@ -2985,17 +2992,13 @@ overlays_in (ptrdiff_t beg, ptrdiff_t end, bool extend,
       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;
-            }
+            break;
           if (empty && node->begin != node->end)
             continue;
         }
@@ -3050,7 +3053,6 @@ next_overlay_change (ptrdiff_t pos)
              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)
@@ -3155,10 +3157,7 @@ overlay_touches_p (ptrdiff_t pos)
      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 true;
   return false;
 }
 
@@ -4692,6 +4691,9 @@ init_buffer_once (void)
   XSETFASTINT (BVAR (&buffer_local_flags, tab_line_format), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, cursor_type), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, extra_line_spacing), idx); ++idx;
+#ifdef HAVE_TREE_SITTER
+  XSETFASTINT (BVAR (&buffer_local_flags, ts_parser_list), idx); ++idx;
+#endif
   XSETFASTINT (BVAR (&buffer_local_flags, cursor_in_non_selected_windows), 
idx); ++idx;
 
   /* buffer_local_flags contains no pointers, so it's safe to treat it
@@ -4760,6 +4762,9 @@ init_buffer_once (void)
   bset_bidi_paragraph_separate_re (&buffer_defaults, Qnil);
   bset_cursor_type (&buffer_defaults, Qt);
   bset_extra_line_spacing (&buffer_defaults, Qnil);
+#ifdef HAVE_TREE_SITTER
+  bset_ts_parser_list (&buffer_defaults, Qnil);
+#endif
   bset_cursor_in_non_selected_windows (&buffer_defaults, Qt);
 
   bset_enable_multibyte_characters (&buffer_defaults, Qt);
diff --git a/src/buffer.h b/src/buffer.h
index 2e80c8a7b0..dded0cd98c 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -573,6 +573,10 @@ struct buffer
      in the display of this buffer.  */
   Lisp_Object extra_line_spacing_;
 
+#ifdef HAVE_TREE_SITTER
+  /* A list of tree-sitter parsers for this buffer.  */
+  Lisp_Object ts_parser_list_;
+#endif
   /* Cursor type to display in non-selected windows.
      t means to use hollow box cursor.
      See `cursor-type' for other values.  */
diff --git a/src/casefiddle.c b/src/casefiddle.c
index 2ea5f09b4c..de2325ebf3 100644
--- a/src/casefiddle.c
+++ b/src/casefiddle.c
@@ -30,6 +30,10 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include "composite.h"
 #include "keymap.h"
 
+#ifdef HAVE_TREE_SITTER
+#include "treesit.h"
+#endif
+
 enum case_action {CASE_UP, CASE_DOWN, CASE_CAPITALIZE, CASE_CAPITALIZE_UP};
 
 /* State for casing individual characters.  */
@@ -530,6 +534,11 @@ casify_region (enum case_action flag, Lisp_Object b, 
Lisp_Object e)
   modify_text (start, end);
   prepare_casing_context (&ctx, flag, true);
 
+#ifdef HAVE_TREE_SITTER
+  ptrdiff_t start_byte = CHAR_TO_BYTE (start);
+  ptrdiff_t old_end_byte = CHAR_TO_BYTE (end);
+#endif
+
   ptrdiff_t orig_end = end;
   record_delete (start, make_buffer_string (start, end, true), false);
   if (NILP (BVAR (current_buffer, enable_multibyte_characters)))
@@ -548,6 +557,9 @@ casify_region (enum case_action flag, Lisp_Object b, 
Lisp_Object e)
     {
       signal_after_change (start, end - start - added, end - start);
       update_compositions (start, end, CHECK_ALL);
+#ifdef HAVE_TREE_SITTER
+      treesit_record_change (start_byte, old_end_byte, CHAR_TO_BYTE (end));
+#endif
     }
 
   return orig_end + added;
diff --git a/src/comp.c b/src/comp.c
index 14012634cc..b6072a866e 100644
--- a/src/comp.c
+++ b/src/comp.c
@@ -947,7 +947,7 @@ obj_to_reloc (Lisp_Object obj)
     }
 
   xsignal1 (Qnative_ice,
-           build_string ("cant't find data in relocation containers"));
+           build_string ("can't find data in relocation containers"));
   assume (false);
 
  found:
@@ -5609,7 +5609,7 @@ file_in_eln_sys_dir (Lisp_Object filename)
 /* Load related routines.  */
 DEFUN ("native-elisp-load", Fnative_elisp_load, Snative_elisp_load, 1, 2, 0,
        doc: /* Load native elisp code FILENAME.
-LATE_LOAD has to be non-nil when loading for deferred compilation.  */)
+LATE-LOAD has to be non-nil when loading for deferred compilation.  */)
   (Lisp_Object filename, Lisp_Object late_load)
 {
   CHECK_STRING (filename);
diff --git a/src/data.c b/src/data.c
index dbf9f029b5..f8b728a480 100644
--- a/src/data.c
+++ b/src/data.c
@@ -262,6 +262,12 @@ for example, (type-of 1) returns `integer'.  */)
           return Qxwidget;
         case PVEC_XWIDGET_VIEW:
           return Qxwidget_view;
+       case PVEC_TS_PARSER:
+         return Qtreesit_parser;
+       case PVEC_TS_NODE:
+         return Qtreesit_node;
+       case PVEC_TS_COMPILED_QUERY:
+         return Qtreesit_compiled_query;
         case PVEC_SQLITE:
           return Qsqlite;
         /* "Impossible" cases.  */
@@ -4283,6 +4289,9 @@ syms_of_data (void)
   DEFSYM (Qterminal, "terminal");
   DEFSYM (Qxwidget, "xwidget");
   DEFSYM (Qxwidget_view, "xwidget-view");
+  DEFSYM (Qtreesit_parser, "treesit-parser");
+  DEFSYM (Qtreesit_node, "treesit-node");
+  DEFSYM (Qtreesit_compiled_query, "treesit-compiled-query");
 
   DEFSYM (Qdefun, "defun");
 
diff --git a/src/dbusbind.c b/src/dbusbind.c
index 1c74180f15..440142757e 100644
--- a/src/dbusbind.c
+++ b/src/dbusbind.c
@@ -422,7 +422,7 @@ xd_signature (char *signature, int dtype, int parent_type, 
Lisp_Object object)
     case DBUS_TYPE_STRING:
     case DBUS_TYPE_OBJECT_PATH:
     case DBUS_TYPE_SIGNATURE:
-      /* We dont check the syntax of signature.  This will be done by
+      /* We don't check the syntax of signature.  This will be done by
         libdbus.  */
       if (dtype == DBUS_TYPE_OBJECT_PATH)
        XD_DBUS_VALIDATE_PATH (object)
@@ -748,7 +748,7 @@ xd_append_arg (int dtype, Lisp_Object object, 
DBusMessageIter *iter)
       case DBUS_TYPE_STRING:
       case DBUS_TYPE_OBJECT_PATH:
       case DBUS_TYPE_SIGNATURE:
-       /* We dont check the syntax of signature.  This will be done
+       /* We don't check the syntax of signature.  This will be done
           by libdbus.  */
        if (dtype == DBUS_TYPE_OBJECT_PATH)
          XD_DBUS_VALIDATE_PATH (object)
diff --git a/src/dispextern.h b/src/dispextern.h
index 2f5f4335fe..2afbdeabaa 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -3495,7 +3495,8 @@ extern bool cursor_in_mouse_face_p (struct window *w);
 extern void tty_draw_row_with_mouse_face (struct window *, struct glyph_row *,
                                          int, int, enum draw_glyphs_face);
 extern void display_tty_menu_item (const char *, int, int, int, int, bool);
-
+extern struct glyph *x_y_to_hpos_vpos (struct window *, int, int, int *, int *,
+                                      int *, int *, int *);
 /* Flags passed to try_window.  */
 #define TRY_WINDOW_CHECK_MARGINS       (1 << 0)
 #define TRY_WINDOW_IGNORE_FONTS_CHANGE (1 << 1)
diff --git a/src/emacs.c b/src/emacs.c
index eba4cf78ad..fe898f988d 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -137,6 +137,10 @@ extern char etext;
 #include <sys/resource.h>
 #endif
 
+/* We don't guard this with HAVE_TREE_SITTER because treesit.o is
+   always compiled (to provide treesit-available-p).  */
+#include "treesit.h"
+
 #include "pdumper.h"
 #include "fingerprint.h"
 #include "epaths.h"
@@ -1936,7 +1940,6 @@ 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 ();
@@ -2271,7 +2274,9 @@ Using an Emacs configured with --with-x-toolkit=lucid 
does not have this problem
 #ifdef HAVE_MODULES
       syms_of_module ();
 #endif
-
+      /* We don't guard this with HAVE_TREE_SITTER because treesit.o
+        is always compiled (to provide treesit-available-p).  */
+      syms_of_treesit ();
 #ifdef HAVE_SOUND
       syms_of_sound ();
 #endif
@@ -3113,8 +3118,6 @@ 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 ea23829948..99f3650fc9 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -1329,7 +1329,7 @@ Then the value of the last BODY form is returned from the 
`condition-case'
 expression.
 
 The special handler (:success BODY...) is invoked if BODYFORM terminated
-without signalling an error.  BODY is then evaluated with VAR bound to
+without signaling an error.  BODY is then evaluated with VAR bound to
 the value returned by BODYFORM.
 
 See also the function `signal' for more info.
@@ -1716,7 +1716,6 @@ 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 ();
 
@@ -1810,7 +1809,7 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object 
data, bool keyboard_quit)
       unbind_to (count, Qnil);
     }
 
-  /* If an error is signalled during a Lisp hook in redisplay, write a
+  /* If an error is signaled during a Lisp hook in redisplay, write a
      backtrace into the buffer *Redisplay-trace*.  */
   if (!debugger_called && !NILP (error_symbol)
       && backtrace_on_redisplay_error
@@ -1903,6 +1902,19 @@ signal_error (const char *s, Lisp_Object arg)
   xsignal (Qerror, Fcons (build_string (s), arg));
 }
 
+void
+define_error (Lisp_Object name, const char *message, Lisp_Object parent)
+{
+  eassert (SYMBOLP (name));
+  eassert (SYMBOLP (parent));
+  Lisp_Object parent_conditions = Fget (parent, Qerror_conditions);
+  eassert (CONSP (parent_conditions));
+  eassert (!NILP (Fmemq (parent, parent_conditions)));
+  eassert (NILP (Fmemq (name, parent_conditions)));
+  Fput (name, Qerror_conditions, pure_cons (name, parent_conditions));
+  Fput (name, Qerror_message, build_pure_c_string (message));
+}
+
 /* Use this for arithmetic overflow, e.g., when an integer result is
    too large even for a bignum.  */
 void
diff --git a/src/font.h b/src/font.h
index 3475189206..d36c45a53c 100644
--- a/src/font.h
+++ b/src/font.h
@@ -220,13 +220,13 @@ enum font_property_index
 #define FONT_WIDTH_FOR_FACE(font)      \
   font_style_symbolic (font, FONT_WIDTH_INDEX, true)
 
-/* Return the numeric weight value corresponding ot the symbol NAME.  */
+/* Return the numeric weight value corresponding to the symbol NAME.  */
 #define FONT_WEIGHT_NAME_NUMERIC(name) \
   (font_style_to_value (FONT_WEIGHT_INDEX, (name), false) >> 8)
-/* Return the numeric slant value corresponding ot the symbol NAME.  */
+/* Return the numeric slant value corresponding to the symbol NAME.  */
 #define FONT_SLANT_NAME_NUMERIC(name)  \
   (font_style_to_value (FONT_SLANT_INDEX, (name), false) >> 8)
-/* Return the numeric width value corresponding ot the symbol NAME.  */
+/* Return the numeric width value corresponding to the symbol NAME.  */
 #define FONT_WIDTH_NAME_NUMERIC(name)  \
   (font_style_to_value (FONT_WIDTH_INDEX, (name), false) >> 8)
 
diff --git a/src/fontset.c b/src/fontset.c
index 4b91eff2ef..b82737d005 100644
--- a/src/fontset.c
+++ b/src/fontset.c
@@ -1663,7 +1663,17 @@ overwrites the previous settings.  */)
            {
              update_auto_fontset_alist (font_object, fontset_obj);
              AUTO_FRAME_ARG (arg, Qfont, Fcons (fontset, font_object));
-             Fmodify_frame_parameters (fr, arg);
+
+#ifdef HAVE_WINDOW_SYSTEM
+             if (FRAME_WINDOW_P (f))
+               /* This is a window-system frame.  Prevent changes of
+                  the `font' parameter here from messing with the
+                  `font-parameter' frame property, as the frame
+                  parameter is not being changed by the user.  */
+               gui_set_frame_parameters_1 (f, arg, true);
+             else
+#endif
+               Fmodify_frame_parameters (fr, arg);
            }
        }
     }
diff --git a/src/frame.c b/src/frame.c
index f076a5ba54..b57b296be5 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -4119,10 +4119,17 @@ frame_float (struct frame *f, Lisp_Object val, enum 
frame_float_type what,
    If a parameter is not specially recognized, do nothing special;
    otherwise call the `gui_set_...' function for that parameter.
    Except for certain geometry properties, always call store_frame_param
-   to store the new value in the parameter alist.  */
+   to store the new value in the parameter alist.
+
+   DEFAULT_PARAMETER should be set if the alist was not specified by
+   the user, or by the face code to set the `font' parameter.  In that
+   case, the `font-parameter' frame parameter should not be changed,
+   so dynamic-setting.el can restore the user's selected font
+   correctly.  */
 
 void
-gui_set_frame_parameters (struct frame *f, Lisp_Object alist)
+gui_set_frame_parameters_1 (struct frame *f, Lisp_Object alist,
+                           bool default_parameter)
 {
   Lisp_Object tail, frame;
 
@@ -4249,7 +4256,7 @@ gui_set_frame_parameters (struct frame *f, Lisp_Object 
alist)
        }
       else
        {
-         register Lisp_Object param_index, old_value;
+         Lisp_Object param_index, old_value;
 
          old_value = get_frame_param (f, prop);
 
@@ -4260,6 +4267,12 @@ gui_set_frame_parameters (struct frame *f, Lisp_Object 
alist)
              && XFIXNAT (param_index) < ARRAYELTS (frame_parms)
              && FRAME_RIF (f)->frame_parm_handlers[XFIXNUM (param_index)])
            (*(FRAME_RIF (f)->frame_parm_handlers[XFIXNUM (param_index)])) (f, 
val, old_value);
+
+         if (!default_parameter && EQ (prop, Qfont))
+           /* The user manually specified the `font' frame parameter.
+              Save that parameter for future use by the
+              dynamic-setting code.  */
+           store_frame_param (f, Qfont_parameter, val);
        }
     }
 
@@ -4410,6 +4423,11 @@ gui_set_frame_parameters (struct frame *f, Lisp_Object 
alist)
   SAFE_FREE ();
 }
 
+void
+gui_set_frame_parameters (struct frame *f, Lisp_Object alist)
+{
+  gui_set_frame_parameters_1 (f, alist, false);
+}
 
 /* Insert a description of internally-recorded parameters of frame F
    into the parameter alist *ALISTPTR that is to be given to the user.
@@ -4586,9 +4604,6 @@ gui_set_font (struct frame *f, Lisp_Object arg, 
Lisp_Object oldval)
 {
   Lisp_Object font_object;
   int fontset = -1;
-#ifdef HAVE_X_WINDOWS
-  Lisp_Object font_param = arg;
-#endif
 
   /* Set the frame parameter back to the old value because we may
      fail to use ARG as the new parameter value.  */
@@ -4627,16 +4642,10 @@ gui_set_font (struct frame *f, Lisp_Object arg, 
Lisp_Object oldval)
        error ("Unknown fontset: %s", SDATA (XCAR (arg)));
       font_object = XCDR (arg);
       arg = AREF (font_object, FONT_NAME_INDEX);
-#ifdef HAVE_X_WINDOWS
-      font_param = Ffont_get (font_object, QCname);
-#endif
     }
   else if (FONT_OBJECT_P (arg))
     {
       font_object = arg;
-#ifdef HAVE_X_WINDOWS
-      font_param = Ffont_get (font_object, QCname);
-#endif
       /* This is to store the XLFD font name in the frame parameter for
         backward compatibility.  We should store the font-object
         itself in the future.  */
@@ -4667,9 +4676,7 @@ gui_set_font (struct frame *f, Lisp_Object arg, 
Lisp_Object oldval)
   if (FRAME_TERMINAL (f)->set_new_font_hook)
     FRAME_TERMINAL (f)->set_new_font_hook (f, font_object, fontset);
   store_frame_param (f, Qfont, arg);
-#ifdef HAVE_X_WINDOWS
-  store_frame_param (f, Qfont_parameter, font_param);
-#endif
+
   /* Recalculate tabbar height.  */
   f->n_tab_bar_rows = 0;
   /* Recalculate toolbar height.  */
@@ -4749,7 +4756,7 @@ gui_set_font_backend (struct frame *f, Lisp_Object 
new_value, Lisp_Object old_va
   if (FRAME_FONT (f))
     {
       /* Reconsider default font after backend(s) change (Bug#23386).  */
-      FRAME_RIF(f)->default_font_parameter (f, Qnil);
+      FRAME_RIF (f)->default_font_parameter (f, Qnil);
       face_change = true;
       windows_or_buffers_changed = 18;
     }
@@ -5451,12 +5458,20 @@ gui_default_parameter (struct frame *f, Lisp_Object 
alist, Lisp_Object prop,
                        enum resource_types type)
 {
   Lisp_Object tem;
+  bool was_unbound;
 
   tem = gui_frame_get_arg (f, alist, prop, xprop, xclass, type);
+
   if (BASE_EQ (tem, Qunbound))
-    tem = deflt;
+    {
+      tem = deflt;
+      was_unbound = true;
+    }
+  else
+    was_unbound = false;
+
   AUTO_FRAME_ARG (arg, prop, tem);
-  gui_set_frame_parameters (f, arg);
+  gui_set_frame_parameters_1 (f, arg, was_unbound);
   return tem;
 }
 
@@ -5946,6 +5961,67 @@ This function is for internal use only.  */)
 
   return f->was_invisible ? Qt : Qnil;
 }
+
+#ifdef HAVE_WINDOW_SYSTEM
+
+DEFUN ("reconsider-frame-fonts", Freconsider_frame_fonts,
+       Sreconsider_frame_fonts, 1, 1, 0,
+       doc: /* Recreate FRAME's default font using updated font parameters.
+Signal an error if FRAME is not a window system frame.  This should be
+called after a `config-changed' event is received, signaling that the
+parameters (such as pixel density) used by the system to open fonts
+have changed.  */)
+  (Lisp_Object frame)
+{
+  struct frame *f;
+  Lisp_Object params, font_parameter;
+
+  f = decode_window_system_frame (frame);
+
+  /* Kludge: if a `font' parameter was already specified,
+     create an alist containing just that parameter.  (bug#59371)
+
+     This sounds so simple, right?  Well, read on below: */
+  params = Qnil;
+
+  /* The difference between Qfont and Qfont_parameter is that the
+     latter is not set automatically by the likes of x_new_font, and
+     implicitly as the default face is realized.  It is only set when
+     the user specifically specifies a `font' frame parameter, and is
+     cleared the moment the frame's font becomes defined by a face
+     attribute, instead of through the `font' frame parameter.  */
+  font_parameter = get_frame_param (f, Qfont_parameter);
+
+  if (!NILP (font_parameter))
+    params = list1 (Fcons (Qfont, font_parameter));
+
+  /* First, call this to reinitialize any font backend specific
+     stuff.  */
+
+  if (FRAME_RIF (f)->default_font_parameter)
+    FRAME_RIF (f)->default_font_parameter (f, params);
+
+  /* For a mysterious reason, x_default_font_parameter sets Qfont to
+     nil in the alist!  */
+
+  if (!NILP (font_parameter))
+    params = list1 (Fcons (Qfont, font_parameter));
+
+  /* Now call this to apply the existing value(s) of the `default'
+     face.  */
+  call2 (Qface_set_after_frame_default, frame, params);
+
+  /* Restore the value of the `font-parameter' parameter, as
+     `face-set-after-frame-default' will have changed it through its
+     calls to `set-face-attribute'.  */
+  if (!NILP (font_parameter))
+    store_frame_param (f, Qfont_parameter, font_parameter);
+
+  return Qnil;
+}
+
+#endif
+
 
 /***********************************************************************
                        Multimonitor data
@@ -6201,6 +6277,7 @@ syms_of_frame (void)
   DEFSYM (Qiconify_top_level, "iconify-top-level");
   DEFSYM (Qmake_invisible, "make-invisible");
   DEFSYM (Quse_frame_synchronization, "use-frame-synchronization");
+  DEFSYM (Qfont_parameter, "font-parameter");
 
   {
     int i;
@@ -6634,6 +6711,6 @@ iconify the top level frame instead.  */);
 #ifdef HAVE_WINDOW_SYSTEM
   defsubr (&Sx_get_resource);
   defsubr (&Sx_parse_geometry);
+  defsubr (&Sreconsider_frame_fonts);
 #endif
-
 }
diff --git a/src/frame.h b/src/frame.h
index 458b6257e4..d6fd62b2ac 100644
--- a/src/frame.h
+++ b/src/frame.h
@@ -1670,6 +1670,7 @@ IMAGE_OPT_FROM_ID (struct frame *f, int id)
 /* The class of this X application.  */
 #define EMACS_CLASS "Emacs"
 
+extern void gui_set_frame_parameters_1 (struct frame *, Lisp_Object, bool);
 extern void gui_set_frame_parameters (struct frame *, Lisp_Object);
 extern void gui_set_fullscreen (struct frame *, Lisp_Object, Lisp_Object);
 extern void gui_set_line_spacing (struct frame *, Lisp_Object, Lisp_Object);
diff --git a/src/ftcrfont.c b/src/ftcrfont.c
index dc765e5aee..ede8f1323c 100644
--- a/src/ftcrfont.c
+++ b/src/ftcrfont.c
@@ -737,7 +737,7 @@ struct font_driver const ftcrfont_driver =
   .filter_properties = ftfont_filter_properties,
   .combining_capability = ftfont_combining_capability,
 #ifdef HAVE_PGTK
-  .cached_font_ok = ftcrfont_cached_font_ok
+  .cached_font_ok = ftcrfont_cached_font_ok,
 #endif
   };
 #ifdef HAVE_HARFBUZZ
@@ -755,6 +755,42 @@ syms_of_ftcrfont (void)
   pdumper_do_now_and_after_load (syms_of_ftcrfont_for_pdumper);
 }
 
+#ifdef HAVE_X_WINDOWS
+
+/* Place the default font options used by Cairo on the given display
+   in OPTIONS.  */
+
+void
+ftcrfont_get_default_font_options (struct x_display_info *dpyinfo,
+                                  cairo_font_options_t *options)
+{
+  Pixmap drawable;
+  cairo_surface_t *surface;
+
+  /* Cairo doesn't allow fetching the default font options for a
+     display, so the only option is to create a drawable, and an Xlib
+     surface for that drawable, and to get the font options from there
+     instead.  */
+
+  drawable = XCreatePixmap (dpyinfo->display, dpyinfo->root_window,
+                           1, 1, dpyinfo->n_planes);
+  surface = cairo_xlib_surface_create (dpyinfo->display, drawable,
+                                      dpyinfo->visual, 1, 1);
+
+  if (!surface)
+    {
+      XFreePixmap (dpyinfo->display, drawable);
+      return;
+    }
+
+  cairo_surface_get_font_options (surface, options);
+  XFreePixmap (dpyinfo->display, drawable);
+  cairo_surface_destroy (surface);
+  return;
+}
+
+#endif
+
 static void
 syms_of_ftcrfont_for_pdumper (void)
 {
diff --git a/src/ftfont.h b/src/ftfont.h
index cfab8d3154..ee56e2d760 100644
--- a/src/ftfont.h
+++ b/src/ftfont.h
@@ -84,4 +84,11 @@ struct font_info
 #endif
 };
 
+#if defined USE_CAIRO && defined HAVE_X_WINDOWS
+
+extern void ftcrfont_get_default_font_options (struct x_display_info *,
+                                              cairo_font_options_t *);
+
+#endif /* USE_CAIRO && HAVE_X_WINDOWS */
+
 #endif /* EMACS_FTFONT_H */
diff --git a/src/haiku_support.h b/src/haiku_support.h
index e940e69bf1..2605a75b40 100644
--- a/src/haiku_support.h
+++ b/src/haiku_support.h
@@ -383,7 +383,7 @@ struct haiku_font_pattern
   /* The number of characters in `wanted_chars'.  */
   int want_chars_len;
 
-  /* List of characters.  The font must fullfill at least one of
+  /* List of characters.  The font must fulfill at least one of
      them for the match to succeed.  */
   int *need_one_of;
 
diff --git a/src/haikufns.c b/src/haikufns.c
index 711202c5df..5717d0354f 100644
--- a/src/haikufns.c
+++ b/src/haikufns.c
@@ -175,10 +175,19 @@ haiku_change_tool_bar_height (struct frame *f, int height)
 void
 haiku_change_tab_bar_height (struct frame *f, int height)
 {
-  int unit = FRAME_LINE_HEIGHT (f);
-  int old_height = FRAME_TAB_BAR_HEIGHT (f);
-  int lines = (height + unit - 1) / unit;
-  Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
+  int unit, old_height, lines;
+  Lisp_Object fullscreen;
+
+  unit = FRAME_LINE_HEIGHT (f);
+  old_height = FRAME_TAB_BAR_HEIGHT (f);
+  fullscreen = get_frame_param (f, Qfullscreen);
+
+  /* This differs from the tool bar code in that the tab bar height is
+     not rounded up.  Otherwise, if redisplay_tab_bar decides to grow
+     the tab bar by even 1 pixel, FRAME_TAB_BAR_LINES will be changed,
+     leading to the tab bar height being incorrectly set upon the next
+     call to x_set_font.  (bug#59285) */
+  lines = height / unit;
 
   /* Make sure we redisplay all windows in this frame.  */
   fset_redisplay (f);
diff --git a/src/haikuselect.c b/src/haikuselect.c
index bd004f4900..e8d3b5f0f7 100644
--- a/src/haikuselect.c
+++ b/src/haikuselect.c
@@ -1260,7 +1260,7 @@ syms_of_haikuselect (void)
 {
   DEFVAR_BOOL ("haiku-signal-invalid-refs", haiku_signal_invalid_refs,
     doc: /* If nil, silently ignore invalid file names in system messages.
-Otherwise, an error will be signalled if adding a file reference to a
+Otherwise, an error will be signaled if adding a file reference to a
 system message failed.  */);
   haiku_signal_invalid_refs = true;
 
diff --git a/src/haikuterm.c b/src/haikuterm.c
index 4e32b74716..496480cbc0 100644
--- a/src/haikuterm.c
+++ b/src/haikuterm.c
@@ -3007,9 +3007,11 @@ haiku_default_font_parameter (struct frame *f, 
Lisp_Object parms)
     font = font_open_by_spec (f, Ffont_get_system_font ());
 
   if (NILP (font))
-      font = !NILP (font_param) ? font_param
-      : gui_display_get_arg (dpyinfo, parms, Qfont, "font", "Font",
-                             RES_TYPE_STRING);
+    font = (!NILP (font_param)
+           ? font_param
+           : gui_display_get_arg (dpyinfo, parms, Qfont,
+                                  "font", "Font",
+                                  RES_TYPE_STRING));
 
   if (! FONTP (font) && ! STRINGP (font))
     {
@@ -3029,13 +3031,6 @@ haiku_default_font_parameter (struct frame *f, 
Lisp_Object parms)
       if (NILP (font))
         error ("No suitable font was found");
     }
-  else if (!NILP (font_param))
-    {
-      /* Remember the explicit font parameter, so we can re-apply it
-         after we've applied the `default' face settings.  */
-      AUTO_FRAME_ARG (arg, Qfont_parameter, font_param);
-      gui_set_frame_parameters (f, arg);
-    }
 
   gui_default_parameter (f, parms, Qfont, font, "font", "Font",
                          RES_TYPE_STRING);
diff --git a/src/insdel.c b/src/insdel.c
index 03ce59b340..d483736c03 100644
--- a/src/insdel.c
+++ b/src/insdel.c
@@ -31,6 +31,10 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include "region-cache.h"
 #include "pdumper.h"
 
+#ifdef HAVE_TREE_SITTER
+#include "treesit.h"
+#endif
+
 static void insert_from_string_1 (Lisp_Object, ptrdiff_t, ptrdiff_t, ptrdiff_t,
                                  ptrdiff_t, bool, bool);
 static void insert_from_buffer_1 (struct buffer *, ptrdiff_t, ptrdiff_t, bool);
@@ -938,6 +942,12 @@ insert_1_both (const char *string,
     set_text_properties (make_fixnum (PT), make_fixnum (PT + nchars),
                         Qnil, Qnil, Qnil);
 
+#ifdef HAVE_TREE_SITTER
+  eassert (nbytes >= 0);
+  eassert (PT_BYTE >= 0);
+  treesit_record_change (PT_BYTE, PT_BYTE, PT_BYTE + nbytes);
+#endif
+
   adjust_point (nchars, nbytes);
 
   check_markers ();
@@ -1068,6 +1078,12 @@ insert_from_string_1 (Lisp_Object string, ptrdiff_t pos, 
ptrdiff_t pos_byte,
   graft_intervals_into_buffer (intervals, PT, nchars,
                               current_buffer, inherit);
 
+#ifdef HAVE_TREE_SITTER
+  eassert (nbytes >= 0);
+  eassert (PT_BYTE >= 0);
+  treesit_record_change (PT_BYTE, PT_BYTE, PT_BYTE + nbytes);
+#endif
+
   adjust_point (nchars, outgoing_nbytes);
 
   check_markers ();
@@ -1134,6 +1150,12 @@ insert_from_gap (ptrdiff_t nchars, ptrdiff_t nbytes, 
bool text_at_gap_tail)
                                   current_buffer, 0);
     }
 
+#ifdef HAVE_TREE_SITTER
+  eassert (nbytes >= 0);
+  eassert (ins_bytepos >= 0);
+  treesit_record_change (ins_bytepos, ins_bytepos, ins_bytepos + nbytes);
+#endif
+
   if (ins_charpos < PT)
     adjust_point (nchars, nbytes);
 
@@ -1283,6 +1305,12 @@ insert_from_buffer_1 (struct buffer *buf,
   /* Insert those intervals.  */
   graft_intervals_into_buffer (intervals, PT, nchars, current_buffer, inherit);
 
+#ifdef HAVE_TREE_SITTER
+  eassert (outgoing_nbytes >= 0);
+  eassert (PT_BYTE >= 0);
+  treesit_record_change (PT_BYTE, PT_BYTE, PT_BYTE + outgoing_nbytes);
+#endif
+
   adjust_point (nchars, outgoing_nbytes);
 }
 
@@ -1519,6 +1547,13 @@ replace_range (ptrdiff_t from, ptrdiff_t to, Lisp_Object 
new,
   graft_intervals_into_buffer (intervals, from, inschars,
                               current_buffer, inherit);
 
+#ifdef HAVE_TREE_SITTER
+  eassert (to_byte >= from_byte);
+  eassert (outgoing_insbytes >= 0);
+  eassert (from_byte >= 0);
+  treesit_record_change (from_byte, to_byte, from_byte + outgoing_insbytes);
+#endif
+
   /* Relocate point as if it were a marker.  */
   if (from < PT)
     adjust_point ((from + inschars - (PT < to ? PT : to)),
@@ -1550,7 +1585,11 @@ replace_range (ptrdiff_t from, ptrdiff_t to, Lisp_Object 
new,
    If MARKERS, relocate markers.
 
    Unlike most functions at this level, never call
-   prepare_to_modify_buffer and never call signal_after_change.  */
+   prepare_to_modify_buffer and never call signal_after_change.
+   Because this function is called in a loop, one character at a time.
+   The caller of 'replace_range_2' calls these hooks for the entire
+   region once.  Apart from signal_after_change, any caller of this
+   function should also call treesit_record_change.  */
 
 void
 replace_range_2 (ptrdiff_t from, ptrdiff_t from_byte,
@@ -1856,6 +1895,12 @@ del_range_2 (ptrdiff_t from, ptrdiff_t from_byte,
 
   check_markers ();
 
+#ifdef HAVE_TREE_SITTER
+  eassert (from_byte <= to_byte);
+  eassert (from_byte >= 0);
+  treesit_record_change (from_byte, to_byte, from_byte);
+#endif
+
   return deletion;
 }
 
diff --git a/src/itree.c b/src/itree.c
index ae69c97d6d..04fa9e827a 100644
--- a/src/itree.c
+++ b/src/itree.c
@@ -111,7 +111,7 @@ along with GNU Emacs.  If not, see 
<http://www.gnu.org/licenses/>.  */
    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
+   The OFFSET of 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,
@@ -131,43 +131,20 @@ along with GNU Emacs.  If not, see 
<http://www.gnu.org/licenses/>.  */
  * | 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
+struct itree_stack
 {
-  nodeptr_and_flag *nodes;
+  struct itree_node **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)
+static struct itree_stack*
+itree_stack_create (intmax_t initial_size)
 {
-  struct interval_stack *stack = xmalloc (sizeof (struct interval_stack));
+  struct itree_stack *stack = xmalloc (sizeof (struct itree_stack));
   stack->size = max (0, initial_size);
   stack->nodes = xmalloc (stack->size * sizeof (struct itree_node*));
   stack->length = 0;
@@ -175,7 +152,7 @@ interval_stack_create (intmax_t initial_size)
 }
 
 static void
-interval_stack_destroy (struct interval_stack *stack)
+itree_stack_destroy (struct itree_stack *stack)
 {
   if (! stack)
     return;
@@ -184,14 +161,8 @@ interval_stack_destroy (struct interval_stack *stack)
   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)
+itree_stack_ensure_space (struct itree_stack *stack, uintmax_t nelements)
 {
   if (nelements > stack->size)
     {
@@ -204,104 +175,32 @@ interval_stack_ensure_space (struct interval_stack 
*stack, uintmax_t nelements)
 /* 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)
+itree_stack_push (struct itree_stack *stack, struct itree_node *node)
 {
   eassert (node);
+  itree_stack_ensure_space (stack, stack->length + 1);
 
-  /* 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->nodes[stack->length] = node;
   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)
+static inline struct itree_node *
+itree_stack_pop (struct itree_stack *stack)
 {
   if (stack->length == 0)
-    return make_nav (NULL, false);
+    return NULL;
   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)
+itree_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.  */
@@ -334,7 +233,7 @@ check_subtree (struct itree_node *node,
      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()
+     implies (NODE.offset == 0) because itree_inherit_offset()
      doesn't always update otick.  It could, but it is not clear there
      is a need.  */
   eassert (node->otick <= tree_otick);
@@ -438,7 +337,7 @@ itree_newlimit (struct itree_node *node)
 /* Update NODE's limit attribute according to its children. */
 
 static void
-interval_tree_update_limit (struct itree_node *node)
+itree_update_limit (struct itree_node *node)
 {
   if (node == NULL)
     return;
@@ -453,7 +352,7 @@ interval_tree_update_limit (struct itree_node *node)
 */
 
 static void
-interval_tree_inherit_offset (uintmax_t otick, struct itree_node *node)
+itree_inherit_offset (uintmax_t otick, struct itree_node *node)
 {
   eassert (node->parent == NULL || node->parent->otick >= node->otick);
   if (node->otick == otick)
@@ -490,7 +389,7 @@ interval_tree_inherit_offset (uintmax_t otick, struct 
itree_node *node)
    stable, i.e. new_limit = old_limit.  */
 
 static void
-interval_tree_propagate_limit (struct itree_node *node)
+itree_propagate_limit (struct itree_node *node)
 {
   ptrdiff_t newlimit;
 
@@ -511,15 +410,15 @@ interval_tree_propagate_limit (struct itree_node *node)
 }
 
 static struct itree_node*
-interval_tree_validate (struct itree_tree *tree, struct itree_node *node)
+itree_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);
+    itree_validate (tree, node->parent);
 
-  interval_tree_inherit_offset (tree->otick, node);
+  itree_inherit_offset (tree->otick, node);
   return node;
 }
 
@@ -550,7 +449,7 @@ ptrdiff_t
 itree_node_begin (struct itree_tree *tree,
                  struct itree_node *node)
 {
-  interval_tree_validate (tree, node);
+  itree_validate (tree, node);
   return node->begin;
 }
 
@@ -560,7 +459,7 @@ ptrdiff_t
 itree_node_end (struct itree_tree *tree,
                struct itree_node *node)
 {
-  interval_tree_validate (tree, node);
+  itree_validate (tree, node);
   return node->end;
 }
 
@@ -588,7 +487,7 @@ itree_clear (struct itree_tree *tree)
 /* Initialize a pre-allocated tree (presumably on the stack).  */
 
 static void
-interval_tree_init (struct itree_tree *tree)
+itree_init (struct itree_tree *tree)
 {
   itree_clear (tree);
 }
@@ -613,15 +512,15 @@ itree_size (struct itree_tree *tree)
 /* Perform the familiar left-rotation on node NODE.  */
 
 static void
-interval_tree_rotate_left (struct itree_tree *tree,
+itree_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);
+  itree_inherit_offset (tree->otick, node);
+  itree_inherit_offset (tree->otick, right);
 
   /* Turn right's left subtree into node's right subtree.  */
   node->right = right->left;
@@ -649,22 +548,22 @@ interval_tree_rotate_left (struct itree_tree *tree,
     node->parent = right;
 
   /* Order matters here.  */
-  interval_tree_update_limit (node);
-  interval_tree_update_limit (right);
+  itree_update_limit (node);
+  itree_update_limit (right);
 }
 
 /* Perform the familiar right-rotation on node NODE.  */
 
 static void
-interval_tree_rotate_right (struct itree_tree *tree,
+itree_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);
+  itree_inherit_offset (tree->otick, node);
+  itree_inherit_offset (tree->otick, left);
 
   node->left = left->right;
   if (left->right != NULL)
@@ -686,8 +585,8 @@ interval_tree_rotate_right (struct itree_tree *tree,
   if (node != NULL)
     node->parent = left;
 
-  interval_tree_update_limit (left);
-  interval_tree_update_limit (node);
+  itree_update_limit (left);
+  itree_update_limit (node);
 }
 
 /* Repair the tree after an insertion.
@@ -695,7 +594,7 @@ interval_tree_rotate_right (struct itree_tree *tree,
    Rebalance the parents as needed to re-establish the RB invariants.  */
 
 static void
-interval_tree_insert_fix (struct itree_tree *tree,
+itree_insert_fix (struct itree_tree *tree,
                          struct itree_node *node)
 {
   eassert (tree->root->red == false);
@@ -729,12 +628,12 @@ interval_tree_insert_fix (struct itree_tree *tree,
              if (node == node->parent->right) /* case 2.a */
                {
                  node = node->parent;
-                 interval_tree_rotate_left (tree, node);
+                 itree_rotate_left (tree, node);
                }
              /* case 3.a */
              node->parent->red = false;
              node->parent->parent->red = true;
-             interval_tree_rotate_right (tree, node->parent->parent);
+             itree_rotate_right (tree, node->parent->parent);
            }
        }
       else
@@ -754,12 +653,12 @@ interval_tree_insert_fix (struct itree_tree *tree,
              if (node == node->parent->left) /* case 2.b */
                {
                  node = node->parent;
-                 interval_tree_rotate_right (tree, node);
+                 itree_rotate_right (tree, node);
                }
              /* case 3.b */
              node->parent->red = false;
              node->parent->parent->red = true;
-             interval_tree_rotate_left (tree, node->parent->parent);
+             itree_rotate_left (tree, node->parent->parent);
            }
        }
     }
@@ -774,13 +673,11 @@ interval_tree_insert_fix (struct itree_tree *tree,
    Note, that inserting a node twice results in undefined behavior.  */
 
 static void
-interval_tree_insert (struct itree_tree *tree, struct itree_node *node)
+itree_insert_node (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 (node->left == NULL && node->right == NULL
+          && node->parent == NULL);
   eassert (check_tree (tree, true)); /* FIXME: Too expensive.  */
 
   struct itree_node *parent = NULL;
@@ -794,7 +691,7 @@ interval_tree_insert (struct itree_tree *tree, struct 
itree_node *node)
      ancestors limit values.  */
   while (child != NULL)
     {
-      interval_tree_inherit_offset (otick, child);
+      itree_inherit_offset (otick, child);
       parent = child;
       eassert (child->offset == 0);
       child->limit = max (child->limit, node->end);
@@ -827,7 +724,7 @@ interval_tree_insert (struct itree_tree *tree, struct 
itree_node *node)
     {
       node->red = true;
       eassert (check_tree (tree, false)); /* FIXME: Too expensive.  */
-      interval_tree_insert_fix (tree, node);
+      itree_insert_fix (tree, node);
     }
 }
 
@@ -838,7 +735,7 @@ itree_insert (struct itree_tree *tree, struct itree_node 
*node,
   node->begin = begin;
   node->end = end;
   node->otick = tree->otick;
-  interval_tree_insert (tree, node);
+  itree_insert_node (tree, node);
 }
 
 /* Safely modify a node's interval. */
@@ -848,35 +745,32 @@ itree_node_set_region (struct itree_tree *tree,
                       struct itree_node *node,
                       ptrdiff_t begin, ptrdiff_t end)
 {
-  interval_tree_validate (tree, node);
+  itree_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);
+      itree_insert_node (tree, node);
     }
   else if (end != node->end)
     {
       node->end = max (node->begin, end);
       eassert (node != NULL);
-      interval_tree_propagate_limit (node);
+      itree_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)
+itree_contains (struct itree_tree *tree, struct itree_node *node)
 {
-  eassert (iter && node);
+  eassert (node);
   struct itree_node *other;
   ITREE_FOREACH (other, tree, node->begin, PTRDIFF_MAX, ASCENDING)
     if (other == node)
-      {
-       ITREE_FOREACH_ABORT ();
-       return true;
-      }
+      return true;
 
   return false;
 }
@@ -891,11 +785,11 @@ itree_limit_is_stable (struct itree_node *node)
 }
 
 static struct itree_node*
-interval_tree_subtree_min (uintmax_t otick, struct itree_node *node)
+itree_subtree_min (uintmax_t otick, struct itree_node *node)
 {
   if (node == NULL)
     return node;
-  while ((interval_tree_inherit_offset (otick, node),
+  while ((itree_inherit_offset (otick, node),
          node->left != NULL))
     node = node->left;
   return node;
@@ -906,7 +800,7 @@ interval_tree_subtree_min (uintmax_t otick, struct 
itree_node *node)
    so re-balance the parents to re-establish the RB invariants.  */
 
 static void
-interval_tree_remove_fix (struct itree_tree *tree,
+itree_remove_fix (struct itree_tree *tree,
                          struct itree_node *node,
                          struct itree_node *parent)
 {
@@ -927,7 +821,7 @@ interval_tree_remove_fix (struct itree_tree *tree,
            {
              other->red = false;
              parent->red = true;
-             interval_tree_rotate_left (tree, parent);
+             itree_rotate_left (tree, parent);
              other = parent->right;
            }
          eassume (other != NULL);
@@ -946,13 +840,13 @@ interval_tree_remove_fix (struct itree_tree *tree,
                {
                  other->left->red = false;
                  other->red = true;
-                 interval_tree_rotate_right (tree, other);
+                 itree_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);
+             itree_rotate_left (tree, parent);
              node = tree->root;
              parent = NULL;
            }
@@ -965,7 +859,7 @@ interval_tree_remove_fix (struct itree_tree *tree,
            {
              other->red = false;
              parent->red = true;
-             interval_tree_rotate_right (tree, parent);
+             itree_rotate_right (tree, parent);
              other = parent->left;
            }
          eassume (other != NULL);
@@ -984,14 +878,14 @@ interval_tree_remove_fix (struct itree_tree *tree,
                {
                  other->right->red = false;
                  other->red = true;
-                 interval_tree_rotate_left (tree, other);
+                 itree_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);
+             itree_rotate_right (tree, parent);
              node = tree->root;
              parent = NULL;
            }
@@ -1024,7 +918,7 @@ itree_total_offset (struct itree_node *node)
    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,
+itree_replace_child (struct itree_tree *tree,
                             struct itree_node *source,
                             struct itree_node *dest)
 {
@@ -1050,11 +944,11 @@ interval_tree_replace_child (struct itree_tree *tree,
    recalculation of `limit`.  Requires both nodes to be using the same
    effective `offset`. */
 static void
-interval_tree_transplant (struct itree_tree *tree,
+itree_transplant (struct itree_tree *tree,
                          struct itree_node *source,
                          struct itree_node *dest)
 {
-  interval_tree_replace_child (tree, source, dest);
+  itree_replace_child (tree, source, dest);
   source->left = dest->left;
   if (source->left != NULL)
     source->left->parent = source;
@@ -1069,17 +963,17 @@ interval_tree_transplant (struct itree_tree *tree,
 struct itree_node*
 itree_remove (struct itree_tree *tree, struct itree_node *node)
 {
-  eassert (interval_tree_contains (tree, node));
+  eassert (itree_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);
+  itree_inherit_offset (tree->otick, node);
   struct itree_node *splice
     = (node->left == NULL || node->right == NULL)
        ? node
-       : interval_tree_subtree_min (tree->otick, node->right);
+       : itree_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
@@ -1100,7 +994,7 @@ itree_remove (struct itree_tree *tree, struct itree_node 
*node)
      `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);
+  itree_replace_child (tree, subtree, splice);
   bool removed_black = !splice->red;
 
   /* Replace `node` with `splice` in the tree and propagate limit
@@ -1109,18 +1003,18 @@ itree_remove (struct itree_tree *tree, struct 
itree_node *node)
      has a new child.  */
   if (splice != node)
     {
-      interval_tree_transplant (tree, splice, node);
-      interval_tree_propagate_limit (subtree_parent);
+      itree_transplant (tree, splice, node);
+      itree_propagate_limit (subtree_parent);
       if (splice != subtree_parent)
-       interval_tree_update_limit (splice);
+       itree_update_limit (splice);
     }
-  interval_tree_propagate_limit (splice->parent);
+  itree_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);
+    itree_remove_fix (tree, subtree, subtree_parent);
 
   eassert ((tree->size == 0) == (tree->root == NULL));
   eassert (check_tree (tree, true)); /* FIXME: Too expensive.  */
@@ -1138,52 +1032,6 @@ itree_remove (struct itree_tree *tree, struct itree_node 
*node)
   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
@@ -1210,10 +1058,11 @@ itree_insert_gap (struct itree_tree *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_stack *saved = itree_stack_create (0);
   struct itree_node *node = NULL;
   if (!before_markers)
     {
+      /* Actually any order would do.  */
       ITREE_FOREACH (node, tree, pos, pos + 1, PRE_ORDER)
        {
          if (node->begin == pos && node->front_advance
@@ -1221,25 +1070,23 @@ itree_insert_gap (struct itree_tree *tree,
                 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);
+           itree_stack_push (saved, node);
        }
     }
   for (size_t i = 0; i < saved->length; ++i)
-    itree_remove (tree, nav_nodeptr (saved->nodes[i]));
+    itree_remove (tree, 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)))
+      const int size = itree_max_height (tree) + 1;
+      struct itree_stack *stack = itree_stack_create (size);
+      itree_stack_push (stack, tree->root);
+      while ((node = itree_stack_pop (stack)))
        {
          /* Process in pre-order. */
-         interval_tree_inherit_offset (tree->otick, node);
+         itree_inherit_offset (tree->otick, node);
          if (pos > node->limit)
            continue;
          if (node->right != NULL)
@@ -1251,10 +1098,10 @@ itree_insert_gap (struct itree_tree *tree,
                  ++tree->otick;
                }
              else
-               interval_stack_push (stack, node->right);
+               itree_stack_push (stack, node->right);
            }
          if (node->left != NULL)
-           interval_stack_push (stack, node->left);
+           itree_stack_push (stack, node->left);
 
          if (before_markers
              ? node->begin >= pos
@@ -1265,17 +1112,15 @@ itree_insert_gap (struct itree_tree *tree,
            {
              node->end += length;
              eassert (node != NULL);
-             interval_tree_propagate_limit (node);
+             itree_propagate_limit (node);
            }
        }
-      interval_stack_destroy (stack);
+      itree_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)))
+  while ((node = itree_stack_pop (saved)))
     {
       eassert (node->otick == ootick);
       eassert (node->begin == pos);
@@ -1283,10 +1128,10 @@ itree_insert_gap (struct itree_tree *tree,
       node->begin += length;
       node->end += length;
       node->otick = notick;
-      interval_tree_insert (tree, node);
+      itree_insert_node (tree, node);
     }
 
-  interval_stack_destroy (saved);
+  itree_stack_destroy (saved);
 }
 
 /* Delete a gap at POS of length LENGTH, contracting all intervals
@@ -1303,16 +1148,14 @@ itree_delete_gap (struct itree_tree *tree,
 
   /* 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);
+  const int size = itree_max_height (tree) + 1;
+  struct itree_stack *stack = itree_stack_create (size);
   struct itree_node *node;
 
-  interval_stack_push (stack, tree->root);
-  nodeptr_and_flag nav;
-  while ((nav = interval_stack_pop (stack)))
+  itree_stack_push (stack, tree->root);
+  while ((node = itree_stack_pop (stack)))
     {
-      node = nav_nodeptr (nav);
-      interval_tree_inherit_offset (tree->otick, node);
+      itree_inherit_offset (tree->otick, node);
       if (pos > node->limit)
        continue;
       if (node->right != NULL)
@@ -1324,10 +1167,10 @@ itree_delete_gap (struct itree_tree *tree,
              ++tree->otick;
            }
          else
-           interval_stack_push (stack, node->right);
+           itree_stack_push (stack, node->right);
        }
       if (node->left != NULL)
-       interval_stack_push (stack, node->left);
+       itree_stack_push (stack, node->left);
 
       if (pos < node->begin)
        node->begin = max (pos, node->begin - length);
@@ -1335,10 +1178,10 @@ itree_delete_gap (struct itree_tree *tree,
        {
          node->end = max (pos , node->end - length);
          eassert (node != NULL);
-         interval_tree_propagate_limit (node);
+         itree_propagate_limit (node);
        }
     }
-  interval_stack_destroy (stack);
+  itree_stack_destroy (stack);
 }
 
 
@@ -1356,81 +1199,217 @@ itree_delete_gap (struct itree_tree *tree,
    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)
+itree_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. */
+/* Return the "next" node in the current traversal order.
 
-struct itree_node *
-itree_iterator_next (struct itree_iterator *g)
-{
-  eassert (g && g->running);
+   Note that this should return all the nodes that we need to traverse
+   in order to traverse the nodes selected by the current narrowing (i.e.
+   `ITER->begin..ITER->end`) so it will also return some nodes which aren't in
+   that narrowing simply because they may have children which are.
 
-  struct itree_node *const null = NULL;
-  struct itree_node *node;
+   The code itself is very unsatifactory because the code of each one
+   of the supported traversals seems completely different from the others.
+   If someone knows how to make it more uniform and "obviously correct",
+   please make yourself heard.  */
 
-  /* 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
+static struct itree_node *
+itree_iter_next_in_subtree (struct itree_node *node,
+                            struct itree_iterator *iter)
+{
+  /* FIXME: Like in the previous version of the iterator, we
+     prune based on `limit` only when moving to a left child,
+     but `limit` can also get smaller when moving to a right child
+     It's actually fairly common, so maybe it would be worthwhile
+     to prune a bit more aggressively here.  */
+  struct itree_node *next;
+  switch (iter->order)
     {
-      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;
+    case ITREE_ASCENDING:
+      next = node->right;
+      if (!next)
+        {
+          while ((next = node->parent)
+                 && next->right == node)
+            node = next;
+          if (!next)
+            return NULL;   /* No more nodes to visit. */
+          node = next;
+        }
+      else
+        {
+          node = next;
+          itree_inherit_offset (iter->otick, node);
+          while ((next = node->left)
+                 && (itree_inherit_offset (iter->otick, next),
+                     iter->begin <= next->limit))
+            node = next;
+        }
+      if (node->begin > iter->end)
+        return NULL;  /* No more nodes within begin..end.  */
+      return node;
+
+    case ITREE_DESCENDING:
+      next = node->left;
+      if (!next
+          || (itree_inherit_offset (iter->otick, next),
+              next->limit < iter->begin))
+        {
+          while ((next = node->parent)
+                 && next->left == node)
+            node = next;
+          if (!next)
+            return NULL;   /* No more nodes to visit. */
+          node = next;
+        }
+      else
+        {
+          node = next;
+          while (node->begin <= iter->end
+                 && (next = node->right))
+            {
+              itree_inherit_offset (iter->otick, next),
+              node = next;
+            }
+        }
+      return node;
+
+    case ITREE_PRE_ORDER:
+      next = node->left;
+      if (next
+          && (itree_inherit_offset (iter->otick, next),
+              !(next->limit < iter->begin)))
+        return next;
+      next = node->right;
+      if (node->begin <= iter->end && next)
+        {
+          itree_inherit_offset (iter->otick, next);
+          return next;
+        }
+      while ((next = node->parent))
+        {
+          if (next->right == node)
+            node = next;
+          else
+            {
+              eassert (next->left == node);
+              node = next;
+              next = node->right;
+              if (node->begin <= iter->end && next)
+                {
+                  itree_inherit_offset (iter->otick, next);
+                  return next;
+                }
+            }
+          }
+      return NULL;
+
+    case ITREE_POST_ORDER:
+      next = node->parent;
+      if (!next || next->right == node)
+        return next;
+      eassert (next->left == node);
+      node = next;
+      next = node->right;
+      if (!(node->begin <= iter->end && next))
+        return node;
+      node = next;
+      itree_inherit_offset (iter->otick, node);
+      while (((next = node->left)
+              && (itree_inherit_offset (iter->otick, next),
+                  iter->begin <= next->limit))
+             || (node->begin <= iter->end
+                 && (next = node->right)
+                 && (itree_inherit_offset (iter->otick, next), true)))
+        node = next;
+      return node;
+
+    default:
+    emacs_abort ();
+    }
+}
 
-         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));
+static struct itree_node *
+itree_iterator_first_node (struct itree_tree *tree,
+                           struct itree_iterator *iter)
+{
+  struct itree_node *node = tree->root;
+  if (node)
+    {
+      struct itree_node dummy;
+      dummy.left = NULL;
+      dummy.parent = NULL;
+      dummy.right = NULL;
+      itree_inherit_offset (iter->otick, node);
+      switch (iter->order)
+        {
+        case ITREE_ASCENDING:
+          dummy.right = node;
+          dummy.begin = PTRDIFF_MIN;
+          node = itree_iter_next_in_subtree (&dummy, iter);
+          break;
+
+        case ITREE_DESCENDING:
+          dummy.left = node;
+          node = itree_iter_next_in_subtree (&dummy, iter);
+          break;
+
+        case ITREE_PRE_ORDER:
+          break;
+
+        case ITREE_POST_ORDER:
+          dummy.parent = &dummy;
+          dummy.left = &dummy;
+          dummy.right = node;
+          dummy.begin = PTRDIFF_MIN;
+          node = itree_iter_next_in_subtree (&dummy, iter);
+          break;
+        default:
+          emacs_abort ();
+        }
+    }
+  return node;
+}
+
+/* Start a iterator enumerating all intervals in [BEGIN,END) in the
+   given ORDER.  */
+
+struct itree_iterator *
+itree_iterator_start (struct itree_iterator *iter,
+                     struct itree_tree *tree,
+                     ptrdiff_t begin, ptrdiff_t end, enum itree_order order)
+{
+  eassert (iter);
+  iter->begin = begin;
+  iter->end = end;
+  iter->otick = tree->otick;
+  iter->order = order;
+  /* Beware: the `node` field always holds "the next" node to consider.
+     so it's always "one node ahead" of what the iterator loop sees.
+     In most respects this makes no difference, but we depend on this
+     detail in `delete_all_overlays` where this allows us to modify
+     the current node knowing that the iterator will not need it to
+     find the next.  */
+  iter->node = itree_iterator_first_node (tree, iter);
+  return iter;
+}
 
+struct itree_node *
+itree_iterator_next (struct itree_iterator *iter)
+{
+  struct itree_node *node = iter->node;
+  while (node
+         && !itree_node_intersects (node, iter->begin, iter->end))
+    {
+      node = itree_iter_next_in_subtree (node, iter);
+      eassert (itree_limit_is_stable (node));
+    }
+  iter->node = node ? itree_iter_next_in_subtree (node, iter) : NULL;
   return node;
 }
 
@@ -1441,7 +1420,7 @@ void
 itree_iterator_narrow (struct itree_iterator *g,
                       ptrdiff_t begin, ptrdiff_t end)
 {
-  eassert (g && g->running);
+  eassert (g);
   eassert (begin >= g->begin);
   eassert (end <= g->end);
   g->begin = max (begin, g->begin);
diff --git a/src/itree.h b/src/itree.h
index 10ee0897c3..291fa53fd3 100644
--- a/src/itree.h
+++ b/src/itree.h
@@ -104,12 +104,9 @@ enum itree_order
     ITREE_ASCENDING,
     ITREE_DESCENDING,
     ITREE_PRE_ORDER,
+    ITREE_POST_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 *);
@@ -128,20 +125,28 @@ 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 *,
+extern struct itree_iterator *itree_iterator_start (struct itree_iterator *,
+                                                   struct itree_tree *,
                                                    ptrdiff_t,
                                                    ptrdiff_t,
-                                                   enum itree_order,
-                                                   const char *, int);
+                                                   enum itree_order);
 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 *);
 
+/* State used when iterating interval. */
+struct itree_iterator
+  {
+    struct itree_node *node;
+    ptrdiff_t begin;
+    ptrdiff_t end;
+    uintmax_t otick;    /* A copy of the tree's `otick`.  */
+    enum itree_order order;
+  };
+
 /* 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`.
+   `DESCENDING`, `POST_ORDER`, or `PRE_ORDER`.
    It should be used as:
 
       ITREE_FOREACH (n, t, beg, end, order)
@@ -152,33 +157,23 @@ extern struct itree_node *itree_iterator_next (struct 
itree_iterator *);
    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
+  /* FIXME: We'd want to declare `n` 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_)
+     of the vars matches the `n` we receive :-(.  */             \
+  if (!t)                                                        \
+    { }                                                          \
+  else                                                           \
+    for (struct itree_iterator itree_local_iter_,                \
+                               *itree_iter_                      \
+            = itree_iterator_start (&itree_local_iter_,          \
+                                    t, beg, end, ITREE_##order); \
+          ((n = itree_iterator_next (itree_iter_)));)
 
 #define ITREE_FOREACH_NARROW(beg, end) \
   itree_iterator_narrow (itree_iter_, beg, end)
diff --git a/src/json.c b/src/json.c
index 9a455f507b..cdcc11358e 100644
--- a/src/json.c
+++ b/src/json.c
@@ -1092,22 +1092,6 @@ usage: (json-parse-buffer &rest args) */)
   return unbind_to (count, lisp);
 }
 
-/* Simplified version of 'define-error' that works with pure
-   objects.  */
-
-static void
-define_error (Lisp_Object name, const char *message, Lisp_Object parent)
-{
-  eassert (SYMBOLP (name));
-  eassert (SYMBOLP (parent));
-  Lisp_Object parent_conditions = Fget (parent, Qerror_conditions);
-  eassert (CONSP (parent_conditions));
-  eassert (!NILP (Fmemq (parent, parent_conditions)));
-  eassert (NILP (Fmemq (name, parent_conditions)));
-  Fput (name, Qerror_conditions, pure_cons (name, parent_conditions));
-  Fput (name, Qerror_message, build_pure_c_string (message));
-}
-
 void
 syms_of_json (void)
 {
diff --git a/src/keyboard.c b/src/keyboard.c
index 069cf0627b..811998823c 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -5720,6 +5720,29 @@ make_scroll_bar_position (struct input_event *ev, 
Lisp_Object type)
                builtin_lisp_symbol (scroll_bar_parts[ev->part]));
 }
 
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
+
+/* Return whether or not the coordinates X and Y are inside the
+   box of the menu-bar window of frame F.  */
+
+static bool
+coords_in_menu_bar_window (struct frame *f, int x, int y)
+{
+  struct window *window;
+
+  if (!WINDOWP (f->menu_bar_window))
+    return false;
+
+  window = XWINDOW (f->menu_bar_window);
+
+  return (y >= WINDOW_TOP_EDGE_Y (window)
+         && x >= WINDOW_LEFT_EDGE_X (window)
+         && y <= WINDOW_BOTTOM_EDGE_Y (window)
+         && x <= WINDOW_RIGHT_EDGE_X (window));
+}
+
+#endif
+
 /* Given a struct input_event, build the lisp event which represents
    it.  If EVENT is 0, build a mouse movement event from the mouse
    movement buffer, which should have a movement event in it.
@@ -5972,10 +5995,32 @@ make_lispy_event (struct input_event *event)
               and ROW are set to frame relative glyph coordinates
               which are then used to determine whether this click is
               in a menu (non-toolkit version).  */
-           if (!toolkit_menubar_in_use (f))
+           if (!toolkit_menubar_in_use (f)
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
+               /* Don't process events for menu bars if they are not
+                  in the menu bar window.  */
+               && (!FRAME_WINDOW_P (f)
+                   || coords_in_menu_bar_window (f, XFIXNUM (event->x),
+                                                 XFIXNUM (event->y)))
+#endif
+               )
              {
-               pixel_to_glyph_coords (f, XFIXNUM (event->x), XFIXNUM 
(event->y),
-                                      &column, &row, NULL, 1);
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
+               if (FRAME_WINDOW_P (f))
+                 {
+                   struct window *menu_w = XWINDOW (f->menu_bar_window);
+                   int x, y, dummy;
+
+                   x = FRAME_TO_WINDOW_PIXEL_X (menu_w, XFIXNUM (event->x));
+                   y = FRAME_TO_WINDOW_PIXEL_Y (menu_w, XFIXNUM (event->y));
+
+                   x_y_to_hpos_vpos (XWINDOW (f->menu_bar_window), x, y, 
&column, &row,
+                                     NULL, NULL, &dummy);
+                 }
+               else
+#endif
+                 pixel_to_glyph_coords (f, XFIXNUM (event->x), XFIXNUM 
(event->y),
+                                        &column, &row, NULL, 1);
 
                /* In the non-toolkit version, clicks on the menu bar
                   are ordinary button events in the event buffer.
@@ -5985,7 +6030,7 @@ make_lispy_event (struct input_event *event)
                   menu bar and Emacs doesn't know about it until
                   after the user makes a selection.)  */
                if (row >= 0 && row < FRAME_MENU_BAR_LINES (f)
-                 && (event->modifiers & down_modifier))
+                   && (event->modifiers & down_modifier))
                  {
                    Lisp_Object items, item;
 
diff --git a/src/lisp.h b/src/lisp.h
index 29bda0b3df..0a0abc3348 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -583,6 +583,8 @@ enum Lisp_Fwd_Type
    your object -- this way, the same object could be used to represent
    several disparate C structures.
 
+   In addition, you need to add switch branches in data.c for Ftype_of.
+
    You also need to add the new type to the constant
    `cl--typeof-types' in lisp/emacs-lisp/cl-preloaded.el.  */
 
@@ -1061,6 +1063,9 @@ enum pvec_type
   PVEC_CONDVAR,
   PVEC_MODULE_FUNCTION,
   PVEC_NATIVE_COMP_UNIT,
+  PVEC_TS_PARSER,
+  PVEC_TS_NODE,
+  PVEC_TS_COMPILED_QUERY,
   PVEC_SQLITE,
 
   /* These should be last, for internal_equal and sxhash_obj.  */
@@ -4855,6 +4860,8 @@ extern void update_search_regs (ptrdiff_t oldstart,
 extern void record_unwind_save_match_data (void);
 extern ptrdiff_t fast_string_match_internal (Lisp_Object, Lisp_Object,
                                             Lisp_Object);
+extern ptrdiff_t fast_c_string_match_internal (Lisp_Object, const char *,
+                                              ptrdiff_t, Lisp_Object);
 
 INLINE ptrdiff_t
 fast_string_match (Lisp_Object regexp, Lisp_Object string)
@@ -4868,8 +4875,21 @@ fast_string_match_ignore_case (Lisp_Object regexp, 
Lisp_Object string)
   return fast_string_match_internal (regexp, string, Vascii_canon_table);
 }
 
-extern ptrdiff_t fast_c_string_match_ignore_case (Lisp_Object, const char *,
-                                                 ptrdiff_t);
+INLINE ptrdiff_t
+fast_c_string_match (Lisp_Object regexp,
+                    const char *string, ptrdiff_t len)
+{
+  return fast_c_string_match_internal (regexp, string, len, Qnil);
+}
+
+INLINE ptrdiff_t
+fast_c_string_match_ignore_case (Lisp_Object regexp,
+                                const char *string, ptrdiff_t len)
+{
+  return fast_c_string_match_internal (regexp, string, len,
+                                      Vascii_canon_table);
+}
+
 extern ptrdiff_t fast_looking_at (Lisp_Object, ptrdiff_t, ptrdiff_t,
                                   ptrdiff_t, ptrdiff_t, Lisp_Object);
 extern ptrdiff_t find_newline1 (ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t,
@@ -5669,6 +5689,11 @@ maybe_gc (void)
     maybe_garbage_collect ();
 }
 
+/* Simplified version of 'define-error' that works with pure
+   objects.  */
+void
+define_error (Lisp_Object name, const char *message, Lisp_Object parent);
+
 INLINE_HEADER_END
 
 #endif /* EMACS_LISP_H */
diff --git a/src/lread.c b/src/lread.c
index e1f4424dae..bd3073aef0 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -1741,12 +1741,15 @@ maybe_swap_for_eln (bool no_native, Lisp_Object 
*filename, int *fd,
                                               Vload_path,
                                               Qnil, Qnil)))
                return;
-             call2 (intern_c_string ("display-warning"),
-                    Qcomp,
-                    CALLN (Fformat,
-                           build_string ("Cannot look up eln file as "
-                                         "no source file was found for %s"),
-                           *filename));
+             Vdelayed_warnings_list
+               = Fcons (list2
+                        (Qcomp,
+                         CALLN (Fformat,
+                                build_string ("Cannot look up eln "
+                                              "file as no source file "
+                                              "was found for %s"),
+                                *filename)),
+                        Vdelayed_warnings_list);
              return;
            }
        }
@@ -5264,6 +5267,14 @@ to the specified file name if a suffix is allowed or 
required.  */);
     Fcons (build_pure_c_string (MODULES_SECONDARY_SUFFIX), Vload_suffixes);
 #endif
 
+  DEFVAR_LISP ("dynamic-library-suffixes", Vdynamic_library_suffixes,
+              doc: /* A list of suffixes for loadable dynamic libraries.  */);
+  Vdynamic_library_suffixes =
+    Fcons (build_pure_c_string (DYNAMIC_LIB_SECONDARY_SUFFIX), Qnil);
+  Vdynamic_library_suffixes =
+    Fcons (build_pure_c_string (DYNAMIC_LIB_SUFFIX),
+          Vdynamic_library_suffixes);
+
 #endif
   DEFVAR_LISP ("module-file-suffix", Vmodule_file_suffix,
               doc: /* Suffix of loadable module file, or nil if modules are 
not supported.  */);
diff --git a/src/nsfns.m b/src/nsfns.m
index 2699cf37a5..d793bcf13f 100644
--- a/src/nsfns.m
+++ b/src/nsfns.m
@@ -632,10 +632,19 @@ ns_set_menu_bar_lines (struct frame *f, Lisp_Object 
value, Lisp_Object oldval)
 void
 ns_change_tab_bar_height (struct frame *f, int height)
 {
-  int unit = FRAME_LINE_HEIGHT (f);
-  int old_height = FRAME_TAB_BAR_HEIGHT (f);
-  int lines = (height + unit - 1) / unit;
-  Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
+  int unit, old_height, lines;
+  Lisp_Object fullscreen;
+
+  unit = FRAME_LINE_HEIGHT (f);
+  old_height = FRAME_TAB_BAR_HEIGHT (f);
+  fullscreen = get_frame_param (f, Qfullscreen);
+
+  /* This differs from the tool bar code in that the tab bar height is
+     not rounded up.  Otherwise, if redisplay_tab_bar decides to grow
+     the tab bar by even 1 pixel, FRAME_TAB_BAR_LINES will be changed,
+     leading to the tab bar height being incorrectly set upon the next
+     call to x_set_font.  (bug#59285) */
+  lines = height / unit;
 
   /* Make sure we redisplay all windows in this frame.  */
   fset_redisplay (f);
diff --git a/src/pgtkfns.c b/src/pgtkfns.c
index 9473e14f5c..a32067af81 100644
--- a/src/pgtkfns.c
+++ b/src/pgtkfns.c
@@ -473,10 +473,19 @@ pgtk_set_tab_bar_lines (struct frame *f, Lisp_Object 
value, Lisp_Object oldval)
 void
 pgtk_change_tab_bar_height (struct frame *f, int height)
 {
-  int unit = FRAME_LINE_HEIGHT (f);
-  int old_height = FRAME_TAB_BAR_HEIGHT (f);
-  int lines = (height + unit - 1) / unit;
-  Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
+  int unit, old_height, lines;
+  Lisp_Object fullscreen;
+
+  unit = FRAME_LINE_HEIGHT (f);
+  old_height = FRAME_TAB_BAR_HEIGHT (f);
+  fullscreen = get_frame_param (f, Qfullscreen);
+
+  /* This differs from the tool bar code in that the tab bar height is
+     not rounded up.  Otherwise, if redisplay_tab_bar decides to grow
+     the tab bar by even 1 pixel, FRAME_TAB_BAR_LINES will be changed,
+     leading to the tab bar height being incorrectly set upon the next
+     call to x_set_font.  (bug#59285) */
+  lines = height / unit;
 
   /* Make sure we redisplay all windows in this frame.  */
   fset_redisplay (f);
@@ -1112,13 +1121,6 @@ pgtk_default_font_parameter (struct frame *f, 
Lisp_Object parms)
       if (NILP (font))
        error ("No suitable font was found");
     }
-  else if (!NILP (font_param))
-    {
-      /* Remember the explicit font parameter, so we can re-apply it after
-         we've applied the `default' face settings.  */
-      AUTO_FRAME_ARG (arg, Qfont_parameter, font_param);
-      gui_set_frame_parameters (f, arg);
-    }
 
   /* This call will make X resources override any system font setting.  */
   gui_default_parameter (f, parms, Qfont, font, "font", "Font",
diff --git a/src/pgtkterm.c b/src/pgtkterm.c
index 4f3e3697ba..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;
     }
@@ -1333,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;
@@ -1606,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,
@@ -2958,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,
@@ -4518,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
@@ -5144,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.  */
@@ -5181,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
@@ -5214,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
@@ -5363,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;
 }
 
@@ -5904,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);
@@ -5971,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?  */
@@ -5989,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)
@@ -6060,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);
@@ -6986,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;
@@ -7118,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);
@@ -7159,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 4c3bf6c4ee..e25c88e251 100644
--- a/src/print.c
+++ b/src/print.c
@@ -47,6 +47,10 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 # include <sys/socket.h> /* for F_DUPFD_CLOEXEC */
 #endif
 
+#ifdef HAVE_TREE_SITTER
+#include "treesit.h"
+#endif
+
 struct terminal;
 
 /* Avoid actual stack overflow in print.  */
@@ -2018,6 +2022,45 @@ print_vectorlike (Lisp_Object obj, Lisp_Object 
printcharfun, bool escapeflag,
       }
       break;
 #endif
+
+#ifdef HAVE_TREE_SITTER
+    case PVEC_TS_PARSER:
+      print_c_string ("#<treesit-parser for ", printcharfun);
+      Lisp_Object language = XTS_PARSER (obj)->language_symbol;
+      /* No need to print the buffer because it's not that useful: we
+        usually know which buffer a parser belongs to.  */
+      print_string (Fsymbol_name (language), printcharfun);
+      printchar ('>', printcharfun);
+      break;
+    case PVEC_TS_NODE:
+      /* Prints #<treesit-node (identifier) in 12-15> or
+         #<treesit-node "keyword" in 28-31>. */
+      print_c_string ("#<treesit-node", printcharfun);
+      if (!treesit_node_uptodate_p (obj))
+       {
+         print_c_string ("-outdated>", printcharfun);
+         break;
+       }
+      printchar (' ', printcharfun);
+      /* Now the node must be up-to-date, and calling functions like
+        Ftreesit_node_start will not signal.  */
+      bool named = treesit_named_node_p (XTS_NODE (obj)->node);
+      const char *delim1 = named ? "(" : "\"";
+      const char *delim2 = named ? ")" : "\"";
+      print_c_string (delim1, printcharfun);
+      print_string (Ftreesit_node_type (obj), printcharfun);
+      print_c_string (delim2, printcharfun);
+      print_c_string (" in ", printcharfun);
+      print_object (Ftreesit_node_start (obj), printcharfun, escapeflag);
+      printchar ('-', printcharfun);
+      print_object (Ftreesit_node_end (obj), printcharfun, escapeflag);
+      printchar ('>', printcharfun);
+      break;
+    case PVEC_TS_COMPILED_QUERY:
+      print_c_string ("#<treesit-compiled-query>", printcharfun);
+      break;
+#endif
+
     case PVEC_SQLITE:
       {
        print_c_string ("#<sqlite ", printcharfun);
diff --git a/src/search.c b/src/search.c
index 1c5831b6de..242681bbba 100644
--- a/src/search.c
+++ b/src/search.c
@@ -496,19 +496,27 @@ fast_string_match_internal (Lisp_Object regexp, 
Lisp_Object string,
   return val;
 }
 
-/* Match REGEXP against STRING, searching all of STRING ignoring case,
-   and return the index of the match, or negative on failure.
-   This does not clobber the match data.
+/* Match REGEXP against STRING, searching all of STRING and return the
+   index of the match, or negative on failure.  This does not clobber
+   the match data.  Table is a canonicalize table for ignoring case,
+   or nil for none.
+
    We assume that STRING contains single-byte characters.  */
 
 ptrdiff_t
-fast_c_string_match_ignore_case (Lisp_Object regexp,
-                                const char *string, ptrdiff_t len)
+fast_c_string_match_internal (Lisp_Object regexp,
+                             const char *string, ptrdiff_t len,
+                             Lisp_Object table)
 {
+  /* FIXME: This is expensive and not obviously correct when it makes
+     a difference. I.e., no longer "fast", and may hide bugs.
+     Something should be done about this.  */
   regexp = string_make_unibyte (regexp);
+  /* Record specpdl index because freeze_pattern pushes an
+     unwind-protect on the specpdl.  */
   specpdl_ref count = SPECPDL_INDEX ();
   struct regexp_cache *cache_entry
-    = compile_pattern (regexp, 0, Vascii_canon_table, 0, 0);
+    = compile_pattern (regexp, 0, table, 0, 0);
   freeze_pattern (cache_entry);
   re_match_object = Qt;
   ptrdiff_t val = re_search (&cache_entry->buf, string, len, 0, len, 0);
diff --git a/src/treesit.c b/src/treesit.c
new file mode 100644
index 0000000000..463e2458a6
--- /dev/null
+++ b/src/treesit.c
@@ -0,0 +1,3076 @@
+/* Tree-sitter integration for GNU Emacs.
+
+Copyright (C) 2021-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 "lisp.h"
+#include "buffer.h"
+
+#include "treesit.h"
+
+#if HAVE_TREE_SITTER
+
+
+/* Dynamic loading of libtree-sitter.  */
+
+#ifdef WINDOWSNT
+# include "w32common.h"
+
+/* In alphabetical order.  */
+#undef ts_language_version
+#undef ts_node_child
+#undef ts_node_child_by_field_name
+#undef ts_node_child_count
+#undef ts_node_descendant_for_byte_range
+#undef ts_node_end_byte
+#undef ts_node_eq
+#undef ts_node_field_name_for_child
+#undef ts_node_first_child_for_byte
+#undef ts_node_first_named_child_for_byte
+#undef ts_node_has_error
+#undef ts_node_is_extra
+#undef ts_node_is_missing
+#undef ts_node_is_named
+#undef ts_node_is_null
+#undef ts_node_named_child
+#undef ts_node_named_child_count
+#undef ts_node_named_descendant_for_byte_range
+#undef ts_node_next_named_sibling
+#undef ts_node_next_sibling
+#undef ts_node_parent
+#undef ts_node_prev_named_sibling
+#undef ts_node_prev_sibling
+#undef ts_node_start_byte
+#undef ts_node_string
+#undef ts_node_type
+#undef ts_parser_delete
+#undef ts_parser_included_ranges
+#undef ts_parser_language
+#undef ts_parser_new
+#undef ts_parser_parse
+#undef ts_parser_set_included_ranges
+#undef ts_parser_set_language
+#undef ts_query_capture_name_for_id
+#undef ts_query_cursor_delete
+#undef ts_query_cursor_exec
+#undef ts_query_cursor_new
+#undef ts_query_cursor_next_match
+#undef ts_query_cursor_set_byte_range
+#undef ts_query_delete
+#undef ts_query_new
+#undef ts_query_predicates_for_pattern
+#undef ts_query_string_value_for_id
+#undef ts_set_allocator
+#undef ts_tree_cursor_current_node
+#undef ts_tree_cursor_goto_first_child
+#undef ts_tree_cursor_goto_next_sibling
+#undef ts_tree_cursor_goto_parent
+#undef ts_tree_cursor_new
+#undef ts_tree_delete
+#undef ts_tree_edit
+#undef ts_tree_get_changed_ranges
+#undef ts_tree_root_node
+
+DEF_DLL_FN (uint32_t, ts_language_version, (const TSLanguage *));
+DEF_DLL_FN (TSNode, ts_node_child, (TSNode, uint32_t));
+DEF_DLL_FN (TSNode, ts_node_child_by_field_name,
+           (TSNode, const char *, uint32_t));
+DEF_DLL_FN (uint32_t, ts_node_child_count, (TSNode));
+DEF_DLL_FN (TSNode, ts_node_descendant_for_byte_range,
+           (TSNode, uint32_t, uint32_t));
+DEF_DLL_FN (uint32_t, ts_node_end_byte, (TSNode));
+DEF_DLL_FN (bool, ts_node_eq, (TSNode, TSNode));
+DEF_DLL_FN (const char *, ts_node_field_name_for_child, (TSNode, uint32_t));
+DEF_DLL_FN (TSNode, ts_node_first_child_for_byte, (TSNode, uint32_t));
+DEF_DLL_FN (TSNode, ts_node_first_named_child_for_byte, (TSNode, uint32_t));
+DEF_DLL_FN (bool, ts_node_has_error, (TSNode));
+DEF_DLL_FN (bool, ts_node_is_extra, (TSNode));
+DEF_DLL_FN (bool, ts_node_is_missing, (TSNode));
+DEF_DLL_FN (bool, ts_node_is_named, (TSNode));
+DEF_DLL_FN (bool, ts_node_is_null, (TSNode));
+DEF_DLL_FN (TSNode, ts_node_named_child, (TSNode, uint32_t));
+DEF_DLL_FN (uint32_t, ts_node_named_child_count, (TSNode));
+DEF_DLL_FN (TSNode, ts_node_named_descendant_for_byte_range,
+           (TSNode, uint32_t, uint32_t));
+DEF_DLL_FN (TSNode, ts_node_next_named_sibling, (TSNode));
+DEF_DLL_FN (TSNode, ts_node_next_sibling, (TSNode));
+DEF_DLL_FN (TSNode, ts_node_parent, (TSNode));
+DEF_DLL_FN (TSNode, ts_node_prev_named_sibling, (TSNode));
+DEF_DLL_FN (TSNode, ts_node_prev_sibling, (TSNode));
+DEF_DLL_FN (uint32_t, ts_node_start_byte, (TSNode));
+DEF_DLL_FN (char *, ts_node_string, (TSNode));
+DEF_DLL_FN (const char *, ts_node_type, (TSNode));
+DEF_DLL_FN (void, ts_parser_delete, (TSParser *));
+DEF_DLL_FN (const TSRange *, ts_parser_included_ranges,
+           (const TSParser *, uint32_t *));
+DEF_DLL_FN (const TSLanguage *, ts_parser_language, (const TSParser *));
+DEF_DLL_FN (TSParser *, ts_parser_new, (void));
+DEF_DLL_FN (TSTree *, ts_parser_parse, (TSParser *, const TSTree *, TSInput));
+DEF_DLL_FN (bool, ts_parser_set_included_ranges,
+           (TSParser *, const TSRange *, uint32_t));
+DEF_DLL_FN (bool, ts_parser_set_language, (TSParser *, const TSLanguage *));
+DEF_DLL_FN (const char *, ts_query_capture_name_for_id,
+           (const TSQuery *, uint32_t, uint32_t *));
+DEF_DLL_FN (void, ts_query_cursor_delete, (TSQueryCursor *));
+DEF_DLL_FN (void, ts_query_cursor_exec,
+           (TSQueryCursor *, const TSQuery *, TSNode));
+DEF_DLL_FN (TSQueryCursor *, ts_query_cursor_new, (void));
+DEF_DLL_FN (bool, ts_query_cursor_next_match,
+           (TSQueryCursor *, TSQueryMatch *));
+DEF_DLL_FN (void, ts_query_cursor_set_byte_range,
+           (TSQueryCursor *, uint32_t, uint32_t));
+DEF_DLL_FN (void, ts_query_delete, (TSQuery *));
+DEF_DLL_FN (TSQuery *, ts_query_new,
+           (const TSLanguage *, const char *, uint32_t, uint32_t *, 
TSQueryError *));
+DEF_DLL_FN (const TSQueryPredicateStep *, ts_query_predicates_for_pattern,
+           ( const TSQuery *, uint32_t, uint32_t *));
+DEF_DLL_FN (const char *, ts_query_string_value_for_id,
+           (const TSQuery *, uint32_t, uint32_t *));
+DEF_DLL_FN (void, ts_set_allocator,
+           (void *(*)(size_t), void *(*)(size_t, size_t), void *(*)(void *, 
size_t), void (*)(void *)));
+DEF_DLL_FN (TSNode, ts_tree_cursor_current_node, (const TSTreeCursor *));
+DEF_DLL_FN (bool, ts_tree_cursor_goto_first_child, (TSTreeCursor *));
+DEF_DLL_FN (bool, ts_tree_cursor_goto_next_sibling, (TSTreeCursor *));
+DEF_DLL_FN (bool, ts_tree_cursor_goto_parent, (TSTreeCursor *));
+DEF_DLL_FN (TSTreeCursor, ts_tree_cursor_new, (TSNode));
+DEF_DLL_FN (void, ts_tree_delete, (TSTree *));
+DEF_DLL_FN (void, ts_tree_edit, (TSTree *, const TSInputEdit *));
+DEF_DLL_FN (TSRange *, ts_tree_get_changed_ranges,
+           (const TSTree *, const TSTree *, uint32_t *));
+DEF_DLL_FN (TSNode, ts_tree_root_node, (const TSTree *));
+
+static bool
+init_treesit_functions (void)
+{
+  HMODULE library = w32_delayed_load (Qtree_sitter);
+
+  if (!library)
+    return false;
+
+  LOAD_DLL_FN (library, ts_language_version);
+  LOAD_DLL_FN (library, ts_node_child);
+  LOAD_DLL_FN (library, ts_node_child_by_field_name);
+  LOAD_DLL_FN (library, ts_node_child_count);
+  LOAD_DLL_FN (library, ts_node_descendant_for_byte_range);
+  LOAD_DLL_FN (library, ts_node_end_byte);
+  LOAD_DLL_FN (library, ts_node_eq);
+  LOAD_DLL_FN (library, ts_node_field_name_for_child);
+  LOAD_DLL_FN (library, ts_node_first_child_for_byte);
+  LOAD_DLL_FN (library, ts_node_first_named_child_for_byte);
+  LOAD_DLL_FN (library, ts_node_has_error);
+  LOAD_DLL_FN (library, ts_node_is_extra);
+  LOAD_DLL_FN (library, ts_node_is_missing);
+  LOAD_DLL_FN (library, ts_node_is_named);
+  LOAD_DLL_FN (library, ts_node_is_null);
+  LOAD_DLL_FN (library, ts_node_named_child);
+  LOAD_DLL_FN (library, ts_node_named_child_count);
+  LOAD_DLL_FN (library, ts_node_named_descendant_for_byte_range);
+  LOAD_DLL_FN (library, ts_node_next_named_sibling);
+  LOAD_DLL_FN (library, ts_node_next_sibling);
+  LOAD_DLL_FN (library, ts_node_parent);
+  LOAD_DLL_FN (library, ts_node_prev_named_sibling);
+  LOAD_DLL_FN (library, ts_node_prev_sibling);
+  LOAD_DLL_FN (library, ts_node_start_byte);
+  LOAD_DLL_FN (library, ts_node_string);
+  LOAD_DLL_FN (library, ts_node_type);
+  LOAD_DLL_FN (library, ts_parser_delete);
+  LOAD_DLL_FN (library, ts_parser_included_ranges);
+  LOAD_DLL_FN (library, ts_parser_language);
+  LOAD_DLL_FN (library, ts_parser_new);
+  LOAD_DLL_FN (library, ts_parser_parse);
+  LOAD_DLL_FN (library, ts_parser_set_included_ranges);
+  LOAD_DLL_FN (library, ts_parser_set_language);
+  LOAD_DLL_FN (library, ts_query_capture_name_for_id);
+  LOAD_DLL_FN (library, ts_query_cursor_delete);
+  LOAD_DLL_FN (library, ts_query_cursor_exec);
+  LOAD_DLL_FN (library, ts_query_cursor_new);
+  LOAD_DLL_FN (library, ts_query_cursor_next_match);
+  LOAD_DLL_FN (library, ts_query_cursor_set_byte_range);
+  LOAD_DLL_FN (library, ts_query_delete);
+  LOAD_DLL_FN (library, ts_query_new);
+  LOAD_DLL_FN (library, ts_query_predicates_for_pattern);
+  LOAD_DLL_FN (library, ts_query_string_value_for_id);
+  LOAD_DLL_FN (library, ts_set_allocator);
+  LOAD_DLL_FN (library, ts_tree_cursor_current_node);
+  LOAD_DLL_FN (library, ts_tree_cursor_goto_first_child);
+  LOAD_DLL_FN (library, ts_tree_cursor_goto_next_sibling);
+  LOAD_DLL_FN (library, ts_tree_cursor_goto_parent);
+  LOAD_DLL_FN (library, ts_tree_cursor_new);
+  LOAD_DLL_FN (library, ts_tree_delete);
+  LOAD_DLL_FN (library, ts_tree_edit);
+  LOAD_DLL_FN (library, ts_tree_get_changed_ranges);
+  LOAD_DLL_FN (library, ts_tree_root_node);
+
+  return true;
+}
+
+#define ts_language_version fn_ts_language_version
+#define ts_node_child fn_ts_node_child
+#define ts_node_child_by_field_name fn_ts_node_child_by_field_name
+#define ts_node_child_count fn_ts_node_child_count
+#define ts_node_descendant_for_byte_range fn_ts_node_descendant_for_byte_range
+#define ts_node_end_byte fn_ts_node_end_byte
+#define ts_node_eq fn_ts_node_eq
+#define ts_node_field_name_for_child fn_ts_node_field_name_for_child
+#define ts_node_first_child_for_byte fn_ts_node_first_child_for_byte
+#define ts_node_first_named_child_for_byte 
fn_ts_node_first_named_child_for_byte
+#define ts_node_has_error fn_ts_node_has_error
+#define ts_node_is_extra fn_ts_node_is_extra
+#define ts_node_is_missing fn_ts_node_is_missing
+#define ts_node_is_named fn_ts_node_is_named
+#define ts_node_is_null fn_ts_node_is_null
+#define ts_node_named_child fn_ts_node_named_child
+#define ts_node_named_child_count fn_ts_node_named_child_count
+#define ts_node_named_descendant_for_byte_range 
fn_ts_node_named_descendant_for_byte_range
+#define ts_node_next_named_sibling fn_ts_node_next_named_sibling
+#define ts_node_next_sibling fn_ts_node_next_sibling
+#define ts_node_parent fn_ts_node_parent
+#define ts_node_prev_named_sibling fn_ts_node_prev_named_sibling
+#define ts_node_prev_sibling fn_ts_node_prev_sibling
+#define ts_node_start_byte fn_ts_node_start_byte
+#define ts_node_string fn_ts_node_string
+#define ts_node_type fn_ts_node_type
+#define ts_parser_delete fn_ts_parser_delete
+#define ts_parser_included_ranges fn_ts_parser_included_ranges
+#define ts_parser_language fn_ts_parser_language
+#define ts_parser_new fn_ts_parser_new
+#define ts_parser_parse fn_ts_parser_parse
+#define ts_parser_set_included_ranges fn_ts_parser_set_included_ranges
+#define ts_parser_set_language fn_ts_parser_set_language
+#define ts_query_capture_name_for_id fn_ts_query_capture_name_for_id
+#define ts_query_cursor_delete fn_ts_query_cursor_delete
+#define ts_query_cursor_exec fn_ts_query_cursor_exec
+#define ts_query_cursor_new fn_ts_query_cursor_new
+#define ts_query_cursor_next_match fn_ts_query_cursor_next_match
+#define ts_query_cursor_set_byte_range fn_ts_query_cursor_set_byte_range
+#define ts_query_delete fn_ts_query_delete
+#define ts_query_new fn_ts_query_new
+#define ts_query_predicates_for_pattern fn_ts_query_predicates_for_pattern
+#define ts_query_string_value_for_id fn_ts_query_string_value_for_id
+#define ts_set_allocator fn_ts_set_allocator
+#define ts_tree_cursor_current_node fn_ts_tree_cursor_current_node
+#define ts_tree_cursor_goto_first_child fn_ts_tree_cursor_goto_first_child
+#define ts_tree_cursor_goto_next_sibling fn_ts_tree_cursor_goto_next_sibling
+#define ts_tree_cursor_goto_parent fn_ts_tree_cursor_goto_parent
+#define ts_tree_cursor_new fn_ts_tree_cursor_new
+#define ts_tree_delete fn_ts_tree_delete
+#define ts_tree_edit fn_ts_tree_edit
+#define ts_tree_get_changed_ranges fn_ts_tree_get_changed_ranges
+#define ts_tree_root_node fn_ts_tree_root_node
+
+#endif /* WINDOWSNT */
+
+
+/* Commentary
+
+   The Emacs wrapper of tree-sitter does not expose everything the C
+   API provides, most notably:
+
+   - It doesn't expose a syntax tree.  The syntax tree is part of the
+     parser object, and updating the tree is handled on the C level.
+
+   - It doesn't expose the tree cursor, either.  Presumably, Lisp is
+     slow enough to make insignificant any performance advantages from
+     using the cursor.  Not exposing the cursor also minimizes the
+     number of new types this adds to Emacs Lisp; currently, this adds
+     only the parser and node types.
+
+   - Because updating the change is handled on the C level as each
+     change is made in the buffer, there is no way for Lisp to update
+     a node.  But since we can just retrieve a new node, it shouldn't
+     be a limitation.
+
+   - I didn't expose setting timeout and cancelation flag for a
+     parser, mainly because I don't think they are really necessary
+     in Emacs's use cases.
+
+   - Many tree-sitter functions take a TSPoint, which is basically a
+     row and column.  Emacs uses a gap buffer and does not keep
+     information about the row and column position of a buffer.
+     According to the author of tree-sitter, those functions only take
+     a TSPoint so that it can be moved alongside the byte position and
+     returned to the caller afterwards, and the position actually used
+     is the specified byte position.  He also said that he _thinks_
+     that just passing a byte position will also work.  As a result, a
+     dummy value is used in place of each TSPoint.  Judging by the
+     nature of parsing algorithms, I think it is safe to use only the
+     byte position, and I don't think this will change in the future.
+
+     See: https://github.com/tree-sitter/tree-sitter/issues/445
+
+   treesit.h has some commentary on the two main data structure for
+   the parser and node.  treesit_ensure_position_synced has some
+   commentary on how we make tree-sitter play well with narrowing (the
+   tree-sitter parser only sees the visible region, so we need to
+   translate positions back and forth).  Most action happens in
+   treesit_ensure_parsed, treesit_read_buffer and
+   treesit_record_change.
+
+   A complete correspondence list between tree-sitter functions and
+   exposed Lisp functions can be found in the manual node (elisp)API
+   Correspondence.
+
+   Placement of CHECK_xxx functions: call CHECK_xxx before using any
+   unchecked Lisp values; these include arguments of Lisp functions,
+   the return value of Fsymbol_value, and that of Fcar or Fcdr on
+   user-specified conses.
+
+   Initializing tree-sitter: there are two entry points to tree-sitter
+   functions: 'treesit-parser-create' and
+   'treesit-language-available-p'.  Technically we only need to call
+   initialization function in those two functions, but in reality we
+   check at the beginning of every Lisp function.  That should be more
+   fool-proof.
+
+   Tree-sitter offset (0-based) and buffer position (1-based):
+     tree-sitter offset + buffer position = buffer position
+     buffer position - buffer position = tree-sitter offset
+
+   Tree-sitter-related code in other files:
+   - src/alloc.c for gc for parser and node
+   - src/casefiddle.c & src/insdel.c for notifying tree-sitter
+     parser of buffer changes.
+   - lisp/emacs-lisp/cl-preloaded.el & data.c & lisp.h for parser and
+     node type.
+   - print.c for printing tree-sitter objects (node, parser, query).
+
+   Regarding signals: only raise signals in Lisp functions.
+
+   Casts from EMACS_INT and ptrdiff_t to uint32_t: We install checks
+   for buffer size and range and thus able to assume these casts never
+   overflow.
+
+   We don't parse at every keystroke.  Instead we only record the
+   changes at each keystroke, and only parse when requested.  It is
+   possible that lazy parsing is worse: instead of dispersed little
+   pauses, now you have less frequent but larger pauses.  I doubt
+   there will be any perceived difference, as the lazy parsing is
+   going to be pretty frequent anyway.  Also this (lazy parsing) is
+   what the mailing list guys wanted.
+
+   Because it is pretty slow (comparing to other tree-sitter
+   operations) for tree-sitter to parse the query and produce a query
+   object, it is very wasteful to reparse the query every time
+   treesit-query-capture is called, and it completely kills the
+   performance of querying in a loop for a moderate amount of times
+   (hundreds of queries takes seconds rather than milliseconds to
+   complete).  Therefore we want some caching.  We can either use a
+   search.c style transparent caching, or simply expose a new type,
+   compiled-ts-query and let the user to manually compile AOT.  I
+   believe AOT compiling gives users more control, makes the
+   performance stable and easy to understand (compiled -> fast,
+   uncompiled -> slow), and avoids some edge cases transparent cache
+   could have (see below).  So I implemented the AOT compilation.
+
+   Problems a transparent cache could have: Suppose we store cache
+   entries in a fixed-length linked-list, and compare with EQ.  1)
+   One-off query could kick out useful cache.  2) if the user messed
+   up and the query doesn't EQ to the cache anymore, the performance
+   mysteriously drops.  3) what if a user uses so many stuff that the
+   default cache size (20) is not enough and we end up thrashing?
+   These are all imaginary scenarios but they are not impossible
+   :-) */
+
+
+/*** Initialization */
+
+bool treesit_initialized = false;
+
+static bool
+load_tree_sitter_if_necessary (bool required)
+{
+#ifdef WINDOWSNT
+  static bool tried_to_initialize_once;
+  static bool tree_sitter_initialized;
+
+  if (!tried_to_initialize_once)
+    {
+      Lisp_Object status;
+
+      tried_to_initialize_once = true;
+      tree_sitter_initialized = init_treesit_functions ();
+      status = tree_sitter_initialized ? Qt : Qnil;
+      Vlibrary_cache = Fcons (Fcons (Qtree_sitter, status), Vlibrary_cache);
+    }
+
+  if (required && !tree_sitter_initialized)
+    xsignal1 (Qtreesit_error,
+             build_string ("tree-sitter library not found or failed to load"));
+
+  return tree_sitter_initialized;
+#else
+  return true;
+#endif
+}
+
+static void *
+treesit_calloc_wrapper (size_t n, size_t size)
+{
+  return xzalloc (n * size);
+}
+
+static void
+treesit_initialize (void)
+{
+  if (!treesit_initialized)
+    {
+      load_tree_sitter_if_necessary (true);
+      ts_set_allocator (xmalloc, treesit_calloc_wrapper, xrealloc, xfree);
+      treesit_initialized = true;
+    }
+}
+
+
+/*** Loading language library */
+
+/* Translates a symbol treesit-<lang> to a C name
+   treesit_<lang>.  */
+static void
+treesit_symbol_to_c_name (char *symbol_name)
+{
+  for (int idx = 0; idx < strlen (symbol_name); idx++)
+    {
+      if (symbol_name[idx] == '-')
+       symbol_name[idx] = '_';
+    }
+}
+
+static bool
+treesit_find_override_name (Lisp_Object language_symbol, Lisp_Object *name,
+                           Lisp_Object *c_symbol)
+{
+  Lisp_Object tem;
+
+  CHECK_LIST (Vtreesit_load_name_override_list);
+
+  tem = Vtreesit_load_name_override_list;
+
+  FOR_EACH_TAIL (tem)
+    {
+      Lisp_Object lang = XCAR (XCAR (tem));
+      CHECK_SYMBOL (lang);
+
+      if (EQ (lang, language_symbol))
+       {
+         *name = Fnth (make_fixnum (1), XCAR (tem));
+         CHECK_STRING (*name);
+         *c_symbol = Fnth (make_fixnum (2), XCAR (tem));
+         CHECK_STRING (*c_symbol);
+
+         return true;
+       }
+    }
+
+  CHECK_LIST_END (tem, Vtreesit_load_name_override_list);
+
+  return false;
+}
+
+/* For example, if Vdynamic_library_suffixes is (".so", ".dylib"),
+   this function pushes "lib_base_name.so" and "lib_base_name.dylib"
+   into *path_candidates.  Obviously path_candidates should be a Lisp
+   list of Lisp strings.  */
+static void
+treesit_load_language_push_for_each_suffix (Lisp_Object lib_base_name,
+                                           Lisp_Object *path_candidates)
+{
+  Lisp_Object suffixes;
+
+  suffixes = Vdynamic_library_suffixes;
+
+  FOR_EACH_TAIL (suffixes)
+    *path_candidates = Fcons (concat2 (lib_base_name, XCAR (suffixes)),
+                             *path_candidates);
+}
+
+/* Load the dynamic library of LANGUAGE_SYMBOL and return the pointer
+   to the language definition.
+
+   If error occurs, return NULL and fill SIGNAL_SYMBOL and SIGNAL_DATA
+   with values suitable for xsignal.  */
+static TSLanguage *
+treesit_load_language (Lisp_Object language_symbol,
+                      Lisp_Object *signal_symbol, Lisp_Object *signal_data)
+{
+  Lisp_Object symbol_name = Fsymbol_name (language_symbol);
+
+  CHECK_LIST (Vtreesit_extra_load_path);
+
+  /* Figure out the library name and C name.  */
+  Lisp_Object lib_base_name
+    = concat2 (build_pure_c_string ("libtree-sitter-"), symbol_name);
+  Lisp_Object base_name
+    = concat2 (build_pure_c_string ("tree-sitter-"), symbol_name);
+
+  /* Override the library name and C name, if appropriate.  */
+  Lisp_Object override_name;
+  Lisp_Object override_c_name;
+  bool found_override = treesit_find_override_name (language_symbol,
+                                                   &override_name,
+                                                   &override_c_name);
+  if (found_override)
+    lib_base_name = override_name;
+
+  /* Now we generate a list of possible library paths.  */
+  Lisp_Object path_candidates = Qnil;
+  /* First push just the filenames to the candidate list, which will
+     make dynlib_open look under standard system load paths.  */
+  treesit_load_language_push_for_each_suffix (lib_base_name, &path_candidates);
+  /* This is used for reporting errors (i.e., just filenames).  */
+  Lisp_Object base_candidates = path_candidates;
+  /* Then push ~/.emacs.d/tree-sitter paths.  */
+  Lisp_Object lib_name
+    = Fexpand_file_name (concat2 (build_string ("tree-sitter/"), 
lib_base_name),
+                        Fsymbol_value (Quser_emacs_directory));
+  treesit_load_language_push_for_each_suffix (lib_name, &path_candidates);
+  /* Then push paths from treesit-extra-load-path.  */
+  Lisp_Object tail;
+
+  tail = Freverse (Vtreesit_extra_load_path);
+
+  FOR_EACH_TAIL (tail)
+    {
+      Lisp_Object expanded_lib = Fexpand_file_name (lib_base_name, XCAR 
(tail));
+      treesit_load_language_push_for_each_suffix (expanded_lib,
+                                                 &path_candidates);
+    }
+
+  /* Try loading the dynamic library by each path candidate.  Stop
+     when succeed, record the error message and try the next one when
+     fail.  */
+  dynlib_handle_ptr handle;
+  const char *error;
+
+  tail = path_candidates;
+  error = NULL;
+  handle = NULL;
+
+  FOR_EACH_TAIL (tail)
+    {
+      char *library_name = SSDATA (XCAR (tail));
+      dynlib_error ();
+      handle = dynlib_open (library_name);
+      error = dynlib_error ();
+      if (error == NULL)
+       break;
+    }
+
+  if (error != NULL)
+    {
+      *signal_symbol = Qtreesit_load_language_error;
+      *signal_data = list3 (Qnot_found, base_candidates,
+                           build_string ("No such file or directory"));
+      return NULL;
+    }
+
+  /* Load TSLanguage.  */
+  eassume (handle != NULL);
+  dynlib_error ();
+  TSLanguage *(*langfn) (void);
+  char *c_name = xstrdup (SSDATA (base_name));
+  treesit_symbol_to_c_name (c_name);
+  if (found_override)
+    c_name = SSDATA (override_c_name);
+  langfn = dynlib_sym (handle, c_name);
+  xfree (c_name);
+  error = dynlib_error ();
+  if (error != NULL)
+    {
+      *signal_symbol = Qtreesit_load_language_error;
+      *signal_data = list2 (Qsymbol_error, build_string (error));
+      return NULL;
+    }
+  TSLanguage *lang = (*langfn) ();
+
+  /* Check if language version matches tree-sitter version.  */
+  TSParser *parser = ts_parser_new ();
+  bool success = ts_parser_set_language (parser, lang);
+  ts_parser_delete (parser);
+  if (!success)
+    {
+      *signal_symbol = Qtreesit_load_language_error;
+      *signal_data = list2 (Qversion_mismatch,
+                           make_fixnum (ts_language_version (lang)));
+      return NULL;
+    }
+  return lang;
+}
+
+DEFUN ("treesit-language-available-p", Ftreesit_langauge_available_p,
+       Streesit_language_available_p,
+       1, 2, 0,
+       doc: /* Return non-nil if LANGUAGE exists and is loadable.
+
+If DETAIL is non-nil, return (t . nil) when LANGUAGE is available,
+(nil . DATA) when unavailable.  DATA is the signal data of
+`treesit-load-language-error'.  */)
+  (Lisp_Object language, Lisp_Object detail)
+{
+  CHECK_SYMBOL (language);
+  treesit_initialize ();
+  Lisp_Object signal_symbol = Qnil;
+  Lisp_Object signal_data = Qnil;
+  if (treesit_load_language (language, &signal_symbol, &signal_data) == NULL)
+    {
+      if (NILP (detail))
+       return Qnil;
+      else
+       return Fcons (Qnil, signal_data);
+    }
+  else
+    {
+      if (NILP (detail))
+       return Qt;
+      else
+       return Fcons (Qt, Qnil);
+    }
+}
+
+DEFUN ("treesit-language-version",
+       Ftreesit_language_version,
+       Streesit_language_version,
+       0, 1, 0,
+       doc: /* Return the language ABI version of the tree-sitter library.
+
+By default, report the latest ABI version supported by the library for
+loading language support modules.  The library is backward-compatible
+with language modules which use older ABI versions; if MIN-COMPATIBLE
+is non-nil, return the oldest compatible ABI version.  */)
+  (Lisp_Object min_compatible)
+{
+  if (NILP (min_compatible))
+    return make_fixnum (TREE_SITTER_LANGUAGE_VERSION);
+  else
+    return make_fixnum (TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION);
+}
+
+/*** Parsing functions */
+
+static void
+treesit_check_parser (Lisp_Object obj)
+{
+  CHECK_TS_PARSER (obj);
+  if (XTS_PARSER (obj)->deleted)
+    xsignal1 (Qtreesit_parser_deleted, obj);
+}
+
+/* An auxiliary function that saves a few lines of code.  Assumes TREE
+   is not NULL.  START_BYTE, OLD_END_BYTE, NEW_END_BYTE must not be
+   larger than UINT32_MAX.  */
+static inline void
+treesit_tree_edit_1 (TSTree *tree, ptrdiff_t start_byte,
+                    ptrdiff_t old_end_byte, ptrdiff_t new_end_byte)
+{
+  eassert (start_byte >= 0);
+  eassert (start_byte <= old_end_byte);
+  eassert (start_byte <= new_end_byte);
+  TSPoint dummy_point = {0, 0};
+  eassert (start_byte <= UINT32_MAX);
+  eassert (old_end_byte <= UINT32_MAX);
+  eassert (new_end_byte <= UINT32_MAX);
+  TSInputEdit edit = {(uint32_t) start_byte,
+                     (uint32_t) old_end_byte,
+                     (uint32_t) new_end_byte,
+                     dummy_point, dummy_point, dummy_point};
+  ts_tree_edit (tree, &edit);
+}
+
+/* Update each parser's tree after the user made an edit.  This
+   function does not parse the buffer and only updates the tree, so it
+   should be very fast.  */
+void
+treesit_record_change (ptrdiff_t start_byte, ptrdiff_t old_end_byte,
+                      ptrdiff_t new_end_byte)
+{
+  Lisp_Object parser_list;
+
+  parser_list = BVAR (current_buffer, ts_parser_list);
+
+  FOR_EACH_TAIL_SAFE (parser_list)
+    {
+      CHECK_CONS (parser_list);
+      Lisp_Object lisp_parser = XCAR (parser_list);
+      treesit_check_parser (lisp_parser);
+      TSTree *tree = XTS_PARSER (lisp_parser)->tree;
+      if (tree != NULL)
+       {
+         eassert (start_byte <= old_end_byte);
+         eassert (start_byte <= new_end_byte);
+         /* Think the recorded change as a delete followed by an
+            insert, and think of them as moving unchanged text back
+            and forth.  After all, the whole point of updating the
+            tree is to update the position of unchanged text.  */
+         ptrdiff_t visible_beg = XTS_PARSER (lisp_parser)->visible_beg;
+         ptrdiff_t visible_end = XTS_PARSER (lisp_parser)->visible_end;
+         eassert (visible_beg >= 0);
+         eassert (visible_beg <= visible_end);
+
+         /* AFFECTED_START/OLD_END/NEW_END are (0-based) offsets from
+            VISIBLE_BEG.  min(visi_end, max(visi_beg, value)) clips
+            value into [visi_beg, visi_end], and subtracting visi_beg
+            gives the offset from visi_beg.  */
+         ptrdiff_t start_offset = (min (visible_end,
+                                        max (visible_beg, start_byte))
+                                   - visible_beg);
+         ptrdiff_t old_end_offset = (min (visible_end,
+                                          max (visible_beg, old_end_byte))
+                                     - visible_beg);
+         ptrdiff_t new_end_offset = (min (visible_end,
+                                          max (visible_beg, new_end_byte))
+                                     - visible_beg);
+         eassert (start_offset <= old_end_offset);
+         eassert (start_offset <= new_end_offset);
+
+         treesit_tree_edit_1 (tree, start_offset, old_end_offset,
+                              new_end_offset);
+         XTS_PARSER (lisp_parser)->need_reparse = true;
+         XTS_PARSER (lisp_parser)->timestamp++;
+
+         /* VISIBLE_BEG/END records tree-sitter's range of view in
+            the buffer.  Ee need to adjust them when tree-sitter's
+            view changes.  */
+         ptrdiff_t visi_beg_delta;
+         if (old_end_byte > new_end_byte)
+           /* Move backward.  */
+           visi_beg_delta = (min (visible_beg, new_end_byte)
+                             - min (visible_beg, old_end_byte));
+         else
+           /* Move forward.  */
+           visi_beg_delta = (old_end_byte < visible_beg
+                             ? new_end_byte - old_end_byte : 0);
+         XTS_PARSER (lisp_parser)->visible_beg = visible_beg + visi_beg_delta;
+         XTS_PARSER (lisp_parser)->visible_end = (visible_end
+                                                  + visi_beg_delta
+                                                  + (new_end_offset
+                                                     - old_end_offset));
+         eassert (XTS_PARSER (lisp_parser)->visible_beg >= 0);
+         eassert (XTS_PARSER (lisp_parser)->visible_beg
+                  <= XTS_PARSER (lisp_parser)->visible_end);
+       }
+    }
+}
+
+/* Make sure PARSER's visible_beg and visible_end are in sync with
+   BUF_BEGV_BYTE and BUG_ZV_BYTE.  When calling this function you must
+   make sure the current buffer's size is not larger than UINT32_MAX.
+   Basically always call treesit_check_buffer_size before this
+   function.  */
+static void
+treesit_ensure_position_synced (Lisp_Object parser)
+{
+  TSTree *tree = XTS_PARSER (parser)->tree;
+
+  if (tree == NULL)
+    return;
+
+  struct buffer *buffer = XBUFFER (XTS_PARSER (parser)->buffer);
+  ptrdiff_t visible_beg = XTS_PARSER (parser)->visible_beg;
+  ptrdiff_t visible_end = XTS_PARSER (parser)->visible_end;
+  eassert (0 <= visible_beg);
+  eassert (visible_beg <= visible_end);
+
+  eassert (BUF_BEGV_BYTE (buffer) <= UINT32_MAX);
+  eassert (BUF_ZV_BYTE (buffer) <= UINT32_MAX);
+
+  /* Before we parse or set ranges, catch up with the narrowing
+     situation.  We change visible_beg and visible_end to match
+     BUF_BEGV_BYTE and BUF_ZV_BYTE, and inform tree-sitter of the
+     change.  We want to move the visible range of tree-sitter to
+     match the narrowed range.  For example,
+     from ________|xxxx|__
+     to   |xxxx|__________ */
+
+  /* 1. Make sure visible_beg <= BUF_BEGV_BYTE.  */
+  if (visible_beg > BUF_BEGV_BYTE (buffer))
+    {
+      /* Tree-sitter sees: insert at the beginning.  */
+      treesit_tree_edit_1 (tree, 0, 0, visible_beg - BUF_BEGV_BYTE (buffer));
+      visible_beg = BUF_BEGV_BYTE (buffer);
+      eassert (visible_beg <= visible_end);
+    }
+  /* 2. Make sure visible_end = BUF_ZV_BYTE.  */
+  if (visible_end < BUF_ZV_BYTE (buffer))
+    {
+      /* Tree-sitter sees: insert at the end.  */
+      treesit_tree_edit_1 (tree, visible_end - visible_beg,
+                          visible_end - visible_beg,
+                          BUF_ZV_BYTE (buffer) - visible_beg);
+      visible_end = BUF_ZV_BYTE (buffer);
+      eassert (visible_beg <= visible_end);
+    }
+  else if (visible_end > BUF_ZV_BYTE (buffer))
+    {
+      /* Tree-sitter sees: delete at the end.  */
+      treesit_tree_edit_1 (tree, BUF_ZV_BYTE (buffer) - visible_beg,
+                          visible_end - visible_beg,
+                          BUF_ZV_BYTE (buffer) - visible_beg);
+      visible_end = BUF_ZV_BYTE (buffer);
+      eassert (visible_beg <= visible_end);
+    }
+  /* 3. Make sure visible_beg = BUF_BEGV_BYTE.  */
+  if (visible_beg < BUF_BEGV_BYTE (buffer))
+    {
+      /* Tree-sitter sees: delete at the beginning.  */
+      treesit_tree_edit_1 (tree, 0, BUF_BEGV_BYTE (buffer) - visible_beg, 0);
+      visible_beg = BUF_BEGV_BYTE (buffer);
+      eassert (visible_beg <= visible_end);
+    }
+  eassert (0 <= visible_beg);
+  eassert (visible_beg <= visible_end);
+
+  XTS_PARSER (parser)->visible_beg = visible_beg;
+  XTS_PARSER (parser)->visible_end = visible_end;
+}
+
+static void
+treesit_check_buffer_size (struct buffer *buffer)
+{
+  ptrdiff_t buffer_size = (BUF_Z (buffer) - BUF_BEG (buffer));
+  if (buffer_size > UINT32_MAX)
+    xsignal2 (Qtreesit_buffer_too_large,
+             build_pure_c_string ("Buffer size cannot be larger than 4GB"),
+             make_fixnum (buffer_size));
+}
+
+static Lisp_Object treesit_make_ranges (const TSRange *, uint32_t, struct 
buffer *);
+
+static void
+treesit_call_after_change_functions (TSTree *old_tree, TSTree *new_tree,
+                                    Lisp_Object parser)
+{
+  uint32_t len;
+  TSRange *ranges = ts_tree_get_changed_ranges (old_tree, new_tree, &len);
+  struct buffer *buf = XBUFFER (XTS_PARSER (parser)->buffer);
+  Lisp_Object lisp_ranges = treesit_make_ranges (ranges, len, buf);
+  xfree (ranges);
+
+  specpdl_ref count = SPECPDL_INDEX ();
+
+  /* let's trust the after change functions and not clone a new ranges
+     for each of them.  */
+  Lisp_Object functions = XTS_PARSER (parser)->after_change_functions;
+  FOR_EACH_TAIL (functions)
+    safe_call2 (XCAR (functions), lisp_ranges, parser);
+
+  unbind_to (count, Qnil);
+}
+
+/* Parse the buffer.  We don't parse until we have to.  When we have
+   to, we call this function to parse and update the tree.  */
+static void
+treesit_ensure_parsed (Lisp_Object parser)
+{
+  if (!XTS_PARSER (parser)->need_reparse)
+    return;
+  TSParser *treesit_parser = XTS_PARSER (parser)->parser;
+  TSTree *tree = XTS_PARSER (parser)->tree;
+  TSInput input = XTS_PARSER (parser)->input;
+  struct buffer *buffer = XBUFFER (XTS_PARSER (parser)->buffer);
+
+  /* Before we parse, catch up with the narrowing situation.  */
+  treesit_check_buffer_size (buffer);
+  treesit_ensure_position_synced (parser);
+
+  TSTree *new_tree = ts_parser_parse (treesit_parser, tree, input);
+  /* This should be very rare (impossible, really): it only happens
+     when 1) language is not set (impossible in Emacs because the user
+     has to supply a language to create a parser), 2) parse canceled
+     due to timeout (impossible because we don't set a timeout), 3)
+     parse canceled due to cancelation flag (impossible because we
+     don't set the flag).  (See comments for ts_parser_parse in
+     tree_sitter/api.h.)  */
+  if (new_tree == NULL)
+    {
+      Lisp_Object buf;
+      XSETBUFFER (buf, buffer);
+      xsignal1 (Qtreesit_parse_error, buf);
+    }
+
+  if (tree != NULL)
+    {
+      treesit_call_after_change_functions (tree, new_tree, parser);
+      ts_tree_delete (tree);
+    }
+
+  XTS_PARSER (parser)->tree = new_tree;
+  XTS_PARSER (parser)->need_reparse = false;
+}
+
+/* This is the read function provided to tree-sitter to read from a
+   buffer.  It reads one character at a time and automatically skips
+   the gap.  */
+static const char*
+treesit_read_buffer (void *parser, uint32_t byte_index,
+                    TSPoint position, uint32_t *bytes_read)
+{
+  struct buffer *buffer = XBUFFER (((struct Lisp_TS_Parser *) parser)->buffer);
+  ptrdiff_t visible_beg = ((struct Lisp_TS_Parser *) parser)->visible_beg;
+  ptrdiff_t visible_end = ((struct Lisp_TS_Parser *) parser)->visible_end;
+  ptrdiff_t byte_pos = byte_index + visible_beg;
+  /* We will make sure visible_beg = BUF_BEGV_BYTE before re-parse (in
+     treesit_ensure_parsed), so byte_pos will never be smaller than
+     BUF_BEG_BYTE.  */
+  eassert (visible_beg = BUF_BEGV_BYTE (buffer));
+  eassert (visible_end = BUF_ZV_BYTE (buffer));
+
+  /* Read one character.  Tree-sitter wants us to set bytes_read to 0
+     if it reads to the end of buffer.  It doesn't say what it wants
+     for the return value in that case, so we just give it an empty
+     string.  */
+  char *beg;
+  int len;
+  /* This function could run from a user command, so it is better to
+     do nothing instead of raising an error.  (It was a pain in the a**
+     to decrypt mega-if-conditions in Emacs source, so I wrote the two
+     branches separately, you are welcome.)  */
+  if (!BUFFER_LIVE_P (buffer))
+    {
+      beg = NULL;
+      len = 0;
+    }
+  /* Reached visible end-of-buffer, tell tree-sitter to read no more.  */
+  else if (byte_pos >= visible_end)
+    {
+      beg = NULL;
+      len = 0;
+    }
+  /* Normal case, read a character.  */
+  else
+    {
+      beg = (char *) BUF_BYTE_ADDRESS (buffer, byte_pos);
+      len = BYTES_BY_CHAR_HEAD ((int) *beg);
+    }
+  /* We never let tree-sitter to parse buffers that large so this
+     assertion should never hit.  */
+  eassert (len < UINT32_MAX);
+  *bytes_read = (uint32_t) len;
+  return beg;
+}
+
+/*** Functions for parser and node object */
+
+/* Wrap the parser in a Lisp_Object to be used in the Lisp
+   machine.  */
+Lisp_Object
+make_treesit_parser (Lisp_Object buffer, TSParser *parser,
+                    TSTree *tree, Lisp_Object language_symbol)
+{
+  struct Lisp_TS_Parser *lisp_parser;
+
+  lisp_parser = ALLOCATE_PSEUDOVECTOR (struct Lisp_TS_Parser,
+                                      buffer, PVEC_TS_PARSER);
+
+  lisp_parser->language_symbol = language_symbol;
+  lisp_parser->after_change_functions = Qnil;
+  lisp_parser->buffer = buffer;
+  lisp_parser->parser = parser;
+  lisp_parser->tree = tree;
+  TSInput input = {lisp_parser, treesit_read_buffer, TSInputEncodingUTF8};
+  lisp_parser->input = input;
+  lisp_parser->need_reparse = true;
+  lisp_parser->visible_beg = BUF_BEGV (XBUFFER (buffer));
+  lisp_parser->visible_end = BUF_ZV (XBUFFER (buffer));
+  lisp_parser->timestamp = 0;
+  lisp_parser->deleted = false;
+  lisp_parser->has_range = false;
+  eassert (lisp_parser->visible_beg <= lisp_parser->visible_end);
+  return make_lisp_ptr (lisp_parser, Lisp_Vectorlike);
+}
+
+/* Wrap the node in a Lisp_Object to be used in the Lisp machine.  */
+Lisp_Object
+make_treesit_node (Lisp_Object parser, TSNode node)
+{
+  struct Lisp_TS_Node *lisp_node;
+
+  lisp_node = ALLOCATE_PSEUDOVECTOR (struct Lisp_TS_Node,
+                                    parser, PVEC_TS_NODE);
+  lisp_node->parser = parser;
+  lisp_node->node = node;
+  lisp_node->timestamp = XTS_PARSER (parser)->timestamp;
+  return make_lisp_ptr (lisp_node, Lisp_Vectorlike);
+}
+
+/* Make a compiled query.  QUERY has to be either a cons or a
+   string.  */
+static Lisp_Object
+make_treesit_query (Lisp_Object query, Lisp_Object language)
+{
+  TSQueryCursor *treesit_cursor = ts_query_cursor_new ();
+  struct Lisp_TS_Query *lisp_query;
+
+  lisp_query = ALLOCATE_PSEUDOVECTOR (struct Lisp_TS_Query,
+                                     source, PVEC_TS_COMPILED_QUERY);
+
+  lisp_query->language = language;
+  lisp_query->source = query;
+  lisp_query->query = NULL;
+  lisp_query->cursor = treesit_cursor;
+  return make_lisp_ptr (lisp_query, Lisp_Vectorlike);
+}
+
+/* The following two functions are called from alloc.c:cleanup_vector.  */
+void
+treesit_delete_parser (struct Lisp_TS_Parser *lisp_parser)
+{
+  ts_tree_delete (lisp_parser->tree);
+  ts_parser_delete (lisp_parser->parser);
+}
+
+void
+treesit_delete_query (struct Lisp_TS_Query *lisp_query)
+{
+  ts_query_delete (lisp_query->query);
+  ts_query_cursor_delete (lisp_query->cursor);
+}
+
+/* The following function is called from print.c:print_vectorlike.  */
+bool
+treesit_named_node_p (TSNode node)
+{
+  return ts_node_is_named (node);
+}
+
+static const char*
+treesit_query_error_to_string (TSQueryError error)
+{
+  switch (error)
+    {
+    case TSQueryErrorNone:
+      return "None";
+    case TSQueryErrorSyntax:
+      return "Syntax error at";
+    case TSQueryErrorNodeType:
+      return "Node type error at";
+    case TSQueryErrorField:
+      return "Field error at";
+    case TSQueryErrorCapture:
+      return "Capture error at";
+    case TSQueryErrorStructure:
+      return "Structure error at";
+    default:
+      return "Unknown error";
+    }
+}
+
+static Lisp_Object
+treesit_compose_query_signal_data (uint32_t error_offset,
+                                  TSQueryError error_type)
+{
+  return list3 (build_string (treesit_query_error_to_string (error_type)),
+               make_fixnum (error_offset + 1),
+               build_pure_c_string ("Debug the query with 
`treesit-query-validate'"));
+}
+
+/* Ensure the QUERY is compiled.  Return the TSQuery.  It could be
+   NULL if error occurs, in which case ERROR_OFFSET and ERROR_TYPE are
+   bound.  If error occurs, return NULL, and assign SIGNAL_SYMBOL and
+   SIGNAL_DATA accordingly.  */
+static TSQuery *
+treesit_ensure_query_compiled (Lisp_Object query, Lisp_Object *signal_symbol,
+                              Lisp_Object *signal_data)
+{
+  /* If query is already compiled (not null), return that, otherwise
+     compile and return it.  */
+  TSQuery *treesit_query = XTS_COMPILED_QUERY (query)->query;
+  if (treesit_query != NULL)
+    return treesit_query;
+
+  /* Get query source and TSLanguage ready.  */
+  Lisp_Object source = XTS_COMPILED_QUERY (query)->source;
+  Lisp_Object language = XTS_COMPILED_QUERY (query)->language;
+  /* This is the main reason why we compile query lazily: to avoid
+     loading languages early.  */
+  TSLanguage *treesit_lang = treesit_load_language (language, signal_symbol,
+                                                   signal_data);
+  if (treesit_lang == NULL)
+    return NULL;
+
+  if (CONSP (source))
+    source = Ftreesit_query_expand (source);
+
+  /* Create TSQuery.  */
+  uint32_t error_offset;
+  TSQueryError error_type;
+  char *treesit_source = SSDATA (source);
+  treesit_query = ts_query_new (treesit_lang, treesit_source,
+                               strlen (treesit_source),
+                               &error_offset, &error_type);
+  if (treesit_query == NULL)
+    {
+      *signal_symbol = Qtreesit_query_error;
+      *signal_data = treesit_compose_query_signal_data (error_offset,
+                                                       error_type);
+    }
+  XTS_COMPILED_QUERY (query)->query = treesit_query;
+  return treesit_query;
+}
+
+DEFUN ("treesit-parser-p",
+       Ftreesit_parser_p, Streesit_parser_p, 1, 1, 0,
+       doc: /* Return t if OBJECT is a tree-sitter parser.  */)
+  (Lisp_Object object)
+{
+  if (TS_PARSERP (object))
+    return Qt;
+  else
+    return Qnil;
+}
+
+DEFUN ("treesit-node-p",
+       Ftreesit_node_p, Streesit_node_p, 1, 1, 0,
+       doc: /* Return t if OBJECT is a tree-sitter node.  */)
+  (Lisp_Object object)
+{
+  if (TS_NODEP (object))
+    return Qt;
+  else
+    return Qnil;
+}
+
+DEFUN ("treesit-compiled-query-p",
+       Ftreesit_compiled_query_p, Streesit_compiled_query_p, 1, 1, 0,
+       doc: /* Return t if OBJECT is a compiled tree-sitter query.  */)
+  (Lisp_Object object)
+{
+  if (TS_COMPILED_QUERY_P (object))
+    return Qt;
+  else
+    return Qnil;
+}
+
+DEFUN ("treesit-query-p",
+       Ftreesit_query_p, Streesit_query_p, 1, 1, 0,
+       doc: /* Return t if OBJECT is a generic tree-sitter query.  */)
+  (Lisp_Object object)
+{
+  if (TS_COMPILED_QUERY_P (object)
+      || CONSP (object) || STRINGP (object))
+    return Qt;
+  else
+    return Qnil;
+}
+
+DEFUN ("treesit-query-language",
+       Ftreesit_query_language, Streesit_query_language, 1, 1, 0,
+       doc: /* Return the language of QUERY.
+QUERY has to be a compiled query.  */)
+  (Lisp_Object query)
+{
+  CHECK_TS_COMPILED_QUERY (query);
+  return XTS_COMPILED_QUERY (query)->language;
+}
+
+DEFUN ("treesit-node-parser",
+       Ftreesit_node_parser, Streesit_node_parser,
+       1, 1, 0,
+       doc: /* Return the parser to which NODE belongs.  */)
+  (Lisp_Object node)
+{
+  CHECK_TS_NODE (node);
+  return XTS_NODE (node)->parser;
+}
+
+DEFUN ("treesit-parser-create",
+       Ftreesit_parser_create, Streesit_parser_create,
+       1, 3, 0,
+       doc: /* Create and return a parser in BUFFER for LANGUAGE.
+
+The parser is automatically added to BUFFER's parser list, as
+returned by `treesit-parser-list'.
+LANGUAGE is a language symbol.  If BUFFER is nil or omitted, it
+defaults to the current buffer.  If BUFFER already has a parser for
+LANGUAGE, return that parser, but if NO-REUSE is non-nil, always
+create a new parser.  */)
+  (Lisp_Object language, Lisp_Object buffer, Lisp_Object no_reuse)
+{
+  treesit_initialize ();
+
+  CHECK_SYMBOL (language);
+  struct buffer *buf;
+  if (NILP (buffer))
+    buf = current_buffer;
+  else
+    {
+      CHECK_BUFFER (buffer);
+      buf = XBUFFER (buffer);
+    }
+  treesit_check_buffer_size (buf);
+
+  /* See if we can reuse a parser.  */
+  for (Lisp_Object tail = BVAR (buf, ts_parser_list);
+       NILP (no_reuse) && !NILP (tail);
+       tail = XCDR (tail))
+    {
+      struct Lisp_TS_Parser *parser = XTS_PARSER (XCAR (tail));
+      if (EQ (parser->language_symbol, language))
+       return XCAR (tail);
+    }
+
+  /* Load language.  */
+  Lisp_Object signal_symbol = Qnil;
+  Lisp_Object signal_data = Qnil;
+  TSParser *parser = ts_parser_new ();
+  TSLanguage *lang = treesit_load_language (language, &signal_symbol,
+                                           &signal_data);
+  if (lang == NULL)
+    xsignal (signal_symbol, signal_data);
+  /* We check language version when loading a language, so this should
+     always succeed.  */
+  ts_parser_set_language (parser, lang);
+
+  /* Create parser.  */
+  Lisp_Object lisp_parser = make_treesit_parser (Fcurrent_buffer (),
+                                                parser, NULL,
+                                                language);
+
+  /* Update parser-list.  */
+  BVAR (buf, ts_parser_list) = Fcons (lisp_parser, BVAR (buf, ts_parser_list));
+
+  return lisp_parser;
+}
+
+DEFUN ("treesit-parser-delete",
+       Ftreesit_parser_delete, Streesit_parser_delete,
+       1, 1, 0,
+       doc: /* Delete PARSER from its buffer's parser list.
+See `treesit-parser-list' for the buffer's parser list.  */)
+  (Lisp_Object parser)
+{
+  treesit_check_parser (parser);
+
+  Lisp_Object buffer = XTS_PARSER (parser)->buffer;
+  struct buffer *buf = XBUFFER (buffer);
+
+  BVAR (buf, ts_parser_list)
+    = Fdelete (parser, BVAR (buf, ts_parser_list));
+
+  XTS_PARSER (parser)->deleted = true;
+  return Qnil;
+}
+
+DEFUN ("treesit-parser-list",
+       Ftreesit_parser_list, Streesit_parser_list,
+       0, 1, 0,
+       doc: /* Return BUFFER's parser list.
+BUFFER defaults to the current buffer.  */)
+  (Lisp_Object buffer)
+{
+  struct buffer *buf;
+  if (NILP (buffer))
+    buf = current_buffer;
+  else
+    {
+      CHECK_BUFFER (buffer);
+      buf = XBUFFER (buffer);
+    }
+  /* Return a fresh list so messing with that list doesn't affect our
+     internal data.  */
+  Lisp_Object return_list = Qnil;
+  Lisp_Object tail;
+
+  tail = BVAR (buf, ts_parser_list);
+
+  FOR_EACH_TAIL (tail)
+    return_list = Fcons (XCAR (tail), return_list);
+
+  return Freverse (return_list);
+}
+
+DEFUN ("treesit-parser-buffer",
+       Ftreesit_parser_buffer, Streesit_parser_buffer,
+       1, 1, 0,
+       doc: /* Return the buffer of PARSER.  */)
+  (Lisp_Object parser)
+{
+  treesit_check_parser (parser);
+  Lisp_Object buf;
+  XSETBUFFER (buf, XBUFFER (XTS_PARSER (parser)->buffer));
+  return buf;
+}
+
+DEFUN ("treesit-parser-language",
+       Ftreesit_parser_language, Streesit_parser_language,
+       1, 1, 0,
+       doc: /* Return PARSER's language symbol.
+This symbol is the one used to create the parser.  */)
+  (Lisp_Object parser)
+{
+  treesit_check_parser (parser);
+  return XTS_PARSER (parser)->language_symbol;
+}
+
+/*** Parser API */
+
+DEFUN ("treesit-parser-root-node",
+       Ftreesit_parser_root_node, Streesit_parser_root_node,
+       1, 1, 0,
+       doc: /* Return the root node of PARSER.  */)
+  (Lisp_Object parser)
+{
+  treesit_check_parser (parser);
+  treesit_initialize ();
+  treesit_ensure_parsed (parser);
+  TSNode root_node = ts_tree_root_node (XTS_PARSER (parser)->tree);
+  return make_treesit_node (parser, root_node);
+}
+
+/* Checks that the RANGES argument of
+   treesit-parser-set-included-ranges is valid.  */
+static void
+treesit_check_range_argument (Lisp_Object ranges)
+{
+  struct buffer *buffer = current_buffer;
+  ptrdiff_t point_min = BUF_BEGV (buffer);
+  ptrdiff_t point_max = BUF_ZV (buffer);
+  EMACS_INT last_point = point_min;
+  Lisp_Object tail;
+
+  tail = ranges;
+
+  CHECK_LIST (tail);
+
+  FOR_EACH_TAIL (tail)
+    {
+      CHECK_CONS (tail);
+      Lisp_Object range = XCAR (tail);
+      CHECK_CONS (range);
+      CHECK_FIXNUM (XCAR (range));
+      CHECK_FIXNUM (XCDR (range));
+      EMACS_INT beg = XFIXNUM (XCAR (range));
+      EMACS_INT end = XFIXNUM (XCDR (range));
+      if (!(last_point <= beg && beg <= end && end <= point_max))
+       xsignal2 (Qtreesit_range_invalid,
+                 build_pure_c_string ("RANGE is either overlapping,"
+                                      " out-of-order or out-of-range"),
+                 ranges);
+      last_point = end;
+    }
+
+  CHECK_LIST_END (tail, ranges);
+}
+
+/* Generate a list of ranges in Lisp from RANGES.  This function
+   doesn't take ownership of RANGES.  BUFFER is used to convert
+   between tree-sitter buffer offset and buffer position.  */
+static Lisp_Object
+treesit_make_ranges (const TSRange *ranges, uint32_t len,
+                    struct buffer *buffer)
+{
+  Lisp_Object list = Qnil;
+  for (int idx = 0; idx < len; idx++)
+    {
+      TSRange range = ranges[idx];
+      uint32_t beg_byte = range.start_byte + BUF_BEGV_BYTE (buffer);
+      uint32_t end_byte = range.end_byte + BUF_BEGV_BYTE (buffer);
+      eassert (BUF_BEGV_BYTE (buffer) <= beg_byte);
+      eassert (beg_byte <= end_byte);
+      eassert (end_byte <= BUF_ZV_BYTE (buffer));
+
+      Lisp_Object lisp_range
+       = Fcons (make_fixnum (buf_bytepos_to_charpos (buffer, beg_byte)),
+                make_fixnum (buf_bytepos_to_charpos (buffer, end_byte)));
+      list = Fcons (lisp_range, list);
+    }
+  return Fnreverse (list);
+}
+
+DEFUN ("treesit-parser-set-included-ranges",
+       Ftreesit_parser_set_included_ranges,
+       Streesit_parser_set_included_ranges,
+       2, 2, 0,
+       doc: /* Limit PARSER to RANGES.
+
+RANGES is a list of (BEG . END), each (BEG . END) defines a region in
+which the parser should operate.  Regions must not overlap, and the
+regions should come in order in the list.  Signal
+`treesit-set-range-error' if the argument is invalid, or something
+else went wrong.  If RANGES is nil, the PARSER is to parse the whole
+buffer.  */)
+  (Lisp_Object parser, Lisp_Object ranges)
+{
+  treesit_check_parser (parser);
+  if (!NILP (ranges))
+    CHECK_CONS (ranges);
+  treesit_check_range_argument (ranges);
+
+  treesit_initialize ();
+  /* Before we parse, catch up with narrowing/widening.  */
+  treesit_check_buffer_size (XBUFFER (XTS_PARSER (parser)->buffer));
+  treesit_ensure_position_synced (parser);
+
+  bool success;
+  if (NILP (ranges))
+    {
+      XTS_PARSER (parser)->has_range = false;
+      /* If RANGES is nil, make parser to parse the whole document.
+        To do that we give tree-sitter a 0 length, the range is a
+        dummy.  */
+      TSRange treesit_range = {{0, 0}, {0, 0}, 0, 0};
+      success = ts_parser_set_included_ranges (XTS_PARSER (parser)->parser,
+                                              &treesit_range , 0);
+    }
+  else
+    {
+      /* Set ranges for PARSER.  */
+      XTS_PARSER (parser)->has_range = true;
+
+      if (list_length (ranges) > UINT32_MAX)
+       xsignal (Qargs_out_of_range, list2 (ranges, Flength (ranges)));
+      uint32_t len = (uint32_t) list_length (ranges);
+      TSRange *treesit_ranges = xmalloc (sizeof (TSRange) * len);
+      struct buffer *buffer = XBUFFER (XTS_PARSER (parser)->buffer);
+
+      for (int idx = 0; !NILP (ranges); idx++, ranges = XCDR (ranges))
+       {
+         Lisp_Object range = XCAR (ranges);
+         EMACS_INT beg_byte = buf_charpos_to_bytepos (buffer,
+                                                      XFIXNUM (XCAR (range)));
+         EMACS_INT end_byte = buf_charpos_to_bytepos (buffer,
+                                                      XFIXNUM (XCDR (range)));
+         /* Shouldn't violate assertion since we just checked for
+            buffer size at the beginning of this function.  */
+         eassert (beg_byte - BUF_BEGV_BYTE (buffer) <= UINT32_MAX);
+         eassert (end_byte - BUF_BEGV_BYTE (buffer) <= UINT32_MAX);
+         /* We don't care about start and end points, put in dummy
+            values.  */
+         TSRange rg = {{0, 0}, {0, 0},
+                       (uint32_t) beg_byte - BUF_BEGV_BYTE (buffer),
+                       (uint32_t) end_byte - BUF_BEGV_BYTE (buffer)};
+         treesit_ranges[idx] = rg;
+       }
+      success = ts_parser_set_included_ranges (XTS_PARSER (parser)->parser,
+                                              treesit_ranges, len);
+      /* Although XFIXNUM could signal, it should be impossible
+        because we have checked the input by treesit_check_range_argument.
+        So there is no need for unwind-protect.  */
+      xfree (treesit_ranges);
+    }
+
+  if (!success)
+    xsignal2 (Qtreesit_range_invalid,
+             build_pure_c_string ("Something went wrong when setting ranges"),
+             ranges);
+
+  XTS_PARSER (parser)->need_reparse = true;
+  return Qnil;
+}
+
+DEFUN ("treesit-parser-included-ranges",
+       Ftreesit_parser_included_ranges,
+       Streesit_parser_included_ranges,
+       1, 1, 0,
+       doc: /* Return the ranges set for PARSER.
+See `treesit-parser-set-ranges'.  If no ranges are set for PARSER,
+return nil.  */)
+  (Lisp_Object parser)
+{
+  treesit_check_parser (parser);
+  treesit_initialize ();
+
+  /* When the parser doesn't have a range set and we call
+     ts_parser_included_ranges on it, it doesn't return an empty list,
+     but rather return some garbled data. (A single range where
+     start_byte = 0, end_byte = UINT32_MAX).  So we need to track
+     whether the parser is ranged ourselves.  */
+  if (!XTS_PARSER (parser)->has_range)
+    return Qnil;
+
+  uint32_t len;
+  const TSRange *ranges
+    = ts_parser_included_ranges (XTS_PARSER (parser)->parser, &len);
+
+  /* Our return value depends on the buffer state (BUF_BEGV_BYTE,
+     etc), so we need to sync up.  */
+  treesit_check_buffer_size (XBUFFER (XTS_PARSER (parser)->buffer));
+  treesit_ensure_position_synced (parser);
+
+  struct buffer *buffer = XBUFFER (XTS_PARSER (parser)->buffer);
+  return treesit_make_ranges (ranges, len, buffer);
+}
+
+DEFUN ("treesit-parser-notifiers", Ftreesit_parser_notifiers,
+       Streesit_parser_notifiers,
+       1, 1, 0,
+       doc: /* Return the list of after-change notifier functions for PARSER.  
*/)
+  (Lisp_Object parser)
+{
+  treesit_check_parser (parser);
+
+  Lisp_Object return_list = Qnil;
+  Lisp_Object tail = XTS_PARSER (parser)->after_change_functions;
+  FOR_EACH_TAIL (tail)
+    return_list = Fcons (XCAR (tail), return_list);
+
+  return return_list;
+}
+
+DEFUN ("treesit-parser-add-notifier", Ftreesit_parser_add_notifier,
+       Streesit_parser_add_notifier,
+       2, 2, 0,
+       doc: /* Add FUNCTION to the list of PARSER's after-change notifiers.
+FUNCTION must be a function symbol, rather than a lambda form.
+FUNCTION should take 2 arguments, RANGES and PARSER.  RANGES is a list
+of cons cells of the form (START . END), where START and END are buffer
+positions.  PARSER is the parser issuing the notification.  */)
+  (Lisp_Object parser, Lisp_Object function)
+{
+  treesit_check_parser (parser);
+  /* For simplicity we don't accept lambda functions.  */
+  CHECK_SYMBOL (function);
+
+  Lisp_Object functions = XTS_PARSER (parser)->after_change_functions;
+  if (!Fmemq (function, functions))
+    XTS_PARSER (parser)->after_change_functions = Fcons (function, functions);
+  return Qnil;
+}
+
+DEFUN ("treesit-parser-remove-notifier", Ftreesit_parser_remove_notifier,
+       Streesit_parser_remove_notifier,
+       2, 2, 0,
+       doc: /* Remove FUNCTION from the list of PARSER's after-change 
notifiers.
+  FUNCTION must be a function symbol, rather than a lambda form.
+FUNCTION should take 2 arguments, RANGES and PARSER.  RANGES is a list
+of cons of the form (START . END), where START and END are buffer
+positions.  PARSER is the parser issuing the notification.   */)
+  (Lisp_Object parser, Lisp_Object function)
+{
+  treesit_check_parser (parser);
+  /* For simplicity we don't accept lambda functions.  */
+  CHECK_SYMBOL (function);
+
+  Lisp_Object functions = XTS_PARSER (parser)->after_change_functions;
+  if (Fmemq (function, functions))
+    XTS_PARSER (parser)->after_change_functions = Fdelq (function, functions);
+  return Qnil;
+}
+
+/*** Node API  */
+
+/* Check that OBJ is a positive integer and signal an error if
+   otherwise.  */
+static void
+treesit_check_positive_integer (Lisp_Object obj)
+{
+  CHECK_INTEGER (obj);
+  if (XFIXNUM (obj) < 0)
+    xsignal1 (Qargs_out_of_range, obj);
+}
+
+static void
+treesit_check_node (Lisp_Object obj)
+{
+  CHECK_TS_NODE (obj);
+  if (!treesit_node_uptodate_p (obj))
+    xsignal1 (Qtreesit_node_outdated, obj);
+}
+
+bool
+treesit_node_uptodate_p (Lisp_Object obj)
+{
+  Lisp_Object lisp_parser = XTS_NODE (obj)->parser;
+  return XTS_NODE (obj)->timestamp == XTS_PARSER (lisp_parser)->timestamp;
+}
+
+DEFUN ("treesit-node-type",
+       Ftreesit_node_type, Streesit_node_type, 1, 1, 0,
+       doc: /* Return the NODE's type as a string.
+If NODE is nil, return nil.  */)
+  (Lisp_Object node)
+{
+  if (NILP (node)) return Qnil;
+  treesit_check_node (node);
+  treesit_initialize ();
+
+  TSNode treesit_node = XTS_NODE (node)->node;
+  const char *type = ts_node_type (treesit_node);
+  return build_string (type);
+}
+
+DEFUN ("treesit-node-start",
+       Ftreesit_node_start, Streesit_node_start, 1, 1, 0,
+       doc: /* Return the NODE's start position in its buffer.
+If NODE is nil, return nil.  */)
+  (Lisp_Object node)
+{
+  if (NILP (node)) return Qnil;
+  treesit_check_node (node);
+  treesit_initialize ();
+
+  TSNode treesit_node = XTS_NODE (node)->node;
+  ptrdiff_t visible_beg = XTS_PARSER (XTS_NODE (node)->parser)->visible_beg;
+  uint32_t start_byte_offset = ts_node_start_byte (treesit_node);
+  struct buffer *buffer
+    = XBUFFER (XTS_PARSER (XTS_NODE (node)->parser)->buffer);
+  ptrdiff_t start_pos
+    = buf_bytepos_to_charpos (buffer,
+                             start_byte_offset + visible_beg);
+  return make_fixnum (start_pos);
+}
+
+DEFUN ("treesit-node-end",
+       Ftreesit_node_end, Streesit_node_end, 1, 1, 0,
+       doc: /* Return the NODE's end position in its buffer.
+If NODE is nil, return nil.  */)
+  (Lisp_Object node)
+{
+  if (NILP (node)) return Qnil;
+  treesit_check_node (node);
+  treesit_initialize ();
+
+  TSNode treesit_node = XTS_NODE (node)->node;
+  ptrdiff_t visible_beg = XTS_PARSER (XTS_NODE (node)->parser)->visible_beg;
+  uint32_t end_byte_offset = ts_node_end_byte (treesit_node);
+  struct buffer *buffer
+    = XBUFFER (XTS_PARSER (XTS_NODE (node)->parser)->buffer);
+  ptrdiff_t end_pos
+    = buf_bytepos_to_charpos (buffer, end_byte_offset + visible_beg);
+  return make_fixnum (end_pos);
+}
+
+DEFUN ("treesit-node-string",
+       Ftreesit_node_string, Streesit_node_string, 1, 1, 0,
+       doc: /* Return the string representation of NODE.
+If NODE is nil, return nil.  */)
+  (Lisp_Object node)
+{
+  if (NILP (node)) return Qnil;
+  treesit_check_node (node);
+  treesit_initialize ();
+
+  TSNode treesit_node = XTS_NODE (node)->node;
+  char *string = ts_node_string (treesit_node);
+  return build_string (string);
+}
+
+DEFUN ("treesit-node-parent",
+       Ftreesit_node_parent, Streesit_node_parent, 1, 1, 0,
+       doc: /* Return the immediate parent of NODE.
+Return nil if NODE has no parent.  If NODE is nil, return nil.  */)
+  (Lisp_Object node)
+{
+  if (NILP (node)) return Qnil;
+  treesit_check_node (node);
+  treesit_initialize ();
+
+  TSNode treesit_node = XTS_NODE (node)->node;
+  TSNode parent = ts_node_parent (treesit_node);
+
+  if (ts_node_is_null (parent))
+    return Qnil;
+
+  return make_treesit_node (XTS_NODE (node)->parser, parent);
+}
+
+DEFUN ("treesit-node-child",
+       Ftreesit_node_child, Streesit_node_child, 2, 3, 0,
+       doc: /* Return the Nth child of NODE.
+
+Return nil if there is no Nth child.  If NAMED is non-nil, look for
+named child only.  NAMED defaults to nil.  If NODE is nil, return
+nil.
+
+N could be negative, e.g., -1 represents the last child.  */)
+  (Lisp_Object node, Lisp_Object n, Lisp_Object named)
+{
+  if (NILP (node)) return Qnil;
+  treesit_check_node (node);
+  CHECK_INTEGER (n);
+  EMACS_INT idx = XFIXNUM (n);
+
+  treesit_initialize ();
+
+  TSNode treesit_node = XTS_NODE (node)->node;
+  TSNode child;
+
+  /* Process negative index.  */
+  if (idx < 0)
+    {
+      if (NILP (named))
+       idx = ts_node_child_count (treesit_node) + idx;
+      else
+       idx = ts_node_named_child_count (treesit_node) + idx;
+    }
+  if (idx < 0)
+    return Qnil;
+  if (idx > UINT32_MAX)
+    xsignal1 (Qargs_out_of_range, n);
+
+  if (NILP (named))
+    child = ts_node_child (treesit_node, (uint32_t) idx);
+  else
+    child = ts_node_named_child (treesit_node, (uint32_t) idx);
+
+  if (ts_node_is_null (child))
+    return Qnil;
+
+  return make_treesit_node (XTS_NODE (node)->parser, child);
+}
+
+DEFUN ("treesit-node-check",
+       Ftreesit_node_check, Streesit_node_check, 2, 2, 0,
+       doc: /* Return non-nil if NODE has PROPERTY, nil otherwise.
+
+PROPERTY could be `named', `missing', `extra', `outdated', or `has-error'.
+
+Named nodes correspond to named rules in the language definition,
+whereas "anonymous" nodes correspond to string literals in the
+language definition.
+
+Missing nodes are inserted by the parser in order to recover from
+certain kinds of syntax errors, i.e., should be there but not there.
+
+Extra nodes represent things like comments, which are not required the
+language definition, but can appear anywhere.
+
+A node is "outdated" if the parser has reparsed at least once after
+the node was created.
+
+A node "has error" if itself is a syntax error or contains any syntax
+errors.  */)
+  (Lisp_Object node, Lisp_Object property)
+{
+  if (NILP (node)) return Qnil;
+  CHECK_TS_NODE (node);
+  CHECK_SYMBOL (property);
+  treesit_initialize ();
+
+  TSNode treesit_node = XTS_NODE (node)->node;
+  bool result;
+
+  if (EQ (property, Qoutdated))
+    return treesit_node_uptodate_p (node) ? Qnil : Qt;
+
+  treesit_check_node (node);
+  if (EQ (property, Qnamed))
+    result = ts_node_is_named (treesit_node);
+  else if (EQ (property, Qmissing))
+    result = ts_node_is_missing (treesit_node);
+  else if (EQ (property, Qextra))
+    result = ts_node_is_extra (treesit_node);
+  else if (EQ (property, Qhas_error))
+    result = ts_node_has_error (treesit_node);
+  else
+    signal_error ("Expecting `named', `missing', `extra', "
+                 "`outdated', or `has-error', but got",
+                 property);
+  return result ? Qt : Qnil;
+}
+
+DEFUN ("treesit-node-field-name-for-child",
+       Ftreesit_node_field_name_for_child,
+       Streesit_node_field_name_for_child, 2, 2, 0,
+       doc: /* Return the field name of the Nth child of NODE.
+
+Return nil if there's no Nth child, or if it has no field.
+If NODE is nil, return nil.
+
+N counts all children, i.e., named ones and anonymous ones.
+
+N could be negative, e.g., -1 represents the last child.  */)
+  (Lisp_Object node, Lisp_Object n)
+{
+  if (NILP (node))
+    return Qnil;
+  treesit_check_node (node);
+  CHECK_INTEGER (n);
+  EMACS_INT idx = XFIXNUM (n);
+  treesit_initialize ();
+
+  TSNode treesit_node = XTS_NODE (node)->node;
+
+  /* Process negative index.  */
+  if (idx < 0)
+    idx = ts_node_child_count (treesit_node) + idx;
+  if (idx < 0)
+    return Qnil;
+  if (idx > UINT32_MAX)
+    xsignal1 (Qargs_out_of_range, n);
+
+  const char *name
+    = ts_node_field_name_for_child (treesit_node, (uint32_t) idx);
+
+  if (name == NULL)
+    return Qnil;
+
+  return build_string (name);
+}
+
+DEFUN ("treesit-node-child-count",
+       Ftreesit_node_child_count,
+       Streesit_node_child_count, 1, 2, 0,
+       doc: /* Return the number of children of NODE.
+
+If NAMED is non-nil, count named children only.  NAMED defaults to
+nil.  If NODE is nil, return nil.  */)
+  (Lisp_Object node, Lisp_Object named)
+{
+  if (NILP (node))
+    return Qnil;
+  treesit_check_node (node);
+  treesit_initialize ();
+
+  TSNode treesit_node = XTS_NODE (node)->node;
+  uint32_t count;
+  if (NILP (named))
+    count = ts_node_child_count (treesit_node);
+  else
+    count = ts_node_named_child_count (treesit_node);
+  return make_fixnum (count);
+}
+
+DEFUN ("treesit-node-child-by-field-name",
+       Ftreesit_node_child_by_field_name,
+       Streesit_node_child_by_field_name, 2, 2, 0,
+       doc: /* Return the child of NODE with FIELD-NAME.
+Return nil if there is no such child.  If NODE is nil, return nil.  */)
+  (Lisp_Object node, Lisp_Object field_name)
+{
+  if (NILP (node))
+    return Qnil;
+  treesit_check_node (node);
+  CHECK_STRING (field_name);
+  treesit_initialize ();
+
+  char *name_str = SSDATA (field_name);
+  TSNode treesit_node = XTS_NODE (node)->node;
+  TSNode child
+    = ts_node_child_by_field_name (treesit_node, name_str,
+                                  strlen (name_str));
+
+  if (ts_node_is_null (child))
+    return Qnil;
+
+  return make_treesit_node (XTS_NODE (node)->parser, child);
+}
+
+DEFUN ("treesit-node-next-sibling",
+       Ftreesit_node_next_sibling,
+       Streesit_node_next_sibling, 1, 2, 0,
+       doc: /* Return the next sibling of NODE.
+
+Return nil if there is no next sibling.  If NAMED is non-nil, look for named
+siblings only.  NAMED defaults to nil.  If NODE is nil, return nil.  */)
+  (Lisp_Object node, Lisp_Object named)
+{
+  if (NILP (node)) return Qnil;
+  treesit_check_node (node);
+  treesit_initialize ();
+
+  TSNode treesit_node = XTS_NODE (node)->node;
+  TSNode sibling;
+  if (NILP (named))
+    sibling = ts_node_next_sibling (treesit_node);
+  else
+    sibling = ts_node_next_named_sibling (treesit_node);
+
+  if (ts_node_is_null (sibling))
+    return Qnil;
+
+  return make_treesit_node (XTS_NODE (node)->parser, sibling);
+}
+
+DEFUN ("treesit-node-prev-sibling",
+       Ftreesit_node_prev_sibling,
+       Streesit_node_prev_sibling, 1, 2, 0,
+       doc: /* Return the previous sibling of NODE.
+
+Return nil if there is no previous sibling.  If NAMED is non-nil, look
+for named siblings only.  NAMED defaults to nil.  If NODE is nil,
+return nil.  */)
+  (Lisp_Object node, Lisp_Object named)
+{
+  if (NILP (node)) return Qnil;
+  treesit_check_node (node);
+  treesit_initialize ();
+
+  TSNode treesit_node = XTS_NODE (node)->node;
+  TSNode sibling;
+
+  if (NILP (named))
+    sibling = ts_node_prev_sibling (treesit_node);
+  else
+    sibling = ts_node_prev_named_sibling (treesit_node);
+
+  if (ts_node_is_null (sibling))
+    return Qnil;
+
+  return make_treesit_node (XTS_NODE (node)->parser, sibling);
+}
+
+DEFUN ("treesit-node-first-child-for-pos",
+       Ftreesit_node_first_child_for_pos,
+       Streesit_node_first_child_for_pos, 2, 3, 0,
+       doc: /* Return the first child of NODE for buffer position POS.
+
+Specifically, return the first child that extends beyond POS.
+Return nil if there is no such child.
+If NAMED is non-nil, look for named children only.  NAMED defaults to nil.
+Note that this function returns an immediate child, not the smallest
+(grand)child.  If NODE is nil, return nil.  */)
+  (Lisp_Object node, Lisp_Object pos, Lisp_Object named)
+{
+  if (NILP (node))
+    return Qnil;
+  treesit_check_node (node);
+  treesit_check_positive_integer (pos);
+
+  struct buffer *buf = XBUFFER (XTS_PARSER (XTS_NODE (node)->parser)->buffer);
+  ptrdiff_t visible_beg = XTS_PARSER (XTS_NODE (node)->parser)->visible_beg;
+  ptrdiff_t byte_pos = buf_charpos_to_bytepos (buf, XFIXNUM (pos));
+
+  if (byte_pos < BUF_BEGV_BYTE (buf) || byte_pos > BUF_ZV_BYTE (buf))
+    xsignal1 (Qargs_out_of_range, pos);
+
+  treesit_initialize ();
+
+  TSNode treesit_node = XTS_NODE (node)->node;
+  TSNode child;
+  if (NILP (named))
+    child = ts_node_first_child_for_byte (treesit_node, byte_pos - 
visible_beg);
+  else
+    child = ts_node_first_named_child_for_byte (treesit_node,
+                                               byte_pos - visible_beg);
+
+  if (ts_node_is_null (child))
+    return Qnil;
+
+  return make_treesit_node (XTS_NODE (node)->parser, child);
+}
+
+DEFUN ("treesit-node-descendant-for-range",
+       Ftreesit_node_descendant_for_range,
+       Streesit_node_descendant_for_range, 3, 4, 0,
+       doc: /* Return the smallest node that covers buffer positions BEG to 
END.
+
+The returned node is a descendant of NODE.
+Return nil if there is no such node.
+If NAMED is non-nil, look for named child only.  NAMED defaults to nil.
+If NODE is nil, return nil.  */)
+  (Lisp_Object node, Lisp_Object beg, Lisp_Object end, Lisp_Object named)
+{
+  if (NILP (node)) return Qnil;
+  treesit_check_node (node);
+  CHECK_INTEGER (beg);
+  CHECK_INTEGER (end);
+
+  struct buffer *buf = XBUFFER (XTS_PARSER (XTS_NODE (node)->parser)->buffer);
+  ptrdiff_t visible_beg = XTS_PARSER (XTS_NODE (node)->parser)->visible_beg;
+  ptrdiff_t byte_beg = buf_charpos_to_bytepos (buf, XFIXNUM (beg));
+  ptrdiff_t byte_end = buf_charpos_to_bytepos (buf, XFIXNUM (end));
+
+  /* Checks for BUFFER_BEG <= BEG <= END <= BUFFER_END.  */
+  if (!(BUF_BEGV_BYTE (buf) <= byte_beg
+       && byte_beg <= byte_end
+       && byte_end <= BUF_ZV_BYTE (buf)))
+    xsignal2 (Qargs_out_of_range, beg, end);
+
+  treesit_initialize ();
+
+  TSNode treesit_node = XTS_NODE (node)->node;
+  TSNode child;
+  if (NILP (named))
+    child = ts_node_descendant_for_byte_range (treesit_node, byte_beg - 
visible_beg,
+                                              byte_end - visible_beg);
+  else
+    child = ts_node_named_descendant_for_byte_range (treesit_node,
+                                                    byte_beg - visible_beg,
+                                                    byte_end - visible_beg);
+
+  if (ts_node_is_null (child))
+    return Qnil;
+
+  return make_treesit_node (XTS_NODE (node)->parser, child);
+}
+
+DEFUN ("treesit-node-eq",
+       Ftreesit_node_eq,
+       Streesit_node_eq, 2, 2, 0,
+       doc: /* Return non-nil if NODE1 and NODE2 are the same node.
+If any one of NODE1 and NODE2 is nil, return nil.  */)
+  (Lisp_Object node1, Lisp_Object node2)
+{
+  if (NILP (node1) || NILP (node2))
+    return Qnil;
+  CHECK_TS_NODE (node1);
+  CHECK_TS_NODE (node2);
+
+  treesit_initialize ();
+
+  TSNode treesit_node_1 = XTS_NODE (node1)->node;
+  TSNode treesit_node_2 = XTS_NODE (node2)->node;
+
+  bool same_node = ts_node_eq (treesit_node_1, treesit_node_2);
+  return same_node ? Qt : Qnil;
+}
+
+/*** Query functions */
+
+DEFUN ("treesit-pattern-expand",
+       Ftreesit_pattern_expand,
+       Streesit_pattern_expand, 1, 1, 0,
+       doc: /* Expand PATTERN to its string form.
+
+PATTERN can be
+
+    :anchor
+    :?
+    :*
+    :+
+    :equal
+    :match
+    (TYPE PATTERN...)
+    [PATTERN...]
+    FIELD-NAME:
+    @CAPTURE-NAME
+    (_)
+    _
+    \"TYPE\"
+
+See Info node `(elisp)Pattern Matching' for detailed explanation.  */)
+  (Lisp_Object pattern)
+{
+  if (EQ (pattern, QCanchor))
+    return build_pure_c_string (".");
+  if (EQ (pattern, intern_c_string (":?")))
+    return build_pure_c_string ("?");
+  if (EQ (pattern, intern_c_string (":*")))
+    return build_pure_c_string ("*");
+  if (EQ (pattern, intern_c_string (":+")))
+    return build_pure_c_string ("+");
+  if (EQ (pattern, QCequal))
+    return build_pure_c_string ("#equal");
+  if (EQ (pattern, QCmatch))
+    return build_pure_c_string ("#match");
+  Lisp_Object opening_delimeter
+    = build_pure_c_string (VECTORP (pattern) ? "[" : "(");
+  Lisp_Object closing_delimiter
+    = build_pure_c_string (VECTORP (pattern) ? "]" : ")");
+  if (VECTORP (pattern) || CONSP (pattern))
+    return concat3 (opening_delimeter,
+                   Fmapconcat (Qtreesit_pattern_expand,
+                               pattern,
+                               build_pure_c_string (" ")),
+                   closing_delimiter);
+  return CALLN (Fformat, build_pure_c_string ("%S"), pattern);
+}
+
+DEFUN ("treesit-query-expand",
+       Ftreesit_query_expand,
+       Streesit_query_expand, 1, 1, 0,
+       doc: /* Expand sexp QUERY to its string form.
+
+A PATTERN in QUERY can be
+
+    :anchor
+    :?
+    :*
+    :+
+    :equal
+    :match
+    (TYPE PATTERN...)
+    [PATTERN...]
+    FIELD-NAME:
+    @CAPTURE-NAME
+    (_)
+    _
+    \"TYPE\"
+
+See Info node `(elisp)Pattern Matching' for detailed explanation.  */)
+  (Lisp_Object query)
+{
+  return Fmapconcat (Qtreesit_pattern_expand,
+                    query, build_pure_c_string (" "));
+}
+
+/* This struct is used for passing captures to be check against
+   predicates.  Captures we check for are the ones in START before
+   END.  For example, if START and END are
+
+   START       END
+    v              v
+   (1 . (2 . (3 . (4 . (5 . (6 . nil))))))
+
+   We only look at captures 1 2 3.  */
+struct capture_range
+{
+  Lisp_Object start;
+  Lisp_Object end;
+};
+
+/* Collect predicates for this match and return them in a list.  Each
+   predicate is a list of strings and symbols.  */
+static Lisp_Object
+treesit_predicates_for_pattern (TSQuery *query, uint32_t pattern_index)
+{
+  uint32_t len;
+  const TSQueryPredicateStep *predicate_list
+    = ts_query_predicates_for_pattern (query, pattern_index, &len);
+  Lisp_Object result = Qnil;
+  Lisp_Object predicate = Qnil;
+  for (int idx = 0; idx < len; idx++)
+    {
+      TSQueryPredicateStep step = predicate_list[idx];
+      switch (step.type)
+       {
+       case TSQueryPredicateStepTypeCapture:
+         {
+           uint32_t str_len;
+           const char *str = ts_query_capture_name_for_id (query,
+                                                           step.value_id,
+                                                           &str_len);
+           predicate = Fcons (intern_c_string_1 (str, str_len),
+                              predicate);
+           break;
+         }
+       case TSQueryPredicateStepTypeString:
+         {
+           uint32_t str_len;
+           const char *str = ts_query_string_value_for_id (query,
+                                                           step.value_id,
+                                                           &str_len);
+           predicate = Fcons (make_string (str, str_len), predicate);
+           break;
+         }
+       case TSQueryPredicateStepTypeDone:
+         result = Fcons (Fnreverse (predicate), result);
+         predicate = Qnil;
+         break;
+       }
+    }
+  return Fnreverse (result);
+}
+
+/* Translate a capture NAME (symbol) to the text of the captured node.
+   Signals treesit-query-error if such node is not captured.  */
+static Lisp_Object
+treesit_predicate_capture_name_to_text (Lisp_Object name,
+                                       struct capture_range captures)
+{
+  Lisp_Object node = Qnil;
+  for (Lisp_Object tail = captures.start; !EQ (tail, captures.end);
+       tail = XCDR (tail))
+    {
+      if (EQ (XCAR (XCAR (tail)), name))
+       {
+         node = XCDR (XCAR (tail));
+         break;
+       }
+    }
+
+  if (NILP (node))
+    xsignal3 (Qtreesit_query_error,
+             build_pure_c_string ("Cannot find captured node"),
+             name, build_pure_c_string ("A predicate can only refer"
+                                        " to captured nodes in the "
+                                        "same pattern"));
+
+  struct buffer *old_buffer = current_buffer;
+  set_buffer_internal (XBUFFER (XTS_PARSER (XTS_NODE (node)->parser)->buffer));
+  Lisp_Object text = Fbuffer_substring (Ftreesit_node_start (node),
+                                       Ftreesit_node_end (node));
+  set_buffer_internal (old_buffer);
+  return text;
+}
+
+/* Handles predicate (#equal A B).  Return true if A equals B; return
+   false otherwise.  A and B can be either string, or a capture name.
+   The capture name evaluates to the text its captured node spans in
+   the buffer.  */
+static bool
+treesit_predicate_equal (Lisp_Object args, struct capture_range captures)
+{
+  if (XFIXNUM (Flength (args)) != 2)
+    xsignal2 (Qtreesit_query_error,
+             build_pure_c_string ("Predicate `equal' requires "
+                                  "two arguments but only given"),
+             Flength (args));
+
+  Lisp_Object arg1 = XCAR (args);
+  Lisp_Object arg2 = XCAR (XCDR (args));
+  Lisp_Object text1 = (STRINGP (arg1)
+                      ? arg1
+                      : treesit_predicate_capture_name_to_text (arg1,
+                                                                captures));
+  Lisp_Object text2 = (STRINGP (arg2)
+                      ? arg2
+                      : treesit_predicate_capture_name_to_text (arg2,
+                                                                captures));
+
+  return !NILP (Fstring_equal (text1, text2));
+}
+
+/* Handles predicate (#match "regexp" @node).  Return true if "regexp"
+   matches the text spanned by @node; return false otherwise.  Matching
+   is case-sensitive.  */
+static bool
+treesit_predicate_match (Lisp_Object args, struct capture_range captures)
+{
+  if (XFIXNUM (Flength (args)) != 2)
+    xsignal2 (Qtreesit_query_error,
+             build_pure_c_string ("Predicate `equal' requires two "
+                                  "arguments but only given"),
+             Flength (args));
+
+  Lisp_Object regexp = XCAR (args);
+  Lisp_Object capture_name = XCAR (XCDR (args));
+  Lisp_Object text = treesit_predicate_capture_name_to_text (capture_name,
+                                                            captures);
+
+  /* It's probably common to get the argument order backwards.  Catch
+     this mistake early and show helpful explanation, because Emacs
+     loves you.  (We put the regexp first because that's what
+     string-match does.)  */
+  if (!STRINGP (regexp))
+    xsignal1 (Qtreesit_query_error,
+             build_pure_c_string ("The first argument to `match' should "
+                                  "be a regexp string, not a capture name"));
+  if (!SYMBOLP (capture_name))
+    xsignal1 (Qtreesit_query_error,
+             build_pure_c_string ("The second argument to `match' should "
+                                  "be a capture name, not a string"));
+
+  if (fast_string_match (regexp, text) >= 0)
+    return true;
+  else
+    return false;
+}
+
+/* About predicates: I decide to hard-code predicates in C instead of
+   implementing an extensible system where predicates are translated
+   to Lisp functions, and new predicates can be added by extending a
+   list of functions, because I really couldn't imagine any useful
+   predicates besides equal and match.  If we later found out that
+   such system is indeed useful and necessary, it can be easily
+   added.  */
+
+/* If all predicates in PREDICATES passes, return true; otherwise
+   return false.  */
+static bool
+treesit_eval_predicates (struct capture_range captures, Lisp_Object predicates)
+{
+  bool pass = true;
+  /* Evaluate each predicates.  */
+  for (Lisp_Object tail = predicates;
+       !NILP (tail); tail = XCDR (tail))
+    {
+      Lisp_Object predicate = XCAR (tail);
+      Lisp_Object fn = XCAR (predicate);
+      Lisp_Object args = XCDR (predicate);
+      if (!NILP (Fstring_equal (fn, build_pure_c_string ("equal"))))
+       pass = treesit_predicate_equal (args, captures);
+      else if (!NILP (Fstring_equal (fn, build_pure_c_string ("match"))))
+       pass = treesit_predicate_match (args, captures);
+      else
+       xsignal3 (Qtreesit_query_error,
+                 build_pure_c_string ("Invalid predicate"),
+                 fn, build_pure_c_string ("Currently Emacs only supports"
+                                          " equal and match predicate"));
+    }
+  /* If all predicates passed, add captures to result list.  */
+  return pass;
+}
+
+DEFUN ("treesit-query-compile",
+       Ftreesit_query_compile,
+       Streesit_query_compile, 2, 3, 0,
+       doc: /* Compile QUERY to a compiled query.
+
+Querying with a compiled query is much faster than an uncompiled one.
+LANGUAGE is the language this query is for.
+
+If EAGER is non-nil, immediately load LANGUAGE and compile the query.
+Otherwise defer the compilation until the query is first used.
+
+Signal `treesit-query-error' if QUERY is malformed or something else
+goes wrong.  (This only happens if EAGER is non-nil.)
+You can use `treesit-query-validate' to validate and debug a query.  */)
+  (Lisp_Object language, Lisp_Object query, Lisp_Object eager)
+{
+  if (NILP (Ftreesit_query_p (query)))
+    wrong_type_argument (Qtreesit_query_p, query);
+  CHECK_SYMBOL (language);
+  if (TS_COMPILED_QUERY_P (query))
+    return query;
+
+  treesit_initialize ();
+
+  Lisp_Object lisp_query = make_treesit_query (query, language);
+
+  /* Maybe actually compile.  */
+  if (NILP (eager))
+    return lisp_query;
+  else
+    {
+      Lisp_Object signal_symbol = Qnil;
+      Lisp_Object signal_data = Qnil;
+      TSQuery *treesit_query = treesit_ensure_query_compiled (lisp_query,
+                                                        &signal_symbol,
+                                                        &signal_data);
+
+      if (treesit_query == NULL)
+       xsignal (signal_symbol, signal_data);
+
+      return lisp_query;
+    }
+}
+
+DEFUN ("treesit-query-capture",
+       Ftreesit_query_capture,
+       Streesit_query_capture, 2, 5, 0,
+       doc: /* Query NODE with patterns in QUERY.
+
+Return a list of (CAPTURE_NAME . NODE).  CAPTURE_NAME is the name
+assigned to the node in PATTERN.  NODE is the captured node.
+
+QUERY is either a string query, a sexp query, or a compiled query.
+See Info node `(elisp)Pattern Matching' for how to write a query in
+either string or sexp form.  When using repeatedly, a compiled query
+is much faster than a string or sexp one, so it is recommend to
+compile your query if it will be used repeatedly.
+
+BEG and END, if both non-nil, specify the region of buffer positions
+in which the query is executed.  Any matching node whose span overlaps
+with the region between BEG and END are captured, it doesn't have to
+be completely in the region.
+
+If NODE-ONLY is non-nil, return a list of nodes.
+
+Besides a node, NODE can also be a parser, in which case the root node
+of that parser is used.
+NODE can also be a language symbol, in which case the root node of a
+parser for that language is used.  If such a parser doesn't exist, it
+is created.
+
+Signal `treesit-query-error' if QUERY is malformed or something else
+goes wrong.  You can use `treesit-query-validate' to validate and debug
+the query.  */)
+  (Lisp_Object node, Lisp_Object query,
+   Lisp_Object beg, Lisp_Object end, Lisp_Object node_only)
+{
+  if (!NILP (beg))
+    CHECK_INTEGER (beg);
+  if (!NILP (end))
+    CHECK_INTEGER (end);
+
+  if (!(TS_COMPILED_QUERY_P (query)
+       || CONSP (query) || STRINGP (query)))
+    wrong_type_argument (Qtreesit_query_p, query);
+
+  /* Resolve NODE into an actual node.  */
+  Lisp_Object lisp_node;
+  if (TS_NODEP (node))
+    lisp_node = node;
+  else if (TS_PARSERP (node))
+    lisp_node = Ftreesit_parser_root_node (node);
+  else if (SYMBOLP (node))
+    {
+      Lisp_Object parser
+       = Ftreesit_parser_create (node, Fcurrent_buffer (), Qnil);
+      lisp_node = Ftreesit_parser_root_node (parser);
+    }
+  else
+    xsignal2 (Qwrong_type_argument,
+             list4 (Qor, Qtreesit_node_p, Qtreesit_parser_p, Qsymbolp),
+             node);
+
+  treesit_initialize ();
+
+  /* Extract C values from Lisp objects.  */
+  TSNode treesit_node
+    = XTS_NODE (lisp_node)->node;
+  Lisp_Object lisp_parser
+    = XTS_NODE (lisp_node)->parser;
+  ptrdiff_t visible_beg
+    = XTS_PARSER (XTS_NODE (lisp_node)->parser)->visible_beg;
+  const TSLanguage *lang
+    = ts_parser_language (XTS_PARSER (lisp_parser)->parser);
+
+  /* Initialize query objects.  At the end of this block, we should
+     have a working TSQuery and a TSQueryCursor.  */
+  TSQuery *treesit_query;
+  TSQueryCursor *cursor;
+  bool needs_to_free_query_and_cursor;
+  if (TS_COMPILED_QUERY_P (query))
+    {
+      Lisp_Object signal_symbol = Qnil;
+      Lisp_Object signal_data = Qnil;
+      treesit_query = treesit_ensure_query_compiled (query, &signal_symbol,
+                                                    &signal_data);
+      cursor = XTS_COMPILED_QUERY (query)->cursor;
+      /* We don't need to free ts_query and cursor because they
+        are stored in a lisp object, which is tracked by gc.  */
+      needs_to_free_query_and_cursor = false;
+      if (treesit_query == NULL)
+       xsignal (signal_symbol, signal_data);
+    }
+  else
+    {
+      /* Since query is not TS_COMPILED_QUERY, it can only be a string
+        or a cons.  */
+      if (CONSP (query))
+       query = Ftreesit_query_expand (query);
+      char *query_string = SSDATA (query);
+      uint32_t error_offset;
+      TSQueryError error_type;
+      treesit_query = ts_query_new (lang, query_string, strlen (query_string),
+                                   &error_offset, &error_type);
+      if (treesit_query == NULL)
+       xsignal (Qtreesit_query_error,
+                treesit_compose_query_signal_data (error_offset,
+                                                   error_type));
+      cursor = ts_query_cursor_new ();
+      needs_to_free_query_and_cursor = true;
+    }
+
+  /* WARN: After this point, free treesit_query and cursor before every
+     signal and return.  */
+
+  /* Set query range.  */
+  if (!NILP (beg) && !NILP (end))
+    {
+      EMACS_INT beg_byte = XFIXNUM (beg);
+      EMACS_INT end_byte = XFIXNUM (end);
+      /* We never let tree-sitter run on buffers too large, so these
+        assertion should never hit.  */
+      eassert (beg_byte - visible_beg <= UINT32_MAX);
+      eassert (end_byte - visible_beg <= UINT32_MAX);
+      ts_query_cursor_set_byte_range (cursor, (uint32_t) beg_byte - 
visible_beg,
+                                     (uint32_t) end_byte - visible_beg);
+    }
+
+  /* Execute query.  */
+  ts_query_cursor_exec (cursor, treesit_query, treesit_node);
+  TSQueryMatch match;
+
+  /* Go over each match, collect captures and predicates.  Include the
+     captures in the RESULT list unconditionally as we get them, then
+     test for predicates.  If predicates pass, then all good, if
+     predicates don't pass, revert the result back to the result
+     before this loop (PREV_RESULT).  (Predicates control the entire
+     match.) This way we don't need to create a list of captures in
+     every for loop and nconc it to RESULT every time.  That is indeed
+     the initial implementation in which Yoav found nconc being the
+     bottleneck (98.4% of the running time spent on nconc).  */
+  Lisp_Object result = Qnil;
+  Lisp_Object prev_result = result;
+  while (ts_query_cursor_next_match (cursor, &match))
+    {
+      /* Record the checkpoint that we may roll back to.  */
+      prev_result = result;
+      /* Get captured nodes.  */
+      const TSQueryCapture *captures = match.captures;
+      for (int idx = 0; idx < match.capture_count; idx++)
+       {
+         uint32_t capture_name_len;
+         TSQueryCapture capture = captures[idx];
+         Lisp_Object captured_node = make_treesit_node (lisp_parser,
+                                                        capture.node);
+
+         Lisp_Object cap;
+         if (NILP (node_only))
+           {
+             const char *capture_name
+               = ts_query_capture_name_for_id (treesit_query, capture.index,
+                                               &capture_name_len);
+             cap = Fcons (intern_c_string_1 (capture_name, capture_name_len),
+                          captured_node);
+           }
+         else
+           cap = captured_node;
+
+         result = Fcons (cap, result);
+       }
+      /* Get predicates.  */
+      Lisp_Object predicates
+       = treesit_predicates_for_pattern (treesit_query,
+                                         match.pattern_index);
+
+      /* captures_lisp = Fnreverse (captures_lisp); */
+      struct capture_range captures_range = { result, prev_result };
+      if (!treesit_eval_predicates (captures_range, predicates))
+       /* Predicates didn't pass, roll back.  */
+       result = prev_result;
+    }
+  if (needs_to_free_query_and_cursor)
+    {
+      ts_query_delete (treesit_query);
+      ts_query_cursor_delete (cursor);
+    }
+  return Fnreverse (result);
+}
+
+/*** Navigation */
+
+/* Return the next/previous named/unnamed sibling of NODE.  FORWARD
+   controls the direction and NAMED controls the nameness.  */
+static TSNode
+treesit_traverse_sibling_helper (TSNode node, bool forward, bool named)
+{
+  if (forward)
+    {
+      if (named)
+       return ts_node_next_named_sibling (node);
+      else
+       return ts_node_next_sibling (node);
+    }
+  else
+    {
+      if (named)
+       return ts_node_prev_named_sibling (node);
+      else
+       return ts_node_prev_sibling (node);
+    }
+}
+
+/* Return the first/last named/unnamed child of NODE.  FORWARD controls
+   the direction and NAMED controls the nameness.  */
+static TSNode
+treesit_traverse_child_helper (TSNode node, bool forward, bool named)
+{
+  if (forward)
+    {
+      if (named)
+       return ts_node_named_child (node, 0);
+      else
+       return ts_node_child (node, 0);
+    }
+  else
+    {
+      if (named)
+       {
+         uint32_t count = ts_node_named_child_count (node);
+         uint32_t idx = count == 0 ? 0 : count - 1;
+         return ts_node_named_child (node, idx);
+       }
+      else
+       {
+         uint32_t count = ts_node_child_count (node);
+         uint32_t idx = count == 0 ? 0 : count - 1;
+         return ts_node_child (node, idx);
+       }
+    }
+}
+
+/* Return true if NODE matches PRED.  PRED can be a string or a
+   function.  This function doesn't check for PRED's type.  */
+static bool
+treesit_traverse_match_predicate (TSNode node, Lisp_Object pred,
+                                 Lisp_Object parser)
+{
+  if (STRINGP (pred))
+    {
+      const char *type = ts_node_type (node);
+      return fast_c_string_match (pred, type, strlen (type)) >= 0;
+    }
+  else
+    {
+      Lisp_Object lisp_node = make_treesit_node (parser, node);
+      return !NILP (CALLN (Ffuncall, pred, lisp_node));
+    }
+
+}
+
+/* Traverse the parse tree starting from ROOT (but ROOT is not
+   matches against PRED).  PRED can be a function (takes a node and
+   returns nil/non-nil),or a string (treated as regexp matching the
+   node's type, ignores case, must be all single byte characters).  If
+   the node satisfies PRED , terminate, set ROOT to that node, and
+   return true.  If no node satisfies PRED, return FALSE.  PARSER is
+   the parser of ROOT.
+
+   LIMIT is the number of levels we descend in the tree.  If NO_LIMIT
+   is true, LIMIT is ignored.  FORWARD controls the direction in which
+   we traverse the tree, true means forward, false backward.  If NAMED
+   is true, only traverse named nodes, if false, all nodes.  If
+   SKIP_ROOT is true, don't match ROOT.  */
+static bool
+treesit_search_dfs (TSNode *root, Lisp_Object pred, Lisp_Object parser,
+                   bool named, bool forward, ptrdiff_t limit, bool no_limit,
+                   bool skip_root)
+{
+  /* TSTreeCursor doesn't allow us to move backward, so we can't use
+     it.  We could use limit == -1 to indicate no_limit == true, but
+     separating them is safer.  */
+  TSNode node = *root;
+
+  if (!skip_root && treesit_traverse_match_predicate (node, pred, parser))
+    {
+      *root = node;
+      return true;
+    }
+
+  if (!no_limit && limit <= 0)
+    return false;
+  else
+    {
+      int count = (named
+                  ? ts_node_named_child_count (node)
+                  : ts_node_child_count (node));
+      for (int offset = 0; offset < count; offset++)
+       {
+         uint32_t idx = forward ? offset : count - offset - 1;
+         TSNode child = (named
+                         ? ts_node_named_child (node, idx)
+                         : ts_node_child (node, idx));
+
+         if (!ts_node_is_null (child)
+             && treesit_search_dfs (&child, pred, parser, named,
+                                    forward, limit - 1, no_limit, false))
+           {
+             *root = child;
+             return true;
+           }
+       }
+      return false;
+    }
+}
+
+/* Go through the whole tree linearly, leaf-first, starting from
+   START.  PRED, PARSER, NAMED, FORWARD are the same as in
+   ts_search_subtre.  If UP_ONLY is true, never go to children, only
+   sibling and parents.  */
+static bool
+treesit_search_forward (TSNode *start, Lisp_Object pred, Lisp_Object parser,
+                       bool named, bool forward)
+{
+  TSNode node = *start;
+
+  /* We don't search for subtree and always search from the leaf
+     nodes.  This way repeated call of this function traverses each
+     node in the tree once and only once:
+
+     (while node (setq node (treesit-search-forward node)))
+  */
+  bool initial = true;
+  while (true)
+    {
+      if (!initial /* We don't match START.  */
+         && treesit_traverse_match_predicate (node, pred, parser))
+       {
+         *start = node;
+         return true;
+       }
+      initial = false;
+
+      TSNode next = treesit_traverse_sibling_helper (node, forward, named);
+      while (ts_node_is_null (next))
+       {
+         /* There is no next sibling, go to parent.  */
+         node = ts_node_parent (node);
+         if (ts_node_is_null (node))
+           return false;
+
+         if (treesit_traverse_match_predicate (node, pred, parser))
+           {
+             *start = node;
+             return true;
+           }
+         next = treesit_traverse_sibling_helper (node, forward, named);
+       }
+      /* We are at the next sibling, deep dive into the first leaf
+        node.  */
+      TSNode next_next = treesit_traverse_child_helper (next, forward, named);
+      while (!ts_node_is_null (next_next))
+       {
+         next = next_next;
+         next_next = treesit_traverse_child_helper (next, forward, named);
+       }
+      /* At this point NEXT is a leaf node.  */
+      node = next;
+    }
+}
+
+DEFUN ("treesit-search-subtree",
+       Ftreesit_search_subtree,
+       Streesit_search_subtree, 2, 5, 0,
+       doc: /* Traverse the parse tree of NODE depth-first using PREDICATE.
+
+Traverse the subtree of NODE, and match PREDICATE with each node along
+the way.  PREDICATE is a regexp string that matches against each
+node's type, or a function that takes a node and returns nil/non-nil.
+
+By default, only traverse named nodes, but if ALL is non-nil, traverse
+all nodes.  If BACKWARD is non-nil, traverse backwards.  If LIMIT is
+non-nil, only traverse nodes up to that number of levels down in the tree.
+
+Return the first matched node, or nil if none matches.  */)
+  (Lisp_Object node, Lisp_Object predicate, Lisp_Object backward,
+   Lisp_Object all, Lisp_Object limit)
+{
+  CHECK_TS_NODE (node);
+  CHECK_TYPE (STRINGP (predicate) || FUNCTIONP (predicate),
+             list3 (Qor, Qstringp, Qfunctionp), predicate);
+  CHECK_SYMBOL (all);
+  CHECK_SYMBOL (backward);
+
+  ptrdiff_t the_limit = 0;
+  bool no_limit = false;
+  if (NILP (limit))
+    no_limit = true;
+  else
+    {
+      CHECK_FIXNUM (limit);
+      the_limit = XFIXNUM (limit);
+    }
+
+  treesit_initialize ();
+
+  TSNode treesit_node = XTS_NODE (node)->node;
+  Lisp_Object parser = XTS_NODE (node)->parser;
+  if (treesit_search_dfs (&treesit_node, predicate, parser, NILP (all),
+                         NILP (backward), the_limit, no_limit, false))
+    return make_treesit_node (parser, treesit_node);
+  else
+    return Qnil;
+}
+
+DEFUN ("treesit-search-forward",
+       Ftreesit_search_forward,
+       Streesit_search_forward, 2, 4, 0,
+       doc: /* Search for node matching PREDICATE in the parse tree of START.
+
+Start traversing the tree from node START, and match PREDICATE with
+each node (except START itself) along the way.  PREDICATE is a regexp
+string that matches against each node's type, or a function that takes
+a node and returns non-nil if it matches.
+
+By default, only search for named nodes, but if ALL is non-nil, search
+for all nodes.  If BACKWARD is non-nil, search backwards.
+
+Return the first matched node, or nil if none matches.
+
+For a tree like below, where START is marked by S, traverse as
+numbered from 1 to 12:
+
+                12
+                |
+       S--------3----------11
+       |        |          |
+  o--o-+--o  1--+--2    6--+-----10
+  |  |                  |        |
+  o  o                +-+-+   +--+--+
+                      |   |   |  |  |
+                      4   5   7  8  9
+
+Note that this function doesn't traverse the subtree of START, and it
+always traverse leaf nodes first, then upwards.  */)
+  (Lisp_Object start, Lisp_Object predicate, Lisp_Object backward,
+   Lisp_Object all)
+{
+  CHECK_TS_NODE (start);
+  CHECK_TYPE (STRINGP (predicate) || FUNCTIONP (predicate),
+             list3 (Qor, Qstringp, Qfunctionp), predicate);
+  CHECK_SYMBOL (all);
+  CHECK_SYMBOL (backward);
+
+  treesit_initialize ();
+
+  TSNode treesit_start = XTS_NODE (start)->node;
+  Lisp_Object parser = XTS_NODE (start)->parser;
+  if (treesit_search_forward (&treesit_start, predicate, parser, NILP (all),
+                             NILP (backward)))
+    return make_treesit_node (parser, treesit_start);
+  else
+    return Qnil;
+}
+
+/* Recursively traverse the tree under CURSOR, and append the result
+   subtree to PARENT's cdr.  See more in Ftreesit_induce_sparse_tree.
+   Note that the top-level children list is reversed, because
+   reasons.  */
+static void
+treesit_build_sparse_tree (TSTreeCursor *cursor, Lisp_Object parent,
+                          Lisp_Object pred, Lisp_Object process_fn,
+                          ptrdiff_t limit, bool no_limit, Lisp_Object parser)
+{
+
+  TSNode node = ts_tree_cursor_current_node (cursor);
+  bool match = treesit_traverse_match_predicate (node, pred, parser);
+  if (match)
+    {
+      /* If this node matches pred, add a new node to the parent's
+        children list.  */
+      Lisp_Object lisp_node = make_treesit_node (parser, node);
+      if (!NILP (process_fn))
+       lisp_node = CALLN (Ffuncall, process_fn, lisp_node);
+
+      Lisp_Object this = Fcons (lisp_node, Qnil);
+      Fsetcdr (parent, Fcons (this, Fcdr (parent)));
+      /* Now for children nodes, this is the new parent.  */
+      parent = this;
+    }
+  /* Go through each child.  */
+  if ((no_limit || limit > 0)
+      && ts_tree_cursor_goto_first_child (cursor))
+    {
+      do
+       {
+         /* Make sure not to use node after the recursive funcall.
+            Then C compilers should be smart enough not to copy NODE
+            to stack.  */
+         treesit_build_sparse_tree (cursor, parent, pred, process_fn,
+                                    limit - 1, no_limit, parser);
+       }
+      while (ts_tree_cursor_goto_next_sibling (cursor));
+      /* Don't forget to come back to this node.  */
+      ts_tree_cursor_goto_parent (cursor);
+    }
+  /* Before we go, reverse children in the sparse tree.  */
+  if (match)
+    /* When match == true, "parent" is actually the node we added in
+       this layer (parent = this).  */
+    Fsetcdr (parent, Fnreverse (Fcdr (parent)));
+}
+
+DEFUN ("treesit-induce-sparse-tree",
+       Ftreesit_induce_sparse_tree,
+       Streesit_induce_sparse_tree, 2, 4, 0,
+       doc: /* Create a sparse tree of ROOT's subtree.
+
+This takes the subtree under ROOT, and combs it so only the nodes
+that match PREDICATE are left, like picking out grapes on the vine.
+PREDICATE is a regexp string that matches against each node's type.
+
+For a subtree on the left that consist of both numbers and letters, if
+PREDICATE is "is letter", the returned tree is the one on the right.
+
+       a                 a              a
+       |                 |              |
+    +---+---+         +---+---+      +---+---+
+    |   |   |         |   |   |      |   |   |
+    b   1   2         b   |   |      b   c   d
+       |   |     =>      |   |  =>      |
+       c   +--+          c   +          e
+       |   |  |          |   |
+     +--+   d  4       +--+   d
+     |  |              |
+     e  5              e
+
+If PROCESS-FN is non-nil, it should be a function of one argument.  In
+that case, instead of returning the matched nodes, pass each node to
+PROCESS-FN, and use its return value instead.
+
+If non-nil, LIMIT is the number of levels to go down the tree from ROOT.
+
+Each node in the returned tree looks like (NODE . (CHILD ...)).  The
+root of this tree might be nil, if ROOT doesn't match PREDICATE.
+
+If no node matches PREDICATE, return nil.
+
+PREDICATE can also be a function that takes a node and returns
+nil/non-nil, but it is slower and more memory consuming than using
+a regexp.  */)
+  (Lisp_Object root, Lisp_Object predicate, Lisp_Object process_fn,
+   Lisp_Object limit)
+{
+  CHECK_TS_NODE (root);
+  CHECK_TYPE (STRINGP (predicate) || FUNCTIONP (predicate),
+             list3 (Qor, Qstringp, Qfunctionp), predicate);
+
+  if (!NILP (process_fn))
+    CHECK_TYPE (FUNCTIONP (process_fn), Qfunctionp, process_fn);
+  ptrdiff_t the_limit = 0;
+  bool no_limit = false;
+  if (NILP (limit))
+    no_limit = true;
+  else
+    {
+      CHECK_FIXNUM (limit);
+      the_limit = XFIXNUM (limit);
+    }
+
+  treesit_initialize ();
+
+  TSTreeCursor cursor = ts_tree_cursor_new (XTS_NODE (root)->node);
+  Lisp_Object parser = XTS_NODE (root)->parser;
+  Lisp_Object parent = Fcons (Qnil, Qnil);
+  treesit_build_sparse_tree (&cursor, parent, predicate, process_fn,
+                            the_limit, no_limit, parser);
+  Fsetcdr (parent, Fnreverse (Fcdr (parent)));
+  if (NILP (Fcdr (parent)))
+    return Qnil;
+  else
+    return parent;
+}
+
+#endif /* HAVE_TREE_SITTER */
+
+DEFUN ("treesit-available-p", Ftreesit_available_p,
+       Streesit_available_p, 0, 0, 0,
+       doc: /* Return non-nil if tree-sitter support is built-in and 
available.  */)
+  (void)
+{
+#if HAVE_TREE_SITTER
+  return load_tree_sitter_if_necessary (false) ? Qt : Qnil;
+#else
+  return Qnil;
+#endif
+}
+
+
+/*** Initialization */
+
+/* Initialize the tree-sitter routines.  */
+void
+syms_of_treesit (void)
+{
+#if HAVE_TREE_SITTER
+  DEFSYM (Qtreesit_parser_p, "treesit-parser-p");
+  DEFSYM (Qtreesit_node_p, "treesit-node-p");
+  DEFSYM (Qtreesit_compiled_query_p, "treesit-compiled-query-p");
+  DEFSYM (Qtreesit_query_p, "treesit-query-p");
+  DEFSYM (Qnamed, "named");
+  DEFSYM (Qmissing, "missing");
+  DEFSYM (Qextra, "extra");
+  DEFSYM (Qoutdated, "outdated");
+  DEFSYM (Qhas_error, "has-error");
+
+  DEFSYM (QCanchor, ":anchor");
+  DEFSYM (QCequal, ":equal");
+  DEFSYM (QCmatch, ":match");
+
+  DEFSYM (Qnot_found, "not-found");
+  DEFSYM (Qsymbol_error, "symbol-error");
+  DEFSYM (Qversion_mismatch, "version-mismatch");
+
+  DEFSYM (Qtreesit_error, "treesit-error");
+  DEFSYM (Qtreesit_query_error, "treesit-query-error");
+  DEFSYM (Qtreesit_parse_error, "treesit-parse-error");
+  DEFSYM (Qtreesit_range_invalid, "treesit-range-invalid");
+  DEFSYM (Qtreesit_buffer_too_large,
+         "treesit-buffer-too-large");
+  DEFSYM (Qtreesit_load_language_error,
+         "treesit-load-language-error");
+  DEFSYM (Qtreesit_node_outdated,
+         "treesit-node-outdated");
+  DEFSYM (Quser_emacs_directory,
+         "user-emacs-directory");
+  DEFSYM (Qtreesit_parser_deleted, "treesit-parser-deleted");
+  DEFSYM (Qtreesit_pattern_expand, "treesit-pattern-expand");
+
+  DEFSYM (Qor, "or");
+
+#ifdef WINDOWSNT
+  DEFSYM (Qtree_sitter, "tree-sitter");
+#endif
+
+  define_error (Qtreesit_error, "Generic tree-sitter error", Qerror);
+  define_error (Qtreesit_query_error, "Query pattern is malformed",
+               Qtreesit_error);
+  /* Should be impossible, no need to document this error.  */
+  define_error (Qtreesit_parse_error, "Parse failed",
+               Qtreesit_error);
+  define_error (Qtreesit_range_invalid,
+               "RANGES are invalid, they have to be ordered and not 
overlapping",
+               Qtreesit_error);
+  define_error (Qtreesit_buffer_too_large, "Buffer too large (> 4GB)",
+               Qtreesit_error);
+  define_error (Qtreesit_load_language_error,
+               "Cannot load language definition",
+               Qtreesit_error);
+  define_error (Qtreesit_node_outdated,
+               "This node is outdated, please retrieve a new one",
+               Qtreesit_error);
+  define_error (Qtreesit_parser_deleted,
+               "This parser is deleted and cannot be used",
+               Qtreesit_error);
+
+  DEFVAR_LISP ("treesit-load-name-override-list",
+              Vtreesit_load_name_override_list,
+              doc:
+              /* An override list for unconventional tree-sitter libraries.
+
+By default, Emacs assumes the dynamic library for LANG is
+libtree-sitter-LANG.EXT, where EXT is the OS specific extension for
+dynamic libraries.  Emacs also assumes that the name of the C function
+the library provides is tree_sitter_LANG.  If that is not the case,
+you can add an entry
+
+    (LANG LIBRARY-BASE-NAME FUNCTION-NAME)
+
+to this list, where LIBRARY-BASE-NAME is the filename of the dynamic
+library without the file-name extension, and FUNCTION-NAME is the
+function provided by the library.  */);
+  Vtreesit_load_name_override_list = Qnil;
+
+  DEFVAR_LISP ("treesit-extra-load-path",
+              Vtreesit_extra_load_path,
+              doc:
+              /* Additional directories to look for tree-sitter language 
definitions.
+The value should be a list of directories.
+When trying to load a tree-sitter language definition,
+Emacs first looks in the directories mentioned in this variable,
+then in the `tree-sitter' subdirectory of `user-emacs-directory', and
+then in the system default locations for dynamic libraries, in that order.  
*/);
+  Vtreesit_extra_load_path = Qnil;
+
+  defsubr (&Streesit_language_available_p);
+  defsubr (&Streesit_language_version);
+
+  defsubr (&Streesit_parser_p);
+  defsubr (&Streesit_node_p);
+  defsubr (&Streesit_compiled_query_p);
+  defsubr (&Streesit_query_p);
+  defsubr (&Streesit_query_language);
+
+  defsubr (&Streesit_node_parser);
+
+  defsubr (&Streesit_parser_create);
+  defsubr (&Streesit_parser_delete);
+  defsubr (&Streesit_parser_list);
+  defsubr (&Streesit_parser_buffer);
+  defsubr (&Streesit_parser_language);
+
+  defsubr (&Streesit_parser_root_node);
+  /* defsubr (&Streesit_parse_string); */
+
+  defsubr (&Streesit_parser_set_included_ranges);
+  defsubr (&Streesit_parser_included_ranges);
+
+  defsubr (&Streesit_parser_notifiers);
+  defsubr (&Streesit_parser_add_notifier);
+  defsubr (&Streesit_parser_remove_notifier);
+
+  defsubr (&Streesit_node_type);
+  defsubr (&Streesit_node_start);
+  defsubr (&Streesit_node_end);
+  defsubr (&Streesit_node_string);
+  defsubr (&Streesit_node_parent);
+  defsubr (&Streesit_node_child);
+  defsubr (&Streesit_node_check);
+  defsubr (&Streesit_node_field_name_for_child);
+  defsubr (&Streesit_node_child_count);
+  defsubr (&Streesit_node_child_by_field_name);
+  defsubr (&Streesit_node_next_sibling);
+  defsubr (&Streesit_node_prev_sibling);
+  defsubr (&Streesit_node_first_child_for_pos);
+  defsubr (&Streesit_node_descendant_for_range);
+  defsubr (&Streesit_node_eq);
+
+  defsubr (&Streesit_pattern_expand);
+  defsubr (&Streesit_query_expand);
+  defsubr (&Streesit_query_compile);
+  defsubr (&Streesit_query_capture);
+
+  defsubr (&Streesit_search_subtree);
+  defsubr (&Streesit_search_forward);
+  defsubr (&Streesit_induce_sparse_tree);
+#endif /* HAVE_TREE_SITTER */
+  defsubr (&Streesit_available_p);
+}
diff --git a/src/treesit.h b/src/treesit.h
new file mode 100644
index 0000000000..1473126c5b
--- /dev/null
+++ b/src/treesit.h
@@ -0,0 +1,200 @@
+/* Header file for the tree-sitter integration.
+
+Copyright (C) 2021-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 EMACS_TREESIT_H
+#define EMACS_TREESIT_H
+
+#include <config.h>
+
+#ifdef HAVE_TREE_SITTER
+
+#include <tree_sitter/api.h>
+#include "lisp.h"
+
+INLINE_HEADER_BEGIN
+
+/* A wrapper for a tree-sitter parser, but also contains a parse tree
+   and other goodies for convenience.  */
+struct Lisp_TS_Parser
+{
+  union vectorlike_header header;
+  /* A symbol representing the language this parser uses.  See the
+     manual for more explanation.  */
+  Lisp_Object language_symbol;
+  /* A list of functions to call after re-parse.  Every function is
+     called with the changed ranges and the parser.  The changed
+     ranges is a list of (BEG . END).  */
+  Lisp_Object after_change_functions;
+  /* The buffer associated with this parser.  */
+  Lisp_Object buffer;
+  /* The pointer to the tree-sitter parser.  Never NULL.  */
+  TSParser *parser;
+  /* Pointer to the syntax tree.  Initially is NULL, so check for NULL
+     before use.  */
+  TSTree *tree;
+  /* Teaches tree-sitter how to read an Emacs buffer.  */
+  TSInput input;
+  /* Re-parsing an unchanged buffer is not free for tree-sitter, so we
+     only make it re-parse when need_reparse == true.  That usually
+     means some change is made in the buffer.  But others could set
+     this field to true to force tree-sitter to re-parse.  */
+  bool need_reparse;
+  /* These two positions record the buffer byte position (1-based) of
+     the "visible region" that tree-sitter sees.  Unlike markers,
+     These two positions do not change as the user inserts and deletes
+     text around them.  Before re-parse, we move these positions to
+     match BUF_BEGV_BYTE and BUF_ZV_BYTE.  Note that we don't need to
+     synchronize these positions when retrieving them in a function
+     that involves a node: if the node is not outdated, these
+     positions are synchronized.  */
+  ptrdiff_t visible_beg;
+  ptrdiff_t visible_end;
+  /* This counter is incremented every time a change is made to the
+     buffer in treesit_record_change.  The node retrieved from this parser
+     inherits this timestamp.  This way we can make sure the node is
+     not outdated when we access its information.  */
+  ptrdiff_t timestamp;
+  /* If this field is true, parser functions raises
+     treesit-parser-deleted signal.  */
+  bool deleted;
+  /* If this field is true, the parser has ranges set.  See
+     Ftreesit_parser_included_ranges for why we need this.  */
+  bool has_range;
+};
+
+/* A wrapper around a tree-sitter node.  */
+struct Lisp_TS_Node
+{
+  union vectorlike_header header;
+  /* This prevents gc from collecting the tree before the node is done
+     with it.  TSNode contains a pointer to the tree it belongs to,
+     and the parser object, when collected by gc, will free that
+     tree.  */
+  Lisp_Object parser;
+  TSNode node;
+  /* A node inherits its parser's timestamp at creation time.  The
+     parser's timestamp increments as the buffer changes.  This way we
+     can make sure the node is not outdated when we access its
+     information.  */
+  ptrdiff_t timestamp;
+};
+
+/* A compiled tree-sitter query.
+
+   When we create a query object by treesit-compile-query, it is not
+   immediately compiled, because that would require the language
+   definition to be loaded.  For example, python.el contains
+
+   (defvar xxx (treesit-compile-query ...))
+
+   and (require 'python.el) requires python's language definition to
+   be available.  In the case of python.el, Emacs requires it when
+   building, so that breaks the build.  */
+struct Lisp_TS_Query
+{
+  union vectorlike_header header;
+  /* Language symbol for the query.  */
+  Lisp_Object language;
+  /* Source lisp (sexp or string) query.  */
+  Lisp_Object source;
+  /* Pointer to the query object.  This can be NULL, meaning this
+     query is not initialized/compiled.  We compile the query when
+     it is used the first time (in treesit-query-capture).  */
+  TSQuery *query;
+  /* Pointer to a cursor.  If we are storing the query object, we
+     might as well store a cursor, too.  */
+  TSQueryCursor *cursor;
+};
+
+INLINE bool
+TS_PARSERP (Lisp_Object x)
+{
+  return PSEUDOVECTORP (x, PVEC_TS_PARSER);
+}
+
+INLINE struct Lisp_TS_Parser *
+XTS_PARSER (Lisp_Object a)
+{
+  eassert (TS_PARSERP (a));
+  return XUNTAG (a, Lisp_Vectorlike, struct Lisp_TS_Parser);
+}
+
+INLINE bool
+TS_NODEP (Lisp_Object x)
+{
+  return PSEUDOVECTORP (x, PVEC_TS_NODE);
+}
+
+INLINE struct Lisp_TS_Node *
+XTS_NODE (Lisp_Object a)
+{
+  eassert (TS_NODEP (a));
+  return XUNTAG (a, Lisp_Vectorlike, struct Lisp_TS_Node);
+}
+
+INLINE bool
+TS_COMPILED_QUERY_P (Lisp_Object x)
+{
+  return PSEUDOVECTORP (x, PVEC_TS_COMPILED_QUERY);
+}
+
+INLINE struct Lisp_TS_Query *
+XTS_COMPILED_QUERY (Lisp_Object a)
+{
+  eassert (TS_COMPILED_QUERY_P (a));
+  return XUNTAG (a, Lisp_Vectorlike, struct Lisp_TS_Query);
+}
+
+INLINE void
+CHECK_TS_PARSER (Lisp_Object parser)
+{
+  CHECK_TYPE (TS_PARSERP (parser), Qtreesit_parser_p, parser);
+}
+
+INLINE void
+CHECK_TS_NODE (Lisp_Object node)
+{
+  CHECK_TYPE (TS_NODEP (node), Qtreesit_node_p, node);
+}
+
+INLINE void
+CHECK_TS_COMPILED_QUERY (Lisp_Object query)
+{
+  CHECK_TYPE (TS_COMPILED_QUERY_P (query),
+             Qtreesit_compiled_query_p, query);
+}
+
+INLINE_HEADER_END
+
+extern void treesit_record_change (ptrdiff_t, ptrdiff_t, ptrdiff_t);
+extern Lisp_Object make_treesit_parser (Lisp_Object, TSParser *, TSTree *,
+                                       Lisp_Object);
+extern Lisp_Object make_treesit_node (Lisp_Object, TSNode);
+
+extern bool treesit_node_uptodate_p (Lisp_Object);
+
+extern void treesit_delete_parser (struct Lisp_TS_Parser *);
+extern void treesit_delete_query (struct Lisp_TS_Query *);
+extern bool treesit_named_node_p (TSNode);
+
+#endif /* HAVE_TREE_SITTER */
+
+extern void syms_of_treesit (void);
+
+#endif /* EMACS_TREESIT_H */
diff --git a/src/w32fns.c b/src/w32fns.c
index c7eddcba6d..887e5a1f7b 100644
--- a/src/w32fns.c
+++ b/src/w32fns.c
@@ -1717,10 +1717,19 @@ w32_set_tab_bar_lines (struct frame *f, Lisp_Object 
value, Lisp_Object oldval)
 void
 w32_change_tab_bar_height (struct frame *f, int height)
 {
-  int unit = FRAME_LINE_HEIGHT (f);
-  int old_height = FRAME_TAB_BAR_HEIGHT (f);
-  int lines = (height + unit - 1) / unit;
-  Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
+  int unit, old_height, lines;
+  Lisp_Object fullscreen;
+
+  unit = FRAME_LINE_HEIGHT (f);
+  old_height = FRAME_TAB_BAR_HEIGHT (f);
+  fullscreen = get_frame_param (f, Qfullscreen);
+
+  /* This differs from the tool bar code in that the tab bar height is
+     not rounded up.  Otherwise, if redisplay_tab_bar decides to grow
+     the tab bar by even 1 pixel, FRAME_TAB_BAR_LINES will be changed,
+     leading to the tab bar height being incorrectly set upon the next
+     call to x_set_font.  (bug#59285) */
+  lines = height / unit;
 
   /* Make sure we redisplay all windows in this frame.  */
   fset_redisplay (f);
@@ -5785,13 +5794,7 @@ w32_default_font_parameter (struct frame *f, Lisp_Object 
parms)
       if (NILP (font))
        error ("No suitable font was found");
     }
-  else if (!NILP (font_param))
-    {
-      /* Remember the explicit font parameter, so we can re-apply it after
-        we've applied the `default' face settings.  */
-      gui_set_frame_parameters (f, Fcons (Fcons (Qfont_parameter, font_param),
-                                          Qnil));
-    }
+
   gui_default_parameter (f, parms, Qfont, font, "font", "Font", 
RES_TYPE_STRING);
 }
 
diff --git a/src/w32inevt.c b/src/w32inevt.c
index 6a1d9afacf..d13b0521c9 100644
--- a/src/w32inevt.c
+++ b/src/w32inevt.c
@@ -648,7 +648,7 @@ handle_file_notifications (struct input_event *hold_quit)
       ns = NULL;
 
       /* Find out if there is a record available in the linked list of
-        notifications sets.  If so, unlink te set from the linked list.
+        notifications sets.  If so, unlink the set from the linked list.
         Use the critical section.  */
       enter_crit ();
       if (notifications_set_head->next != notifications_set_head)
diff --git a/src/xdisp.c b/src/xdisp.c
index f6a279636a..b5f013ea6a 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -2330,7 +2330,7 @@ pixel_to_glyph_coords (struct frame *f, int pix_x, int 
pix_y, int *x, int *y,
    text, or we can't tell because W's current matrix is not up to
    date.  */
 
-static struct glyph *
+struct glyph *
 x_y_to_hpos_vpos (struct window *w, int x, int y, int *hpos, int *vpos,
                  int *dx, int *dy, int *area)
 {
@@ -3342,7 +3342,8 @@ init_iterator (struct it *it, struct window *w,
     {
       /* Mode lines, menu bar in terminal frames.  */
       it->first_visible_x = 0;
-      it->last_visible_x = body_width = WINDOW_PIXEL_WIDTH (w);
+      it->last_visible_x =
+       WINDOW_PIXEL_WIDTH (w) - WINDOW_RIGHT_DIVIDER_WIDTH (w);
     }
   else
     {
@@ -3410,8 +3411,13 @@ init_iterator (struct it *it, struct window *w,
       face = FACE_FROM_ID_OR_NULL (it->f, remapped_base_face_id);
       if (face && face->box != FACE_NO_BOX)
        {
+         int box_thickness = face->box_vertical_line_width;
          it->face_box_p = true;
          it->start_of_box_run_p = true;
+         /* Make sure we will have enough horizontal space to add the
+            right box line at the end.  */
+         if (box_thickness > 0)
+           it->last_visible_x -= box_thickness;
        }
     }
 
@@ -5324,6 +5330,8 @@ display_min_width (struct it *it, ptrdiff_t bufpos,
          /* Insert the stretch glyph.  */
          it->object = list3 (Qspace, QCwidth, w);
          produce_stretch_glyph (it);
+         if (it->area == TEXT_AREA)
+           it->current_x += it->pixel_width;
          it->min_width_property = Qnil;
        }
     }
@@ -7036,17 +7044,11 @@ 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)))
-       {
-         ITREE_FOREACH_ABORT ();
-         return true;
-       }
+       return true;
       str = Foverlay_get (overlay, Qafter_string);
       if (STRINGP (str) && SCHARS (str)
          && memchr (SDATA (str), '\n', SBYTES (str)))
-       {
-         ITREE_FOREACH_ABORT ();
-         return true;
-       }
+       return true;
     }
 
   /* Check for 'display' properties whose values include strings.  */
@@ -26718,7 +26720,17 @@ display_mode_line (struct window *w, enum face_id 
face_id, Lisp_Object format)
     {
       struct glyph *last = (it.glyph_row->glyphs[TEXT_AREA]
                            + it.glyph_row->used[TEXT_AREA] - 1);
+      int box_thickness = face->box_vertical_line_width;
       last->right_box_line_p = true;
+      /* Add back the space for the right box line we subtracted in
+        init_iterator, since the right_box_line_p flag will make the
+        glyph wider.  We actually add only as much space as is
+        available for the last glyph of the modeline and whatever
+        space is left beyond it, since that glyph could be only
+        partially visible */
+      if (box_thickness > 0)
+       last->pixel_width += max (0, (box_thickness
+                                     - (it.current_x - it.last_visible_x)));
     }
 
   return it.glyph_row->height;
diff --git a/src/xfaces.c b/src/xfaces.c
index 7d6bc38762..f96e538360 100644
--- a/src/xfaces.c
+++ b/src/xfaces.c
@@ -3808,8 +3808,12 @@ set_font_frame_param (Lisp_Object frame, Lisp_Object 
lface)
          ASET (lface, LFACE_FONT_INDEX, font);
        }
       f->default_face_done_p = false;
-      AUTO_FRAME_ARG (arg, Qfont, font);
-      Fmodify_frame_parameters (frame, arg);
+      AUTO_LIST2 (arg, AUTO_CONS_EXPR (Qfont, font),
+                 /* Clear the `font-parameter' frame property, as the
+                    font is now being specified by a face, not a
+                    frame property.  */
+                 AUTO_CONS_EXPR (Qfont_parameter, Qnil));
+      gui_set_frame_parameters_1 (f, arg, true);
     }
 }
 
@@ -4207,7 +4211,17 @@ Default face attributes override any local face 
attributes.  */)
            {
              Lisp_Object name = newface->font->props[FONT_NAME_INDEX];
              AUTO_FRAME_ARG (arg, Qfont, name);
-             Fmodify_frame_parameters (frame, arg);
+
+#ifdef HAVE_WINDOW_SYSTEM
+             if (FRAME_WINDOW_P (f))
+               /* This is a window-system frame.  Prevent changes of
+                  the `font' parameter here from messing with the
+                  `font-parameter' frame property, as the frame
+                  parameter is not being changed by the user.  */
+               gui_set_frame_parameters_1 (f, arg, true);
+             else
+#endif
+               Fmodify_frame_parameters (frame, arg);
            }
 
          if (STRINGP (gvec[LFACE_FOREGROUND_INDEX]))
diff --git a/src/xfns.c b/src/xfns.c
index 3ff7a8c286..95092ce05f 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -1362,13 +1362,21 @@ x_set_mouse_color (struct frame *f, Lisp_Object arg, 
Lisp_Object oldval)
       char *xmessage = alloca (1 + message_length);
       memcpy (xmessage, cursor_data.error_string, message_length);
 
-      x_uncatch_errors ();
+      x_uncatch_errors_after_check ();
+
+      /* XFreeCursor can generate BadCursor errors, because
+        XCreateFontCursor is not a request that waits for a reply,
+        and as such can return IDs that will not actually be used by
+        the server.  */
+      x_ignore_errors_for_next_request (FRAME_DISPLAY_INFO (f));
 
       /* Free any successfully created cursors.  */
       for (i = 0; i < mouse_cursor_max; i++)
        if (cursor_data.cursor[i] != 0)
          XFreeCursor (dpy, cursor_data.cursor[i]);
 
+      x_stop_ignoring_errors (FRAME_DISPLAY_INFO (f));
+
       /* This should only be able to fail if the server's serial
         number tracking is broken.  */
       if (cursor_data.error_cursor >= 0)
@@ -1750,10 +1758,19 @@ x_set_tab_bar_lines (struct frame *f, Lisp_Object 
value, Lisp_Object oldval)
 void
 x_change_tab_bar_height (struct frame *f, int height)
 {
-  int unit = FRAME_LINE_HEIGHT (f);
-  int old_height = FRAME_TAB_BAR_HEIGHT (f);
-  int lines = (height + unit - 1) / unit;
-  Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
+  int unit, old_height, lines;
+  Lisp_Object fullscreen;
+
+  unit = FRAME_LINE_HEIGHT (f);
+  old_height = FRAME_TAB_BAR_HEIGHT (f);
+  fullscreen = get_frame_param (f, Qfullscreen);
+
+  /* This differs from the tool bar code in that the tab bar height is
+     not rounded up.  Otherwise, if redisplay_tab_bar decides to grow
+     the tab bar by even 1 pixel, FRAME_TAB_BAR_LINES will be changed,
+     leading to the tab bar height being incorrectly set upon the next
+     call to x_set_font.  (bug#59285) */
+  lines = height / unit;
 
   /* Make sure we redisplay all windows in this frame.  */
   fset_redisplay (f);
@@ -4506,9 +4523,11 @@ x_default_font_parameter (struct frame *f, Lisp_Object 
parms)
     }
 
   if (NILP (font))
-      font = !NILP (font_param) ? font_param
-      : gui_display_get_arg (dpyinfo, parms, Qfont, "font", "Font",
-                             RES_TYPE_STRING);
+    font = (!NILP (font_param)
+           ? font_param
+           : gui_display_get_arg (dpyinfo, parms,
+                                  Qfont, "font", "Font",
+                                  RES_TYPE_STRING));
 
   if (! FONTP (font) && ! STRINGP (font))
     {
@@ -4540,13 +4559,6 @@ x_default_font_parameter (struct frame *f, Lisp_Object 
parms)
       if (NILP (font))
        error ("No suitable font was found");
     }
-  else if (!NILP (font_param))
-    {
-      /* Remember the explicit font parameter, so we can re-apply it after
-        we've applied the `default' face settings.  */
-      AUTO_FRAME_ARG (arg, Qfont_parameter, font_param);
-      gui_set_frame_parameters (f, arg);
-    }
 
   /* This call will make X resources override any system font setting.  */
   gui_default_parameter (f, parms, Qfont, font, "font", "Font", 
RES_TYPE_STRING);
diff --git a/src/xselect.c b/src/xselect.c
index 9480ac18c1..a381fa2352 100644
--- a/src/xselect.c
+++ b/src/xselect.c
@@ -967,7 +967,7 @@ x_handle_selection_request (struct selection_input_event 
*event)
    x_reply_selection_request.  If FOR_MULTIPLE, write out
    the data even if conversion fails, using conversion_fail_tag.
 
-   Return true iff successful.  */
+   Return true if successful.  */
 
 static bool
 x_convert_selection (Lisp_Object selection_symbol,
diff --git a/src/xsettings.c b/src/xsettings.c
index 15e7ff5499..00b67539d4 100644
--- a/src/xsettings.c
+++ b/src/xsettings.c
@@ -56,6 +56,7 @@ typedef unsigned int CARD32;
 
 #ifdef USE_CAIRO
 #include <fontconfig/fontconfig.h>
+#include "ftfont.h"
 #elif defined HAVE_XFT
 #include <X11/Xft/Xft.h>
 #endif
@@ -765,7 +766,7 @@ parse_settings (unsigned char *prop,
 #ifndef HAVE_PGTK
 /* Read settings from the XSettings property window on display for DPYINFO.
    Store settings read in SETTINGS.
-   Return true iff successful.  */
+   Return true if successful.  */
 
 static bool
 read_settings (Display_Info *dpyinfo, struct xsettings *settings)
@@ -826,6 +827,7 @@ apply_xft_settings (Display_Info *dpyinfo,
 #else
   FcConfigSubstitute (NULL, pat, FcMatchPattern);
   options = cairo_font_options_create ();
+  ftcrfont_get_default_font_options (dpyinfo, options);
   cairo_ft_font_options_substitute (options, pat);
   cairo_font_options_destroy (options);
   FcDefaultSubstitute (pat);
@@ -884,6 +886,14 @@ apply_xft_settings (Display_Info *dpyinfo,
     }
 #endif
 
+#ifdef USE_CAIRO
+  /* When Cairo is being used, set oldsettings.dpi to dpyinfo->resx.
+     This is a gross hack, but seeing as Cairo fails to report
+     anything reasonable, just use it to avoid config-changed events
+     being sent at startup.  */
+  oldsettings.dpi = dpyinfo->resx;
+#endif
+
   if ((settings->seen & SEEN_DPI) != 0
       && settings->dpi > 0
       /* The following conjunct avoids setting `changed' to true when
diff --git a/src/xterm.c b/src/xterm.c
index 7a1fd6086c..af652a0d85 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -1134,6 +1134,7 @@ static void x_clean_failable_requests (struct 
x_display_info *);
 static struct frame *x_tooltip_window_to_frame (struct x_display_info *,
                                                Window, bool *);
 static Window x_get_window_below (Display *, Window, int, int, int *, int *);
+static void x_set_input_focus (struct x_display_info *, Window, Time);
 
 #ifndef USE_TOOLKIT_SCROLL_BARS
 static void x_scroll_bar_redraw (struct scroll_bar *);
@@ -5322,6 +5323,46 @@ struct xi_known_valuator
   struct xi_known_valuator *next;
 };
 
+/* Populate the scroll valuator at INDEX in DEVICE with the scroll
+   valuator information provided in INFO.
+
+   The information consists of:
+
+     - whether or not the valuator is horizontal.
+
+     - whether or not the valuator's value is currently unknown,
+       until the next XI_Motion event is received or the valuator's
+       value is restored by the caller upon encountering valuator
+       class data.
+
+     - what the current value of the valuator is.  This is set to
+       DBL_MIN for debugging purposes, but can be any value, as
+       invalid_p is currently true.
+
+     - the increment, which defines the amount of movement equal to a
+       single unit of scrolling.  For example, if the increment is
+       2.0, then a WHEEL_DOWN or WHEEL_UP event will be sent every
+       time the valuator value changes by 2.0, unless
+       mwheel-coalesce-scroll-events is nil.
+
+     - the number used in XI_Motion events and elsewhere to identify
+       the valuator.  */
+
+static void
+xi_populate_scroll_valuator (struct xi_device_t *device,
+                            int index, XIScrollClassInfo *info)
+{
+  struct xi_scroll_valuator_t *valuator;
+
+  valuator = &device->valuators[index];
+  valuator->horizontal
+    = (info->scroll_type == XIScrollTypeHorizontal);
+  valuator->invalid_p = true;
+  valuator->emacs_value = DBL_MIN;
+  valuator->increment = info->increment;
+  valuator->number = info->number;
+}
+
 #endif
 
 static void
@@ -5330,7 +5371,6 @@ xi_populate_device_from_info (struct x_display_info 
*dpyinfo,
                              XIDeviceInfo *device)
 {
 #ifdef HAVE_XINPUT2_1
-  struct xi_scroll_valuator_t *valuator;
   struct xi_known_valuator *values, *tem;
   int actual_valuator_count, c;
   XIScrollClassInfo *info;
@@ -5431,15 +5471,8 @@ xi_populate_device_from_info (struct x_display_info 
*dpyinfo,
        case XIScrollClass:
          {
            info = (XIScrollClassInfo *) device->classes[c];
-
-           valuator = &xi_device->valuators[actual_valuator_count++];
-           valuator->horizontal
-             = (info->scroll_type == XIScrollTypeHorizontal);
-           valuator->invalid_p = true;
-           valuator->emacs_value = DBL_MIN;
-           valuator->increment = info->increment;
-           valuator->number = info->number;
-
+           xi_populate_scroll_valuator (xi_device, actual_valuator_count++,
+                                        info);
            break;
          }
 
@@ -13163,13 +13196,9 @@ xi_handle_new_classes (struct x_display_info *dpyinfo, 
struct xi_device_t *devic
        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;
+         xi_populate_scroll_valuator (device,
+                                      device->scroll_valuator_count++,
+                                      scroll);
          break;
 
 #ifdef HAVE_XINPUT2_2
@@ -14234,6 +14263,136 @@ x_get_window_below (Display *dpy, Window window,
   return value;
 }
 
+/* Like XTmouse_position, but much faster.  */
+
+static void
+x_fast_mouse_position (struct frame **fp, int insist, Lisp_Object *bar_window,
+                      enum scroll_bar_part *part, Lisp_Object *x, Lisp_Object 
*y,
+                      Time *timestamp)
+{
+  int root_x, root_y, win_x, win_y;
+  unsigned int mask;
+  Window dummy;
+  struct scroll_bar *bar;
+  struct x_display_info *dpyinfo;
+  Lisp_Object tail, frame;
+  struct frame *f1;
+
+  dpyinfo = FRAME_DISPLAY_INFO (*fp);
+
+  if (dpyinfo->last_mouse_scroll_bar && !insist)
+    {
+      bar = dpyinfo->last_mouse_scroll_bar;
+
+      if (bar->horizontal)
+       x_horizontal_scroll_bar_report_motion (fp, bar_window, part,
+                                              x, y, timestamp);
+      else
+       x_scroll_bar_report_motion (fp, bar_window, part,
+                                   x, y, timestamp);
+
+      return;
+    }
+
+  if (!EQ (Vx_use_fast_mouse_position, Qreally_fast))
+    {
+      /* This means that Emacs should select a frame and report the
+        mouse position relative to it.  The approach used here avoids
+        making multiple roundtrips to the X server querying for the
+        window beneath the pointer, and was borrowed from
+        haiku_mouse_position in haikuterm.c.  */
+
+      FOR_EACH_FRAME (tail, frame)
+       {
+         if (FRAME_X_P (XFRAME (frame))
+             && (FRAME_DISPLAY_INFO (XFRAME (frame))
+                 == dpyinfo))
+           XFRAME (frame)->mouse_moved = false;
+       }
+
+      if (gui_mouse_grabbed (dpyinfo)
+         && !EQ (track_mouse, Qdropping)
+         && !EQ (track_mouse, Qdrag_source))
+       /* Pick the last mouse frame if dropping.  */
+       f1 = dpyinfo->last_mouse_frame;
+      else
+       /* Otherwise, pick the last mouse motion frame.  */
+       f1 = dpyinfo->last_mouse_motion_frame;
+
+      if (!f1 && (FRAME_X_P (SELECTED_FRAME ())
+                 && (FRAME_DISPLAY_INFO (SELECTED_FRAME ())
+                     == dpyinfo)))
+       f1 = SELECTED_FRAME ();
+
+      if (!f1 || (!FRAME_X_P (f1) && (insist > 0)))
+       FOR_EACH_FRAME (tail, frame)
+         if (FRAME_X_P (XFRAME (frame))
+             && (FRAME_DISPLAY_INFO (XFRAME (frame))
+                 == dpyinfo)
+             && !FRAME_TOOLTIP_P (XFRAME (frame)))
+           f1 = XFRAME (frame);
+
+      if (f1 && FRAME_TOOLTIP_P (f1))
+       f1 = NULL;
+
+      if (f1 && FRAME_X_P (f1) && FRAME_X_WINDOW (f1))
+       {
+         if (!x_query_pointer (dpyinfo->display, FRAME_X_WINDOW (f1),
+                               &dummy, &dummy, &root_x, &root_y,
+                               &win_x, &win_y, &mask))
+           /* The pointer is out of the screen.  */
+           return;
+
+         remember_mouse_glyph (f1, win_x, win_y,
+                               &dpyinfo->last_mouse_glyph);
+         dpyinfo->last_mouse_glyph_frame = f1;
+
+         *bar_window = Qnil;
+         *part = scroll_bar_nowhere;
+
+         /* If track-mouse is `drag-source' and the mouse pointer is
+            certain to not be actually under the chosen frame, return
+            NULL in FP.  */
+         if (EQ (track_mouse, Qdrag_source)
+             && (win_x < 0 || win_y < 0
+                 || win_x >= FRAME_PIXEL_WIDTH (f1)
+                 || win_y >= FRAME_PIXEL_HEIGHT (f1)))
+           *fp = NULL;
+         else
+           *fp = f1;
+
+         *timestamp = dpyinfo->last_mouse_movement_time;
+         XSETINT (*x, win_x);
+         XSETINT (*y, win_y);
+       }
+    }
+  else
+    {
+      /* This means Emacs should only report the coordinates of the
+        last mouse motion.  */
+
+      if (dpyinfo->last_mouse_motion_frame)
+       {
+         *fp = dpyinfo->last_mouse_motion_frame;
+         *timestamp = dpyinfo->last_mouse_movement_time;
+         *x = make_fixnum (dpyinfo->last_mouse_motion_x);
+         *y = make_fixnum (dpyinfo->last_mouse_motion_y);
+         *bar_window = Qnil;
+         *part = scroll_bar_nowhere;
+
+         FOR_EACH_FRAME (tail, frame)
+           {
+             if (FRAME_X_P (XFRAME (frame))
+                 && (FRAME_DISPLAY_INFO (XFRAME (frame))
+                     == dpyinfo))
+               XFRAME (frame)->mouse_moved = false;
+           }
+
+         dpyinfo->last_mouse_motion_frame->mouse_moved = false;
+       }
+    }
+}
+
 /* Return the current position of the mouse.
    *FP should be a frame which indicates which display to ask about.
 
@@ -14260,9 +14419,27 @@ XTmouse_position (struct frame **fp, int insist, 
Lisp_Object *bar_window,
                  Time *timestamp)
 {
   struct frame *f1, *maybe_tooltip;
-  struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (*fp);
+  struct x_display_info *dpyinfo;
   bool unrelated_tooltip;
 
+  dpyinfo = FRAME_DISPLAY_INFO (*fp);
+
+  if (!NILP (Vx_use_fast_mouse_position))
+    {
+      /* The user says that Emacs is running over the network, and a
+        fast approximation of `mouse-position' should be used.
+
+        Depending on what the value of `x-use-fast-mouse-position'
+        is, do one of two things: only perform the XQueryPointer to
+        obtain the coordinates from the last mouse frame, or only
+        return the last mouse motion frame and the
+        last_mouse_motion_x and Y.  */
+
+      x_fast_mouse_position (fp, insist, bar_window, part, x,
+                            y, timestamp);
+      return;
+    }
+
   block_input ();
 
   if (dpyinfo->last_mouse_scroll_bar && insist == 0)
@@ -14384,7 +14561,7 @@ XTmouse_position (struct frame **fp, int insist, 
Lisp_Object *bar_window,
                    break;
                  }
 #ifdef USE_GTK
-               /* We don't wan't to know the innermost window.  We
+               /* We don't want to know the innermost window.  We
                   want the edit window.  For non-Gtk+ the innermost
                   window is the edit window.  For Gtk+ it might not
                   be.  It might be the tool bar for example.  */
@@ -14415,7 +14592,7 @@ XTmouse_position (struct frame **fp, int insist, 
Lisp_Object *bar_window,
               never use them in that case.)  */
 
 #ifdef USE_GTK
-           /* We don't wan't to know the innermost window.  We
+           /* We don't want to know the innermost window.  We
               want the edit window.  */
            f1 = x_window_to_frame (dpyinfo, win);
 #else
@@ -27535,11 +27712,10 @@ x_ewmh_activate_frame (struct frame *f)
            {
              time = x_get_server_time (f);
 
-             x_ignore_errors_for_next_request (dpyinfo);
-             XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
-                             RevertToParent, time);
+             x_set_input_focus (FRAME_DISPLAY_INFO (f),
+                                FRAME_OUTER_WINDOW (f),
+                                time);
              XRaiseWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f));
-             x_stop_ignoring_errors (dpyinfo);
 
              return;
            }
@@ -27584,6 +27760,57 @@ x_get_toplevel_parent (struct frame *f)
   return parent;
 }
 
+static void
+x_set_input_focus (struct x_display_info *dpyinfo, Window window,
+                  Time time)
+{
+#ifdef HAVE_XINPUT2
+  struct xi_device_t *device;
+#endif
+
+  /* Do the equivalent of XSetInputFocus with the specified window and
+     time, but use the attachment to the device that Emacs has
+     designated the client pointer on X Input Extension builds.
+     Asynchronously trap errors around the generated XI_SetFocus or
+     SetInputFocus request, in case the device has been destroyed or
+     the window obscured.
+
+     The revert_to will be set to RevertToParent for generated
+     SetInputFocus requests.  */
+
+#ifdef HAVE_XINPUT2
+  if (dpyinfo->supports_xi2
+      && dpyinfo->client_pointer_device != -1)
+    {
+      device = xi_device_from_id (dpyinfo, dpyinfo->client_pointer_device);
+
+      /* The device is a master pointer.  Use its attachment, which
+        should be the master keyboard.  */
+
+      if (device)
+       {
+         eassert (device->use == XIMasterPointer);
+
+         x_ignore_errors_for_next_request (dpyinfo);
+         XISetFocus (dpyinfo->display, device->attachment,
+                     /* Note that the input extension
+                        only supports RevertToParent-type
+                        behavior.  */
+                     window, time);
+         x_stop_ignoring_errors (dpyinfo);
+
+         return;
+       }
+    }
+#endif
+
+  /* Otherwise, use the pointer device that the X server says is the
+     client pointer.  */
+  x_ignore_errors_for_next_request (dpyinfo);
+  XSetInputFocus (dpyinfo->display, window, RevertToParent, time);
+  x_stop_ignoring_errors (dpyinfo);
+}
+
 /* 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.
@@ -27632,8 +27859,6 @@ x_focus_frame (struct frame *f, bool noactivate)
          return;
        }
 
-      /* Ignore any BadMatch error this request might result in.  */
-      x_ignore_errors_for_next_request (dpyinfo);
       if (NILP (Vx_no_window_manager))
        {
          /* Use the last user time.  It is invalid to use CurrentTime
@@ -27651,15 +27876,19 @@ x_focus_frame (struct frame *f, bool noactivate)
              && !dpyinfo->x_focus_frame)
            time = x_get_server_time (f);
 
-         XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
-                         RevertToParent, time);
+         /* Ignore any BadMatch error this request might result in.
+            A BadMatch error can occur if the window was obscured
+            after the time of the last user interaction without
+            changing the last-focus-change-time.  */
+         x_set_input_focus (FRAME_DISPLAY_INFO (f), FRAME_OUTER_WINDOW (f),
+                            time);
        }
       else
-       XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
-                       /* But when no window manager is in use, we
-                          don't care.  */
-                       RevertToParent, CurrentTime);
-      x_stop_ignoring_errors (dpyinfo);
+       x_set_input_focus (FRAME_DISPLAY_INFO (f), FRAME_OUTER_WINDOW (f),
+                          /* But when no window manager is in use,
+                             respecting the ICCCM doesn't really
+                             matter.  */
+                          CurrentTime);
     }
 }
 
@@ -30019,7 +30248,7 @@ x_term_init (Lisp_Object display_name, char 
*xrm_option, char *resource_name)
       XrmPutLineResource (&xrdb, "Emacs.dialog.*.font: 9x15");
 
     /* Do not destroy the font struct returned above with XFreeFont;
-       that also destroys the font, leading to to X protocol errors at
+       that also destroys the font, leading to X protocol errors at
        XtCloseDisplay.  Just free the font info structure.
        (Bug#18403) */
     XFreeFontInfo (NULL, query_result, 1);
@@ -31078,6 +31307,7 @@ With MS Windows, Haiku windowing or Nextstep, the value 
is t.  */);
   DEFSYM (Qimitate_pager, "imitate-pager");
   DEFSYM (Qnewer_time, "newer-time");
   DEFSYM (Qraise_and_focus, "raise-and-focus");
+  DEFSYM (Qreally_fast, "really-fast");
 
   DEFVAR_LISP ("x-ctrl-keysym", Vx_ctrl_keysym,
     doc: /* Which keys Emacs uses for the ctrl modifier.
@@ -31357,4 +31587,19 @@ not bypass window manager focus stealing prevention):
   - The symbol `raise-and-focus', which means to raise the window and
     focus it manually.  */);
   Vx_allow_focus_stealing = Qnewer_time;
+
+  DEFVAR_LISP ("x-use-fast-mouse-position", Vx_use_fast_mouse_position,
+    doc: /* How to make `mouse-position' faster.
+
+`mouse-position' and `mouse-pixel-position' default to querying the X
+server for the window under the mouse pointer.  This results in
+accurate results, but is also very slow when the X connection has
+moderate to high latency.  Setting this variable to a non-nil value
+makes Emacs query only for the position of the pointer, which is
+usually faster.  Doing so improves the performance of dragging to
+select text over slow X connections.
+
+If that is still too slow, setting this variable to the symbol
+`really-fast' will make Emacs return only cached values.  */);
+  Vx_use_fast_mouse_position = Qnil;
 }
diff --git a/src/xterm.h b/src/xterm.h
index 1124dcceb4..c36920081d 100644
--- a/src/xterm.h
+++ b/src/xterm.h
@@ -580,7 +580,9 @@ struct x_display_info
   Time last_user_time;
 
   /* Position where the mouse was last time we reported a motion.
-     This is a position on last_mouse_motion_frame.  */
+     This is a position on last_mouse_motion_frame.  It is used in
+     some situations to report the mouse position as well: see
+     XTmouse_position.  */
   int last_mouse_motion_x;
   int last_mouse_motion_y;
 
@@ -1605,22 +1607,15 @@ SELECTION_EVENT_DISPLAY (struct selection_input_event 
*ev)
 
 extern void x_free_gcs (struct frame *);
 extern void x_relative_mouse_position (struct frame *, int *, int *);
-extern void x_real_pos_and_offsets (struct frame *f,
-                                    int *left_offset_x,
-                                    int *right_offset_x,
-                                    int *top_offset_y,
-                                    int *bottom_offset_y,
-                                    int *x_pixels_diff,
-                                    int *y_pixels_diff,
-                                    int *xptr,
-                                    int *yptr,
-                                    int *outer_border);
-extern void x_default_font_parameter (struct frame* f, Lisp_Object parms);
+extern void x_real_pos_and_offsets (struct frame *, int *, int *, int *,
+                                    int *, int *, int *, int *, int *,
+                                   int *);
+extern void x_default_font_parameter (struct frame *, Lisp_Object);
 
 /* From xrdb.c.  */
 
-XrmDatabase x_load_resources (Display *, const char *, const char *,
-                             const char *);
+extern XrmDatabase x_load_resources (Display *, const char *, const char *,
+                                    const char *);
 extern const char *x_get_string_resource (void *, const char *, const char *);
 
 /* Defined in xterm.c */
diff --git a/test/lisp/auth-source-pass-tests.el 
b/test/lisp/auth-source-pass-tests.el
index f5147a7ce0..6e6671efca 100644
--- a/test/lisp/auth-source-pass-tests.el
+++ b/test/lisp/auth-source-pass-tests.el
@@ -25,7 +25,7 @@
 
 ;;; Code:
 
-(require 'ert)
+(require 'ert-x)
 
 (require 'auth-source-pass)
 
@@ -59,7 +59,7 @@
   "Contains a list of all messages passed to `auth-source-do-debug'.")
 
 (defun auth-source-pass--have-message-matching (regexp)
-  "Return non-nil iff at least one `auth-source-do-debug' match REGEXP."
+  "Return non-nil if at least one `auth-source-do-debug' match REGEXP."
   (seq-find (lambda (message)
               (string-match regexp message))
             auth-source-pass--debug-log))
@@ -109,7 +109,7 @@ ENTRY, HOSTNAME, USER and PORT are the same as in
 (put 'auth-source-pass-match-entry-p 'ert-explainer 
#'auth-source-pass--explain-match-entry-p)
 
 (defun auth-source-pass--includes-sorted-entries (entries hostname &optional 
user port)
-  "Return non-nil iff ENTRIES matching the parameters are found in store.
+  "Return non-nil if ENTRIES matching the parameters are found in store.
 ENTRIES should be sorted from most specific to least specific.
 
 HOSTNAME, USER and PORT are passed unchanged to
@@ -157,7 +157,7 @@ result is ordered the same way as the suffixes."
             (auth-source-pass--generate-entry-suffixes hostname user port))))
 
 (defun auth-source-pass-match-entry-p (entry hostname &optional user port)
-  "Return non-nil iff an ENTRY matching the parameters is found in store.
+  "Return non-nil if an ENTRY matching the parameters is found in store.
 
 HOSTNAME, USER and PORT are passed unchanged to
 `auth-source-pass--matching-entries'."
@@ -166,7 +166,7 @@ HOSTNAME, USER and PORT are passed unchanged to
    (auth-source-pass--matching-entries hostname user port)))
 
 (defun auth-source-pass-match-any-entry-p (hostname &optional user port)
-  "Return non-nil iff there is at least one entry matching the parameters.
+  "Return non-nil if there is at least one entry matching the parameters.
 
 HOSTNAME, USER and PORT are passed unchanged to
 `auth-source-pass--matching-entries'."
@@ -466,7 +466,10 @@ HOSTNAME, USER and PORT are passed unchanged to
 (ert-deftest auth-source-pass-can-start-from-auth-source-search ()
   (auth-source-pass--with-store '(("gitlab.com" ("user" . "someone")))
     (auth-source-pass-enable)
-    (let ((result (car (auth-source-search :host "gitlab.com"))))
+    ;; This also asserts an aspect of traditional search behavior
+    ;; relative to `auth-source-pass-extra-query-keywords'.
+    (let* ((auth-source-pass-extra-query-keywords nil)
+           (result (car (auth-source-search :host "gitlab.com"))))
       (should (equal (plist-get result :user) "someone"))
       (should (equal (plist-get result :host) "gitlab.com")))))
 
@@ -488,6 +491,266 @@ HOSTNAME, USER and PORT are passed unchanged to
     (should (auth-source-pass--have-message-matching
              "found 2 entries matching \"gitlab.com\": (\"a/gitlab.com\" 
\"b/gitlab.com\")"))))
 
+
+;;;; Option `auth-source-pass-extra-query-keywords' (bug#58985)
+
+;; No entry has the requested port, but a result is still returned.
+
+(ert-deftest auth-source-pass-extra-query-keywords--wild-port-miss-netrc ()
+  (ert-with-temp-file netrc-file
+    :text "\
+machine x.com password a
+machine x.com port 42 password b
+"
+    (let* ((auth-sources (list netrc-file))
+           (auth-source-do-cache nil)
+           (results (auth-source-search :host "x.com" :port 22 :max 2)))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results '((:host "x.com" :secret "a")))))))
+
+(ert-deftest auth-source-pass-extra-query-keywords--wild-port-miss ()
+  (auth-source-pass--with-store '(("x.com" (secret . "a"))
+                                  ("x.com:42" (secret . "b")))
+    (auth-source-pass-enable)
+    (let* ((auth-source-pass-extra-query-keywords t)
+           (results (auth-source-search :host "x.com" :port 22 :max 2)))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results '((:host "x.com" :secret "a")))))))
+
+;; One of two entries has the requested port, both returned.
+
+(ert-deftest auth-source-pass-extra-query-keywords--wild-port-hit-netrc ()
+  (ert-with-temp-file netrc-file
+    :text "\
+machine x.com password a
+machine x.com port 42 password b
+"
+    (let* ((auth-sources (list netrc-file))
+           (auth-source-do-cache nil)
+           (results (auth-source-search :host "x.com" :port 42 :max 2)))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results '((:host "x.com" :secret "a")
+                               (:host "x.com" :port "42" :secret "b")))))))
+
+(ert-deftest auth-source-pass-extra-query-keywords--wild-port-hit ()
+  (auth-source-pass--with-store '(("x.com" (secret . "a"))
+                                  ("x.com:42" (secret . "b")))
+    (auth-source-pass-enable)
+    (let* ((auth-source-pass-extra-query-keywords t)
+           (results (auth-source-search :host "x.com" :port 42 :max 2)))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results
+                     '((:host "x.com" :secret "a")
+                       (:host "x.com" :port 42 :secret "b")))))))
+
+;; No entry has the requested port, but :port is required, so search fails.
+
+(ert-deftest auth-source-pass-extra-query-keywords--wild-port-req-miss-netrc ()
+  (ert-with-temp-file netrc-file
+    :text "\
+machine x.com password a
+machine x.com port 42 password b
+"
+    (let* ((auth-sources (list netrc-file))
+           (auth-source-do-cache nil)
+           (results (auth-source-search
+                     :host "x.com" :port 22 :require '(:port) :max 2)))
+      (should-not results))))
+
+(ert-deftest auth-source-pass-extra-query-keywords--wild-port-req-miss ()
+  (let ((auth-source-pass-extra-query-keywords t))
+    (auth-source-pass--with-store '(("x.com" (secret . "a"))
+                                    ("x.com:42" (secret . "b")))
+      (auth-source-pass-enable)
+      (should-not (auth-source-search
+                   :host "x.com" :port 22 :require '(:port) :max 2)))))
+
+;; Specifying a :host without a :user finds a lone entry and does not
+;; include extra fields (i.e., :port nil) in the result.
+;; https://lists.gnu.org/archive/html/emacs-devel/2022-11/msg00130.html
+
+(ert-deftest auth-source-pass-extra-query-keywords--netrc-akib ()
+  (ert-with-temp-file netrc-file
+    :text "\
+machine x.com password a
+machine disroot.org user akib password b
+machine z.com password c
+"
+    (let* ((auth-sources (list netrc-file))
+           (auth-source-do-cache nil)
+           (results (auth-source-search :host "disroot.org" :max 2)))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results
+                     '((:host "disroot.org" :user "akib" :secret "b")))))))
+
+(ert-deftest auth-source-pass-extra-query-keywords--akib ()
+  (auth-source-pass--with-store '(("x.com" (secret . "a"))
+                                  ("akib@disroot.org" (secret . "b"))
+                                  ("z.com" (secret . "c")))
+    (auth-source-pass-enable)
+    (let* ((auth-source-pass-extra-query-keywords t)
+           (results (auth-source-search :host "disroot.org" :max 2)))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results
+                     '((:host "disroot.org" :user "akib" :secret "b")))))))
+
+;; Searches for :host are case-sensitive, and a returned host isn't
+;; normalized.
+
+(ert-deftest auth-source-pass-extra-query-keywords--netrc-host ()
+  (ert-with-temp-file netrc-file
+    :text "\
+machine libera.chat password a
+machine Libera.Chat password b
+"
+    (let* ((auth-sources (list netrc-file))
+           (auth-source-do-cache nil)
+           (results (auth-source-search :host "Libera.Chat" :max 2)))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results '((:host "Libera.Chat" :secret "b")))))))
+
+(ert-deftest auth-source-pass-extra-query-keywords--host ()
+  (auth-source-pass--with-store '(("libera.chat" (secret . "a"))
+                                  ("Libera.Chat" (secret . "b")))
+    (auth-source-pass-enable)
+    (let* ((auth-source-pass-extra-query-keywords t)
+           (results (auth-source-search :host "Libera.Chat" :max 2)))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results
+                     '((:host "Libera.Chat" :secret "b")))))))
+
+
+;; A retrieved store entry mustn't be nil regardless of whether its
+;; path contains port or user components.
+
+(ert-deftest auth-source-pass-extra-query-keywords--baseline ()
+  (let ((auth-source-pass-extra-query-keywords t))
+    (auth-source-pass--with-store '(("x.com"))
+      (auth-source-pass-enable)
+      (should-not (auth-source-search :host "x.com")))))
+
+;; Output port type (int or string) matches that of input parameter.
+
+(ert-deftest auth-source-pass-extra-query-keywords--port-type ()
+  (let ((auth-source-pass-extra-query-keywords t)
+        (f (lambda (r) (setf (plist-get r :secret) (auth-info-password r)) r)))
+    (auth-source-pass--with-store '(("x.com:42" (secret . "a")))
+      (auth-source-pass-enable)
+      (should (equal (mapcar f (auth-source-search :host "x.com" :port 42))
+                     '((:host "x.com" :port 42 :secret "a")))))
+    (auth-source-pass--with-store '(("x.com:42" (secret . "a")))
+      (auth-source-pass-enable)
+      (should (equal (mapcar f (auth-source-search :host "x.com" :port "42"))
+                     '((:host "x.com" :port "42" :secret "a")))))))
+
+;; Match precision sometimes takes a back seat to the traversal
+;; ordering.  Specifically, the :host (h1, ...) args hold greater sway
+;; over the output because they determine the first coordinate in the
+;; sequence of (host, user, port) combinations visited.  (Taking a
+;; tree-wise view, these become the depth-1 nodes in a DFS.)
+
+;; Note that all trailing /user forms are demoted for the sake of
+;; predictability (see tests further below for details).  This means
+;; that, in the following test, /bar is held in limbo, followed by
+;; /foo, but they both retain priority over "gnu.org", as noted above.
+
+(ert-deftest auth-source-pass-extra-query-keywords--hosts-first ()
+  (auth-source-pass--with-store '(("x.com:42/bar" (secret . "a"))
+                                  ("gnu.org" (secret . "b"))
+                                  ("x.com" (secret . "c"))
+                                  ("fake.com" (secret . "d"))
+                                  ("x.com/foo" (secret . "e")))
+    (auth-source-pass-enable)
+    (let* ((auth-source-pass-extra-query-keywords t)
+           (results (auth-source-search :host '("x.com" "gnu.org") :max 3)))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results
+                     ;; Notice gnu.org is never considered ^
+                     '((:host "x.com" :secret "c")
+                       (:host "x.com" :user "bar" :port "42" :secret "a")
+                       (:host "x.com" :user "foo" :secret "e")))))))
+
+;; This is another example given in the bug thread.
+
+(ert-deftest auth-source-pass-extra-query-keywords--ambiguous-user-host ()
+  (auth-source-pass--with-store '(("foo.com/bar.org" (secret . "a"))
+                                  ("foo.com" (secret . "b"))
+                                  ("bar.org" (secret . "c"))
+                                  ("fake.com" (secret . "d")))
+    (auth-source-pass-enable)
+    (let* ((auth-source-pass-extra-query-keywords t)
+           (results (auth-source-search :host "bar.org" :max 3)))
+      (dolist (result results)
+        (setf (plist-get result :secret) (auth-info-password result)))
+      (should (equal results '((:host "bar.org" :secret "c")))))))
+
+;; This conveys the same idea as `user-priorities', just below, but
+;; with slightly more realistic and less legible values.
+
+(ert-deftest auth-source-pass-extra-query-keywords--suffixed-user ()
+  (let ((store (sort (copy-sequence '(("x.com:42/bar" (secret . "a"))
+                                      ("bar@x.com" (secret . "b"))
+                                      ("x.com" (secret . "?"))
+                                      ("bar@y.org" (secret . "c"))
+                                      ("fake.com" (secret . "?"))
+                                      ("fake.com/bar" (secret . "d"))
+                                      ("y.org/bar" (secret . "?"))
+                                      ("bar@fake.com" (secret . "e"))))
+                     (lambda (&rest _) (zerop (random 2))))))
+    (auth-source-pass--with-store store
+      (auth-source-pass-enable)
+      (let* ((auth-source-pass-extra-query-keywords t)
+             (results (auth-source-search :host '("x.com" "fake.com" "y.org")
+                                          :user "bar"
+                                          :require '(:user) :max 5)))
+        (dolist (result results)
+          (setf (plist-get result :secret) (auth-info-password result)))
+        (should (equal results
+                       '((:host "x.com" :user "bar" :secret "b")
+                         (:host "x.com" :user "bar" :port "42" :secret "a")
+                         (:host "fake.com" :user "bar" :secret "e")
+                         (:host "fake.com" :user "bar" :secret "d")
+                         (:host "y.org" :user "bar" :secret "c"))))))))
+
+;; This is a more distilled version of `suffixed-user', above.  It
+;; better illustrates that search order takes precedence over "/user"
+;; demotion because otherwise * and ** would be swapped, below.  It
+;; follows that omitting the :port 2, gets you {u@h:1, u@h:2, h:1/u,
+;; h:2/u, u@g:1}.
+
+(ert-deftest auth-source-pass-extra-query-keywords--user-priorities ()
+  (let ((store (sort (copy-sequence '(("h:1/u" (secret . "/"))
+                                      ("h:2/u" (secret . "/"))
+                                      ("u@h:1" (secret . "@"))
+                                      ("u@h:2" (secret . "@"))
+                                      ("g:1/u" (secret . "/"))
+                                      ("g:2/u" (secret . "/"))
+                                      ("u@g:1" (secret . "@"))
+                                      ("u@g:2" (secret . "@"))))
+                     (lambda (&rest _) (zerop (random 2))))))
+    (auth-source-pass--with-store store
+      (auth-source-pass-enable)
+      (let* ((auth-source-pass-extra-query-keywords t)
+             (results (auth-source-search :host '("h" "g")
+                                          :port 2
+                                          :max 5)))
+        (dolist (result results)
+          (setf (plist-get result :secret) (auth-info-password result)))
+        (should (equal results
+                       '((:host "h" :user "u" :port 2 :secret "@")
+                         (:host "h" :user "u" :port 2 :secret "/") ; *
+                         (:host "g" :user "u" :port 2 :secret "@") ; **
+                         (:host "g" :user "u" :port 2 :secret "/"))))))))
+
 (provide 'auth-source-pass-tests)
 
 ;;; auth-source-pass-tests.el ends here
diff --git a/test/lisp/cedet/srecode/fields-tests.el 
b/test/lisp/cedet/srecode/fields-tests.el
index 292ac4e3b5..c9e0d4601b 100644
--- a/test/lisp/cedet/srecode/fields-tests.el
+++ b/test/lisp/cedet/srecode/fields-tests.el
@@ -66,7 +66,7 @@ It is filled with some text."
 
       (when (and (overlayp (oref f overlay))
                 (not (overlay-get (oref f overlay) 'srecode-init-only)))
-       (error "Field creation overlay is not tagged w/ init flag"))
+        (error "Field creation overlay is not tagged with init flag"))
 
       (srecode-overlaid-activate f)
 
diff --git a/test/lisp/emacs-lisp/package-resources/elpa-packages.eld 
b/test/lisp/emacs-lisp/package-resources/elpa-packages.eld
new file mode 100644
index 0000000000..7884aac278
--- /dev/null
+++ b/test/lisp/emacs-lisp/package-resources/elpa-packages.eld
@@ -0,0 +1,3 @@
+;; Dummy elpa-package.eld
+
+(() :version 1)
diff --git 
a/test/lisp/emacs-lisp/package-resources/newer-versions/elpa-packages.eld 
b/test/lisp/emacs-lisp/package-resources/newer-versions/elpa-packages.eld
new file mode 100644
index 0000000000..7884aac278
--- /dev/null
+++ b/test/lisp/emacs-lisp/package-resources/newer-versions/elpa-packages.eld
@@ -0,0 +1,3 @@
+;; Dummy elpa-package.eld
+
+(() :version 1)
diff --git a/test/lisp/emacs-lisp/package-resources/signed/elpa-packages.eld 
b/test/lisp/emacs-lisp/package-resources/signed/elpa-packages.eld
new file mode 100644
index 0000000000..7884aac278
--- /dev/null
+++ b/test/lisp/emacs-lisp/package-resources/signed/elpa-packages.eld
@@ -0,0 +1,3 @@
+;; Dummy elpa-package.eld
+
+(() :version 1)
diff --git 
a/test/lisp/emacs-lisp/package-resources/signed/elpa-packages.eld.sig 
b/test/lisp/emacs-lisp/package-resources/signed/elpa-packages.eld.sig
new file mode 100644
index 0000000000..39202ca75e
Binary files /dev/null and 
b/test/lisp/emacs-lisp/package-resources/signed/elpa-packages.eld.sig differ
diff --git a/test/lisp/emacs-lisp/package-resources/signed/update-signatures.sh 
b/test/lisp/emacs-lisp/package-resources/signed/update-signatures.sh
index 30e74156c0..ddd96748ec 100755
--- a/test/lisp/emacs-lisp/package-resources/signed/update-signatures.sh
+++ b/test/lisp/emacs-lisp/package-resources/signed/update-signatures.sh
@@ -30,4 +30,5 @@ rm $KEYRING
 #$GPG --export-secret-keys -armor > "../key.sec"
 $GPG --import ../key.sec
 $GPG --detach-sign --sign "./archive-contents"
+$GPG --detach-sign --sign "./elpa-packages.eld"
 $GPG --detach-sign --sign "./signed-good-1.0.el"
diff --git 
a/test/lisp/emacs-lisp/package-resources/with-nil-entry/elpa-packages.eld 
b/test/lisp/emacs-lisp/package-resources/with-nil-entry/elpa-packages.eld
new file mode 100644
index 0000000000..7884aac278
--- /dev/null
+++ b/test/lisp/emacs-lisp/package-resources/with-nil-entry/elpa-packages.eld
@@ -0,0 +1,3 @@
+;; Dummy elpa-package.eld
+
+(() :version 1)
diff --git a/test/lisp/emacs-lisp/syntax-tests.el 
b/test/lisp/emacs-lisp/syntax-tests.el
index f266db5c70..d8fc5c4fa8 100644
--- a/test/lisp/emacs-lisp/syntax-tests.el
+++ b/test/lisp/emacs-lisp/syntax-tests.el
@@ -56,7 +56,7 @@
      "\\(?2:[abc]+\\)foo\\(\\2\\)" 2)
     "\\(?4:[abc]+\\)foo\\(\\4\\)"))
   ;; Emacs supports only the back-references \1,...,\9, so when a
-  ;; shift would result in \10 or more, an error must be signalled.
+  ;; shift would result in \10 or more, an error must be signaled.
   (should-error
    (syntax-propertize--shift-groups-and-backrefs "\\(a\\)\\3" 7)))
 
diff --git a/test/lisp/erc/erc-dcc-tests.el b/test/lisp/erc/erc-dcc-tests.el
index 8645d7f104..74cbb7d947 100644
--- a/test/lisp/erc/erc-dcc-tests.el
+++ b/test/lisp/erc/erc-dcc-tests.el
@@ -167,7 +167,8 @@
 
 (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"))
+    (let* ((inhibit-message noninteractive)
+           (proc (start-process "fake" (current-buffer) "sleep" "10"))
            (elt (list :nick "tester!~tester@fake.irc"
                       :type 'GET
                       :peer nil
diff --git a/test/lisp/erc/erc-networks-tests.el 
b/test/lisp/erc/erc-networks-tests.el
index 32bdfa11ff..fc12bf7ce3 100644
--- a/test/lisp/erc/erc-networks-tests.el
+++ b/test/lisp/erc/erc-networks-tests.el
@@ -1704,4 +1704,21 @@
 
   (erc-networks-tests--clean-bufs))
 
+(ert-deftest erc-networks--determine ()
+  (should (eq (erc-networks--determine "irc.libera.chat") 'Libera.Chat))
+  (should (eq (erc-networks--determine "irc.oftc.net") 'OFTC))
+  (should (eq (erc-networks--determine "irc.dal.net") 'DALnet))
+
+  (let ((erc-server-announced-name "zirconium.libera.chat"))
+    (should (eq (erc-networks--determine) 'Libera.Chat)))
+  (let ((erc-server-announced-name "weber.oftc.net"))
+    (should (eq (erc-networks--determine) 'OFTC)))
+  (let ((erc-server-announced-name "redemption.ix.us.dal.net"))
+    (should (eq (erc-networks--determine) 'DALnet)))
+
+  ;; Failure
+  (let ((erc-server-announced-name "irc-us2.alphachat.net"))
+    (should (eq (erc-networks--determine)
+                erc-networks--name-missing-sentinel))))
+
 ;;; erc-networks-tests.el ends here
diff --git a/test/lisp/erc/erc-scenarios-base-compat-rename-bouncer.el 
b/test/lisp/erc/erc-scenarios-base-compat-rename-bouncer.el
index 474739d01b..2ffa86aff6 100644
--- a/test/lisp/erc/erc-scenarios-base-compat-rename-bouncer.el
+++ b/test/lisp/erc/erc-scenarios-base-compat-rename-bouncer.el
@@ -106,7 +106,7 @@
         (erc-d-t-search-for 1 "<joe>")
         (erc-d-t-absent-for 0.1 "<bob>")
         (should (eq erc-server-process erc-server-process-bar))
-        (erc-d-t-search-for 10 "keeps you from dishonour")
+        (erc-d-t-search-for 10 "joe: It is a rupture")
         (erc-d-t-wait-for 5 (not (erc-server-process-alive)))))
 
     (when more (funcall more))))
diff --git a/test/lisp/erc/erc-scenarios-base-reconnect.el 
b/test/lisp/erc/erc-scenarios-base-reconnect.el
index 49298dc594..8762f33b30 100644
--- a/test/lisp/erc/erc-scenarios-base-reconnect.el
+++ b/test/lisp/erc/erc-scenarios-base-reconnect.el
@@ -224,4 +224,50 @@
       (with-current-buffer "#chan"
         (funcall expect 10 "here comes the lady")))))
 
+
+(ert-deftest erc-scenarios-base-cancel-reconnect ()
+  :tags '(:expensive-test)
+  (erc-scenarios-common-with-cleanup
+      ((erc-scenarios-common-dialog "base/reconnect")
+       (dumb-server (erc-d-run "localhost" t 'timer 'timer 'timer-last))
+       (port (process-contact dumb-server :service))
+       (expect (erc-d-t-make-expecter))
+       (erc-server-auto-reconnect t)
+       erc-autojoin-channels-alist
+       erc-server-buffer)
+
+    (ert-info ("Connect to foonet")
+      (setq erc-server-buffer (erc :server "127.0.0.1"
+                                   :port port
+                                   :nick "tester"
+                                   :password "changeme"
+                                   :full-name "tester"))
+      (with-current-buffer erc-server-buffer
+        (should (string= (buffer-name) (format "127.0.0.1:%d" port)))))
+
+    (ert-info ("Two connection attempts, all stymied")
+      (with-current-buffer erc-server-buffer
+        (ert-info ("First two attempts behave normally")
+          (dotimes (n 2)
+            (ert-info ((format "Initial attempt %d" (1+ n)))
+              (funcall expect 3 "Opening connection")
+              (funcall expect 2 "Password incorrect")
+              (funcall expect 2 "Connection failed!")
+              (funcall expect 2 "Re-establishing connection"))))
+        (ert-info ("/RECONNECT cancels timer but still attempts to connect")
+          (erc-cmd-RECONNECT)
+          (funcall expect 2 "Canceled")
+          (funcall expect 3 "Opening connection")
+          (funcall expect 2 "Password incorrect")
+          (funcall expect 2 "Connection failed!")
+          (funcall expect 2 "Re-establishing connection"))
+        (ert-info ("Explicitly cancel timer")
+          (erc-cmd-RECONNECT "cancel")
+          (funcall expect 2 "Canceled")
+          (erc-d-t-absent-for 1 "Opening connection" (point)))))
+
+    (ert-info ("Server buffer is unique and temp name is absent")
+      (should (equal (list (get-buffer (format "127.0.0.1:%d" port)))
+                     (erc-scenarios-common-buflist "127.0.0.1"))))))
+
 ;;; erc-scenarios-base-reconnect.el ends here
diff --git a/test/lisp/erc/erc-scenarios-misc.el 
b/test/lisp/erc/erc-scenarios-misc.el
index ded620ccc1..8557a77906 100644
--- a/test/lisp/erc/erc-scenarios-misc.el
+++ b/test/lisp/erc/erc-scenarios-misc.el
@@ -177,4 +177,32 @@
         (erc-scenarios-common-say "Hi")
         (funcall expect 10 "Hola")))))
 
+(defvar url-irc-function)
+
+(ert-deftest erc-scenarios-handle-irc-url ()
+  :tags '(:expensive-test)
+  (erc-scenarios-common-with-cleanup
+      ((erc-scenarios-common-dialog "join/legacy")
+       (dumb-server (erc-d-run "localhost" t 'foonet))
+       (port (process-contact dumb-server :service))
+       (expect (erc-d-t-make-expecter))
+       (url-irc-function 'url-irc-erc)
+       (erc-url-connect-function
+        (lambda (scheme &rest r)
+          (ert-info ("Connect to foonet")
+            (should (equal scheme "irc"))
+            (with-current-buffer (apply #'erc `(:full-name "tester" ,@r))
+              (should (string= (buffer-name)
+                               (format "127.0.0.1:%d" port)))
+              (current-buffer))))))
+
+    (with-temp-buffer
+      (insert (format ";; irc://tester:changeme@127.0.0.1:%d/#chan" port))
+      (goto-char 10)
+      (browse-url-at-point))
+
+    (ert-info ("Connected")
+      (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan"))
+        (funcall expect 10 "welcome")))))
+
 ;;; erc-scenarios-misc.el ends here
diff --git a/test/lisp/erc/erc-services-tests.el 
b/test/lisp/erc/erc-services-tests.el
index c22d4cf75e..7ff2e36e77 100644
--- a/test/lisp/erc/erc-services-tests.el
+++ b/test/lisp/erc/erc-services-tests.el
@@ -474,7 +474,6 @@
     ("GNU.chat:irc/#chan" (secret . "foo"))))
 
 (ert-deftest erc--auth-source-search--pass-standard ()
-  (ert-skip "Pass backend not yet supported")
   (let ((store erc-join-tests--auth-source-pass-entries)
         (auth-sources '(password-store))
         (auth-source-do-cache nil))
@@ -487,7 +486,6 @@
       (erc-services-tests--auth-source-standard #'erc-auth-source-search))))
 
 (ert-deftest erc--auth-source-search--pass-announced ()
-  (ert-skip "Pass backend not yet supported")
   (let ((store erc-join-tests--auth-source-pass-entries)
         (auth-sources '(password-store))
         (auth-source-do-cache nil))
@@ -500,7 +498,6 @@
       (erc-services-tests--auth-source-announced #'erc-auth-source-search))))
 
 (ert-deftest erc--auth-source-search--pass-overrides ()
-  (ert-skip "Pass backend not yet supported")
   (let ((store
          `(,@erc-join-tests--auth-source-pass-entries
            ("GNU.chat:6697/#chan" (secret . "spam"))
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index c88dd9888d..ff5d802697 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -953,4 +953,229 @@
     (kill-buffer "ExampleNet")
     (kill-buffer "#chan")))
 
+(defvar erc-tests--ipv6-examples
+  '("1:2:3:4:5:6:7:8"
+    "::ffff:10.0.0.1" "::ffff:1.2.3.4" "::ffff:0.0.0.0"
+    "1:2:3:4:5:6:77:88" "::ffff:255.255.255.255"
+    "fe08::7:8" "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
+    "1:2:3:4:5:6:7:8" "1::" "1:2:3:4:5:6:7::" "1::8"
+    "1:2:3:4:5:6::8" "1:2:3:4:5:6::8" "1::7:8" "1:2:3:4:5::7:8"
+    "1:2:3:4:5::8" "1::6:7:8" "1:2:3:4::6:7:8" "1:2:3:4::8"
+    "1::5:6:7:8" "1:2:3::5:6:7:8" "1:2:3::8" "1::4:5:6:7:8"
+    "1:2::4:5:6:7:8" "1:2::8" "1::3:4:5:6:7:8" "1::3:4:5:6:7:8"
+    "1::8" "::2:3:4:5:6:7:8" "::2:3:4:5:6:7:8" "::8"
+    "::" "fe08::7:8%eth0" "fe08::7:8%1" "::255.255.255.255"
+    "::ffff:255.255.255.255" "::ffff:0:255.255.255.255"
+    "2001:db8:3:4::192.0.2.33" "64:ff9b::192.0.2.33"))
+
+(ert-deftest erc--server-connect-dumb-ipv6-regexp ()
+  (dolist (a erc-tests--ipv6-examples)
+    (should-not (string-match erc--server-connect-dumb-ipv6-regexp a))
+    (should (string-match erc--server-connect-dumb-ipv6-regexp
+                          (concat "[" a "]")))))
+
+(ert-deftest erc-select-read-args ()
+
+  (ert-info ("Defaults to TLS")
+    (should (equal (ert-simulate-keys "\r\r\r\r"
+                     (erc-select-read-args))
+                   (list :server "irc.libera.chat"
+                         :port 6697
+                         :nick (user-login-name)
+                         :password nil))))
+
+  (ert-info ("Override default TLS")
+    (should (equal (ert-simulate-keys "irc://irc.libera.chat\r\r\r\r"
+                     (erc-select-read-args))
+                   (list :server "irc.libera.chat"
+                         :port 6667
+                         :nick (user-login-name)
+                         :password nil))))
+
+  (ert-info ("Address includes port")
+    (should (equal (ert-simulate-keys
+                       "localhost:6667\rnick\r\r"
+                     (erc-select-read-args))
+                   (list :server "localhost"
+                         :port 6667
+                         :nick "nick"
+                         :password nil))))
+
+  (ert-info ("Address includes nick, password skipped via option")
+    (should (equal (ert-simulate-keys "nick@localhost:6667\r"
+                     (let (erc-prompt-for-password)
+                       (erc-select-read-args)))
+                   (list :server "localhost"
+                         :port 6667
+                         :nick "nick"
+                         :password nil))))
+
+  (ert-info ("Address includes nick and password")
+    (should (equal (ert-simulate-keys "nick:sesame@localhost:6667\r"
+                     (erc-select-read-args))
+                   (list :server "localhost"
+                         :port 6667
+                         :nick "nick"
+                         :password "sesame"))))
+
+  (ert-info ("IPv6 address plain")
+    (should (equal (ert-simulate-keys "::1\r\r\r\r"
+                     (erc-select-read-args))
+                   (list :server "[::1]"
+                         :port 6667
+                         :nick (user-login-name)
+                         :password nil))))
+
+  (ert-info ("IPv6 address with port")
+    (should (equal (ert-simulate-keys "[::1]:6667\r\r\r"
+                     (erc-select-read-args))
+                   (list :server "[::1]"
+                         :port 6667
+                         :nick (user-login-name)
+                         :password nil))))
+
+  (ert-info ("IPv6 address includes nick")
+    (should (equal (ert-simulate-keys "nick@[::1]:6667\r\r"
+                     (erc-select-read-args))
+                   (list :server "[::1]"
+                         :port 6667
+                         :nick "nick"
+                         :password nil)))))
+
+(ert-deftest erc-tls ()
+  (let (calls)
+    (cl-letf (((symbol-function 'user-login-name)
+               (lambda (&optional _) "tester"))
+              ((symbol-function 'erc-open)
+               (lambda (&rest r) (push r calls))))
+
+      (ert-info ("Defaults")
+        (erc-tls)
+        (should (equal (pop calls)
+                       '("irc.libera.chat" 6697 "tester" "unknown" t
+                         nil nil nil nil nil "user" nil))))
+
+      (ert-info ("Full")
+        (erc-tls :server "irc.gnu.org"
+                 :port 7000
+                 :user "bobo"
+                 :nick "bob"
+                 :full-name "Bob's Name"
+                 :password "bob:changeme"
+                 :client-certificate t
+                 :id 'GNU.org)
+        (should (equal (pop calls)
+                       '("irc.gnu.org" 7000 "bob" "Bob's Name" t
+                         "bob:changeme" nil nil nil t "bobo" GNU.org))))
+
+      ;; Values are often nil when called by lisp code, which leads to
+      ;; null params.  This is why `erc-open' recomputes almost
+      ;; everything.
+      (ert-info ("Fallback")
+        (let ((erc-nick "bob")
+              (erc-server "irc.gnu.org")
+              (erc-email-userid "bobo")
+              (erc-user-full-name "Bob's Name"))
+          (erc-tls :server nil
+                   :port 7000
+                   :nick nil
+                   :password "bob:changeme"))
+        (should (equal (pop calls)
+                       '(nil 7000 nil "Bob's Name" t
+                             "bob:changeme" nil nil nil nil "bobo" nil)))))))
+
+(defun erc-tests--make-server-buf (name)
+  (with-current-buffer (get-buffer-create name)
+    (erc-mode)
+    (setq erc-server-process (start-process "sleep" (current-buffer)
+                                            "sleep" "1")
+          erc-session-server (concat "irc." name ".org")
+          erc-session-port 6667
+          erc-network (intern name))
+    (set-process-query-on-exit-flag erc-server-process nil)
+    (current-buffer)))
+
+(defun erc-tests--make-client-buf (server name)
+  (unless (bufferp server)
+    (setq server (get-buffer server)))
+  (with-current-buffer (get-buffer-create name)
+    (erc-mode)
+    (setq erc--target (erc--target-from-string name))
+    (dolist (v '(erc-server-process
+                 erc-session-server
+                 erc-session-port
+                 erc-network))
+      (set v (buffer-local-value v server)))
+    (current-buffer)))
+
+(ert-deftest erc-handle-irc-url ()
+  (let* (calls
+         rvbuf
+         erc-networks-alist
+         erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook
+         (erc-url-connect-function
+          (lambda (&rest r)
+            (push r calls)
+            (if (functionp rvbuf) (funcall rvbuf) rvbuf))))
+
+    (cl-letf (((symbol-function 'erc-cmd-JOIN)
+               (lambda (&rest r) (push r calls))))
+
+      (with-current-buffer (erc-tests--make-server-buf "foonet")
+        (setq rvbuf (current-buffer)))
+      (erc-tests--make-server-buf "barnet")
+      (erc-tests--make-server-buf "baznet")
+
+      (ert-info ("Unknown network")
+        (erc-handle-irc-url "irc.foonet.org" 6667 "#chan" nil nil "irc")
+        (should (equal '("#chan" nil) (pop calls)))
+        (should-not calls))
+
+      (ert-info ("Unknown network, no port")
+        (erc-handle-irc-url "irc.foonet.org" nil "#chan" nil nil "irc")
+        (should (equal '("#chan" nil) (pop calls)))
+        (should-not calls))
+
+      (ert-info ("Known network, no port")
+        (setq erc-networks-alist '((foonet "irc.foonet.org")))
+        (erc-handle-irc-url "irc.foonet.org" nil "#chan" nil nil "irc")
+        (should (equal '("#chan" nil) (pop calls)))
+        (should-not calls))
+
+      (ert-info ("Known network, different port")
+        (erc-handle-irc-url "irc.foonet.org" 6697 "#chan" nil nil "irc")
+        (should (equal '("#chan" nil) (pop calls)))
+        (should-not calls))
+
+      (ert-info ("Known network, existing chan with key")
+        (erc-tests--make-client-buf "foonet" "#chan")
+        (erc-handle-irc-url "irc.foonet.org" nil "#chan?sec" nil nil "irc")
+        (should (equal '("#chan" "sec") (pop calls)))
+        (should-not calls))
+
+      (ert-info ("Unknown network, connect, no chan")
+        (erc-handle-irc-url "irc.gnu.org" nil nil nil nil "irc")
+        (should (equal '("irc" :server "irc.gnu.org") (pop calls)))
+        (should-not calls))
+
+      (ert-info ("Unknown network, connect, chan")
+        (with-current-buffer "foonet"
+          (should-not (local-variable-p 'erc-after-connect)))
+        (setq rvbuf (lambda () (erc-tests--make-server-buf "gnu")))
+        (erc-handle-irc-url "irc.gnu.org" nil "#spam" nil nil "irc")
+        (should (equal '("irc" :server "irc.gnu.org") (pop calls)))
+        (should-not calls)
+        (with-current-buffer "gnu"
+          (should (local-variable-p 'erc-after-connect))
+          (funcall (car erc-after-connect))
+          (should (equal '("#spam" nil) (pop calls)))
+          (should-not (local-variable-p 'erc-after-connect)))
+        (should-not calls))))
+
+  (when noninteractive
+    (kill-buffer "foonet")
+    (kill-buffer "barnet")
+    (kill-buffer "baznet")
+    (kill-buffer "#chan")))
+
 ;;; erc-tests.el ends here
diff --git a/test/lisp/erc/resources/erc-d/erc-d-tests.el 
b/test/lisp/erc/resources/erc-d/erc-d-tests.el
index a4befd96b5..8dd5cef7aa 100644
--- a/test/lisp/erc/resources/erc-d/erc-d-tests.el
+++ b/test/lisp/erc/resources/erc-d/erc-d-tests.el
@@ -562,6 +562,7 @@ DUMB-SERVER-VAR are bound accordingly in BODY."
           ;;
           (erc-server-flood-penalty 0.05)
           erc-autojoin-channels-alist
+          erc-after-connect
           erc-server-auto-reconnect)
      (should-not erc-d--slow-mo)
      (with-current-buffer "*erc-d-server*" (erc-d-t-search-for 4 "Starting"))
diff --git a/test/lisp/erc/resources/erc-scenarios-common.el 
b/test/lisp/erc/resources/erc-scenarios-common.el
index bc2cb68cd8..601c9e95c8 100644
--- a/test/lisp/erc/resources/erc-scenarios-common.el
+++ b/test/lisp/erc/resources/erc-scenarios-common.el
@@ -73,7 +73,7 @@
     (require 'erc-d-t)
     (require 'erc-d)))
 
-(require 'erc-backend)
+(require 'erc)
 
 (eval-when-compile (require 'erc-join)
                    (require 'erc-services))
@@ -125,6 +125,7 @@
       (erc-auth-source-parameters-join-function nil)
       (erc-autojoin-channels-alist nil)
       (erc-server-auto-reconnect nil)
+      (erc-after-connect nil)
       (erc-d-linger-secs 10)
       ,@bindings)))
 
@@ -295,7 +296,7 @@ buffer-naming collisions involving bouncers in ERC."
         (erc-d-t-search-for 1 "<joe>")
         (erc-d-t-absent-for 0.1 "<bob>")
         (erc-d-t-wait-for 5 (eq erc-server-process erc-server-process-bar))
-        (erc-d-t-search-for 15 "keeps you from dishonour")
+        (erc-d-t-search-for 15 "joe: It is a rupture")
         (erc-d-t-wait-for 5 (not (erc-server-process-alive)))))
 
     (when after (funcall after))))
diff --git a/test/lisp/erc/resources/join/legacy/foonet.eld 
b/test/lisp/erc/resources/join/legacy/foonet.eld
index 344ba7c1da..4025094a59 100644
--- a/test/lisp/erc/resources/join/legacy/foonet.eld
+++ b/test/lisp/erc/resources/join/legacy/foonet.eld
@@ -1,5 +1,5 @@
 ;; -*- mode: lisp-data; -*-
-((pass 1 "PASS :changeme"))
+((pass 10 "PASS :changeme"))
 ((nick 1 "NICK tester"))
 ((user 1 "USER user 0 * :tester")
  (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester")
diff --git a/test/lisp/net/browse-url-tests.el 
b/test/lisp/net/browse-url-tests.el
index 1c993958b8..dc81976821 100644
--- a/test/lisp/net/browse-url-tests.el
+++ b/test/lisp/net/browse-url-tests.el
@@ -56,6 +56,15 @@
               'browse-url--man))
   (should-not (browse-url-select-handler "man:ls" 'external)))
 
+(ert-deftest browse-url-tests-select-handler-irc ()
+  (should (eq (browse-url-select-handler "irc://localhost" 'internal)
+              'browse-url--irc))
+  (should-not (browse-url-select-handler "irc://localhost" 'external))
+  (should (eq (browse-url-select-handler "irc6://localhost")
+              'browse-url--irc))
+  (should (eq (browse-url-select-handler "ircs://tester@irc.gnu.org/#chan")
+              'browse-url--irc)))
+
 (ert-deftest browse-url-tests-select-handler-file ()
   (should (eq (browse-url-select-handler "file://foo.txt")
               'browse-url-emacs))
diff --git a/test/lisp/net/dbus-tests.el b/test/lisp/net/dbus-tests.el
index 7631842918..c808e6350e 100644
--- a/test/lisp/net/dbus-tests.el
+++ b/test/lisp/net/dbus-tests.el
@@ -407,7 +407,7 @@
     :session dbus--test-service
     '(:array (:dict-entry :string "string" :boolean t :boolean t)))
    :type 'wrong-type-argument)
-  ;; The first element ist not of a basic type.
+  ;; The first element is not of a basic type.
   (should-error
    (dbus-check-arguments
     :session dbus--test-service
@@ -1093,7 +1093,7 @@ is in progress."
     (dbus-unregister-service :session dbus--test-service)))
 
 (ert-deftest dbus-test06-register-property-emits-signal ()
-  "Check property registration for an own service, including signalling."
+  "Check property registration for an own service, including signaling."
   (skip-unless dbus--test-enabled-session-bus)
   (dbus-ignore-errors (dbus-unregister-service :session dbus--test-service))
 
@@ -1136,7 +1136,7 @@ is in progress."
            dbus--test-interface property)
           "foo"))
 
-        ;; Set property.  The new value shall be signalled.
+        ;; Set property.  The new value shall be signaled.
         (setq dbus--test-signal-received nil)
         (should
          (equal
diff --git a/test/lisp/net/eudc-resources/bbdb 
b/test/lisp/net/eudc-resources/bbdb
new file mode 100644
index 0000000000..b730bb51cc
--- /dev/null
+++ b/test/lisp/net/eudc-resources/bbdb
@@ -0,0 +1,3 @@
+;; -*- mode: Emacs-Lisp; coding: utf-8; -*-
+;;; file-format: 9
+["Emacs" "ERT3" nil nil nil nil nil ("emacs-ert-test-3@bbdb.gnu.org") ((notes 
. " ")) "c8bd3a63-3a83-48a7-a95b-be118a923e00" "2022-11-19 16:36:04 +0000" 
"2022-11-19 16:36:04 +0000" nil]
diff --git a/test/lisp/net/eudc-resources/dc=gnu,dc=org.ldif 
b/test/lisp/net/eudc-resources/dc=gnu,dc=org.ldif
new file mode 100644
index 0000000000..9db4be2028
--- /dev/null
+++ b/test/lisp/net/eudc-resources/dc=gnu,dc=org.ldif
@@ -0,0 +1,15 @@
+# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
+# CRC32 12f0e8c3
+dn: dc=gnu
+objectClass: dcObject
+objectClass: organization
+dc: gnu
+o: The ldap.gnu.org organization
+description: An organization for the following person
+structuralObjectClass: organization
+entryUUID: 43dd74ec-fc0d-103c-8d5c-7dbac5615d14
+creatorsName:
+createTimestamp: 20221119042038Z
+entryCSN: 20221119042038.100000Z#000000#000#000000
+modifiersName:
+modifyTimestamp: 20221119042038Z
diff --git 
a/test/lisp/net/eudc-resources/dc=gnu,dc=org/cn=emacs-ert-test-1.ldif 
b/test/lisp/net/eudc-resources/dc=gnu,dc=org/cn=emacs-ert-test-1.ldif
new file mode 100644
index 0000000000..7828f179df
--- /dev/null
+++ b/test/lisp/net/eudc-resources/dc=gnu,dc=org/cn=emacs-ert-test-1.ldif
@@ -0,0 +1,17 @@
+# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
+# CRC32 a33c0168
+dn: cn=emacs-ert-test-1
+objectClass: OpenLDAPperson
+cn: emacs-ert-test-1
+description:: RW1hY3Mg
+uid: 1
+sn: ERT1
+givenName: Emacs
+mail: emacs-ert-test-1@ldap.gnu.org
+structuralObjectClass: OpenLDAPperson
+entryUUID: 43dd805e-fc0d-103c-8d5d-7dbac5615d14
+creatorsName:
+createTimestamp: 20221119042038Z
+entryCSN: 20221119042038.100350Z#000000#000#000000
+modifiersName:
+modifyTimestamp: 20221119042038Z
diff --git 
a/test/lisp/net/eudc-resources/dc=gnu,dc=org/cn=emacs-ert-test-2.ldif 
b/test/lisp/net/eudc-resources/dc=gnu,dc=org/cn=emacs-ert-test-2.ldif
new file mode 100644
index 0000000000..d3de3d81c7
--- /dev/null
+++ b/test/lisp/net/eudc-resources/dc=gnu,dc=org/cn=emacs-ert-test-2.ldif
@@ -0,0 +1,17 @@
+# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
+# CRC32 56119237
+dn: cn=emacs-ert-test-2
+objectClass: OpenLDAPperson
+cn: emacs-ert-test-2
+description:: RW1hY3Mg
+uid: 2
+sn: ERT2
+givenName: Emacs
+mail: emacs-ert-test-2@ldap.gnu.org
+structuralObjectClass: OpenLDAPperson
+entryUUID: 43dd92b0-fc0d-103c-8d5e-7dbac5615d14
+creatorsName:
+createTimestamp: 20221119042038Z
+entryCSN: 20221119042038.100819Z#000000#000#000000
+modifiersName:
+modifyTimestamp: 20221119042038Z
diff --git a/test/lisp/net/eudc-resources/slapd.conf 
b/test/lisp/net/eudc-resources/slapd.conf
new file mode 100644
index 0000000000..9afafe7676
--- /dev/null
+++ b/test/lisp/net/eudc-resources/slapd.conf
@@ -0,0 +1,7 @@
+include /etc/ldap/schema/core.schema
+include /etc/ldap/schema/cosine.schema
+include /etc/ldap/schema/inetorgperson.schema
+include /etc/ldap/schema/openldap.schema
+database ldif
+directory eudc-resources
+suffix "dc=gnu,dc=org"
diff --git a/test/lisp/net/eudc-tests.el b/test/lisp/net/eudc-tests.el
index c326dcc793..212db65cb2 100644
--- a/test/lisp/net/eudc-tests.el
+++ b/test/lisp/net/eudc-tests.el
@@ -267,5 +267,45 @@
                      '(((email . "Lars Ingebrigtsen <larsi@mail-abbrev.com>, \
 Karl Fogel <kfogel@mail-abbrev.com")))))))))
 
+(require 'ldap)
+(ert-deftest eudcb-ldap ()
+  "Test the LDAP back-end."
+  (skip-unless (and (file-exists-p "/usr/sbin/slapd")
+                    (file-exists-p "/usr/bin/ldapsearch")))
+  (cd (concat (ert-resource-directory) ".."))
+  (let ((ldap-process
+         (start-process "slapd" "*slapd*" "/usr/sbin/slapd"
+                        "-h" "ldap://127.0.0.1:3899"; "-d" "0" "-4"
+                        "-f" (ert-resource-file "slapd.conf")))
+        (ldap-host-parameters-alist '(("ldap://localhost:3899";
+                                       base "dc=gnu,dc=org" auth simple)))
+        (eudc-server-hotlist '(("ldap://localhost:3899"; . ldap)))
+        (eudc-ignore-options-file t))
+    (sleep-for 1) ; Wait for slapd to start.
+    (should (equal (with-temp-buffer
+                     (insert "emacs-ert-test-1")
+                     (eudc-expand-try-all)
+                     (buffer-string))
+                   "Emacs ERT1 <emacs-ert-test-1@ldap.gnu.org>"))
+    (kill-process ldap-process)))
+
+(eval-and-compile
+  (push (expand-file-name "../elpa/packages/bbdb/lisp" source-directory)
+        load-path)
+  (defvar bbdb-file)
+  (require 'bbdb nil t))
+
+(ert-deftest eudcb-bbdb ()
+  "Test the BBDB back-end."
+  (skip-unless (featurep 'bbdb))
+  (let ((bbdb-file (ert-resource-file "bbdb"))
+        (eudc-server-hotlist '(("" . bbdb)))
+        (eudc-ignore-options-file t))
+    (should (equal (with-temp-buffer
+                     (insert "emacs-ert-test-3")
+                     (eudc-expand-try-all)
+                     (buffer-string))
+                   "Emacs ERT3 <emacs-ert-test-3@bbdb.gnu.org>"))))
+
 (provide 'eudc-tests)
 ;;; eudc-tests.el ends here
diff --git a/test/lisp/net/mailcap-tests.el b/test/lisp/net/mailcap-tests.el
index 9e60a243b3..04462dbc8b 100644
--- a/test/lisp/net/mailcap-tests.el
+++ b/test/lisp/net/mailcap-tests.el
@@ -125,7 +125,7 @@
 
 (ert-deftest mailcap-view-file ()
   (with-pristine-mailcap
-   ;; Try using a lambda as viewer and check wether
+   ;; Try using a lambda as viewer and check whether
    ;; `mailcap-view-file' works correctly.
    (let* ((mailcap-mime-extensions '((".test" . "test/test"))))
      (mailcap-add "test/test" 'mailcap--test-viewer)
diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el
index 46fef558bf..a5bae46a58 100644
--- a/test/lisp/net/tramp-tests.el
+++ b/test/lisp/net/tramp-tests.el
@@ -140,10 +140,11 @@ A resource file is in the resource directory as per
        ((eq system-type 'windows-nt) null-device)
        (t (add-to-list
            'tramp-methods
-           '("mock"
-            (tramp-login-program       "sh")
+           `("mock"
+            (tramp-login-program       ,tramp-default-remote-shell)
             (tramp-login-args          (("-i")))
-            (tramp-remote-shell        "/bin/sh")
+             (tramp-direct-async       ("-c"))
+            (tramp-remote-shell        ,tramp-default-remote-shell)
             (tramp-remote-shell-args   ("-c"))
             (tramp-connection-timeout  10)))
           (add-to-list
diff --git a/test/lisp/server-tests.el b/test/lisp/server-tests.el
new file mode 100644
index 0000000000..351b8ef8d1
--- /dev/null
+++ b/test/lisp/server-tests.el
@@ -0,0 +1,41 @@
+;;; server-tests.el --- Emacs server 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 'server)
+
+;;; Tests:
+
+(ert-deftest server-test/server-start-sets-minor-mode ()
+  "Ensure that calling `server-start' also sets `server-mode' properly."
+  (server-start)
+  (unwind-protect
+      (progn
+        ;; Make sure starting the server activates the minor mode.
+        (should (eq server-mode t))
+        (should (memq 'server-mode global-minor-modes)))
+    ;; Always stop the server, even if the above checks fail.
+    (server-start t))
+  ;; Make sure stopping the server deactivates the minor mode.
+  (should (eq server-mode nil))
+  (should-not (memq 'server-mode global-minor-modes)))
+
+;;; server-tests.el ends here
diff --git a/test/lisp/simple-tests.el b/test/lisp/simple-tests.el
index d067f3e586..6e48f11fc0 100644
--- a/test/lisp/simple-tests.el
+++ b/test/lisp/simple-tests.el
@@ -85,15 +85,16 @@
                  "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")))
+  (let ((text-quoting-style 'grave))
+    (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'
diff --git a/test/lisp/subr-tests.el b/test/lisp/subr-tests.el
index cc9610cd39..e22d1c7be0 100644
--- a/test/lisp/subr-tests.el
+++ b/test/lisp/subr-tests.el
@@ -1106,7 +1106,7 @@ final or penultimate step during initialization."))
 
 (ert-deftest test-keymap-parse-macros ()
   (should (equal (key-parse "C-x ( C-d C-x )") [24 40 4 24 41]))
-  (should (equal (kbd "C-x ( C-d C-x )") ""))
+  (should (equal (kbd "C-x ( C-d C-x )") "\^D"))
   (should (equal (kbd "C-x ( C-x )") "")))
 
 (defvar subr-test--global)
diff --git a/test/lisp/vc/vc-tests.el b/test/lisp/vc/vc-tests.el
index dc4d3af699..13248a3650 100644
--- a/test/lisp/vc/vc-tests.el
+++ b/test/lisp/vc/vc-tests.el
@@ -127,7 +127,7 @@ Don't set it globally, the functions should be let-bound.")
 
 (defun vc-test--create-repo-function (backend)
   "Run the `vc-create-repo' backend function.
-For backends which dont support it, it is emulated."
+For backends which don't support it, it is emulated."
 
   (cond
    ((eq backend 'CVS)
diff --git a/test/manual/noverlay/overlay-perf.el 
b/test/manual/noverlay/overlay-perf.el
index e84941c08f..4d79254a53 100644
--- a/test/manual/noverlay/overlay-perf.el
+++ b/test/manual/noverlay/overlay-perf.el
@@ -157,7 +157,7 @@ Return test-total elapsed time."
       (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)))
+               (error "%s expects a natnum argument" (car args)))
              (if (equal (car args) "-c")
                  (setq k (string-to-number (cadr args)))
                (setq n (string-to-number (cadr args))))
diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el
index 3fc52eaf8b..0e6d717cbb 100644
--- a/test/src/buffer-tests.el
+++ b/test/src/buffer-tests.el
@@ -42,7 +42,6 @@ recorded calls conveniently."
      overlay
      hooks-property
      (list (lambda (ov &rest args)
-             (message "  %S called on %S with args %S" hooks-property ov args)
              (should inhibit-modification-hooks)
              (should (eq ov overlay))
              (push (list hooks-property args)
@@ -175,47 +174,41 @@ properties."
                                     (t 1 2 0))
                                    (insert-behind-hooks
                                     (t 1 2 0)))))))
-      (message "BEGIN overlay-modification-hooks test-case %S" test-case)
-
-      ;; All three hooks ignore the overlay's `front-advance' and
-      ;; `rear-advance' option, so test both ways while expecting the same
-      ;; result.
-      (dolist (advance '(nil t))
-        (message "  advance is %S" advance)
-        (let-alist test-case
-          (with-temp-buffer
-            ;; Set up the temporary buffer and overlay as specified by
-            ;; the test case.
-            (insert (or .buffer-text "1234"))
-            (let ((overlay (make-overlay
-                            (or .overlay-beg 2)
-                            (or .overlay-end 4)
-                            nil
-                            advance advance)))
-              (message "  (buffer-string) is %S" (buffer-string))
-              (message "  overlay is %S" overlay)
-              (overlay-tests-start-recording-modification-hooks overlay)
-
-              ;; Modify the buffer, possibly inducing calls to the
-              ;; overlay's modification hooks.
-              (should (or .insert-at .replace))
-              (when .insert-at
-                (goto-char .insert-at)
-                (insert "x")
-                (message "  inserted \"x\" at %S, buffer-string now %S"
-                         .insert-at (buffer-string)))
-              (when .replace
-                (goto-char (point-min))
-                (search-forward .replace)
-                (replace-match "x")
-                (message "  replaced %S with \"x\"" .replace))
-
-              ;; Verify that the expected and actual modification hook
-              ;; calls match.
-              (should (equal
-                       .expected-calls
-                       (overlay-tests-get-recorded-modification-hooks
-                        overlay)))))))))
+      (ert-info ((format "test-case: %S" test-case))
+        ;; All three hooks ignore the overlay's `front-advance' and
+        ;; `rear-advance' option, so test both ways while expecting the same
+        ;; result.
+        (dolist (advance '(nil t))
+          (ert-info ((format "advance is %S" advance))
+            (let-alist test-case
+              (with-temp-buffer
+                ;; Set up the temporary buffer and overlay as specified by
+                ;; the test case.
+                (insert (or .buffer-text "1234"))
+                (let ((overlay (make-overlay
+                                (or .overlay-beg 2)
+                                (or .overlay-end 4)
+                                nil
+                                advance advance)))
+                  (overlay-tests-start-recording-modification-hooks overlay)
+
+                  ;; Modify the buffer, possibly inducing calls to the
+                  ;; overlay's modification hooks.
+                  (should (or .insert-at .replace))
+                  (when .insert-at
+                    (goto-char .insert-at)
+                    (insert "x"))
+                  (when .replace
+                    (goto-char (point-min))
+                    (search-forward .replace)
+                    (replace-match "x"))
+
+                  ;; Verify that the expected and actual modification hook
+                  ;; calls match.
+                  (should (equal
+                           .expected-calls
+                           (overlay-tests-get-recorded-modification-hooks
+                            overlay)))))))))))
 
 (ert-deftest overlay-modification-hooks-message-other-buf ()
   "Test for bug#21824.
@@ -8429,7 +8422,7 @@ Finally, kill the buffer and its temporary file."
          (insert "foo\n")
          (should buffer-auto-save-file-name)
          (setq auto-save buffer-auto-save-file-name)
-         (do-auto-save)
+         (do-auto-save t)
          (should (file-exists-p auto-save))
          (kill-buffer (current-buffer))
          (should (file-exists-p auto-save)))))))
@@ -8444,7 +8437,7 @@ Finally, kill the buffer and its temporary file."
          (insert "foo\n")
          (should buffer-auto-save-file-name)
          (setq auto-save buffer-auto-save-file-name)
-         (do-auto-save)
+         (do-auto-save t)
          (should (file-exists-p auto-save))
          ;; This should delete the auto-save file.
          (kill-buffer (current-buffer))
@@ -8460,7 +8453,7 @@ Finally, kill the buffer and its temporary file."
          (insert "foo\n")
          (should buffer-auto-save-file-name)
          (setq auto-save buffer-auto-save-file-name)
-         (do-auto-save)
+         (do-auto-save t)
          (should (file-exists-p auto-save))
          ;; This should not delete the auto-save file.
          (kill-buffer (current-buffer))
@@ -8475,7 +8468,7 @@ Finally, kill the buffer and its temporary file."
       (insert "foo")
       (should (buffer-modified-p))
       (should-not (eq (buffer-modified-p) 'autosaved))
-      (do-auto-save nil t)
+      (do-auto-save t t)
       (should (eq (buffer-modified-p) 'autosaved))
       (with-silent-modifications
         (put-text-property 1 3 'face 'bold))
@@ -8499,7 +8492,7 @@ Finally, kill the buffer and its temporary file."
       (restore-buffer-modified-p nil)
       (should-not (buffer-modified-p))
       (insert "bar")
-      (do-auto-save nil t)
+      (do-auto-save t t)
       (should (eq (buffer-modified-p) 'autosaved))
       (insert "zot")
       (restore-buffer-modified-p 'autosaved)
diff --git a/test/src/comp-resources/comp-test-funcs.el 
b/test/src/comp-resources/comp-test-funcs.el
index 9092f040c8..03925d4d2e 100644
--- a/test/src/comp-resources/comp-test-funcs.el
+++ b/test/src/comp-resources/comp-test-funcs.el
@@ -211,10 +211,10 @@
       (comp-tests-err-arith-f)
     (arith-error (concat "arith-error "
                          (error-message-string err)
-                         " catched"))
+                         " caught"))
     (error (concat "error "
                    (error-message-string err)
-                   " catched"))))
+                   " caught"))))
 (defun comp-tests-condition-case-1-f ()
   ;; Bpushhandler Bpophandler
   (condition-case
@@ -222,10 +222,10 @@
       (comp-tests-err-foo-f)
     (arith-error (concat "arith-error "
                          (error-message-string err)
-                         " catched"))
+                         " caught"))
     (error (concat "error "
                    (error-message-string err)
-                   " catched"))))
+                   " caught"))))
 (defun comp-tests-catch-f (f)
   (catch 'foo
     (funcall f)))
diff --git a/test/src/comp-tests.el b/test/src/comp-tests.el
index 734b4a0d22..4e512098a3 100644
--- a/test/src/comp-tests.el
+++ b/test/src/comp-tests.el
@@ -298,9 +298,9 @@ Check that the resulting binaries do not differ."
 (comp-deftest non-locals ()
   "Test non locals."
   (should (string= (comp-tests-condition-case-0-f)
-                   "arith-error Arithmetic error catched"))
+                   "arith-error Arithmetic error caught"))
   (should (string= (comp-tests-condition-case-1-f)
-                   "error Foo catched"))
+                   "error Foo caught"))
   (should (= (comp-tests-catch-f
               (lambda () (throw 'foo 3)))
              3))
diff --git a/test/src/eval-tests.el b/test/src/eval-tests.el
index bb2f04e8ee..0e12e4dbd8 100644
--- a/test/src/eval-tests.el
+++ b/test/src/eval-tests.el
@@ -222,7 +222,7 @@ expressions works for identifiers starting with period."
 
 (ert-deftest eval-tests/funcall-with-delayed-message ()
   ;; Check that `funcall-with-delayed-message' displays its message before
-  ;; its function terminates iff the timeout is short enough.
+  ;; its function terminates if the timeout is short enough.
 
   ;; This also serves as regression test for bug#55628 where a short
   ;; timeout was rounded up to the next whole second.
diff --git a/test/src/font-tests.el b/test/src/font-tests.el
index 7e9669c651..683d331d75 100644
--- a/test/src/font-tests.el
+++ b/test/src/font-tests.el
@@ -115,7 +115,7 @@ expected font properties from parsing NAME.")
 (defun test-font-parse ()
   "Test font name parsing."
   (interactive)
-  (switch-to-buffer (generate-new-buffer "*Font Pase Test*"))
+  (switch-to-buffer (generate-new-buffer "*Font Parse Test*"))
   (setq show-trailing-whitespace nil)
   (let ((pass-face '((t :foreground "green")))
        (fail-face '((t :foreground "red"))))
diff --git a/test/src/thread-tests.el b/test/src/thread-tests.el
index 75d67140a9..731fa893c1 100644
--- a/test/src/thread-tests.el
+++ b/test/src/thread-tests.el
@@ -217,7 +217,7 @@
        (while (not threads-mutex-key)
         (thread-yield))
        (thread-signal thr 'quit nil)
-       ;; `quit' is not catched by `should-error'.  We must indicate it.
+       ;; `quit' is not caught by `should-error'.  We must indicate it.
        (condition-case nil
            (thread-join thr)
          (quit (signal 'error nil)))))))
diff --git a/test/src/treesit-tests.el b/test/src/treesit-tests.el
new file mode 100644
index 0000000000..59264722ba
--- /dev/null
+++ b/test/src/treesit-tests.el
@@ -0,0 +1,535 @@
+;;; treesit-tests.el --- tests for src/treesit.c         -*- lexical-binding: 
t; -*-
+
+;; Copyright (C) 2021-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 'treesit)
+
+(ert-deftest treesit-basic-parsing ()
+  "Test basic parsing routines."
+  (skip-unless (treesit-language-available-p 'json))
+  (with-temp-buffer
+    (let ((parser (treesit-parser-create 'json)))
+      (should
+       (eq parser (car (treesit-parser-list))))
+      (should
+       (equal (treesit-node-string
+               (treesit-parser-root-node parser))
+              "(ERROR)"))
+
+      (insert "[1,2,3]")
+      (should
+       (equal (treesit-node-string
+               (treesit-parser-root-node parser))
+              "(document (array (number) (number) (number)))"))
+
+      (goto-char (point-min))
+      (forward-char 3)
+      (insert "{\"name\": \"Bob\"},")
+      (should
+       (equal
+        (treesit-node-string
+         (treesit-parser-root-node parser))
+        "(document (array (number) (object (pair key: (string 
(string_content)) value: (string (string_content)))) (number) (number)))")))))
+
+(ert-deftest treesit-node-api ()
+  "Tests for node API."
+  (skip-unless (treesit-language-available-p 'json))
+  (with-temp-buffer
+    (let (parser root-node doc-node object-node pair-node)
+      (progn
+        (insert "[1,2,{\"name\": \"Bob\"},3]")
+        (setq parser (treesit-parser-create 'json))
+        (setq root-node (treesit-parser-root-node
+                         parser)))
+      ;; `treesit-node-type'.
+      (should (equal "document" (treesit-node-type root-node)))
+      ;; `treesit-node-check'.
+      (should (eq t (treesit-node-check root-node 'named)))
+      (should (eq nil (treesit-node-check root-node 'missing)))
+      (should (eq nil (treesit-node-check root-node 'extra)))
+      (should (eq nil (treesit-node-check root-node 'has-error)))
+      ;; `treesit-node-child'.
+      (setq doc-node (treesit-node-child root-node 0))
+      (should (equal "array" (treesit-node-type doc-node)))
+      (should (equal (treesit-node-string doc-node)
+                     "(array (number) (number) (object (pair key: (string 
(string_content)) value: (string (string_content)))) (number))"))
+      ;; `treesit-node-child-count'.
+      (should (eql 9 (treesit-node-child-count doc-node)))
+      (should (eql 4 (treesit-node-child-count doc-node t)))
+      ;; `treesit-node-field-name-for-child'.
+      (setq object-node (treesit-node-child doc-node 2 t))
+      (setq pair-node (treesit-node-child object-node 0 t))
+      (should (equal "object" (treesit-node-type object-node)))
+      (should (equal "pair" (treesit-node-type pair-node)))
+      (should (equal "key"
+                     (treesit-node-field-name-for-child
+                      pair-node 0)))
+      ;; `treesit-node-child-by-field-name'.
+      (should (equal "(string (string_content))"
+                     (treesit-node-string
+                      (treesit-node-child-by-field-name
+                       pair-node "key"))))
+      ;; `treesit-node-next-sibling'.
+      (should (equal "(number)"
+                     (treesit-node-string
+                      (treesit-node-next-sibling object-node t))))
+      (should (equal "(\",\")"
+                     (treesit-node-string
+                      (treesit-node-next-sibling object-node))))
+      ;; `treesit-node-prev-sibling'.
+      (should (equal "(number)"
+                     (treesit-node-string
+                      (treesit-node-prev-sibling object-node t))))
+      (should (equal "(\",\")"
+                     (treesit-node-string
+                      (treesit-node-prev-sibling object-node))))
+      ;; `treesit-node-first-child-for-pos'.
+      (should (equal "(number)"
+                     (treesit-node-string
+                      (treesit-node-first-child-for-pos
+                       doc-node 3 t))))
+      (should (equal "(\",\")"
+                     (treesit-node-string
+                      (treesit-node-first-child-for-pos
+                       doc-node 3))))
+      ;; `treesit-node-descendant-for-range'.
+      (should (equal "(\"{\")"
+                     (treesit-node-string
+                      (treesit-node-descendant-for-range
+                       root-node 6 7))))
+      (should (equal "(object (pair key: (string (string_content)) value: 
(string (string_content))))"
+                     (treesit-node-string
+                      (treesit-node-descendant-for-range
+                       root-node 6 7 t))))
+      ;; `treesit-node-eq'.
+      (should (treesit-node-eq root-node root-node))
+      (should (not (treesit-node-eq root-node doc-node))))))
+
+(ert-deftest treesit-query-api ()
+  "Tests for query API."
+  (skip-unless (treesit-language-available-p 'json))
+  (with-temp-buffer
+    (let (parser root-node pattern doc-node object-node pair-node)
+      (progn
+        (insert "[1,2,{\"name\": \"Bob\"},3]")
+        (setq parser (treesit-parser-create 'json))
+        (setq root-node (treesit-parser-root-node
+                         parser)))
+
+      ;; Test `treesit-query-capture' on string, sexp and compiled
+      ;; queries.
+      (dolist (query1
+               ;; String query.
+               '("(string) @string
+(pair key: (_) @keyword)
+((_) @bob (#match \"^B.b$\" @bob))
+(number) @number
+((number) @n3 (#equal \"3\" @n3)) "
+                 ;; Sexp query.
+                 ((string) @string
+                  (pair key: (_) @keyword)
+                  ((_) @bob (:match "^B.b$" @bob))
+                  (number) @number
+                  ((number) @n3 (:equal "3" @n3)))))
+        ;; Test `treesit-query-compile'.
+        (dolist (query (list query1
+                             (treesit-query-compile 'json query1)))
+          (should
+           (equal
+            '((number . "1") (number . "2")
+              (keyword . "\"name\"")
+              (string . "\"name\"")
+              (string . "\"Bob\"")
+              (bob . "Bob")
+              (number . "3")
+              (n3 . "3"))
+            (mapcar (lambda (entry)
+                      (cons (car entry)
+                            (treesit-node-text
+                             (cdr entry))))
+                    (treesit-query-capture root-node query))))))
+      ;; Test `treesit-query-expand'.
+      (should
+       (equal
+        "(type field: (_) @capture .) ? * + \"return\""
+        (treesit-query-expand
+         '((type field: (_) @capture :anchor)
+           :? :* :+ "return")))))))
+
+(ert-deftest treesit-narrow ()
+  "Tests if narrowing works."
+  (skip-unless (treesit-language-available-p 'json))
+  (with-temp-buffer
+    (let (parser root-node pattern doc-node object-node pair-node)
+      (progn
+        (insert "xxx[1,{\"name\": \"Bob\"},2,3]xxx")
+        (narrow-to-region (+ (point-min) 3) (- (point-max) 3))
+        (setq parser (treesit-parser-create 'json))
+        (setq root-node (treesit-parser-root-node
+                         parser)))
+      ;; This test is from the basic test.
+      (should
+       (equal
+        (treesit-node-string
+         (treesit-parser-root-node parser))
+        "(document (array (number) (object (pair key: (string 
(string_content)) value: (string (string_content)))) (number) (number)))"))
+
+      (widen)
+      (goto-char (point-min))
+      (insert "ooo")
+      (should (equal "oooxxx[1,{\"name\": \"Bob\"},2,3]xxx"
+                     (buffer-string)))
+      (delete-region 10 26)
+      (should (equal "oooxxx[1,2,3]xxx"
+                     (buffer-string)))
+      (narrow-to-region (+ (point-min) 6) (- (point-max) 3))
+      ;; This test is also from the basic test.
+      (should
+       (equal (treesit-node-string
+               (treesit-parser-root-node parser))
+              "(document (array (number) (number) (number)))"))
+      (widen)
+      (goto-char (point-max))
+      (insert "[1,2]")
+      (should (equal "oooxxx[1,2,3]xxx[1,2]"
+                     (buffer-string)))
+      (narrow-to-region (- (point-max) 5) (point-max))
+      (should
+       (equal (treesit-node-string
+               (treesit-parser-root-node parser))
+              "(document (array (number) (number)))"))
+      (widen)
+      (goto-char (point-min))
+      (insert "[1]")
+      (should (equal "[1]oooxxx[1,2,3]xxx[1,2]"
+                     (buffer-string)))
+      (narrow-to-region (point-min) (+ (point-min) 3))
+      (should
+       (equal (treesit-node-string
+               (treesit-parser-root-node parser))
+              "(document (array (number)))")))))
+
+(ert-deftest treesit-cross-boundary ()
+  "Tests for cross-boundary edits.
+Cross-boundary means crossing visible_beg and visible_end.  We
+don't test if parser parses correctly, instead we just check
+edits like this don't produce assertion errors.  (I inserted a
+bunch of assertions that checks e.g. visible_beg <=
+visible_end.)"
+  (skip-unless (treesit-language-available-p 'json))
+  (with-temp-buffer
+    (let (parser root-node pattern doc-node object-node pair-node)
+      (progn
+        (insert "xxx[1,{\"name\": \"Bob\"},2,3]xxx")
+        (narrow-to-region (+ (point-min) 3) (- (point-max) 3))
+        (setq parser (treesit-parser-create 'json))
+        ;; Now visible_beg/end = visible boundary.
+        (setq root-node (treesit-parser-root-node parser)))
+      ;; Now parser knows the content of the visible region.
+      (widen)
+      ;; Now visible_beg/end don't change, but visible region expanded.
+      (delete-region 1 7)
+      ;; (1) This change is across visible_beg.  I expect
+      ;; ts_record_change to receive (start=1, old_end=7, new_end=1).
+      (treesit-parser-root-node parser)
+      ;; Above form forces a parse which calls
+      ;; `ts_ensure_position_synced'. Now visible_beg/end matches the
+      ;; visible region (whole buffer).  We want to test that this
+      ;; doesn't cause assertion error.
+
+      (should (equal "{\"name\": \"Bob\"},2,3]xxx" (buffer-string)))
+      (narrow-to-region 1 16)
+      (should (equal "{\"name\": \"Bob\"}" (buffer-string)))
+      (treesit-parser-root-node parser)
+      ;; Call `ts_ensure_position_synced' again to update visible_beg/end.
+      (widen)
+      (goto-char 14)
+      (insert "by")
+      ;; (2) This change is inside [visible_beg, visible_end].
+      (should (equal "{\"name\": \"Bobby\"},2,3]xxx" (buffer-string)))
+      (delete-region 14 23)
+      ;; This delete is across visible_end.
+      (should (equal "{\"name\": \"Bobxxx" (buffer-string)))
+      (treesit-parser-root-node parser)
+      ;; visible_beg/end synced.
+
+      (narrow-to-region 3 7)
+      (should (equal "name" (buffer-string)))
+      (treesit-parser-root-node parser)
+      ;; visible_beg/end synced.
+      (widen)
+      (goto-char (point-min))
+      (insert "zzz")
+      (should (equal "zzz{\"name\": \"Bobxxx" (buffer-string)))
+      ;; (3) Test inserting before visible_beg.
+      (treesit-parser-root-node parser)
+      ;; visible_beg/end synced.
+
+      (narrow-to-region 4 11)
+      (should (equal "{\"name\"" (buffer-string)))
+      (treesit-parser-root-node parser)
+      ;; visible_beg/end synced.
+      (widen)
+      (goto-char (point-max))
+      (insert "yyy")
+      ;; (4) This change is after visible_end.
+      (treesit-parser-root-node parser)
+      ;; Sync up visible_beg/end.
+      (should (equal "zzz{\"name\": \"Bobxxxyyy" (buffer-string)))
+
+      (narrow-to-region 1 17)
+      (should (equal "zzz{\"name\": \"Bob" (buffer-string)))
+      (treesit-parser-root-node parser)
+      ;; Sync up visible_beg/end.
+      (widen)
+      (delete-region 13 (point-max))
+      (treesit-parser-root-node parser)
+      ;; Sync up visible_beg/end.
+      (should (equal "zzz{\"name\": " (buffer-string)))
+      ;; Ideally we want to also test the case where we delete and
+      ;; insert simultaneously, but the only such use is in
+      ;; `casify_region', all others either only inserts or only
+      ;; deletes.  I'll leave it to someone to try to write a test
+      ;; that calls that.
+      )))
+
+(ert-deftest treesit-range ()
+  "Tests if range works."
+  (skip-unless (treesit-language-available-p 'json))
+  (with-temp-buffer
+    (let (parser root-node pattern doc-node object-node pair-node)
+      (progn
+        (insert "[[1],oooxxx[1,2,3],xxx[1,2]]")
+        (setq parser (treesit-parser-create 'json))
+        (setq root-node (treesit-parser-root-node
+                         parser)))
+
+      (should (eq (treesit-parser-included-ranges parser) nil))
+
+      (should-error
+       (treesit-parser-set-included-ranges
+        parser '((1 . 6) (5 . 20)))
+       :type '(treesit-range-invalid))
+
+      (treesit-parser-set-included-ranges
+       parser '((1 . 6) (12 . 20) (23 . 29)))
+      (should (equal '((1 . 6) (12 . 20) (23 . 29))
+                     (treesit-parser-included-ranges parser)))
+      (should (equal "(document (array (array (number)) (array (number) 
(number) (number)) (array (number) (number))))"
+                     (treesit-node-string
+                      (treesit-parser-root-node parser))))
+
+      (treesit-parser-set-included-ranges parser nil)
+      (should (eq (treesit-parser-included-ranges parser) nil))
+
+      ;; `treesit--merge-ranges'.
+      (let ((old-ranges '((1 . 10) ; (1) -- before (a)
+                          (20 . 30); (2) -- intersect with (b)
+                          (42 . 46) (47 . 48) ; (3) -- inside (c)
+                          (55 . 65) (70 . 75) ; (4) -- intersect start-end
+                          (80 . 90) ; (4)
+                          ))
+            (new-ranges '((10 . 15) ; (a)
+                          (18 . 25) (26 . 28) ; (b)
+                          (40 . 50) ; (c)
+                          (90 . 100) ; (d) -- after (4)
+                          ))
+            (result '((1 . 10) ; (1)
+                      (10 . 15) ; (a)
+                      (18 . 25) (26 . 28) ; (b)
+                      (40 . 50) ; (c)
+                      (80 . 90) ; (4)
+                      (90 . 100) ; (d)
+                      )))
+        (should (equal (treesit--merge-ranges
+                        old-ranges new-ranges 60 75)
+                       result)))
+      ;; TODO: More tests.
+      )))
+
+(ert-deftest treesit-multi-lang ()
+  "Tests if parsing multiple language works."
+  (skip-unless (and (treesit-language-available-p 'html)
+                    (treesit-language-available-p 'css)
+                    (treesit-language-available-p 'javascript)))
+  (with-temp-buffer
+    (let (html css js html-range css-range js-range)
+      (progn
+        (insert "<html><script>1</script><style>body {}</style></html>")
+        (setq html (treesit-parser-create 'html))
+        (setq css (treesit-parser-create 'css))
+        (setq js (treesit-parser-create 'javascript)))
+      ;; JavaScript.
+      (setq js-range
+            (treesit-query-range
+             'html
+             '((script_element (raw_text) @capture))))
+      (should (equal '((15 . 16)) js-range))
+      (treesit-parser-set-included-ranges js js-range)
+      (should (equal "(program (expression_statement (number)))"
+                     (treesit-node-string
+                      (treesit-parser-root-node js))))
+      ;; CSS.
+      (setq css-range
+            (treesit-query-range
+             'html
+             '((style_element (raw_text) @capture))))
+      (should (equal '((32 . 39)) css-range))
+      (treesit-parser-set-included-ranges css css-range)
+      (should
+       (equal "(stylesheet (rule_set (selectors (tag_name)) (block)))"
+              (treesit-node-string
+               (treesit-parser-root-node css))))
+      ;; TODO: More tests.
+      )))
+
+(ert-deftest treesit-parser-supplemental ()
+  "Supplemental node functions."
+  (skip-unless (treesit-language-available-p 'json))
+  ;; `treesit-parse-string'.
+  (should (equal (treesit-node-string
+                  (treesit-parse-string
+                   "[1,2,{\"name\": \"Bob\"},3]"
+                   'json))
+                 "(document (array (number) (number) (object (pair key: 
(string (string_content)) value: (string (string_content)))) (number)))"))
+  (with-temp-buffer
+    (let (parser root-node doc-node object-node pair-node)
+      (progn
+        (insert "[1,2,{\"name\": \"Bob\"},3]")
+        (setq parser (treesit-parser-create 'json))
+        (setq root-node (treesit-parser-root-node
+                         parser))
+        (setq doc-node (treesit-node-child root-node 0)))
+      )))
+
+(ert-deftest treesit-node-supplemental ()
+  "Supplemental node functions."
+  (skip-unless (treesit-language-available-p 'json))
+  (let (parser root-node doc-node array-node)
+    (progn
+      (insert "[1,2,{\"name\": \"Bob\"},3]")
+      (setq parser (treesit-parser-create 'json))
+      (setq root-node (treesit-parser-root-node
+                       parser))
+      (setq doc-node (treesit-node-child root-node 0)))
+    ;; `treesit-node-buffer'.
+    (should (equal (treesit-node-buffer root-node)
+                   (current-buffer)))
+    ;; `treesit-node-language'.
+    (should (eq (treesit-node-language root-node)
+                'json))
+    ;; `treesit-node-at'.
+    (should (equal (treesit-node-string
+                    (treesit-node-at 1 'json))
+                   "(\"[\")"))
+    ;; `treesit-node-on'
+    (should (equal (treesit-node-string
+                    (treesit-node-on 1 2 'json))
+                   "(\"[\")"))
+    ;; `treesit-buffer-root-node'.
+    (should (treesit-node-eq
+             (treesit-buffer-root-node 'json)
+             root-node))
+    ;; `treesit-filter-child'.
+    (should (equal (mapcar
+                    (lambda (node)
+                      (treesit-node-type node))
+                    (treesit-filter-child
+                     doc-node (lambda (node)
+                                (treesit-node-check node 'named))))
+                   '("number" "number" "object" "number")))
+    ;; `treesit-node-text'.
+    (should (equal (treesit-node-text doc-node)
+                   "[1,2,{\"name\": \"Bob\"},3]"))
+    ;; `treesit-node-index'.
+    (should (eq (treesit-node-index doc-node)
+                0))
+    ;; TODO:
+    ;; `treesit-parent-until'
+    ;; `treesit-parent-while'
+    ;; `treesit-node-children'
+    ;; `treesit-node-field-name'
+    ;; `treesit-search-forward-goto'
+    ))
+
+(ert-deftest treesit-node-at ()
+  "Test `treesit-node-at'."
+  (skip-unless (treesit-language-available-p 'json))
+  (let (parser root-node)
+    (progn
+      (insert "[1,  2, 3,4]  ")
+      (setq parser (treesit-parser-create 'json))
+      (setq root-node (treesit-parser-root-node
+                       parser)))
+    ;; Point at ",", should return ",".
+    (goto-char (point-min))
+    (search-forward "1")
+    (should (equal (treesit-node-text
+                    (treesit-node-at (point)))
+                   ","))
+    ;; Point behind ",", should still return the ",".
+    (search-forward ",")
+    (should (equal (treesit-node-text
+                    (treesit-node-at (point)))
+                   ","))
+    ;; Point between "," and "2", should return 2.
+    (forward-char)
+    (should (equal (treesit-node-text
+                    (treesit-node-at (point)))
+                   "2"))
+    ;; EOF, should return the last leaf node "]".
+    (goto-char (point-max))
+    (should (equal (treesit-node-text
+                    (treesit-node-at (point)))
+                   "]"))))
+
+(ert-deftest treesit-node-check ()
+  "Test `treesit-node-check'."
+  (skip-unless (treesit-language-available-p 'json))
+  (let (parser root-node array-node comment-node)
+    (progn
+      (insert "/* comment */ [1,  2, 3,4  ")
+      (setq parser (treesit-parser-create 'json))
+      (setq root-node (treesit-parser-root-node
+                       parser))
+      (setq comment-node (treesit-node-child root-node 0))
+      (setq array-node (treesit-node-child root-node 1)))
+
+    (should (treesit-node-check comment-node 'extra))
+    (should (treesit-node-check array-node 'has-error))
+    (should-error (treesit-node-check array-node 'xxx))
+    (should (treesit-node-check (treesit-node-child array-node -1)
+                                'missing))
+    (goto-char (point-max))
+    (insert "]")
+    (should (treesit-node-check array-node 'outdated))))
+
+;; TODO
+;; - Functions in treesit.el
+;; - treesit-load-name-override-list
+;; - treesit-search-subtree
+;; - treesit-search-forward
+;; - treesit-induce-sparse-tree
+;; - treesit-search-forward
+
+
+(provide 'treesit-tests)
+;;; treesit-tests.el ends here



reply via email to

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