Skip to content

Commit b741052

Browse files
committed
fix: better handling of comments surrounding empty stmts
Closes #137
1 parent c6d193e commit b741052

File tree

3 files changed

+142
-40
lines changed

3 files changed

+142
-40
lines changed

src/parsing/parser.rs

Lines changed: 81 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ fn parse_node_with_inner_parse<'a>(node: Node<'a>, context: &mut Context<'a>, in
5050
let node_kind = node.kind();
5151
// println!("Node kind: {:?}", node_kind);
5252
// println!("Text: {:?}", node.text());
53+
// println!("Span: {:?}", node.span());
5354

5455
// store info
5556
let past_current_node = std::mem::replace(&mut context.current_node, node);
@@ -4899,44 +4900,43 @@ fn parse_comments_as_trailing<'a>(node: &dyn Spanned, trailing_comments: Comment
48994900
}
49004901

49014902
items.extend(parse_comment_collection(trailing_comments_on_same_line.into_iter(), Some(node), None, context));
4903+
items
4904+
}
49024905

4903-
return items;
4906+
fn get_trailing_comments_same_line<'a>(
4907+
node: &dyn Spanned,
4908+
trailing_comments: CommentsIterator<'a>,
4909+
context: &mut Context<'a>,
4910+
) -> Vec<&'a Comment> {
4911+
// use the roslyn definition of trailing comments
4912+
let node_end_line = node.end_line_fast(context.module);
4913+
let trailing_comments_on_same_line = trailing_comments.into_iter()
4914+
.filter(|c|c.start_line_fast(context.module) <= node_end_line) // less than or equal instead of just equal in order to include "forgotten" comments
4915+
.collect::<Vec<_>>();
49044916

4905-
fn get_trailing_comments_same_line<'a>(
4906-
node: &dyn Spanned,
4907-
trailing_comments: CommentsIterator<'a>,
4908-
context: &mut Context<'a>,
4909-
) -> Vec<&'a Comment> {
4910-
// use the roslyn definition of trailing comments
4911-
let node_end_line = node.end_line_fast(context.module);
4912-
let trailing_comments_on_same_line = trailing_comments.into_iter()
4913-
.filter(|c|c.start_line_fast(context.module) <= node_end_line) // less than or equal instead of just equal in order to include "forgotten" comments
4914-
.collect::<Vec<_>>();
4915-
4916-
// don't do extra work
4917-
if trailing_comments_on_same_line.is_empty() {
4918-
return trailing_comments_on_same_line;
4919-
}
4920-
4921-
// block comments after a comma on the same line as the next token are not considered a trailing comment of this node
4922-
// ex. `a, /* 1 */ b`, the comment belongs to `b` and not `a`
4923-
let comma_end = if node.text_fast(context.module) == "," {
4924-
Some(node.hi())
4925-
} else {
4926-
context.token_finder.get_next_token_if_comma(&node.span()).map(|t| t.hi())
4927-
};
4928-
if let Some(comma_end) = comma_end {
4929-
let next_token_pos = context.token_finder.get_next_token_pos_after(&comma_end);
4930-
let next_token_pos_line_start = next_token_pos.start_line_fast(context.module);
4931-
if next_token_pos_line_start == node_end_line {
4932-
return trailing_comments_on_same_line
4933-
.into_iter()
4934-
.filter(|c| c.lo() < comma_end)
4935-
.collect::<Vec<_>>()
4936-
}
4917+
// don't do extra work
4918+
if trailing_comments_on_same_line.is_empty() {
4919+
return trailing_comments_on_same_line;
4920+
}
4921+
4922+
// block comments after a comma on the same line as the next token are not considered a trailing comment of this node
4923+
// ex. `a, /* 1 */ b`, the comment belongs to `b` and not `a`
4924+
let comma_end = if node.text_fast(context.module) == "," {
4925+
Some(node.hi())
4926+
} else {
4927+
context.token_finder.get_next_token_if_comma(&node.span()).map(|t| t.hi())
4928+
};
4929+
if let Some(comma_end) = comma_end {
4930+
let next_token_pos = context.token_finder.get_next_token_pos_after(&comma_end);
4931+
let next_token_pos_line_start = next_token_pos.start_line_fast(context.module);
4932+
if next_token_pos_line_start == node_end_line {
4933+
return trailing_comments_on_same_line
4934+
.into_iter()
4935+
.filter(|c| c.lo() < comma_end)
4936+
.collect::<Vec<_>>()
49374937
}
4938-
trailing_comments_on_same_line
49394938
}
4939+
trailing_comments_on_same_line
49404940
}
49414941

49424942
fn get_jsx_empty_expr_comments<'a>(node: &JSXEmptyExpr, context: &mut Context<'a>) -> CommentsIterator<'a> {
@@ -5126,9 +5126,25 @@ fn parse_statements<'a>(inner_span: Span, stmts: Vec<Node<'a>>, context: &mut Co
51265126
last_node = Some(node.span());
51275127
} else {
51285128
let mut items = PrintItems::new();
5129-
let mut comments = node.leading_comments_fast(context.module);
5130-
comments.extend(node.trailing_comments_fast(context.module));
5131-
items.extend(parse_comments_as_statements(comments, None, context));
5129+
let leading_comments = node.leading_comments_fast(context.module);
5130+
items.extend(parse_comments_as_statements(leading_comments.clone().into_iter(), None, context));
5131+
let trailing_comments = get_trailing_comments_same_line(
5132+
&node,
5133+
node.trailing_comments_fast(context.module),
5134+
context,
5135+
);
5136+
if !trailing_comments.is_empty() {
5137+
if !leading_comments.is_empty() {
5138+
items.push_signal(Signal::NewLine);
5139+
}
5140+
items.extend(parse_comment_collection(
5141+
trailing_comments.into_iter(),
5142+
None,
5143+
None,
5144+
context,
5145+
));
5146+
}
5147+
51325148
parsed_nodes.push(items);
51335149

51345150
// ensure if this is last that it parses the trailing comment statements
@@ -5175,6 +5191,30 @@ fn parse_statements<'a>(inner_span: Span, stmts: Vec<Node<'a>>, context: &mut Co
51755191
}
51765192
}
51775193

5194+
fn parse_member_or_member_expr_stmt_comments(node: &Node, context: &mut Context) -> PrintItems {
5195+
let mut items = PrintItems::new();
5196+
let leading_comments = node.leading_comments_fast(context.module);
5197+
items.extend(parse_comments_as_statements(leading_comments.clone().into_iter(), None, context));
5198+
let trailing_comments = get_trailing_comments_same_line(
5199+
&node,
5200+
node.trailing_comments_fast(context.module),
5201+
context,
5202+
);
5203+
if !trailing_comments.is_empty() {
5204+
if !leading_comments.is_empty() {
5205+
items.push_signal(Signal::NewLine);
5206+
}
5207+
items.extend(parse_comment_collection(
5208+
trailing_comments.into_iter(),
5209+
None,
5210+
None,
5211+
context,
5212+
));
5213+
}
5214+
5215+
items
5216+
}
5217+
51785218
#[derive(PartialEq, Debug)]
51795219
enum StmtGroupKind {
51805220
Imports,
@@ -5302,9 +5342,10 @@ fn parse_members<'a, FShouldUseBlankLine>(
53025342

53035343
last_node = Some(node);
53045344
} else {
5305-
let mut comments = node.leading_comments_fast(context.module);
5306-
comments.extend(node.trailing_comments_fast(context.module));
5307-
items.extend(parse_comments_as_statements(comments, None, context));
5345+
items.extend(parse_member_or_member_expr_stmt_comments(
5346+
&node,
5347+
context,
5348+
));
53085349

53095350
// ensure if this is last that it parses the trailing comment statements
53105351
if i == children_len - 1 {

tests/specs/issues/issue0137.txt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
== should not move comment when there is an empty stmt above ==
2+
test;;
3+
4+
// comment
5+
asdf;
6+
7+
[expect]
8+
test;
9+
10+
// comment
11+
asdf;
12+
13+
== should not move comment when there is an empty stmt above in a class decl ==
14+
class Test {
15+
test;;
16+
17+
// comment
18+
asdf;
19+
}
20+
21+
22+
[expect]
23+
class Test {
24+
test;
25+
26+
// comment
27+
asdf;
28+
}

tests/specs/statements/emptyStatement/EmptyStatement_All.txt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,45 @@ test;
1212
;
1313
// 4
1414

15+
16+
// 5
17+
; // 6
18+
19+
// 7
20+
1521
[expect]
1622
/* 1 */
1723
// 2
1824
// 3
1925
// 4
2026

27+
// 5
28+
// 6
29+
30+
// 7
31+
32+
== should format when comments with multiple empty stmts ==
33+
/* 1 */; /* 2 */ ; /* 3 */ ; // 4
34+
35+
[expect]
36+
/* 1 */
37+
/* 2 */
38+
/* 3 */
39+
// 4
40+
41+
== should format when comments with multiple empty stmts in class decl ==
42+
class Test {
43+
/* 1 */; /* 2 */ ; /* 3 */ ; // 4
44+
}
45+
46+
[expect]
47+
class Test {
48+
/* 1 */
49+
/* 2 */
50+
/* 3 */
51+
// 4
52+
}
53+
2154
== should format when between nodes ==
2255
test;
2356
;

0 commit comments

Comments
 (0)