Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes to autofmt, make it more aggressive #2230

Merged
merged 4 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/autofmt/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
// dont let our extension kick on the rsx files - we're fixing it!
"dioxus.formatOnSave": "disabled",
// enable this when modifying tab-based indentation
// When inside .rsx files, dont automatically use spaces
// "files.autoSave": "off"
}
9 changes: 8 additions & 1 deletion packages/autofmt/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,11 @@ impl Writer<'_> {
write!(self.out, ", ")?;
}

for child in children {
for (id, child) in children.iter().enumerate() {
self.write_ident(child)?;
if id != children.len() - 1 && children.len() > 1 {
write!(self.out, ", ")?;
}
}

write!(self.out, " ")?;
Expand Down Expand Up @@ -163,6 +166,10 @@ impl Writer<'_> {

let name = &field.name;
match &field.content {
ContentField::ManExpr(_exp) if field.can_be_shorthand() => {
write!(self.out, "{name}")?;
}

ContentField::ManExpr(exp) => {
let out = unparse_expr(exp);
let mut lines = out.split('\n').peekable();
Expand Down
98 changes: 36 additions & 62 deletions packages/autofmt/src/element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,13 @@ impl Writer<'_> {
let children_len = self.is_short_children(children);
let is_small_children = children_len.is_some();

// if we have few attributes and a lot of children, place the attrs on top
// if we have one long attribute and a lot of children, place the attrs on top
if is_short_attr_list && !is_small_children {
opt_level = ShortOptimization::PropsOnTop;
}

// even if the attr is long, it should be put on one line
// However if we have childrne we need to just spread them out for readability
if !is_short_attr_list && attributes.len() <= 1 {
if children.is_empty() {
opt_level = ShortOptimization::Oneliner;
Expand Down Expand Up @@ -303,8 +304,12 @@ impl Writer<'_> {

fn write_named_attribute(&mut self, attr: &ElementAttrNamed) -> Result {
self.write_attribute_name(&attr.attr.name)?;
write!(self.out, ": ")?;
self.write_attribute_value(&attr.attr.value)?;

// if the attribute is a shorthand, we don't need to write the colon, just the name
if !attr.attr.can_be_shorthand() {
write!(self.out, ": ")?;
self.write_attribute_value(&attr.attr.value)?;
}

Ok(())
}
Expand Down Expand Up @@ -332,10 +337,6 @@ impl Writer<'_> {
beginning.is_empty()
}

pub fn is_empty_children(&self, children: &[BodyNode]) -> bool {
children.is_empty()
}

// check if the children are short enough to be on the same line
// We don't have the notion of current line depth - each line tries to be < 80 total
// returns the total line length if it's short
Expand All @@ -352,74 +353,47 @@ impl Writer<'_> {
return Some(0);
}

for child in children {
if self.current_span_is_primary(child.span()) {
'line: for line in self.src[..child.span().start().line - 1].iter().rev() {
match (line.trim().starts_with("//"), line.is_empty()) {
(true, _) => return None,
(_, true) => continue 'line,
_ => break 'line,
}
}
}
// Any comments push us over the limit automatically
if self.children_have_comments(children) {
return None;
}

match children {
[BodyNode::Text(ref text)] => Some(ifmt_to_string(text).len()),
[BodyNode::Component(ref comp)] => {
let attr_len = self.field_len(&comp.fields, &comp.manual_props);

if attr_len > 80 {
None
} else if comp.children.is_empty() {
Some(attr_len)
} else {
None
}
}
// TODO: let rawexprs to be inlined
[BodyNode::RawExpr(ref expr)] => get_expr_length(expr),
[BodyNode::Element(ref el)] => {
let attr_len = self.is_short_attrs(&el.attributes);

if el.children.is_empty() && attr_len < 80 {
return Some(el.name.to_string().len());
}

if el.children.len() == 1 {
if let BodyNode::Text(ref text) = el.children[0] {
let value = ifmt_to_string(text);

if value.len() + el.name.to_string().len() + attr_len < 80 {
return Some(value.len() + el.name.to_string().len() + attr_len);
}
}
}
// TODO: let rawexprs to be inlined
[BodyNode::Component(ref comp)] if comp.fields.is_empty() => Some(
comp.name
.segments
.iter()
.map(|s| s.ident.to_string().len() + 2)
.sum::<usize>(),
),

// Feedback on discord indicates folks don't like combining multiple children on the same line
// We used to do a lot of math to figure out if we should expand out the line, but folks just
// don't like it.
_ => None,
}
}

None
}
// todo, allow non-elements to be on the same line
items => {
let mut total_count = 0;

for item in items {
match item {
BodyNode::Component(_) | BodyNode::Element(_) => return None,
BodyNode::Text(text) => {
total_count += ifmt_to_string(text).len();
}
BodyNode::RawExpr(expr) => match get_expr_length(expr) {
Some(len) => total_count += len,
None => return None,
},
BodyNode::ForLoop(_forloop) => return None,
BodyNode::IfChain(_chain) => return None,
fn children_have_comments(&self, children: &[BodyNode]) -> bool {
for child in children {
if self.current_span_is_primary(child.span()) {
'line: for line in self.src[..child.span().start().line - 1].iter().rev() {
match (line.trim().starts_with("//"), line.is_empty()) {
(true, _) => return true,
(_, true) => continue 'line,
_ => break 'line,
}
}

Some(total_count)
}
}

false
}

/// empty everything except for some comments
Expand Down
21 changes: 7 additions & 14 deletions packages/autofmt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,21 +141,14 @@ pub fn write_block_out(body: CallBody) -> Option<String> {
}

fn write_body(buf: &mut Writer, body: &CallBody) {
let is_short = buf.is_short_children(&body.roots).is_some();
let is_empty = buf.is_empty_children(&body.roots);
if (is_short && !buf.out.indent.split_line_attributes()) || is_empty {
// write all the indents with spaces and commas between
for idx in 0..body.roots.len() - 1 {
let ident = &body.roots[idx];
buf.write_ident(ident).unwrap();
write!(&mut buf.out.buf, ", ").unwrap();
match body.roots.len() {
0 => {}
1 if matches!(body.roots[0], BodyNode::Text(_)) => {
write!(buf.out, " ").unwrap();
buf.write_ident(&body.roots[0]).unwrap();
write!(buf.out, " ").unwrap();
}

// write the last ident without a comma
let ident = &body.roots[body.roots.len() - 1];
buf.write_ident(ident).unwrap();
} else {
buf.write_body_indented(&body.roots).unwrap();
_ => buf.write_body_indented(&body.roots).unwrap(),
}
}

Expand Down
21 changes: 17 additions & 4 deletions packages/autofmt/src/prettier_please.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,20 @@ pub fn unparse_expr(expr: &Expr) -> String {

// Split off the fn main and then cut the tabs off the front
fn unwrapped(raw: String) -> String {
raw.strip_prefix("fn main() {\n")
let mut o = raw
.strip_prefix("fn main() {\n")
.unwrap()
.strip_suffix("}\n")
.unwrap()
.lines()
.map(|line| line.strip_prefix(" ").unwrap()) // todo: set this to tab level
.collect::<Vec<_>>()
.join("\n")
.join("\n");

// remove the semicolon
o.pop();

o
}

fn wrapped(expr: &Expr) -> File {
Expand All @@ -31,7 +37,7 @@ fn wrapped(expr: &Expr) -> File {
//
Item::Verbatim(quote::quote! {
fn main() {
#expr
#expr;
}
}),
],
Expand All @@ -42,7 +48,7 @@ fn wrapped(expr: &Expr) -> File {
fn unparses_raw() {
let expr = syn::parse_str("1 + 1").unwrap();
let unparsed = unparse(&wrapped(&expr));
assert_eq!(unparsed, "fn main() {\n 1 + 1\n}\n");
assert_eq!(unparsed, "fn main() {\n 1 + 1;\n}\n");
}

#[test]
Expand All @@ -52,6 +58,13 @@ fn unparses_completely() {
assert_eq!(unparsed, "1 + 1");
}

#[test]
fn unparses_let_guard() {
let expr = syn::parse_str("let Some(url) = &link.location").unwrap();
let unparsed = unparse_expr(&expr);
assert_eq!(unparsed, "let Some(url) = &link.location");
}

#[test]
fn weird_ifcase() {
let contents = r##"
Expand Down
52 changes: 43 additions & 9 deletions packages/autofmt/src/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,11 @@ impl<'a> Writer<'a> {
pub(crate) fn is_short_attrs(&mut self, attributes: &[AttributeType]) -> usize {
let mut total = 0;

// No more than 3 attributes before breaking the line
if attributes.len() > 3 {
return 100000;
}

for attr in attributes {
if self.current_span_is_primary(attr.start()) {
'line: for line in self.src[..attr.start().start().line - 1].iter().rev() {
Expand All @@ -209,7 +214,13 @@ impl<'a> Writer<'a> {
dioxus_rsx::ElementAttrName::Custom(name) => name.value().len() + 2,
};
total += name_len;
total += self.attr_value_len(&attr.attr.value);

//
if attr.attr.value.is_shorthand() {
total += 2;
} else {
total += self.attr_value_len(&attr.attr.value);
}
}
AttributeType::Spread(expr) => {
let expr_len = self.retrieve_formatted_expr(expr).len();
Expand All @@ -233,11 +244,12 @@ impl<'a> Writer<'a> {
fn write_for_loop(&mut self, forloop: &ForLoop) -> std::fmt::Result {
write!(
self.out,
"for {} in {} {{",
"for {} in ",
forloop.pat.clone().into_token_stream(),
unparse_expr(&forloop.expr)
)?;

self.write_inline_expr(&forloop.expr)?;

if forloop.body.is_empty() {
write!(self.out, "}}")?;
return Ok(());
Expand Down Expand Up @@ -265,12 +277,9 @@ impl<'a> Writer<'a> {
..
} = chain;

write!(
self.out,
"{} {} {{",
if_token.to_token_stream(),
unparse_expr(cond)
)?;
write!(self.out, "{} ", if_token.to_token_stream(),)?;

self.write_inline_expr(cond)?;

self.write_body_indented(then_branch)?;

Expand All @@ -296,6 +305,31 @@ impl<'a> Writer<'a> {

Ok(())
}

/// An expression within a for or if block that might need to be spread out across several lines
fn write_inline_expr(&mut self, expr: &Expr) -> std::fmt::Result {
let unparsed = unparse_expr(expr);
let mut lines = unparsed.lines();
let first_line = lines.next().unwrap();
write!(self.out, "{first_line}")?;

let mut was_multiline = false;

for line in lines {
was_multiline = true;
self.out.tabbed_line()?;
write!(self.out, "{line}")?;
}

if was_multiline {
self.out.tabbed_line()?;
write!(self.out, "{{")?;
} else {
write!(self.out, " {{")?;
}

Ok(())
}
}

pub(crate) trait SpanLength {
Expand Down
4 changes: 4 additions & 0 deletions packages/autofmt/tests/samples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,8 @@ twoway![
tinynoopt,
trailing_expr,
many_exprs,
shorthand,
docsite,
letsome,
fat_exprs,
];
7 changes: 5 additions & 2 deletions packages/autofmt/tests/samples/collapse_expr.rsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
fn itworks() {
rsx!( "{name}", "{name}", "{name}" )
rsx! {
"{name}"
"{name}"
"{name}"
}
}

6 changes: 5 additions & 1 deletion packages/autofmt/tests/samples/complex.rsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,9 @@ rsx! {
}
}

div { "asdbascasdbasd", "asbdasbdabsd", {asbdabsdbasdbas} }
div {
"asdbascasdbasd"
"asbdasbdabsd"
{asbdabsdbasdbas}
}
}
Loading
Loading