feat: improve autoformatting algorithm
- tons of new tests for various cases to be consistent
This commit is contained in:
1 parent
36ba49fe27
commit
804d9af1c7
3 files changed
+321
-44
No files matched your search
+114
-32
@@ -32,28 +32,36 @@ pub fn get_format_blocks(contents: &str) -> Vec<FormattedBlock> {
|
|||||||
|
|
||||||
let mut formatted_blocks = Vec::new();
|
let mut formatted_blocks = Vec::new();
|
||||||
|
|
||||||
|
let mut last_bracket_end = 0;
|
||||||
|
|
||||||
for item in matches {
|
for item in matches {
|
||||||
let Match { start, end, k } = item;
|
let Match { start, end, k } = item;
|
||||||
|
|
||||||
match cur_match {
|
if start < last_bracket_end {
|
||||||
Some(ref this_match) => {
|
continue;
|
||||||
// abort nested matches - these get handled automatically
|
|
||||||
if start < this_match.end {
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
cur_match = Some(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
cur_match = Some(item);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// match cur_match {
|
||||||
|
// Some(ref this_match) => {
|
||||||
|
// // abort nested matches - these get handled automatically
|
||||||
|
// if start < this_match.end {
|
||||||
|
// continue;
|
||||||
|
// } else {
|
||||||
|
// cur_match = Some(item);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// None => {
|
||||||
|
// cur_match = Some(item);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
let remaining = &contents[end - 1..];
|
let remaining = &contents[end - 1..];
|
||||||
|
|
||||||
if let Some(bracket_end) = find_bracket_end(remaining) {
|
if let Some(bracket_end) = find_bracket_end(remaining) {
|
||||||
let sub_string = &contents[end..bracket_end + end - 1];
|
let sub_string = &contents[end..bracket_end + end - 1];
|
||||||
|
|
||||||
|
last_bracket_end = bracket_end + end - 1;
|
||||||
|
|
||||||
// with the edge brackets
|
// with the edge brackets
|
||||||
// println!("{}", &contents[end - 1..bracket_end + end]);
|
// println!("{}", &contents[end - 1..bracket_end + end]);
|
||||||
|
|
||||||
@@ -65,7 +73,7 @@ pub fn get_format_blocks(contents: &str) -> Vec<FormattedBlock> {
|
|||||||
writeln!(output).unwrap();
|
writeln!(output).unwrap();
|
||||||
|
|
||||||
for line in new.lines() {
|
for line in new.lines() {
|
||||||
writeln!(output, "\t{}", line).ok();
|
writeln!(output, " {}", line).ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
formatted_blocks.push(FormattedBlock {
|
formatted_blocks.push(FormattedBlock {
|
||||||
@@ -74,7 +82,11 @@ pub fn get_format_blocks(contents: &str) -> Vec<FormattedBlock> {
|
|||||||
end: end + bracket_end - 1,
|
end: end + bracket_end - 1,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
panic!("failed to format block: {}", sub_string);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
panic!("failed to find end of block: {}", remaining);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,58 +117,120 @@ pub fn write_ident(buf: &mut String, node: &BodyNode, indent: usize) -> fmt::Res
|
|||||||
} = el;
|
} = el;
|
||||||
|
|
||||||
write_tabs(buf, indent)?;
|
write_tabs(buf, indent)?;
|
||||||
writeln!(buf, "{name} {{")?;
|
write!(buf, "{name} {{")?;
|
||||||
|
|
||||||
|
let total_attr_len = attributes
|
||||||
|
.iter()
|
||||||
|
.map(|attr| match &attr.attr {
|
||||||
|
ElementAttr::AttrText { name, value } => value.value().len(),
|
||||||
|
ElementAttr::AttrExpression { name, value } => 10,
|
||||||
|
ElementAttr::CustomAttrText { name, value } => value.value().len(),
|
||||||
|
ElementAttr::CustomAttrExpression { name, value } => 10,
|
||||||
|
ElementAttr::EventTokens { name, tokens } => 1000000,
|
||||||
|
ElementAttr::Meta(_) => todo!(),
|
||||||
|
})
|
||||||
|
.sum::<usize>();
|
||||||
|
|
||||||
|
let is_long_attr_list = total_attr_len > 80;
|
||||||
|
|
||||||
if let Some(key) = key {
|
if let Some(key) = key {
|
||||||
let key = key.value();
|
let key = key.value();
|
||||||
write_tabs(buf, indent + 1)?;
|
if is_long_attr_list {
|
||||||
|
write_tabs(buf, indent + 1)?;
|
||||||
|
} else {
|
||||||
|
write!(buf, " ")?;
|
||||||
|
}
|
||||||
write!(buf, "key: \"{key}\"")?;
|
write!(buf, "key: \"{key}\"")?;
|
||||||
|
|
||||||
if !attributes.is_empty() {
|
if !attributes.is_empty() {
|
||||||
writeln!(buf, ",")?;
|
writeln!(buf, ",")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for attr in attributes {
|
let mut attr_iter = attributes.iter().peekable();
|
||||||
write_tabs(buf, indent + 1)?;
|
while let Some(attr) = attr_iter.next() {
|
||||||
|
if is_long_attr_list {
|
||||||
|
writeln!(buf)?;
|
||||||
|
write_tabs(buf, indent + 1)?;
|
||||||
|
} else {
|
||||||
|
write!(buf, " ")?;
|
||||||
|
}
|
||||||
|
|
||||||
match &attr.attr {
|
match &attr.attr {
|
||||||
ElementAttr::AttrText { name, value } => {
|
ElementAttr::AttrText { name, value } => {
|
||||||
writeln!(buf, "{name}: \"{value}\",", value = value.value())?;
|
write!(buf, "{name}: \"{value}\"", value = value.value())?;
|
||||||
}
|
}
|
||||||
ElementAttr::AttrExpression { name, value } => {
|
ElementAttr::AttrExpression { name, value } => {
|
||||||
let out = prettyplease::unparse_expr(value);
|
let out = prettyplease::unparse_expr(value);
|
||||||
writeln!(buf, "{}: {},", name, out)?;
|
write!(buf, "{}: {}", name, out)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
ElementAttr::CustomAttrText { name, value } => todo!(),
|
ElementAttr::CustomAttrText { name, value } => {
|
||||||
ElementAttr::CustomAttrExpression { name, value } => todo!(),
|
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 } => {
|
ElementAttr::EventTokens { name, tokens } => {
|
||||||
let out = prettyplease::unparse_expr(tokens);
|
let out = prettyplease::unparse_expr(tokens);
|
||||||
|
|
||||||
let mut lines = out.split('\n').peekable();
|
let mut lines = out.split('\n').peekable();
|
||||||
let first = lines.next().unwrap();
|
let first = lines.next().unwrap();
|
||||||
writeln!(buf, "{}: {}", name, first)?;
|
write!(buf, "{}: {}", name, first)?;
|
||||||
|
|
||||||
while let Some(line) = lines.next() {
|
while let Some(line) = lines.next() {
|
||||||
write_tabs(buf, indent + 1)?;
|
write_tabs(buf, indent + 1)?;
|
||||||
write!(buf, "{}", line)?;
|
write!(buf, "{}", line)?;
|
||||||
// writeln!(buf, "{}", line)?;
|
// write!(buf, "{}", line)?;
|
||||||
if lines.peek().is_none() {
|
if lines.peek().is_none() {
|
||||||
writeln!(buf, ",")?;
|
write!(buf, "")?;
|
||||||
} else {
|
|
||||||
writeln!(buf)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ElementAttr::Meta(_) => {}
|
ElementAttr::Meta(_) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if attr_iter.peek().is_some() || !children.is_empty() {
|
||||||
|
write!(buf, ",")?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for child in children {
|
if children.len() == 1 && children[0].is_litstr() && !is_long_attr_list {
|
||||||
write_ident(buf, child, indent + 1)?;
|
if let BodyNode::Text(text) = &children[0] {
|
||||||
|
let text_val = text.value();
|
||||||
|
if total_attr_len + text_val.len() > 80 {
|
||||||
|
writeln!(buf)?;
|
||||||
|
write_tabs(buf, indent + 1)?;
|
||||||
|
writeln!(buf, "\"{}\"", text.value())?;
|
||||||
|
write_tabs(buf, indent)?;
|
||||||
|
} else {
|
||||||
|
write!(buf, " \"{}\" ", text.value())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if is_long_attr_list || !children.is_empty() {
|
||||||
|
writeln!(buf)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for child in children {
|
||||||
|
write_ident(buf, child, indent + 1)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_long_attr_list || !children.is_empty() {
|
||||||
|
write_tabs(buf, indent)?;
|
||||||
|
} else {
|
||||||
|
write!(buf, " ")?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
write_tabs(buf, indent)?;
|
|
||||||
writeln!(buf, "}}")?;
|
writeln!(buf, "}}")?;
|
||||||
}
|
}
|
||||||
BodyNode::Component(component) => {
|
BodyNode::Component(component) => {
|
||||||
@@ -167,10 +241,15 @@ pub fn write_ident(buf: &mut String, node: &BodyNode, indent: usize) -> fmt::Res
|
|||||||
manual_props,
|
manual_props,
|
||||||
} = component;
|
} = component;
|
||||||
|
|
||||||
let name = name.to_token_stream().to_string();
|
let mut name = name.to_token_stream().to_string();
|
||||||
|
name.retain(|c| !c.is_whitespace());
|
||||||
|
|
||||||
write_tabs(buf, indent)?;
|
write_tabs(buf, indent)?;
|
||||||
writeln!(buf, "{name} {{")?;
|
write!(buf, "{name} {{")?;
|
||||||
|
|
||||||
|
if !body.is_empty() || !children.is_empty() {
|
||||||
|
writeln!(buf)?;
|
||||||
|
}
|
||||||
|
|
||||||
for field in body {
|
for field in body {
|
||||||
write_tabs(buf, indent + 1)?;
|
write_tabs(buf, indent + 1)?;
|
||||||
@@ -216,7 +295,10 @@ pub fn write_ident(buf: &mut String, node: &BodyNode, indent: usize) -> fmt::Res
|
|||||||
write_ident(buf, child, indent + 1)?;
|
write_ident(buf, child, indent + 1)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
write_tabs(buf, indent)?;
|
if !body.is_empty() || !children.is_empty() {
|
||||||
|
write_tabs(buf, indent)?;
|
||||||
|
}
|
||||||
|
|
||||||
writeln!(buf, "}}")?;
|
writeln!(buf, "}}")?;
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
+198
-12
@@ -75,6 +75,103 @@ fn formats_component() {
|
|||||||
print!("{formatted}");
|
print!("{formatted}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn formats_element() {
|
||||||
|
let block = r#"
|
||||||
|
div {
|
||||||
|
a: "1234567891012345678910123456789101234567891012345678910123456789101234567891012345678910123456789101234567891012345678910",
|
||||||
|
a: "123",
|
||||||
|
a: "123",
|
||||||
|
a: "123",
|
||||||
|
a: "123",
|
||||||
|
a: "123",
|
||||||
|
a: "123",
|
||||||
|
a: "123",
|
||||||
|
a: "123",
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let formatted = fmt_block(block).unwrap();
|
||||||
|
|
||||||
|
print!("{formatted}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn formats_element_short() {
|
||||||
|
let block = r#"
|
||||||
|
div {
|
||||||
|
a: "123",
|
||||||
|
a: "123",
|
||||||
|
a: "123",
|
||||||
|
a: "123",
|
||||||
|
a: "123",
|
||||||
|
a: "123",
|
||||||
|
a: "123",
|
||||||
|
a: "123",
|
||||||
|
a: "123",
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let formatted = fmt_block(block).unwrap();
|
||||||
|
|
||||||
|
print!("{formatted}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn formats_element_nested() {
|
||||||
|
let block = r#"
|
||||||
|
h3 {
|
||||||
|
class: "mb-2 text-xl font-bold",
|
||||||
|
"Invite Member"
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let formatted = fmt_block(block).unwrap();
|
||||||
|
|
||||||
|
print!("{formatted}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn formats_element_nested_no_trailing_tabs() {
|
||||||
|
let block = r#"
|
||||||
|
img { class: "mb-6 mx-auto h-24", src: "artemis-assets/images/friends.png", alt: "", }
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let formatted = fmt_block(block).unwrap();
|
||||||
|
|
||||||
|
print!("{formatted}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn formats_element_with_correct_indent() {
|
||||||
|
let block = r###"
|
||||||
|
div {
|
||||||
|
|
||||||
|
a { class: "py-2 px-3 bg-indigo-500 hover:bg-indigo-600 rounded text-xs text-white", href: "#",
|
||||||
|
"Send invitation"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"###;
|
||||||
|
|
||||||
|
let formatted = fmt_block(block).unwrap();
|
||||||
|
|
||||||
|
print!("{formatted}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn small_elements_and_text_are_small() {
|
||||||
|
let block = r###"
|
||||||
|
a { class: " text-white",
|
||||||
|
"Send invitation"
|
||||||
|
}
|
||||||
|
"###;
|
||||||
|
|
||||||
|
let formatted = fmt_block(block).unwrap();
|
||||||
|
|
||||||
|
print!("{formatted}");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn formats_component_man_props() {
|
fn formats_component_man_props() {
|
||||||
let block = r#"
|
let block = r#"
|
||||||
@@ -94,6 +191,18 @@ fn formats_component_man_props() {
|
|||||||
print!("{formatted}");
|
print!("{formatted}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn formats_component_tiny() {
|
||||||
|
let block = r#"
|
||||||
|
Component { a: 123
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let formatted = fmt_block(block).unwrap();
|
||||||
|
|
||||||
|
print!("{formatted}");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn formats_document() {
|
fn formats_document() {
|
||||||
let block = r#"
|
let block = r#"
|
||||||
@@ -114,6 +223,24 @@ rsx!{
|
|||||||
print!("{formatted:?}");
|
print!("{formatted:?}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn component_path_mod_style() {
|
||||||
|
let block = r#"
|
||||||
|
rsx!{
|
||||||
|
my::thing::Component {
|
||||||
|
adsasd: "asd", // this is a comment
|
||||||
|
onclick: move |_| {
|
||||||
|
let blah = 120;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let formatted = get_format_blocks(block);
|
||||||
|
|
||||||
|
print!("{formatted:?}");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn formats_valid_rust_src() {
|
fn formats_valid_rust_src() {
|
||||||
let src = r#"
|
let src = r#"
|
||||||
@@ -159,20 +286,79 @@ fn NavItem<'a>(cx: Scope, to: &'static str, children: Element<'a>, icon: Shape)
|
|||||||
|
|
||||||
let block = formatted.into_iter().next().unwrap();
|
let block = formatted.into_iter().next().unwrap();
|
||||||
|
|
||||||
// println!("{}", &src[block.start..block.end]);
|
src.replace_range(
|
||||||
// println!("{}", &src[block.start..block.end - 1]);
|
block.start - 1..block.end + 1,
|
||||||
|
&format!("{{ {} }}", &block.formatted),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 = get_format_blocks(&src);
|
||||||
|
|
||||||
|
dbg!(&formatted);
|
||||||
|
|
||||||
|
let block = formatted.into_iter().next().unwrap();
|
||||||
|
|
||||||
src.replace_range(
|
src.replace_range(
|
||||||
block.start - 1..block.end + 1,
|
block.start - 1..block.end + 1,
|
||||||
&format!("{{ {} }}", &block.formatted),
|
&format!("{{ {} }}", &block.formatted),
|
||||||
// &format!("{{ {} }}", &block.formatted),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
println!("{src}");
|
|
||||||
|
|
||||||
println!("\tthing");
|
|
||||||
|
|
||||||
// for block in formatted {
|
|
||||||
// println!("{}", block.formatted);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
@@ -23,6 +23,15 @@ pub enum BodyNode {
|
|||||||
Meta(String),
|
Meta(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl BodyNode {
|
||||||
|
pub fn is_litstr(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
BodyNode::Text(_) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Parse for BodyNode {
|
impl Parse for BodyNode {
|
||||||
fn parse(stream: ParseStream) -> Result<Self> {
|
fn parse(stream: ParseStream) -> Result<Self> {
|
||||||
if stream.peek(LitStr) {
|
if stream.peek(LitStr) {
|
||||||
|
|||||||
Reference in new issue
Block a user