Skip to content
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

rustdoc: use more precise URLs for jump-to-definition links #101868

Merged
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
8 changes: 5 additions & 3 deletions src/librustdoc/html/highlight.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ pub(crate) struct HrefContext<'a, 'b, 'c> {
/// This field is used to know "how far" from the top of the directory we are to link to either
/// documentation pages or other source pages.
pub(crate) root_path: &'c str,
/// This field is used to calculate precise local URLs.
pub(crate) current_href: &'c str,
}

/// Decorations are represented as a map from CSS class to vector of character ranges.
Expand Down Expand Up @@ -977,9 +979,9 @@ fn string_without_closing_tag<T: Display>(
// a link to their definition can be generated using this:
// https://github.com/rust-lang/rust/blob/60f1a2fc4b535ead9c85ce085fdce49b1b097531/src/librustdoc/html/render/context.rs#L315-L338
match href {
LinkFromSrc::Local(span) => context
.href_from_span(*span, true)
.map(|s| format!("{}{}", href_context.root_path, s)),
LinkFromSrc::Local(span) => {
context.href_from_span_relative(*span, href_context.current_href)
}
LinkFromSrc::External(def_id) => {
format::href_with_root_path(*def_id, context, Some(href_context.root_path))
.ok()
Expand Down
30 changes: 30 additions & 0 deletions src/librustdoc/html/render/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use crate::formats::FormatRenderer;
use crate::html::escape::Escape;
use crate::html::format::{join_with_double_colon, Buffer};
use crate::html::markdown::{self, plain_text_summary, ErrorCodes, IdMap};
use crate::html::url_parts_builder::UrlPartsBuilder;
use crate::html::{layout, sources};
use crate::scrape_examples::AllCallLocations;
use crate::try_err;
Expand Down Expand Up @@ -370,6 +371,35 @@ impl<'tcx> Context<'tcx> {
anchor = anchor
))
}

pub(crate) fn href_from_span_relative(
&self,
span: clean::Span,
relative_to: &str,
) -> Option<String> {
self.href_from_span(span, false).map(|s| {
let mut url = UrlPartsBuilder::new();
let mut dest_href_parts = s.split('/');
let mut cur_href_parts = relative_to.split('/');
for (cur_href_part, dest_href_part) in (&mut cur_href_parts).zip(&mut dest_href_parts) {
if cur_href_part != dest_href_part {
url.push(dest_href_part);
break;
}
}
for dest_href_part in dest_href_parts {
url.push(dest_href_part);
}
let loline = span.lo(self.sess()).line;
let hiline = span.hi(self.sess()).line;
format!(
"{}{}#{}",
"../".repeat(cur_href_parts.count()),
url.finish(),
if loline == hiline { loline.to_string() } else { format!("{loline}-{hiline}") }
)
})
}
}

/// Generates the documentation for `crate` into the directory `dst`
Expand Down
5 changes: 4 additions & 1 deletion src/librustdoc/html/sources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,11 +288,14 @@ pub(crate) fn print_src(
}
}
line_numbers.write_str("</pre>");
let current_href = &context
.href_from_span(clean::Span::new(file_span), false)
.expect("only local crates should have sources emitted");
highlight::render_source_with_highlighting(
s,
buf,
line_numbers,
highlight::HrefContext { context, file_span, root_path },
highlight::HrefContext { context, file_span, root_path, current_href },
decoration_info,
);
}
8 changes: 4 additions & 4 deletions src/test/rustdoc/check-source-code-urls-to-def-std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ fn babar() {}
// @has - '//a[@href="{{channel}}/std/primitive.u32.html"]' 'u32'
// @has - '//a[@href="{{channel}}/std/primitive.str.html"]' 'str'
// @has - '//a[@href="{{channel}}/std/primitive.bool.html"]' 'bool'
// @has - '//a[@href="../../src/foo/check-source-code-urls-to-def-std.rs.html#7"]' 'babar'
// @has - '//a[@href="#7"]' 'babar'
pub fn foo(a: u32, b: &str, c: String) {
let x = 12;
let y: bool = true;
Expand All @@ -31,12 +31,12 @@ macro_rules! data {
pub fn another_foo() {
// This is known limitation: if the macro doesn't generate anything, the visitor
// can't find any item or anything that could tell us that it comes from expansion.
// @!has - '//a[@href="../../src/foo/check-source-code-urls-to-def-std.rs.html#19"]' 'yolo!'
// @!has - '//a[@href="#19"]' 'yolo!'
yolo!();
// @has - '//a[@href="{{channel}}/std/macro.eprintln.html"]' 'eprintln!'
eprintln!();
// @has - '//a[@href="../../src/foo/check-source-code-urls-to-def-std.rs.html#27-29"]' 'data!'
// @has - '//a[@href="#27-29"]' 'data!'
let x = data!(4);
// @has - '//a[@href="../../src/foo/check-source-code-urls-to-def-std.rs.html#23-25"]' 'bar!'
// @has - '//a[@href="#23-25"]' 'bar!'
bar!(x);
}
22 changes: 11 additions & 11 deletions src/test/rustdoc/check-source-code-urls-to-def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ extern crate source_code;

// @has 'src/foo/check-source-code-urls-to-def.rs.html'

// @has - '//a[@href="../../src/foo/auxiliary/source-code-bar.rs.html#1-17"]' 'bar'
// @has - '//a[@href="auxiliary/source-code-bar.rs.html#1-17"]' 'bar'
#[path = "auxiliary/source-code-bar.rs"]
pub mod bar;

// @count - '//a[@href="../../src/foo/auxiliary/source-code-bar.rs.html#5"]' 4
// @count - '//a[@href="auxiliary/source-code-bar.rs.html#5"]' 4
use bar::Bar;
// @has - '//a[@href="../../src/foo/auxiliary/source-code-bar.rs.html#13"]' 'self'
// @has - '//a[@href="../../src/foo/auxiliary/source-code-bar.rs.html#14"]' 'Trait'
// @has - '//a[@href="auxiliary/source-code-bar.rs.html#13"]' 'self'
// @has - '//a[@href="auxiliary/source-code-bar.rs.html#14"]' 'Trait'
use bar::sub::{self, Trait};

pub struct Foo;
Expand All @@ -31,26 +31,26 @@ fn babar() {}
// @has - '//a/@href' '/struct.String.html'
// @has - '//a/@href' '/primitive.u32.html'
// @has - '//a/@href' '/primitive.str.html'
// @count - '//a[@href="../../src/foo/check-source-code-urls-to-def.rs.html#23"]' 5
// @count - '//a[@href="#23"]' 5
// @has - '//a[@href="../../source_code/struct.SourceCode.html"]' 'source_code::SourceCode'
pub fn foo(a: u32, b: &str, c: String, d: Foo, e: bar::Bar, f: source_code::SourceCode) {
let x = 12;
let y: Foo = Foo;
let z: Bar = bar::Bar { field: Foo };
babar();
// @has - '//a[@href="../../src/foo/check-source-code-urls-to-def.rs.html#26"]' 'hello'
// @has - '//a[@href="#26"]' 'hello'
y.hello();
}

// @has - '//a[@href="../../src/foo/auxiliary/source-code-bar.rs.html#14"]' 'bar::sub::Trait'
// @has - '//a[@href="../../src/foo/auxiliary/source-code-bar.rs.html#14"]' 'Trait'
// @has - '//a[@href="auxiliary/source-code-bar.rs.html#14"]' 'bar::sub::Trait'
// @has - '//a[@href="auxiliary/source-code-bar.rs.html#14"]' 'Trait'
pub fn foo2<T: bar::sub::Trait, V: Trait>(t: &T, v: &V, b: bool) {}

pub trait AnotherTrait {}
pub trait WhyNot {}

// @has - '//a[@href="../../src/foo/check-source-code-urls-to-def.rs.html#49"]' 'AnotherTrait'
// @has - '//a[@href="../../src/foo/check-source-code-urls-to-def.rs.html#50"]' 'WhyNot'
// @has - '//a[@href="#49"]' 'AnotherTrait'
// @has - '//a[@href="#50"]' 'WhyNot'
pub fn foo3<T, V>(t: &T, v: &V)
where
T: AnotherTrait,
Expand All @@ -59,7 +59,7 @@ where

pub trait AnotherTrait2 {}

// @has - '//a[@href="../../src/foo/check-source-code-urls-to-def.rs.html#60"]' 'AnotherTrait2'
// @has - '//a[@href="#60"]' 'AnotherTrait2'
pub fn foo4() {
let x: Vec<AnotherTrait2> = Vec::new();
}
Expand Down