]> git.lizzy.rs Git - rust.git/commitdiff
Separate rustdoc passes into separate modules.
authorCorey Farwell <coreyf@rwell.org>
Sun, 25 Sep 2016 21:38:32 +0000 (17:38 -0400)
committerCorey Farwell <coreyf@rwell.org>
Sun, 25 Sep 2016 21:40:16 +0000 (17:40 -0400)
src/librustdoc/passes.rs [deleted file]
src/librustdoc/passes/collapse_docs.rs [new file with mode: 0644]
src/librustdoc/passes/mod.rs [new file with mode: 0644]
src/librustdoc/passes/strip_hidden.rs [new file with mode: 0644]
src/librustdoc/passes/strip_priv_imports.rs [new file with mode: 0644]
src/librustdoc/passes/strip_private.rs [new file with mode: 0644]
src/librustdoc/passes/unindent_comments.rs [new file with mode: 0644]

diff --git a/src/librustdoc/passes.rs b/src/librustdoc/passes.rs
deleted file mode 100644 (file)
index 92f24ce..0000000
+++ /dev/null
@@ -1,416 +0,0 @@
-// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use rustc::hir::def_id::DefId;
-use rustc::middle::privacy::AccessLevels;
-use rustc::util::nodemap::DefIdSet;
-use std::cmp;
-use std::mem;
-use std::string::String;
-use std::usize;
-
-use clean::{self, Attributes, GetDefId};
-use clean::Item;
-use plugins;
-use fold;
-use fold::DocFolder;
-use fold::FoldItem::Strip;
-
-/// Strip items marked `#[doc(hidden)]`
-pub fn strip_hidden(krate: clean::Crate) -> plugins::PluginResult {
-    let mut retained = DefIdSet();
-
-    // strip all #[doc(hidden)] items
-    let krate = {
-        struct Stripper<'a> {
-            retained: &'a mut DefIdSet,
-            update_retained: bool,
-        }
-        impl<'a> fold::DocFolder for Stripper<'a> {
-            fn fold_item(&mut self, i: Item) -> Option<Item> {
-                if i.attrs.list("doc").has_word("hidden") {
-                    debug!("found one in strip_hidden; removing");
-                    // use a dedicated hidden item for given item type if any
-                    match i.inner {
-                        clean::StructFieldItem(..) | clean::ModuleItem(..) => {
-                            // We need to recurse into stripped modules to
-                            // strip things like impl methods but when doing so
-                            // we must not add any items to the `retained` set.
-                            let old = mem::replace(&mut self.update_retained, false);
-                            let ret = Strip(self.fold_item_recur(i).unwrap()).fold();
-                            self.update_retained = old;
-                            return ret;
-                        }
-                        _ => return None,
-                    }
-                } else {
-                    if self.update_retained {
-                        self.retained.insert(i.def_id);
-                    }
-                }
-                self.fold_item_recur(i)
-            }
-        }
-        let mut stripper = Stripper{ retained: &mut retained, update_retained: true };
-        stripper.fold_crate(krate)
-    };
-
-    // strip all impls referencing stripped items
-    let mut stripper = ImplStripper { retained: &retained };
-    stripper.fold_crate(krate)
-}
-
-/// Strip private items from the point of view of a crate or externally from a
-/// crate, specified by the `xcrate` flag.
-pub fn strip_private(mut krate: clean::Crate) -> plugins::PluginResult {
-    // This stripper collects all *retained* nodes.
-    let mut retained = DefIdSet();
-    let access_levels = krate.access_levels.clone();
-
-    // strip all private items
-    {
-        let mut stripper = Stripper {
-            retained: &mut retained,
-            access_levels: &access_levels,
-            update_retained: true,
-        };
-        krate = ImportStripper.fold_crate(stripper.fold_crate(krate));
-    }
-
-    // strip all impls referencing private items
-    let mut stripper = ImplStripper { retained: &retained };
-    stripper.fold_crate(krate)
-}
-
-struct Stripper<'a> {
-    retained: &'a mut DefIdSet,
-    access_levels: &'a AccessLevels<DefId>,
-    update_retained: bool,
-}
-
-impl<'a> fold::DocFolder for Stripper<'a> {
-    fn fold_item(&mut self, i: Item) -> Option<Item> {
-        match i.inner {
-            clean::StrippedItem(..) => {
-                // We need to recurse into stripped modules to strip things
-                // like impl methods but when doing so we must not add any
-                // items to the `retained` set.
-                let old = mem::replace(&mut self.update_retained, false);
-                let ret = self.fold_item_recur(i);
-                self.update_retained = old;
-                return ret;
-            }
-            // These items can all get re-exported
-            clean::TypedefItem(..) | clean::StaticItem(..) |
-            clean::StructItem(..) | clean::EnumItem(..) |
-            clean::TraitItem(..) | clean::FunctionItem(..) |
-            clean::VariantItem(..) | clean::MethodItem(..) |
-            clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) |
-            clean::ConstantItem(..) | clean::UnionItem(..) => {
-                if i.def_id.is_local() {
-                    if !self.access_levels.is_exported(i.def_id) {
-                        return None;
-                    }
-                }
-            }
-
-            clean::StructFieldItem(..) => {
-                if i.visibility != Some(clean::Public) {
-                    return Strip(i).fold();
-                }
-            }
-
-            clean::ModuleItem(..) => {
-                if i.def_id.is_local() && i.visibility != Some(clean::Public) {
-                    let old = mem::replace(&mut self.update_retained, false);
-                    let ret = Strip(self.fold_item_recur(i).unwrap()).fold();
-                    self.update_retained = old;
-                    return ret;
-                }
-            }
-
-            // handled in the `strip-priv-imports` pass
-            clean::ExternCrateItem(..) | clean::ImportItem(..) => {}
-
-            clean::DefaultImplItem(..) | clean::ImplItem(..) => {}
-
-            // tymethods/macros have no control over privacy
-            clean::MacroItem(..) | clean::TyMethodItem(..) => {}
-
-            // Primitives are never stripped
-            clean::PrimitiveItem(..) => {}
-
-            // Associated consts and types are never stripped
-            clean::AssociatedConstItem(..) |
-            clean::AssociatedTypeItem(..) => {}
-        }
-
-        let fastreturn = match i.inner {
-            // nothing left to do for traits (don't want to filter their
-            // methods out, visibility controlled by the trait)
-            clean::TraitItem(..) => true,
-
-            // implementations of traits are always public.
-            clean::ImplItem(ref imp) if imp.trait_.is_some() => true,
-            // Struct variant fields have inherited visibility
-            clean::VariantItem(clean::Variant {
-                kind: clean::StructVariant(..)
-            }) => true,
-            _ => false,
-        };
-
-        let i = if fastreturn {
-            if self.update_retained {
-                self.retained.insert(i.def_id);
-            }
-            return Some(i);
-        } else {
-            self.fold_item_recur(i)
-        };
-
-        i.and_then(|i| {
-            match i.inner {
-                // emptied modules have no need to exist
-                clean::ModuleItem(ref m)
-                    if m.items.is_empty() &&
-                       i.doc_value().is_none() => None,
-                _ => {
-                    if self.update_retained {
-                        self.retained.insert(i.def_id);
-                    }
-                    Some(i)
-                }
-            }
-        })
-    }
-}
-
-// This stripper discards all impls which reference stripped items
-struct ImplStripper<'a> {
-    retained: &'a DefIdSet
-}
-
-impl<'a> fold::DocFolder for ImplStripper<'a> {
-    fn fold_item(&mut self, i: Item) -> Option<Item> {
-        if let clean::ImplItem(ref imp) = i.inner {
-            // emptied none trait impls can be stripped
-            if imp.trait_.is_none() && imp.items.is_empty() {
-                return None;
-            }
-            if let Some(did) = imp.for_.def_id() {
-                if did.is_local() && !imp.for_.is_generic() &&
-                    !self.retained.contains(&did)
-                {
-                    return None;
-                }
-            }
-            if let Some(did) = imp.trait_.def_id() {
-                if did.is_local() && !self.retained.contains(&did) {
-                    return None;
-                }
-            }
-        }
-        self.fold_item_recur(i)
-    }
-}
-
-// This stripper discards all private import statements (`use`, `extern crate`)
-struct ImportStripper;
-impl fold::DocFolder for ImportStripper {
-    fn fold_item(&mut self, i: Item) -> Option<Item> {
-        match i.inner {
-            clean::ExternCrateItem(..) |
-            clean::ImportItem(..) if i.visibility != Some(clean::Public) => None,
-            _ => self.fold_item_recur(i)
-        }
-    }
-}
-
-pub fn strip_priv_imports(krate: clean::Crate)  -> plugins::PluginResult {
-    ImportStripper.fold_crate(krate)
-}
-
-pub fn unindent_comments(krate: clean::Crate) -> plugins::PluginResult {
-    struct CommentCleaner;
-    impl fold::DocFolder for CommentCleaner {
-        fn fold_item(&mut self, mut i: Item) -> Option<Item> {
-            let mut avec: Vec<clean::Attribute> = Vec::new();
-            for attr in &i.attrs {
-                match attr {
-                    &clean::NameValue(ref x, ref s)
-                            if "doc" == *x => {
-                        avec.push(clean::NameValue("doc".to_string(),
-                                                   unindent(s)))
-                    }
-                    x => avec.push(x.clone())
-                }
-            }
-            i.attrs = avec;
-            self.fold_item_recur(i)
-        }
-    }
-    let mut cleaner = CommentCleaner;
-    let krate = cleaner.fold_crate(krate);
-    krate
-}
-
-pub fn collapse_docs(krate: clean::Crate) -> plugins::PluginResult {
-    struct Collapser;
-    impl fold::DocFolder for Collapser {
-        fn fold_item(&mut self, mut i: Item) -> Option<Item> {
-            let mut docstr = String::new();
-            for attr in &i.attrs {
-                if let clean::NameValue(ref x, ref s) = *attr {
-                    if "doc" == *x {
-                        docstr.push_str(s);
-                        docstr.push('\n');
-                    }
-                }
-            }
-            let mut a: Vec<clean::Attribute> = i.attrs.iter().filter(|&a| match a {
-                &clean::NameValue(ref x, _) if "doc" == *x => false,
-                _ => true
-            }).cloned().collect();
-            if !docstr.is_empty() {
-                a.push(clean::NameValue("doc".to_string(), docstr));
-            }
-            i.attrs = a;
-            self.fold_item_recur(i)
-        }
-    }
-    let mut collapser = Collapser;
-    let krate = collapser.fold_crate(krate);
-    krate
-}
-
-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().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()
-    }
-}
-
-#[cfg(test)]
-mod unindent_tests {
-    use super::unindent;
-
-    #[test]
-    fn should_unindent() {
-        let s = "    line1\n    line2".to_string();
-        let r = unindent(&s);
-        assert_eq!(r, "line1\nline2");
-    }
-
-    #[test]
-    fn should_unindent_multiple_paragraphs() {
-        let s = "    line1\n\n    line2".to_string();
-        let r = unindent(&s);
-        assert_eq!(r, "line1\n\nline2");
-    }
-
-    #[test]
-    fn should_leave_multiple_indent_levels() {
-        // Line 2 is indented another level beyond the
-        // base indentation and should be preserved
-        let s = "    line1\n\n        line2".to_string();
-        let r = unindent(&s);
-        assert_eq!(r, "line1\n\n    line2");
-    }
-
-    #[test]
-    fn should_ignore_first_line_indent() {
-        // The first line of the first paragraph may not be indented as
-        // far due to the way the doc string was written:
-        //
-        // #[doc = "Start way over here
-        //          and continue here"]
-        let s = "line1\n    line2".to_string();
-        let r = unindent(&s);
-        assert_eq!(r, "line1\nline2");
-    }
-
-    #[test]
-    fn should_not_ignore_first_line_indent_in_a_single_line_para() {
-        let s = "line1\n\n    line2".to_string();
-        let r = unindent(&s);
-        assert_eq!(r, "line1\n\n    line2");
-    }
-
-    #[test]
-    fn should_unindent_tabs() {
-        let s = "\tline1\n\tline2".to_string();
-        let r = unindent(&s);
-        assert_eq!(r, "line1\nline2");
-    }
-
-    #[test]
-    fn should_trim_mixed_indentation() {
-        let s = "\t    line1\n\t    line2".to_string();
-        let r = unindent(&s);
-        assert_eq!(r, "line1\nline2");
-
-        let s = "    \tline1\n    \tline2".to_string();
-        let r = unindent(&s);
-        assert_eq!(r, "line1\nline2");
-    }
-}
diff --git a/src/librustdoc/passes/collapse_docs.rs b/src/librustdoc/passes/collapse_docs.rs
new file mode 100644 (file)
index 0000000..26be203
--- /dev/null
@@ -0,0 +1,45 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::string::String;
+
+use clean::{self, Item};
+use plugins;
+use fold;
+use fold::DocFolder;
+
+pub fn collapse_docs(krate: clean::Crate) -> plugins::PluginResult {
+    struct Collapser;
+    impl fold::DocFolder for Collapser {
+        fn fold_item(&mut self, mut i: Item) -> Option<Item> {
+            let mut docstr = String::new();
+            for attr in &i.attrs {
+                if let clean::NameValue(ref x, ref s) = *attr {
+                    if "doc" == *x {
+                        docstr.push_str(s);
+                        docstr.push('\n');
+                    }
+                }
+            }
+            let mut a: Vec<clean::Attribute> = i.attrs.iter().filter(|&a| match a {
+                &clean::NameValue(ref x, _) if "doc" == *x => false,
+                _ => true
+            }).cloned().collect();
+            if !docstr.is_empty() {
+                a.push(clean::NameValue("doc".to_string(), docstr));
+            }
+            i.attrs = a;
+            self.fold_item_recur(i)
+        }
+    }
+    let mut collapser = Collapser;
+    let krate = collapser.fold_crate(krate);
+    krate
+}
diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs
new file mode 100644 (file)
index 0000000..62a6cad
--- /dev/null
@@ -0,0 +1,177 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use rustc::hir::def_id::DefId;
+use rustc::middle::privacy::AccessLevels;
+use rustc::util::nodemap::DefIdSet;
+use std::mem;
+
+use clean::{self, GetDefId, Item};
+use fold;
+use fold::FoldItem::Strip;
+
+mod collapse_docs;
+pub use self::collapse_docs::collapse_docs;
+
+mod strip_hidden;
+pub use self::strip_hidden::strip_hidden;
+
+mod strip_private;
+pub use self::strip_private::strip_private;
+
+mod strip_priv_imports;
+pub use self::strip_priv_imports::strip_priv_imports;
+
+mod unindent_comments;
+pub use self::unindent_comments::unindent_comments;
+
+struct Stripper<'a> {
+    retained: &'a mut DefIdSet,
+    access_levels: &'a AccessLevels<DefId>,
+    update_retained: bool,
+}
+
+impl<'a> fold::DocFolder for Stripper<'a> {
+    fn fold_item(&mut self, i: Item) -> Option<Item> {
+        match i.inner {
+            clean::StrippedItem(..) => {
+                // We need to recurse into stripped modules to strip things
+                // like impl methods but when doing so we must not add any
+                // items to the `retained` set.
+                let old = mem::replace(&mut self.update_retained, false);
+                let ret = self.fold_item_recur(i);
+                self.update_retained = old;
+                return ret;
+            }
+            // These items can all get re-exported
+            clean::TypedefItem(..) | clean::StaticItem(..) |
+            clean::StructItem(..) | clean::EnumItem(..) |
+            clean::TraitItem(..) | clean::FunctionItem(..) |
+            clean::VariantItem(..) | clean::MethodItem(..) |
+            clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) |
+            clean::ConstantItem(..) | clean::UnionItem(..) => {
+                if i.def_id.is_local() {
+                    if !self.access_levels.is_exported(i.def_id) {
+                        return None;
+                    }
+                }
+            }
+
+            clean::StructFieldItem(..) => {
+                if i.visibility != Some(clean::Public) {
+                    return Strip(i).fold();
+                }
+            }
+
+            clean::ModuleItem(..) => {
+                if i.def_id.is_local() && i.visibility != Some(clean::Public) {
+                    let old = mem::replace(&mut self.update_retained, false);
+                    let ret = Strip(self.fold_item_recur(i).unwrap()).fold();
+                    self.update_retained = old;
+                    return ret;
+                }
+            }
+
+            // handled in the `strip-priv-imports` pass
+            clean::ExternCrateItem(..) | clean::ImportItem(..) => {}
+
+            clean::DefaultImplItem(..) | clean::ImplItem(..) => {}
+
+            // tymethods/macros have no control over privacy
+            clean::MacroItem(..) | clean::TyMethodItem(..) => {}
+
+            // Primitives are never stripped
+            clean::PrimitiveItem(..) => {}
+
+            // Associated consts and types are never stripped
+            clean::AssociatedConstItem(..) |
+            clean::AssociatedTypeItem(..) => {}
+        }
+
+        let fastreturn = match i.inner {
+            // nothing left to do for traits (don't want to filter their
+            // methods out, visibility controlled by the trait)
+            clean::TraitItem(..) => true,
+
+            // implementations of traits are always public.
+            clean::ImplItem(ref imp) if imp.trait_.is_some() => true,
+            // Struct variant fields have inherited visibility
+            clean::VariantItem(clean::Variant {
+                kind: clean::StructVariant(..)
+            }) => true,
+            _ => false,
+        };
+
+        let i = if fastreturn {
+            if self.update_retained {
+                self.retained.insert(i.def_id);
+            }
+            return Some(i);
+        } else {
+            self.fold_item_recur(i)
+        };
+
+        i.and_then(|i| {
+            match i.inner {
+                // emptied modules have no need to exist
+                clean::ModuleItem(ref m)
+                    if m.items.is_empty() &&
+                       i.doc_value().is_none() => None,
+                _ => {
+                    if self.update_retained {
+                        self.retained.insert(i.def_id);
+                    }
+                    Some(i)
+                }
+            }
+        })
+    }
+}
+
+// This stripper discards all impls which reference stripped items
+struct ImplStripper<'a> {
+    retained: &'a DefIdSet
+}
+
+impl<'a> fold::DocFolder for ImplStripper<'a> {
+    fn fold_item(&mut self, i: Item) -> Option<Item> {
+        if let clean::ImplItem(ref imp) = i.inner {
+            // emptied none trait impls can be stripped
+            if imp.trait_.is_none() && imp.items.is_empty() {
+                return None;
+            }
+            if let Some(did) = imp.for_.def_id() {
+                if did.is_local() && !imp.for_.is_generic() &&
+                    !self.retained.contains(&did)
+                {
+                    return None;
+                }
+            }
+            if let Some(did) = imp.trait_.def_id() {
+                if did.is_local() && !self.retained.contains(&did) {
+                    return None;
+                }
+            }
+        }
+        self.fold_item_recur(i)
+    }
+}
+
+// This stripper discards all private import statements (`use`, `extern crate`)
+struct ImportStripper;
+impl fold::DocFolder for ImportStripper {
+    fn fold_item(&mut self, i: Item) -> Option<Item> {
+        match i.inner {
+            clean::ExternCrateItem(..) |
+            clean::ImportItem(..) if i.visibility != Some(clean::Public) => None,
+            _ => self.fold_item_recur(i)
+        }
+    }
+}
diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs
new file mode 100644 (file)
index 0000000..5a505ba
--- /dev/null
@@ -0,0 +1,64 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use rustc::util::nodemap::DefIdSet;
+use std::mem;
+
+use clean::{self, Attributes};
+use clean::Item;
+use plugins;
+use fold;
+use fold::DocFolder;
+use fold::FoldItem::Strip;
+use passes::ImplStripper;
+
+/// Strip items marked `#[doc(hidden)]`
+pub fn strip_hidden(krate: clean::Crate) -> plugins::PluginResult {
+    let mut retained = DefIdSet();
+
+    // strip all #[doc(hidden)] items
+    let krate = {
+        struct Stripper<'a> {
+            retained: &'a mut DefIdSet,
+            update_retained: bool,
+        }
+        impl<'a> fold::DocFolder for Stripper<'a> {
+            fn fold_item(&mut self, i: Item) -> Option<Item> {
+                if i.attrs.list("doc").has_word("hidden") {
+                    debug!("found one in strip_hidden; removing");
+                    // use a dedicated hidden item for given item type if any
+                    match i.inner {
+                        clean::StructFieldItem(..) | clean::ModuleItem(..) => {
+                            // We need to recurse into stripped modules to
+                            // strip things like impl methods but when doing so
+                            // we must not add any items to the `retained` set.
+                            let old = mem::replace(&mut self.update_retained, false);
+                            let ret = Strip(self.fold_item_recur(i).unwrap()).fold();
+                            self.update_retained = old;
+                            return ret;
+                        }
+                        _ => return None,
+                    }
+                } else {
+                    if self.update_retained {
+                        self.retained.insert(i.def_id);
+                    }
+                }
+                self.fold_item_recur(i)
+            }
+        }
+        let mut stripper = Stripper{ retained: &mut retained, update_retained: true };
+        stripper.fold_crate(krate)
+    };
+
+    // strip all impls referencing stripped items
+    let mut stripper = ImplStripper { retained: &retained };
+    stripper.fold_crate(krate)
+}
diff --git a/src/librustdoc/passes/strip_priv_imports.rs b/src/librustdoc/passes/strip_priv_imports.rs
new file mode 100644 (file)
index 0000000..91f8be4
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use clean;
+use fold::DocFolder;
+use plugins;
+use passes::ImportStripper;
+
+pub fn strip_priv_imports(krate: clean::Crate)  -> plugins::PluginResult {
+    ImportStripper.fold_crate(krate)
+}
diff --git a/src/librustdoc/passes/strip_private.rs b/src/librustdoc/passes/strip_private.rs
new file mode 100644 (file)
index 0000000..acd7357
--- /dev/null
@@ -0,0 +1,38 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use rustc::util::nodemap::DefIdSet;
+
+use clean;
+use plugins;
+use fold::DocFolder;
+use passes::{ImplStripper, ImportStripper, Stripper};
+
+/// Strip private items from the point of view of a crate or externally from a
+/// crate, specified by the `xcrate` flag.
+pub fn strip_private(mut krate: clean::Crate) -> plugins::PluginResult {
+    // This stripper collects all *retained* nodes.
+    let mut retained = DefIdSet();
+    let access_levels = krate.access_levels.clone();
+
+    // strip all private items
+    {
+        let mut stripper = Stripper {
+            retained: &mut retained,
+            access_levels: &access_levels,
+            update_retained: true,
+        };
+        krate = ImportStripper.fold_crate(stripper.fold_crate(krate));
+    }
+
+    // strip all impls referencing private items
+    let mut stripper = ImplStripper { retained: &retained };
+    stripper.fold_crate(krate)
+}
diff --git a/src/librustdoc/passes/unindent_comments.rs b/src/librustdoc/passes/unindent_comments.rs
new file mode 100644 (file)
index 0000000..343a5cf
--- /dev/null
@@ -0,0 +1,166 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::cmp;
+use std::string::String;
+use std::usize;
+
+use clean::{self, Item};
+use plugins;
+use fold::{self, DocFolder};
+
+pub fn unindent_comments(krate: clean::Crate) -> plugins::PluginResult {
+    struct CommentCleaner;
+    impl fold::DocFolder for CommentCleaner {
+        fn fold_item(&mut self, mut i: Item) -> Option<Item> {
+            let mut avec: Vec<clean::Attribute> = Vec::new();
+            for attr in &i.attrs {
+                match attr {
+                    &clean::NameValue(ref x, ref s)
+                            if "doc" == *x => {
+                        avec.push(clean::NameValue("doc".to_string(),
+                                                   unindent(s)))
+                    }
+                    x => avec.push(x.clone())
+                }
+            }
+            i.attrs = avec;
+            self.fold_item_recur(i)
+        }
+    }
+    let mut cleaner = CommentCleaner;
+    let krate = cleaner.fold_crate(krate);
+    krate
+}
+
+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().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()
+    }
+}
+
+#[cfg(test)]
+mod unindent_tests {
+    use super::unindent;
+
+    #[test]
+    fn should_unindent() {
+        let s = "    line1\n    line2".to_string();
+        let r = unindent(&s);
+        assert_eq!(r, "line1\nline2");
+    }
+
+    #[test]
+    fn should_unindent_multiple_paragraphs() {
+        let s = "    line1\n\n    line2".to_string();
+        let r = unindent(&s);
+        assert_eq!(r, "line1\n\nline2");
+    }
+
+    #[test]
+    fn should_leave_multiple_indent_levels() {
+        // Line 2 is indented another level beyond the
+        // base indentation and should be preserved
+        let s = "    line1\n\n        line2".to_string();
+        let r = unindent(&s);
+        assert_eq!(r, "line1\n\n    line2");
+    }
+
+    #[test]
+    fn should_ignore_first_line_indent() {
+        // The first line of the first paragraph may not be indented as
+        // far due to the way the doc string was written:
+        //
+        // #[doc = "Start way over here
+        //          and continue here"]
+        let s = "line1\n    line2".to_string();
+        let r = unindent(&s);
+        assert_eq!(r, "line1\nline2");
+    }
+
+    #[test]
+    fn should_not_ignore_first_line_indent_in_a_single_line_para() {
+        let s = "line1\n\n    line2".to_string();
+        let r = unindent(&s);
+        assert_eq!(r, "line1\n\n    line2");
+    }
+
+    #[test]
+    fn should_unindent_tabs() {
+        let s = "\tline1\n\tline2".to_string();
+        let r = unindent(&s);
+        assert_eq!(r, "line1\nline2");
+    }
+
+    #[test]
+    fn should_trim_mixed_indentation() {
+        let s = "\t    line1\n\t    line2".to_string();
+        let r = unindent(&s);
+        assert_eq!(r, "line1\nline2");
+
+        let s = "    \tline1\n    \tline2".to_string();
+        let r = unindent(&s);
+        assert_eq!(r, "line1\nline2");
+    }
+}