Skip to main content

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