]> git.proxmox.com Git - rustc.git/blame - src/librustdoc/passes/unindent_comments.rs
New upstream version 1.41.1+dfsg1
[rustc.git] / src / librustdoc / passes / unindent_comments.rs
CommitLineData
9e0c209e
SL
1use std::cmp;
2use std::string::String;
3use std::usize;
4
9fa01778 5use crate::clean::{self, DocFragment, Item};
532ac7d7 6use crate::core::DocContext;
9fa01778
XL
7use crate::fold::{self, DocFolder};
8use crate::passes::Pass;
b7449926 9
416331ca
XL
10#[cfg(test)]
11mod tests;
12
532ac7d7
XL
13pub const UNINDENT_COMMENTS: Pass = Pass {
14 name: "unindent-comments",
60c5eb7d 15 run: unindent_comments,
532ac7d7
XL
16 description: "removes excess indentation on comments in order for markdown to like it",
17};
9e0c209e 18
532ac7d7 19pub fn unindent_comments(krate: clean::Crate, _: &DocContext<'_>) -> clean::Crate {
476ff2be 20 CommentCleaner.fold_crate(krate)
9e0c209e
SL
21}
22
23struct CommentCleaner;
24
25impl fold::DocFolder for CommentCleaner {
26 fn fold_item(&mut self, mut i: Item) -> Option<Item> {
476ff2be 27 i.attrs.unindent_doc_comments();
9e0c209e
SL
28 self.fold_item_recur(i)
29 }
30}
31
476ff2be
SL
32impl clean::Attributes {
33 pub fn unindent_doc_comments(&mut self) {
ff7c6d11
XL
34 unindent_fragments(&mut self.doc_strings);
35 }
36}
37
38fn unindent_fragments(docs: &mut Vec<DocFragment>) {
39 for fragment in docs {
40 match *fragment {
60c5eb7d
XL
41 DocFragment::SugaredDoc(_, _, ref mut doc_string)
42 | DocFragment::RawDoc(_, _, ref mut doc_string)
43 | DocFragment::Include(_, _, _, ref mut doc_string) => {
44 *doc_string = unindent(doc_string)
45 }
476ff2be
SL
46 }
47 }
48}
49
9e0c209e 50fn unindent(s: &str) -> String {
60c5eb7d 51 let lines = s.lines().collect::<Vec<&str>>();
9e0c209e
SL
52 let mut saw_first_line = false;
53 let mut saw_second_line = false;
54 let min_indent = lines.iter().fold(usize::MAX, |min_indent, line| {
9e0c209e
SL
55 // After we see the first non-whitespace line, look at
56 // the line we have. If it is not whitespace, and therefore
57 // part of the first paragraph, then ignore the indentation
58 // level of the first line
59 let ignore_previous_indents =
60c5eb7d 60 saw_first_line && !saw_second_line && !line.chars().all(|c| c.is_whitespace());
9e0c209e 61
60c5eb7d 62 let min_indent = if ignore_previous_indents { usize::MAX } else { min_indent };
9e0c209e
SL
63
64 if saw_first_line {
65 saw_second_line = true;
66 }
67
68 if line.chars().all(|c| c.is_whitespace()) {
69 min_indent
70 } else {
71 saw_first_line = true;
72 let mut whitespace = 0;
73 line.chars().all(|char| {
74 // Compare against either space or tab, ignoring whether they
75 // are mixed or not
76 if char == ' ' || char == '\t' {
77 whitespace += 1;
78 true
79 } else {
80 false
81 }
82 });
83 cmp::min(min_indent, whitespace)
84 }
85 });
86
87 if !lines.is_empty() {
60c5eb7d
XL
88 let mut unindented = vec![lines[0].trim_start().to_string()];
89 unindented.extend_from_slice(
90 &lines[1..]
91 .iter()
92 .map(|&line| {
93 if line.chars().all(|c| c.is_whitespace()) {
94 line.to_string()
95 } else {
96 assert!(line.len() >= min_indent);
97 line[min_indent..].to_string()
98 }
99 })
100 .collect::<Vec<_>>(),
101 );
9e0c209e
SL
102 unindented.join("\n")
103 } else {
104 s.to_string()
105 }
106}