blob: a9cf5a87f5493671365f49c0b95490f737be4415 [file] [log] [blame]
use std::cmp;
use std::string::String;
use crate::clean::{self, DocFragment, Item};
use crate::core::DocContext;
use crate::fold::{self, DocFolder};
use crate::passes::Pass;
#[cfg(test)]
mod tests;
pub const UNINDENT_COMMENTS: Pass = Pass {
name: "unindent-comments",
run: unindent_comments,
description: "removes excess indentation on comments in order for markdown to like it",
};
pub fn unindent_comments(krate: clean::Crate, _: &DocContext<'_>) -> clean::Crate {
CommentCleaner.fold_crate(krate)
}
struct CommentCleaner;
impl fold::DocFolder for CommentCleaner {
fn fold_item(&mut self, mut i: Item) -> Option<Item> {
i.attrs.unindent_doc_comments();
self.fold_item_recur(i)
}
}
impl clean::Attributes {
pub fn unindent_doc_comments(&mut self) {
unindent_fragments(&mut self.doc_strings);
}
}
fn unindent_fragments(docs: &mut Vec<DocFragment>) {
for fragment in docs {
fragment.doc = unindent(&fragment.doc);
}
}
fn unindent(s: &str) -> String {
let lines = s.lines().collect::<Vec<&str>>();
let mut saw_first_line = false;
let mut saw_second_line = false;
let min_indent = lines.iter().fold(usize::MAX, |min_indent, line| {
// After we see the first non-whitespace line, look at
// the line we have. If it is not whitespace, and therefore
// part of the first paragraph, then ignore the indentation
// level of the first line
let ignore_previous_indents =
saw_first_line && !saw_second_line && !line.chars().all(|c| c.is_whitespace());
let min_indent = if ignore_previous_indents { usize::MAX } else { min_indent };
if saw_first_line {
saw_second_line = true;
}
if line.chars().all(|c| c.is_whitespace()) {
min_indent
} else {
saw_first_line = true;
let mut whitespace = 0;
line.chars().all(|char| {
// Compare against either space or tab, ignoring whether they
// are mixed or not
if char == ' ' || char == '\t' {
whitespace += 1;
true
} else {
false
}
});
cmp::min(min_indent, whitespace)
}
});
if !lines.is_empty() {
let mut unindented = vec![lines[0].trim_start().to_string()];
unindented.extend_from_slice(
&lines[1..]
.iter()
.map(|&line| {
if line.chars().all(|c| c.is_whitespace()) {
line.to_string()
} else {
assert!(line.len() >= min_indent);
line[min_indent..].to_string()
}
})
.collect::<Vec<_>>(),
);
unindented.join("\n")
} else {
s.to_string()
}
}