Skip to content

Fix [src] hyperlinks in rustdoc #23213

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 19, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
[rustdoc] Fix source hyperlinks in docs
* rustdoc was doubly appending the file name to the path of where to
  generate the source files, meanwhile, the [src] hyperlinks were not
* Added a flag to rustdoc::html::render::clean_srcpath to ignore the
  last path component, i.e. the file name itself to prevent the issue
* This also avoids creating directories with the same name as source
  files, and it makes sure the link to `main.css` is correct as well.
* Added regression tests to ensure the rustdoc heirarchy of rendered
  source files remains consistent

Fixes #23192
  • Loading branch information
ipetkov committed Mar 18, 2015
commit af6cf85b9815644c77c77cdd141d476efc426d99
17 changes: 12 additions & 5 deletions src/librustdoc/html/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -692,16 +692,23 @@ fn shortty(item: &clean::Item) -> ItemType {

/// Takes a path to a source file and cleans the path to it. This canonicalizes
/// things like ".." to components which preserve the "top down" hierarchy of a
/// static HTML tree.
/// static HTML tree. Each component in the cleaned path will be passed as an
/// argument to `f`. The very last component of the path (ie the file name) will
/// be passed to `f` if `keep_filename` is true, and ignored otherwise.
// FIXME (#9639): The closure should deal with &[u8] instead of &str
// FIXME (#9639): This is too conservative, rejecting non-UTF-8 paths
fn clean_srcpath<F>(src_root: &Path, p: &Path, mut f: F) where
fn clean_srcpath<F>(src_root: &Path, p: &Path, keep_filename: bool, mut f: F) where
F: FnMut(&str),
{
// make it relative, if possible
let p = p.relative_from(src_root).unwrap_or(p);

for c in p.iter().map(|x| x.to_str().unwrap()) {
let mut iter = p.iter().map(|x| x.to_str().unwrap()).peekable();
while let Some(c) = iter.next() {
if !keep_filename && iter.peek().is_none() {
break;
}

if ".." == c {
f("up");
} else {
Expand Down Expand Up @@ -803,7 +810,7 @@ impl<'a> SourceCollector<'a> {
// Create the intermediate directories
let mut cur = self.dst.clone();
let mut root_path = String::from_str("../../");
clean_srcpath(&self.cx.src_root, &p, |component| {
clean_srcpath(&self.cx.src_root, &p, false, |component| {
cur.push(component);
mkdir(&cur).unwrap();
root_path.push_str("../");
Expand Down Expand Up @@ -1368,7 +1375,7 @@ impl<'a> Item<'a> {
if ast_util::is_local(self.item.def_id) {
let mut path = Vec::new();
clean_srcpath(&cx.src_root, Path::new(&self.item.source.filename),
|component| {
true, |component| {
path.push(component.to_string());
});
let href = if self.item.source.loline == self.item.source.hiline {
Expand Down
5 changes: 5 additions & 0 deletions src/test/run-make/rustdoc-src-links/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-include ../tools.mk
all:
$(HOST_RPATH_ENV) $(RUSTDOC) -w html -o $(TMPDIR)/doc foo.rs
$(HTMLDOCCK) $(TMPDIR)/doc foo.rs
$(HTMLDOCCK) $(TMPDIR)/doc qux/mod.rs
43 changes: 43 additions & 0 deletions src/test/run-make/rustdoc-src-links/foo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright 2015 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.

#![crate_name = "foo"]

//! Dox
// @has src/foo/foo.rs.html
// @has foo/index.html '//a/@href' '../src/foo/foo.rs.html'

pub mod qux;

// @has foo/bar/index.html '//a/@href' '../../src/foo/foo.rs.html'
pub mod bar {

/// Dox
// @has foo/bar/baz/index.html '//a/@href' '../../../src/foo/foo.rs.html'
pub mod baz {
/// Dox
// @has foo/bar/baz/fn.baz.html '//a/@href' '../../../src/foo/foo.rs.html'
pub fn baz() { }
}

/// Dox
// @has foo/bar/trait.Foobar.html '//a/@href' '../../src/foo/foo.rs.html'
pub trait Foobar { fn dummy(&self) { } }

// @has foo/bar/struct.Foo.html '//a/@href' '../../src/foo/foo.rs.html'
pub struct Foo { x: i32, y: u32 }

// @has foo/bar/fn.prawns.html '//a/@href' '../../src/foo/foo.rs.html'
pub fn prawns((a, b): (i32, u32), Foo { x, y }: Foo) { }
}

/// Dox
// @has foo/fn.modfn.html '//a/@href' '../src/foo/foo.rs.html'
pub fn modfn() { }
39 changes: 39 additions & 0 deletions src/test/run-make/rustdoc-src-links/qux/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright 2015 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.

//! Dox
// @has src/foo/qux/mod.rs.html
// @has foo/qux/index.html '//a/@href' '../../src/foo/qux/mod.rs.html'

// @has foo/qux/bar/index.html '//a/@href' '../../../src/foo/qux/mod.rs.html'
pub mod bar {

/// Dox
// @has foo/qux/bar/baz/index.html '//a/@href' '../../../../src/foo/qux/mod.rs.html'
pub mod baz {
/// Dox
// @has foo/qux/bar/baz/fn.baz.html '//a/@href' '../../../../src/foo/qux/mod.rs.html'
pub fn baz() { }
}

/// Dox
// @has foo/qux/bar/trait.Foobar.html '//a/@href' '../../../src/foo/qux/mod.rs.html'
pub trait Foobar { fn dummy(&self) { } }

// @has foo/qux/bar/struct.Foo.html '//a/@href' '../../../src/foo/qux/mod.rs.html'
pub struct Foo { x: i32, y: u32 }

// @has foo/qux/bar/fn.prawns.html '//a/@href' '../../../src/foo/qux/mod.rs.html'
pub fn prawns((a, b): (i32, u32), Foo { x, y }: Foo) { }
}

/// Dox
// @has foo/qux/fn.modfn.html '//a/@href' '../../src/foo/qux/mod.rs.html'
pub fn modfn() { }