Autofmt nested rsx using syn::Visitor (#2279)
* Nested macros using visitor pattern
This commit is contained in:
1 parent
0b7c7701a3
commit
67af2d89dd
7 files changed
+321
-30
No files matched your search
@@ -1,5 +1,145 @@
|
||||
use prettyplease::unparse;
|
||||
use syn::{Expr, File, Item};
|
||||
use syn::{visit_mut::VisitMut, Expr, File, Item};
|
||||
|
||||
use crate::Writer;
|
||||
|
||||
impl Writer<'_> {
|
||||
pub fn unparse_expr(&mut self, expr: &Expr) -> String {
|
||||
struct ReplaceMacros<'a, 'b> {
|
||||
writer: &'a mut Writer<'b>,
|
||||
formatted_stack: Vec<String>,
|
||||
}
|
||||
|
||||
impl VisitMut for ReplaceMacros<'_, '_> {
|
||||
fn visit_stmt_mut(&mut self, _expr: &mut syn::Stmt) {
|
||||
if let syn::Stmt::Macro(i) = _expr {
|
||||
// replace the macro with a block that roughly matches the macro
|
||||
if let Some("rsx" | "render") = i
|
||||
.mac
|
||||
.path
|
||||
.segments
|
||||
.last()
|
||||
.map(|i| i.ident.to_string())
|
||||
.as_deref()
|
||||
{
|
||||
// format the macro in place
|
||||
// we'll use information about the macro to replace it with another formatted block
|
||||
// once we've written out the unparsed expr from prettyplease, we can replace
|
||||
// this dummy block with the actual formatted block
|
||||
let formatted = crate::fmt_block_from_expr(
|
||||
self.writer.raw_src,
|
||||
syn::ExprMacro {
|
||||
attrs: i.attrs.clone(),
|
||||
mac: i.mac.clone(),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
*_expr = syn::Stmt::Expr(
|
||||
syn::parse_quote!(dioxus_autofmt_block__________),
|
||||
i.semi_token,
|
||||
);
|
||||
|
||||
// Save this formatted block for later, when we apply it to the original expr
|
||||
self.formatted_stack.push(formatted);
|
||||
}
|
||||
}
|
||||
|
||||
syn::visit_mut::visit_stmt_mut(self, _expr);
|
||||
}
|
||||
|
||||
fn visit_expr_mut(&mut self, _expr: &mut syn::Expr) {
|
||||
if let syn::Expr::Macro(i) = _expr {
|
||||
// replace the macro with a block that roughly matches the macro
|
||||
if let Some("rsx" | "render") = i
|
||||
.mac
|
||||
.path
|
||||
.segments
|
||||
.last()
|
||||
.map(|i| i.ident.to_string())
|
||||
.as_deref()
|
||||
{
|
||||
// format the macro in place
|
||||
// we'll use information about the macro to replace it with another formatted block
|
||||
// once we've written out the unparsed expr from prettyplease, we can replace
|
||||
// this dummy block with the actual formatted block
|
||||
let formatted = crate::fmt_block_from_expr(
|
||||
self.writer.raw_src,
|
||||
syn::ExprMacro {
|
||||
attrs: i.attrs.clone(),
|
||||
mac: i.mac.clone(),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
*_expr = syn::parse_quote!(dioxus_autofmt_block__________);
|
||||
|
||||
// Save this formatted block for later, when we apply it to the original expr
|
||||
self.formatted_stack.push(formatted);
|
||||
}
|
||||
}
|
||||
|
||||
syn::visit_mut::visit_expr_mut(self, _expr);
|
||||
}
|
||||
}
|
||||
|
||||
// Visit the expr and replace the macros with formatted blocks
|
||||
let mut replacer = ReplaceMacros {
|
||||
writer: self,
|
||||
formatted_stack: vec![],
|
||||
};
|
||||
|
||||
// builds the expression stack
|
||||
let mut modified_expr = expr.clone();
|
||||
replacer.visit_expr_mut(&mut modified_expr);
|
||||
|
||||
// now unparsed with the modified expression
|
||||
let mut unparsed = unparse_expr(&modified_expr);
|
||||
|
||||
// walk each line looking for the dioxus_autofmt_block__________ token
|
||||
// if we find it, replace it with the formatted block
|
||||
// if there's indentation we want to presreve it
|
||||
|
||||
// now we can replace the macros with the formatted blocks
|
||||
for formatted in replacer.formatted_stack.drain(..) {
|
||||
let fmted = if formatted.contains('\n') {
|
||||
format!("rsx! {{{formatted}\n}}")
|
||||
} else {
|
||||
format!("rsx! {{{formatted}}}")
|
||||
};
|
||||
let mut out_fmt = String::new();
|
||||
let mut whitespace = 0;
|
||||
|
||||
for line in unparsed.lines() {
|
||||
if line.contains("dioxus_autofmt_block__________") {
|
||||
whitespace = line.chars().take_while(|c| c.is_whitespace()).count();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (idx, fmt_line) in fmted.lines().enumerate() {
|
||||
// Push the indentation
|
||||
if idx > 0 {
|
||||
out_fmt.push_str(&" ".repeat(whitespace));
|
||||
}
|
||||
|
||||
out_fmt.push_str(fmt_line);
|
||||
|
||||
// Push a newline
|
||||
out_fmt.push('\n');
|
||||
}
|
||||
|
||||
// Remove the last newline
|
||||
out_fmt.pop();
|
||||
|
||||
// Replace the dioxus_autofmt_block__________ token with the formatted block
|
||||
unparsed = unparsed.replacen("dioxus_autofmt_block__________", &out_fmt, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
unparsed
|
||||
}
|
||||
}
|
||||
|
||||
/// Unparse an expression back into a string
|
||||
///
|
||||
|
||||
Reference in new issue
Block a user