cairo_lang_syntax/node/
mod.rs

1use core::hash::Hash;
2
3use cairo_lang_filesystem::db::FilesGroup;
4use cairo_lang_filesystem::ids::{FileId, SmolStrId};
5use cairo_lang_filesystem::span::{TextOffset, TextPosition, TextSpan, TextWidth};
6use cairo_lang_proc_macros::DebugWithDb;
7use cairo_lang_utils::require;
8use salsa::Database;
9use salsa::plumbing::AsId;
10use vector_map::VecMap;
11
12use self::ast::TriviaGreen;
13use self::green::GreenNode;
14use self::ids::{GreenId, SyntaxStablePtrId};
15use self::kind::SyntaxKind;
16use crate::node::db::SyntaxGroup;
17use crate::node::iter::{Preorder, WalkEvent};
18
19pub mod ast;
20pub mod db;
21pub mod element_list;
22pub mod green;
23pub mod helpers;
24pub mod ids;
25pub mod iter;
26pub mod key_fields;
27pub mod kind;
28pub mod stable_ptr;
29pub mod with_db;
30
31#[cfg(test)]
32mod ast_test;
33#[cfg(test)]
34mod test_utils;
35
36/// Private enum for syntax node id. This holds data used to identify the node.
37#[derive(Debug, Clone, PartialEq, Eq, Hash, DebugWithDb, salsa::Update)]
38#[debug_db(dyn Database)]
39pub enum SyntaxNodeId<'db> {
40    Root(FileId<'db>),
41    Child {
42        /// Parent of this node, either another node or if node is root, its file id.
43        parent: SyntaxNode<'db>,
44        /// Chronological index among all nodes with the same (parent, kind, key_fields).
45        index: usize,
46        /// Which fields are used is determined by each SyntaxKind.
47        /// For example, a function item might use the name of the function.
48        key_fields: Box<[GreenId<'db>]>,
49    },
50}
51
52/// Private tracked struct containing the actual SyntaxNode data.
53/// This is kept private so the public `new` function generated by salsa can't be called directly.
54#[salsa::tracked]
55#[derive(Debug)]
56struct SyntaxNodeData<'a> {
57    #[tracked]
58    green: GreenId<'a>,
59    /// Number of characters from the beginning of the file to the start of the span of this
60    /// syntax subtree.
61    #[tracked]
62    offset: TextOffset,
63    /// Unique identifier for this node.
64    #[returns(ref)]
65    id: SyntaxNodeId<'a>,
66}
67
68impl<'db> SyntaxNodeData<'db> {
69    /// Gets the kind of the given node's parent if it exists.
70    pub fn parent(&self, db: &'db dyn Database) -> Option<SyntaxNode<'db>> {
71        match self.id(db) {
72            SyntaxNodeId::Root(_) => None,
73            SyntaxNodeId::Child { parent, .. } => Some(*parent),
74        }
75    }
76}
77
78impl<'db> cairo_lang_debug::DebugWithDb<'db> for SyntaxNodeData<'db> {
79    type Db = dyn Database;
80    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db Self::Db) -> std::fmt::Result {
81        f.debug_struct("SyntaxNode")
82            .field("green", &self.green(db).debug(db))
83            .field("offset", &self.offset(db))
84            .field("id", &self.id(db).debug(db))
85            .finish()
86    }
87}
88
89/// SyntaxNode. Untyped view of the syntax tree. Adds parent() and offset() capabilities.
90///
91/// This is a public wrapper around a private tracked struct. Construction only happens through
92/// tracked functions to ensure uniqueness of SyntaxNodes.
93/// Use `SyntaxNode::new_root` or `SyntaxNode::new_root_with_offset` to create root nodes.
94#[derive(Clone, Copy, PartialEq, Eq, Hash, salsa::Update)]
95pub struct SyntaxNode<'a> {
96    data: SyntaxNodeData<'a>,
97    /// Cached parent data to avoid database lookups. None for root nodes.
98    parent: Option<SyntaxNodeData<'a>>,
99    /// Cached kind to avoid database lookups.
100    kind: SyntaxKind,
101    /// Cached parent kind to avoid database lookups. None for root nodes.
102    parent_kind: Option<SyntaxKind>,
103}
104
105impl<'db> std::fmt::Debug for SyntaxNode<'db> {
106    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107        write!(f, "SyntaxNode({:x})", self.data.as_id().index())
108    }
109}
110
111impl<'db> cairo_lang_debug::DebugWithDb<'db> for SyntaxNode<'db> {
112    type Db = dyn Database;
113    fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db Self::Db) -> std::fmt::Result {
114        self.data.fmt(f, db)
115    }
116}
117
118impl<'db> SyntaxNode<'db> {
119    /// Get the offset of this syntax node from the beginning of the file.
120    pub fn offset(self, db: &'db dyn Database) -> TextOffset {
121        self.data.offset(db)
122    }
123
124    /// Get the parent syntax node, if any.
125    pub fn parent(self, db: &'db dyn Database) -> Option<SyntaxNode<'db>> {
126        match self.data.id(db) {
127            SyntaxNodeId::Child { parent, .. } => Some(*parent),
128            SyntaxNodeId::Root(_) => None,
129        }
130    }
131
132    /// Get the grandparent syntax node, if any.
133    /// This uses a cached parent when available, avoiding some database lookups.
134    pub fn grandparent(self, db: &'db dyn Database) -> Option<SyntaxNode<'db>> {
135        self.parent?.parent(db)
136    }
137
138    /// Check if this syntax node is the root of the syntax tree.
139    pub fn is_root(self) -> bool {
140        self.parent.is_none()
141    }
142
143    /// Get the stable pointer for this syntax node.
144    pub fn stable_ptr(self, _db: &'db dyn Database) -> SyntaxStablePtrId<'db> {
145        SyntaxStablePtrId(self)
146    }
147
148    pub fn raw_id(&self, db: &'db dyn Database) -> &'db SyntaxNodeId<'db> {
149        self.data.id(db)
150    }
151
152    /// Get the key fields of this syntax node. These define the unique identifier of the node.
153    pub fn key_fields(self, db: &'db dyn Database) -> &'db [GreenId<'db>] {
154        match self.data.id(db) {
155            SyntaxNodeId::Child { key_fields, .. } => key_fields,
156            SyntaxNodeId::Root(_) => &[],
157        }
158    }
159
160    /// Returns the file id of the file containing this node.
161    pub fn file_id(&self, db: &'db dyn Database) -> FileId<'db> {
162        // Use cached parent if available to avoid database lookup
163        if let Some(parent_data) = self.parent {
164            return match parent_data.id(db) {
165                SyntaxNodeId::Child { parent, .. } => parent.file_id(db),
166                SyntaxNodeId::Root(file_id) => *file_id,
167            };
168        }
169        match self.data.id(db) {
170            SyntaxNodeId::Root(file_id) => *file_id,
171            SyntaxNodeId::Child { .. } => {
172                unreachable!("Parent already checked and found to not exist.")
173            }
174        }
175    }
176
177    /// Returns the stable pointer of the `n`th parent of this stable pointer.
178    /// n = 0: returns itself.
179    /// n = 1: return the parent.
180    /// n = 2: return the grand parent.
181    /// And so on...
182    /// Assumes that the `n`th parent exists. Panics otherwise.
183    pub fn nth_parent<'r: 'db>(&self, db: &'r dyn Database, mut n: usize) -> SyntaxNode<'db> {
184        let mut result = Some(*self);
185        while let Some(p) = result
186            && n > 1
187        {
188            result = p.grandparent(db);
189            n -= 2;
190        }
191        if let Some(p) = result
192            && n == 1
193        {
194            result = p.parent(db);
195        }
196        result.unwrap_or_else(|| {
197            panic!(
198                "N'th parent did not exist. File {} Offset {:?}",
199                self.file_id(db).long(db).full_path(db),
200                self.offset(db)
201            )
202        })
203    }
204}
205
206/// Create a new syntax node.
207pub fn new_syntax_node<'db>(
208    db: &'db dyn Database,
209    green: GreenId<'db>,
210    offset: TextOffset,
211    id: SyntaxNodeId<'db>,
212    kind: SyntaxKind,
213) -> SyntaxNode<'db> {
214    let (parent, parent_kind) = match &id {
215        SyntaxNodeId::Child { parent, .. } => (Some(parent.data), Some(parent.kind)),
216        SyntaxNodeId::Root(_) => (None, None),
217    };
218    let data = SyntaxNodeData::new(db, green, offset, id);
219    SyntaxNode { data, parent, kind, parent_kind }
220}
221
222/// A tracked function to prevent root duplication.
223#[salsa::tracked]
224fn new_root_node<'db>(
225    db: &'db dyn Database,
226    file_id: FileId<'db>,
227    green: GreenId<'db>,
228    offset: TextOffset,
229) -> SyntaxNode<'db> {
230    let kind = green.long(db).kind;
231    new_syntax_node(db, green, offset, SyntaxNodeId::Root(file_id), kind)
232}
233
234// Construction methods
235impl<'a> SyntaxNode<'a> {
236    /// Create a new root syntax node.
237    pub fn new_root(db: &'a dyn Database, file_id: FileId<'a>, green: GreenId<'a>) -> Self {
238        new_root_node(db, file_id, green, TextOffset::START)
239    }
240
241    /// Create a new root syntax node with a custom initial offset.
242    pub fn new_root_with_offset(
243        db: &'a dyn Database,
244        file_id: FileId<'a>,
245        green: GreenId<'a>,
246        initial_offset: Option<TextOffset>,
247    ) -> Self {
248        new_root_node(db, file_id, green, initial_offset.unwrap_or_default())
249    }
250
251    // Basic accessors
252
253    /// Get the width of this syntax node.
254    pub fn width(&self, db: &dyn Database) -> TextWidth {
255        self.green_node(db).width(db)
256    }
257
258    /// Get the syntax kind of this node.
259    pub fn kind(&self, _db: &dyn Database) -> SyntaxKind {
260        self.kind
261    }
262
263    /// Get the span of this syntax node.
264    pub fn span(&self, db: &dyn Database) -> TextSpan {
265        TextSpan::new_with_width(self.offset(db), self.width(db))
266    }
267
268    /// Returns the text of the token if this node is a token.
269    pub fn text(&self, db: &'a dyn Database) -> Option<SmolStrId<'a>> {
270        match &self.green_node(db).details {
271            green::GreenNodeDetails::Token(text) => Some(*text),
272            green::GreenNodeDetails::Node { .. } => None,
273        }
274    }
275
276    /// Returns the green node of the syntax node.
277    pub fn green_node(&self, db: &'a dyn Database) -> &'a GreenNode<'a> {
278        self.data.green(db).long(db)
279    }
280
281    /// Returns the span of the syntax node without trivia.
282    pub fn span_without_trivia(&self, db: &dyn Database) -> TextSpan {
283        let green_node = self.green_node(db);
284        let (leading, trailing) = both_trivia_width(db, green_node);
285        let offset = self.offset(db);
286        let start = offset.add_width(leading);
287        let end = offset.add_width(green_node.width(db)).sub_width(trailing);
288        TextSpan::new(start, end)
289    }
290
291    /// Gets the inner token from a terminal SyntaxNode. If the given node is not a terminal,
292    /// returns None.
293    pub fn get_terminal_token(&'a self, db: &'a dyn Database) -> Option<SyntaxNode<'a>> {
294        let green_node = self.green_node(db);
295        require(green_node.kind.is_terminal())?;
296        // At this point we know we should have a second child which is the token.
297        self.get_children(db).get(1).copied()
298    }
299
300    // Children and tree navigation
301
302    /// Gets the children syntax nodes of the current node.
303    pub fn get_children(&self, db: &'a dyn Database) -> &'a [SyntaxNode<'a>] {
304        db.get_children(*self)
305    }
306
307    /// Implementation of [SyntaxNode::get_children].
308    pub(crate) fn get_children_impl(&self, db: &'a dyn Database) -> Vec<SyntaxNode<'a>> {
309        let mut offset = self.offset(db);
310        let self_green = self.green_node(db);
311        let children = self_green.children();
312        let mut res: Vec<SyntaxNode<'_>> = Vec::with_capacity(children.len());
313        let mut key_map = VecMap::<_, usize>::new();
314        for green_id in children {
315            let green = green_id.long(db);
316            let width = green.width(db);
317            let kind = green.kind;
318            let rng = key_fields::key_fields_range(kind);
319            let key_fields: &'a [GreenId<'a>] = &green.children()[rng];
320            let key_count = key_map.entry((kind, key_fields)).or_insert(0);
321            let index = *key_count;
322            *key_count += 1;
323            // Create the SyntaxNode view for the child.
324            res.push(new_syntax_node(
325                db,
326                *green_id,
327                offset,
328                SyntaxNodeId::Child { parent: *self, index, key_fields: Box::from(key_fields) },
329                kind,
330            ));
331
332            offset = offset.add_width(width);
333        }
334        res
335    }
336
337    // Text and span utilities
338
339    /// Returns the start of the span of the syntax node without trivia.
340    pub fn span_start_without_trivia(&self, db: &dyn Database) -> TextOffset {
341        let green_node = self.green_node(db);
342        let leading = leading_trivia_width(db, green_node);
343        self.offset(db).add_width(leading)
344    }
345
346    /// Returns the end of the span of the syntax node without trivia.
347    pub fn span_end_without_trivia(&self, db: &dyn Database) -> TextOffset {
348        let green_node = self.green_node(db);
349        let trailing = trailing_trivia_width(db, green_node);
350        self.offset(db).add_width(green_node.width(db)).sub_width(trailing)
351    }
352
353    /// Lookups a syntax node using an offset.
354    pub fn lookup_offset(&self, db: &'a dyn Database, offset: TextOffset) -> SyntaxNode<'a> {
355        for child in self.get_children(db).iter() {
356            if child.offset(db).add_width(child.width(db)) > offset {
357                return child.lookup_offset(db, offset);
358            }
359        }
360        *self
361    }
362
363    /// Lookups a syntax node using a position.
364    pub fn lookup_position(&self, db: &'a dyn Database, position: TextPosition) -> SyntaxNode<'a> {
365        match position.offset_in_file(db, self.stable_ptr(db).file_id(db)) {
366            Some(offset) => self.lookup_offset(db, offset),
367            None => *self,
368        }
369    }
370
371    /// Returns all the text under the syntax node.
372    pub fn get_text(&self, db: &'a dyn Database) -> &'a str {
373        // A `None` return from reading the file content is only expected in the case of an IO
374        // error. Since a SyntaxNode exists and is being processed, we should have already
375        // successfully accessed this file earlier, therefore it should never fail.
376        let file_content =
377            db.file_content(self.stable_ptr(db).file_id(db)).expect("Failed to read file content");
378
379        self.span(db).take(file_content)
380    }
381
382    /// Returns all the text under the syntax node.
383    /// It traverses all the syntax tree of the node, but ignores functions and modules.
384    /// We ignore those, because if there's some inner functions or modules, we don't want to get
385    /// raw text of them. Comments inside them refer themselves directly, not this SyntaxNode.
386    pub fn get_text_without_inner_commentable_children(&self, db: &dyn Database) -> String {
387        let mut buffer = String::new();
388
389        match &self.green_node(db).details {
390            green::GreenNodeDetails::Token(text) => buffer.push_str(text.long(db)),
391            green::GreenNodeDetails::Node { .. } => {
392                for child in self.get_children(db).iter() {
393                    let kind = child.kind(db);
394
395                    // Checks all the items that the inner comment can be bubbled to (implementation
396                    // function is also a FunctionWithBody).
397                    if !matches!(
398                        kind,
399                        SyntaxKind::FunctionWithBody
400                            | SyntaxKind::ItemModule
401                            | SyntaxKind::TraitItemFunction
402                    ) {
403                        buffer.push_str(&SyntaxNode::get_text_without_inner_commentable_children(
404                            child, db,
405                        ));
406                    }
407                }
408            }
409        }
410        buffer
411    }
412
413    /// Returns all the text of the item without comments trivia.
414    /// It traverses all the syntax tree of the node.
415    pub fn get_text_without_all_comment_trivia(&self, db: &dyn Database) -> String {
416        let mut buffer = String::new();
417
418        match &self.green_node(db).details {
419            green::GreenNodeDetails::Token(text) => buffer.push_str(text.long(db)),
420            green::GreenNodeDetails::Node { .. } => {
421                for child in self.get_children(db).iter() {
422                    if let Some(trivia) = ast::Trivia::cast(db, *child) {
423                        trivia.elements(db).for_each(|element| {
424                            if !matches!(
425                                element,
426                                ast::Trivium::SingleLineComment(_)
427                                    | ast::Trivium::SingleLineDocComment(_)
428                                    | ast::Trivium::SingleLineInnerComment(_)
429                            ) {
430                                buffer.push_str(
431                                    &element
432                                        .as_syntax_node()
433                                        .get_text_without_all_comment_trivia(db),
434                                );
435                            }
436                        });
437                    } else {
438                        buffer
439                            .push_str(&SyntaxNode::get_text_without_all_comment_trivia(child, db));
440                    }
441                }
442            }
443        }
444        buffer
445    }
446
447    /// Returns all the text under the syntax node, without the outmost trivia (the leading trivia
448    /// of the first token and the trailing trivia of the last token).
449    ///
450    /// Note that this traverses the syntax tree, and generates a new string, so use responsibly.
451    pub fn get_text_without_trivia(self, db: &'a dyn Database) -> SmolStrId<'a> {
452        let file_content =
453            db.file_content(self.stable_ptr(db).file_id(db)).expect("Failed to read file content");
454        SmolStrId::from(db, self.span_without_trivia(db).take(file_content))
455    }
456
457    /// Returns the text under the syntax node, according to the given span.
458    ///
459    /// `span` is assumed to be contained within the span of self.
460    ///
461    /// Note that this traverses the syntax tree, and generates a new string, so use responsibly.
462    pub fn get_text_of_span(self, db: &'a dyn Database, span: TextSpan) -> &'a str {
463        assert!(self.span(db).contains(span));
464        let file_content =
465            db.file_content(self.stable_ptr(db).file_id(db)).expect("Failed to read file content");
466        span.take(file_content)
467    }
468
469    // Tree traversal iterators
470
471    /// Traverse the subtree rooted at the current node (including the current node) in preorder.
472    ///
473    /// This is a shortcut for [`Self::preorder`] paired with filtering for [`WalkEvent::Enter`]
474    /// events only.
475    pub fn descendants(&self, db: &'a dyn Database) -> impl Iterator<Item = SyntaxNode<'a>> + 'a {
476        self.preorder(db).filter_map(|event| match event {
477            WalkEvent::Enter(node) => Some(node),
478            WalkEvent::Leave(_) => None,
479        })
480    }
481
482    /// Traverse the subtree rooted at the current node (including the current node) in preorder,
483    /// excluding tokens.
484    pub fn preorder(&self, db: &'a dyn Database) -> Preorder<'a> {
485        Preorder::new(*self, db)
486    }
487
488    /// Gets all the leaves of the SyntaxTree, where the self node is the root of a tree.
489    pub fn tokens(&self, db: &'a dyn Database) -> impl Iterator<Item = Self> + 'a {
490        self.preorder(db).filter_map(|event| match event {
491            WalkEvent::Enter(node) if node.green_node(db).kind.is_terminal() => Some(node),
492            _ => None,
493        })
494    }
495
496    /// Mirror of [`TypedSyntaxNode::cast`].
497    pub fn cast<T: TypedSyntaxNode<'a>>(self, db: &'a dyn Database) -> Option<T> {
498        T::cast(db, self)
499    }
500
501    // Ancestor queries
502
503    /// Creates an iterator that yields ancestors of this syntax node.
504    pub fn ancestors(&self, db: &'a dyn Database) -> impl Iterator<Item = SyntaxNode<'a>> + 'a {
505        // We aren't reusing `ancestors_with_self` here to avoid cloning this node.
506        std::iter::successors(self.parent(db), |n| n.parent(db))
507    }
508
509    /// Creates an iterator that yields this syntax node and walks up its ancestors.
510    pub fn ancestors_with_self(
511        &self,
512        db: &'a dyn Database,
513    ) -> impl Iterator<Item = SyntaxNode<'a>> + 'a {
514        std::iter::successors(Some(*self), |n| n.parent(db))
515    }
516
517    /// Checks whether this syntax node is strictly above the given syntax node in the syntax tree.
518    pub fn is_ancestor(&self, db: &dyn Database, node: &SyntaxNode<'_>) -> bool {
519        node.ancestors(db).any(|n| n == *self)
520    }
521
522    /// Checks whether this syntax node is strictly under the given syntax node in the syntax tree.
523    pub fn is_descendant(&self, db: &dyn Database, node: &SyntaxNode<'_>) -> bool {
524        node.is_ancestor(db, self)
525    }
526
527    /// Checks whether this syntax node is or is above the given syntax node in the syntax tree.
528    pub fn is_ancestor_or_self(&self, db: &dyn Database, node: &SyntaxNode<'_>) -> bool {
529        node.ancestors_with_self(db).any(|n| n == *self)
530    }
531
532    /// Checks whether this syntax node is or is under the given syntax node in the syntax tree.
533    pub fn is_descendant_or_self(&self, db: &dyn Database, node: &SyntaxNode<'_>) -> bool {
534        node.is_ancestor_or_self(db, self)
535    }
536
537    // Specific ancestor/parent queries
538
539    /// Finds the first ancestor of a given kind.
540    pub fn ancestor_of_kind(
541        &self,
542        db: &'a dyn Database,
543        kind: SyntaxKind,
544    ) -> Option<SyntaxNode<'a>> {
545        self.ancestors(db).find(|node| node.kind(db) == kind)
546    }
547
548    /// Finds the first ancestor of a given kind and returns it in typed form.
549    pub fn ancestor_of_type<T: TypedSyntaxNode<'a>>(&self, db: &'a dyn Database) -> Option<T> {
550        self.ancestors(db).find_map(|node| T::cast(db, node))
551    }
552
553    /// Finds the parent of a given kind.
554    pub fn parent_of_kind(&self, db: &'a dyn Database, kind: SyntaxKind) -> Option<SyntaxNode<'a>> {
555        self.parent(db).filter(|node| node.kind(db) == kind)
556    }
557
558    /// Finds the parent of a given kind and returns it in typed form.
559    pub fn parent_of_type<T: TypedSyntaxNode<'a>>(&self, db: &'a dyn Database) -> Option<T> {
560        self.parent(db).and_then(|node| T::cast(db, node))
561    }
562
563    /// Finds the first parent of one of the kinds.
564    pub fn ancestor_of_kinds(
565        &self,
566        db: &'a dyn Database,
567        kinds: &[SyntaxKind],
568    ) -> Option<SyntaxNode<'a>> {
569        self.ancestors(db).find(|node| kinds.contains(&node.kind(db)))
570    }
571
572    /// Gets the kind of the given node's parent if it exists.
573    pub fn parent_kind(&self, _db: &dyn Database) -> Option<SyntaxKind> {
574        self.parent_kind
575    }
576
577    /// Gets the kind of the given node's grandparent if it exists.
578    pub fn grandparent_kind(&self, db: &dyn Database) -> Option<SyntaxKind> {
579        self.parent(db)?.parent_kind(db)
580    }
581
582    /// Gets the kind of the given node's grandgrandparent if it exists.
583    pub fn grandgrandparent_kind(&self, db: &dyn Database) -> Option<SyntaxKind> {
584        self.grandparent(db)?.parent_kind(db)
585    }
586}
587
588/// Trait for the typed view of the syntax tree. All the internal node implementations are under
589/// the ast module.
590pub trait TypedSyntaxNode<'a>: Sized {
591    /// The relevant SyntaxKind. None for enums.
592    const OPTIONAL_KIND: Option<SyntaxKind>;
593    type StablePtr: TypedStablePtr<'a>;
594    type Green;
595    fn missing(db: &'a dyn Database) -> Self::Green;
596    fn from_syntax_node(db: &'a dyn Database, node: SyntaxNode<'a>) -> Self;
597    fn cast(db: &'a dyn Database, node: SyntaxNode<'a>) -> Option<Self>;
598    fn as_syntax_node(&self) -> SyntaxNode<'a>;
599    fn stable_ptr(&self, db: &'a dyn Database) -> Self::StablePtr;
600}
601
602pub trait Token<'a>: TypedSyntaxNode<'a> {
603    fn new_green(db: &'a dyn Database, text: SmolStrId<'a>) -> Self::Green;
604    fn text(&self, db: &'a dyn Database) -> SmolStrId<'a>;
605}
606
607pub trait Terminal<'a>: TypedSyntaxNode<'a> {
608    const KIND: SyntaxKind;
609    type TokenType: Token<'a>;
610    fn new_green(
611        db: &'a dyn Database,
612        leading_trivia: TriviaGreen<'a>,
613        token: <<Self as Terminal<'a>>::TokenType as TypedSyntaxNode<'a>>::Green,
614        trailing_trivia: TriviaGreen<'a>,
615    ) -> <Self as TypedSyntaxNode<'a>>::Green;
616    /// Returns the text of the token of this terminal (excluding the trivia).
617    fn text(&self, db: &'a dyn Database) -> SmolStrId<'a>;
618    /// Casts a syntax node to this terminal type's token and then walks up to return the terminal.
619    fn cast_token(db: &'a dyn Database, node: SyntaxNode<'a>) -> Option<Self> {
620        if node.kind(db) == Self::TokenType::OPTIONAL_KIND? {
621            Some(Self::from_syntax_node(db, node.parent(db)?))
622        } else {
623            None
624        }
625    }
626}
627
628/// Trait for stable pointers to syntax nodes.
629pub trait TypedStablePtr<'a> {
630    type SyntaxNode: TypedSyntaxNode<'a>;
631    /// Returns the syntax node pointed to by this stable pointer.
632    fn lookup(&self, db: &'a dyn Database) -> Self::SyntaxNode;
633    /// Returns the untyped stable pointer.
634    fn untyped(self) -> SyntaxStablePtrId<'a>;
635}
636
637/// Returns the width of the leading trivia of the given green node.
638fn leading_trivia_width<'a>(db: &'a dyn Database, green: &GreenNode<'a>) -> TextWidth {
639    match &green.details {
640        green::GreenNodeDetails::Token(_) => TextWidth::default(),
641        green::GreenNodeDetails::Node { children, width } => {
642            if *width == TextWidth::default() {
643                return TextWidth::default();
644            }
645            if green.kind.is_terminal() {
646                return children[0].long(db).width(db);
647            }
648            let non_empty = find_non_empty_child(db, &mut children.iter())
649                .expect("Parent width non-empty - one of the children should be non-empty");
650            leading_trivia_width(db, non_empty)
651        }
652    }
653}
654
655/// Returns the width of the trailing trivia of the given green node.
656fn trailing_trivia_width<'a>(db: &'a dyn Database, green: &GreenNode<'a>) -> TextWidth {
657    match &green.details {
658        green::GreenNodeDetails::Token(_) => TextWidth::default(),
659        green::GreenNodeDetails::Node { children, width } => {
660            if *width == TextWidth::default() {
661                return TextWidth::default();
662            }
663            if green.kind.is_terminal() {
664                return children[2].long(db).width(db);
665            }
666            let non_empty = find_non_empty_child(db, &mut children.iter().rev())
667                .expect("Parent width non-empty - one of the children should be non-empty");
668            trailing_trivia_width(db, non_empty)
669        }
670    }
671}
672
673/// Returns the width of the leading and trailing trivia of the given green node.
674fn both_trivia_width<'a>(db: &'a dyn Database, green: &GreenNode<'a>) -> (TextWidth, TextWidth) {
675    match &green.details {
676        green::GreenNodeDetails::Token(_) => (TextWidth::default(), TextWidth::default()),
677        green::GreenNodeDetails::Node { children, width } => {
678            if *width == TextWidth::default() {
679                return (TextWidth::default(), TextWidth::default());
680            }
681            if green.kind.is_terminal() {
682                return (children[0].long(db).width(db), children[2].long(db).width(db));
683            }
684            let mut iter = children.iter();
685            let first_non_empty = find_non_empty_child(db, &mut iter)
686                .expect("Parent width non-empty - one of the children should be non-empty");
687            if let Some(last_non_empty) = find_non_empty_child(db, &mut iter.rev()) {
688                (
689                    leading_trivia_width(db, first_non_empty),
690                    trailing_trivia_width(db, last_non_empty),
691                )
692            } else {
693                both_trivia_width(db, first_non_empty)
694            }
695        }
696    }
697}
698
699/// Finds the first non-empty child in the given iterator.
700fn find_non_empty_child<'a>(
701    db: &'a dyn Database,
702    child_iter: &mut impl Iterator<Item = &'a GreenId<'a>>,
703) -> Option<&'a GreenNode<'a>> {
704    child_iter.find_map(|child| {
705        let child = child.long(db);
706        (child.width(db) != TextWidth::default()).then_some(child)
707    })
708}