// Copyright 2012 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 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. /*! Divides the document tree into pages. Each page corresponds is a logical section. There may be pages for individual modules, pages for the crate, indexes, etc. */ use astsrv; use config; use doc::ItemUtils; use doc; use fold::Fold; use fold; use pass::Pass; use std::comm::*; use std::task; use syntax::ast; #[cfg(test)] use doc::PageUtils; pub fn mk_pass(output_style: config::OutputStyle) -> Pass { Pass { name: ~"page", f: |srv, doc| run(srv, doc, output_style) } } pub fn run( _srv: astsrv::Srv, doc: doc::Doc, output_style: config::OutputStyle ) -> doc::Doc { if output_style == config::DocPerCrate { return doc; } let (result_port, result_chan) = stream(); let (page_port, page_chan) = stream(); let page_chan = SharedChan::new(page_chan); do task::spawn { result_chan.send(make_doc_from_pages(&page_port)); }; find_pages(doc, page_chan); result_port.recv() } type PagePort = Port>; type PageChan = SharedChan>; fn make_doc_from_pages(page_port: &PagePort) -> doc::Doc { let mut pages = ~[]; loop { let val = page_port.recv(); if val.is_some() { pages.push(val.unwrap()); } else { break; } } doc::Doc { pages: pages } } fn find_pages(doc: doc::Doc, page_chan: PageChan) { let fold = Fold { ctxt: page_chan.clone(), fold_crate: fold_crate, fold_mod: fold_mod, fold_nmod: fold_nmod, .. fold::default_any_fold(page_chan.clone()) }; (fold.fold_doc)(&fold, doc.clone()); page_chan.send(None); } fn fold_crate(fold: &fold::Fold, doc: doc::CrateDoc) -> doc::CrateDoc { let doc = fold::default_seq_fold_crate(fold, doc); let page = doc::CratePage(doc::CrateDoc { topmod: strip_mod(doc.topmod.clone()), .. doc.clone() }); fold.ctxt.send(Some(page)); doc } fn fold_mod(fold: &fold::Fold, doc: doc::ModDoc) -> doc::ModDoc { let doc = fold::default_any_fold_mod(fold, doc); if doc.id() != ast::CRATE_NODE_ID { let doc = strip_mod(doc.clone()); let page = doc::ItemPage(doc::ModTag(doc)); fold.ctxt.send(Some(page)); } doc } fn strip_mod(doc: doc::ModDoc) -> doc::ModDoc { doc::ModDoc { items: do doc.items.iter().filter |item| { match **item { doc::ModTag(_) | doc::NmodTag(_) => false, _ => true } }.map(|x| (*x).clone()).collect::<~[doc::ItemTag]>(), .. doc.clone() } } fn fold_nmod(fold: &fold::Fold, doc: doc::NmodDoc) -> doc::NmodDoc { let doc = fold::default_seq_fold_nmod(fold, doc); let page = doc::ItemPage(doc::NmodTag(doc.clone())); fold.ctxt.send(Some(page)); return doc; } #[cfg(test)] mod test { use astsrv; use config; use attr_pass; use doc; use extract; use prune_hidden_pass; use page_pass::run; fn mk_doc_( output_style: config::OutputStyle, source: ~str ) -> doc::Doc { do astsrv::from_str(source.clone()) |srv| { let doc = extract::from_srv(srv.clone(), ~""); let doc = (attr_pass::mk_pass().f)(srv.clone(), doc); let doc = (prune_hidden_pass::mk_pass().f)(srv.clone(), doc); run(srv.clone(), doc, output_style) } } fn mk_doc(source: ~str) -> doc::Doc { mk_doc_(config::DocPerMod, source.clone()) } #[test] fn should_not_split_the_doc_into_pages_for_doc_per_crate() { let doc = mk_doc_( config::DocPerCrate, ~"mod a { } mod b { mod c { } }" ); assert_eq!(doc.pages.len(), 1u); } #[test] fn should_make_a_page_for_every_mod() { let doc = mk_doc(~"mod a { }"); // hidden __std_macros module at the start. assert_eq!(doc.pages.mods()[0].name_(), ~"a"); } #[test] fn should_remove_mods_from_containing_mods() { let doc = mk_doc(~"mod a { }"); assert!(doc.cratemod().mods().is_empty()); } }