cairo_lang_syntax/node/
with_db.rs

1use cairo_lang_filesystem::span::TextWidth;
2use cairo_lang_primitive_token::{PrimitiveSpan, PrimitiveToken, ToPrimitiveTokenStream};
3
4use super::SyntaxNode;
5use super::db::SyntaxGroup;
6
7pub struct SyntaxNodeWithDb<'a, Db: SyntaxGroup> {
8    node: &'a SyntaxNode,
9    db: &'a Db,
10}
11
12impl<'a, Db: SyntaxGroup> SyntaxNodeWithDb<'a, Db> {
13    pub fn new(node: &'a SyntaxNode, db: &'a Db) -> Self {
14        Self { node, db }
15    }
16}
17
18impl<'a, Db: SyntaxGroup> ToPrimitiveTokenStream for SyntaxNodeWithDb<'a, Db> {
19    type Iter = SyntaxNodeWithDbIterator<'a, Db>;
20
21    fn to_primitive_token_stream(&self) -> Self::Iter {
22        // The lifetime of the iterator should extend 'a because it derives from both node and db
23        SyntaxNodeWithDbIterator::new(
24            Box::new(
25                self.node.tokens(self.db).flat_map(|node| token_from_syntax_node(node, self.db)),
26            ),
27            self.db,
28        )
29    }
30}
31
32pub struct SyntaxNodeWithDbIterator<'a, Db: SyntaxGroup> {
33    inner: Box<dyn Iterator<Item = PrimitiveToken> + 'a>,
34    _db: &'a Db,
35}
36
37impl<'a, Db: SyntaxGroup> SyntaxNodeWithDbIterator<'a, Db> {
38    pub fn new(inner: Box<dyn Iterator<Item = PrimitiveToken> + 'a>, db: &'a Db) -> Self {
39        Self { inner, _db: db }
40    }
41}
42
43impl<Db: SyntaxGroup> Iterator for SyntaxNodeWithDbIterator<'_, Db> {
44    type Item = PrimitiveToken;
45
46    fn next(&mut self) -> Option<Self::Item> {
47        self.inner.next()
48    }
49}
50
51/// Create a `PrimitiveToken` representation from a `SyntaxNode`.
52///
53/// The `PrimitiveToken` keeps two information - some text content and a span associated with it.
54/// A `SyntaxNode` consists of some token, trivia associated with this token, and a span describing
55/// the token either with or without trivia.
56/// We split the content of the `SyntaxNode` into three parts: the prefix trivia, the main content
57/// of the node, and the suffix trivia. Each part is represented as a separate `PrimitiveToken`
58/// with its corresponding span.
59fn token_from_syntax_node(node: SyntaxNode, db: &dyn SyntaxGroup) -> Vec<PrimitiveToken> {
60    let span_without_trivia = node.span_without_trivia(db);
61    let span_with_trivia = node.span(db);
62    let text = node.get_text(db);
63    let mut result = Vec::new();
64    let prefix_len = span_without_trivia.start - span_with_trivia.start;
65    let (prefix, rest) = text.split_at(prefix_len.as_u32() as usize);
66    if prefix_len > TextWidth::ZERO {
67        result.push(PrimitiveToken {
68            content: prefix.to_string(),
69            span: Some(PrimitiveSpan {
70                start: span_with_trivia.start.as_u32() as usize,
71                end: span_without_trivia.start.as_u32() as usize,
72            }),
73        });
74    }
75    let suffix_len = span_with_trivia.end - span_without_trivia.end;
76    let (content, suffix) = rest.split_at(rest.len() - suffix_len.as_u32() as usize);
77    if !content.is_empty() {
78        result.push(PrimitiveToken {
79            content: content.to_string(),
80            span: Some(PrimitiveSpan {
81                start: span_without_trivia.start.as_u32() as usize,
82                end: span_without_trivia.end.as_u32() as usize,
83            }),
84        });
85    }
86    if suffix_len > TextWidth::ZERO {
87        result.push(PrimitiveToken {
88            content: suffix.to_string(),
89            span: Some(PrimitiveSpan {
90                start: span_without_trivia.end.as_u32() as usize,
91                end: span_with_trivia.end.as_u32() as usize,
92            }),
93        });
94    }
95    result
96}