Fix treesit-node-first-child-for-pos (bug#60127)

The problem is due to a bug in ts_node_first_child_for_pos, but
tree-sitter is moving pretty slowly right now so I reimplemented a
correct version of it in treesit.c.

* src/treesit.c (treesit_cursor_first_child_for_byte): New function.
(Ftreesit_node_first_child_for_pos): Use the new function.
This commit is contained in:
Yuan Fu 2023-01-17 00:57:54 -08:00
parent b36cc7e7bb
commit 7c61a30410
No known key found for this signature in database
GPG key ID: 56E19BC57664A442

View file

@ -2095,6 +2095,41 @@ return nil. */)
return make_treesit_node (XTS_NODE (node)->parser, sibling);
}
/* Our reimplementation of ts_node_first_child_for_byte. The current
implementation of that function has problems (see bug#60127), so
before it's fixed upstream, we use our own reimplementation of it.
Return true if there is a valid sibling, return false otherwise.
If the return value is false, the position of the cursor is
undefined. (We use cursor because technically we can't make a null
node for ourselves, also, using cursor is more convenient.)
TODO: Remove this function once tree-sitter fixed the bug. */
static bool treesit_cursor_first_child_for_byte
(TSTreeCursor *cursor, ptrdiff_t pos, bool named)
{
if (!ts_tree_cursor_goto_first_child (cursor))
return false;
TSNode node = ts_tree_cursor_current_node (cursor);
while (ts_node_end_byte (node) <= pos)
{
if (ts_tree_cursor_goto_next_sibling (cursor))
node = ts_tree_cursor_current_node (cursor);
else
/* Reached the end and still can't find a valid sibling. */
return false;
}
while (named && (!ts_node_is_named (node)))
{
if (ts_tree_cursor_goto_next_sibling (cursor))
node = ts_tree_cursor_current_node (cursor);
else
/* Reached the end and still can't find a named sibling. */
return false;
}
return true;
}
DEFUN ("treesit-node-first-child-for-pos",
Ftreesit_node_first_child_for_pos,
Streesit_node_first_child_for_pos, 2, 3, 0,
@ -2119,16 +2154,17 @@ Note that this function returns an immediate child, not the smallest
ptrdiff_t byte_pos = buf_charpos_to_bytepos (buf, XFIXNUM (pos));
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))
TSTreeCursor cursor = ts_tree_cursor_new (treesit_node);
ptrdiff_t treesit_pos = byte_pos - visible_beg;
bool success;
success = treesit_cursor_first_child_for_byte (&cursor, treesit_pos,
!NILP (named));
TSNode child = ts_tree_cursor_current_node (&cursor);
ts_tree_cursor_delete (&cursor);
if (!success)
return Qnil;
return make_treesit_node (XTS_NODE (node)->parser, child);
}