use proc_macro::{Span, TokenStream, TokenTree}; #[derive(Debug)] pub enum Markup { Block(Block), Literal { content: String, span: Span, }, Symbol { symbol: TokenStream, }, Splice { expr: TokenStream, outer_span: Span, }, Element { name: TokenStream, attrs: Attrs, body: ElementBody, }, Let { at_span: Span, tokens: TokenStream, }, Special { segments: Vec<Special>, }, Match { at_span: Span, head: TokenStream, arms: Vec<MatchArm>, arms_span: Span, }, } impl Markup { pub fn span(&self) -> Span { match *self { Markup::Block(ref block) => block.span(), Markup::Literal { span, .. } => span, Markup::Symbol { ref symbol } => span_tokens(symbol.clone()), Markup::Splice { outer_span, .. } => outer_span, Markup::Element { ref name, ref body, .. } => { let name_span = span_tokens(name.clone()); name_span.join(body.span()).unwrap_or(name_span) }, Markup::Let { at_span, ref tokens } => { at_span.join(span_tokens(tokens.clone())).unwrap_or(at_span) }, Markup::Special { ref segments } => { join_spans(segments.iter().map(|segment| segment.span())) }, Markup::Match { at_span, arms_span, .. } => { at_span.join(arms_span).unwrap_or(at_span) }, } } } pub type Attrs = Vec<Attr>; #[derive(Debug)] pub enum Attr { Class { dot_span: Span, name: Markup, toggler: Option<Toggler>, }, Id { hash_span: Span, name: Markup, }, Attribute { attribute: Attribute, }, } impl Attr { pub fn span(&self) -> Span { match *self { Attr::Class { dot_span, ref name, ref toggler } => { let name_span = name.span(); let dot_name_span = dot_span.join(name_span).unwrap_or(dot_span); if let Some(toggler) = toggler { dot_name_span.join(toggler.cond_span).unwrap_or(name_span) } else { dot_name_span } }, Attr::Id { hash_span, ref name } => { let name_span = name.span(); hash_span.join(name_span).unwrap_or(hash_span) }, Attr::Attribute { ref attribute } => attribute.span(), } } } #[derive(Debug)] pub enum ElementBody { Void { semi_span: Span }, Block { block: Block }, } impl ElementBody { pub fn span(&self) -> Span { match *self { ElementBody::Void { semi_span } => semi_span, ElementBody::Block { ref block } => block.span(), } } } #[derive(Debug)] pub struct Block { pub markups: Vec<Markup>, pub outer_span: Span, } impl Block { pub fn span(&self) -> Span { self.outer_span } } #[derive(Debug)] pub struct Special { pub at_span: Span, pub head: TokenStream, pub body: Block, } impl Special { pub fn span(&self) -> Span { let body_span = self.body.span(); self.at_span.join(body_span).unwrap_or(self.at_span) } } #[derive(Debug)] pub struct Attribute { pub name: TokenStream, pub attr_type: AttrType, } impl Attribute { fn span(&self) -> Span { let name_span = span_tokens(self.name.clone()); if let Some(attr_type_span) = self.attr_type.span() { name_span.join(attr_type_span).unwrap_or(name_span) } else { name_span } } } #[derive(Debug)] pub enum AttrType { Normal { value: Markup, }, Empty { toggler: Option<Toggler>, }, } impl AttrType { fn span(&self) -> Option<Span> { match *self { AttrType::Normal { ref value } => Some(value.span()), AttrType::Empty { ref toggler } => toggler.as_ref().map(|toggler| toggler.span()), } } } #[derive(Debug)] pub struct Toggler { pub cond: TokenStream, pub cond_span: Span, } impl Toggler { fn span(&self) -> Span { self.cond_span } } #[derive(Debug)] pub struct MatchArm { pub head: TokenStream, pub body: Block, } pub fn span_tokens<I: IntoIterator<Item=TokenTree>>(tokens: I) -> Span { join_spans(tokens.into_iter().map(|token| token.span())) } pub fn join_spans<I: IntoIterator<Item=Span>>(spans: I) -> Span { let mut iter = spans.into_iter(); let mut span = match iter.next() { Some(span) => span, None => return Span::call_site(), }; for new_span in iter { if let Some(joined) = span.join(new_span) { span = joined; } } span }