//! A typed layer over the untyped syntax tree. //! //! The AST is rooted in the [`MarkupNode`]. use std::num::NonZeroUsize; use std::ops::Deref; use super::{NodeData, NodeKind, RawFields, Span, SyntaxNode, Unit}; use crate::util::EcoString; /// A typed AST node. pub trait TypedNode: Sized { /// Convert a node into its typed variant. fn from_untyped(node: &SyntaxNode) -> Option; /// A reference to the underlying syntax node. fn as_untyped(&self) -> &SyntaxNode; /// The source code location. fn span(&self) -> Span { self.as_untyped().span() } } macro_rules! node { ($(#[$attr:meta])* $name:ident) => { node!{ $(#[$attr])* $name: NodeKind::$name { .. } } }; ($(#[$attr:meta])* $name:ident: $variants:pat) => { #[derive(Debug, Clone, PartialEq, Hash)] #[repr(transparent)] $(#[$attr])* pub struct $name(SyntaxNode); impl TypedNode for $name { fn from_untyped(node: &SyntaxNode) -> Option { if matches!(node.kind(), $variants) { Some(Self(node.clone())) } else { None } } fn as_untyped(&self) -> &SyntaxNode { &self.0 } } }; } node! { /// The syntactical root capable of representing a full parsed document. Markup } impl Markup { /// The children. pub fn children(&self) -> impl DoubleEndedIterator + '_ { let mut was_stmt = false; self.0 .children() .filter(move |node| { // Ignore linebreak directly after statements without semicolons. let kind = node.kind(); let keep = !was_stmt || !matches!(kind, NodeKind::Space { newlines: 1 }); was_stmt = kind.is_stmt(); keep }) .filter_map(SyntaxNode::cast) } } /// A single piece of markup. #[derive(Debug, Clone, PartialEq)] pub enum MarkupNode { /// Whitespace. Space(Space), /// A forced line break: `\`. Linebreak(Linebreak), /// Plain text without markup. Text(Text), /// An escape sequence: `\#`, `\u{1F5FA}`. Escape(Escape), /// A shorthand for a unicode codepoint. For example, `~` for non-breaking /// space or `-?` for a soft hyphen. Shorthand(Shorthand), /// A smart quote: `'` or `"`. SmartQuote(SmartQuote), /// Strong content: `*Strong*`. Strong(Strong), /// Emphasized content: `_Emphasized_`. Emph(Emph), /// Raw text with optional syntax highlighting: `` `...` ``. Raw(Raw), /// A hyperlink: `https://typst.org`. Link(Link), /// A label: `