Skip to content

Commit 88c693b

Browse files
authored
fix: don't eat comments in more situations (#4423)
* fix: don't eat comments in more situations * clippy...
1 parent 57dba19 commit 88c693b

File tree

4 files changed

+149
-17
lines changed

4 files changed

+149
-17
lines changed

packages/autofmt/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ pub fn try_fmt_file(
9797

9898
// TESTME
9999
// Writing *should* not fail but it's possible that it does
100-
if writer.write_rsx_call(&body.body).is_err() {
100+
if writer.write_rsx_call(&body).is_err() {
101101
let span = writer.invalid_exprs.pop().unwrap_or_else(Span::call_site);
102102
return Err(syn::Error::new(span, "Failed emit valid rsx - likely due to partially complete expressions in the rsx! macro"));
103103
}
@@ -149,7 +149,7 @@ pub fn try_fmt_file(
149149
/// that passed partial expansion but failed to parse.
150150
pub fn write_block_out(body: &CallBody) -> Option<String> {
151151
let mut buf = Writer::new("", IndentOptions::default());
152-
buf.write_rsx_call(&body.body).ok()?;
152+
buf.write_rsx_call(body).ok()?;
153153
buf.consume()
154154
}
155155

@@ -158,7 +158,7 @@ pub fn fmt_block(block: &str, indent_level: usize, indent: IndentOptions) -> Opt
158158

159159
let mut buf = Writer::new(block, indent);
160160
buf.out.indent_level = indent_level;
161-
buf.write_rsx_call(&body.body).ok()?;
161+
buf.write_rsx_call(&body).ok()?;
162162

163163
// writing idents leaves the final line ended at the end of the last ident
164164
if buf.out.buf.contains('\n') {

packages/autofmt/src/writer.rs

Lines changed: 75 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,23 +36,38 @@ impl<'a> Writer<'a> {
3636
Some(self.out.buf)
3737
}
3838

39-
pub fn write_rsx_call(&mut self, body: &TemplateBody) -> Result {
40-
if body.roots.is_empty() {
39+
pub fn write_rsx_call(&mut self, body: &CallBody) -> Result {
40+
if body.body.roots.is_empty() {
4141
return Ok(());
4242
}
4343

44-
if Self::is_short_rsx_call(&body.roots) {
44+
if Self::is_short_rsx_call(&body.body.roots) {
4545
write!(self.out, " ")?;
46-
self.write_ident(&body.roots[0])?;
46+
self.write_ident(&body.body.roots[0])?;
4747
write!(self.out, " ")?;
4848
} else {
4949
self.out.new_line()?;
50-
self.write_body_indented(&body.roots)?
50+
self.write_body_indented(&body.body.roots)?;
51+
self.write_trailing_body_comments(body)?;
5152
}
5253

5354
Ok(())
5455
}
5556

57+
fn write_trailing_body_comments(&mut self, body: &CallBody) -> Result {
58+
if let Some(span) = body.span {
59+
self.out.indent_level += 1;
60+
let comments = self.accumulate_comments(span.span().end());
61+
if !comments.is_empty() {
62+
self.out.new_line()?;
63+
self.apply_comments(comments)?;
64+
self.out.buf.pop(); // remove the trailing newline, forcing us to end at the end of the comment
65+
}
66+
self.out.indent_level -= 1;
67+
}
68+
Ok(())
69+
}
70+
5671
// Expects to be written directly into place
5772
pub fn write_ident(&mut self, node: &BodyNode) -> Result {
5873
match node {
@@ -301,7 +316,8 @@ impl<'a> Writer<'a> {
301316
let children_len = self
302317
.is_short_children(children)
303318
.map_err(|_| std::fmt::Error)?;
304-
let is_small_children = children_len.is_some();
319+
let has_trailing_comments = self.has_trailing_comments(children, brace);
320+
let is_small_children = children_len.is_some() && !has_trailing_comments;
305321

306322
// if we have one long attribute and a lot of children, place the attrs on top
307323
if is_short_attr_list && !is_small_children {
@@ -310,7 +326,11 @@ impl<'a> Writer<'a> {
310326

311327
// even if the attr is long, it should be put on one line
312328
// However if we have childrne we need to just spread them out for readability
313-
if !is_short_attr_list && attributes.len() <= 1 && spreads.is_empty() {
329+
if !is_short_attr_list
330+
&& attributes.len() <= 1
331+
&& spreads.is_empty()
332+
&& !has_trailing_comments
333+
{
314334
if children.is_empty() {
315335
opt_level = ShortOptimization::Oneliner;
316336
} else {
@@ -328,7 +348,11 @@ impl<'a> Writer<'a> {
328348
}
329349

330350
// If there's nothing at all, empty optimization
331-
if attributes.is_empty() && children.is_empty() && spreads.is_empty() {
351+
if attributes.is_empty()
352+
&& children.is_empty()
353+
&& spreads.is_empty()
354+
&& !has_trailing_comments
355+
{
332356
opt_level = ShortOptimization::Empty;
333357

334358
// Write comments if they exist
@@ -932,9 +956,8 @@ impl<'a> Writer<'a> {
932956
}
933957

934958
fn final_span_of_node(node: &BodyNode) -> Span {
935-
// Write the trailing comments if there are any
936959
// Get the ending span of the node
937-
let span = match node {
960+
match node {
938961
BodyNode::Element(el) => el
939962
.brace
940963
.as_ref()
@@ -952,8 +975,7 @@ impl<'a> Writer<'a> {
952975
Some(b) => b.span.span(),
953976
None => i.then_brace.span.span(),
954977
},
955-
};
956-
span
978+
}
957979
}
958980

959981
fn final_span_of_attr(&self, attr: &Attribute) -> Span {
@@ -969,4 +991,45 @@ impl<'a> Writer<'a> {
969991
.unwrap_or_else(|| ex.then_value.span()),
970992
}
971993
}
994+
995+
fn has_trailing_comments(&self, children: &[BodyNode], brace: &Brace) -> bool {
996+
let brace_span = brace.span.span();
997+
998+
let Some(last_node) = children.last() else {
999+
return false;
1000+
};
1001+
1002+
// Check for any comments after the last node between the last brace
1003+
let final_span = Self::final_span_of_node(last_node);
1004+
let final_span = final_span.end();
1005+
let mut line = final_span.line;
1006+
let mut column = final_span.column;
1007+
loop {
1008+
let Some(src_line) = self.src.get(line - 1) else {
1009+
return false;
1010+
};
1011+
1012+
// the line might contain emoji or other unicode characters - this will cause issues
1013+
let Some(mut whitespace) = src_line.get(column..).map(|s| s.trim()) else {
1014+
return false;
1015+
};
1016+
1017+
let offset = 0;
1018+
whitespace = whitespace[offset..].trim();
1019+
1020+
if whitespace.starts_with("//") {
1021+
return true;
1022+
}
1023+
1024+
if line == brace_span.end().line {
1025+
// If we reached the end of the brace span, stop
1026+
break;
1027+
}
1028+
1029+
line += 1;
1030+
column = 0; // reset column to the start of the next line
1031+
}
1032+
1033+
false
1034+
}
9721035
}

packages/autofmt/tests/samples/comments.rsx

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,69 @@ rsx! {
5959
// please dont eat me 3
6060
abc: 123,
6161
}
62+
63+
div {
64+
// I am just a comment
65+
}
66+
67+
div {
68+
"text"
69+
// I am just a comment
70+
}
71+
72+
div {
73+
div {}
74+
// I am just a comment
75+
}
76+
77+
div {
78+
{some_expr()}
79+
// I am just a comment
80+
}
81+
82+
div {
83+
"text" // I am just a comment
84+
}
85+
86+
div {
87+
div {} // I am just a comment
88+
}
89+
90+
div {
91+
{some_expr()} // I am just a comment
92+
}
93+
94+
div {
95+
// Please dont eat me 1
96+
div {
97+
// Please dont eat me 2
98+
}
99+
// Please dont eat me 3
100+
}
101+
102+
div {
103+
"hi"
104+
// Please dont eat me 1
105+
}
106+
div {
107+
"hi" // Please dont eat me 1
108+
// Please dont eat me 2
109+
}
110+
111+
// Please dont eat me 2
112+
Component {}
113+
114+
// Please dont eat me 1
115+
Component {
116+
// Please dont eat me 2
117+
}
118+
119+
// Please dont eat me 1
120+
Component {
121+
// Please dont eat me 2
122+
}
123+
124+
// Please dont eat me 1
125+
//
126+
// Please dont eat me 2
62127
}

packages/rsx/src/rsx_call.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//! This mostly just defers to the root TemplateBody with some additional tooling to provide better errors.
44
//! Currently the additional tooling doesn't do much.
55
6-
use proc_macro2::TokenStream as TokenStream2;
6+
use proc_macro2::{Span, TokenStream as TokenStream2};
77
use quote::ToTokens;
88
use std::{cell::Cell, fmt::Debug};
99
use syn::{
@@ -26,12 +26,15 @@ use crate::{BodyNode, TemplateBody};
2626
pub struct CallBody {
2727
pub body: TemplateBody,
2828
pub template_idx: Cell<usize>,
29+
pub span: Option<Span>,
2930
}
3031

3132
impl Parse for CallBody {
3233
fn parse(input: ParseStream) -> Result<Self> {
3334
// Defer to the `new` method such that we can wire up hotreload information
34-
Ok(CallBody::new(input.parse()?))
35+
let mut body = CallBody::new(input.parse()?);
36+
body.span = Some(input.span());
37+
Ok(body)
3538
}
3639
}
3740

@@ -49,6 +52,7 @@ impl CallBody {
4952
let body = CallBody {
5053
body,
5154
template_idx: Cell::new(0),
55+
span: None,
5256
};
5357

5458
body.body.template_idx.set(body.next_template_idx());

0 commit comments

Comments
 (0)