wip: more cleanup, more tests

This commit is contained in:
Jonathan Kelley committed 2022-06-27 16:24:36 -04:00
1 parent a2a194ca40
commit d70b436157
10 files changed
+560 -423

No files matched your search

+3 -74
View File
@@ -1,79 +1,8 @@
use crate::{util::*, write_ident}; use crate::{util::*, FormattedBlock};
use dioxus_rsx::*; use dioxus_rsx::*;
use std::fmt::Write; use std::fmt::Write;
use triple_accel::{levenshtein_search, Match}; 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] #[test]
fn format_block_basic() { fn format_block_basic() {
let block = r#" let block = r#"
@@ -129,7 +58,7 @@ fn format_block_basic() {
// div { class: "asdasd", p { "hello!" } } // 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);
} }
+75 -9
View File
@@ -1,17 +1,83 @@
use std::fmt::Result; use std::fmt::{Result, Write};
struct Buffer { use dioxus_rsx::BodyNode;
buf: String,
cur_line: usize, pub struct Buffer {
cur_indent: usize, pub buf: String,
pub line: usize,
pub indent: usize,
} }
impl Buffer { impl Buffer {
fn write_tabs(&mut self, num: usize) -> Result { pub fn new() -> Self {
todo!() Self {
buf: String::new(),
line: 0,
indent: 0,
}
} }
fn new_line(&mut self) -> Result { // Create a new line and tab it to the current tab level
todo!() 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(())
} }
} }
+76 -69
View File
@@ -1,78 +1,85 @@
use crate::{util::*, write_ident}; use crate::Buffer;
use dioxus_rsx::*; use dioxus_rsx::*;
use quote::ToTokens; use quote::ToTokens;
use std::fmt::{self, Write}; use std::fmt::{self, Result, Write};
pub fn write_component( impl Buffer {
component: &Component, pub fn write_component(
buf: &mut String, &mut self,
indent: usize, Component {
lines: &[&str], name,
) -> Result<(), fmt::Error> { body,
let Component { children,
name, manual_props,
body, prop_gen_args,
children, }: &Component,
manual_props, lines: &[&str],
prop_gen_args, ) -> Result {
} = component; let mut name = name.to_token_stream().to_string();
let mut name = name.to_token_stream().to_string(); name.retain(|c| !c.is_whitespace());
name.retain(|c| !c.is_whitespace()); self.tab()?;
write_tabs(buf, indent)?; write!(self.buf, "{name}")?;
write!(buf, "{name}")?;
if let Some(generics) = prop_gen_args { if let Some(generics) = prop_gen_args {
let mut written = generics.to_token_stream().to_string(); let mut written = generics.to_token_stream().to_string();
written.retain(|c| !c.is_whitespace()); written.retain(|c| !c.is_whitespace());
write!(buf, "{}", written)?; write!(self.buf, "{}", written)?;
} }
write!(buf, " {{")?;
if !body.is_empty() || !children.is_empty() { write!(self.buf, " {{")?;
writeln!(buf)?;
} if !body.is_empty() || !children.is_empty() {
for field in body { self.new_line()?;
write_tabs(buf, indent + 1)?; }
let name = &field.name;
match &field.content { for field in body {
ContentField::ManExpr(exp) => { self.indented_tab()?;
let out = prettyplease::unparse_expr(exp); let name = &field.name;
writeln!(buf, "{}: {},", name, out)?; match &field.content {
} ContentField::ManExpr(exp) => {
ContentField::Formatted(s) => { let out = prettyplease::unparse_expr(exp);
writeln!(buf, "{}: \"{}\",", name, s.value())?; writeln!(self.buf, "{}: {},", name, out)?;
} }
ContentField::OnHandlerRaw(exp) => { ContentField::Formatted(s) => {
let out = prettyplease::unparse_expr(exp); writeln!(self.buf, "{}: \"{}\",", name, s.value())?;
let mut lines = out.split('\n').peekable(); }
let first = lines.next().unwrap(); ContentField::OnHandlerRaw(exp) => {
write!(buf, "{}: {}", name, first)?; let out = prettyplease::unparse_expr(exp);
for line in lines { let mut lines = out.split('\n').peekable();
writeln!(buf)?; let first = lines.next().unwrap();
write_tabs(buf, indent + 1)?; write!(self.buf, "{}: {}", name, first)?;
write!(buf, "{}", line)?; 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 { if let Some(exp) = manual_props {
write_tabs(buf, indent + 1)?; self.indented_tab()?;
let out = prettyplease::unparse_expr(exp); let out = prettyplease::unparse_expr(exp);
let mut lines = out.split('\n').peekable(); let mut lines = out.split('\n').peekable();
let first = lines.next().unwrap(); let first = lines.next().unwrap();
write!(buf, "..{}", first)?; write!(self.buf, "..{}", first)?;
for line in lines { for line in lines {
writeln!(buf)?; self.new_line()?;
write_tabs(buf, indent + 1)?; self.indented_tab()?;
write!(buf, "{}", line)?; 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(())
} }
+174 -178
View File
@@ -1,6 +1,6 @@
use crate::{util::*, write_ident}; use crate::{util::*, Buffer};
use dioxus_rsx::*; use dioxus_rsx::*;
use std::{fmt, fmt::Result, fmt::Write}; use std::{fmt::Result, fmt::Write};
enum ShortOptimization { enum ShortOptimization {
// Special because we want to print the closing bracket immediately // Special because we want to print the closing bracket immediately
@@ -16,108 +16,181 @@ enum ShortOptimization {
NoOpt, NoOpt,
} }
pub fn write_element( impl Buffer {
Element { pub fn write_element(
name, &mut self,
key, Element {
attributes, name,
children, key,
_is_static, attributes,
}: &Element, children,
buf: &mut String, _is_static,
lines: &[&str], }: &Element,
indent: usize, lines: &[&str],
) -> Result { ) -> Result {
/* /*
1. Write the tag 1. Write the tag
2. Write the key 2. Write the key
3. Write the attributes 3. Write the attributes
4. Write the children 4. Write the children
*/ */
write!(buf, "{name} {{")?; write!(self.buf, "{name} {{")?;
// decide if we have any special optimizations // decide if we have any special optimizations
// Default with none, opt the cases in one-by-one // Default with none, opt the cases in one-by-one
let mut opt_level = ShortOptimization::NoOpt; let mut opt_level = ShortOptimization::NoOpt;
// check if we have a lot of attributes // check if we have a lot of attributes
let is_short_attr_list = is_short_attrs(attributes); let is_short_attr_list = is_short_attrs(attributes);
let is_small_children = is_short_children(children); let is_small_children = is_short_children(children);
// if we have few attributes and a lot of children, place the attrs on top // if we have few attributes and a lot of children, place the attrs on top
if is_short_attr_list && !is_small_children { if is_short_attr_list && !is_small_children {
opt_level = ShortOptimization::PropsOnTop; 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, " }}")?;
} }
ShortOptimization::PropsOnTop => { // even if the attr is long, it should be put on one line
write!(buf, " ")?; if !is_short_attr_list && attributes.len() <= 1 {
write_attributes(buf, attributes, true, indent)?; if children.is_empty() {
opt_level = ShortOptimization::Oneliner;
if !children.is_empty() && !attributes.is_empty() { } else {
write!(buf, ",")?; 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 => { // if we have few children and few attributes, make it a one-liner
// write the key if is_short_attr_list && is_small_children {
opt_level = ShortOptimization::Oneliner;
}
// write the attributes // If there's nothing at all, empty optimization
write_attributes(buf, attributes, false, indent)?; if attributes.is_empty() && children.is_empty() && key.is_none() {
opt_level = ShortOptimization::Empty;
}
// write the children match opt_level {
for child in children { ShortOptimization::Empty => write!(self.buf, "}}")?,
writeln!(buf)?; ShortOptimization::Oneliner => {
write_tabs(buf, indent + 1)?; write!(self.buf, " ")?;
write_ident(buf, lines, child, indent + 1)?; 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)?; ShortOptimization::PropsOnTop => {
write_tabs(buf, indent)?; write!(self.buf, " ")?;
write!(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 { fn is_short_attrs(attrs: &[ElementAttrNamed]) -> bool {
@@ -132,106 +205,29 @@ fn is_short_children(children: &[BodyNode]) -> bool {
return true; return true;
} }
if children.len() == 1 { match children {
if let BodyNode::Text(ref text) = &children[0] { [BodyNode::Text(ref text)] => text.value().len() < 80,
return 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() { fn write_key() {
// if let Some(key) = key.as_ref().map(|f| f.value()) { // if let Some(key) = key.as_ref().map(|f| f.value()) {
// if is_long_attr_list { // if is_long_attr_list {
// writeln!(buf)?; // self.new_line()?;
// write_tabs(buf, indent + 1)?; // self.write_tabs( indent + 1)?;
// } else { // } else {
// write!(buf, " ")?; // write!(self.buf, " ")?;
// } // }
// write!(buf, "key: \"{key}\"")?; // write!(self.buf, "key: \"{key}\"")?;
// if !attributes.is_empty() { // 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(())
}
+44 -30
View File
@@ -1,39 +1,53 @@
//! pretty printer for rsx! //! pretty printer for rsx!
use std::fmt::{self, Write}; use std::fmt::{self, Result, Write};
pub fn write_raw_expr( use crate::Buffer;
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;
for line_id in start.line - 1..end.line { impl Buffer {
let line = lines[line_id]; 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 { use syn::spanned::Spanned;
for _ in 0..-offset { let placement = exp.span();
write!(buf, " ")?; 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()?;
// }
// }
-23
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())
}
+72 -6
View File
@@ -1,8 +1,7 @@
//! pretty printer for rsx code //! pretty printer for rsx code
use dioxus_rsx::*; pub use crate::buffer::*;
use proc_macro2::TokenStream as TokenStream2; use crate::util::*;
use quote::ToTokens;
mod block; mod block;
mod buffer; mod buffer;
@@ -10,8 +9,75 @@ mod children;
mod component; mod component;
mod element; mod element;
mod expr; mod expr;
mod ident;
mod util; mod util;
pub use block::{fmt_block, get_format_blocks}; // pub use block::{fmt_block, get_format_blocks};
pub use ident::write_ident;
/// 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)
}
-7
View File
@@ -14,13 +14,6 @@ pub fn extract_attr_len(attributes: &[ElementAttrNamed]) -> usize {
.sum() .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> { pub fn find_bracket_end(contents: &str) -> Option<usize> {
let mut depth = 0; let mut depth = 0;
let mut i = 0; let mut i = 0;
+106 -27
View File
@@ -2,6 +2,16 @@ use dioxus_autofmt::*;
use proc_macro2::TokenStream as TokenStream2; use proc_macro2::TokenStream as TokenStream2;
use syn::{Attribute, Meta}; 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] #[test]
fn formats_block() { fn formats_block() {
let block = r#" let block = r#"
@@ -46,7 +56,7 @@ fn formats_block() {
let formatted = fmt_block(block).unwrap(); let formatted = fmt_block(block).unwrap();
print!("{formatted}"); println!("{formatted}");
} }
#[test] #[test]
@@ -62,6 +72,54 @@ fn parse_comment() {
dbg!(parsed); 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] #[test]
fn formats_component() { fn formats_component() {
let block = r#" let block = r#"
@@ -76,7 +134,7 @@ fn formats_component() {
let formatted = fmt_block(block).unwrap(); let formatted = fmt_block(block).unwrap();
print!("{formatted}"); println!("{formatted}");
} }
#[test] #[test]
@@ -97,7 +155,7 @@ fn formats_element() {
let formatted = fmt_block(block).unwrap(); let formatted = fmt_block(block).unwrap();
print!("{formatted}"); println!("{formatted}");
} }
#[test] #[test]
@@ -118,7 +176,7 @@ fn formats_element_short() {
let formatted = fmt_block(block).unwrap(); let formatted = fmt_block(block).unwrap();
print!("{formatted}"); println!("{formatted}");
} }
#[test] #[test]
@@ -132,7 +190,25 @@ fn formats_element_nested() {
let formatted = fmt_block(block).unwrap(); 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] #[test]
@@ -143,7 +219,10 @@ fn formats_element_nested_no_trailing_tabs() {
let formatted = fmt_block(block).unwrap(); 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] #[test]
@@ -160,20 +239,20 @@ fn formats_element_with_correct_indent() {
let formatted = fmt_block(block).unwrap(); let formatted = fmt_block(block).unwrap();
print!("{formatted}"); println!("{formatted}");
} }
#[test] #[test]
fn small_elements_and_text_are_small() { fn small_elements_and_text_are_small() {
let block = r###" let block = r###"
a { class: " text-white", a { class: "text-white",
"Send invitation" "Send invitation"
} }
"###; "###;
let formatted = fmt_block(block).unwrap(); let formatted = fmt_block(block).unwrap();
print!("{formatted}"); assert_eq!(formatted, r#"a { class: "text-white", "Send invitation" }"#);
} }
#[test] #[test]
@@ -192,7 +271,7 @@ fn formats_component_man_props() {
let formatted = fmt_block(block).unwrap(); let formatted = fmt_block(block).unwrap();
print!("{formatted}"); println!("{formatted}");
} }
#[test] #[test]
@@ -204,24 +283,24 @@ fn formats_component_tiny() {
let formatted = fmt_block(block).unwrap(); let formatted = fmt_block(block).unwrap();
print!("{formatted}"); println!("{formatted}");
} }
#[test] #[test]
fn formats_exprs() { fn formats_exprs() {
let block = r#" let block = r#"
ul { ul {
(0..10).map(|f| rsx!{ div {}
li { (0..10).map(|f| rsx! {
"hi" li { "hi" }
}
}) })
div {}
} }
"#; "#;
let formatted = fmt_block(block).unwrap(); let formatted = fmt_block(block).unwrap();
print!("{formatted}"); println!("{formatted}");
} }
#[test] #[test]
@@ -238,7 +317,7 @@ fn formats_exprs_neg_indent() {
let formatted = fmt_block(block).unwrap(); let formatted = fmt_block(block).unwrap();
print!("{formatted}"); println!("{formatted}");
} }
#[test] #[test]
@@ -261,7 +340,7 @@ fn formats_exprs_handlers() {
let formatted = fmt_block(block).unwrap(); let formatted = fmt_block(block).unwrap();
print!("{formatted}"); println!("{formatted}");
} }
#[test] #[test]
@@ -296,7 +375,7 @@ fn formats_complex() {
let formatted = fmt_block(block).unwrap(); let formatted = fmt_block(block).unwrap();
print!("{formatted}"); println!("{formatted}");
} }
#[test] #[test]
@@ -314,9 +393,9 @@ rsx!{
"#; "#;
let formatted = get_format_blocks(block); let formatted = fmt_file(block);
print!("{formatted:?}"); println!("{formatted:?}");
} }
#[test] #[test]
@@ -332,9 +411,9 @@ rsx!{
} }
"#; "#;
let formatted = get_format_blocks(block); let formatted = fmt_file(block);
print!("{formatted:?}"); println!("{formatted:?}");
} }
#[test] #[test]
@@ -350,7 +429,7 @@ rsx! {
} }
"#; "#;
let formatted = get_format_blocks(src); let formatted = fmt_file(src);
println!("{formatted:?}"); println!("{formatted:?}");
} }
@@ -378,7 +457,7 @@ fn NavItem<'a>(cx: Scope, to: &'static str, children: Element<'a>, icon: Shape)
"# "#
.to_string(); .to_string();
let formatted = get_format_blocks(&src); let formatted = fmt_file(&src);
let block = formatted.into_iter().next().unwrap(); 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(); .to_string();
let formatted = get_format_blocks(&src); let formatted = fmt_file(&src);
dbg!(&formatted); dbg!(&formatted);
@@ -469,7 +548,7 @@ pub fn Alert(cx: Scope) -> Element {
"### "###
.to_string(); .to_string();
let formatted = get_format_blocks(&src); let formatted = fmt_file(&src);
dbg!(&formatted); dbg!(&formatted);
} }
+10
View File
@@ -209,6 +209,16 @@ pub enum ElementAttr {
/// onclick: {} /// onclick: {}
EventTokens { name: Ident, tokens: Expr }, EventTokens { name: Ident, tokens: Expr },
} }
impl ElementAttr {
pub fn is_expr(&self) -> bool {
matches!(
self,
ElementAttr::AttrExpression { .. }
| ElementAttr::CustomAttrExpression { .. }
| ElementAttr::EventTokens { .. }
)
}
}
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq)]
pub struct ElementAttrNamed { pub struct ElementAttrNamed {