Make use of SpanRange instead of Span
This commit is contained in:
parent
ef120ab2d0
commit
7d16af9a06
3 changed files with 82 additions and 89 deletions
maud_macros/src
|
@ -1,18 +1,19 @@
|
|||
use proc_macro2::{Span, TokenStream, TokenTree};
|
||||
use proc_macro2::{TokenStream, TokenTree};
|
||||
use proc_macro_error::SpanRange;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Markup {
|
||||
Block(Block),
|
||||
Literal {
|
||||
content: String,
|
||||
span: Span,
|
||||
span: SpanRange,
|
||||
},
|
||||
Symbol {
|
||||
symbol: TokenStream,
|
||||
},
|
||||
Splice {
|
||||
expr: TokenStream,
|
||||
outer_span: Span,
|
||||
outer_span: SpanRange,
|
||||
},
|
||||
Element {
|
||||
name: TokenStream,
|
||||
|
@ -20,22 +21,22 @@ pub enum Markup {
|
|||
body: ElementBody,
|
||||
},
|
||||
Let {
|
||||
at_span: Span,
|
||||
at_span: SpanRange,
|
||||
tokens: TokenStream,
|
||||
},
|
||||
Special {
|
||||
segments: Vec<Special>,
|
||||
},
|
||||
Match {
|
||||
at_span: Span,
|
||||
at_span: SpanRange,
|
||||
head: TokenStream,
|
||||
arms: Vec<MatchArm>,
|
||||
arms_span: Span,
|
||||
arms_span: SpanRange,
|
||||
},
|
||||
}
|
||||
|
||||
impl Markup {
|
||||
pub fn span(&self) -> Span {
|
||||
pub fn span(&self) -> SpanRange {
|
||||
match *self {
|
||||
Markup::Block(ref block) => block.span(),
|
||||
Markup::Literal { span, .. } => span,
|
||||
|
@ -43,16 +44,16 @@ impl Markup {
|
|||
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)
|
||||
name_span.join_range(body.span())
|
||||
},
|
||||
Markup::Let { at_span, ref tokens } => {
|
||||
at_span.join(span_tokens(tokens.clone())).unwrap_or(at_span)
|
||||
at_span.join_range(span_tokens(tokens.clone()))
|
||||
},
|
||||
Markup::Special { ref segments } => {
|
||||
join_spans(segments.iter().map(Special::span))
|
||||
join_ranges(segments.iter().map(Special::span))
|
||||
},
|
||||
Markup::Match { at_span, arms_span, .. } => {
|
||||
at_span.join(arms_span).unwrap_or(at_span)
|
||||
at_span.join_range(arms_span)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -63,12 +64,12 @@ pub type Attrs = Vec<Attr>;
|
|||
#[derive(Debug)]
|
||||
pub enum Attr {
|
||||
Class {
|
||||
dot_span: Span,
|
||||
dot_span: SpanRange,
|
||||
name: Markup,
|
||||
toggler: Option<Toggler>,
|
||||
},
|
||||
Id {
|
||||
hash_span: Span,
|
||||
hash_span: SpanRange,
|
||||
name: Markup,
|
||||
},
|
||||
Attribute {
|
||||
|
@ -77,20 +78,20 @@ pub enum Attr {
|
|||
}
|
||||
|
||||
impl Attr {
|
||||
pub fn span(&self) -> Span {
|
||||
pub fn span(&self) -> SpanRange {
|
||||
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);
|
||||
let dot_name_span = dot_span.join_range(name_span);
|
||||
if let Some(toggler) = toggler {
|
||||
dot_name_span.join(toggler.cond_span).unwrap_or(name_span)
|
||||
dot_name_span.join_range(toggler.cond_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)
|
||||
hash_span.join_range(name_span)
|
||||
},
|
||||
Attr::Attribute { ref attribute } => attribute.span(),
|
||||
}
|
||||
|
@ -99,12 +100,12 @@ impl Attr {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub enum ElementBody {
|
||||
Void { semi_span: Span },
|
||||
Void { semi_span: SpanRange },
|
||||
Block { block: Block },
|
||||
}
|
||||
|
||||
impl ElementBody {
|
||||
pub fn span(&self) -> Span {
|
||||
pub fn span(&self) -> SpanRange {
|
||||
match *self {
|
||||
ElementBody::Void { semi_span } => semi_span,
|
||||
ElementBody::Block { ref block } => block.span(),
|
||||
|
@ -115,26 +116,26 @@ impl ElementBody {
|
|||
#[derive(Debug)]
|
||||
pub struct Block {
|
||||
pub markups: Vec<Markup>,
|
||||
pub outer_span: Span,
|
||||
pub outer_span: SpanRange,
|
||||
}
|
||||
|
||||
impl Block {
|
||||
pub fn span(&self) -> Span {
|
||||
pub fn span(&self) -> SpanRange {
|
||||
self.outer_span
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Special {
|
||||
pub at_span: Span,
|
||||
pub at_span: SpanRange,
|
||||
pub head: TokenStream,
|
||||
pub body: Block,
|
||||
}
|
||||
|
||||
impl Special {
|
||||
pub fn span(&self) -> Span {
|
||||
pub fn span(&self) -> SpanRange {
|
||||
let body_span = self.body.span();
|
||||
self.at_span.join(body_span).unwrap_or(self.at_span)
|
||||
self.at_span.join_range(body_span)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,10 +146,10 @@ pub struct Attribute {
|
|||
}
|
||||
|
||||
impl Attribute {
|
||||
fn span(&self) -> Span {
|
||||
fn span(&self) -> SpanRange {
|
||||
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)
|
||||
name_span.join_range(attr_type_span)
|
||||
} else {
|
||||
name_span
|
||||
}
|
||||
|
@ -166,7 +167,7 @@ pub enum AttrType {
|
|||
}
|
||||
|
||||
impl AttrType {
|
||||
fn span(&self) -> Option<Span> {
|
||||
fn span(&self) -> Option<SpanRange> {
|
||||
match *self {
|
||||
AttrType::Normal { ref value } => Some(value.span()),
|
||||
AttrType::Empty { ref toggler } => toggler.as_ref().map(Toggler::span),
|
||||
|
@ -177,11 +178,11 @@ impl AttrType {
|
|||
#[derive(Debug)]
|
||||
pub struct Toggler {
|
||||
pub cond: TokenStream,
|
||||
pub cond_span: Span,
|
||||
pub cond_span: SpanRange,
|
||||
}
|
||||
|
||||
impl Toggler {
|
||||
fn span(&self) -> Span {
|
||||
fn span(&self) -> SpanRange {
|
||||
self.cond_span
|
||||
}
|
||||
}
|
||||
|
@ -192,20 +193,16 @@ pub struct MatchArm {
|
|||
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 span_tokens<I: IntoIterator<Item=TokenTree>>(tokens: I) -> SpanRange {
|
||||
join_ranges(tokens.into_iter().map(|s| SpanRange::single_span(s.span())))
|
||||
}
|
||||
|
||||
pub fn join_spans<I: IntoIterator<Item=Span>>(spans: I) -> Span {
|
||||
let mut iter = spans.into_iter();
|
||||
let mut span = match iter.next() {
|
||||
pub fn join_ranges<I: IntoIterator<Item=SpanRange>>(ranges: I) -> SpanRange {
|
||||
let mut iter = ranges.into_iter();
|
||||
let first = match iter.next() {
|
||||
Some(span) => span,
|
||||
None => return Span::call_site(),
|
||||
None => return SpanRange::call_site(),
|
||||
};
|
||||
for new_span in iter {
|
||||
if let Some(joined) = span.join(new_span) {
|
||||
span = joined;
|
||||
}
|
||||
}
|
||||
span
|
||||
let last = iter.last().unwrap_or(first);
|
||||
first.join_range(last)
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ use proc_macro2::{
|
|||
TokenTree,
|
||||
};
|
||||
use quote::quote;
|
||||
use proc_macro_error::SpanRange;
|
||||
|
||||
use crate::ast::*;
|
||||
|
||||
|
@ -63,7 +64,7 @@ impl Generator {
|
|||
.map(|arm| self.match_arm(arm))
|
||||
.collect();
|
||||
let mut body = TokenTree::Group(Group::new(Delimiter::Brace, body));
|
||||
body.set_span(arms_span.into());
|
||||
body.set_span(arms_span.collapse());
|
||||
let head: TokenStream = head.into();
|
||||
quote!(#head #body)
|
||||
});
|
||||
|
@ -75,7 +76,7 @@ impl Generator {
|
|||
let mut build = self.builder();
|
||||
self.markups(markups, &mut build);
|
||||
let mut block = TokenTree::Group(Group::new(Delimiter::Brace, build.finish()));
|
||||
block.set_span(outer_span.into());
|
||||
block.set_span(outer_span.collapse());
|
||||
TokenStream::from(block)
|
||||
}
|
||||
|
||||
|
@ -203,7 +204,7 @@ fn desugar_classes_or_ids(
|
|||
};
|
||||
let head = desugar_toggler(toggler);
|
||||
markups.push(Markup::Special {
|
||||
segments: vec![Special { at_span: Span::call_site(), head: head.into(), body }],
|
||||
segments: vec![Special { at_span: SpanRange::call_site(), head: head.into(), body }],
|
||||
});
|
||||
}
|
||||
Some(Attribute {
|
||||
|
@ -211,7 +212,7 @@ fn desugar_classes_or_ids(
|
|||
attr_type: AttrType::Normal {
|
||||
value: Markup::Block(Block {
|
||||
markups,
|
||||
outer_span: Span::call_site(),
|
||||
outer_span: SpanRange::call_site(),
|
||||
}),
|
||||
},
|
||||
})
|
||||
|
@ -236,7 +237,7 @@ fn desugar_toggler(Toggler { cond, cond_span }: Toggler) -> TokenStream {
|
|||
// wrap it in parentheses to avoid parse errors
|
||||
if cond.clone().into_iter().any(is_braced_block) {
|
||||
let mut wrapped_cond = TokenTree::Group(Group::new(Delimiter::Parenthesis, cond));
|
||||
wrapped_cond.set_span(cond_span.into());
|
||||
wrapped_cond.set_span(cond_span.collapse());
|
||||
cond = TokenStream::from(wrapped_cond);
|
||||
}
|
||||
quote!(if #cond)
|
||||
|
|
|
@ -7,7 +7,7 @@ use proc_macro2::{
|
|||
TokenStream,
|
||||
TokenTree,
|
||||
};
|
||||
use proc_macro_error::{abort, abort_call_site};
|
||||
use proc_macro_error::{abort, abort_call_site, SpanRange};
|
||||
use std::collections::HashMap;
|
||||
use std::mem;
|
||||
|
||||
|
@ -128,13 +128,11 @@ impl Parser {
|
|||
"for" => self.for_expr(at_span, keyword),
|
||||
"match" => self.match_expr(at_span, keyword),
|
||||
"let" => {
|
||||
let ident_span = ident.span();
|
||||
let span = at_span.join(ident_span).unwrap_or(ident_span);
|
||||
let span = SpanRange { first: at_span, last: ident.span() };
|
||||
abort!(span, "`@let` only works inside a block");
|
||||
},
|
||||
other => {
|
||||
let ident_span = ident.span();
|
||||
let span = at_span.join(ident_span).unwrap_or(ident_span);
|
||||
let span = SpanRange { first: at_span, last: ident.span() };
|
||||
abort!(span, "unknown keyword `@{}`", other);
|
||||
}
|
||||
}
|
||||
|
@ -146,7 +144,7 @@ impl Parser {
|
|||
},
|
||||
// Element
|
||||
TokenTree::Ident(ident) => {
|
||||
let ident_string = ident.to_string();
|
||||
let _ident_string = ident.to_string();
|
||||
// Is this a keyword that's missing a '@'?
|
||||
// TODO: warning or error?
|
||||
// match ident_string.as_str() {
|
||||
|
@ -171,12 +169,12 @@ impl Parser {
|
|||
// Splice
|
||||
TokenTree::Group(ref group) if group.delimiter() == Delimiter::Parenthesis => {
|
||||
self.advance();
|
||||
ast::Markup::Splice { expr: group.stream(), outer_span: group.span() }
|
||||
ast::Markup::Splice { expr: group.stream(), outer_span: SpanRange::single_span(group.span()) }
|
||||
}
|
||||
// Block
|
||||
TokenTree::Group(ref group) if group.delimiter() == Delimiter::Brace => {
|
||||
self.advance();
|
||||
ast::Markup::Block(self.block(group.stream(), group.span()))
|
||||
ast::Markup::Block(self.block(group.stream(), SpanRange::single_span(group.span())))
|
||||
},
|
||||
// ???
|
||||
token => {
|
||||
|
@ -193,7 +191,7 @@ impl Parser {
|
|||
.unwrap_or_else(|_| abort!(lit, "expected string"));
|
||||
ast::Markup::Literal {
|
||||
content,
|
||||
span: lit.span(),
|
||||
span: SpanRange::single_span(lit.span()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,18 +208,18 @@ impl Parser {
|
|||
let body = loop {
|
||||
match self.next() {
|
||||
Some(TokenTree::Group(ref block)) if block.delimiter() == Delimiter::Brace => {
|
||||
break self.block(block.stream(), block.span());
|
||||
break self.block(block.stream(), SpanRange::single_span(block.span()));
|
||||
},
|
||||
Some(token) => head.push(token),
|
||||
None => {
|
||||
let head_span = ast::span_tokens(head);
|
||||
let span = at_span.join(head_span).unwrap_or(head_span);
|
||||
let mut span = ast::span_tokens(head);
|
||||
span.first = at_span;
|
||||
abort!(span, "expected body for this `@if`");
|
||||
},
|
||||
}
|
||||
};
|
||||
segments.push(ast::Special {
|
||||
at_span,
|
||||
at_span: SpanRange::single_span(at_span),
|
||||
head: head.into_iter().collect(),
|
||||
body,
|
||||
});
|
||||
|
@ -251,16 +249,15 @@ impl Parser {
|
|||
_ => {
|
||||
match self.next() {
|
||||
Some(TokenTree::Group(ref group)) if group.delimiter() == Delimiter::Brace => {
|
||||
let body = self.block(group.stream(), group.span());
|
||||
let body = self.block(group.stream(), SpanRange::single_span(group.span()));
|
||||
segments.push(ast::Special {
|
||||
at_span,
|
||||
at_span: SpanRange::single_span(at_span),
|
||||
head: vec![else_keyword].into_iter().collect(),
|
||||
body,
|
||||
});
|
||||
},
|
||||
_ => {
|
||||
let else_span = else_keyword.span();
|
||||
let span = at_span.join(else_span).unwrap_or(else_span);
|
||||
let span = SpanRange { first: at_span, last: else_keyword.span() };
|
||||
abort!(span, "expected body for this `@else`");
|
||||
},
|
||||
}
|
||||
|
@ -281,17 +278,17 @@ impl Parser {
|
|||
let body = loop {
|
||||
match self.next() {
|
||||
Some(TokenTree::Group(ref block)) if block.delimiter() == Delimiter::Brace => {
|
||||
break self.block(block.stream(), block.span());
|
||||
break self.block(block.stream(), SpanRange::single_span(block.span()));
|
||||
},
|
||||
Some(token) => head.push(token),
|
||||
None => {
|
||||
let span = at_span.join(keyword_span).unwrap_or(keyword_span);
|
||||
let span = SpanRange { first: at_span, last: keyword_span };
|
||||
abort!(span, "expected body for this `@while`");
|
||||
},
|
||||
}
|
||||
};
|
||||
ast::Markup::Special {
|
||||
segments: vec![ast::Special { at_span, head: head.into_iter().collect(), body }],
|
||||
segments: vec![ast::Special { at_span: SpanRange::single_span(at_span), head: head.into_iter().collect(), body }],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -309,7 +306,7 @@ impl Parser {
|
|||
},
|
||||
Some(token) => head.push(token),
|
||||
None => {
|
||||
let span = at_span.join(keyword_span).unwrap_or(keyword_span);
|
||||
let span = SpanRange { first: at_span, last: keyword_span };
|
||||
abort!(span, "missing `in` in `@for` loop");
|
||||
},
|
||||
}
|
||||
|
@ -317,17 +314,17 @@ impl Parser {
|
|||
let body = loop {
|
||||
match self.next() {
|
||||
Some(TokenTree::Group(ref block)) if block.delimiter() == Delimiter::Brace => {
|
||||
break self.block(block.stream(), block.span());
|
||||
break self.block(block.stream(), SpanRange::single_span(block.span()));
|
||||
},
|
||||
Some(token) => head.push(token),
|
||||
None => {
|
||||
let span = at_span.join(keyword_span).unwrap_or(keyword_span);
|
||||
let span = SpanRange { first: at_span, last: keyword_span };
|
||||
abort!(span, "expected body for this `@for`");
|
||||
},
|
||||
}
|
||||
};
|
||||
ast::Markup::Special {
|
||||
segments: vec![ast::Special { at_span, head: head.into_iter().collect(), body }],
|
||||
segments: vec![ast::Special { at_span: SpanRange::single_span(at_span), head: head.into_iter().collect(), body }],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -340,17 +337,17 @@ impl Parser {
|
|||
let (arms, arms_span) = loop {
|
||||
match self.next() {
|
||||
Some(TokenTree::Group(ref body)) if body.delimiter() == Delimiter::Brace => {
|
||||
let span = body.span();
|
||||
let span = SpanRange::single_span(body.span());
|
||||
break (self.with_input(body.stream()).match_arms(), span);
|
||||
},
|
||||
Some(token) => head.push(token),
|
||||
None => {
|
||||
let span = at_span.join(keyword_span).unwrap_or(keyword_span);
|
||||
let span = SpanRange { first: at_span, last: keyword_span };
|
||||
abort!(span, "expected body for this `@match`");
|
||||
},
|
||||
}
|
||||
};
|
||||
ast::Markup::Match { at_span, head: head.into_iter().collect(), arms, arms_span }
|
||||
ast::Markup::Match { at_span: SpanRange::single_span(at_span), head: head.into_iter().collect(), arms, arms_span }
|
||||
}
|
||||
|
||||
fn match_arms(&mut self) -> Vec<ast::MatchArm> {
|
||||
|
@ -389,7 +386,7 @@ impl Parser {
|
|||
let body = match self.next() {
|
||||
// $pat => { $stmts }
|
||||
Some(TokenTree::Group(ref body)) if body.delimiter() == Delimiter::Brace => {
|
||||
let body = self.block(body.stream(), body.span());
|
||||
let body = self.block(body.stream(), SpanRange::single_span(body.span()));
|
||||
// Trailing commas are optional if the match arm is a braced block
|
||||
if let Some(TokenTree::Punct(ref punct)) = self.peek() {
|
||||
if punct.as_char() == ',' {
|
||||
|
@ -400,15 +397,13 @@ impl Parser {
|
|||
},
|
||||
// $pat => $expr
|
||||
Some(first_token) => {
|
||||
let mut span = first_token.span();
|
||||
let mut span = SpanRange::single_span(first_token.span());
|
||||
let mut body = vec![first_token];
|
||||
loop {
|
||||
match self.next() {
|
||||
Some(TokenTree::Punct(ref punct)) if punct.as_char() == ',' => break,
|
||||
Some(token) => {
|
||||
if let Some(bigger_span) = span.join(token.span()) {
|
||||
span = bigger_span;
|
||||
}
|
||||
span.last = token.span();
|
||||
body.push(token);
|
||||
},
|
||||
None => break,
|
||||
|
@ -441,8 +436,8 @@ impl Parser {
|
|||
}
|
||||
},
|
||||
None => {
|
||||
let tokens_span = ast::span_tokens(tokens);
|
||||
let span = at_span.join(tokens_span).unwrap_or(tokens_span);
|
||||
let mut span = ast::span_tokens(tokens);
|
||||
span.first = at_span;
|
||||
abort!(span, "unexpected end of `@let` expression");
|
||||
}
|
||||
}
|
||||
|
@ -459,8 +454,8 @@ impl Parser {
|
|||
}
|
||||
},
|
||||
None => {
|
||||
let tokens_span = ast::span_tokens(tokens);
|
||||
let span = at_span.join(tokens_span).unwrap_or(tokens_span);
|
||||
let mut span = ast::span_tokens(tokens);
|
||||
span.first = at_span;
|
||||
abort!(
|
||||
span,
|
||||
"unexpected end of `@let` expression";
|
||||
|
@ -469,7 +464,7 @@ impl Parser {
|
|||
},
|
||||
}
|
||||
}
|
||||
ast::Markup::Let { at_span, tokens: tokens.into_iter().collect() }
|
||||
ast::Markup::Let { at_span: SpanRange::single_span(at_span), tokens: tokens.into_iter().collect() }
|
||||
}
|
||||
|
||||
/// Parses an element node.
|
||||
|
@ -486,7 +481,7 @@ impl Parser {
|
|||
if punct.as_char() == ';' || punct.as_char() == '/' => {
|
||||
// Void element
|
||||
self.advance();
|
||||
ast::ElementBody::Void { semi_span: punct.span() }
|
||||
ast::ElementBody::Void { semi_span: SpanRange::single_span(punct.span()) }
|
||||
},
|
||||
_ => {
|
||||
match self.markup() {
|
||||
|
@ -546,20 +541,20 @@ impl Parser {
|
|||
self.commit(attempt);
|
||||
let name = self.class_or_id_name();
|
||||
let toggler = self.attr_toggler();
|
||||
attrs.push(ast::Attr::Class { dot_span: punct.span(), name, toggler });
|
||||
attrs.push(ast::Attr::Class { dot_span: SpanRange::single_span(punct.span()), name, toggler });
|
||||
},
|
||||
// ID shorthand
|
||||
(None, Some(TokenTree::Punct(ref punct))) if punct.as_char() == '#' => {
|
||||
self.commit(attempt);
|
||||
let name = self.class_or_id_name();
|
||||
attrs.push(ast::Attr::Id { hash_span: punct.span(), name });
|
||||
attrs.push(ast::Attr::Id { hash_span: SpanRange::single_span(punct.span()), name });
|
||||
},
|
||||
// If it's not a valid attribute, backtrack and bail out
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
||||
let mut attr_map: HashMap<String, Vec<Span>> = HashMap::new();
|
||||
let mut attr_map: HashMap<String, Vec<SpanRange>> = HashMap::new();
|
||||
let mut has_class = false;
|
||||
for attr in &attrs {
|
||||
let name = match attr {
|
||||
|
@ -607,7 +602,7 @@ impl Parser {
|
|||
self.advance();
|
||||
Some(ast::Toggler {
|
||||
cond: group.stream(),
|
||||
cond_span: group.span(),
|
||||
cond_span: SpanRange::single_span(group.span()),
|
||||
})
|
||||
},
|
||||
_ => None,
|
||||
|
@ -657,7 +652,7 @@ impl Parser {
|
|||
}
|
||||
|
||||
/// Parses the given token stream as a Maud expression.
|
||||
fn block(&mut self, body: TokenStream, outer_span: Span) -> ast::Block {
|
||||
fn block(&mut self, body: TokenStream, outer_span: SpanRange) -> ast::Block {
|
||||
let markups = self.with_input(body).markups();
|
||||
ast::Block { markups, outer_span }
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue