diff --git a/docs/classes/tree_sitter.Node.rst b/docs/classes/tree_sitter.Node.rst index 5b5b057..45284b6 100644 --- a/docs/classes/tree_sitter.Node.rst +++ b/docs/classes/tree_sitter.Node.rst @@ -9,6 +9,9 @@ Node .. automethod:: child .. automethod:: child_by_field_id .. automethod:: child_by_field_name + .. automethod:: child_containing_descendant + + .. versionadded:: 0.23.0 .. automethod:: children_by_field_id .. automethod:: children_by_field_name .. automethod:: descendant_for_byte_range diff --git a/tree_sitter/__init__.pyi b/tree_sitter/__init__.pyi index a4a7911..653863d 100644 --- a/tree_sitter/__init__.pyi +++ b/tree_sitter/__init__.pyi @@ -119,6 +119,7 @@ class Node: def named_child(self, index: int, /) -> Node | None: ... def child_by_field_id(self, id: int, /) -> Node | None: ... def child_by_field_name(self, name: str, /) -> Node | None: ... + def child_containing_descendant(self, descendant: Node, /) -> Node | None: ... def children_by_field_id(self, id: int, /) -> list[Node]: ... def children_by_field_name(self, name: str, /) -> list[Node]: ... def field_name_for_child(self, child_index: int, /) -> str | None: ... diff --git a/tree_sitter/binding/node.c b/tree_sitter/binding/node.c index 66345c9..4717e7b 100644 --- a/tree_sitter/binding/node.c +++ b/tree_sitter/binding/node.c @@ -279,6 +279,20 @@ PyObject *node_named_descendant_for_point_range(Node *self, PyObject *args) { return node_new_internal(state, descendant, self->tree); } +PyObject *node_child_containing_descendant(Node *self, PyObject *args) { + ModuleState *state = GET_MODULE_STATE(self); + TSNode descendant; + if (!PyArg_ParseTuple(args, "O!:child_containing_descendant", &descendant, state->node_type)) { + return NULL; + } + + TSNode child = ts_node_child_containing_descendant(self->node, descendant); + if (ts_node_is_null(child)) { + Py_RETURN_NONE; + } + return node_new_internal(state, child, self->tree); +} + PyObject *node_get_id(Node *self, void *Py_UNUSED(payload)) { return PyLong_FromVoidPtr((void *)self->node.id); } @@ -578,7 +592,7 @@ PyDoc_STRVAR(node_child_by_field_id_doc, PyDoc_STRVAR(node_children_by_field_id_doc, "children_by_field_id(self, id, /)\n--\n\n" "Get a list of children with the given numerical field id." - DOC_SEE_ALSO ":meth:`children_by_field_name`" ); + DOC_SEE_ALSO ":meth:`children_by_field_name`"); PyDoc_STRVAR(node_child_by_field_name_doc, "child_by_field_name(self, name, /)\n--\n\n" "Get the first child with the given field name."); PyDoc_STRVAR(node_children_by_field_name_doc, "children_by_field_name(self, name, /)\n--\n\n" @@ -598,6 +612,9 @@ PyDoc_STRVAR(node_descendant_for_point_range_doc, PyDoc_STRVAR(node_named_descendant_for_point_range_doc, "named_descendant_for_point_range(self, start_point, end_point, /)\n--\n\n" "Get the smallest *named* node within this node that spans the given point range."); +PyDoc_STRVAR(node_child_containing_descendant_doc, + "child_containing_descendant(self, descendant, /)\n--\n\n" + "Get the child of the node that contains the given descendant."); static PyMethodDef node_methods[] = { { @@ -678,6 +695,12 @@ static PyMethodDef node_methods[] = { .ml_flags = METH_VARARGS, .ml_doc = node_named_descendant_for_point_range_doc, }, + { + .ml_name = "child_containing_descendant", + .ml_meth = (PyCFunction)node_child_containing_descendant, + .ml_flags = METH_VARARGS, + .ml_doc = node_child_containing_descendant_doc, + }, {NULL}, }; @@ -702,7 +725,7 @@ static PyGetSetDef node_accessors[] = { NULL}, {"is_extra", (getter)node_get_is_extra, NULL, PyDoc_STR("Check if this node is _extra_.\n\nExtra nodes represent things which are not " - "required the grammar but can appear anywhere (e.g. whitespace)."), + "required by the grammar but can appear anywhere (e.g. whitespace)."), NULL}, {"has_changes", (getter)node_get_has_changes, NULL, PyDoc_STR("Check if this node has been edited."), NULL},