diff --git a/src/org.rs b/src/org.rs index 1a3a144..f0e736b 100644 --- a/src/org.rs +++ b/src/org.rs @@ -1,5 +1,5 @@ use rowan::ast::AstNode; -use rowan::GreenNode; +use rowan::{GreenNode, TextSize}; use crate::ast::Document; use crate::config::ParseConfig; @@ -66,4 +66,41 @@ impl Org { } find(SyntaxNode::new_root(self.green.clone())) } + + /// Returns node in given offset + /// + /// ```rust + /// use orgize::{Org, ast::Headline}; + /// + /// let org = Org::parse("\n\n* foo\n* bar"); + /// + /// assert!(org.node_at_offset::(0).is_none()); + /// + /// let hdl = org.node_at_offset::(2).unwrap(); + /// assert_eq!(hdl.title_raw(), "foo"); + /// + /// let hdl = org.node_at_offset::(9).unwrap(); + /// assert_eq!(hdl.title_raw(), "bar"); + /// + /// assert!(org.node_at_offset::(999).is_none()); + /// ``` + pub fn node_at_offset>( + &self, + offset: impl Into, + ) -> Option { + let offset = offset.into(); + fn find>( + node: SyntaxNode, + offset: TextSize, + ) -> Option { + if !node.text_range().contains(offset) { + None + } else if N::can_cast(node.kind()) { + N::cast(node) + } else { + node.children().find_map(|node| find(node, offset)) + } + } + find(SyntaxNode::new_root(self.green.clone()), offset) + } }