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