Skip to content

Commit 4895699

Browse files
authored
Merge pull request rust-lang#3083 from scampi/itemized_blocks
Handle itemized blocks in comments
2 parents 839c494 + 375c878 commit 4895699

File tree

14 files changed

+818
-96
lines changed

14 files changed

+818
-96
lines changed

src/comment.rs

Lines changed: 134 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,58 @@ impl CodeBlockAttribute {
433433
}
434434
}
435435

436+
/// Block that is formatted as an item.
437+
///
438+
/// An item starts with either a star `*` or a dash `-`. Different level of indentation are
439+
/// handled.
440+
struct ItemizedBlock {
441+
/// the number of whitespaces up to the item sigil
442+
indent: usize,
443+
/// the string that marks the start of an item
444+
opener: String,
445+
/// sequence of whitespaces to prefix new lines that are part of the item
446+
line_start: String,
447+
}
448+
449+
impl ItemizedBlock {
450+
/// Returns true if the line is formatted as an item
451+
fn is_itemized_line(line: &str) -> bool {
452+
let trimmed = line.trim_left();
453+
trimmed.starts_with("* ") || trimmed.starts_with("- ")
454+
}
455+
456+
/// Creates a new ItemizedBlock described with the given line.
457+
/// The `is_itemized_line` needs to be called first.
458+
fn new(line: &str) -> ItemizedBlock {
459+
let space_to_sigil = line.chars().take_while(|c| c.is_whitespace()).count();
460+
let indent = space_to_sigil + 2;
461+
ItemizedBlock {
462+
indent,
463+
opener: line[..indent].to_string(),
464+
line_start: " ".repeat(indent),
465+
}
466+
}
467+
468+
/// Returns a `StringFormat` used for formatting the content of an item
469+
fn create_string_format<'a>(&'a self, fmt: &'a StringFormat) -> StringFormat<'a> {
470+
StringFormat {
471+
opener: "",
472+
closer: "",
473+
line_start: "",
474+
line_end: "",
475+
shape: Shape::legacy(fmt.shape.width.saturating_sub(self.indent), Indent::empty()),
476+
trim_end: true,
477+
config: fmt.config,
478+
}
479+
}
480+
481+
/// Returns true if the line is part of the current itemized block
482+
fn in_block(&self, line: &str) -> bool {
483+
!ItemizedBlock::is_itemized_line(line)
484+
&& self.indent <= line.chars().take_while(|c| c.is_whitespace()).count()
485+
}
486+
}
487+
436488
fn rewrite_comment_inner(
437489
orig: &str,
438490
block_style: bool,
@@ -493,15 +545,17 @@ fn rewrite_comment_inner(
493545
let mut code_block_buffer = String::with_capacity(128);
494546
let mut is_prev_line_multi_line = false;
495547
let mut code_block_attr = None;
548+
let mut item_block_buffer = String::with_capacity(128);
549+
let mut item_block: Option<ItemizedBlock> = None;
496550
let comment_line_separator = format!("{}{}", indent_str, line_start);
497-
let join_code_block_with_comment_line_separator = |s: &str| {
551+
let join_block = |s: &str, sep: &str| {
498552
let mut result = String::with_capacity(s.len() + 128);
499553
let mut iter = s.lines().peekable();
500554
while let Some(line) = iter.next() {
501555
result.push_str(line);
502556
result.push_str(match iter.peek() {
503-
Some(next_line) if next_line.is_empty() => comment_line_separator.trim_right(),
504-
Some(..) => &comment_line_separator,
557+
Some(next_line) if next_line.is_empty() => sep.trim_right(),
558+
Some(..) => &sep,
505559
None => "",
506560
});
507561
}
@@ -511,7 +565,26 @@ fn rewrite_comment_inner(
511565
for (i, (line, has_leading_whitespace)) in lines.enumerate() {
512566
let is_last = i == count_newlines(orig);
513567

514-
if let Some(ref attr) = code_block_attr {
568+
if let Some(ref ib) = item_block {
569+
if ib.in_block(&line) {
570+
item_block_buffer.push_str(&line);
571+
item_block_buffer.push('\n');
572+
continue;
573+
}
574+
is_prev_line_multi_line = false;
575+
fmt.shape = Shape::legacy(max_chars, fmt_indent);
576+
let item_fmt = ib.create_string_format(&fmt);
577+
result.push_str(&comment_line_separator);
578+
result.push_str(&ib.opener);
579+
match rewrite_string(&item_block_buffer.replace("\n", " "), &item_fmt) {
580+
Some(s) => result.push_str(&join_block(
581+
&s,
582+
&format!("{}{}", &comment_line_separator, ib.line_start),
583+
)),
584+
None => result.push_str(&join_block(&item_block_buffer, &comment_line_separator)),
585+
};
586+
item_block_buffer.clear();
587+
} else if let Some(ref attr) = code_block_attr {
515588
if line.starts_with("```") {
516589
let code_block = match attr {
517590
CodeBlockAttribute::Ignore | CodeBlockAttribute::Text => {
@@ -529,7 +602,7 @@ fn rewrite_comment_inner(
529602
};
530603
if !code_block.is_empty() {
531604
result.push_str(&comment_line_separator);
532-
result.push_str(&join_code_block_with_comment_line_separator(&code_block));
605+
result.push_str(&join_block(&code_block, &comment_line_separator));
533606
}
534607
code_block_buffer.clear();
535608
result.push_str(&comment_line_separator);
@@ -538,46 +611,42 @@ fn rewrite_comment_inner(
538611
} else {
539612
code_block_buffer.push_str(&hide_sharp_behind_comment(line));
540613
code_block_buffer.push('\n');
541-
542-
if is_last {
543-
// There is a code block that is not properly enclosed by backticks.
544-
// We will leave them untouched.
545-
result.push_str(&comment_line_separator);
546-
result.push_str(&join_code_block_with_comment_line_separator(
547-
&trim_custom_comment_prefix(&code_block_buffer),
548-
));
549-
}
550614
}
615+
continue;
616+
}
551617

618+
code_block_attr = None;
619+
item_block = None;
620+
if line.starts_with("```") {
621+
code_block_attr = Some(CodeBlockAttribute::new(&line[3..]))
622+
} else if config.wrap_comments() && ItemizedBlock::is_itemized_line(&line) {
623+
let ib = ItemizedBlock::new(&line);
624+
item_block_buffer.push_str(&line[ib.indent..]);
625+
item_block_buffer.push('\n');
626+
item_block = Some(ib);
552627
continue;
553-
} else {
554-
code_block_attr = if line.starts_with("```") {
555-
Some(CodeBlockAttribute::new(&line[3..]))
556-
} else {
557-
None
558-
};
628+
}
559629

560-
if result == opener {
561-
let force_leading_whitespace = opener == "/* " && count_newlines(orig) == 0;
562-
if !has_leading_whitespace && !force_leading_whitespace && result.ends_with(' ') {
563-
result.pop();
564-
}
565-
if line.is_empty() {
566-
continue;
567-
}
568-
} else if is_prev_line_multi_line && !line.is_empty() {
569-
result.push(' ')
570-
} else if is_last && line.is_empty() {
571-
// trailing blank lines are unwanted
572-
if !closer.is_empty() {
573-
result.push_str(&indent_str);
574-
}
575-
break;
576-
} else {
577-
result.push_str(&comment_line_separator);
578-
if !has_leading_whitespace && result.ends_with(' ') {
579-
result.pop();
580-
}
630+
if result == opener {
631+
let force_leading_whitespace = opener == "/* " && count_newlines(orig) == 0;
632+
if !has_leading_whitespace && !force_leading_whitespace && result.ends_with(' ') {
633+
result.pop();
634+
}
635+
if line.is_empty() {
636+
continue;
637+
}
638+
} else if is_prev_line_multi_line && !line.is_empty() {
639+
result.push(' ')
640+
} else if is_last && line.is_empty() {
641+
// trailing blank lines are unwanted
642+
if !closer.is_empty() {
643+
result.push_str(&indent_str);
644+
}
645+
break;
646+
} else {
647+
result.push_str(&comment_line_separator);
648+
if !has_leading_whitespace && result.ends_with(' ') {
649+
result.pop();
581650
}
582651
}
583652

@@ -631,6 +700,30 @@ fn rewrite_comment_inner(
631700
is_prev_line_multi_line = false;
632701
}
633702
}
703+
if !code_block_buffer.is_empty() {
704+
// There is a code block that is not properly enclosed by backticks.
705+
// We will leave them untouched.
706+
result.push_str(&comment_line_separator);
707+
result.push_str(&join_block(
708+
&trim_custom_comment_prefix(&code_block_buffer),
709+
&comment_line_separator,
710+
));
711+
}
712+
if !item_block_buffer.is_empty() {
713+
// the last few lines are part of an itemized block
714+
let ib = item_block.unwrap();
715+
fmt.shape = Shape::legacy(max_chars, fmt_indent);
716+
let item_fmt = ib.create_string_format(&fmt);
717+
result.push_str(&comment_line_separator);
718+
result.push_str(&ib.opener);
719+
match rewrite_string(&item_block_buffer.replace("\n", " "), &item_fmt) {
720+
Some(s) => result.push_str(&join_block(
721+
&s,
722+
&format!("{}{}", &comment_line_separator, ib.line_start),
723+
)),
724+
None => result.push_str(&join_block(&item_block_buffer, &comment_line_separator)),
725+
};
726+
}
634727

635728
result.push_str(closer);
636729
if result.ends_with(opener) && opener.ends_with(' ') {

0 commit comments

Comments
 (0)