Skip to content

Commit c73dc43

Browse files
authored
fix(autofmt): add support for handling comments in various expression types (#5461)
* fix(autofmt): add support for handling comments in various expression types * fmt
1 parent 689e39b commit c73dc43

6 files changed

Lines changed: 164 additions & 4 deletions

File tree

packages/autofmt/src/writer.rs

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,14 @@ impl<'a> Writer<'a> {
712712
Ok(())
713713
}
714714

715+
fn span_has_line_comments(&self, span: Span) -> bool {
716+
span.source_text().is_some_and(|source| {
717+
source
718+
.lines()
719+
.any(|line| line.trim_start().starts_with("//"))
720+
})
721+
}
722+
715723
fn attr_value_len(&mut self, value: &AttributeValue) -> usize {
716724
match value {
717725
AttributeValue::IfExpr(if_chain) => {
@@ -734,11 +742,23 @@ impl<'a> Writer<'a> {
734742
}
735743
AttributeValue::AttrExpr(expr) => expr
736744
.as_expr()
737-
.map(|expr| self.attr_expr_len(&expr))
745+
.map(|expr| {
746+
if self.span_has_line_comments(expr.span()) {
747+
100000
748+
} else {
749+
self.attr_expr_len(&expr)
750+
}
751+
})
738752
.unwrap_or(100000),
739753
AttributeValue::EventTokens(closure) => closure
740754
.as_expr()
741-
.map(|expr| self.attr_expr_len(&expr))
755+
.map(|expr| {
756+
if self.span_has_line_comments(expr.span()) {
757+
100000
758+
} else {
759+
self.attr_expr_len(&expr)
760+
}
761+
})
742762
.unwrap_or(100000),
743763
}
744764
}
@@ -846,6 +866,9 @@ impl<'a> Writer<'a> {
846866

847867
let pretty = self.retrieve_formatted_expr(&expr).to_string();
848868
let source = src_span.source_text().unwrap_or_default();
869+
let source_has_line_comments = source
870+
.lines()
871+
.any(|line| line.trim_start().starts_with("//"));
849872
let mut src_lines = source.lines().peekable();
850873

851874
// Comments already in pretty output (from nested rsx!) - skip these from source
@@ -940,7 +963,9 @@ impl<'a> Writer<'a> {
940963
let is_call = src_trimmed.ends_with('(')
941964
|| src_trimmed.ends_with(',')
942965
|| src_trimmed.ends_with('{');
943-
if !is_call {
966+
let is_commented_block =
967+
source_has_line_comments && src_trimmed.ends_with('{');
968+
if is_commented_block || !is_call {
944969
multiline = Some(vec![*src]);
945970
break;
946971
}
@@ -1008,7 +1033,17 @@ impl<'a> Writer<'a> {
10081033
}
10091034

10101035
// Write multi-line with adjusted indentation
1011-
let base_indent = ml[0].chars().take_while(|c| c.is_whitespace()).count();
1036+
let base_indent = if source_has_line_comments && ml[0].trim_end().ends_with('{')
1037+
{
1038+
ml.iter()
1039+
.skip(1)
1040+
.filter(|line| !line.trim().is_empty())
1041+
.map(|line| line.chars().take_while(|c| c.is_whitespace()).count())
1042+
.min()
1043+
.unwrap_or(0)
1044+
} else {
1045+
ml[0].chars().take_while(|c| c.is_whitespace()).count()
1046+
};
10121047
let target: String = line.chars().take_while(|c| c.is_whitespace()).collect();
10131048

10141049
for (i, src_line) in ml.iter().enumerate() {

packages/autofmt/tests/samples.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ twoway![
2828
attributes,
2929
basic_expr,
3030
collapse_expr,
31+
comments_attr_expr_blocks,
3132
comments,
33+
comments_async_closure,
34+
comments_expr_with_strings,
35+
comments_nested_closures,
3236
commentshard,
3337
complex,
3438
docsite,
@@ -66,3 +70,37 @@ twoway![
6670
blank_lines_preserved,
6771
forloop_tuple,
6872
];
73+
74+
fn assert_idempotent(src: &str) {
75+
let src = src.replace("\r", "");
76+
77+
let once =
78+
dioxus_autofmt::apply_formats(&src, dioxus_autofmt::fmt_file(&src, Default::default()))
79+
.replace("\r", "");
80+
let twice =
81+
dioxus_autofmt::apply_formats(&once, dioxus_autofmt::fmt_file(&once, Default::default()))
82+
.replace("\r", "");
83+
84+
pretty_assertions::assert_eq!(src, once);
85+
pretty_assertions::assert_eq!(once, twice);
86+
}
87+
88+
#[test]
89+
fn comments_async_closure_is_idempotent() {
90+
assert_idempotent(include_str!("./samples/comments_async_closure.rsx"));
91+
}
92+
93+
#[test]
94+
fn comments_nested_closures_is_idempotent() {
95+
assert_idempotent(include_str!("./samples/comments_nested_closures.rsx"));
96+
}
97+
98+
#[test]
99+
fn comments_attr_expr_blocks_is_idempotent() {
100+
assert_idempotent(include_str!("./samples/comments_attr_expr_blocks.rsx"));
101+
}
102+
103+
#[test]
104+
fn comments_expr_with_strings_is_idempotent() {
105+
assert_idempotent(include_str!("./samples/comments_expr_with_strings.rsx"));
106+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
rsx! {
2+
button {
3+
class: "control-button",
4+
onclick: move |_| async move {
5+
// comment
6+
()
7+
},
8+
"print content"
9+
}
10+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
rsx! {
2+
Component {
3+
label: {
4+
let url = "https://example.com";
5+
6+
// comment before formatted tail expr
7+
format!("{url}/docs")
8+
},
9+
}
10+
11+
Component {
12+
status: {
13+
// comment before branch
14+
if is_ready() {
15+
"ready"
16+
} else {
17+
"waiting"
18+
}
19+
},
20+
}
21+
22+
div {
23+
data_mode: {
24+
// comment before block expr
25+
if is_ready() {
26+
"ready"
27+
} else {
28+
"waiting"
29+
}
30+
},
31+
}
32+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
rsx! {
2+
button {
3+
onclick: move |_| async move {
4+
let url = "https://example.com";
5+
6+
// keep this comment with the block, not the string literal
7+
println!("{url}");
8+
},
9+
"strings"
10+
}
11+
12+
Component {
13+
message: {
14+
let prefix = "value://";
15+
16+
// comment before final expression that contains //
17+
format!("{prefix}done")
18+
},
19+
}
20+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
rsx! {
2+
button {
3+
onclick: move |_| async move {
4+
let nested = move || {
5+
// nested closure comment
6+
()
7+
};
8+
9+
nested();
10+
},
11+
"nested closure"
12+
}
13+
14+
button {
15+
onclick: move |_| async move {
16+
let nested = move || async move {
17+
// nested async closure comment
18+
()
19+
};
20+
21+
nested().await;
22+
},
23+
"nested async closure"
24+
}
25+
}

0 commit comments

Comments
 (0)