mago_syntax/ast/
sequence.rs1use mago_database::file::FileId;
11use mago_span::HasSpan;
12use mago_span::Position;
13use mago_span::Span;
14use mago_syntax_core::ast::TokenSeparatedSequence as CoreTokenSeparatedSequence;
15
16use crate::token::Token;
17
18pub use mago_syntax_core::ast::Sequence;
19
20pub type TokenSeparatedSequence<'arena, T> = CoreTokenSeparatedSequence<'arena, T, Token<'arena>>;
22
23pub trait TokenSeparatedSequenceExt<'arena, T: HasSpan> {
27 fn first_span(&self, file_id: FileId) -> Option<Span>;
28 fn last_span(&self, file_id: FileId) -> Option<Span>;
29 fn span(&self, file_id: FileId, from: Position) -> Span;
30}
31
32impl<'arena, T: HasSpan> TokenSeparatedSequenceExt<'arena, T> for TokenSeparatedSequence<'arena, T> {
33 #[inline]
34 fn first_span(&self, file_id: FileId) -> Option<Span> {
35 match (self.tokens.first(), self.nodes.first()) {
36 (Some(token), Some(node)) => {
37 let token_end = token.start.offset + token.value.len() as u32;
38 if token_end <= node.span().start.offset { Some(token.span_for(file_id)) } else { Some(node.span()) }
39 }
40 (Some(token), None) => Some(token.span_for(file_id)),
41 (None, Some(node)) => Some(node.span()),
42 (None, None) => None,
43 }
44 }
45
46 #[inline]
47 fn last_span(&self, file_id: FileId) -> Option<Span> {
48 match (self.tokens.last(), self.nodes.last()) {
49 (Some(token), Some(node)) => {
50 if token.start.offset >= node.span().end.offset {
51 Some(token.span_for(file_id))
52 } else {
53 Some(node.span())
54 }
55 }
56 (Some(token), None) => Some(token.span_for(file_id)),
57 (None, Some(node)) => Some(node.span()),
58 (None, None) => None,
59 }
60 }
61
62 #[inline]
63 fn span(&self, file_id: FileId, from: Position) -> Span {
64 match (self.first_span(file_id), self.last_span(file_id)) {
65 (Some(first), Some(last)) => Span::new(file_id, first.start, last.end),
66 _ => Span::new(file_id, from, from),
67 }
68 }
69}