Skip to content

Commit 8f24a8e

Browse files
committed
feat: implement token_at_offset method for Node.
1 parent ab1ae5c commit 8f24a8e

File tree

1 file changed

+90
-0
lines changed

1 file changed

+90
-0
lines changed

parser/src/cst/mod.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

877916
impl 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

Comments
 (0)