3 use rustc_span::symbol::kw;
5 use crate::clean::{self, DocFragment, DocFragmentKind, Item};
6 use crate::core::DocContext;
7 use crate::fold::{self, DocFolder};
8 use crate::passes::Pass;
13 crate const UNINDENT_COMMENTS: Pass = Pass {
14 name: "unindent-comments",
15 run: unindent_comments,
16 description: "removes excess indentation on comments in order for markdown to like it",
19 crate fn unindent_comments(krate: clean::Crate, _: &mut DocContext<'_>) -> clean::Crate {
20 CommentCleaner.fold_crate(krate)
23 struct CommentCleaner;
25 impl fold::DocFolder for CommentCleaner {
26 fn fold_item(&mut self, mut i: Item) -> Option<Item> {
27 i.attrs.unindent_doc_comments();
28 Some(self.fold_item_recur(i))
32 impl clean::Attributes {
33 crate fn unindent_doc_comments(&mut self) {
34 unindent_fragments(&mut self.doc_strings);
38 fn unindent_fragments(docs: &mut Vec<DocFragment>) {
39 // `add` is used in case the most common sugared doc syntax is used ("/// "). The other
40 // fragments kind's lines are never starting with a whitespace unless they are using some
41 // markdown formatting requiring it. Therefore, if the doc block have a mix between the two,
42 // we need to take into account the fact that the minimum indent minus one (to take this
43 // whitespace into account).
50 // In this case, you want "hello! another" and not "hello! another".
51 let add = if docs.windows(2).any(|arr| arr[0].kind != arr[1].kind)
52 && docs.iter().any(|d| d.kind == DocFragmentKind::SugaredDoc)
54 // In case we have a mix of sugared doc comments and "raw" ones, we want the sugared one to
55 // "decide" how much the minimum indent will be.
61 // `min_indent` is used to know how much whitespaces from the start of each lines must be
67 // In here, the `min_indent` is 1 (because non-sugared fragment are always counted with minimum
68 // 1 whitespace), meaning that "hello!" will be considered a codeblock because it starts with 4
69 // (5 - 1) whitespaces.
70 let min_indent = match docs
73 fragment.doc.as_str().lines().fold(usize::MAX, |min_indent, line| {
74 if line.chars().all(|c| c.is_whitespace()) {
77 // Compare against either space or tab, ignoring whether they are
79 let whitespace = line.chars().take_while(|c| *c == ' ' || *c == '\t').count();
80 cmp::min(min_indent, whitespace)
81 + if fragment.kind == DocFragmentKind::SugaredDoc { 0 } else { add }
91 for fragment in docs {
92 if fragment.doc == kw::Empty {
96 let min_indent = if fragment.kind != DocFragmentKind::SugaredDoc && min_indent > 0 {
102 fragment.indent = min_indent;