From 69dc5d3f0ed04d3c0b08b8e09efe62a279fa8962 Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Wed, 28 Jan 2026 01:06:57 -0800 Subject: [PATCH] Fix tree-sitter traversal slowness (bug#80108) * configure.ac (LIBSYSTEMD_CFLAGS): Increase minimal required tree-sitter version to 0.20.10. * src/treesit.c (treesit_traverse_sibling_helper): When traversing forward, use the new function ts_tree_cursor_goto_previous_sibling. --- configure.ac | 32 ++++---------------------------- src/treesit.c | 48 ++++++------------------------------------------ 2 files changed, 10 insertions(+), 70 deletions(-) diff --git a/configure.ac b/configure.ac index 0d7c58d8020..420ab6dabe6 100644 --- a/configure.ac +++ b/configure.ac @@ -4069,39 +4069,15 @@ TREE_SITTER_OBJ= NEED_DYNLIB=no 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], + dnl Tree-sitter 0.20.10 added ts_tree_cursor_goto_previous_sibling, we + dnl need it for a more efficient implementation for traversing the + dnl parse tree backwards (bug#80108). + EMACS_CHECK_MODULES([TREE_SITTER], [tree-sitter >= 0.20.10], [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.]) NEED_DYNLIB=yes - 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.]) - NEED_DYNLIB=yes - 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= diff --git a/src/treesit.c b/src/treesit.c index ae73885e71d..e9ae1ad3605 100644 --- a/src/treesit.c +++ b/src/treesit.c @@ -4278,50 +4278,14 @@ treesit_traverse_sibling_helper (TSTreeCursor *cursor, } else /* Backward. */ { - /* Go to first child and go through each sibling, until we find - the one just before the starting node. */ - TSNode start = ts_tree_cursor_current_node (cursor); - if (!ts_tree_cursor_goto_parent (cursor)) - return false; - treesit_assume_true (ts_tree_cursor_goto_first_child (cursor)); - - /* Now CURSOR is at the first child. If we started at the first - child, then there is no further siblings. */ - TSNode first_child = ts_tree_cursor_current_node (cursor); - if (ts_node_eq (first_child, start)) - return false; - - /* PROBE is always DELTA siblings ahead of CURSOR. */ - TSTreeCursor probe = ts_tree_cursor_copy (cursor); - /* This is position of PROBE minus position of CURSOR. */ - ptrdiff_t delta = 0; - TSNode probe_node; - TSNode cursor_node; - while (ts_tree_cursor_goto_next_sibling (&probe)) + if (!named) + return ts_tree_cursor_goto_previous_sibling (cursor); + /* Else named... */ + while (ts_tree_cursor_goto_previous_sibling (cursor)) { - /* Move PROBE forward, if it equals to the starting node, - CURSOR points to the node we want (prev valid sibling of - the starting node). */ - delta++; - probe_node = ts_tree_cursor_current_node (&probe); - - /* PROBE matched, depending on NAMED, return true/false. */ - if (ts_node_eq (probe_node, start)) - { - ts_tree_cursor_delete (&probe); - cursor_node = ts_tree_cursor_current_node (cursor); - ts_tree_cursor_delete (&probe); - return (!named || (named && ts_node_is_named (cursor_node))); - } - - /* PROBE didn't match, move CURSOR forward to PROBE's - position, but if we are looking for named nodes, only - move CURSOR to PROBE if PROBE is at a named node. */ - if (!named || (named && ts_node_is_named (probe_node))) - for (; delta > 0; delta--) - treesit_assume_true (ts_tree_cursor_goto_next_sibling (cursor)); + if (ts_node_is_named (ts_tree_cursor_current_node (cursor))) + return true; } - ts_tree_cursor_delete (&probe); return false; } }