about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Howell <michael@notriddle.com>2022-09-15 13:03:04 -0700
committerMichael Howell <michael@notriddle.com>2022-09-15 13:03:04 -0700
commitef24747703b6a30abb242d43f09c584edfed1b24 (patch)
tree615c9dd0d169e48b25b205f5eb7b16d0c12a2fdf
parent35a0407814a6b5a04f0929105631e9c69e293e9d (diff)
downloadrust-ef24747703b6a30abb242d43f09c584edfed1b24.tar.gz
rust-ef24747703b6a30abb242d43f09c584edfed1b24.zip
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
-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
3 files changed, 39 insertions, 4 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,
     );
 }