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.
This commit is contained in:
Yuan Fu 2026-01-28 01:06:57 -08:00
parent 32cffe1707
commit 69dc5d3f0e
No known key found for this signature in database
GPG key ID: 56E19BC57664A442
2 changed files with 10 additions and 70 deletions

View file

@ -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=

View file

@ -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;
}
}