emacs-diffs
[Top][All Lists]
Advanced

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

master 8036739c1b: Merge from origin/emacs-29


From: Stefan Kangas
Subject: master 8036739c1b: Merge from origin/emacs-29
Date: Tue, 13 Dec 2022 18:11:46 -0500 (EST)

branch: master
commit 8036739c1bb6fab46b7728b3acac7511e066d64b
Merge: 19d608da2a cd5856e403
Author: Stefan Kangas <stefankangas@gmail.com>
Commit: Stefan Kangas <stefankangas@gmail.com>

    Merge from origin/emacs-29
    
    cd5856e4038 Fix bug when calling `rgrep` non-interactively
    ba4bdd6a259 Adapt Tramp specific tests in eglot-tests.el
    1d5c35c8e46 * lisp/textmodes/texinfo.el (texinfo-flymake): Improve do...
    a99d0e7e6c9 Support a function in the BUFFER-LIST arg of list-buffers...
    def51dd6458 ; Fix typos
    4980ed7a6d9 Don't allow lazy highlight from recursive minibuffers
    4ef8b9f5441 Improve resetting face attributes when looking for suitab...
    c4b8bc90a8e ; Fix typos in doc strings
    c45eb138451 ; * lisp/bs.el (bs-attributes-list): Doc fix
    d6adaf487d8 Add lexical-binding to example package header
    03ad1a92a2d Add improved tree-sitter navigation
    a5272e2a7cc ; * test/src/treesit-tests.el: Add outline headers.
    489bcacc7c3 Add cross-reference to flush-lines
    0f9e6532b14 Use font-lock-number-face for numeric values in csharp-mode
    4bccb7b211e Make treesit-query-validate create a read-only buffer
    c0fe6c72cec Improve dockerfile-ts-mode imenu generation (Bug#59979)
    631908f7017 Add "->" to python--treesit-operators (bug#59968)
    5d4274d9b65 ; * admin/notes/tree-sitter/build-module/build.sh: Add -f...
    d264b75669d Align C++ access specifiers to their enclosing class/stru...
    ca67d988d87 Add cmake-ts-mode
    8ec923775de Tweak various ts-mode's indent and fontification (bug#59931)
    647b6a8099f Add expression for generic_name in csharp-ts-mode (bug#59...
    5b178efd85a ; Adjust eglot test to recent autopep8/pycodestyle
    58b8ed8b55c ; Avoid compilation warning on MS-Windows
    40c23c11e88 * lisp/outline.el: Fix the value 'insert' of outline-mino...
    527eb11de20 * lisp/minibuffer.el (completions-group-separator): Rever...
    42d740fb2cb ; Skip two eglot tests when typescript is missing
    19ef86f775a ; Remove outdated text describing overlays
    081bf583007 Skip Eglot rust-analyzer tests if 'cargo' isn't available
    
    # Conflicts:
    #       lisp/progmodes/typescript-ts-mode.el
    #       lisp/treesit.el
---
 ChangeLog.3                                   |   2 +-
 admin/notes/tree-sitter/build-module/batch.sh |   1 +
 admin/notes/tree-sitter/build-module/build.sh |   5 +-
 doc/lispref/display.texi                      |  12 +-
 doc/lispref/package.texi                      |   4 +-
 etc/NEWS                                      |   5 +
 lisp/ChangeLog.15                             |   2 +-
 lisp/apropos.el                               |   2 +-
 lisp/bs.el                                    |  10 +-
 lisp/buff-menu.el                             |  23 +-
 lisp/emacs-lisp/cl-extra.el                   |   2 +-
 lisp/isearch.el                               |   2 +-
 lisp/minibuffer.el                            |   4 +-
 lisp/outline.el                               |   2 +-
 lisp/progmodes/c-ts-mode.el                   |   6 +-
 lisp/progmodes/cmake-ts-mode.el               | 234 ++++++++++++++++++++
 lisp/progmodes/csharp-mode.el                 |   8 +-
 lisp/progmodes/dockerfile-ts-mode.el          |   7 +-
 lisp/progmodes/eglot.el                       |   2 +-
 lisp/progmodes/grep.el                        |   4 +
 lisp/progmodes/java-ts-mode.el                |   4 +-
 lisp/progmodes/js.el                          |   1 +
 lisp/progmodes/json-ts-mode.el                |   5 +-
 lisp/progmodes/project.el                     |  25 ++-
 lisp/progmodes/python.el                      |   3 +-
 lisp/progmodes/typescript-ts-mode.el          |   1 +
 lisp/progmodes/xref.el                        |   2 +-
 lisp/replace.el                               |   7 +-
 lisp/textmodes/texinfo.el                     |   2 +
 lisp/treesit.el                               | 210 +++++++++++++++++-
 src/timefns.c                                 |   4 +
 src/xfaces.c                                  |  81 ++++---
 test/lisp/progmodes/eglot-tests.el            |  69 +++---
 test/src/treesit-tests.el                     | 295 ++++++++++++++++++++++++++
 34 files changed, 937 insertions(+), 109 deletions(-)

diff --git a/ChangeLog.3 b/ChangeLog.3
index edb738b56c..b162f96cb1 100644
--- a/ChangeLog.3
+++ b/ChangeLog.3
@@ -1654,7 +1654,7 @@
        This reverts commit 78f76fe16e2737b40694f82af28d17a90a21ed7b.
 
        The commit made calls to cl-concatenate bug out, since
-       autoloading defalises doesn't work very well (bug#54901).
+       autoloading defaliases doesn't work very well (bug#54901).
 
 2022-04-12  Eli Zaretskii  <eliz@gnu.org>
 
diff --git a/admin/notes/tree-sitter/build-module/batch.sh 
b/admin/notes/tree-sitter/build-module/batch.sh
index 6dce000caa..c395449977 100755
--- a/admin/notes/tree-sitter/build-module/batch.sh
+++ b/admin/notes/tree-sitter/build-module/batch.sh
@@ -2,6 +2,7 @@
 
 languages=(
     'c'
+    'cmake'
     'cpp'
     'css'
     'c-sharp'
diff --git a/admin/notes/tree-sitter/build-module/build.sh 
b/admin/notes/tree-sitter/build-module/build.sh
index cc31e3f6f0..d020ee92c3 100755
--- a/admin/notes/tree-sitter/build-module/build.sh
+++ b/admin/notes/tree-sitter/build-module/build.sh
@@ -23,6 +23,9 @@ case "${lang}" in
     "dockerfile")
         namespace="camdencheek"
         ;;
+    "cmake")
+        namespace="uyha"
+        ;;
     "typescript")
         sourcedir="tree-sitter-typescript/typescript/src"
         grammardir="tree-sitter-typescript/typescript"
@@ -43,7 +46,7 @@ cd "${sourcedir}"
 
 ### Build
 
-cc -c -I. parser.c
+cc -fPIC -c -I. parser.c
 # Compile scanner.c.
 if test -f scanner.c
 then
diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index 340aa400cf..13dc228261 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -1462,17 +1462,7 @@ object that belongs to a particular buffer, and has a 
specified
 beginning and end.  It also has properties that you can examine and set;
 these affect the display of the text within the overlay.
 
-@cindex scalability of overlays
-@cindex overlays, scalability
-The visual effect of an overlay is the same as of the corresponding
-text property (@pxref{Text Properties}).  However, due to a different
-implementation, overlays generally don't scale well (many operations
-take a time that is proportional to the number of overlays in the
-buffer).  If you need to affect the visual appearance of many portions
-in the buffer, we recommend using text properties.
-
-An overlay uses markers to record its beginning and end; thus,
-editing the text of the buffer adjusts the beginning and end of each
+Editing the text of the buffer adjusts the beginning and end of each
 overlay so that it stays with the text.  When you create the overlay,
 you can specify whether text inserted at the beginning should be
 inside the overlay or outside, and likewise for the end of the overlay.
diff --git a/doc/lispref/package.texi b/doc/lispref/package.texi
index a503a7edde..4cb0c3214a 100644
--- a/doc/lispref/package.texi
+++ b/doc/lispref/package.texi
@@ -152,9 +152,9 @@ the various headers, as illustrated by the following 
example:
 
 @example
 @group
-;;; superfrobnicator.el --- Frobnicate and bifurcate flanges
+;;; superfrobnicator.el --- Frobnicate and bifurcate flanges  -*- 
lexical-binding:t -*-
 
-;; Copyright (C) 2011 Free Software Foundation, Inc.
+;; Copyright (C) 2022 Free Software Foundation, Inc.
 @end group
 
 ;; Author: J. R. Hacker <jrh@@example.com>
diff --git a/etc/NEWS b/etc/NEWS
index 8f6c67a3cb..457a351634 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -91,6 +91,11 @@ A major mode based on the tree-sitter library for editing
 Dockerfiles.  It includes support for font-locking, indentation, Imenu,
 and which-func.
 
+** New major mode 'cmake-ts-mode'.
+A major mode based on the tree-sitter library for editing CMake files.
+It includes support for font-locking, indentation, Imenu, and
+which-func.
+
 
 * Incompatible Lisp Changes in Emacs 30.1
 
diff --git a/lisp/ChangeLog.15 b/lisp/ChangeLog.15
index 8a21c291e7..ac1befac1e 100644
--- a/lisp/ChangeLog.15
+++ b/lisp/ChangeLog.15
@@ -10320,7 +10320,7 @@
        * bs.el, expand.el, ido.el, image-dired.el, lpr.el, pcomplete.el,
        * skeleton.el, term.el, time.el, wid-edit.el, woman.el,
        * calc/calc-graph.el, calc/calc-help.el, calc/calc-incom.el,
-       * calc/calc.el, emacs-lisp/cl-extra.el, emacs-lips/cl-loaddefs.el,
+       * calc/calc.el, emacs-lisp/cl-extra.el, emacs-lisp/cl-loaddefs.el,
        * emulation/cua-rect.el, emulation/viper-ex.el, eshell/esh-test.el,
        * eshell/eshell.el, gnus/gnus-uu.el, gnus/nndoc.el, gnus/nnrss.el,
        * gnus/rfc2047.el, gnus/utf7.el, international/utf-7.el,
diff --git a/lisp/apropos.el b/lisp/apropos.el
index a731926f45..89f1f150a1 100644
--- a/lisp/apropos.el
+++ b/lisp/apropos.el
@@ -496,7 +496,7 @@ Intended as a value for `revert-buffer-function'."
               outline-level (lambda () 1)
               outline-minor-mode-cycle t
               outline-minor-mode-highlight t
-              outline-minor-mode-use-buttons t))
+              outline-minor-mode-use-buttons 'insert))
 
 (defvar apropos-multi-type t
   "If non-nil, this apropos query concerns multiple types.
diff --git a/lisp/bs.el b/lisp/bs.el
index d7eed8f661..59672c4fd4 100644
--- a/lisp/bs.el
+++ b/lisp/bs.el
@@ -157,15 +157,15 @@ HEADER         : String for header for first line or a 
function
                  which calculates column title.
 MINIMUM-LENGTH : Minimum width of column (number or name of function).
                  The function must return a positive integer.
-MAXIMUM-LENGTH : Maximum width of column (number or name of function)
-                 (currently ignored).
+MAXIMUM-LENGTH : Ignored.
 ALIGNMENT      : Alignment of column (`left', `right', `middle').
 FUN-OR-STRING  : Name of a function for calculating the value or a
                  string for a constant value.
 
-The function gets as parameter the buffer where we have started
-buffer selection and the list of all buffers to show.  The function must
-return a string representing the column's value."
+Functions for HEADER and MINIMUM-LENGTH are called with no arguments.
+FUN-OR-STRING gets as argument the buffer where we have started
+buffer selection and the list of all buffers to show.  The function
+must return a string representing the column's value."
   :group 'bs-appearance
   :type '(repeat sexp))
 
diff --git a/lisp/buff-menu.el b/lisp/buff-menu.el
index 588fe599a4..448a5395c0 100644
--- a/lisp/buff-menu.el
+++ b/lisp/buff-menu.el
@@ -107,6 +107,9 @@ 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-local Buffer-menu-buffer-list nil
+  "The current list of buffers or function to return buffers.")
+
 (defvar-keymap Buffer-menu-mode-map
   :doc "Local keymap for `Buffer-menu-mode' buffers."
   :parent tabulated-list-mode-map
@@ -628,8 +631,10 @@ This behaves like invoking \\[read-only-mode] in that 
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.
+If BUFFER-LIST is non-nil, it should be either a list of buffers
+or a function that returns a list of buffers; it means
+list those buffers and no others.
+See more at `Buffer-menu-buffer-list'.
 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'."
@@ -639,6 +644,7 @@ See more at `Buffer-menu-filter-predicate'."
       (Buffer-menu-mode)
       (setq Buffer-menu-files-only
            (and files-only (>= (prefix-numeric-value files-only) 0)))
+      (setq Buffer-menu-buffer-list buffer-list)
       (setq Buffer-menu-filter-predicate filter-predicate)
       (list-buffers--refresh buffer-list old-buffer)
       (tabulated-list-print))
@@ -665,9 +671,16 @@ See more at `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
-                       (buffer-list (if Buffer-menu-use-frame-buffer-list
-                                        (selected-frame)))))
+    (dolist (buffer (cond
+                     ((functionp buffer-list)
+                      (funcall buffer-list))
+                     (buffer-list)
+                     ((functionp Buffer-menu-buffer-list)
+                      (funcall Buffer-menu-buffer-list))
+                     (Buffer-menu-buffer-list)
+                     (t (buffer-list
+                         (if Buffer-menu-use-frame-buffer-list
+                             (selected-frame))))))
       (with-current-buffer buffer
        (let* ((name (buffer-name))
               (file buffer-file-name))
diff --git a/lisp/emacs-lisp/cl-extra.el b/lisp/emacs-lisp/cl-extra.el
index 66b214554e..60b01051a3 100644
--- a/lisp/emacs-lisp/cl-extra.el
+++ b/lisp/emacs-lisp/cl-extra.el
@@ -552,7 +552,7 @@ too large if positive or too small if negative)."
                        ,new)))))
   (seq-subseq seq start end))
 
-;;; This isn't a defalias because autoloading defalises doesn't work
+;;; This isn't a defalias because autoloading defaliases doesn't work
 ;;; very well.
 
 ;;;###autoload
diff --git a/lisp/isearch.el b/lisp/isearch.el
index bc3697deb0..6a17d18c45 100644
--- a/lisp/isearch.el
+++ b/lisp/isearch.el
@@ -4435,7 +4435,7 @@ CASE-FOLD: The value of `isearch-case-fold' to use for 
lazy
 highlighting.
 LAX-WHITESPACE: The value of `isearch-lax-whitespace' and
 `isearch-regexp-lax-whitespace' to use for lazy highlighting."
-  (if (not highlight)
+  (if (or (not highlight) (minibufferp))
       #'ignore
     (let ((unwind (make-symbol "minibuffer-lazy-highlight--unwind"))
           (after-change (make-symbol 
"minibuffer-lazy-highlight--after-change"))
diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index 5faa3c8d4e..6e42296e7b 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -1326,9 +1326,9 @@ pair of a group title string and a list of group 
candidate strings."
   :version "28.1")
 
 (defface completions-group-separator
-  '((t :inherit shadow :underline t))
+  '((t :inherit shadow :strike-through t))
   "Face used for the separator lines between the candidate groups."
-  :version "29.1")
+  :version "28.1")
 
 (defun completion--cycle-threshold (metadata)
   (let* ((cat (completion-metadata-get metadata 'category))
diff --git a/lisp/outline.el b/lisp/outline.el
index 2c3f9798ec..53bfc4d556 100644
--- a/lisp/outline.el
+++ b/lisp/outline.el
@@ -1817,7 +1817,7 @@ With a prefix argument, show headings up to that LEVEL."
         (unless o
           (when (eq outline-minor-mode-use-buttons 'insert)
             (let ((inhibit-read-only t))
-              (insert "  ")
+              (insert (apply #'propertize "  " (text-properties-at (point))))
               (beginning-of-line)))
           (setq o (make-overlay (point) (1+ (point))))
           (overlay-put o 'outline-button t)
diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el
index d21937f355..e6b6156ddd 100644
--- a/lisp/progmodes/c-ts-mode.el
+++ b/lisp/progmodes/c-ts-mode.el
@@ -120,6 +120,8 @@ MODE is either `c' or `cpp'."
            ((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)
+           ,@(when (eq mode 'cpp)
+               '(((node-is "access_specifier") parent-bol 0)))
            ((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)
@@ -251,7 +253,9 @@ MODE is either `c' or `cpp'."
    :language mode
    :feature 'string
    `((string_literal) @font-lock-string-face
-     (system_lib_string) @font-lock-string-face)
+     (system_lib_string) @font-lock-string-face
+     ,@(when (eq mode 'cpp)
+         '((raw_string_literal) @font-lock-string-face)))
 
    :language mode
    :feature 'literal
diff --git a/lisp/progmodes/cmake-ts-mode.el b/lisp/progmodes/cmake-ts-mode.el
new file mode 100644
index 0000000000..15934a290b
--- /dev/null
+++ b/lisp/progmodes/cmake-ts-mode.el
@@ -0,0 +1,234 @@
+;;; cmake-ts-mode.el --- tree-sitter support for CMake  -*- lexical-binding: 
t; -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+
+;; Author     : Randy Taylor <dev@rjt.dev>
+;; Maintainer : Randy Taylor <dev@rjt.dev>
+;; Created    : December 2022
+;; Keywords   : cmake languages tree-sitter
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+
+;;; Code:
+
+(require 'treesit)
+(eval-when-compile (require 'rx))
+
+(declare-function treesit-parser-create "treesit.c")
+(declare-function treesit-induce-sparse-tree "treesit.c")
+(declare-function treesit-node-child "treesit.c")
+(declare-function treesit-node-start "treesit.c")
+(declare-function treesit-node-type "treesit.c")
+
+(defcustom cmake-ts-mode-indent-offset 2
+  "Number of spaces for each indentation step in `cmake-ts-mode'."
+  :version "29.1"
+  :type 'integer
+  :safe 'integerp
+  :group 'cmake)
+
+(defvar cmake-ts-mode--syntax-table
+  (let ((table (make-syntax-table)))
+    (modify-syntax-entry ?#  "<" table)
+    (modify-syntax-entry ?\n ">" table)
+    (modify-syntax-entry ?$  "'" table)
+    table)
+  "Syntax table for `cmake-ts-mode'.")
+
+(defvar cmake-ts-mode--indent-rules
+  `((cmake
+     ((node-is ")") parent-bol 0)
+     ((node-is "else_command") parent-bol 0)
+     ((node-is "elseif_command") parent-bol 0)
+     ((node-is "endforeach_command") parent-bol 0)
+     ((node-is "endfunction_command") parent-bol 0)
+     ((node-is "endif_command") parent-bol 0)
+     ((parent-is "foreach_loop") parent-bol cmake-ts-mode-indent-offset)
+     ((parent-is "function_def") parent-bol cmake-ts-mode-indent-offset)
+     ((parent-is "if_condition") parent-bol cmake-ts-mode-indent-offset)
+     ((parent-is "normal_command") parent-bol cmake-ts-mode-indent-offset)))
+  "Tree-sitter indent rules for `cmake-ts-mode'.")
+
+(defvar cmake-ts-mode--constants
+  '("1" "ON" "TRUE" "YES" "Y" "0" "OFF" "FALSE" "NO" "N" "IGNORE"
+    "NOTFOUND")
+  "CMake constants for tree-sitter font-locking.")
+
+(defvar cmake-ts-mode--keywords
+  '((else) (elseif) (endforeach) (endfunction) (endif) (endmacro)
+    (endwhile) (foreach) (function) (if) (macro) (while))
+  "CMake keywords for tree-sitter font-locking.")
+
+(defvar cmake-ts-mode--foreach-options
+  '("IN" "ITEMS" "LISTS" "RANGE" "ZIP_LISTS")
+  "CMake foreach options for tree-sitter font-locking.")
+
+(defvar cmake-ts-mode--if-conditions
+  '("AND" "COMMAND" "DEFINED" "EQUAL" "EXISTS" "GREATER"
+    "GREATER_EQUAL" "LESS" "LESS_EQUAL" "MATCHES" "NOT" "OR"
+    "PATH_EQUAL" "STREQUAL" "STRGREATER" "STRGREATER_EQUAL" "STRLESS"
+    "STRLESS_EQUAL" "VERSION_EQUAL" "VERSION_GREATER"
+    "VERSION_GREATER_EQUAL" "VERSION_LESS" "VERSION_LESS_EQUAL")
+  "CMake if conditions for tree-sitter font-locking.")
+
+(defvar cmake-ts-mode--font-lock-settings
+  (treesit-font-lock-rules
+   :language 'cmake
+   :feature 'bracket
+   '((["(" ")"]) @font-lock-bracket-face)
+
+   :language 'cmake
+   :feature 'builtin
+   `(((foreach_command
+       ((argument) @font-lock-constant-face
+        (:match ,(rx-to-string
+                  `(seq bol
+                        (or ,@cmake-ts-mode--foreach-options)
+                        eol))
+                @font-lock-constant-face))))
+     ((if_command
+       ((argument) @font-lock-constant-face
+        (:match ,(rx-to-string
+                  `(seq bol
+                        (or ,@cmake-ts-mode--if-conditions)
+                        eol))
+                @font-lock-constant-face)))))
+
+   :language 'cmake
+   :feature 'comment
+   '([(bracket_comment) (line_comment)] @font-lock-comment-face)
+
+   :language 'cmake
+   :feature 'constant
+   `(((argument) @font-lock-constant-face
+      (:match ,(rx-to-string
+                `(seq bol
+                      (or ,@cmake-ts-mode--constants)
+                      eol))
+              @font-lock-constant-face)))
+
+   :language 'cmake
+   :feature 'function
+   '((normal_command (identifier) @font-lock-function-name-face))
+
+   :language 'cmake
+   :feature 'keyword
+   `([,@cmake-ts-mode--keywords] @font-lock-keyword-face)
+
+   :language 'cmake
+   :feature 'number
+   '(((unquoted_argument) @font-lock-number-face
+      (:match "^[[:digit:]]*\\.?[[:digit:]]*\\.?[[:digit:]]+$" 
@font-lock-number-face)))
+
+   :language 'cmake
+   :feature 'string
+   '([(bracket_argument) (quoted_argument)] @font-lock-string-face)
+
+   :language 'cmake
+   :feature 'escape-sequence
+   :override t
+   '((escape_sequence) @font-lock-escape-face)
+
+   :language 'cmake
+   :feature 'misc-punctuation
+   ;; Don't override strings.
+   :override 'nil
+   '((["$" "{" "}" "<" ">"]) @font-lock-misc-punctuation-face)
+
+   :language 'cmake
+   :feature 'variable
+   :override t
+   '((variable) @font-lock-variable-name-face)
+
+   :language 'cmake
+   :feature 'error
+   :override t
+   '((ERROR) @font-lock-warning-face))
+  "Tree-sitter font-lock settings for `cmake-ts-mode'.")
+
+(defun cmake-ts-mode--imenu ()
+  "Return Imenu alist for the current buffer."
+  (let* ((node (treesit-buffer-root-node))
+         (func-tree (treesit-induce-sparse-tree
+                     node "function_def" nil 1000))
+         (func-index (cmake-ts-mode--imenu-1 func-tree)))
+    (append
+     (when func-index `(("Function" . ,func-index))))))
+
+(defun cmake-ts-mode--imenu-1 (node)
+  "Helper for `cmake-ts-mode--imenu'.
+Find string representation for NODE and set marker, then recurse
+the subtrees."
+  (let* ((ts-node (car node))
+         (children (cdr node))
+         (subtrees (mapcan #'cmake-ts-mode--imenu-1
+                           children))
+         (name (when ts-node
+                 (pcase (treesit-node-type ts-node)
+                   ("function_def"
+                    (treesit-node-text
+                     (treesit-node-child (treesit-node-child ts-node 0) 2) 
t)))))
+         (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))))))
+
+;;;###autoload
+(add-to-list 'auto-mode-alist
+             '("\\(?:CMakeLists\\.txt\\|\\.cmake\\)$" . cmake-ts-mode))
+
+;;;###autoload
+(define-derived-mode cmake-ts-mode prog-mode "CMake"
+  "Major mode for editing CMake files, powered by tree-sitter."
+  :group 'cmake
+  :syntax-table cmake-ts-mode--syntax-table
+
+  (when (treesit-ready-p 'cmake)
+    (treesit-parser-create 'cmake)
+
+    ;; Comments.
+    (setq-local comment-start "# ")
+    (setq-local comment-end "")
+    (setq-local comment-start-skip (rx "#" (* (syntax whitespace))))
+
+    ;; Imenu.
+    (setq-local imenu-create-index-function #'cmake-ts-mode--imenu)
+    (setq-local which-func-functions nil)
+
+    ;; Indent.
+    (setq-local treesit-simple-indent-rules cmake-ts-mode--indent-rules)
+
+    ;; Font-lock.
+    (setq-local treesit-font-lock-settings cmake-ts-mode--font-lock-settings)
+    (setq-local treesit-font-lock-feature-list
+                '((comment)
+                  (keyword string)
+                  (builtin constant escape-sequence function number variable)
+                  (bracket error misc-punctuation)))
+
+    (treesit-major-mode-setup)))
+
+(provide 'cmake-ts-mode)
+
+;;; cmake-ts-mode.el ends here
diff --git a/lisp/progmodes/csharp-mode.el b/lisp/progmodes/csharp-mode.el
index 306a1e2bf8..1fd051db3c 100644
--- a/lisp/progmodes/csharp-mode.el
+++ b/lisp/progmodes/csharp-mode.el
@@ -709,8 +709,8 @@ compilation and evaluation time conflicts."
    :language 'c-sharp
    :override t
    :feature 'literal
-   `((integer_literal) @font-lock-constant-face
-     (real_literal) @font-lock-constant-face
+   `((integer_literal) @font-lock-number-face
+     (real_literal) @font-lock-number-face
      (null_literal) @font-lock-constant-face
      (boolean_literal) @font-lock-constant-face)
    :language 'c-sharp
@@ -784,6 +784,10 @@ compilation and evaluation time conflicts."
      (invocation_expression
       (member_access_expression
        (generic_name (identifier) @font-lock-function-name-face)))
+     (invocation_expression
+      (member_access_expression
+       expression: (identifier) @font-lock-variable-name-face
+       name: (generic_name (type_argument_list (identifier)))))
      (invocation_expression
       (member_access_expression
        ((identifier) @font-lock-variable-name-face
diff --git a/lisp/progmodes/dockerfile-ts-mode.el 
b/lisp/progmodes/dockerfile-ts-mode.el
index 544e0f82d6..40d90cc2df 100644
--- a/lisp/progmodes/dockerfile-ts-mode.el
+++ b/lisp/progmodes/dockerfile-ts-mode.el
@@ -33,6 +33,7 @@
 (declare-function treesit-parser-create "treesit.c")
 (declare-function treesit-induce-sparse-tree "treesit.c")
 (declare-function treesit-node-child "treesit.c")
+(declare-function treesit-node-child-by-field-name "treesit.c")
 (declare-function treesit-node-start "treesit.c")
 (declare-function treesit-node-type "treesit.c")
 
@@ -117,8 +118,10 @@ the subtrees."
                            children))
          (name (when ts-node
                  (pcase (treesit-node-type ts-node)
-                   ("from_instruction" (treesit-node-text
-                                        (treesit-node-child ts-node 1) t)))))
+                   ("from_instruction"
+                    (treesit-node-text
+                     (or (treesit-node-child-by-field-name ts-node "as")
+                         (treesit-node-child ts-node 1)) t)))))
          (marker (when ts-node
                    (set-marker (make-marker)
                                (treesit-node-start ts-node)))))
diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index 2427e7b9d3..9c5a361df7 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -183,7 +183,7 @@ chosen (interactively or automatically)."
                       finally (funcall err)))))))
 
 (defvar eglot-server-programs `((rust-mode . ,(eglot-alternatives 
'("rust-analyzer" "rls")))
-                                (cmake-mode . ("cmake-language-server"))
+                                ((cmake-mode cmake-ts-mode) . 
("cmake-language-server"))
                                 (vimrc-mode . ("vim-language-server" 
"--stdio"))
                                 ((python-mode python-ts-mode)
                                  . ,(eglot-alternatives
diff --git a/lisp/progmodes/grep.el b/lisp/progmodes/grep.el
index 2446e86abb..35cac43bee 100644
--- a/lisp/progmodes/grep.el
+++ b/lisp/progmodes/grep.el
@@ -1254,6 +1254,10 @@ or not."
                                          nil default-directory t))
                (confirm (equal current-prefix-arg '(4))))
           (list regexp files dir confirm))))))
+  ;; If called non-interactively, also compute the defaults if we
+  ;; haven't already.
+  (unless grep-find-template
+    (grep-compute-defaults))
   (when (and (stringp regexp) (> (length regexp) 0))
     (unless (and dir (file-accessible-directory-p dir))
       (setq dir default-directory))
diff --git a/lisp/progmodes/java-ts-mode.el b/lisp/progmodes/java-ts-mode.el
index d5f4f55fe0..39d02bbfe4 100644
--- a/lisp/progmodes/java-ts-mode.el
+++ b/lisp/progmodes/java-ts-mode.el
@@ -73,6 +73,7 @@
      ((node-is "]") parent-bol 0)
      ((and (parent-is "comment") comment-end) comment-start -1)
      ((parent-is "comment") comment-start-skip 0)
+     ((parent-is "text_block") no-indent)
      ((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)
@@ -162,7 +163,8 @@
    :language 'java
    :override t
    :feature 'string
-   `((string_literal) @font-lock-string-face)
+   `((string_literal) @font-lock-string-face
+     (text_block) @font-lock-string-face)
    :language 'java
    :override t
    :feature 'literal
diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el
index da47f682d7..88d0c373bb 100644
--- a/lisp/progmodes/js.el
+++ b/lisp/progmodes/js.el
@@ -3876,6 +3876,7 @@ Currently there are `js-mode' and `js-ts-mode'."
     ;; Indent.
     (setq-local treesit-simple-indent-rules js--treesit-indent-rules)
     ;; Navigation.
+    (setq-local treesit-defun-prefer-top-level t)
     (setq-local treesit-defun-type-regexp
                 (rx (or "class_declaration"
                         "method_definition"
diff --git a/lisp/progmodes/json-ts-mode.el b/lisp/progmodes/json-ts-mode.el
index a118908a00..6fb982e37b 100644
--- a/lisp/progmodes/json-ts-mode.el
+++ b/lisp/progmodes/json-ts-mode.el
@@ -74,6 +74,9 @@
 
 (defvar json-ts-mode--font-lock-settings
   (treesit-font-lock-rules
+   :language 'json
+   :feature 'comment
+   '((comment) @font-lock-comment-face)
    :language 'json
    :feature 'bracket
    '((["[" "]" "{" "}"]) @font-lock-bracket-face)
@@ -161,7 +164,7 @@ the subtrees."
   ;; Font-lock.
   (setq-local treesit-font-lock-settings json-ts-mode--font-lock-settings)
   (setq-local treesit-font-lock-feature-list
-              '((constant number pair string)
+              '((comment constant number pair string)
                 (escape-sequence)
                 (bracket delimiter error)))
 
diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el
index 7cdaba9c07..4fd855255b 100644
--- a/lisp/progmodes/project.el
+++ b/lisp/progmodes/project.el
@@ -1340,18 +1340,33 @@ 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)))
+  (let* ((pr (project-current t))
+         (buffer-list-function
+          (lambda ()
+            (seq-filter
+             (lambda (buffer)
+               (let ((name (buffer-name buffer))
+                     (file (buffer-file-name buffer)))
+                 (and (or (not (string= (substring name 0 1) " "))
+                          file)
+                      (not (eq buffer (current-buffer)))
+                      (or file (not Buffer-menu-files-only)))))
+             (project-buffers pr)))))
     (display-buffer
      (if (version< emacs-version "29.0.50")
-         (let ((buf (list-buffers-noselect arg (project-buffers pr))))
+         (let ((buf (list-buffers-noselect
+                     arg (with-current-buffer
+                             (get-buffer-create "*Buffer List*")
+                           (let ((Buffer-menu-files-only arg))
+                             (funcall buffer-list-function))))))
            (with-current-buffer buf
              (setq-local revert-buffer-function
                          (lambda (&rest _ignored)
-                           (list-buffers--refresh (project-buffers pr))
+                           (list-buffers--refresh
+                            (funcall buffer-list-function))
                            (tabulated-list-print t))))
            buf)
-       (list-buffers-noselect
-        arg nil (lambda (buf) (memq buf (project-buffers pr))))))))
+       (list-buffers-noselect arg buffer-list-function)))))
 
 (defcustom project-kill-buffer-conditions
   '(buffer-file-name    ; All file-visiting buffers are included.
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index ebee703499..0e0898ffe2 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -993,7 +993,8 @@ It makes underscores and dots word constituent chars.")
 
 (defvar python--treesit-operators
   '("-" "-=" "!=" "*" "**" "**=" "*=" "/" "//" "//=" "/=" "&" "%" "%="
-    "^" "+" "+=" "<" "<<" "<=" "<>" "=" "==" ">" ">=" ">>" "|" "~" "@" "@="))
+    "^" "+" "->" "+=" "<" "<<" "<=" "<>" "=" ":=" "==" ">" ">=" ">>" "|"
+    "~" "@" "@="))
 
 (defvar python--treesit-special-attributes
   '("__annotations__" "__closure__" "__code__"
diff --git a/lisp/progmodes/typescript-ts-mode.el 
b/lisp/progmodes/typescript-ts-mode.el
index aaf551850d..6a5a6e0de2 100644
--- a/lisp/progmodes/typescript-ts-mode.el
+++ b/lisp/progmodes/typescript-ts-mode.el
@@ -332,6 +332,7 @@ Argument LANGUAGE is either `typescript' or `tsx'."
   (setq-local treesit-text-type-regexp
               (regexp-opt '("comment"
                             "template_string")))
+  (setq-local treesit-defun-prefer-top-level t)
 
   ;; Electric
   (setq-local electric-indent-chars
diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el
index 1e4aa4eba5..0790d3a565 100644
--- a/lisp/progmodes/xref.el
+++ b/lisp/progmodes/xref.el
@@ -994,7 +994,7 @@ point."
   (setq-local add-log-current-defun-function
              #'xref--add-log-current-defun)
   (setq-local outline-minor-mode-cycle t
-              outline-minor-mode-use-buttons t
+              outline-minor-mode-use-buttons 'insert
               outline-search-function
               (lambda (&optional bound move backward looking-at)
                 (outline-search-text-property
diff --git a/lisp/replace.el b/lisp/replace.el
index 6f59166e35..302cb65543 100644
--- a/lisp/replace.el
+++ b/lisp/replace.el
@@ -1039,7 +1039,10 @@ They are deleted _before_ looking for the next match.  
Hence, a match
 starting on the same line at which another match ended is ignored.
 
 Return the number of deleted matching lines.  When called interactively,
-also print the number."
+also print the number.
+
+If you want to not just delete the lines, but also add them to
+the kill ring, use the \\[kill-matching-lines] command instead."
   (interactive
    (progn
      (barf-if-buffer-read-only)
@@ -1104,7 +1107,7 @@ Return the number of killed matching lines.  When called
 interactively, also print the number.
 
 If you merely want to delete the lines, without adding them to
-the kill ring, the \\[flush-lines] command is faster."
+the kill ring, the \\[delete-matching-lines] command is faster."
   (interactive
    (progn
      (barf-if-buffer-read-only)
diff --git a/lisp/textmodes/texinfo.el b/lisp/textmodes/texinfo.el
index 98672f42b3..24e93bf001 100644
--- a/lisp/textmodes/texinfo.el
+++ b/lisp/textmodes/texinfo.el
@@ -347,6 +347,8 @@ Subexpression 1 is what goes into the corresponding `@end' 
statement.")
 (defun texinfo-flymake (report-fn &rest _)
   "Texinfo checking for Flymake.
 
+It uses either \"makeinfo\" or \"texi2any\", in that order.
+
 REPORT-FN is the callback function."
   (let ((executable (or (executable-find "makeinfo")
                         (executable-find "texi2any")))
diff --git a/lisp/treesit.el b/lisp/treesit.el
index 9bb261b66d..913a1d8c5b 100644
--- a/lisp/treesit.el
+++ b/lisp/treesit.el
@@ -1569,8 +1569,25 @@ BACKWARD and ALL are the same as in 
`treesit-search-forward'."
   "A regexp that matches the node type of defun nodes.
 For example, \"(function|class)_definition\".
 
+Sometimes not all nodes matched by the regexp are valid defuns.
+In that case, set this variable to a cons cell of the
+form (REGEXP . FILTER), where FILTER is a function that takes a
+node (the matched node) and returns t if node is valid, or nil
+for invalid node.
+
 This is used by `treesit-beginning-of-defun' and friends.")
 
+(defvar-local treesit-defun-tactic 'nested
+  "Determines how does Emacs treat nested defuns.
+If the value is `top-level', Emacs only moves across top-level
+defuns, if the value is `nested', Emacs recognizes nested defuns.")
+
+(defvar-local treesit-defun-skipper #'treesit-default-defun-skipper
+  "A function called after tree-sitter navigation moved a step.
+It is called with no arguments.  By default, this function tries
+to move to the beginning of a line, either by moving to the empty
+newline after a defun, or the beginning of a defun.")
+
 (defvar-local treesit-defun-prefer-top-level nil
   "When non-nil, Emacs prefers top-level defun.
 
@@ -1648,6 +1665,196 @@ comments and multiline string literals.  For example,
 \"text_block\" in the case of a string.  This is used by
 `prog-fill-reindent-defun' and friends.")
 
+(defun treesit-default-defun-skipper ()
+  "Skips spaces after navigating a defun.
+This function tries to move to the beginning of a line, either by
+moving to the empty newline after a defun, or to the beginning of
+the current line if the beginning of the defun is indented."
+  (cond ((and (looking-at (rx (* (or " " "\\t")) "\n"))
+              (not (looking-at (rx bol))))
+         (goto-char (match-end 0)))
+        ((save-excursion
+           (skip-chars-backward " \t")
+           (eq (point) (line-beginning-position)))
+         (goto-char (line-beginning-position)))))
+
+;; prev-sibling:
+;; 1. end-of-node before pos
+;; 2. highest such node
+;;
+;; next-sibling:
+;; 1. beg-of-node after pos
+;; 2. highest such node
+;;
+;; parent:
+;; 1. node covers pos
+;; 2. smallest such node
+(defun treesit--defuns-around (pos regexp &optional pred)
+  "Return the previous, next, and parent defun around POS.
+
+Return a list of (PREV NEXT PARENT), where PREV and NEXT are
+previous and next sibling defuns around POS, and PARENT is the
+parent defun surrounding POS.  All of three could be nil if no
+sound defun exists.
+
+REGEXP and PRED are the same as in `treesit-defun-type-regexp'."
+  (let* ((node (treesit-node-at pos))
+         ;; NODE-BEFORE/AFTER = NODE when POS is completely in NODE,
+         ;; but if not, that means point could be in between two
+         ;; defun, in that case we want to use a node that's actually
+         ;; before/after point.
+         (node-before (if (>= (treesit-node-start node) pos)
+                          (treesit-search-forward-goto node "" t t t)
+                        node))
+         (node-after (if (<= (treesit-node-end node) pos)
+                         (treesit-search-forward-goto node "" nil nil t)
+                       node))
+         (result (list nil nil nil))
+         (pred (or pred (lambda (_) t))))
+    ;; 1. Find previous and next sibling defuns.
+    (cl-loop
+     for idx from 0 to 1
+     for node in (list node-before node-after)
+     for backward in '(t nil)
+     for pos-pred in (list (lambda (n) (<= (treesit-node-end n) pos))
+                           (lambda (n) (>= (treesit-node-start n) pos)))
+     ;; If point is inside a defun, our process below will never
+     ;; return a next/prev sibling outside of that defun, effectively
+     ;; any prev/next sibling is locked inside the smallest defun
+     ;; covering point, which is the correct behavior.  That's because
+     ;; when there exists a defun that covers point,
+     ;; `treesit-search-forward' will first reach that defun, after
+     ;; that we only go upwards in the tree, so other defuns outside
+     ;; of the covering defun is never reached.  (Don't use
+     ;; `treesit-search-forward-goto' as it breaks when NODE-AFTER is
+     ;; the last token of a parent defun: it will skip the parent
+     ;; defun because it wants to ensure progress.)
+     do (cl-loop for cursor = (when node
+                                (save-excursion
+                                  (treesit-search-forward
+                                   node regexp backward backward)))
+                 then (treesit-node-parent cursor)
+                 while cursor
+                 if (and (string-match-p
+                          regexp (treesit-node-type cursor))
+                         (funcall pred cursor)
+                         (funcall pos-pred cursor))
+                 do (setf (nth idx result) cursor)))
+    ;; 2. Find the parent defun.
+    (setf (nth 2 result)
+          (cl-loop for cursor = (or (nth 0 result)
+                                    (nth 1 result)
+                                    node)
+                   then (treesit-node-parent cursor)
+                   while cursor
+                   if (and (string-match-p
+                            regexp (treesit-node-type cursor))
+                           (funcall pred cursor)
+                           (not (member cursor result)))
+                   return cursor))
+    result))
+
+(defun treesit--top-level-defun (node regexp &optional pred)
+  "Return the top-level parent defun of NODE.
+REGEXP and PRED are the same as in `treesit-defun-type-regexp'."
+  (let* ((pred (or pred (lambda (_) t))))
+    ;; `treesit-search-forward-goto' will make sure the matched node
+    ;; is before POS.
+    (cl-loop for cursor = node
+             then (treesit-node-parent cursor)
+             while cursor
+             if (and (string-match-p
+                      regexp (treesit-node-type cursor))
+                     (funcall pred cursor))
+             do (setq node cursor))
+    node))
+
+(defun treesit--navigate-defun (pos arg side &optional recursing)
+  "Navigate defun ARG steps from POS.
+
+If ARG is positive, move forward that many steps, if negative,
+move backward.  If SIDE is `beg', stop at the beginning of a
+defun, if SIDE is `end', stop at the end.
+
+This function doesn't actually move point, it just returns the
+position it would move to.  If there aren't enough defuns to move
+across, return nil.
+
+RECURSING is an internal parameter, if non-nil, it means this
+function is called recursively."
+  (pcase-let*
+      ((counter (abs arg))
+       (`(,regexp . ,pred)
+        (if (consp treesit-defun-type-regexp)
+            treesit-defun-type-regexp
+          (cons treesit-defun-type-regexp nil)))
+       ;; Move POS to the beg/end of NODE.  If NODE is nil, terminate.
+       ;; Return the position we moved to.
+       (advance (lambda (node)
+                  (let ((dest (pcase side
+                                ('beg (treesit-node-start node))
+                                ('end (treesit-node-end node)))))
+                    (if (null dest)
+                        (throw 'term nil)
+                      dest)))))
+    (catch 'term
+      (while (> counter 0)
+        (pcase-let
+            ((`(,prev ,next ,parent)
+              (treesit--defuns-around pos regexp pred)))
+          ;; When PARENT is nil, nested and top-level are the same,
+          ;; there there is a PARENT, make PARENT to be the top-level
+          ;; parent and pretend there is no nested PREV and NEXT.
+          (when (and (eq treesit-defun-tactic 'top-level)
+                     parent)
+            (setq parent (treesit--top-level-defun
+                          parent regexp pred)
+                  prev nil
+                  next nil))
+          ;; Move...
+          (if (> arg 0)
+              ;; ...forward.
+              (if (and (eq side 'beg)
+                       ;; Should we skip the defun (recurse)?
+                       (cond (next (not recursing)) ; [1] (see below)
+                             (parent t) ; [2]
+                             (t nil)))
+                  ;; Special case: go to next beg-of-defun.  Set POS
+                  ;; to the end of next/parent defun, and run one more
+                  ;; step.  If there is a next defun, step over it, so
+                  ;; we only need to recurse once, so we don't need to
+                  ;; recurse if we are already recursing [1]. If there
+                  ;; is no next but a parent, keep stepping out
+                  ;; (recursing) until we got out of the parents until
+                  ;; (1) there is a next sibling defun, or (2) no more
+                  ;; parents [2].
+                  (setq pos
+                        (or (treesit--navigate-defun
+                             (treesit-node-end (or next parent))
+                             1 'beg t)
+                            (throw 'term nil)))
+                ;; Normal case.
+                (setq pos (funcall advance (or next parent))))
+            ;; ...backward.
+            (if (and (eq side 'end)
+                     (cond (prev (not recursing))
+                           (parent t)
+                           (t nil)))
+                ;; Special case: go to prev end-of-defun.
+                (setq pos
+                      (or (treesit--navigate-defun
+                           (treesit-node-start (or prev parent))
+                           -1 'end t)
+                          (throw 'term nil)))
+              ;; Normal case.
+              (setq pos (funcall advance (or prev parent)))))
+          ;; A successful step! Decrement counter.
+          (cl-decf counter))))
+    ;; Counter equal to 0 means we successfully stepped ARG steps.
+    (if (eq counter 0)
+        pos
+      nil)))
+
 ;;; Activating tree-sitter
 
 (defun treesit-ready-p (language &optional quiet)
@@ -1844,7 +2051,8 @@ to the offending pattern and highlight the pattern."
              (goto-char (point-min))
              (insert (format "%s: %d\n" message start))
              (forward-char start)))
-         (pop-to-buffer buf))))))
+         (pop-to-buffer buf)))))
+  (view-mode))
 
 ;;; Explorer
 
diff --git a/src/timefns.c b/src/timefns.c
index 9beec1ce38..dcc6403fd9 100644
--- a/src/timefns.c
+++ b/src/timefns.c
@@ -40,6 +40,10 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include <stdio.h>
 #include <stdlib.h>
 
+#ifdef WINDOWSNT
+extern clock_t sys_clock (void);
+#endif
+
 #ifdef HAVE_TIMEZONE_T
 # include <sys/param.h>
 # if defined __NetBSD_Version__ && __NetBSD_Version__ < 700000000
diff --git a/src/xfaces.c b/src/xfaces.c
index 7dbcacb35a..be4a7ca71c 100644
--- a/src/xfaces.c
+++ b/src/xfaces.c
@@ -6023,6 +6023,8 @@ font_maybe_unset_attribute (Lisp_Object font_object,
 {
   Lisp_Object tail = Vface_font_lax_matched_attributes;
 
+  eassert (CONSP (tail));
+
   FOR_EACH_TAIL_SAFE (tail)
     {
       if (EQ (XCAR (tail), symbol))
@@ -6100,20 +6102,31 @@ realize_gui_face (struct face_cache *cache, Lisp_Object 
attrs[LFACE_VECTOR_SIZE]
             fonts that are exact matches for these weight, slant, and
             width attributes, which could lead to suboptimal or wrong
             font selection.  (bug#5934) */
-         font_maybe_unset_attribute (spec, FONT_WEIGHT_INDEX, QCweight);
-         font_maybe_unset_attribute (spec, FONT_SLANT_INDEX, QCslant);
-         font_maybe_unset_attribute (spec, FONT_WIDTH_INDEX, QCwidth);
-         /* Also allow unsetting other attributes for debugging
-            purposes.  But not FONT_EXTRA_INDEX; that is not safe to
-            touch, at least in the Haiku font backend.  */
-         font_maybe_unset_attribute (spec, FONT_FAMILY_INDEX, QCfamily);
-         font_maybe_unset_attribute (spec, FONT_FOUNDRY_INDEX, QCfoundry);
-         font_maybe_unset_attribute (spec, FONT_REGISTRY_INDEX, QCregistry);
-         font_maybe_unset_attribute (spec, FONT_ADSTYLE_INDEX, QCadstyle);
-         font_maybe_unset_attribute (spec, FONT_SIZE_INDEX, QCsize);
-         font_maybe_unset_attribute (spec, FONT_DPI_INDEX, QCdpi);
-         font_maybe_unset_attribute (spec, FONT_SPACING_INDEX, QCspacing);
-         font_maybe_unset_attribute (spec, FONT_AVGWIDTH_INDEX, QCavgwidth);
+         if (EQ (Vface_font_lax_matched_attributes, Qt))
+           {
+             /* The default case: clear the font attributes that
+                affect its appearance the least, to try to find some
+                font that is close, if not exact, match.  */
+             ASET (spec, FONT_WEIGHT_INDEX, Qnil);
+             ASET (spec, FONT_SLANT_INDEX, Qnil);
+             ASET (spec, FONT_WIDTH_INDEX, Qnil);
+           }
+         else if (!NILP (Vface_font_lax_matched_attributes))
+           {
+             /* Also allow unsetting specific attributes for
+                debugging purposes.  */
+             font_maybe_unset_attribute (spec, FONT_WEIGHT_INDEX, QCweight);
+             font_maybe_unset_attribute (spec, FONT_SLANT_INDEX, QCslant);
+             font_maybe_unset_attribute (spec, FONT_WIDTH_INDEX, QCwidth);
+             font_maybe_unset_attribute (spec, FONT_FAMILY_INDEX, QCfamily);
+             font_maybe_unset_attribute (spec, FONT_FOUNDRY_INDEX, QCfoundry);
+             font_maybe_unset_attribute (spec, FONT_REGISTRY_INDEX, 
QCregistry);
+             font_maybe_unset_attribute (spec, FONT_ADSTYLE_INDEX, QCadstyle);
+             font_maybe_unset_attribute (spec, FONT_SIZE_INDEX, QCsize);
+             font_maybe_unset_attribute (spec, FONT_DPI_INDEX, QCdpi);
+             font_maybe_unset_attribute (spec, FONT_SPACING_INDEX, QCspacing);
+             font_maybe_unset_attribute (spec, FONT_AVGWIDTH_INDEX, 
QCavgwidth);
+           }
 
          attrs[LFACE_FONT_INDEX] = font_load_for_lface (f, attrs, spec);
        }
@@ -7406,22 +7419,32 @@ clear the face cache, see `clear-face-cache'.  */);
 
   DEFVAR_LISP ("face-font-lax-matched-attributes",
               Vface_font_lax_matched_attributes,
-              doc: /* Font-related face attributes to match in lax manner when 
realizing faces.
-
-The value should be a list of font-related face attribute symbols;
-see `set-face-attribute' for the full list of attributes.  The
+              doc: /* Whether to match some face attributes in lax manner when 
realizing faces.
+
+If non-nil, some font-related face attributes will be matched in a lax
+manner when looking for candidate fonts.
+If the value is t, the default, the search for fonts will not insist
+on exact match for 3 font attributes: weight, width, and slant.
+Instead, it will examine the available fonts with various values of
+these attributes, and select the font that is the closest possible
+match.  (If an exact match is available, it will still be selected,
+as that is the closest match.)  For example, looking for a semi-bold
+font might select a bold or a medium-weight font if no semi-bold font
+matching other attributes can be found.  This is especially important
+when the `default' face specifies unusual values for one or more of
+these 3 attributes, which other installed fonts don't support.
+
+The value can also be a list of font-related face attribute symbols;
+see `set-face-attribute' for the full list of attributes.  Then the
 corresponding face attributes will be treated as "soft" constraints
-when looking for suitable fonts: if an exact match is not possible,
-a font can be selected that is a close, but not an exact, match.  For
-example, looking for a semi-bold font might select a bold or a medium
-font if no semi-bold font matching other attributes is found.  Emacs
-still tries to find a font that is the closest possible match; in
-particular, if a font is available that matches the face attributes
-exactly, it will be selected.
-
-Note that if the `:extra' attribute is present in the value, it
-will be ignored.  */);
-  Vface_font_lax_matched_attributes = list3 (QCweight, QCslant, QCwidth);
+in the manner described above, instead of the default 3 attributes.
+
+If the value is nil, candidate fonts might be rejected if the don't
+have exactly the same values of attributes as the face requests.
+
+This variable exists for debugging of the font-selection process,
+and we advise not to change it otherwise.  */);
+  Vface_font_lax_matched_attributes = Qt;
 
 #ifdef HAVE_WINDOW_SYSTEM
   defsubr (&Sbitmap_spec_p);
diff --git a/test/lisp/progmodes/eglot-tests.el 
b/test/lisp/progmodes/eglot-tests.el
index 2b4de8c27d..f6b53fd0c6 100644
--- a/test/lisp/progmodes/eglot-tests.el
+++ b/test/lisp/progmodes/eglot-tests.el
@@ -31,18 +31,29 @@
 ;; Some of these tests rely on the GNU ELPA package company.el and
 ;; yasnippet.el being available.
 
+;; Some of the tests require access to a remote host files.  Since
+;; this could be problematic, a mock-up connection method "mock" is
+;; used.  Emulating a remote connection, it simply calls "sh -i".
+;; Tramp's file name handlers still run, so this test is sufficient
+;; except for connection establishing.
+
+;; If you want to test a real Tramp connection, set
+;; $REMOTE_TEMPORARY_FILE_DIRECTORY to a suitable value in order to
+;; overwrite the default value.  If you want to skip tests accessing a
+;; remote host, set this environment variable to "/dev/null" or
+;; whatever is appropriate on your system.
+
 ;;; Code:
 (require 'eglot)
 (require 'cl-lib)
 (require 'ert)
+(require 'tramp) ; must be prior ert-x
 (require 'ert-x) ; ert-simulate-command
 (require 'edebug)
 (require 'python) ; some tests use pylsp
 (require 'cc-mode) ; c-mode-hook
 (require 'company nil t)
 (require 'yasnippet nil t)
-(require 'tramp)
-(require 'tramp-sh)
 (require 'subr-x)
 (require 'flymake) ; project-diagnostics
 
@@ -159,7 +170,11 @@ then restored."
              do (set sym val))
     (dolist (buf buffers-to-delete) ;; have to save otherwise will get prompted
       (with-current-buffer buf (save-buffer) (kill-buffer)))
-    (delete-directory fixture-directory 'recursive)))
+    (delete-directory fixture-directory 'recursive)
+    ;; Delete Tramp buffers if needed.
+    (when (file-remote-p temporary-file-directory)
+      (tramp-cleanup-connection
+       (tramp-dissect-file-name temporary-file-directory) nil 
'keep-password))))
 
 (cl-defmacro eglot--with-timeout (timeout &body body)
   (declare (indent 1) (debug t))
@@ -329,7 +344,7 @@ Pass TIMEOUT to `eglot--with-timeout'."
        (should-error (eglot--current-server-or-lose))))))
 
 (ert-deftest auto-detect-running-server ()
-  "Visit a file and \\[eglot], then visit a neighbour."
+  "Visit a file and \\[eglot], then visit a neighbor."
   (skip-unless (executable-find "clangd"))
   (eglot-tests--auto-detect-running-server-1))
 
@@ -431,6 +446,7 @@ Pass TIMEOUT to `eglot--with-timeout'."
 (ert-deftest diagnostic-tags-unnecessary-code ()
   "Test rendering of diagnostics tagged \"unnecessary\"."
   (skip-unless (executable-find "rust-analyzer"))
+  (skip-unless (executable-find "cargo"))
   (eglot--with-fixture
       '(("diagnostic-tag-project" .
          (("main.rs" .
@@ -672,7 +688,9 @@ pylsp prefers autopep over yafp, despite its README stating 
the contrary."
       (eglot-format (line-beginning-position) (line-end-position))
       (should (looking-at "ss"))
       (should
-       (string= (buffer-string) "def a():pass\n\n\ndef b(): pass\n"))
+       (or (string= (buffer-string) "def a():pass\n\n\ndef b(): pass\n")
+           ;; autopep8 2.0.0 (pycodestyle: 2.9.1)
+           (string= (buffer-string) "def a():pass\n\ndef b(): pass")))
       ;; now format the whole buffer
       (eglot-format-buffer)
       (should
@@ -701,7 +719,7 @@ pylsp prefers autopep over yafp, despite its README stating 
the contrary."
        (string= (buffer-string) "def a():\n    pass\n\n\ndef b():\n    
pass\n")))))
 
 (ert-deftest rust-on-type-formatting ()
-  "Test textDocument/onTypeFormatting agains rust-analyzer."
+  "Test textDocument/onTypeFormatting against rust-analyzer."
   (skip-unless (executable-find "rust-analyzer"))
   (skip-unless (executable-find "cargo"))
   (eglot--with-fixture
@@ -722,7 +740,8 @@ pylsp prefers autopep over yafp, despite its README stating 
the contrary."
 
 (ert-deftest javascript-basic ()
   "Test basic autocompletion in a JavaScript LSP."
-  (skip-unless (executable-find "typescript-language-server"))
+  (skip-unless (and (executable-find "typescript-language-server")
+                    (executable-find "tsserver")))
   (eglot--with-fixture
       '(("project" . (("hello.js" . "console.log('Hello world!');"))))
     (with-current-buffer
@@ -751,7 +770,8 @@ pylsp prefers autopep over yafp, despite its README stating 
the contrary."
 
 (ert-deftest project-wide-diagnostics-typescript ()
   "Test diagnostics through multiple files in a TypeScript LSP."
-  (skip-unless (executable-find "typescript-language-server"))
+  (skip-unless (and (executable-find "typescript-language-server")
+                    (executable-find "tsserver")))
   (eglot--with-fixture
       '(("project" . (("hello.ts" . "const thing = 5;\nexport { thin }")
                       ("hello2.ts" . "import { thing } from './hello'"))))
@@ -780,6 +800,7 @@ pylsp prefers autopep over yafp, despite its README stating 
the contrary."
 (ert-deftest project-wide-diagnostics-rust-analyzer ()
   "Test diagnostics through multiple files in a TypeScript LSP."
   (skip-unless (executable-find "rust-analyzer"))
+  (skip-unless (executable-find "cargo"))
   (eglot--with-fixture
       '(("project" .
          (("main.rs" .
@@ -849,7 +870,7 @@ pylsp prefers autopep over yafp, despite its README stating 
the contrary."
           (funcall eglot-move-to-column-function 71)
           (should (looking-at "p")))))))
 
-(ert-deftest eglot-lsp-abiding-column ()
+(ert-deftest eglot-tests-lsp-abiding-column ()
   "Test basic `eglot-lsp-abiding-column' and 
`eglot-move-to-lsp-abiding-column'."
   (skip-unless (executable-find "clangd"))
   (eglot-tests--lsp-abiding-column-1))
@@ -1235,37 +1256,25 @@ macro will assume it exists."
   ;; (should (eglot--glob-match "prefix/{**/*.d.ts,**/*.js,foo.[0-9]}" 
"prefix/foo.8"))
   )
 
+(defvar tramp-histfile-override)
 (defun eglot--call-with-tramp-test (fn)
-  ;; Set up a loopback TRAMP method that’s just a shell so the remote
-  ;; host is really just the local host.
+  ;; Set up a Tramp method that’s just a shell so the remote host is
+  ;; really just the local host.
   (let ((tramp-remote-path (cons 'tramp-own-remote-path tramp-remote-path))
         (tramp-histfile-override t)
-        (tramp-methods '(("loopback"
-                          (tramp-login-program "/bin/sh")
-                          (tramp-remote-shell "/bin/sh")
-                          (tramp-remote-shell-login ("-l"))
-                          (tramp-remote-shell-args ("-c")))))
-        (temporary-file-directory (concat "/loopback::"
-                                          temporary-file-directory)))
-    ;; With ‘temporary-file-directory’ bound to the ‘loopback’ TRAMP
-    ;; method, fixtures will be automatically made “remote".
-    (unwind-protect
-        (funcall fn)
-      ;; Tramp leave some buffers behind, and some time later,
-      ;; `project-buffers' will trip over them causing a hard to debug
-      ;; intermittent test failure somewhere else.
-      (dolist (buf (buffer-list))
-        (when (string-match-p "^\\*tramp" (buffer-name buf))
-          (kill-buffer buf))))))
+        (temporary-file-directory ert-remote-temporary-file-directory))
+    ;; We must check the remote LSP server.  So far, just "clangd" is used.
+    (let ((default-directory temporary-file-directory))
+      (unless (executable-find "clangd" 'remote)
+        (ert-skip "Remote clangd not found")))
+    (funcall fn)))
 
 (ert-deftest eglot--tramp-test ()
   "Ensure LSP servers can be used over TRAMP."
-  (skip-unless (executable-find "clangd"))
   (eglot--call-with-tramp-test #'eglot-tests--auto-detect-running-server-1))
 
 (ert-deftest eglot--tramp-test-2 ()
   "Ensure LSP servers can be used over TRAMP."
-  (skip-unless (executable-find "clangd"))
   (eglot--call-with-tramp-test #'eglot-tests--lsp-abiding-column-1))
 
 (ert-deftest eglot--path-to-uri-windows ()
diff --git a/test/src/treesit-tests.el b/test/src/treesit-tests.el
index 1cc2217bd3..3617264502 100644
--- a/test/src/treesit-tests.el
+++ b/test/src/treesit-tests.el
@@ -54,6 +54,7 @@
 (declare-function treesit-node-descendant-for-range "treesit.c")
 (declare-function treesit-node-eq "treesit.c")
 
+;;; Basic API
 
 (ert-deftest treesit-basic-parsing ()
   "Test basic parsing routines."
@@ -161,6 +162,8 @@
       (should (treesit-node-eq root-node root-node))
       (should (not (treesit-node-eq root-node doc-node))))))
 
+;;; Indirect buffer
+
 (ert-deftest treesit-indirect-buffer ()
   "Tests for indirect buffers."
   (skip-unless (treesit-language-available-p 'json))
@@ -195,6 +198,8 @@
       (kill-buffer base)
       (kill-buffer indirect))))
 
+;;; Query
+
 (ert-deftest treesit-query-api ()
   "Tests for query API."
   (skip-unless (treesit-language-available-p 'json))
@@ -249,6 +254,8 @@
          '((type field: (_) @capture :anchor)
            :? :* :+ "return")))))))
 
+;;; Narrow
+
 (ert-deftest treesit-narrow ()
   "Tests if narrowing works."
   (skip-unless (treesit-language-available-p 'json))
@@ -385,6 +392,8 @@ visible_end.)"
       ;; that calls that.
       )))
 
+;;; Range
+
 (ert-deftest treesit-range ()
   "Tests if range works."
   (skip-unless (treesit-language-available-p 'json))
@@ -438,6 +447,8 @@ visible_end.)"
       ;; TODO: More tests.
       )))
 
+;;; Multiple language
+
 (ert-deftest treesit-multi-lang ()
   "Tests if parsing multiple language works."
   (skip-unless (and (treesit-language-available-p 'html)
@@ -474,6 +485,8 @@ visible_end.)"
       ;; TODO: More tests.
       )))
 
+;;; Supplemental functions
+
 (ert-deftest treesit-parser-supplemental ()
   "Supplemental node functions."
   (skip-unless (treesit-language-available-p 'json))
@@ -594,6 +607,288 @@ visible_end.)"
     (insert "]")
     (should (treesit-node-check array-node 'outdated))))
 
+;;; Defun navigation
+;;
+;; I've setup a framework for easier testing of defun navigation.
+;;
+;; To use it for a particular language, first write a test program
+;; similar to `treesit--ert-defun-navigation-python-program', and
+;; insert markers.  Markers that marks BOLs are defined as follows:
+;;
+;; 100 Before 1st parent
+;; 101 Beg of 1st parent
+;; 102 End of 1st parent
+;; 103 Beg of 2nd parent
+;; 104 Beg of 1st method
+;; 105 End of 1st method
+;; 106 Beg of 2nd method
+;; 107 End of 2nd method
+;; 108 End of 2nd parent
+;; 109 Beg of 3rd parent
+;; 110 End of 3rd parent
+;; 999 Dummy markers
+;;
+;; Then add marker 0-9 following the definition given in
+;; `treesit--ert-defun-navigation-nested-master'.  Then you can use
+;; `treesit--ert-test-defun-navigation', pass the test program you
+;; just wrote, and the appropriate master:
+;;
+;; - `treesit--ert-defun-navigation-nested-master' for nested defun
+;; - `treesit--ert-defun-navigation-top-level-master' for top-level
+
+
+(defun treesit--ert-insert-and-parse-marker (opening closing text)
+  "Insert TEXT and parse the marker positions in it.
+
+TEXT should be a string in which contains position markings
+like (1).  OPENING and CLOSING are position marking's delimiters,
+for (1), OPENING and CLOSING should be \"(\" and \")\",
+respectively.
+
+This function inserts TEXT, parses and removes all the markings,
+and returns an alist of (NUMBER . POS), where number is each
+marking's number, and POS is each marking's position."
+  (declare (indent 2))
+  (let (result)
+    (insert text)
+    (goto-char (point-min))
+    (while (re-search-forward
+            (rx-to-string `(seq ,opening (group (+ digit)) ,closing))
+            nil t)
+      (let ((idx (string-to-number (match-string 1))))
+        (push (cons idx (match-beginning 0)) result)
+        (delete-region (match-beginning 0) (match-end 0))))
+    (nreverse result)))
+
+(defun treesit--ert-collect-positions (positions functions)
+  "Collect positions after calling each function in FUNCTIONS.
+
+POSITIONS should be a list of buffer positions, FUNCTIONS should
+be a list of functions.  This function collects the return value
+of each function in FUNCTIONS starting at each position in
+POSITIONS.
+
+Return a list of (POS...) where each POS corresponds to a
+function in FUNCTIONS.  For example, if buffer content is
+\"123\", POSITIONS is (2 3), FUNCTIONS is (point-min point-max),
+the return value is ((1 3) (1 3))."
+  (cl-loop for pos in positions
+           collect (cl-loop for fn in functions
+                            collect (progn
+                                      (goto-char pos)
+                                      (funcall fn)))))
+
+(defun treesit--ert-test-defun-navigation
+    (init program master &optional opening closing)
+  "Run defun navigation tests on PROGRAM and MASTER.
+
+INIT is a setup function that runs right after this function
+creates a temporary buffer.  It should take no argument.
+
+PROGRAM is a program source in string, MASTER is a list of
+\(START PREV-BEG NEXT-END PREV-END NEXT-BEG), where START is the
+starting marker position, and the rest are marker positions the
+corresponding navigation should stop at (after running
+`treesit-defun-skipper').
+
+OPENING and CLOSING are the same as in
+`treesit--ert-insert-and-parse-marker', by default they are \"[\"
+and \"]\"."
+  (with-temp-buffer
+    (funcall init)
+    (let* ((opening (or opening "["))
+           (closing (or closing "]"))
+           ;; Insert program and parse marker positions.
+           (marker-alist (treesit--ert-insert-and-parse-marker
+                             opening closing program))
+           ;; Translate marker positions into buffer positions.
+           (decoded-master
+            (cl-loop for record in master
+                     collect
+                     (cl-loop for pos in record
+                              collect (alist-get pos marker-alist))))
+           ;; Collect positions each function returns.
+           (positions
+            (treesit--ert-collect-positions
+             ;; The first column of DECODED-MASTER.
+             (mapcar #'car decoded-master)
+             ;; Four functions: next-end, prev-beg, next-beg, prev-end.
+             (mapcar (lambda (conf)
+                       (lambda ()
+                         (if-let ((pos (funcall
+                                        #'treesit--navigate-defun
+                                        (point) (car conf) (cdr conf))))
+                             (save-excursion
+                               (goto-char pos)
+                               (funcall treesit-defun-skipper)
+                               (point)))))
+                     '((-1 . beg)
+                       (1 . end)
+                       (-1 . end)
+                       (1 . beg))))))
+      ;; Verify each position.
+      (cl-loop for record in decoded-master
+               for orig-record in master
+               for poss in positions
+               for name = (format "marker %d" (car orig-record))
+               do (should (equal (cons name (cdr record))
+                                 (cons name poss)))))))
+
+(defvar treesit--ert-defun-navigation-python-program
+  "[100]
+[101]class Class1():
+[999]    prop = 0
+[102]
+[103]class Class2():[0]
+[104]    [1]def method1():
+[999]        [2]return 0[3]
+[105]    [4]
+[106]    [5]def method2():
+[999]        [6]return 0[7]
+[107]    [8]
+[999]    prop = 0[9]
+[108]
+[109]class Class3():
+[999]    prop = 0[10]
+[110]
+"
+  "Python source for navigation test.")
+
+(defvar treesit--ert-defun-navigation-js-program
+  "[100]
+[101]class Class1 {
+[999]}
+[102]
+[103]class Class2 {[0]
+[104]  [1]method1() {
+[999]    [2]return 0;
+[999]  }[3]
+[105]  [4]
+[106]  [5]method2() {
+[999]    [6]return 0;
+[999]  }[7]
+[107][8]
+[999]}[9]
+[108]
+[109]class class3 {
+[999]}[10]
+[110]
+"
+  "Javascript source for navigation test.")
+
+(defvar treesit--ert-defun-navigation-bash-program
+  "[100]
+[101]parent1 () {
+[999]}
+[102]
+[103]parent2 () {[0]
+[104]    [1]sibling1 () {
+[999]        [2]echo hi
+[999]    }[3]
+[105]    [4]
+[106]    [5]sibling2 () {
+[999]        [6]echo hi
+[999]    }[7]
+[107][8]
+[999]}[9]
+[108]
+[109]parent3 () {
+[999]}
+[110]
+"
+  "Javascript source for navigation test.")
+
+(defvar treesit--ert-defun-navigation-nested-master
+  ;; START PREV-BEG NEXT-END PREV-END NEXT-BEG
+  '((0 103 105 102 106) ; Between Beg of parent & 1st sibling.
+    (1 103 105 102 106) ; Beg of 1st sibling.
+    (2 104 105 102 106) ; Inside 1st sibling.
+    (3 104 107 102 109) ; End of 1st sibling.
+    (4 104 107 102 109) ; Between 1st sibling & 2nd sibling.
+    (5 104 107 102 109) ; Beg of 2nd sibling.
+    (6 106 107 105 109) ; Inside 2nd sibling.
+    (7 106 108 105 109) ; End of 2nd sibling.
+    (8 106 108 105 109) ; Between 2nd sibling & end of parent.
+    (9 103 110 102 nil) ; End of parent.
+
+    (100 nil 102 nil 103) ; Before 1st parent.
+    (101 nil 102 nil 103) ; Beg of 1st parent.
+    (102 101 108 nil 109) ; Between 1st & 2nd parent.
+    (103 101 108 nil 109) ; Beg of 2nd parent.
+    (110 109 nil 108 nil) ; After 3rd parent.
+    )
+  "Master of nested navigation test.
+
+This basically says, e.g., \"start with point on marker 0, go to
+the prev-beg, now point should be at marker 103\", etc.")
+
+(defvar treesit--ert-defun-navigation-top-level-master
+  ;; START PREV-BEG NEXT-END NEXT-BEG PREV-END
+  '((0 103 108 102 109) ; Between Beg of parent & 1st sibling.
+    (1 103 108 102 109) ; Beg of 1st sibling.
+    (2 103 108 102 109) ; Inside 1st sibling.
+    (3 103 108 102 109) ; End of 1st sibling.
+    (4 103 108 102 109) ; Between 1st sibling & 2nd sibling.
+    (5 103 108 102 109) ; Beg of 2nd sibling.
+    (6 103 108 102 109) ; Inside 2nd sibling.
+    (7 103 108 102 109) ; End of 2nd sibling.
+    (8 103 108 102 109) ; Between 2nd sibling & end of parent.
+    (9 103 110 102 nil) ; End of parent.
+
+    ;; Top-level defuns should be identical to the nested test.
+    (100 nil 102 nil 103) ; Before 1st parent.
+    (101 nil 102 nil 103) ; Beg of 1st parent.
+    (102 101 108 nil 109) ; Between 1st & 2nd parent.
+    (103 101 108 nil 109) ; Beg of 2nd parent.
+    (110 109 nil 108 nil) ; After 3rd parent.
+    )
+  "Master of top-level navigation test.")
+
+(ert-deftest treesit-defun-navigation-nested-1 ()
+  "Test defun navigation."
+  (skip-unless (treesit-language-available-p 'python))
+  ;; Nested defun navigation
+  (let ((treesit-defun-tactic 'nested))
+    (require 'python)
+    (treesit--ert-test-defun-navigation
+     'python-ts-mode
+     treesit--ert-defun-navigation-python-program
+     treesit--ert-defun-navigation-nested-master)))
+
+(ert-deftest treesit-defun-navigation-nested-2 ()
+  "Test defun navigation using `js-ts-mode'."
+  (skip-unless (treesit-language-available-p 'javascript))
+  ;; Nested defun navigation
+  (let ((treesit-defun-tactic 'nested))
+    (require 'js)
+    (treesit--ert-test-defun-navigation
+     'js-ts-mode
+     treesit--ert-defun-navigation-js-program
+     treesit--ert-defun-navigation-nested-master)))
+
+(ert-deftest treesit-defun-navigation-nested-3 ()
+  "Test defun navigation using `bash-ts-mode'."
+  (skip-unless (treesit-language-available-p 'bash))
+  ;; Nested defun navigation
+  (let ((treesit-defun-tactic 'nested))
+    (treesit--ert-test-defun-navigation
+     (lambda ()
+       (treesit-parser-create 'bash)
+       (setq-local treesit-defun-type-regexp "function_definition"))
+     treesit--ert-defun-navigation-bash-program
+     treesit--ert-defun-navigation-nested-master)))
+
+(ert-deftest treesit-defun-navigation-top-level ()
+  "Test top-level only defun navigation."
+  (skip-unless (treesit-language-available-p 'python))
+  ;; Nested defun navigation
+  (let ((treesit-defun-tactic 'top-level))
+    (require 'python)
+    (treesit--ert-test-defun-navigation
+     'python-ts-mode
+     treesit--ert-defun-navigation-python-program
+     treesit--ert-defun-navigation-top-level-master)))
+
 ;; TODO
 ;; - Functions in treesit.el
 ;; - treesit-load-name-override-list



reply via email to

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