Autofmt nested rsx using syn::Visitor (#2279)

* Nested macros using visitor pattern
This commit is contained in:
Jonathan Kelley authored and GitHub committed 2024-04-08 22:15:52 -07:00
1 parent 0b7c7701a3
commit 67af2d89dd
7 files changed
+321 -30

No files matched your search

+141 -1
View File
@@ -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
///