Skip to content

Commit fec65a6

Browse files
authored
Indentation of multi post-comments to a list item (#4606)
* Fix for issue #3847 - handle list item one-line multi post-comments * Fix issue #3847 - indentation of list item several one-line post-comments * modify comment * Add handling of first one-line block-comment * Use some common comments functions * Add handling of multi-line comment as first comment with debug info * Some enhancements and remove preserve of empty lines after first multiline comment * Some refactoring and added comments and test cases * Fix for the case when the first post-comment is in new line
1 parent d11fde8 commit fec65a6

File tree

3 files changed

+508
-14
lines changed

3 files changed

+508
-14
lines changed

Diff for: src/formatting/lists.rs

+115-14
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use unicode_segmentation::UnicodeSegmentation;
88

99
use crate::config::{lists::*, Config, IndentStyle};
1010
use crate::formatting::{
11-
comment::{find_comment_end, rewrite_comment, FindUncommented},
11+
comment::{comment_style, find_comment_end, rewrite_comment, FindUncommented},
1212
rewrite::RewriteContext,
1313
shape::{Indent, Shape},
1414
utils::{
@@ -421,6 +421,33 @@ where
421421
result.push_str(formatting.separator);
422422
}
423423

424+
// Note about post-comments indentation:
425+
// In the original code the the item separator may follow some comments that
426+
// may span over some lines. E.g.:
427+
// item1 /* 1st comment line 1
428+
// * line 2 */
429+
// /* 2nd comment */,
430+
// item2,
431+
//
432+
// In this case, rustfmt moves the separator right after the item:
433+
// item1, /* 1st comment line 1
434+
// * line 2 */
435+
// /* 2nd comment */
436+
// item2,
437+
//
438+
// In this code, only the 1st comment is regarded as post comment of item1.
439+
// 2nd comment is regarded as pre-comment of item2, therefore the output
440+
// of another round of formatting this code is:
441+
// item1, /* 1st comment line 1
442+
// * line 2 */
443+
// /* 2nd comment */
444+
// item2,
445+
//
446+
// i.e. 2nd comment is now indented as pre-comment.
447+
//
448+
// This why in the code below a first multiline post-comment is indented
449+
// differently then the other post-comments.
450+
424451
if tactic != DefinitiveListTactic::Horizontal && item.post_comment.is_some() {
425452
let comment = item.post_comment.as_ref().unwrap();
426453
let overhead = last_line_width(&result) + first_line_width(comment.trim());
@@ -444,19 +471,88 @@ where
444471
};
445472
let width = formatting.shape.width.checked_sub(overhead).unwrap_or(1);
446473
let offset = formatting.shape.indent + overhead;
447-
let comment_shape = Shape::legacy(width, offset);
448-
449-
// Use block-style only for the last item or multiline comments.
450-
let block_style = !formatting.ends_with_newline && last
451-
|| comment.trim().contains('\n')
452-
|| comment.trim().len() > width;
453-
454-
rewrite_comment(
455-
comment.trim_start(),
456-
block_style,
457-
comment_shape,
458-
formatting.config,
459-
)
474+
let comment_start_trimmed = comment.trim_start();
475+
476+
// Find if first comment is single line and the end of the first comment
477+
// when it is a multi-line block comment (since the first post-comment
478+
// is added to the same line of the list item, its indentation is important
479+
// only when it is a multiline comment).
480+
let style = comment_style(
481+
comment_start_trimmed,
482+
formatting.config.normalize_comments(),
483+
);
484+
let (first_comment_single_line, first_comment_end) =
485+
if !formatting.config.normalize_comments() && style.is_line_comment() {
486+
// Line comment (not normalizaed)
487+
(true, None)
488+
} else if style.is_block_comment() {
489+
match find_comment_end(&comment_start_trimmed) {
490+
Some(i) => {
491+
if comment_start_trimmed[..i].contains('\n') {
492+
// Multiline-comment (may be because of normalization)
493+
(false, Some(i))
494+
} else {
495+
// One line coment (Block or normalizaed Line)
496+
(true, None)
497+
}
498+
}
499+
_ => (false, None), // Unknow comment end
500+
}
501+
} else {
502+
(false, None) // Unexpected case - non-block comment with normalization
503+
};
504+
505+
// Closure for formatting post-comment with specific Shape
506+
let rewrite_post_comment_with_shape = |cmt: &str, shape: Shape| {
507+
// Use block-style only for the last item or multiline comments.
508+
let block_style = !formatting.ends_with_newline && last
509+
|| cmt.trim().contains('\n')
510+
|| cmt.trim().len() > width;
511+
rewrite_comment(&cmt, block_style, shape, formatting.config)
512+
};
513+
514+
// Properly indent first and other comments
515+
match (first_comment_single_line, first_comment_end) {
516+
(_, None) => {
517+
// First comment not multiline - same indentation for all comments
518+
let comment_shape =
519+
if first_comment_single_line && comment_start_trimmed.contains("\n") {
520+
formatting.shape
521+
} else {
522+
Shape::legacy(width, offset)
523+
};
524+
rewrite_post_comment_with_shape(comment_start_trimmed, comment_shape)
525+
}
526+
(false, Some(comment_end)) => {
527+
// Separate indentation for first multi-line block comment
528+
let formatted_first_comment = rewrite_comment(
529+
&comment_start_trimmed[..comment_end],
530+
true,
531+
Shape::legacy(width, offset),
532+
formatting.config,
533+
)?;
534+
let second_comment_start = comment_start_trimmed[comment_end..]
535+
.find(|c: char| !c.is_whitespace())
536+
.map_or(None, |i| Some(i + comment_end));
537+
let formatted_all_comments = match second_comment_start {
538+
Some(i) => {
539+
let second_comment = comment_start_trimmed[i..].to_string();
540+
let formatted = rewrite_post_comment_with_shape(
541+
&second_comment,
542+
formatting.shape,
543+
)?;
544+
let indent = formatting
545+
.shape
546+
.indent
547+
.to_string_with_newline(formatting.config);
548+
format!("{}{}{}", formatted_first_comment, indent, formatted)
549+
}
550+
_ => formatted_first_comment,
551+
};
552+
Some(formatted_all_comments)
553+
}
554+
(_, _) => unreachable!(),
555+
}
460556
};
461557

462558
let mut formatted_comment = rewrite_post_comment(&mut item_max_width)?;
@@ -699,6 +795,11 @@ pub(crate) fn get_comment_end(
699795
find_comment_end(&post_snippet[i..]).unwrap() + i,
700796
separator_index + 1,
701797
),
798+
// Comment is preceeded by new line and followed by a separator.
799+
(Some(i), Some(_)) if separator_index > i => cmp::max(
800+
find_comment_end(&post_snippet[i..]).unwrap() + i,
801+
separator_index + 1,
802+
),
702803
// Potential *single* line comment.
703804
(_, Some(j)) if j > separator_index => j + 1,
704805
_ => post_snippet.len(),

Diff for: tests/source/issue-3847.rs

+205
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
// Tests for multi one-line post-comments of list item
2+
// (related cases when `normalize_comments` is set are already included in other test files).
3+
4+
// Original cases from issue #3847
5+
type T1 = Result<
6+
u32 // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam
7+
// diam ac cursus. Aliquam condimentum in erat quis pretium.
8+
// accumsan urna. Cras volutpat sit amet quam.
9+
,
10+
bool,
11+
>;
12+
type T2 = Result<
13+
u32, // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam
14+
// diam ac cursus. Aliquam condimentum in erat quis pretium.
15+
// accumsan urna. Cras volutpat sit amet quam.
16+
bool,
17+
>;
18+
19+
// Additional test cases with lists
20+
fn main() {
21+
let a = ["GOOD" // Comment1
22+
// Comment2
23+
,
24+
];
25+
let b = ["WASBAD" // Comment1
26+
// Comment2
27+
,
28+
"CCC",
29+
];
30+
}
31+
32+
// Tests with one multi-line block comment
33+
type T3_good = Result<
34+
u32 /* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam
35+
* diam ac cursus. Aliquam condimentum in erat quis pretium.
36+
* accumsan urna. Cras volutpat sit amet quam. */
37+
,
38+
bool,
39+
>;
40+
type T4_good = Result<
41+
u32, /* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam
42+
* diam ac cursus. Aliquam condimentum in erat quis pretium.
43+
* accumsan urna. Cras volutpat sit amet quam. */
44+
bool,
45+
>;
46+
47+
fn main() {
48+
let a = ["WASGOOD1" /* Comment1
49+
* Comment2 */
50+
,
51+
"WASGOOD2", /* Comment1
52+
* Comment2 */
53+
"CCC",
54+
];
55+
}
56+
57+
// Tests with one-line block-comments
58+
type T5_good = Result<
59+
u32 /* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam */
60+
/* diam ac cursus. Aliquam condimentum in erat quis pretium. */
61+
/* accumsan urna. Cras volutpat sit amet quam. */
62+
,
63+
bool,
64+
>;
65+
type T6_good = Result<
66+
u32, /* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam */
67+
/* diam ac cursus. Aliquam condimentum in erat quis pretium. */
68+
/* accumsan urna. Cras volutpat sit amet quam. */
69+
bool,
70+
>;
71+
72+
// Tests with mix one-line and multi-linecomments - one-line is first
73+
type T8_good = Result<
74+
u32 // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam
75+
/* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam
76+
* diam ac cursus. Aliquam condimentum in erat quis pretium.
77+
* accumsan urna. Cras volutpat sit amet quam. */,
78+
bool,
79+
>;
80+
type T9_good = Result<
81+
u32 /* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam */
82+
/* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam
83+
* diam ac cursus. Aliquam condimentum in erat quis pretium.
84+
* accumsan urna. Cras volutpat sit amet quam. */
85+
// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam
86+
,
87+
bool,
88+
>;
89+
type T9_good = Result<
90+
u32 /* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam */
91+
/* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam
92+
* diam ac cursus. Aliquam condimentum in erat quis pretium.
93+
* accumsan urna. Cras volutpat sit amet quam. */,
94+
// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam
95+
bool,
96+
>;
97+
98+
// Tests with mix one-line and multi-linecomments - multi-line is first
99+
type T10_good = Result<
100+
u32 /* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam
101+
* diam ac cursus. Aliquam condimentum in erat quis pretium.
102+
* accumsan urna. Cras volutpat sit amet quam. */
103+
// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam
104+
,
105+
bool,
106+
>;
107+
type T11_good = Result<
108+
u32 /* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam
109+
* diam ac cursus. Aliquam condimentum in erat quis pretium.
110+
* accumsan urna. Cras volutpat sit amet quam. */
111+
/* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam */,
112+
// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam
113+
bool,
114+
>;
115+
type T12_good = Result<
116+
u32 /* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam
117+
* diam ac cursus. Aliquam condimentum in erat quis pretium.
118+
* accumsan urna. Cras volutpat sit amet quam. */
119+
/* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam */
120+
// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam
121+
,
122+
bool,
123+
>;
124+
type T12_good = Result<
125+
u32 /* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam
126+
* diam ac cursus. Aliquam condimentum in erat quis pretium.
127+
* accumsan urna. Cras volutpat sit amet quam. */
128+
/* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam */,
129+
// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam
130+
bool,
131+
>;
132+
type T13_good = Result<
133+
u32 /* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam
134+
* diam ac cursus. Aliquam condimentum in erat quis pretium.
135+
* accumsan urna. */ /*Cras volutpat sit amet quam. */
136+
/* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam */
137+
,
138+
bool,
139+
>;
140+
141+
// Tests with mix one-line and multi-linecomments -
142+
// multi-line is first with newline between comments
143+
type T14_good = Result<
144+
u32 /* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam
145+
* diam ac cursus. Aliquam condimentum in erat quis pretium.
146+
* accumsan urna. Cras volutpat sit amet quam. */
147+
148+
/* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam */,
149+
// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam
150+
bool,
151+
>;
152+
type T15_good = Result<
153+
u32 /* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam
154+
* diam ac cursus. Aliquam condimentum in erat quis pretium.
155+
* accumsan urna. Cras volutpat sit amet quam. */
156+
157+
158+
/* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam */
159+
160+
// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam
161+
,
162+
bool,
163+
>;
164+
165+
// Tests with first comment is not in same line of item
166+
type T16_good = Result<
167+
u32
168+
/* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam */
169+
/* diam ac cursus. Aliquam condimentum in erat quis pretium. */
170+
/* accumsan urna. Cras volutpat sit amet quam. */,
171+
bool,
172+
>;
173+
type T17_good = Result<
174+
u32
175+
/* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam */
176+
/* diam ac cursus. Aliquam condimentum in erat quis pretium. */
177+
/* accumsan urna. Cras volutpat sit amet quam. */
178+
,
179+
bool,
180+
>;
181+
type T18_good = Result<
182+
u32
183+
/* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam */
184+
/* diam ac cursus. Aliquam condimentum in erat quis pretium. */,
185+
/* accumsan urna. Cras volutpat sit amet quam. */
186+
bool,
187+
>;
188+
type T19_good = Result<
189+
u32
190+
/* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam
191+
* diam ac cursus. Aliquam condimentum in erat quis pretium.
192+
* accumsan urna. Cras volutpat sit amet quam. */,
193+
/* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam */
194+
// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam
195+
bool,
196+
>;
197+
type T20_good = Result<
198+
u32
199+
/* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam
200+
* diam ac cursus. Aliquam condimentum in erat quis pretium.
201+
* accumsan urna. Cras volutpat sit amet quam. */
202+
/* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam */,
203+
// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam
204+
bool,
205+
>;

0 commit comments

Comments
 (0)