feat: add field_name_for_named_child

This commit is contained in:
Amaan Qureshi 2024-09-08 20:49:13 -04:00
parent 8667e3ea0c
commit 7e3f572655
5 changed files with 109 additions and 1 deletions

View file

@ -308,6 +308,13 @@ fn test_node_field_name_for_child() {
.child_by_field_name("value")
.unwrap();
// -------------------
// left: (identifier) 0
// operator: "+" 1 <--- (not a named child)
// (comment) 2 <--- (is an extra)
// right: (identifier) 3
// -------------------
assert_eq!(binary_expression_node.field_name_for_child(0), Some("left"));
assert_eq!(
binary_expression_node.field_name_for_child(1),
@ -323,6 +330,44 @@ fn test_node_field_name_for_child() {
assert_eq!(binary_expression_node.field_name_for_child(4), None);
}
#[test]
fn test_node_field_name_for_named_child() {
let mut parser = Parser::new();
parser.set_language(&get_language("c")).unwrap();
let tree = parser
.parse("int w = x + /* y is special! */ y;", None)
.unwrap();
let translation_unit_node = tree.root_node();
let declaration_node = translation_unit_node.named_child(0).unwrap();
let binary_expression_node = declaration_node
.child_by_field_name("declarator")
.unwrap()
.child_by_field_name("value")
.unwrap();
// -------------------
// left: (identifier) 0
// operator: "+" _ <--- (not a named child)
// (comment) 1 <--- (is an extra)
// right: (identifier) 2
// -------------------
assert_eq!(
binary_expression_node.field_name_for_named_child(0),
Some("left")
);
// The comment should not have a field name, as it's just an extra
assert_eq!(binary_expression_node.field_name_for_named_child(1), None);
// The operator is not a named child, so the named child at index 2 is the right child
assert_eq!(
binary_expression_node.field_name_for_named_child(2),
Some("right")
);
// Negative test - Not a valid child index
assert_eq!(binary_expression_node.field_name_for_named_child(3), None);
}
#[test]
fn test_node_child_by_field_name_with_extra_hidden_children() {
let mut parser = Parser::new();

View file

@ -373,6 +373,13 @@ extern "C" {
child_index: u32,
) -> *const ::core::ffi::c_char;
}
extern "C" {
#[doc = " Get the field name for node's named child at the given index, where zero\n represents the first named child. Returns NULL, if no field is found."]
pub fn ts_node_field_name_for_named_child(
self_: TSNode,
named_child_index: u32,
) -> *const ::core::ffi::c_char;
}
extern "C" {
#[doc = " Get the node's number of children."]
pub fn ts_node_child_count(self_: TSNode) -> u32;
@ -642,7 +649,7 @@ extern "C" {
pub fn ts_query_cursor_set_timeout_micros(self_: *mut TSQueryCursor, timeout_micros: u64);
}
extern "C" {
#[doc = " Get the duration in microseconds that query execution is allowed to take."]
#[doc = " Get the duration in microseconds that query execution is allowed to take.\n\n This is set via [`ts_query_cursor_set_timeout_micros`]."]
pub fn ts_query_cursor_timeout_micros(self_: *const TSQueryCursor) -> u64;
}
extern "C" {

View file

@ -1236,6 +1236,14 @@ impl<'tree> Node<'tree> {
}
}
/// Get the field name of this node's named child at the given index.
pub fn field_name_for_named_child(&self, named_child_index: u32) -> Option<&'static str> {
unsafe {
let ptr = ffi::ts_node_field_name_for_named_child(self.0, named_child_index);
(!ptr.is_null()).then(|| CStr::from_ptr(ptr).to_str().unwrap())
}
}
/// Iterate over this node's children.
///
/// A [`TreeCursor`] is used to retrieve the children efficiently. Obtain

View file

@ -570,6 +570,12 @@ TSNode ts_node_child(TSNode self, uint32_t child_index);
*/
const char *ts_node_field_name_for_child(TSNode self, uint32_t child_index);
/**
* Get the field name for node's named child at the given index, where zero
* represents the first named child. Returns NULL, if no field is found.
*/
const char *ts_node_field_name_for_named_child(TSNode self, uint32_t named_child_index);
/**
* Get the node's number of children.
*/

View file

@ -688,6 +688,48 @@ const char *ts_node_field_name_for_child(TSNode self, uint32_t child_index) {
return NULL;
}
const char *ts_node_field_name_for_named_child(TSNode self, uint32_t named_child_index) {
TSNode result = self;
bool did_descend = true;
const char *inherited_field_name = NULL;
while (did_descend) {
did_descend = false;
TSNode child;
uint32_t index = 0;
NodeChildIterator iterator = ts_node_iterate_children(&result);
while (ts_node_child_iterator_next(&iterator, &child)) {
if (ts_node__is_relevant(child, false)) {
if (index == named_child_index) {
if (ts_node_is_extra(child)) {
return NULL;
}
const char *field_name = ts_node__field_name_from_language(result, iterator.structural_child_index - 1);
if (field_name) return field_name;
return inherited_field_name;
}
index++;
} else {
uint32_t named_grandchild_index = named_child_index - index;
uint32_t grandchild_count = ts_node__relevant_child_count(child, false);
if (named_grandchild_index < grandchild_count) {
const char *field_name = ts_node__field_name_from_language(result, iterator.structural_child_index - 1);
if (field_name) inherited_field_name = field_name;
did_descend = true;
result = child;
named_child_index = named_grandchild_index;
break;
}
index += grandchild_count;
}
}
}
return NULL;
}
TSNode ts_node_child_by_field_name(
TSNode self,
const char *name,