Skip to content

Commit db09dac

Browse files
committed
add documentation and clarify some code about list stringification
1 parent 9865ce0 commit db09dac

File tree

1 file changed

+88
-78
lines changed

1 file changed

+88
-78
lines changed

src/lists.rs

Lines changed: 88 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -275,33 +275,31 @@ where
275275
}
276276
}
277277

278-
struct ItemState {
278+
/// Stores the state of the ongoing stringification of a list item.
279+
struct WriteListItemState {
280+
/// The offset of the item in the list.
279281
ith: usize,
282+
/// Whether or not the item is the first in the list.
280283
first: bool,
284+
/// Whether or not the item is the last in the list.
281285
last: bool,
286+
/// Whether or not the item is the first to be written on the current line.
282287
first_item_on_line: bool,
288+
/// The placement of the separator
289+
sep_place: SeparatorPlace,
290+
/// Whether or not the separator should be written.
291+
separate: bool,
292+
/// Whether or not the separator can be trailing.
293+
trailing_separator: bool,
283294
}
284295

285-
impl ItemState {
286-
fn new() -> ItemState {
287-
ItemState {
296+
impl WriteListItemState {
297+
fn new(formatting: &ListFormatting<'_>) -> WriteListItemState {
298+
WriteListItemState {
288299
ith: 0,
289300
first: false,
290301
last: false,
291302
first_item_on_line: true,
292-
}
293-
}
294-
}
295-
296-
struct WriteListState {
297-
sep_place: SeparatorPlace,
298-
separate: bool,
299-
trailing_separator: bool,
300-
}
301-
302-
impl WriteListState {
303-
fn new(formatting: &ListFormatting<'_>) -> WriteListState {
304-
WriteListState {
305303
sep_place: SeparatorPlace::from_tactic(
306304
formatting.separator_place,
307305
formatting.tactic,
@@ -314,27 +312,36 @@ impl WriteListState {
314312
}
315313
}
316314

317-
fn should_separate(&mut self, item_state: &ItemState) {
315+
/// Set if a separator should be written for the current item.
316+
fn should_separate(&mut self) {
318317
self.separate = match self.sep_place {
319-
SeparatorPlace::Front => !item_state.first,
320-
SeparatorPlace::Back => !item_state.last || self.trailing_separator,
318+
SeparatorPlace::Front => !self.first,
319+
SeparatorPlace::Back => !self.last || self.trailing_separator,
321320
};
322321
}
323322

323+
/// Returns true if a separtor should be placed in front of the current item.
324324
fn should_separate_if_front(&self) -> bool {
325325
self.separate && self.sep_place.is_front()
326326
}
327327

328+
/// Returns true if a separtor should be placed at the back of the current item.
328329
fn should_separate_if_back(&self) -> bool {
329330
self.separate && self.sep_place.is_back()
330331
}
331332
}
332333

334+
/// Align comments written after items.
333335
struct PostCommentAlignment {
336+
/// The largest item width in a group of items.
334337
item_max_width: Option<usize>,
338+
/// The length of the first item.
335339
first_item_len: usize,
340+
/// Whether or not an item that is not the first has the same length as the first item.
336341
middle_item_same_len_first: bool,
342+
/// The length of the last item.
337343
last_item_len: usize,
344+
/// Whether or not an item that is not the last has the same length as the last item.
338345
middle_item_same_len_last: bool,
339346
}
340347

@@ -373,11 +380,12 @@ impl PostCommentAlignment {
373380
}
374381
}
375382

383+
/// Get the length of the largest item starting at the ith-item.
376384
fn update_item_max_width<T: AsRef<ListItem>>(
377385
&mut self,
378386
items: &[T],
379387
formatting: &ListFormatting<'_>,
380-
item_state: &ItemState,
388+
item_state: &WriteListItemState,
381389
inner_item: &str,
382390
overhead: usize,
383391
) {
@@ -423,30 +431,35 @@ impl PostCommentAlignment {
423431
max_width
424432
}
425433

426-
fn compute(
434+
/// Computes the number whitespaces to insert after an item so that its post comment is aligned
435+
/// with others.
436+
///
437+
/// The alignment logic is complicated by the fact that the alignment is based on the longest
438+
/// item, without the additional space taken by the separator. For the first and last items
439+
/// where the separator may be missing, some compensation needs to be computed.
440+
fn alignment(
427441
&self,
428-
item_state: &ItemState,
442+
item_state: &WriteListItemState,
429443
inner_item_len: usize,
430444
fmt: &ListFormatting<'_>,
431-
write_list_state: &WriteListState,
432445
) -> usize {
433446
// 1 = whitespace before the post_comment
434-
let alignment = self
435-
.item_max_width
436-
.unwrap_or(0)
437-
.saturating_sub(inner_item_len)
438-
+ 1;
439447
if self.item_max_width.is_none() {
440-
return alignment;
448+
return 1;
441449
}
442-
let first_item_longest = self.item_max_width.unwrap() == self.first_item_len;
443-
let last_item_longest = self.item_max_width.unwrap() == self.last_item_len;
444-
let alignment = match write_list_state.sep_place {
450+
let item_max_width = self.item_max_width.unwrap();
451+
let alignment = item_max_width.saturating_sub(inner_item_len) + 1;
452+
let first_item_longest = item_max_width == self.first_item_len;
453+
let last_item_longest = item_max_width == self.last_item_len;
454+
455+
let alignment = match item_state.sep_place {
445456
/*
446457
* Front separator: first item is missing the separator and needs to be compensated
447458
*/
448459
SeparatorPlace::Front => {
449460
match (item_state.first, first_item_longest) {
461+
// in case a middle item has the same length as the first, then all items have
462+
// the correct alignment: only the first item needs to account for the separator
450463
_ if self.middle_item_same_len_first => {
451464
alignment
452465
+ if item_state.first {
@@ -457,11 +470,13 @@ impl PostCommentAlignment {
457470
}
458471
// first item is the longest: others need to minus the (separator + padding)
459472
// to the alignment
460-
(false, true) => {
461-
// FIXME: document the trim the leading whitespace
462-
alignment.saturating_sub(fmt.separator.trim_start().len())
463-
- if fmt.padding { 1 } else { 0 }
464-
}
473+
(false, true) => alignment
474+
.saturating_sub(if item_state.first_item_on_line {
475+
fmt.separator.trim_start().len()
476+
} else {
477+
fmt.separator.len()
478+
})
479+
.saturating_sub(if fmt.padding { 1 } else { 0 }),
465480
// first item is not the longest: first needs to add (searator + padding)
466481
// to the alignment
467482
(true, false) => {
@@ -480,7 +495,7 @@ impl PostCommentAlignment {
480495
match (item_state.last, last_item_longest) {
481496
_ if self.middle_item_same_len_last => {
482497
alignment
483-
+ if item_state.last && !write_list_state.separate {
498+
+ if item_state.last && !item_state.separate {
484499
fmt.separator.len()
485500
} else {
486501
0
@@ -491,7 +506,7 @@ impl PostCommentAlignment {
491506
alignment.saturating_sub(fmt.separator.len())
492507
}
493508
// last item is not the longest: last needs to add sep to the alignment
494-
(true, false) if !write_list_state.separate => alignment + fmt.separator.len(),
509+
(true, false) if !item_state.separate => alignment + fmt.separator.len(),
495510
_ => alignment,
496511
}
497512
}
@@ -507,47 +522,36 @@ where
507522
T: AsRef<ListItem>,
508523
{
509524
let tactic = formatting.tactic;
525+
let indent_str = &formatting.shape.indent.to_string(formatting.config);
510526

511527
let mut result = String::with_capacity(128);
512528
let mut iter = items.iter().enumerate().peekable();
529+
530+
// Mixed tactic state
513531
let mut prev_item_had_post_comment = false;
514532
let mut prev_item_is_nested_import = false;
533+
let mut line_len = 0;
515534

516-
let mut item_state = ItemState::new();
517-
let mut write_list_state = WriteListState::new(formatting);
535+
let mut item_state = WriteListItemState::new(formatting);
518536
let mut pca = PostCommentAlignment::new(items);
519537

520-
let mut line_len = 0;
521-
let indent_str = &formatting.shape.indent.to_string(formatting.config);
522538
while let Some((i, item)) = iter.next() {
523539
let item = item.as_ref();
524540
let inner_item = item.item.as_ref()?;
541+
542+
if !item.is_substantial() {
543+
continue;
544+
}
545+
525546
item_state.ith = i;
526547
item_state.first = i == 0;
527548
item_state.last = iter.peek().is_none();
528-
write_list_state.should_separate(&item_state);
529-
let item_sep_len = if write_list_state.separate {
549+
item_state.should_separate();
550+
let item_sep_len = if item_state.separate {
530551
formatting.separator.len()
531552
} else {
532553
0
533554
};
534-
535-
// Item string may be multi-line. Its length (used for block comment alignment)
536-
// should be only the length of the last line.
537-
let item_last_line = if item.is_multiline() {
538-
inner_item.lines().last().unwrap_or("")
539-
} else {
540-
inner_item.as_ref()
541-
};
542-
let mut item_last_line_width = item_last_line.len() + item_sep_len;
543-
if item_last_line.starts_with(&**indent_str) {
544-
item_last_line_width -= indent_str.len();
545-
}
546-
547-
if !item.is_substantial() {
548-
continue;
549-
}
550-
551555
match tactic {
552556
_ if !formatting.custom_list_tactic.is_empty() => {
553557
if *formatting
@@ -599,19 +603,20 @@ where
599603
line_len = 0;
600604
item_state.first_item_on_line = true;
601605
if formatting.ends_with_newline {
602-
write_list_state.trailing_separator = true;
606+
item_state.trailing_separator = true;
603607
}
604608
} else if formatting.padding && line_len > 0 {
605609
result.push(' ');
606610
line_len += 1;
607611
}
608612

609613
if item_state.last && formatting.ends_with_newline {
610-
write_list_state.separate =
611-
formatting.trailing_separator != SeparatorTactic::Never;
614+
item_state.separate = formatting.trailing_separator != SeparatorTactic::Never;
612615
}
613616

614617
line_len += total_width;
618+
prev_item_had_post_comment = item.post_comment.is_some();
619+
prev_item_is_nested_import = inner_item.contains("::");
615620
}
616621
_ => {}
617622
}
@@ -654,7 +659,7 @@ where
654659
pca.item_max_width = None;
655660
}
656661

657-
if write_list_state.should_separate_if_front() && !item_state.first {
662+
if item_state.should_separate_if_front() && !item_state.first {
658663
if formatting.padding {
659664
result.push_str(formatting.separator.trim());
660665
result.push(' ');
@@ -685,7 +690,7 @@ where
685690
result.push_str(&formatted_comment);
686691
}
687692

688-
if write_list_state.should_separate_if_back() {
693+
if item_state.should_separate_if_back() {
689694
result.push_str(formatting.separator);
690695
}
691696

@@ -699,6 +704,18 @@ where
699704
} else if let Some(max_width) = item_max_width {
700705
max_width + 2
701706
} else {
707+
// Item string may be multi-line. Its length (used for block comment alignment)
708+
// should be only the length of the last line.
709+
let item_last_line = if item.is_multiline() {
710+
inner_item.lines().last().unwrap_or("")
711+
} else {
712+
inner_item.as_ref()
713+
};
714+
let mut item_last_line_width = item_last_line.len() + item_sep_len;
715+
if item_last_line.starts_with(&**indent_str) {
716+
item_last_line_width -= indent_str.len();
717+
}
718+
702719
// 1 = space between item and comment.
703720
item_last_line_width + 1
704721
};
@@ -725,11 +742,10 @@ where
725742
if !starts_with_newline(comment) {
726743
if formatting.align_comments {
727744
let mut comment_alignment =
728-
pca.compute(&item_state, inner_item.len(), formatting, &write_list_state);
745+
pca.alignment(&item_state, inner_item.len(), formatting);
729746
if first_line_width(&formatted_comment)
730747
+ last_line_width(&result)
731748
+ comment_alignment
732-
+ 1
733749
> formatting.config.max_width()
734750
{
735751
pca.item_max_width = None;
@@ -741,12 +757,8 @@ where
741757
overhead,
742758
);
743759
formatted_comment = rewrite_post_comment(pca.item_max_width)?;
744-
comment_alignment = pca.compute(
745-
&item_state,
746-
inner_item.len(),
747-
formatting,
748-
&write_list_state,
749-
);
760+
comment_alignment =
761+
pca.alignment(&item_state, inner_item.len(), formatting);
750762
}
751763
result.push_str(&" ".repeat(comment_alignment));
752764
} else {
@@ -774,8 +786,6 @@ where
774786
}
775787

776788
item_state.first_item_on_line = false;
777-
prev_item_had_post_comment = item.post_comment.is_some();
778-
prev_item_is_nested_import = inner_item.contains("::");
779789
}
780790

781791
Some(result)

0 commit comments

Comments
 (0)