diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 8270c8f3a20..a18804be275 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -53,6 +53,7 @@ use std::rc::Rc; use std::u32; use std::str::Str as StrTrait; // Conflicts with Str variant use std::char::Char as CharTrait; // Conflicts with Char variant +use std::path::Path as FsPath; // Conflicts with Path struct use core::DocContext; use doctree; @@ -115,6 +116,7 @@ impl, U> Clean> for syntax::owned_slice::OwnedSlice { #[deriving(Clone, Encodable, Decodable)] pub struct Crate { pub name: String, + pub src: FsPath, pub module: Option, pub externs: Vec<(ast::CrateNum, ExternalCrate)>, pub primitives: Vec, @@ -194,6 +196,7 @@ impl<'a, 'tcx> Clean for visit_ast::RustdocVisitor<'a, 'tcx> { Crate { name: name.to_string(), + src: cx.src.clone(), module: Some(module), externs: externs, primitives: primitives, diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 3fbb2a8749f..492517bad93 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -83,6 +83,9 @@ pub struct Context { /// String representation of how to get back to the root path of the 'doc/' /// folder in terms of a relative URL. pub root_path: String, + /// The path to the crate root source minus the file name. + /// Used for simplifying paths to the highlighted source code files. + pub src_root: Path, /// The current destination folder of where HTML artifacts should be placed. /// This changes as the context descends into the module hierarchy. pub dst: Path, @@ -249,6 +252,7 @@ pub fn run(mut krate: clean::Crate, passes: HashSet) -> io::IoResult<()> { let mut cx = Context { dst: dst, + src_root: krate.src.dir_path(), passes: passes, current: Vec::new(), root_path: String::new(), @@ -642,8 +646,13 @@ fn mkdir(path: &Path) -> io::IoResult<()> { /// things like ".." to components which preserve the "top down" hierarchy of a /// static HTML tree. // FIXME (#9639): The closure should deal with &[u8] instead of &str -fn clean_srcpath(src: &[u8], f: |&str|) { +// FIXME (#9639): This is too conservative, rejecting non-UTF-8 paths +fn clean_srcpath(src_root: &Path, src: &[u8], f: |&str|) { let p = Path::new(src); + + // make it relative, if possible + let p = p.path_relative_from(src_root).unwrap_or(p); + if p.as_vec() != b"." { for c in p.str_components().map(|x|x.unwrap()) { if ".." == c { @@ -749,7 +758,7 @@ impl<'a> SourceCollector<'a> { // Create the intermediate directories let mut cur = self.dst.clone(); let mut root_path = String::from_str("../../"); - clean_srcpath(p.dirname(), |component| { + clean_srcpath(&self.cx.src_root, p.dirname(), |component| { cur.push(component); mkdir(&cur).unwrap(); root_path.push_str("../"); @@ -1299,13 +1308,13 @@ impl<'a> Item<'a> { /// If `None` is returned, then a source link couldn't be generated. This /// may happen, for example, with externally inlined items where the source /// of their crate documentation isn't known. - fn href(&self) -> Option { + fn href(&self, cx: &Context) -> Option { // If this item is part of the local crate, then we're guaranteed to // know the span, so we plow forward and generate a proper url. The url // has anchors for the line numbers that we're linking to. if ast_util::is_local(self.item.def_id) { let mut path = Vec::new(); - clean_srcpath(self.item.source.filename.as_bytes(), |component| { + clean_srcpath(&cx.src_root, self.item.source.filename.as_bytes(), |component| { path.push(component.to_string()); }); let href = if self.item.source.loline == self.item.source.hiline { @@ -1412,7 +1421,7 @@ impl<'a> fmt::Show for Item<'a> { // this page, and this link will be auto-clicked. The `id` attribute is // used to find the link to auto-click. if self.cx.include_sources && !is_primitive { - match self.href() { + match self.href(self.cx) { Some(l) => { try!(write!(fmt, "[src]", self.item.def_id.node, l));