wip: more cleanup, more tests

This commit is contained in:
Jonathan Kelley 2022-06-27 16:24:36 -04:00
parent a2a194ca40
commit d70b436157
10 changed files with 560 additions and 423 deletions

View file

@ -1,79 +1,8 @@
use crate::{util::*, write_ident};
use crate::{util::*, FormattedBlock};
use dioxus_rsx::*;
use std::fmt::Write;
use triple_accel::{levenshtein_search, Match};
#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, PartialEq, Hash)]
pub struct FormattedBlock {
pub formatted: String,
pub start: usize,
pub end: usize,
}
pub fn get_format_blocks(contents: &str) -> Vec<FormattedBlock> {
let matches = levenshtein_search(b"rsx! {", contents.as_bytes()).peekable();
let mut formatted_blocks = Vec::new();
let mut last_bracket_end = 0;
// find the rsx! marker
for Match { start, end, k } in matches {
// ensure the marker is not nested
if start < last_bracket_end {
continue;
}
let remaining = &contents[end - 1..];
let bracket_end = find_bracket_end(remaining).unwrap();
let sub_string = &contents[end..bracket_end + end - 1];
last_bracket_end = bracket_end + end - 1;
let new = fmt_block(sub_string).unwrap();
let stripped = &contents[end + 1..bracket_end + end - 1];
if stripped == new {
continue;
}
// if we have code to push, we want the code to end up on the right lines with the right indentation
let mut output = String::new();
writeln!(output).unwrap();
for line in new.lines() {
writeln!(output, "{}", line).ok();
}
formatted_blocks.push(FormattedBlock {
formatted: output,
start: end,
end: end + bracket_end - 1,
});
}
formatted_blocks
}
struct Isolate<'a> {
contents: &'a str,
start: usize,
end: usize,
}
fn isolate_body_of_rsx(contents: &str, Match { start, end, k }: triple_accel::Match) -> Isolate {
todo!()
}
pub fn fmt_block(block: &str) -> Option<String> {
let mut buf = String::new();
let lines = block.split('\n').collect::<Vec<_>>();
for node in &syn::parse_str::<CallBody>(block).ok()?.roots {
write_ident(&mut buf, &lines, node, 0).ok()?;
}
Some(buf)
}
#[test]
fn format_block_basic() {
let block = r#"
@ -129,7 +58,7 @@ fn format_block_basic() {
// div { class: "asdasd", p { "hello!" } }
let edits = get_format_blocks(block);
// let edits = get_format_blocks(block);
println!("{}", edits[0].formatted);
// println!("{}", edits[0].formatted);
}

View file

@ -1,17 +1,83 @@
use std::fmt::Result;
use std::fmt::{Result, Write};
struct Buffer {
buf: String,
cur_line: usize,
cur_indent: usize,
use dioxus_rsx::BodyNode;
pub struct Buffer {
pub buf: String,
pub line: usize,
pub indent: usize,
}
impl Buffer {
fn write_tabs(&mut self, num: usize) -> Result {
todo!()
pub fn new() -> Self {
Self {
buf: String::new(),
line: 0,
indent: 0,
}
}
fn new_line(&mut self) -> Result {
todo!()
// Create a new line and tab it to the current tab level
pub fn tabbed_line(&mut self) -> Result {
self.new_line()?;
self.tab()
}
// Create a new line and tab it to the current tab level
pub fn indented_tabbed_line(&mut self) -> Result {
self.new_line()?;
self.indented_tab()
}
pub fn tab(&mut self) -> Result {
self.write_tabs(self.indent)
}
pub fn indented_tab(&mut self) -> Result {
self.write_tabs(self.indent + 1)
}
pub fn write_tabs(&mut self, num: usize) -> std::fmt::Result {
for _ in 0..num {
write!(self.buf, " ")?
}
Ok(())
}
pub fn new_line(&mut self) -> Result {
writeln!(self.buf)
}
pub fn write_indented_ident(&mut self, lines: &[&str], node: &BodyNode) -> Result {
self.write_ident(lines, node)?;
Ok(())
}
pub fn write_ident(&mut self, lines: &[&str], node: &BodyNode) -> Result {
match node {
BodyNode::Element(el) => self.write_element(el, lines),
BodyNode::Component(component) => self.write_component(component, lines),
BodyNode::Text(text) => self.write_text(text),
BodyNode::RawExpr(exp) => self.write_raw_expr(exp, lines),
}
}
pub fn write_text(&mut self, text: &syn::LitStr) -> Result {
write!(self.buf, "\"{}\"", text.value())
}
// Push out the indent level and write each component, line by line
pub fn write_body_indented(&mut self, children: &[BodyNode], lines: &[&str]) -> Result {
self.indent += 1;
for child in children {
// Exprs handle their own indenting/line breaks
if !matches!(child, BodyNode::RawExpr(_)) {
self.tabbed_line()?;
}
self.write_ident(lines, child)?;
}
self.indent -= 1;
Ok(())
}
}

View file

@ -1,78 +1,85 @@
use crate::{util::*, write_ident};
use crate::Buffer;
use dioxus_rsx::*;
use quote::ToTokens;
use std::fmt::{self, Write};
use std::fmt::{self, Result, Write};
pub fn write_component(
component: &Component,
buf: &mut String,
indent: usize,
lines: &[&str],
) -> Result<(), fmt::Error> {
let Component {
name,
body,
children,
manual_props,
prop_gen_args,
} = component;
let mut name = name.to_token_stream().to_string();
name.retain(|c| !c.is_whitespace());
write_tabs(buf, indent)?;
write!(buf, "{name}")?;
if let Some(generics) = prop_gen_args {
let mut written = generics.to_token_stream().to_string();
written.retain(|c| !c.is_whitespace());
write!(buf, "{}", written)?;
}
write!(buf, " {{")?;
if !body.is_empty() || !children.is_empty() {
writeln!(buf)?;
}
for field in body {
write_tabs(buf, indent + 1)?;
let name = &field.name;
match &field.content {
ContentField::ManExpr(exp) => {
let out = prettyplease::unparse_expr(exp);
writeln!(buf, "{}: {},", name, out)?;
}
ContentField::Formatted(s) => {
writeln!(buf, "{}: \"{}\",", name, s.value())?;
}
ContentField::OnHandlerRaw(exp) => {
let out = prettyplease::unparse_expr(exp);
let mut lines = out.split('\n').peekable();
let first = lines.next().unwrap();
write!(buf, "{}: {}", name, first)?;
for line in lines {
writeln!(buf)?;
write_tabs(buf, indent + 1)?;
write!(buf, "{}", line)?;
impl Buffer {
pub fn write_component(
&mut self,
Component {
name,
body,
children,
manual_props,
prop_gen_args,
}: &Component,
lines: &[&str],
) -> Result {
let mut name = name.to_token_stream().to_string();
name.retain(|c| !c.is_whitespace());
self.tab()?;
write!(self.buf, "{name}")?;
if let Some(generics) = prop_gen_args {
let mut written = generics.to_token_stream().to_string();
written.retain(|c| !c.is_whitespace());
write!(self.buf, "{}", written)?;
}
write!(self.buf, " {{")?;
if !body.is_empty() || !children.is_empty() {
self.new_line()?;
}
for field in body {
self.indented_tab()?;
let name = &field.name;
match &field.content {
ContentField::ManExpr(exp) => {
let out = prettyplease::unparse_expr(exp);
writeln!(self.buf, "{}: {},", name, out)?;
}
ContentField::Formatted(s) => {
writeln!(self.buf, "{}: \"{}\",", name, s.value())?;
}
ContentField::OnHandlerRaw(exp) => {
let out = prettyplease::unparse_expr(exp);
let mut lines = out.split('\n').peekable();
let first = lines.next().unwrap();
write!(self.buf, "{}: {}", name, first)?;
for line in lines {
self.new_line()?;
self.indented_tab()?;
write!(self.buf, "{}", line)?;
}
writeln!(self.buf, ",")?;
}
writeln!(buf, ",")?;
}
}
}
if let Some(exp) = manual_props {
write_tabs(buf, indent + 1)?;
let out = prettyplease::unparse_expr(exp);
let mut lines = out.split('\n').peekable();
let first = lines.next().unwrap();
write!(buf, "..{}", first)?;
for line in lines {
writeln!(buf)?;
write_tabs(buf, indent + 1)?;
write!(buf, "{}", line)?;
if let Some(exp) = manual_props {
self.indented_tab()?;
let out = prettyplease::unparse_expr(exp);
let mut lines = out.split('\n').peekable();
let first = lines.next().unwrap();
write!(self.buf, "..{}", first)?;
for line in lines {
self.new_line()?;
self.indented_tab()?;
write!(self.buf, "{}", line)?;
}
self.new_line()?;
}
writeln!(buf)?;
for child in children {
self.write_indented_ident(lines, child)?;
}
if !body.is_empty() || !children.is_empty() {
self.tab()?;
}
writeln!(self.buf, "}}")?;
Ok(())
}
for child in children {
write_ident(buf, lines, child, indent + 1)?;
}
if !body.is_empty() || !children.is_empty() {
write_tabs(buf, indent)?;
}
writeln!(buf, "}}")?;
Ok(())
}

View file

@ -1,6 +1,6 @@
use crate::{util::*, write_ident};
use crate::{util::*, Buffer};
use dioxus_rsx::*;
use std::{fmt, fmt::Result, fmt::Write};
use std::{fmt::Result, fmt::Write};
enum ShortOptimization {
// Special because we want to print the closing bracket immediately
@ -16,108 +16,181 @@ enum ShortOptimization {
NoOpt,
}
pub fn write_element(
Element {
name,
key,
attributes,
children,
_is_static,
}: &Element,
buf: &mut String,
lines: &[&str],
indent: usize,
) -> Result {
/*
1. Write the tag
2. Write the key
3. Write the attributes
4. Write the children
*/
impl Buffer {
pub fn write_element(
&mut self,
Element {
name,
key,
attributes,
children,
_is_static,
}: &Element,
lines: &[&str],
) -> Result {
/*
1. Write the tag
2. Write the key
3. Write the attributes
4. Write the children
*/
write!(buf, "{name} {{")?;
write!(self.buf, "{name} {{")?;
// decide if we have any special optimizations
// Default with none, opt the cases in one-by-one
let mut opt_level = ShortOptimization::NoOpt;
// decide if we have any special optimizations
// Default with none, opt the cases in one-by-one
let mut opt_level = ShortOptimization::NoOpt;
// check if we have a lot of attributes
let is_short_attr_list = is_short_attrs(attributes);
let is_small_children = is_short_children(children);
// check if we have a lot of attributes
let is_short_attr_list = is_short_attrs(attributes);
let is_small_children = is_short_children(children);
// if we have few attributes and a lot of children, place the attrs on top
if is_short_attr_list && !is_small_children {
opt_level = ShortOptimization::PropsOnTop;
}
// if we have few children and few attributes, make it a one-liner
if is_short_attr_list && is_small_children {
opt_level = ShortOptimization::Oneliner;
}
// If there's nothing at all, empty optimization
if attributes.is_empty() && children.is_empty() && key.is_none() {
opt_level = ShortOptimization::Empty;
}
match opt_level {
ShortOptimization::Empty => write!(buf, "}}")?,
ShortOptimization::Oneliner => {
write!(buf, " ")?;
write_attributes(buf, attributes, true, indent)?;
if !children.is_empty() && !attributes.is_empty() {
write!(buf, ", ")?;
}
// write the children
for child in children {
write_ident(buf, lines, child, indent + 1)?;
}
write!(buf, " }}")?;
// if we have few attributes and a lot of children, place the attrs on top
if is_short_attr_list && !is_small_children {
opt_level = ShortOptimization::PropsOnTop;
}
ShortOptimization::PropsOnTop => {
write!(buf, " ")?;
write_attributes(buf, attributes, true, indent)?;
if !children.is_empty() && !attributes.is_empty() {
write!(buf, ",")?;
// even if the attr is long, it should be put on one line
if !is_short_attr_list && attributes.len() <= 1 {
if children.is_empty() {
opt_level = ShortOptimization::Oneliner;
} else {
opt_level = ShortOptimization::PropsOnTop;
}
// write the children
for child in children {
writeln!(buf)?;
write_tabs(buf, indent + 1)?;
write_ident(buf, lines, child, indent + 1)?;
}
writeln!(buf)?;
write_tabs(buf, indent)?;
write!(buf, "}}")?;
}
ShortOptimization::NoOpt => {
// write the key
// if we have few children and few attributes, make it a one-liner
if is_short_attr_list && is_small_children {
opt_level = ShortOptimization::Oneliner;
}
// write the attributes
write_attributes(buf, attributes, false, indent)?;
// If there's nothing at all, empty optimization
if attributes.is_empty() && children.is_empty() && key.is_none() {
opt_level = ShortOptimization::Empty;
}
// write the children
for child in children {
writeln!(buf)?;
write_tabs(buf, indent + 1)?;
write_ident(buf, lines, child, indent + 1)?;
match opt_level {
ShortOptimization::Empty => write!(self.buf, "}}")?,
ShortOptimization::Oneliner => {
write!(self.buf, " ")?;
self.write_attributes(attributes, true)?;
if !children.is_empty() && !attributes.is_empty() {
write!(self.buf, ", ")?;
}
// write the children
for child in children {
self.write_ident(lines, child)?;
}
write!(self.buf, " }}")?;
}
writeln!(buf)?;
write_tabs(buf, indent)?;
write!(buf, "}}")?;
ShortOptimization::PropsOnTop => {
write!(self.buf, " ")?;
self.write_attributes(attributes, true)?;
if !children.is_empty() && !attributes.is_empty() {
write!(self.buf, ",")?;
}
// write the children
self.write_body_indented(children, lines)?;
self.tabbed_line()?;
write!(self.buf, "}}")?;
}
ShortOptimization::NoOpt => {
// write the key
// write the attributes
self.write_attributes(attributes, false)?;
self.write_body_indented(children, lines)?;
self.tabbed_line()?;
write!(self.buf, "}}")?;
}
}
Ok(())
}
Ok(())
fn write_attributes(&mut self, attributes: &[ElementAttrNamed], sameline: bool) -> Result {
let mut attr_iter = attributes.iter().peekable();
while let Some(attr) = attr_iter.next() {
if !sameline {
self.indented_tabbed_line()?;
}
self.write_attribute(attr)?;
if attr_iter.peek().is_some() {
write!(self.buf, ",")?;
if sameline {
write!(self.buf, " ")?;
}
}
}
Ok(())
}
fn write_attribute(&mut self, attr: &ElementAttrNamed) -> Result {
match &attr.attr {
ElementAttr::AttrText { name, value } => {
write!(self.buf, "{name}: \"{value}\"", value = value.value())?;
}
ElementAttr::AttrExpression { name, value } => {
let out = prettyplease::unparse_expr(value);
write!(self.buf, "{}: {}", name, out)?;
}
ElementAttr::CustomAttrText { name, value } => {
write!(
self.buf,
"\"{name}\": \"{value}\"",
name = name.value(),
value = value.value()
)?;
}
ElementAttr::CustomAttrExpression { name, value } => {
let out = prettyplease::unparse_expr(value);
write!(self.buf, "\"{}\": {}", name.value(), out)?;
}
ElementAttr::EventTokens { name, tokens } => {
let out = prettyplease::unparse_expr(tokens);
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.buf, "{}: {}", name, first)?;
} else {
writeln!(self.buf, "{}: {}", name, first)?;
while let Some(line) = lines.next() {
self.indented_tab()?;
write!(self.buf, "{}", line)?;
if lines.peek().is_none() {
write!(self.buf, "")?;
} else {
writeln!(self.buf)?;
}
}
}
}
}
Ok(())
}
}
fn is_short_attrs(attrs: &[ElementAttrNamed]) -> bool {
@ -132,106 +205,29 @@ fn is_short_children(children: &[BodyNode]) -> bool {
return true;
}
if children.len() == 1 {
if let BodyNode::Text(ref text) = &children[0] {
return text.value().len() < 80;
}
}
match children {
[BodyNode::Text(ref text)] => text.value().len() < 80,
[BodyNode::Element(ref el)] => {
// && !el.attributes.iter().any(|f| f.attr.is_expr())
false
extract_attr_len(&el.attributes) < 80 && is_short_children(&el.children)
}
_ => false,
}
}
fn write_key() {
// if let Some(key) = key.as_ref().map(|f| f.value()) {
// if is_long_attr_list {
// writeln!(buf)?;
// write_tabs(buf, indent + 1)?;
// self.new_line()?;
// self.write_tabs( indent + 1)?;
// } else {
// write!(buf, " ")?;
// write!(self.buf, " ")?;
// }
// write!(buf, "key: \"{key}\"")?;
// write!(self.buf, "key: \"{key}\"")?;
// if !attributes.is_empty() {
// write!(buf, ",")?;
// write!(self.buf, ",")?;
// }
// }
}
fn write_attributes(
buf: &mut String,
attributes: &[ElementAttrNamed],
sameline: bool,
indent: usize,
) -> Result {
let mut attr_iter = attributes.iter().peekable();
while let Some(attr) = attr_iter.next() {
write_attribute(buf, attr, indent)?;
if attr_iter.peek().is_some() {
write!(buf, ",")?;
if sameline {
write!(buf, " ")?;
} else {
writeln!(buf)?;
write_tabs(buf, indent + 1)?;
}
}
}
Ok(())
}
fn write_attribute(buf: &mut String, attr: &ElementAttrNamed, indent: usize) -> Result {
match &attr.attr {
ElementAttr::AttrText { name, value } => {
write!(buf, "{name}: \"{value}\"", value = value.value())?;
}
ElementAttr::AttrExpression { name, value } => {
let out = prettyplease::unparse_expr(value);
write!(buf, "{}: {}", name, out)?;
}
ElementAttr::CustomAttrText { name, value } => {
write!(
buf,
"\"{name}\": \"{value}\"",
name = name.value(),
value = value.value()
)?;
}
ElementAttr::CustomAttrExpression { name, value } => {
let out = prettyplease::unparse_expr(value);
write!(buf, "\"{}\": {}", name.value(), out)?;
}
ElementAttr::EventTokens { name, tokens } => {
let out = prettyplease::unparse_expr(tokens);
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!(buf, "{}: {}", name, first)?;
} else {
writeln!(buf, "{}: {}", name, first)?;
while let Some(line) = lines.next() {
write_tabs(buf, indent + 1)?;
write!(buf, "{}", line)?;
if lines.peek().is_none() {
write!(buf, "")?;
} else {
writeln!(buf)?;
}
}
}
}
}
Ok(())
}

View file

@ -1,39 +1,53 @@
//! pretty printer for rsx!
use std::fmt::{self, Write};
use std::fmt::{self, Result, Write};
pub fn write_raw_expr(
exp: &syn::Expr,
indent: usize,
lines: &[&str],
buf: &mut String,
) -> Result<(), fmt::Error> {
use syn::spanned::Spanned;
let placement = exp.span();
let start = placement.start();
let end = placement.end();
let num_spaces_desired = (indent * 4) as isize;
let first = lines[start.line - 1];
let num_spaces_real = first.chars().take_while(|c| c.is_whitespace()).count() as isize;
let offset = num_spaces_real - num_spaces_desired;
use crate::Buffer;
for line_id in start.line - 1..end.line {
let line = lines[line_id];
impl Buffer {
pub fn write_raw_expr(&mut self, exp: &syn::Expr, lines: &[&str]) -> Result {
/*
We want to normalize the expr to the appropriate indent level.
*/
// trim the leading whitespace
// in a perfect world, just fire up the rust pretty printer
// pretty_print_rust_code_as_if_it_were_rustfmt()
if offset < 0 {
for _ in 0..-offset {
write!(buf, " ")?;
use syn::spanned::Spanned;
let placement = exp.span();
let start = placement.start();
let end = placement.end();
let num_spaces_desired = (self.indent * 4) as isize;
let first = lines[start.line - 1];
let num_spaces_real = first.chars().take_while(|c| c.is_whitespace()).count() as isize;
let offset = num_spaces_real - num_spaces_desired;
for line in &lines[start.line - 1..end.line] {
writeln!(self.buf)?;
// trim the leading whitespace
if offset < 0 {
for _ in 0..-offset {
write!(self.buf, " ")?;
}
write!(self.buf, "{}", line)?;
} else {
let offset = offset as usize;
let right = &line[offset..];
write!(self.buf, "{}", right)?;
}
writeln!(buf, "{}", line)?;
} else {
let offset = offset as usize;
let right = &line[offset..];
writeln!(buf, "{}", right)?;
}
}
Ok(())
Ok(())
}
}
// :(
// fn pretty_print_rust_code_as_if_it_were_rustfmt(code: &str) -> String {
// let formatted = prettyplease::unparse_expr(exp);
// for line in formatted.lines() {
// write!(self.buf, "{}", line)?;
// self.new_line()?;
// }
// }

View file

@ -1,23 +0,0 @@
use crate::{
component::write_component, element::write_element, expr::write_raw_expr, util::write_tabs,
};
use dioxus_rsx::*;
use std::fmt::{self, Write};
pub fn write_ident(
buf: &mut String,
lines: &[&str],
node: &BodyNode,
indent: usize,
) -> fmt::Result {
match node {
BodyNode::Element(el) => write_element(el, buf, lines, indent),
BodyNode::Component(component) => write_component(component, buf, indent, lines),
BodyNode::Text(text) => write_text(text, buf, indent),
BodyNode::RawExpr(exp) => write_raw_expr(exp, indent, lines, buf),
}
}
fn write_text(text: &syn::LitStr, buf: &mut String, indent: usize) -> fmt::Result {
write!(buf, "\"{}\"", text.value())
}

View file

@ -1,8 +1,7 @@
//! pretty printer for rsx code
use dioxus_rsx::*;
use proc_macro2::TokenStream as TokenStream2;
use quote::ToTokens;
pub use crate::buffer::*;
use crate::util::*;
mod block;
mod buffer;
@ -10,8 +9,75 @@ mod children;
mod component;
mod element;
mod expr;
mod ident;
mod util;
pub use block::{fmt_block, get_format_blocks};
pub use ident::write_ident;
// pub use block::{fmt_block, get_format_blocks};
/// A modification to the original file to be applied by an IDE
///
/// Right now this re-writes entire rsx! blocks at a time, instead of precise line-by-line changes.
///
/// In a "perfect" world we would have tiny edits to preserve things like cursor states and selections. The API here makes
/// it possible to migrate to a more precise modification approach in the future without breaking existing code.
///
/// Note that this is tailored to VSCode's TextEdit API and not a general Diff API. Line numbers are not accurate if
/// multiple edits are applied in a single file without tracking text shifts.
#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, PartialEq, Hash)]
pub struct FormattedBlock {
/// The new contents of the block
pub formatted: String,
/// The line number of the first line of the block.
pub start: usize,
/// The end of the block, exclusive.
pub end: usize,
}
/// Format a file into a list of `FormattedBlock`s to be applied by an IDE for autoformatting.
///
/// This function expects a complete file, not just a block of code. To format individual rsx! blocks, use fmt_block instead.
pub fn fmt_file(contents: &str) -> Vec<FormattedBlock> {
let mut formatted_blocks = Vec::new();
let mut last_bracket_end = 0;
use triple_accel::{levenshtein_search, Match};
for Match { end, k, start } in levenshtein_search(b"rsx! {", contents.as_bytes()) {
// ensure the marker is not nested
if start < last_bracket_end {
continue;
}
let remaining = &contents[end - 1..];
let bracket_end = find_bracket_end(remaining).unwrap();
let sub_string = &contents[end..bracket_end + end - 1];
last_bracket_end = bracket_end + end - 1;
let new = fmt_block(sub_string).unwrap();
let stripped = &contents[end + 1..bracket_end + end - 1];
if stripped == new {
continue;
}
formatted_blocks.push(FormattedBlock {
formatted: new,
start: end,
end: end + bracket_end - 1,
});
}
formatted_blocks
}
pub fn fmt_block(block: &str) -> Option<String> {
let mut buf = Buffer::new();
let lines = block.split('\n').collect::<Vec<_>>();
for node in &syn::parse_str::<dioxus_rsx::CallBody>(block).ok()?.roots {
buf.write_ident(&lines, node).ok()?;
}
Some(buf.buf)
}

View file

@ -14,13 +14,6 @@ pub fn extract_attr_len(attributes: &[ElementAttrNamed]) -> usize {
.sum()
}
pub fn write_tabs(f: &mut dyn Write, num: usize) -> std::fmt::Result {
for _ in 0..num {
write!(f, " ")?
}
Ok(())
}
pub fn find_bracket_end(contents: &str) -> Option<usize> {
let mut depth = 0;
let mut i = 0;

View file

@ -2,6 +2,16 @@ use dioxus_autofmt::*;
use proc_macro2::TokenStream as TokenStream2;
use syn::{Attribute, Meta};
fn test_block(wrong: &str, right: &str) {
let formatted = fmt_block(wrong).unwrap();
assert_eq!(formatted, right);
}
fn print_block(wrong: &str) {
let formatted = fmt_block(wrong).unwrap();
println!("{}", formatted);
}
#[test]
fn formats_block() {
let block = r#"
@ -46,7 +56,7 @@ fn formats_block() {
let formatted = fmt_block(block).unwrap();
print!("{formatted}");
println!("{formatted}");
}
#[test]
@ -62,6 +72,54 @@ fn parse_comment() {
dbg!(parsed);
}
#[test]
fn print_cases() {
print_block(
r#"
div {
adsasd: "asd",
h1 {"asd"}
div {
div {
"hello"
}
div {
"goodbye"
}
div { class: "broccoli",
div {
"hi"
}
}
div { class: "broccolibroccolibroccolibroccolibroccolibroccolibroccolibroccolibroccolibroccoli",
div {
"hi"
}
}
div { class: "alksdjasd", onclick: move |_| {
liberty!();
},
div {
"hi"
}
}
commented{
// is unparalled
class: "asdasd",
// My genius
div {
"hi"
}
}
}
}
"#,
);
}
#[test]
fn formats_component() {
let block = r#"
@ -76,7 +134,7 @@ fn formats_component() {
let formatted = fmt_block(block).unwrap();
print!("{formatted}");
println!("{formatted}");
}
#[test]
@ -97,7 +155,7 @@ fn formats_element() {
let formatted = fmt_block(block).unwrap();
print!("{formatted}");
println!("{formatted}");
}
#[test]
@ -118,7 +176,7 @@ fn formats_element_short() {
let formatted = fmt_block(block).unwrap();
print!("{formatted}");
println!("{formatted}");
}
#[test]
@ -132,7 +190,25 @@ fn formats_element_nested() {
let formatted = fmt_block(block).unwrap();
print!("{formatted}");
assert_eq!(
formatted,
r#"h3 { class: "mb-2 text-xl font-bold", "Invite Member" }"#
);
}
#[test]
fn formats_element_props_on_top() {
let block = r#"
h3 {
class: "mb-2 text-xl font-bold mb-2 text-xl font-bold mb-2 text-xl font-bold mb-2 text-xl font-bold mb-2 text-xl font-bold",
"Invite Member"
}
"#;
let formatted = fmt_block(block).unwrap();
println!("{formatted}");
}
#[test]
@ -143,7 +219,10 @@ fn formats_element_nested_no_trailing_tabs() {
let formatted = fmt_block(block).unwrap();
print!("{formatted}");
assert_eq!(
formatted,
r#"img { class: "mb-6 mx-auto h-24", src: "artemis-assets/images/friends.png", alt: "" }"#
);
}
#[test]
@ -160,20 +239,20 @@ fn formats_element_with_correct_indent() {
let formatted = fmt_block(block).unwrap();
print!("{formatted}");
println!("{formatted}");
}
#[test]
fn small_elements_and_text_are_small() {
let block = r###"
a { class: " text-white",
a { class: "text-white",
"Send invitation"
}
"###;
let formatted = fmt_block(block).unwrap();
print!("{formatted}");
assert_eq!(formatted, r#"a { class: "text-white", "Send invitation" }"#);
}
#[test]
@ -192,7 +271,7 @@ fn formats_component_man_props() {
let formatted = fmt_block(block).unwrap();
print!("{formatted}");
println!("{formatted}");
}
#[test]
@ -204,24 +283,24 @@ fn formats_component_tiny() {
let formatted = fmt_block(block).unwrap();
print!("{formatted}");
println!("{formatted}");
}
#[test]
fn formats_exprs() {
let block = r#"
ul {
(0..10).map(|f| rsx!{
li {
"hi"
}
div {}
(0..10).map(|f| rsx! {
li { "hi" }
})
div {}
}
"#;
let formatted = fmt_block(block).unwrap();
print!("{formatted}");
println!("{formatted}");
}
#[test]
@ -238,7 +317,7 @@ fn formats_exprs_neg_indent() {
let formatted = fmt_block(block).unwrap();
print!("{formatted}");
println!("{formatted}");
}
#[test]
@ -261,7 +340,7 @@ fn formats_exprs_handlers() {
let formatted = fmt_block(block).unwrap();
print!("{formatted}");
println!("{formatted}");
}
#[test]
@ -296,7 +375,7 @@ fn formats_complex() {
let formatted = fmt_block(block).unwrap();
print!("{formatted}");
println!("{formatted}");
}
#[test]
@ -314,9 +393,9 @@ rsx!{
"#;
let formatted = get_format_blocks(block);
let formatted = fmt_file(block);
print!("{formatted:?}");
println!("{formatted:?}");
}
#[test]
@ -332,9 +411,9 @@ rsx!{
}
"#;
let formatted = get_format_blocks(block);
let formatted = fmt_file(block);
print!("{formatted:?}");
println!("{formatted:?}");
}
#[test]
@ -350,7 +429,7 @@ rsx! {
}
"#;
let formatted = get_format_blocks(src);
let formatted = fmt_file(src);
println!("{formatted:?}");
}
@ -378,7 +457,7 @@ fn NavItem<'a>(cx: Scope, to: &'static str, children: Element<'a>, icon: Shape)
"#
.to_string();
let formatted = get_format_blocks(&src);
let formatted = fmt_file(&src);
let block = formatted.into_iter().next().unwrap();
@ -446,7 +525,7 @@ fn NavItem<'a>(cx: Scope, to: &'static str, children: Element<'a>, icon: Shape)
"#
.to_string();
let formatted = get_format_blocks(&src);
let formatted = fmt_file(&src);
dbg!(&formatted);
@ -469,7 +548,7 @@ pub fn Alert(cx: Scope) -> Element {
"###
.to_string();
let formatted = get_format_blocks(&src);
let formatted = fmt_file(&src);
dbg!(&formatted);
}

View file

@ -209,6 +209,16 @@ pub enum ElementAttr {
/// onclick: {}
EventTokens { name: Ident, tokens: Expr },
}
impl ElementAttr {
pub fn is_expr(&self) -> bool {
matches!(
self,
ElementAttr::AttrExpression { .. }
| ElementAttr::CustomAttrExpression { .. }
| ElementAttr::EventTokens { .. }
)
}
}
#[derive(PartialEq, Eq)]
pub struct ElementAttrNamed {