@@ -872,6 +872,45 @@ impl<M> Node<M> {
872872 // real siblings.
873873 self . inner . siblings_with_tokens ( direction) . skip ( 1 ) . map ( |x| x. into ( ) )
874874 }
875+
876+ /// Returns a token at a given offset of this node.
877+ ///
878+ /// Depending on what is at the given offset it will return None,
879+ /// single token or two adjacent tokens.
880+ ///
881+ /// ```rust
882+ /// # use yara_x_parser::Parser;
883+ /// # use yara_x_parser::cst::TokenAtOffset;
884+ /// // Get the root node, which contains 26 characters.
885+ /// let mut rule_decl = Parser::new(b"rule test {condition:true}")
886+ /// .try_into_cst()
887+ /// .unwrap()
888+ /// .root();
889+ ///
890+ /// // Should find single `SyntaxKind::RULE_KW` token at offset 0.
891+ /// let single_token = rule_decl.token_at_offset(0);
892+ /// let rule_kw = rule_decl.first_token().unwrap();
893+ /// assert_eq!(single_token, TokenAtOffset::Single(rule_kw));
894+ ///
895+ /// // Should find both `SyntaxKind::RULE_KW` and
896+ /// // `SyntaxKind::WHITESPACE` tokens at offset 4.
897+ /// let between_tokens = rule_decl.token_at_offset(4);
898+ /// let left_rule_kw = rule_decl.first_token().unwrap();
899+ /// let right_whitespace = rule_decl.first_token().unwrap().next_token().unwrap();
900+ /// assert_eq!(between_tokens, TokenAtOffset::Between(left_rule_kw, right_whitespace));
901+ ///
902+ /// // Should return `TokenAtOffset::None` for an empty file.
903+ /// let mut empty = Parser::new(b"")
904+ /// .try_into_cst()
905+ /// .unwrap()
906+ /// .root();
907+ ///
908+ /// let none = empty.token_at_offset(0);
909+ /// assert_eq!(none, TokenAtOffset::None);
910+ /// ```
911+ pub fn token_at_offset ( & self , offset : u32 ) -> TokenAtOffset < M > {
912+ TokenAtOffset :: from ( self . inner . token_at_offset ( offset. into ( ) ) )
913+ }
875914}
876915
877916impl Node < Immutable > {
@@ -927,3 +966,54 @@ impl<M> Iterator for NodesAndTokens<M> {
927966 self . inner . next ( ) . map ( |x| x. into ( ) )
928967 }
929968}
969+
970+ /// Represents tokens found at a given offset.
971+ ///
972+ /// This is the value returned by [`Node::token_at_offset`].
973+ ///
974+ /// NOTE: This API is still unstable and should not be used by third-party code.
975+ #[ derive( Debug , PartialEq ) ]
976+ #[ doc( hidden) ]
977+ pub enum TokenAtOffset < M > {
978+ None ,
979+ Single ( Token < M > ) ,
980+ Between ( Token < M > , Token < M > ) ,
981+ }
982+
983+ impl < M > TokenAtOffset < M > {
984+ /// Returns the token option, giving preference to the right token
985+ /// if the offset is between two leaves.
986+ pub fn right_biased ( self ) -> Option < Token < M > > {
987+ match self {
988+ TokenAtOffset :: None => None ,
989+ TokenAtOffset :: Single ( token) => Some ( token) ,
990+ TokenAtOffset :: Between ( _, right) => Some ( right) ,
991+ }
992+ }
993+
994+ /// Returns the token option, giving preference to the left token
995+ /// if the offset is between two leaves.
996+ pub fn left_biased ( self ) -> Option < Token < M > > {
997+ match self {
998+ TokenAtOffset :: None => None ,
999+ TokenAtOffset :: Single ( token) => Some ( token) ,
1000+ TokenAtOffset :: Between ( left, _) => Some ( left) ,
1001+ }
1002+ }
1003+ }
1004+
1005+ impl < M > From < rowan:: TokenAtOffset < rowan:: SyntaxToken < YARA > > >
1006+ for TokenAtOffset < M >
1007+ {
1008+ fn from ( value : rowan:: TokenAtOffset < rowan:: SyntaxToken < YARA > > ) -> Self {
1009+ match value {
1010+ rowan:: TokenAtOffset :: None => Self :: None ,
1011+ rowan:: TokenAtOffset :: Single ( token) => {
1012+ Self :: Single ( Token :: new ( token) )
1013+ }
1014+ rowan:: TokenAtOffset :: Between ( left, right) => {
1015+ Self :: Between ( Token :: new ( left) , Token :: new ( right) )
1016+ }
1017+ }
1018+ }
1019+ }
0 commit comments