fix some edge cases around multiline attributes
This commit is contained in:
1 parent
f69b84bf34
commit
b6ea722d94
7 files changed
+77
-205
No files matched your search
@@ -14,7 +14,7 @@ keywords = ["dom", "ui", "gui", "react"]
|
|||||||
dioxus-rsx = { workspace = true }
|
dioxus-rsx = { workspace = true }
|
||||||
proc-macro2 = { version = "1.0.6", features = ["span-locations"] }
|
proc-macro2 = { version = "1.0.6", features = ["span-locations"] }
|
||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
syn = { version = "1.0.11", features = ["full", "extra-traits"] }
|
syn = { version = "1.0.11", features = ["full", "extra-traits", "visit"] }
|
||||||
serde = { version = "1.0.136", features = ["derive"] }
|
serde = { version = "1.0.136", features = ["derive"] }
|
||||||
prettyplease = { package = "prettier-please", version = "0.1.16", features = [
|
prettyplease = { package = "prettier-please", version = "0.1.16", features = [
|
||||||
"verbatim",
|
"verbatim",
|
||||||
|
|||||||
@@ -3,178 +3,24 @@
|
|||||||
//! Returns all macros that match a pattern. You can use this information to autoformat them later
|
//! Returns all macros that match a pattern. You can use this information to autoformat them later
|
||||||
|
|
||||||
use proc_macro2::LineColumn;
|
use proc_macro2::LineColumn;
|
||||||
use syn::{Block, Expr, File, Item, Macro, Stmt};
|
use syn::{visit::Visit, File, Macro};
|
||||||
|
|
||||||
type CollectedMacro<'a> = &'a Macro;
|
type CollectedMacro<'a> = &'a Macro;
|
||||||
|
|
||||||
pub fn collect_from_file<'a>(file: &'a File, macros: &mut Vec<CollectedMacro<'a>>) {
|
pub fn collect_from_file<'a, 'b>(file: &'a File, macros: &'b mut Vec<CollectedMacro<'a>>) {
|
||||||
for item in file.items.iter() {
|
MacroCollector::visit_file(&mut MacroCollector { macros }, file);
|
||||||
collect_from_item(item, macros);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn collect_from_item<'a>(item: &'a Item, macros: &mut Vec<CollectedMacro<'a>>) {
|
struct MacroCollector<'a, 'b> {
|
||||||
match item {
|
macros: &'a mut Vec<CollectedMacro<'b>>,
|
||||||
Item::Fn(f) => collect_from_block(&f.block, macros),
|
|
||||||
|
|
||||||
// Ignore macros if they're not rsx or render
|
|
||||||
Item::Macro(macro_) => {
|
|
||||||
if macro_.mac.path.segments[0].ident == "rsx"
|
|
||||||
|| macro_.mac.path.segments[0].ident == "render"
|
|
||||||
{
|
|
||||||
macros.push(¯o_.mac);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Currently disabled since we're not focused on autoformatting these
|
|
||||||
Item::Impl(_imp) => {}
|
|
||||||
Item::Trait(_) => {}
|
|
||||||
|
|
||||||
// Global-ish things
|
|
||||||
Item::Static(f) => collect_from_expr(&f.expr, macros),
|
|
||||||
Item::Const(f) => collect_from_expr(&f.expr, macros),
|
|
||||||
Item::Mod(s) => {
|
|
||||||
if let Some((_, block)) = &s.content {
|
|
||||||
for item in block {
|
|
||||||
collect_from_item(item, macros);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// None of these we can really do anything with at the item level
|
|
||||||
Item::Macro2(_)
|
|
||||||
| Item::Enum(_)
|
|
||||||
| Item::ExternCrate(_)
|
|
||||||
| Item::ForeignMod(_)
|
|
||||||
| Item::TraitAlias(_)
|
|
||||||
| Item::Type(_)
|
|
||||||
| Item::Struct(_)
|
|
||||||
| Item::Union(_)
|
|
||||||
| Item::Use(_)
|
|
||||||
| Item::Verbatim(_) => {}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn collect_from_block<'a>(block: &'a Block, macros: &mut Vec<CollectedMacro<'a>>) {
|
impl<'a, 'b> Visit<'b> for MacroCollector<'a, 'b> {
|
||||||
for stmt in &block.stmts {
|
fn visit_macro(&mut self, i: &'b Macro) {
|
||||||
match stmt {
|
self.macros.push(i);
|
||||||
Stmt::Item(item) => collect_from_item(item, macros),
|
|
||||||
Stmt::Local(local) => {
|
|
||||||
if let Some((_eq, init)) = &local.init {
|
|
||||||
collect_from_expr(init, macros);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Stmt::Expr(exp) | Stmt::Semi(exp, _) => collect_from_expr(exp, macros),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn collect_from_expr<'a>(expr: &'a Expr, macros: &mut Vec<CollectedMacro<'a>>) {
|
|
||||||
// collect an expr from the exprs, descending into blocks
|
|
||||||
match expr {
|
|
||||||
Expr::Macro(macro_) => {
|
|
||||||
if macro_.mac.path.segments[0].ident == "rsx"
|
|
||||||
|| macro_.mac.path.segments[0].ident == "render"
|
|
||||||
{
|
|
||||||
macros.push(¯o_.mac);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr::MethodCall(e) => {
|
|
||||||
collect_from_expr(&e.receiver, macros);
|
|
||||||
for expr in e.args.iter() {
|
|
||||||
collect_from_expr(expr, macros);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Expr::Assign(exp) => {
|
|
||||||
collect_from_expr(&exp.left, macros);
|
|
||||||
collect_from_expr(&exp.right, macros);
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr::Async(b) => collect_from_block(&b.block, macros),
|
|
||||||
Expr::Block(b) => collect_from_block(&b.block, macros),
|
|
||||||
Expr::Closure(c) => collect_from_expr(&c.body, macros),
|
|
||||||
Expr::Let(l) => collect_from_expr(&l.expr, macros),
|
|
||||||
Expr::Unsafe(u) => collect_from_block(&u.block, macros),
|
|
||||||
Expr::Loop(l) => collect_from_block(&l.body, macros),
|
|
||||||
|
|
||||||
Expr::Call(c) => {
|
|
||||||
collect_from_expr(&c.func, macros);
|
|
||||||
for expr in c.args.iter() {
|
|
||||||
collect_from_expr(expr, macros);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr::ForLoop(b) => {
|
|
||||||
collect_from_expr(&b.expr, macros);
|
|
||||||
collect_from_block(&b.body, macros);
|
|
||||||
}
|
|
||||||
Expr::If(f) => {
|
|
||||||
collect_from_expr(&f.cond, macros);
|
|
||||||
collect_from_block(&f.then_branch, macros);
|
|
||||||
if let Some((_, else_branch)) = &f.else_branch {
|
|
||||||
collect_from_expr(else_branch, macros);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Expr::Yield(y) => {
|
|
||||||
if let Some(expr) = &y.expr {
|
|
||||||
collect_from_expr(expr, macros);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr::Return(r) => {
|
|
||||||
if let Some(expr) = &r.expr {
|
|
||||||
collect_from_expr(expr, macros);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr::Match(l) => {
|
|
||||||
collect_from_expr(&l.expr, macros);
|
|
||||||
for arm in l.arms.iter() {
|
|
||||||
if let Some((_, expr)) = &arm.guard {
|
|
||||||
collect_from_expr(expr, macros);
|
|
||||||
}
|
|
||||||
|
|
||||||
collect_from_expr(&arm.body, macros);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr::While(w) => {
|
|
||||||
collect_from_expr(&w.cond, macros);
|
|
||||||
collect_from_block(&w.body, macros);
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't both formatting these for now
|
|
||||||
Expr::Array(_)
|
|
||||||
| Expr::AssignOp(_)
|
|
||||||
| Expr::Await(_)
|
|
||||||
| Expr::Binary(_)
|
|
||||||
| Expr::Box(_)
|
|
||||||
| Expr::Break(_)
|
|
||||||
| Expr::Cast(_)
|
|
||||||
| Expr::Continue(_)
|
|
||||||
| Expr::Field(_)
|
|
||||||
| Expr::Group(_)
|
|
||||||
| Expr::Index(_)
|
|
||||||
| Expr::Lit(_)
|
|
||||||
| Expr::Paren(_)
|
|
||||||
| Expr::Path(_)
|
|
||||||
| Expr::Range(_)
|
|
||||||
| Expr::Reference(_)
|
|
||||||
| Expr::Repeat(_)
|
|
||||||
| Expr::Struct(_)
|
|
||||||
| Expr::Try(_)
|
|
||||||
| Expr::TryBlock(_)
|
|
||||||
| Expr::Tuple(_)
|
|
||||||
| Expr::Type(_)
|
|
||||||
| Expr::Unary(_)
|
|
||||||
| Expr::Verbatim(_) => {}
|
|
||||||
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn byte_offset(input: &str, location: LineColumn) -> usize {
|
pub fn byte_offset(input: &str, location: LineColumn) -> usize {
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
for _ in 1..location.line {
|
for _ in 1..location.line {
|
||||||
|
|||||||
@@ -165,7 +165,14 @@ impl Writer<'_> {
|
|||||||
match &field.content {
|
match &field.content {
|
||||||
ContentField::ManExpr(exp) => {
|
ContentField::ManExpr(exp) => {
|
||||||
let out = prettyplease::unparse_expr(exp);
|
let out = prettyplease::unparse_expr(exp);
|
||||||
write!(self.out, "{name}: {out}")?;
|
let mut lines = out.split('\n').peekable();
|
||||||
|
let first = lines.next().unwrap();
|
||||||
|
write!(self.out, "{name}: {first}")?;
|
||||||
|
for line in lines {
|
||||||
|
self.out.new_line()?;
|
||||||
|
self.out.indented_tab()?;
|
||||||
|
write!(self.out, "{line}")?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ContentField::Formatted(s) => {
|
ContentField::Formatted(s) => {
|
||||||
write!(
|
write!(
|
||||||
|
|||||||
@@ -216,7 +216,26 @@ impl Writer<'_> {
|
|||||||
}
|
}
|
||||||
ElementAttr::AttrExpression { name, value } => {
|
ElementAttr::AttrExpression { name, value } => {
|
||||||
let out = prettyplease::unparse_expr(value);
|
let out = prettyplease::unparse_expr(value);
|
||||||
write!(self.out, "{name}: {out}")?;
|
let mut lines = out.split('\n').peekable();
|
||||||
|
let first = lines.next().unwrap();
|
||||||
|
|
||||||
|
// a one-liner for whatever reason
|
||||||
|
// Does not need a new line
|
||||||
|
if lines.peek().is_none() {
|
||||||
|
write!(self.out, "{name}: {first}")?;
|
||||||
|
} else {
|
||||||
|
writeln!(self.out, "{name}: {first}")?;
|
||||||
|
|
||||||
|
while let Some(line) = lines.next() {
|
||||||
|
self.out.indented_tab()?;
|
||||||
|
write!(self.out, "{line}")?;
|
||||||
|
if lines.peek().is_none() {
|
||||||
|
write!(self.out, "")?;
|
||||||
|
} else {
|
||||||
|
writeln!(self.out)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ElementAttr::CustomAttrText { name, value } => {
|
ElementAttr::CustomAttrText { name, value } => {
|
||||||
|
|||||||
@@ -27,41 +27,29 @@ impl Writer<'_> {
|
|||||||
// If the expr is multiline, we want to collect all of its lines together and write them out properly
|
// If the expr is multiline, we want to collect all of its lines together and write them out properly
|
||||||
// This involves unshifting the first line if it's aligned
|
// This involves unshifting the first line if it's aligned
|
||||||
let first_line = &self.src[start.line - 1];
|
let first_line = &self.src[start.line - 1];
|
||||||
write!(
|
write!(self.out, "{}", &first_line[start.column - 1..].trim_start())?;
|
||||||
self.out,
|
|
||||||
"{}",
|
|
||||||
&first_line[start.column - 1..first_line.len()].trim()
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let first_prefix = &self.src[start.line - 1][..start.column];
|
let prev_block_indent_level = crate::leading_whitespaces(&first_line) / 4;
|
||||||
let offset = match first_prefix.trim() {
|
|
||||||
"" => 0,
|
|
||||||
_ => first_prefix
|
|
||||||
.chars()
|
|
||||||
.rev()
|
|
||||||
.take_while(|c| c.is_whitespace())
|
|
||||||
.count() as isize,
|
|
||||||
};
|
|
||||||
|
|
||||||
for (id, line) in self.src[start.line..end.line].iter().enumerate() {
|
for (id, line) in self.src[start.line..end.line].iter().enumerate() {
|
||||||
writeln!(self.out)?;
|
writeln!(self.out)?;
|
||||||
// trim the leading whitespace
|
// check if this is the last line
|
||||||
let line = match id {
|
let line = {
|
||||||
x if x == (end.line - start.line) - 1 => &line[..end.column],
|
if id == (end.line - start.line) - 1 {
|
||||||
_ => line,
|
&line[..end.column]
|
||||||
|
} else {
|
||||||
|
line
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if offset < 0 {
|
// trim the leading whitespace
|
||||||
for _ in 0..-offset {
|
let previous_indent = crate::leading_whitespaces(line) / 4;
|
||||||
write!(self.out, " ")?;
|
let offset = previous_indent.saturating_sub(prev_block_indent_level);
|
||||||
}
|
let required_indent = self.out.indent + offset;
|
||||||
|
self.out.write_tabs(required_indent)?;
|
||||||
|
|
||||||
write!(self.out, "{line}")?;
|
let line = line.trim_start();
|
||||||
} else {
|
write!(self.out, "{line}")?;
|
||||||
let offset = offset as usize;
|
|
||||||
let right = &line[offset..];
|
|
||||||
write!(self.out, "{right}")?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ pub fn fmt_file(contents: &str) -> Vec<FormattedBlock> {
|
|||||||
|
|
||||||
let mut writer = Writer::new(contents);
|
let mut writer = Writer::new(contents);
|
||||||
|
|
||||||
// Dont parse nested macros
|
// Don't parse nested macros
|
||||||
let mut end_span = LineColumn { column: 0, line: 0 };
|
let mut end_span = LineColumn { column: 0, line: 0 };
|
||||||
for item in macros {
|
for item in macros {
|
||||||
let macro_path = &item.path.segments[0].ident;
|
let macro_path = &item.path.segments[0].ident;
|
||||||
@@ -68,16 +68,11 @@ pub fn fmt_file(contents: &str) -> Vec<FormattedBlock> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// item.parse_body::<CallBody>();
|
|
||||||
let body = item.parse_body::<CallBody>().unwrap();
|
let body = item.parse_body::<CallBody>().unwrap();
|
||||||
|
|
||||||
let rsx_start = macro_path.span().start();
|
let rsx_start = macro_path.span().start();
|
||||||
|
|
||||||
writer.out.indent = &writer.src[rsx_start.line - 1]
|
writer.out.indent = leading_whitespaces(&writer.src[rsx_start.line - 1]) / 4;
|
||||||
.chars()
|
|
||||||
.take_while(|c| *c == ' ')
|
|
||||||
.count()
|
|
||||||
/ 4;
|
|
||||||
|
|
||||||
write_body(&mut writer, &body);
|
write_body(&mut writer, &body);
|
||||||
|
|
||||||
@@ -230,3 +225,14 @@ pub(crate) fn write_ifmt(input: &IfmtInput, writable: &mut impl Write) -> std::f
|
|||||||
let display = DisplayIfmt(input);
|
let display = DisplayIfmt(input);
|
||||||
write!(writable, "{}", display)
|
write!(writable, "{}", display)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn leading_whitespaces(input: &str) -> usize {
|
||||||
|
input
|
||||||
|
.chars()
|
||||||
|
.map_while(|c| match c {
|
||||||
|
' ' => Some(1),
|
||||||
|
'\t' => Some(4),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
@@ -109,11 +109,17 @@ async fn autoformat_project(check: bool) -> Result<()> {
|
|||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
if res.is_err() {
|
match res {
|
||||||
eprintln!("error formatting file: {}", _path.display());
|
Err(err) => {
|
||||||
|
eprintln!("error formatting file: {}\n{err}", _path.display());
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Ok(Err(err)) => {
|
||||||
|
eprintln!("error formatting file: {}\n{err}", _path.display());
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Ok(Ok(res)) => Some(res),
|
||||||
}
|
}
|
||||||
|
|
||||||
res
|
|
||||||
})
|
})
|
||||||
.collect::<FuturesUnordered<_>>()
|
.collect::<FuturesUnordered<_>>()
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
@@ -122,7 +128,7 @@ async fn autoformat_project(check: bool) -> Result<()> {
|
|||||||
let files_formatted: usize = counts
|
let files_formatted: usize = counts
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|f| match f {
|
.map(|f| match f {
|
||||||
Ok(Ok(res)) => res,
|
Some(res) => res,
|
||||||
_ => 0,
|
_ => 0,
|
||||||
})
|
})
|
||||||
.sum();
|
.sum();
|
||||||
|
|||||||
Reference in new issue
Block a user