Skip to content

Commit 7a436e8

Browse files
committed
feat: add Node::last_token, Node::descendants
1 parent 47aa0d7 commit 7a436e8

File tree

1 file changed

+85
-0
lines changed

1 file changed

+85
-0
lines changed

crates/postgresql-cst-parser/src/tree_sitter.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,59 @@ impl<'a> Node<'a> {
207207
pub fn is_comment(&self) -> bool {
208208
matches!(self.kind(), SyntaxKind::C_COMMENT | SyntaxKind::SQL_COMMENT)
209209
}
210+
211+
/// Return the rightmost token in the subtree of this node
212+
/// this is not tree-sitter's API
213+
pub fn last_token(&self) -> Option<Node<'a>> {
214+
match &self.node_or_token {
215+
NodeOrToken::Node(node) => node.last_token().map(|token| Node {
216+
input: self.input,
217+
range_map: Rc::clone(&self.range_map),
218+
node_or_token: NodeOrToken::Token(token),
219+
}),
220+
NodeOrToken::Token(token) => Some(Node {
221+
input: self.input,
222+
range_map: Rc::clone(&self.range_map),
223+
node_or_token: NodeOrToken::Token(token),
224+
}),
225+
}
226+
}
227+
228+
/// Returns an iterator over all descendant nodes (not including tokens)
229+
/// this is not tree-sitter's API
230+
pub fn descendants(&self) -> impl Iterator<Item = Node<'a>> {
231+
struct Descendants<'a> {
232+
input: &'a str,
233+
range_map: Rc<HashMap<TextRange, Range>>,
234+
iter: Box<dyn Iterator<Item = &'a ResolvedNode> + 'a>,
235+
}
236+
237+
impl<'a> Iterator for Descendants<'a> {
238+
type Item = Node<'a>;
239+
240+
fn next(&mut self) -> Option<Self::Item> {
241+
self.iter.next().map(|node| Node {
242+
input: self.input,
243+
range_map: Rc::clone(&self.range_map),
244+
node_or_token: NodeOrToken::Node(node),
245+
})
246+
}
247+
}
248+
249+
if let Some(node) = self.node_or_token.as_node() {
250+
Descendants {
251+
input: self.input,
252+
range_map: Rc::clone(&self.range_map),
253+
iter: Box::new(node.descendants()),
254+
}
255+
} else {
256+
Descendants {
257+
input: self.input,
258+
range_map: Rc::clone(&self.range_map),
259+
iter: Box::new(std::iter::empty()),
260+
}
261+
}
262+
}
210263
}
211264

212265
impl<'a> From<Node<'a>> for TreeCursor<'a> {
@@ -513,4 +566,36 @@ from
513566

514567
assert_eq!(stmt_count, 2);
515568
}
569+
570+
#[test]
571+
fn test_last_token_returns_rightmost_token() {
572+
let src = "SELECT u.*, (v).id, name;";
573+
let tree = parse(src).unwrap();
574+
let root = tree.root_node();
575+
576+
let target_list = root
577+
.descendants()
578+
.find(|node| node.kind() == SyntaxKind::target_list)
579+
.expect("should find target_list");
580+
581+
// last token of the target_list is returned
582+
let last_token = target_list.last_token().expect("should have last token");
583+
assert_eq!(last_token.text(), "name");
584+
585+
let target_els = target_list
586+
.children()
587+
.into_iter()
588+
.filter(|node| node.kind() == SyntaxKind::target_el)
589+
.collect::<Vec<_>>();
590+
591+
let mut last_tokens = target_els
592+
.iter()
593+
.map(|node| node.last_token().expect("should have last token"));
594+
595+
// last token of each target_el is returned
596+
assert_eq!(last_tokens.next().unwrap().text(), "*");
597+
assert_eq!(last_tokens.next().unwrap().text(), "id");
598+
assert_eq!(last_tokens.next().unwrap().text(), "name");
599+
assert!(last_tokens.next().is_none());
600+
}
516601
}

0 commit comments

Comments
 (0)