From dffee22736ef89f606f0d02241f0dcb754890edf Mon Sep 17 00:00:00 2001 From: Niranjan Hasabnis Date: Sat, 15 May 2021 00:18:32 +0000 Subject: [PATCH 1/5] Adding API to get field name of a TSNode This PR adds an API to get name of the field of TSNode's child. It uses same set of arguments as that of ts_node_child, but returns field name if it is found, otherwise it returns NULL. This API is useful to implement custom printing of S-expressions such as following: "(binary_expression (binary_expression_left (identifier)) (binary_expression_operator ("+")) (binary_expression_right (identifier) )" Currently, ts_node_string does not allow any customization for printing. --- lib/include/tree_sitter/api.h | 6 ++++++ lib/src/node.c | 17 +++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/lib/include/tree_sitter/api.h b/lib/include/tree_sitter/api.h index 22837035..4633d069 100644 --- a/lib/include/tree_sitter/api.h +++ b/lib/include/tree_sitter/api.h @@ -487,6 +487,12 @@ TSNode ts_node_parent(TSNode); */ TSNode ts_node_child(TSNode, uint32_t); +/** + * Get the field_name for node's child at the given index, where zero represents + * the first child. Returns NULL, if no field is found. + */ +const char *ts_node_child_field_name(TSNode, uint32_t); + /** * Get the node's number of children. */ diff --git a/lib/src/node.c b/lib/src/node.c index 8498f9c5..faf02536 100644 --- a/lib/src/node.c +++ b/lib/src/node.c @@ -578,6 +578,23 @@ recur: return ts_node__null(); } +const char *ts_node_child_field_name(TSNode self, uint32_t child_index) { + const TSFieldMapEntry *field_map, *field_map_end; + ts_language_field_map( + self.tree->language, + ts_node__subtree(self).ptr->production_id, + &field_map, + &field_map_end + ); + + for (const TSFieldMapEntry *i = field_map; i < field_map_end; i++) { + if (i->child_index == child_index) { + return self.tree->language->field_names[i->field_id]; + } + } + return NULL; +} + TSNode ts_node_child_by_field_name( TSNode self, const char *name, From 3be4bc7abb6c01d9c878a78889868a1ef86b2506 Mon Sep 17 00:00:00 2001 From: Niranjan Hasabnis Date: Mon, 17 May 2021 17:52:52 +0000 Subject: [PATCH 2/5] Syntactic improvements --- lib/src/node.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/node.c b/lib/src/node.c index faf02536..8a172813 100644 --- a/lib/src/node.c +++ b/lib/src/node.c @@ -579,15 +579,15 @@ recur: } const char *ts_node_child_field_name(TSNode self, uint32_t child_index) { - const TSFieldMapEntry *field_map, *field_map_end; + const TSFieldMapEntry *field_map_start = NULL, *field_map_end = NULL; ts_language_field_map( self.tree->language, ts_node__subtree(self).ptr->production_id, - &field_map, + &field_map_start, &field_map_end ); - for (const TSFieldMapEntry *i = field_map; i < field_map_end; i++) { + for (const TSFieldMapEntry *i = field_map_start; i < field_map_end; i++) { if (i->child_index == child_index) { return self.tree->language->field_names[i->field_id]; } From c0f702216577ba62b07514eb18cc3fe44ea0de70 Mon Sep 17 00:00:00 2001 From: Niranjan Hasabnis Date: Thu, 20 May 2021 23:29:25 +0000 Subject: [PATCH 3/5] Function rename --- lib/src/node.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/node.c b/lib/src/node.c index 8a172813..ad360cfa 100644 --- a/lib/src/node.c +++ b/lib/src/node.c @@ -578,7 +578,7 @@ recur: return ts_node__null(); } -const char *ts_node_child_field_name(TSNode self, uint32_t child_index) { +const char *ts_node_field_name_for_child(TSNode self, uint32_t child_index) { const TSFieldMapEntry *field_map_start = NULL, *field_map_end = NULL; ts_language_field_map( self.tree->language, From c31acb8fec0028c5761c2b00dde72f169dd04bb9 Mon Sep 17 00:00:00 2001 From: Niranjan Hasabnis Date: Fri, 21 May 2021 01:50:10 +0000 Subject: [PATCH 4/5] Changing API name; Adding unit test and Rust bindings --- cli/src/tests/node_test.rs | 19 +++++++++++++++++++ lib/binding_rust/bindings.rs | 5 +++++ lib/binding_rust/lib.rs | 12 ++++++++++++ lib/include/tree_sitter/api.h | 4 ++-- 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/cli/src/tests/node_test.rs b/cli/src/tests/node_test.rs index 9a47ce06..c5c9e720 100644 --- a/cli/src/tests/node_test.rs +++ b/cli/src/tests/node_test.rs @@ -248,6 +248,25 @@ fn test_node_parent_of_child_by_field_name() { ); } +#[test] +fn test_node_field_name_for_child() { + let mut parser = Parser::new(); + parser.set_language(get_language("c")).unwrap(); + let tree = parser.parse("x + y;", None).unwrap(); + let translation_unit_node = tree.root_node(); + let binary_expression_node = translation_unit_node + .named_child(0) + .unwrap() + .named_child(0) + .unwrap(); + + assert_eq!(binary_expression_node.field_name_for_child(0), Some("left")); + assert_eq!(binary_expression_node.field_name_for_child(1), Some("operator")); + assert_eq!(binary_expression_node.field_name_for_child(2), Some("right")); + // Negative test - Not a valid child index + assert_eq!(binary_expression_node.field_name_for_child(3), None); +} + #[test] fn test_node_child_by_field_name_with_extra_hidden_children() { let mut parser = Parser::new(); diff --git a/lib/binding_rust/bindings.rs b/lib/binding_rust/bindings.rs index a749ef98..50da12fc 100644 --- a/lib/binding_rust/bindings.rs +++ b/lib/binding_rust/bindings.rs @@ -434,6 +434,11 @@ extern "C" { #[doc = " child."] pub fn ts_node_child(arg1: TSNode, arg2: u32) -> TSNode; } +extern "C" { + #[doc = " Get the field name for node's child at the given index, where zero represents"] + #[doc = " the first child. Returns NULL, if no field is found."] + pub fn ts_node_field_name_for_child(arg1: TSNode, arg2: u32) -> *const ::std::os::raw::c_char; +} extern "C" { #[doc = " Get the node\'s number of children."] pub fn ts_node_child_count(arg1: TSNode) -> u32; diff --git a/lib/binding_rust/lib.rs b/lib/binding_rust/lib.rs index 22a0c2c1..771a8433 100644 --- a/lib/binding_rust/lib.rs +++ b/lib/binding_rust/lib.rs @@ -859,6 +859,18 @@ impl<'tree> Node<'tree> { Self::new(unsafe { ffi::ts_node_child_by_field_id(self.0, field_id) }) } + /// Get the field name of this node's child at the given index. + pub fn field_name_for_child(&self, child_index: u32) -> Option<&'static str> { + unsafe { + let ptr = ffi::ts_node_field_name_for_child(self.0, child_index); + if ptr.is_null() { + None + } else { + Some(CStr::from_ptr(ptr).to_str().unwrap()) + } + } + } + /// Iterate over this node's children. /// /// A [TreeCursor] is used to retrieve the children efficiently. Obtain diff --git a/lib/include/tree_sitter/api.h b/lib/include/tree_sitter/api.h index 4633d069..43315415 100644 --- a/lib/include/tree_sitter/api.h +++ b/lib/include/tree_sitter/api.h @@ -488,10 +488,10 @@ TSNode ts_node_parent(TSNode); TSNode ts_node_child(TSNode, uint32_t); /** - * Get the field_name for node's child at the given index, where zero represents + * Get the field name for node's child at the given index, where zero represents * the first child. Returns NULL, if no field is found. */ -const char *ts_node_child_field_name(TSNode, uint32_t); +const char *ts_node_field_name_for_child(TSNode, uint32_t); /** * Get the node's number of children. From c0e2b43d34d4b4c763306f5f3d87a49077b56882 Mon Sep 17 00:00:00 2001 From: Niranjan Hasabnis Date: Fri, 21 May 2021 15:43:10 +0000 Subject: [PATCH 5/5] Fixing Rust formatting issue --- cli/src/tests/node_test.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/cli/src/tests/node_test.rs b/cli/src/tests/node_test.rs index c5c9e720..776ca2c7 100644 --- a/cli/src/tests/node_test.rs +++ b/cli/src/tests/node_test.rs @@ -255,15 +255,21 @@ fn test_node_field_name_for_child() { let tree = parser.parse("x + y;", None).unwrap(); let translation_unit_node = tree.root_node(); let binary_expression_node = translation_unit_node - .named_child(0) - .unwrap() - .named_child(0) - .unwrap(); + .named_child(0) + .unwrap() + .named_child(0) + .unwrap(); assert_eq!(binary_expression_node.field_name_for_child(0), Some("left")); - assert_eq!(binary_expression_node.field_name_for_child(1), Some("operator")); - assert_eq!(binary_expression_node.field_name_for_child(2), Some("right")); - // Negative test - Not a valid child index + assert_eq!( + binary_expression_node.field_name_for_child(1), + Some("operator") + ); + assert_eq!( + binary_expression_node.field_name_for_child(2), + Some("right") + ); + // Negative test - Not a valid child index assert_eq!(binary_expression_node.field_name_for_child(3), None); }