3 use crate::clean::{self, DocFragment, DocFragmentKind, Item};
4 use crate::core::DocContext;
5 use crate::fold::{self, DocFolder};
6 use crate::passes::Pass;
11 pub const UNINDENT_COMMENTS: Pass = Pass {
12 name: "unindent-comments",
13 run: unindent_comments,
14 description: "removes excess indentation on comments in order for markdown to like it",
17 pub fn unindent_comments(krate: clean::Crate, _: &DocContext<'_>) -> clean::Crate {
18 CommentCleaner.fold_crate(krate)
21 struct CommentCleaner;
23 impl fold::DocFolder for CommentCleaner {
24 fn fold_item(&mut self, mut i: Item) -> Option<Item> {
25 i.attrs.unindent_doc_comments();
26 self.fold_item_recur(i)
30 impl clean::Attributes {
31 pub fn unindent_doc_comments(&mut self) {
32 unindent_fragments(&mut self.doc_strings);
36 fn unindent_fragments(docs: &mut Vec<DocFragment>) {
37 let mut saw_first_line = false;
38 let mut saw_second_line = false;
40 let add = if !docs.windows(2).all(|arr| arr[0].kind == arr[1].kind)
41 && docs.iter().any(|d| d.kind == DocFragmentKind::SugaredDoc)
43 // In case we have a mix of sugared doc comments and "raw" ones, we want the sugared one to
44 // "decide" how much the minimum indent will be.
50 let min_indent = match docs
53 fragment.doc.lines().fold(usize::MAX, |min_indent, line| {
54 // After we see the first non-whitespace line, look at
55 // the line we have. If it is not whitespace, and therefore
56 // part of the first paragraph, then ignore the indentation
57 // level of the first line
58 let ignore_previous_indents =
59 saw_first_line && !saw_second_line && !line.chars().all(|c| c.is_whitespace());
61 let min_indent = if ignore_previous_indents { usize::MAX } else { min_indent };
64 saw_second_line = true;
67 if line.chars().all(|c| c.is_whitespace()) {
70 saw_first_line = true;
71 // Compare against either space or tab, ignoring whether they are
73 let whitespace = line.chars().take_while(|c| *c == ' ' || *c == '\t').count();
74 cmp::min(min_indent, whitespace)
75 + if fragment.kind == DocFragmentKind::SugaredDoc { 0 } else { add }
85 let mut first_ignored = false;
86 for fragment in docs {
87 let lines: Vec<_> = fragment.doc.lines().collect();
89 if !lines.is_empty() {
90 let min_indent = if fragment.kind != DocFragmentKind::SugaredDoc && min_indent > 0 {
96 let mut iter = lines.iter();
97 let mut result = if !first_ignored {
99 vec![iter.next().unwrap().trim_start().to_string()]
103 result.extend_from_slice(
106 if line.chars().all(|c| c.is_whitespace()) {
109 assert!(line.len() >= min_indent);
110 line[min_indent..].to_string()
113 .collect::<Vec<_>>(),
115 fragment.doc = result.join("\n");