1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
12 use std::string::String;
15 use clean::{self, DocFragment, Item};
16 use fold::{self, DocFolder};
19 pub const UNINDENT_COMMENTS: Pass =
20 Pass::late("unindent-comments", unindent_comments,
21 "removes excess indentation on comments in order for markdown to like it");
23 pub fn unindent_comments(krate: clean::Crate) -> clean::Crate {
24 CommentCleaner.fold_crate(krate)
27 struct CommentCleaner;
29 impl fold::DocFolder for CommentCleaner {
30 fn fold_item(&mut self, mut i: Item) -> Option<Item> {
31 i.attrs.unindent_doc_comments();
32 self.fold_item_recur(i)
36 impl clean::Attributes {
37 pub fn unindent_doc_comments(&mut self) {
38 unindent_fragments(&mut self.doc_strings);
42 fn unindent_fragments(docs: &mut Vec<DocFragment>) {
43 for fragment in docs {
45 DocFragment::SugaredDoc(_, _, ref mut doc_string) |
46 DocFragment::RawDoc(_, _, ref mut doc_string) |
47 DocFragment::Include(_, _, _, ref mut doc_string) =>
48 *doc_string = unindent(doc_string),
53 fn unindent(s: &str) -> String {
54 let lines = s.lines().collect::<Vec<&str> >();
55 let mut saw_first_line = false;
56 let mut saw_second_line = false;
57 let min_indent = lines.iter().fold(usize::MAX, |min_indent, line| {
59 // After we see the first non-whitespace line, look at
60 // the line we have. If it is not whitespace, and therefore
61 // part of the first paragraph, then ignore the indentation
62 // level of the first line
63 let ignore_previous_indents =
66 !line.chars().all(|c| c.is_whitespace());
68 let min_indent = if ignore_previous_indents {
75 saw_second_line = true;
78 if line.chars().all(|c| c.is_whitespace()) {
81 saw_first_line = true;
82 let mut whitespace = 0;
83 line.chars().all(|char| {
84 // Compare against either space or tab, ignoring whether they
86 if char == ' ' || char == '\t' {
93 cmp::min(min_indent, whitespace)
97 if !lines.is_empty() {
98 let mut unindented = vec![ lines[0].trim_start().to_string() ];
99 unindented.extend_from_slice(&lines[1..].iter().map(|&line| {
100 if line.chars().all(|c| c.is_whitespace()) {
103 assert!(line.len() >= min_indent);
104 line[min_indent..].to_string()
106 }).collect::<Vec<_>>());
107 unindented.join("\n")
118 fn should_unindent() {
119 let s = " line1\n line2".to_string();
120 let r = unindent(&s);
121 assert_eq!(r, "line1\nline2");
125 fn should_unindent_multiple_paragraphs() {
126 let s = " line1\n\n line2".to_string();
127 let r = unindent(&s);
128 assert_eq!(r, "line1\n\nline2");
132 fn should_leave_multiple_indent_levels() {
133 // Line 2 is indented another level beyond the
134 // base indentation and should be preserved
135 let s = " line1\n\n line2".to_string();
136 let r = unindent(&s);
137 assert_eq!(r, "line1\n\n line2");
141 fn should_ignore_first_line_indent() {
142 // The first line of the first paragraph may not be indented as
143 // far due to the way the doc string was written:
145 // #[doc = "Start way over here
146 // and continue here"]
147 let s = "line1\n line2".to_string();
148 let r = unindent(&s);
149 assert_eq!(r, "line1\nline2");
153 fn should_not_ignore_first_line_indent_in_a_single_line_para() {
154 let s = "line1\n\n line2".to_string();
155 let r = unindent(&s);
156 assert_eq!(r, "line1\n\n line2");
160 fn should_unindent_tabs() {
161 let s = "\tline1\n\tline2".to_string();
162 let r = unindent(&s);
163 assert_eq!(r, "line1\nline2");
167 fn should_trim_mixed_indentation() {
168 let s = "\t line1\n\t line2".to_string();
169 let r = unindent(&s);
170 assert_eq!(r, "line1\nline2");
172 let s = " \tline1\n \tline2".to_string();
173 let r = unindent(&s);
174 assert_eq!(r, "line1\nline2");
178 fn should_not_trim() {
179 let s = "\t line1 \n\t line2".to_string();
180 let r = unindent(&s);
181 assert_eq!(r, "line1 \nline2");
183 let s = " \tline1 \n \tline2".to_string();
184 let r = unindent(&s);
185 assert_eq!(r, "line1 \nline2");