feat: fix autoformatting around one liners

This commit is contained in:
Jonathan Kelley committed 2022-07-05 17:49:16 -04:00
1 parent 64d669e934
commit fa9ebe17cc
18 files changed
+259 -699

No files matched your search

+6 -1
View File
@@ -12,4 +12,9 @@ syn = { version = "1.0.11", features = ["full", "extra-traits"] }
dioxus-rsx = { path = "../rsx" }
triple_accel = "0.4.0"
serde = { version = "1.0.136", features = ["derive"] }
prettyplease = { git = "https://github.com/DioxusLabs/prettyplease-macro-fmt.git", features = ["verbatim"] }
prettyplease = { git = "https://github.com/DioxusLabs/prettyplease-macro-fmt.git", features = [
"verbatim",
] }
[dev-dependencies]
pretty_assertions = "1.2.1"
+7 -2
View File
@@ -1,7 +1,6 @@
use std::{
collections::{HashMap, VecDeque},
fmt::{Result, Write},
rc::Rc,
};
use dioxus_rsx::{BodyNode, ElementAttr, ElementAttrNamed};
@@ -118,6 +117,13 @@ impl Buffer {
pub fn write_body_indented(&mut self, children: &[BodyNode]) -> Result {
self.indent += 1;
self.write_body_no_indent(children)?;
self.indent -= 1;
Ok(())
}
pub fn write_body_no_indent(&mut self, children: &[BodyNode]) -> Result {
for child in children {
// Exprs handle their own indenting/line breaks
if !matches!(child, BodyNode::RawExpr(_)) {
@@ -128,7 +134,6 @@ impl Buffer {
self.write_ident(child)?;
}
self.indent -= 1;
Ok(())
}
+16 -2
View File
@@ -4,6 +4,7 @@ use quote::ToTokens;
use std::fmt::{Result, Write};
use syn::{spanned::Spanned, AngleBracketedGenericArguments};
#[derive(Debug)]
enum ShortOptimization {
// Special because we want to print the closing bracket immediately
Empty,
@@ -69,6 +70,15 @@ impl Buffer {
opt_level = ShortOptimization::NoOpt;
}
// Useful for debugging
// dbg!(
// name.to_token_stream().to_string(),
// &opt_level,
// attr_len,
// is_short_attr_list,
// is_small_children
// );
match opt_level {
ShortOptimization::Empty => {}
ShortOptimization::Oneliner => {
@@ -101,6 +111,11 @@ impl Buffer {
ShortOptimization::NoOpt => {
self.write_component_fields(fields, manual_props, false)?;
if !children.is_empty() && !fields.is_empty() {
write!(self.buf, ",")?;
}
self.write_body_indented(children)?;
self.tabbed_line()?;
}
@@ -196,7 +211,6 @@ impl Buffer {
.map(|field| match &field.content {
ContentField::Formatted(s) => s.value().len() ,
ContentField::OnHandlerRaw(exp) | ContentField::ManExpr(exp) => {
let formatted = prettyplease::unparse_expr(exp);
let len = if formatted.contains('\n') {
10000
@@ -207,7 +221,7 @@ impl Buffer {
len
},
} + 10)
.sum::<usize>() + self.indent * 4;
.sum::<usize>();
match manual_props {
Some(p) => {
+10 -3
View File
@@ -95,7 +95,9 @@ impl Buffer {
}
ShortOptimization::PropsOnTop => {
write!(self.buf, " ")?;
if !attributes.is_empty() || key.is_some() {
write!(self.buf, " ")?;
}
self.write_attributes(attributes, key, true)?;
if !children.is_empty() && !attributes.is_empty() {
@@ -141,13 +143,18 @@ impl Buffer {
}
write!(self.buf, "key: \"{}\"", key.value())?;
if !attributes.is_empty() {
write!(self.buf, ", ")?;
write!(self.buf, ",")?;
if sameline {
write!(self.buf, " ")?;
}
}
}
while let Some(attr) = attr_iter.next() {
self.indent += 1;
self.write_comments(attr.attr.start())?;
if !sameline {
self.write_comments(attr.attr.start())?;
}
self.indent -= 1;
if !sameline {
+6 -1
View File
@@ -98,7 +98,12 @@ pub fn fmt_block(block: &str, indent_level: usize) -> Option<String> {
let body = syn::parse_str::<dioxus_rsx::CallBody>(block).unwrap();
buf.write_body_indented(&body.roots).unwrap();
// Oneliner optimization
if buf.is_short_children(&body.roots).is_some() {
buf.write_ident(&body.roots[0]).unwrap();
} else {
buf.write_body_indented(&body.roots).unwrap();
}
// writing idents leaves the final line ended at the end of the last ident
if buf.buf.contains('\n') {
-53
View File
@@ -1,53 +0,0 @@
#[test]
fn formats_file_properly() {
let src = include_str!("./samples/thing.rsx");
let formatted = dioxus_autofmt::fmt_file(src);
let out = dioxus_autofmt::apply_formats(src, formatted);
println!("{}", out);
}
#[test]
fn already_formatted_file_properly() {
let src = include_str!("./samples/pre.rsx");
let formatted = dioxus_autofmt::fmt_file(src);
dbg!(&formatted);
let out = dioxus_autofmt::apply_formats(src, formatted);
println!("{}", out);
}
#[test]
fn comment_case_work() {
let src = include_str!("./samples/comments.rsx");
let formatted = dioxus_autofmt::fmt_file(src);
let out = dioxus_autofmt::apply_formats(src, formatted);
println!("{}", out);
}
#[test]
fn comment_attr_case_work() {
let src = include_str!("./samples/attrs.rsx");
let formatted = dioxus_autofmt::fmt_file(src);
let out = dioxus_autofmt::apply_formats(src, formatted);
println!("{}", out);
}
#[test]
fn tiny() {
// todo: get oneliner rsx working
let _src = include_str!("./samples/tiny.rsx");
// let formatted = dioxus_autofmt::fmt_file(src);
// let out = dioxus_autofmt::apply_formats(src, formatted);
// println!("{}", out);
}
+25
View File
@@ -0,0 +1,25 @@
macro_rules! twoway {
($val:literal => $name:ident) => {
#[test]
fn $name() {
let src = include_str!(concat!("./samples/", $val, ".rsx"));
let formatted = dioxus_autofmt::fmt_file(src);
let out = dioxus_autofmt::apply_formats(src, formatted);
pretty_assertions::assert_eq!(&src, &out);
}
};
}
twoway! ("simple" => simple);
twoway! ("comments" => comments);
twoway! ("attributes" => attributes);
twoway! ("manual_props" => manual_props);
twoway! ("complex" => complex);
twoway! ("tiny" => tiny);
twoway! ("tinynoopt" => tinynoopt);
-225
View File
@@ -1,225 +0,0 @@
rsx! {
// Simple case
div { key: "a", class: "ban", style: "color: red" }
// Many attributes
div {
div {
key: "ddd",
class: "asd",
class: "asd",
class: "asd",
class: "asd",
class: "asd",
class: "asd",
blah: 123,
onclick: move |_| {
let blah = 120;
true
},
onclick: move |_| {
let blah = 120;
true
},
onclick: move |_| {
let blah = 120;
true
},
onclick: move |_| {
let blah = 120;
true
},
div {
div { "hi" }
h2 { class: "asd" }
}
Component {}
// Generics
Component<Generic> {}
}
}
// Intertwined comments
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" }
div {}
}
}
}
// Regular comments
div { adsasd: "asd", block: "asd",
// this is a comment
"hello"
// this is a comment 1
// this is a comment 2
"hello"
div {
// this is a comment
"hello"
}
div { key: "asd", class: "asdsda",
div {}
h3 {}
}
}
// Components
Component {
adsasd: "asd",
// this is a comment
onclick: move |_| {
let blah = 120;
let blah = 122;
}
}
// Manual props
div {
Component {
adsasd: "asd",
onclick: move |_| {
let a = a;
},
div { "thing" }
}
Component {
asdasd: "asdasd",
asdasd: "asdasdasdasdasdasdasdasdasdasd",
..Props {
a: 10,
b: 20
}
}
Component {
asdasd: "asdasd",
..Props {
a: 10,
b: 20,
c: {
fn main() {}
},
}
"content"
}
}
// Long attributes
div {
a: "1234567891012345678910123456789101234567891012345678910123456789101234567891012345678910123456789101234567891012345678910",
a: "123",
a: "123",
a: "123",
a: "123",
a: "123",
a: "123",
a: "123",
a: "123"
}
// Short attributes
div { a: "123", a: "123", a: "123", a: "123", a: "123", a: "123", a: "123", a: "123", a: "123" }
// Compression
h3 { class: "mb-2 text-xl font-bold", "Invite Member" }
a { class: "text-white", "Send invitation" }
// Props on tops
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"
}
// No children, minimal props
img { class: "mb-6 mx-auto h-24", src: "artemis-assets/images/friends.png", alt: "" }
// One level compression
div {
a { class: "py-2 px-3 bg-indigo-500 hover:bg-indigo-600 rounded text-xs text-white", href: "#", "Send invitation" }
}
// Tiny component
Component { a: 123 }
// Expressions
ul {
div {}
(0..10).map(|f| rsx! {
li { "hi" }
})
div {}
}
// Complex nesting with components
button {
class: "flex items-center pl-3 py-3 pr-2 text-gray-500 hover:bg-indigo-50 rounded",
onclick: move |evt| {
show_user_menu.set(!show_user_menu.get());
evt.cancel_bubble();
},
onclick: move |evt| show_user_menu.set(!show_user_menu.get()),
span { class: "inline-block mr-4", icons::icon_14 {} }
span { "Settings" }
}
// Complex nesting with handlers
li {
Link { class: "flex items-center pl-3 py-3 pr-4 {active_class} rounded", to: "{to}",
span { class: "inline-block mr-3", icons::icon_0 {} }
span { "{name}" }
children.is_some().then(|| rsx! {
span {
class: "inline-block ml-auto hover:bg-gray-500",
onclick: move |evt| {
// open.set(!open.get());
evt.cancel_bubble();
},
icons::icon_8 {}
}
})
}
div { class: "px-4",
is_current.then(|| rsx!{ children })
}
}
// No nesting
Component {
adsasd: "asd",
onclick: move |_| {
let blah = 120;
}
}
// Component path
my::thing::Component {
adsasd: "asd",
onclick: move |_| {
let blah = 120;
}
}
}
@@ -0,0 +1,50 @@
rsx! {
div {
key: "ddd",
class: "asd",
class: "asd",
class: "asd",
class: "asd",
class: "asd",
class: "asd",
blah: 123,
onclick: move |_| {
let blah = 120;
true
},
onclick: move |_| {
let blah = 120;
true
},
onclick: move |_| {
let blah = 120;
true
},
onclick: move |_| {
let blah = 120;
true
},
div {
div { "hi" }
h2 { class: "asd" }
}
Component {}
Component<Generic> {}
}
// Long attributes
div {
a: "1234567891012345678910123456789101234567891012345678910123456789101234567891012345678910123456789101234567891012345678910",
a: "123",
a: "123",
a: "123",
a: "123",
a: "123",
a: "123",
a: "123",
a: "123"
}
// Short attributes
div { a: "123", a: "123", a: "123", a: "123", a: "123", a: "123", a: "123", a: "123", a: "123" }
}
-35
View File
@@ -1,35 +0,0 @@
rsx! {
div {
// My comment here 1
// My comment here 2
// My comment here 3
// My comment here 4
class: "asdasd",
// Comment here
onclick: move |_| {
let a = 10;
let b = 40;
let c = 50;
},
// my comment
// This here
"hi"
}
// Comment head
div {
class: "asd",
"Jon"
}
// Comment head
div {
// Collapse
class: "asd",
"Jon"
}
}
+22 -39
View File
@@ -1,48 +1,31 @@
rsx! {
div {
// The title
"hello"
// My comment here 1
// My comment here 2
// My comment here 3
// My comment here 4
class: "asdasd",
// The title
"hello"
// Comment here
onclick: move |_| {
let a = 10;
let b = 40;
let c = 50;
},
// The title
"hello"
// my comment
// The title
"hello"
// This here
"hi"
}
// body
div { "text" }
// Comment head
div { class: "asd", "Jon" }
div {
// Attr
class: "asdads",
"health"
}
div {
// adadsasd
// adadsasd
// adadsasd
// adadsasd
// adadsasd
// adadsasd
div {}
}
// Comment head
div {
// Collapse
class: "asd",
"Jon"
}
}
@@ -0,0 +1,50 @@
rsx! {
// Complex nesting with components
button {
class: "flex items-center pl-3 py-3 pr-2 text-gray-500 hover:bg-indigo-50 rounded",
onclick: move |evt| {
show_user_menu.set(!show_user_menu.get());
evt.cancel_bubble();
},
onclick: move |evt| show_user_menu.set(!show_user_menu.get()),
span { class: "inline-block mr-4", icons::icon_14 {} }
span { "Settings" }
}
// Complex nesting with handlers
li {
Link { class: "flex items-center pl-3 py-3 pr-4 {active_class} rounded", to: "{to}",
span { class: "inline-block mr-3", icons::icon_0 {} }
span { "{name}" }
children.is_some().then(|| rsx! {
span {
class: "inline-block ml-auto hover:bg-gray-500",
onclick: move |evt| {
// open.set(!open.get());
evt.cancel_bubble();
},
icons::icon_8 {}
}
})
}
div { class: "px-4",
is_current.then(|| rsx!{ children })
}
}
// No nesting
Component {
adsasd: "asd",
onclick: move |_| {
let blah = 120;
}
}
// Component path
my::thing::Component {
adsasd: "asd",
onclick: move |_| {
let blah = 120;
}
}
}
@@ -0,0 +1,27 @@
rsx! {
div {
Component {
adsasd: "asd",
onclick: move |_| {
let a = a;
},
div { "thing" }
}
Component {
asdasd: "asdasd",
asdasd: "asdasdasdasdasdasdasdasdasdasd",
..Props { a: 10, b: 20 }
}
Component {
asdasd: "asdasd",
..Props {
a: 10,
b: 20,
c: {
fn main() {}
},
},
"content"
}
}
}
-3
View File
@@ -1,3 +0,0 @@
rsx! {
div {}
}
+34
View File
@@ -0,0 +1,34 @@
rsx! {
div { "hello world!" }
div {
"hello world!"
"goodbye world!"
}
// Simple div
div { "hello world!" }
// Compression with attributes
div { key: "a", class: "ban", style: "color: red" }
// Nested one level
div { div { "nested" } }
// Nested two level
div { div { h1 { "highly nested" } } }
// Compression
h3 { class: "mb-2 text-xl font-bold", "Invite Member" }
a { class: "text-white", "Send invitation" }
// Props on tops
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"
}
// No children, minimal props
img { class: "mb-6 mx-auto h-24", src: "artemis-assets/images/friends.png", alt: "" }
// One level compression
div { a { class: "py-2 px-3 bg-indigo-500 hover:bg-indigo-600 rounded text-xs text-white", href: "#", "Send invitation" } }
}
-227
View File
@@ -1,227 +0,0 @@
fn app(cx: Scope) -> Element {
cx.render(rsx! {
div {
div {
key: "asdasd",
class: "asdasd",
class: "asdasd",
class: "asdasd",
class: "asdasd",
class: "asdasd",
class: "asdasd",
class: "asdasd",
class: "asdasd",
class: "asdasd",
class: "asdasd",
class: "asdasd",
class: "asdasd",
class: "asdasd",
}
}
h1 {"hi"}
h1 {"hi"}
h1 {"hi"}
h1 {"hi"}
div {
div {
key: "ddd",
class: "asd",
class: "asd",
class: "asd",
class: "asd",
class: "asd",
class: "asd",
blah: 123,
onclick: move |_| {
let blah = 120;
true
},
onclick: move |_| {
let blah = 120;
true
},
onclick: move |_| {
let blah = 120;
true
},
onclick: move |_| {
let blah = 120;
true
},
div {
div { "hi" }
h2 { class: "asd" }
}
Component {}
// Generics
Component<Generic> {}
}
}
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" }
div {}
}
}
}
// Components
Component {
adsasd: "asd",
// this is a comment
onclick: move |_| {
let blah = 120;
let blah = 122;
}
}
div {
Component {
adsasd: "asd",
onclick: move |_| {
let a = a;
},
div { "thing" }
}
Component {
asdasd: "asdasd",
asdasd: "asdasdasdasdasdasdasdasdasdasd",
..Props {
a: 10,
b: 20
}
}
Component {
asdasd: "asdasd",
..Props {
a: 10,
b: 20,
c: {
fn main() {}
},
}
"content"
}
}
div {
a: "1234567891012345678910123456789101234567891012345678910123456789101234567891012345678910123456789101234567891012345678910",
a: "123",
a: "123",
a: "123",
a: "123",
a: "123",
a: "123",
a: "123",
a: "123"
}
// Short attributes
div { a: "123", a: "123", a: "123", a: "123", a: "123", a: "123", a: "123", a: "123", a: "123" }
// Compression
h3 { class: "mb-2 text-xl font-bold", "Invite Member" }
a { class: "text-white", "Send invitation" }
// Props on tops
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"
}
// No children, minimal props
img { class: "mb-6 mx-auto h-24", src: "artemis-assets/images/friends.png", alt: "" }
// One level compression
div {
a { class: "py-2 px-3 bg-indigo-500 hover:bg-indigo-600 rounded text-xs text-white", href: "#", "Send invitation" }
}
// Tiny component
Component { a: 123 }
// Expressions
ul {
div {}
(0..10).map(|f| rsx! {
li { "hi" }
})
div {}
}
// Complex nesting with components
button {
class: "flex items-center pl-3 py-3 pr-2 text-gray-500 hover:bg-indigo-50 rounded",
onclick: move |evt| {
show_user_menu.set(!show_user_menu.get());
evt.cancel_bubble();
},
onclick: move |evt| show_user_menu.set(!show_user_menu.get()),
span { class: "inline-block mr-4", icons::icon_14 {} }
span { "Settings" }
}
// Complex nesting with handlers
li {
Link { class: "flex items-center pl-3 py-3 pr-4 {active_class} rounded", to: "{to}",
span { class: "inline-block mr-3", icons::icon_0 {} }
span { "{name}" }
children.is_some().then(|| rsx! {
span {
class: "inline-block ml-auto hover:bg-gray-500",
onclick: move |evt| {
// open.set(!open.get());
evt.cancel_bubble();
},
icons::icon_8 {}
}
})
}
div { class: "px-4",
is_current.then(|| rsx!{ children })
}
}
// No nesting
Component {
adsasd: "asd",
onclick: move |_| {
let blah = 120;
}
}
// Component path
my::thing::Component {
adsasd: "asd",
onclick: move |_| {
let blah = 120;
}
}
})
}
@@ -0,0 +1,6 @@
rsx! {
div {
div {}
div {}
}
}
-108
View File
@@ -1,108 +0,0 @@
use dioxus_autofmt::*;
#[test]
fn formats_valid_rust_src() {
let src = r#"
//
rsx! {
div {}
div {
h3 {"asd"
}
}
}
"#;
let formatted = fmt_file(src);
println!("{formatted:#?}");
}
#[test]
fn formats_valid_rust_src_with_indents() {
let src = r#"
#[inline_props]
fn NavItem<'a>(cx: Scope, to: &'static str, children: Element<'a>, icon: Shape) -> Element {
const ICON_SIZE: u32 = 36;
rsx! {
div { h1 { "thing" } }
}
}
"#
.to_string();
let formatted = fmt_file(&src);
assert!(formatted.is_empty());
}
#[test]
fn formats_multiple_blocks() {
let mut src = r#"
#[inline_props]
fn NavItem<'a>(cx: Scope, to: &'static str, children: Element<'a>, icon: Shape) -> Element {
const ICON_SIZE: u32 = 36;
rsx! {
div { h1 { "thing" } }
}
rsx! {
div {
Ball {
a: rsx! {
"asdasd"
}
}
}
}
}
#[inline_props]
fn NavItem<'a>(cx: Scope, to: &'static str, children: Element<'a>, icon: Shape) -> Element {
const ICON_SIZE: u32 = 36;
rsx! {
div { h1 { "thing" } }
}
rsx! {
div {
Ball {
a: rsx! {
"asdasd"
}
}
}
}
}
"#
.to_string();
let formatted = fmt_file(&src);
dbg!(&formatted);
let block = formatted.into_iter().next().unwrap();
src.replace_range(
block.start - 1..block.end + 1,
&format!("{{ {} }}", &block.formatted),
);
}
#[test]
fn empty_blocks() {
let src = r###"
pub fn Alert(cx: Scope) -> Element {
cx.render(rsx! {
div {}
})
}
"###
.to_string();
let formatted = fmt_file(&src);
dbg!(&formatted);
}