(index<- )        ./librustdoc/passes.rs

    git branch:    * master           5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
    modified:    Fri May  9 13:02:28 2014
   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.
   4  //
   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.
  10  
  11  use collections::HashSet;
  12  use rustc::util::nodemap::NodeSet;
  13  use std::cmp;
  14  use std::strbuf::StrBuf;
  15  use std::uint;
  16  use syntax::ast;
  17  
  18  use clean;
  19  use clean::Item;
  20  use plugins;
  21  use fold;
  22  use fold::DocFolder;
  23  
  24  /// Strip items marked `#[doc(hidden)]`
  25  pub fn strip_hidden(krateclean::Crate) -> plugins::PluginResult {
  26      let mut stripped = HashSet::new();
  27  
  28      // strip all #[doc(hidden)] items
  29      let krate = {
  30          struct Stripper<'a> {
  31              stripped: &'a mut HashSet<ast::NodeId>
  32          };
  33          impl<'a> fold::DocFolder for Stripper<'a> {
  34              fn fold_item(&mut self, iItem) -> Option<Item> {
  35                  if i.is_hidden_from_doc() {
  36                      debug!("found one in strip_hidden; removing");
  37                      self.stripped.insert(i.id);
  38  
  39                      // use a dedicated hidden item for given item type if any
  40                      match i.inner {
  41                          clean::StructFieldItem(..) => {
  42                              return Some(clean::Item {
  43                                  inner: clean::StructFieldItem(clean::HiddenStructField),
  44                                  ..i
  45                              });
  46                          }
  47                          _ => {
  48                              return None;
  49                          }
  50                      }
  51                  }
  52  
  53                  self.fold_item_recur(i)
  54              }
  55          }
  56          let mut stripper = Stripper{ stripped: &mut stripped };
  57          stripper.fold_crate(krate)
  58      };
  59  
  60      // strip any traits implemented on stripped items
  61      let krate = {
  62          struct ImplStripper<'a> {
  63              stripped: &'a mut HashSet<ast::NodeId>
  64          };
  65          impl<'a> fold::DocFolder for ImplStripper<'a> {
  66              fn fold_item(&mut self, iItem) -> Option<Item> {
  67                  match i.inner {
  68                      clean::ImplItem(clean::Impl{ for_: clean::ResolvedPath{ id: for_id, .. },
  69                                                   .. }) => {
  70                          if self.stripped.contains(&for_id) {
  71                              return None;
  72                          }
  73                      }
  74                      _ => {}
  75                  }
  76                  self.fold_item_recur(i)
  77              }
  78          }
  79          let mut stripper = ImplStripper{ stripped: &mut stripped };
  80          stripper.fold_crate(krate)
  81      };
  82  
  83      (krate, None)
  84  }
  85  
  86  /// Strip private items from the point of view of a crate or externally from a
  87  /// crate, specified by the `xcrate` flag.
  88  pub fn strip_private(mut krateclean::Crate) -> plugins::PluginResult {
  89      // This stripper collects all *retained* nodes.
  90      let mut retained = HashSet::new();
  91      let analysis = super::analysiskey.get().unwrap();
  92      let exported_items = analysis.exported_items.clone();
  93  
  94      // strip all private items
  95      {
  96          let mut stripper = Stripper {
  97              retained: &mut retained,
  98              exported_items: &exported_items,
  99          };
 100          krate = stripper.fold_crate(krate);
 101      }
 102  
 103      // strip all private implementations of traits
 104      {
 105          let mut stripper = ImplStripper(&retained);
 106          krate = stripper.fold_crate(krate);
 107      }
 108      (krate, None)
 109  }
 110  
 111  struct Stripper<'a> {
 112      retained: &'a mut HashSet<ast::NodeId>,
 113      exported_items: &'a NodeSet,
 114  }
 115  
 116  impl<'a> fold::DocFolder for Stripper<'a> {
 117      fn fold_item(&mut self, iItem) -> Option<Item> {
 118          match i.inner {
 119              // These items can all get re-exported
 120              clean::TypedefItem(..) | clean::StaticItem(..) |
 121              clean::StructItem(..) | clean::EnumItem(..) |
 122              clean::TraitItem(..) | clean::FunctionItem(..) |
 123              clean::VariantItem(..) | clean::MethodItem(..) |
 124              clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) => {
 125                  if !self.exported_items.contains(&i.id) {
 126                      return None;
 127                  }
 128              }
 129  
 130              clean::ViewItemItem(..) => {
 131                  if i.visibility != Some(ast::Public) {
 132                      return None
 133                  }
 134              }
 135  
 136              clean::StructFieldItem(..) => {
 137                  if i.visibility != Some(ast::Public) {
 138                      return Some(clean::Item {
 139                          inner: clean::StructFieldItem(clean::HiddenStructField),
 140                          ..i
 141                      })
 142                  }
 143              }
 144  
 145              // handled below
 146              clean::ModuleItem(..) => {}
 147  
 148              // trait impls for private items should be stripped
 149              clean::ImplItem(clean::Impl{ for_: clean::ResolvedPath{ id: ref for_id, .. }, .. }) => {
 150                  if !self.exported_items.contains(for_id) {
 151                      return None;
 152                  }
 153              }
 154              clean::ImplItem(..) => {}
 155  
 156              // tymethods/macros have no control over privacy
 157              clean::MacroItem(..) | clean::TyMethodItem(..) => {}
 158          }
 159  
 160          let fastreturn = match i.inner {
 161              // nothing left to do for traits (don't want to filter their
 162              // methods out, visibility controlled by the trait)
 163              clean::TraitItem(..) => true,
 164  
 165              // implementations of traits are always public.
 166              clean::ImplItem(ref imp) if imp.trait_.is_some() => true,
 167  
 168              _ => false,
 169          };
 170  
 171          let i = if fastreturn {
 172              self.retained.insert(i.id);
 173              return Some(i);
 174          } else {
 175              self.fold_item_recur(i)
 176          };
 177  
 178          match i {
 179              Some(i) => {
 180                  match i.inner {
 181                      // emptied modules/impls have no need to exist
 182                      clean::ModuleItem(ref m)
 183                          if m.items.len() == 0 &&
 184                             i.doc_value().is_none() => None,
 185                      clean::ImplItem(ref i) if i.methods.len() == 0 => None,
 186                      _ => {
 187                          self.retained.insert(i.id);
 188                          Some(i)
 189                      }
 190                  }
 191              }
 192              None => None,
 193          }
 194      }
 195  }
 196  
 197  // This stripper discards all private impls of traits
 198  struct ImplStripper<'a>(&'a HashSet<ast::NodeId>);
 199  impl<'a> fold::DocFolder for ImplStripper<'a> {
 200      fn fold_item(&mut self, iItem) -> Option<Item> {
 201          match i.inner {
 202              clean::ImplItem(ref imp) => {
 203                  match imp.trait_ {
 204                      Some(clean::ResolvedPath{ id, .. }) => {
 205                          let ImplStripper(s) = *self;
 206                          if !s.contains(&id) {
 207                              return None;
 208                          }
 209                      }
 210                      Some(..) | None => {}
 211                  }
 212              }
 213              _ => {}
 214          }
 215          self.fold_item_recur(i)
 216      }
 217  }
 218  
 219  
 220  pub fn unindent_comments(krateclean::Crate) -> plugins::PluginResult {
 221      struct CommentCleaner;
 222      impl fold::DocFolder for CommentCleaner {
 223          fn fold_item(&mut self, iItem) -> Option<Item> {
 224              let mut i = i;
 225              let mut avecVec<clean::Attribute> = Vec::new();
 226              for attr in i.attrs.iter() {
 227                  match attr {
 228                      &clean::NameValue(ref x, ref s) if "doc" == *x => avec.push(
 229                          clean::NameValue("doc".to_owned(), unindent(*s))),
 230                      x => avec.push(x.clone())
 231                  }
 232              }
 233              i.attrs = avec;
 234              self.fold_item_recur(i)
 235          }
 236      }
 237      let mut cleaner = CommentCleaner;
 238      let krate = cleaner.fold_crate(krate);
 239      (krate, None)
 240  }
 241  
 242  pub fn collapse_docs(krateclean::Crate) -> plugins::PluginResult {
 243      struct Collapser;
 244      impl fold::DocFolder for Collapser {
 245          fn fold_item(&mut self, iItem) -> Option<Item> {
 246              let mut docstr = StrBuf::new();
 247              let mut i = i;
 248              for attr in i.attrs.iter() {
 249                  match *attr {
 250                      clean::NameValue(ref x, ref s) if "doc" == *x => {
 251                          docstr.push_str(s.clone());
 252                          docstr.push_char('\n');
 253                      },
 254                      _ => ()
 255                  }
 256              }
 257              let mut aVec<clean::Attribute> = i.attrs.iter().filter(|&a| match a {
 258                  &clean::NameValue(ref x, _) if "doc" == *x => false,
 259                  _ => true
 260              }).map(|x| x.clone()).collect();
 261              if docstr.len() > 0 {
 262                  a.push(clean::NameValue("doc".to_owned(), docstr.into_owned()));
 263              }
 264              i.attrs = a;
 265              self.fold_item_recur(i)
 266          }
 267      }
 268      let mut collapser = Collapser;
 269      let krate = collapser.fold_crate(krate);
 270      (krate, None)
 271  }
 272  
 273  pub fn unindent(s: &str) -> ~str {
 274      let lines = s.lines_any().collect::<Vec<&str> >();
 275      let mut saw_first_line = false;
 276      let mut saw_second_line = false;
 277      let min_indent = lines.iter().fold(uint::MAX, |min_indent, line| {
 278  
 279          // After we see the first non-whitespace line, look at
 280          // the line we have. If it is not whitespace, and therefore
 281          // part of the first paragraph, then ignore the indentation
 282          // level of the first line
 283          let ignore_previous_indents =
 284              saw_first_line &&
 285              !saw_second_line &&
 286              !line.is_whitespace();
 287  
 288          let min_indent = if ignore_previous_indents {
 289              uint::MAX
 290          } else {
 291              min_indent
 292          };
 293  
 294          if saw_first_line {
 295              saw_second_line = true;
 296          }
 297  
 298          if line.is_whitespace() {
 299              min_indent
 300          } else {
 301              saw_first_line = true;
 302              let mut spaces = 0;
 303              line.chars().all(|char| {
 304                  // Only comparing against space because I wouldn't
 305                  // know what to do with mixed whitespace chars
 306                  if char == ' ' {
 307                      spaces += 1;
 308                      true
 309                  } else {
 310                      false
 311                  }
 312              });
 313              cmp::min(min_indent, spaces)
 314          }
 315      });
 316  
 317      if lines.len() >= 1 {
 318          let mut unindented = vec!( lines.get(0).trim() );
 319          unindented.push_all(lines.tail().iter().map(|&line| {
 320              if line.is_whitespace() {
 321                  line
 322              } else {
 323                  assert!(line.len() >= min_indent);
 324                  line.slice_from(min_indent)
 325              }
 326          }).collect::<Vec<_>>().as_slice());
 327          unindented.connect("\n")
 328      } else {
 329          s.to_owned()
 330      }
 331  }
 332  
 333  #[cfg(test)]
 334  mod unindent_tests {
 335      use super::unindent;
 336  
 337      #[test]
 338      fn should_unindent() {
 339          let s = "    line1\n    line2".to_owned();
 340          let r = unindent(s);
 341          assert_eq!(r, "line1\nline2".to_owned());
 342      }
 343  
 344      #[test]
 345      fn should_unindent_multiple_paragraphs() {
 346          let s = "    line1\n\n    line2".to_owned();
 347          let r = unindent(s);
 348          assert_eq!(r, "line1\n\nline2".to_owned());
 349      }
 350  
 351      #[test]
 352      fn should_leave_multiple_indent_levels() {
 353          // Line 2 is indented another level beyond the
 354          // base indentation and should be preserved
 355          let s = "    line1\n\n        line2".to_owned();
 356          let r = unindent(s);
 357          assert_eq!(r, "line1\n\n    line2".to_owned());
 358      }
 359  
 360      #[test]
 361      fn should_ignore_first_line_indent() {
 362          // Thi first line of the first paragraph may not be indented as
 363          // far due to the way the doc string was written:
 364          //
 365          // #[doc = "Start way over here
 366          //          and continue here"]
 367          let s = "line1\n    line2".to_owned();
 368          let r = unindent(s);
 369          assert_eq!(r, "line1\nline2".to_owned());
 370      }
 371  
 372      #[test]
 373      fn should_not_ignore_first_line_indent_in_a_single_line_para() {
 374          let s = "line1\n\n    line2".to_owned();
 375          let r = unindent(s);
 376          assert_eq!(r, "line1\n\n    line2".to_owned());
 377      }
 378  }


librustdoc/passes.rs:219:1-219:1 -fn- definition:
pub fn unindent_comments(krate: clean::Crate) -> plugins::PluginResult {
    struct CommentCleaner;
    impl fold::DocFolder for CommentCleaner {
references:- 2
librustdoc/test.rs:
81:     let krate = v.clean();
82:     let (krate, _) = passes::unindent_comments(krate);
83:     let (krate, _) = passes::collapse_docs(krate);
librustdoc/lib.rs:
66:      "strips all doc(hidden) items from the output"),
67:     ("unindent-comments", passes::unindent_comments,
68:      "removes excess indentation on comments in order for markdown to like it"),


librustdoc/passes.rs:62:8-62:8 -struct- definition:
        struct ImplStripper<'a> {
            stripped: &'a mut HashSet<ast::NodeId>
        };
references:- 2
64:         };
65:         impl<'a> fold::DocFolder for ImplStripper<'a> {
66:             fn fold_item(&mut self, i: Item) -> Option<Item> {
--
78:         }
79:         let mut stripper = ImplStripper{ stripped: &mut stripped };
80:         stripper.fold_crate(krate)


librustdoc/passes.rs:30:8-30:8 -struct- definition:
        struct Stripper<'a> {
            stripped: &'a mut HashSet<ast::NodeId>
        };
references:- 2
32:         };
33:         impl<'a> fold::DocFolder for Stripper<'a> {
34:             fn fold_item(&mut self, i: Item) -> Option<Item> {
--
55:         }
56:         let mut stripper = Stripper{ stripped: &mut stripped };
57:         stripper.fold_crate(krate)


librustdoc/passes.rs:110:1-110:1 -struct- definition:
struct Stripper<'a> {
    retained: &'a mut HashSet<ast::NodeId>,
    exported_items: &'a NodeSet,
references:- 2
116: impl<'a> fold::DocFolder for Stripper<'a> {
117:     fn fold_item(&mut self, i: Item) -> Option<Item> {


librustdoc/passes.rs:241:1-241:1 -fn- definition:
pub fn collapse_docs(krate: clean::Crate) -> plugins::PluginResult {
    struct Collapser;
    impl fold::DocFolder for Collapser {
references:- 2
librustdoc/lib.rs:
68:      "removes excess indentation on comments in order for markdown to like it"),
69:     ("collapse-docs", passes::collapse_docs,
70:      "concatenates all document attributes into one document attribute"),
librustdoc/test.rs:
82:     let (krate, _) = passes::unindent_comments(krate);
83:     let (krate, _) = passes::collapse_docs(krate);