]> git.proxmox.com Git - rustc.git/blame - src/librustdoc/passes/unindent_comments.rs
Merge tag 'debian/1.52.1+dfsg1-1_exp2' into proxmox/buster
[rustc.git] / src / librustdoc / passes / unindent_comments.rs
CommitLineData
9e0c209e 1use std::cmp;
9e0c209e 2
29967ef6 3use crate::clean::{self, DocFragment, DocFragmentKind, Item};
532ac7d7 4use crate::core::DocContext;
9fa01778
XL
5use crate::fold::{self, DocFolder};
6use crate::passes::Pass;
b7449926 7
416331ca
XL
8#[cfg(test)]
9mod tests;
10
fc512014 11crate const UNINDENT_COMMENTS: Pass = Pass {
532ac7d7 12 name: "unindent-comments",
60c5eb7d 13 run: unindent_comments,
532ac7d7
XL
14 description: "removes excess indentation on comments in order for markdown to like it",
15};
9e0c209e 16
6a06907d 17crate fn unindent_comments(krate: clean::Crate, _: &mut DocContext<'_>) -> clean::Crate {
476ff2be 18 CommentCleaner.fold_crate(krate)
9e0c209e
SL
19}
20
21struct CommentCleaner;
22
23impl fold::DocFolder for CommentCleaner {
24 fn fold_item(&mut self, mut i: Item) -> Option<Item> {
476ff2be 25 i.attrs.unindent_doc_comments();
fc512014 26 Some(self.fold_item_recur(i))
9e0c209e
SL
27 }
28}
29
476ff2be 30impl clean::Attributes {
fc512014 31 crate fn unindent_doc_comments(&mut self) {
ff7c6d11
XL
32 unindent_fragments(&mut self.doc_strings);
33 }
34}
35
36fn unindent_fragments(docs: &mut Vec<DocFragment>) {
29967ef6
XL
37 // `add` is used in case the most common sugared doc syntax is used ("/// "). The other
38 // fragments kind's lines are never starting with a whitespace unless they are using some
39 // markdown formatting requiring it. Therefore, if the doc block have a mix between the two,
40 // we need to take into account the fact that the minimum indent minus one (to take this
41 // whitespace into account).
42 //
43 // For example:
44 //
45 // /// hello!
46 // #[doc = "another"]
47 //
48 // In this case, you want "hello! another" and not "hello! another".
49 let add = if docs.windows(2).any(|arr| arr[0].kind != arr[1].kind)
50 && docs.iter().any(|d| d.kind == DocFragmentKind::SugaredDoc)
51 {
52 // In case we have a mix of sugared doc comments and "raw" ones, we want the sugared one to
53 // "decide" how much the minimum indent will be.
54 1
55 } else {
56 0
57 };
9e0c209e 58
29967ef6
XL
59 // `min_indent` is used to know how much whitespaces from the start of each lines must be
60 // removed. Example:
61 //
62 // /// hello!
63 // #[doc = "another"]
64 //
65 // In here, the `min_indent` is 1 (because non-sugared fragment are always counted with minimum
66 // 1 whitespace), meaning that "hello!" will be considered a codeblock because it starts with 4
67 // (5 - 1) whitespaces.
68 let min_indent = match docs
69 .iter()
70 .map(|fragment| {
5869c6ff 71 fragment.doc.as_str().lines().fold(usize::MAX, |min_indent, line| {
29967ef6
XL
72 if line.chars().all(|c| c.is_whitespace()) {
73 min_indent
74 } else {
75 // Compare against either space or tab, ignoring whether they are
76 // mixed or not.
77 let whitespace = line.chars().take_while(|c| *c == ' ' || *c == '\t').count();
78 cmp::min(min_indent, whitespace)
79 + if fragment.kind == DocFragmentKind::SugaredDoc { 0 } else { add }
80 }
81 })
82 })
83 .min()
84 {
85 Some(x) => x,
86 None => return,
87 };
9e0c209e 88
29967ef6 89 for fragment in docs {
5869c6ff 90 if fragment.doc.as_str().lines().count() == 0 {
29967ef6 91 continue;
9e0c209e
SL
92 }
93
29967ef6
XL
94 let min_indent = if fragment.kind != DocFragmentKind::SugaredDoc && min_indent > 0 {
95 min_indent - add
9e0c209e 96 } else {
29967ef6
XL
97 min_indent
98 };
99
5869c6ff 100 fragment.indent = min_indent;
9e0c209e
SL
101 }
102}