diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index a48526d39fd0a..d7cbac8fc2b05 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -453,7 +453,7 @@ override `ignore`. ### `--runtool`, `--runtool-arg`: program to run tests with; args to pass to it -Using thses options looks like this: +Using these options looks like this: ```bash $ rustdoc src/lib.rs -Z unstable-options --runtool runner --runtool-arg --do-thing --runtool-arg --do-other-thing @@ -467,3 +467,20 @@ $ rustdoc src/lib.rs -Z unstable-options --runtool valgrind ``` Another use case would be to run a test inside an emulator, or through a Virtual Machine. + +### `--source-code-external-url`: using external source code + +In case you don't want to generate the source code files and instead use existing ones (available +through an HTTP URL!), you can use this option: + +```bash +$ rustdoc src/lib.rs -Z unstable-options --source-code-external-url 'https://somewhere.com' +``` + +Note that generated source URLs will look like this: + +```text +https://somewhere.com/[crate name] +``` + +It is equivalent to the local `src/[crate name]` folder. diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 33b3e800374e3..eb44ef60b0041 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -220,6 +220,8 @@ pub struct RenderOptions { pub generate_search_filter: bool, /// Option (disabled by default) to generate files used by RLS and some other tools. pub generate_redirect_pages: bool, + /// Base URL to use for source code linking. + pub source_code_external_url: Option, } impl Options { @@ -496,6 +498,12 @@ impl Options { let enable_per_target_ignores = matches.opt_present("enable-per-target-ignores"); let document_private = matches.opt_present("document-private-items"); let document_hidden = matches.opt_present("document-hidden-items"); + let source_code_external_url = matches.opt_str("source-code-external-url").map(|mut h| { + if !h.ends_with('/') { + h.push('/'); + } + h.replace("\\", "/") + }); let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format); @@ -552,6 +560,7 @@ impl Options { markdown_playground_url, generate_search_filter, generate_redirect_pages, + source_code_external_url, }, }) } diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 4dd2a6562a4cd..f7eb545c6beb7 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -200,6 +200,7 @@ crate struct SharedContext { /// The default edition used to parse doctests. pub edition: Edition, pub codes: ErrorCodes, + pub source_code_external_url: Option, playground: Option, } @@ -409,6 +410,7 @@ pub fn run( static_root_path, generate_search_filter, generate_redirect_pages, + source_code_external_url, .. } = options; @@ -467,6 +469,7 @@ pub fn run( collapsed: krate.collapsed, src_root, include_sources, + source_code_external_url, local_sources: Default::default(), issue_tracker_base_url, layout, @@ -1550,6 +1553,21 @@ impl Context { } } +fn compute_path(path: String) -> String { + if path.split('/').find(|x| *x == "..").is_none() { + return path; + } + let mut new_path = Vec::new(); + for part in path.split('/') { + if part != ".." { + new_path.push(part); + } else if !new_path.is_empty() { + new_path.pop(); + } + } + new_path.join("/") +} + impl Context { /// Generates a url appropriate for an `href` attribute back to the source of /// this item. @@ -1602,13 +1620,26 @@ impl Context { } else { format!("{}-{}", item.source.loline, item.source.hiline) }; - Some(format!( - "{root}src/{krate}/{path}#{lines}", - root = Escape(&root), - krate = krate, - path = path, - lines = lines - )) + if let Some(ref source_code_external_url) = self.shared.source_code_external_url { + Some(format!( + "{path}#{lines}", + path = compute_path(format!( + "{root}/{path}", + root = source_code_external_url, + krate = krate, + path = path, + ),), + lines = lines + )) + } else { + Some(format!( + "{root}src/{krate}/{path}#{lines}", + root = Escape(&root), + krate = krate, + path = path, + lines = lines + )) + } } } diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index 79e7d3d783b80..6f13c4a3842e2 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -74,17 +74,6 @@ impl<'a> SourceCollector<'a> { return Ok(()); } - let contents = match fs::read_to_string(&p) { - Ok(contents) => contents, - Err(e) => { - return Err(Error::new(e, &p)); - } - }; - - // Remove the utf-8 BOM if any - let contents = - if contents.starts_with("\u{feff}") { &contents[3..] } else { &contents[..] }; - // Create the intermediate directories let mut cur = self.dst.clone(); let mut root_path = String::from("../../"); @@ -101,30 +90,44 @@ impl<'a> SourceCollector<'a> { cur.push(&fname); href.push_str(&fname.to_string_lossy()); - let title = format!( - "{} -- source", - cur.file_name().expect("failed to get file name").to_string_lossy() - ); - let desc = format!("Source to the Rust file `{}`.", filename); - let page = layout::Page { - title: &title, - css_class: "source", - root_path: &root_path, - static_root_path: self.scx.static_root_path.as_deref(), - description: &desc, - keywords: BASIC_KEYWORDS, - resource_suffix: &self.scx.resource_suffix, - extra_scripts: &[&format!("source-files{}", self.scx.resource_suffix)], - static_extra_scripts: &[&format!("source-script{}", self.scx.resource_suffix)], - }; - let v = layout::render( - &self.scx.layout, - &page, - "", - |buf: &mut _| print_src(buf, &contents), - &self.scx.themes, - ); - self.scx.fs.write(&cur, v.as_bytes())?; + // we don't emit source if we have an external location for it + if self.scx.source_code_external_url.is_none() { + let contents = match fs::read_to_string(&p) { + Ok(contents) => contents, + Err(e) => { + return Err(Error::new(e, &p)); + } + }; + + // Remove the utf-8 BOM if any + let contents = + if contents.starts_with("\u{feff}") { &contents[3..] } else { &contents[..] }; + + let title = format!( + "{} -- source", + cur.file_name().expect("failed to get file name").to_string_lossy() + ); + let desc = format!("Source to the Rust file `{}`.", filename); + let page = layout::Page { + title: &title, + css_class: "source", + root_path: &root_path, + static_root_path: self.scx.static_root_path.as_deref(), + description: &desc, + keywords: BASIC_KEYWORDS, + resource_suffix: &self.scx.resource_suffix, + extra_scripts: &[&format!("source-files{}", self.scx.resource_suffix)], + static_extra_scripts: &[&format!("source-script{}", self.scx.resource_suffix)], + }; + let v = layout::render( + &self.scx.layout, + &page, + "", + |buf: &mut _| print_src(buf, &contents), + &self.scx.themes, + ); + self.scx.fs.write(&cur, v.as_bytes())?; + } self.scx.local_sources.insert(p.clone(), href); Ok(()) } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 4ea14ab9077c6..2dac90475325a 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -418,6 +418,14 @@ fn opts() -> Vec { "specified the rustc-like binary to use as the test builder", ) }), + unstable("source-code-external-url", |o| { + o.optopt( + "", + "source-code-external-url", + "Base URL for external source code linking", + "URL", + ) + }), ] } diff --git a/src/test/rustdoc/source_code_external_url.rs b/src/test/rustdoc/source_code_external_url.rs new file mode 100644 index 0000000000000..2d606defb2c2d --- /dev/null +++ b/src/test/rustdoc/source_code_external_url.rs @@ -0,0 +1,8 @@ +// ignore-tidy-linelength +// compile-flags: -Z unstable-options --source-code-external-url https://a.a + +#![crate_name = "foo"] + +// @has foo/struct.Foo.html +// @has - '//h1[@class="fqn"]//a[@href="https://a.a/foo/source_code_external_url.rs.html#8"]' '[src]' +pub struct Foo; diff --git a/src/test/rustdoc/source_code_external_url2.rs b/src/test/rustdoc/source_code_external_url2.rs new file mode 100644 index 0000000000000..7bbee0ee76f38 --- /dev/null +++ b/src/test/rustdoc/source_code_external_url2.rs @@ -0,0 +1,8 @@ +// ignore-tidy-linelength +// compile-flags: -Z unstable-options --source-code-external-url https://a.a/ + +#![crate_name = "foo"] + +// @has foo/struct.Foo.html +// @has - '//h1[@class="fqn"]//a[@href="https://a.a/foo/source_code_external_url2.rs.html#8"]' '[src]' +pub struct Foo;