about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorGuillaume Gomez <guillaume1.gomez@gmail.com>2022-09-16 13:07:19 +0200
committerGitHub <noreply@github.com>2022-09-16 13:07:19 +0200
commitc21dcd79144acc2f0fa6ca5b9d423758db2ae94b (patch)
tree1b47245a29ced7bdda89abf8549ce23164a66b62 /src
parent9a72ded562b2c8b78478550d4df09d1499a3dae1 (diff)
parent669498ca0a880e2272fee62bf5dc50f3bc07e75b (diff)
downloadrust-c21dcd79144acc2f0fa6ca5b9d423758db2ae94b.tar.gz
rust-c21dcd79144acc2f0fa6ca5b9d423758db2ae94b.zip
Rollup merge of #101868 - notriddle:notriddle/short-links-jump-to-definition, r=GuillaumeGomez
rustdoc: use more precise URLs for jump-to-definition links

As an example, this cuts down <https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_middle/ty/mod.rs.html> by about 11%.

    $ du -h new_mod.rs.html old_mod.rs.html
    296K	new_mod.rs.html
    332K	old_mod.rs.html

Like https://github.com/rust-lang/rust/pull/83237, but separate code since source links have a different URL structure.

Related to [Zulip discussion](https://rust-lang.zulipchat.com/#narrow/stream/266220-rustdoc/topic/RFC.20for.20.22jump.20to.20definition.22.20feature/near/299029786) and [the jump-to-definition pre-RFC](https://github.com/GuillaumeGomez/rfcs/pull/1).
Diffstat (limited to 'src')
-rw-r--r--src/librustdoc/html/highlight.rs8
-rw-r--r--src/librustdoc/html/render/context.rs30
-rw-r--r--src/librustdoc/html/sources.rs5
-rw-r--r--src/test/rustdoc/check-source-code-urls-to-def-std.rs8
-rw-r--r--src/test/rustdoc/check-source-code-urls-to-def.rs22
5 files changed, 54 insertions, 19 deletions
diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index 1e6cab8fcd3..8922bf37785 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -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.
@@ -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()
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 01b96dc7215..62def4a94e8 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -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;
@@ -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`
diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs
index f37c54e4298..2e2bee78b95 100644
--- a/src/librustdoc/html/sources.rs
+++ b/src/librustdoc/html/sources.rs
@@ -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,
     );
 }
diff --git a/src/test/rustdoc/check-source-code-urls-to-def-std.rs b/src/test/rustdoc/check-source-code-urls-to-def-std.rs
index 3396b234a77..e12d8445f4f 100644
--- a/src/test/rustdoc/check-source-code-urls-to-def-std.rs
+++ b/src/test/rustdoc/check-source-code-urls-to-def-std.rs
@@ -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;
@@ -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);
 }
diff --git a/src/test/rustdoc/check-source-code-urls-to-def.rs b/src/test/rustdoc/check-source-code-urls-to-def.rs
index ec44e1bc65c..d00a3e35519 100644
--- a/src/test/rustdoc/check-source-code-urls-to-def.rs
+++ b/src/test/rustdoc/check-source-code-urls-to-def.rs
@@ -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;
@@ -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,
@@ -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();
 }