Auto merge of #115948 - notriddle:notriddle/logo-lockup, r=fmease

rustdoc: show crate name beside smaller logo

*Blocked on https://github.com/rust-lang/cargo/pull/12800*

## Summary

In this PR, the crate name and version are always shown in the sidebar, even in subpages, and the lateral navigation is always shown in the sidebar, even in modules.

Clicking the crate name does the same thing clicking the logo always did: take you to the crate root (the crate's home page, at least within Rustdoc).

The Rust logo is also no longer shown by default for non-Rust docs.

### Screenshots

<details><summary>Before</summary>

| | Macro | Module |
|--|-------|--------|
| In crate | ![image](https://github.com/rust-lang/rust/assets/1593513/d5db0a46-2bb6-44a2-a3aa-2d915ecb8595) |![image](https://github.com/rust-lang/rust/assets/1593513/61f8c1ee-c298-4e2c-b791-18ecb79ab83b)
| In module[^1] | ![image](https://github.com/rust-lang/rust/assets/1593513/73abca59-0b69-4650-a1e2-7278ca34795c) | ![image](https://github.com/rust-lang/rust/assets/1593513/0baf02c2-2ec7-4674-80e5-a6a74a973376)

[^1]: This PR also includes a bug fix for derive macros not showing up in the lateral navigation part of the sidebar

</details>

#### Whole sidebar screenshots

| | Macro | Module |
|--|-------|--------|
| In crate | ![image](https://github.com/rust-lang/rust/assets/1593513/75d1bd07-41f7-4f11-ba24-fd5476e0586a) | ![image](https://github.com/rust-lang/rust/assets/1593513/52960259-2b65-4131-b380-01826f0a0eb7)
| In module | ![image](https://github.com/rust-lang/rust/assets/1593513/06e57928-8cb0-41bd-b152-be16cc53e5ec) | ![image](https://github.com/rust-lang/rust/assets/1593513/37291c69-2a07-4467-a382-d9b029084a47)

#### Different logo configurations

|         | Short crate name | Long crate name |
|---------|------------------|-----------------|
| Root    | ![short-root]    | ![long-root]
| Subpage | ![short-subpage] | ![long-subpage]

[short-root]: https://github.com/rust-lang/rust/assets/1593513/9e2b4fa8-f581-4106-b562-1e0372c13f79
[short-subpage]: https://github.com/rust-lang/rust/assets/1593513/8331cdb8-fa13-4671-a1e2-dcc1cdca7451
[long-root]: https://github.com/rust-lang/rust/assets/1593513/7d377fec-0f1d-4343-9f82-0e35a8f58056
[long-subpage]: https://github.com/rust-lang/rust/assets/1593513/3b3094a4-63c9-477c-8c15-b6075837df30

##### Without a logo

![image](https://github.com/rust-lang/rust/assets/1593513/66672b79-6c59-4be8-a527-25ef6f0b04ab)

### Preview pages

https://notriddle.com/rustdoc-html-demo-5/sidebar-layout-rocket/rocket/index.html

https://notriddle.com/rustdoc-html-demo-5/sidebar-layout-rocket/rocket_sync_db_pools/index.html

https://notriddle.com/rustdoc-html-demo-5/sidebar-layout-rust-compiler/index.html

https://notriddle.com/rustdoc-html-demo-5/sidebar-layout-rust/std/index.html

https://notriddle.com/rustdoc-html-demo-5/sidebar-layout-rocket/tokio/index.html

## Motivation

This improves visual information density (the construct with the logo and crate name is *shorter* than the logo on its own, because it's not square) and navigation clarity (we can now see what clicking the Rust logo does, specifically).

Compare this with the layout at [Phoenix's Hexdocs] (which is what this proposal is closely based on), the old proposal on [Internals Discourse] (which always says "Rust standard library" in the sidebar, but doesn't do the side-by-side layout).

[Phoenix's Hexdocs]: https://hexdocs.pm/phoenix/1.7.7/overview.html
[Internals Discourse]: https://internals.rust-lang.org/t/poc-of-a-new-design-for-the-generated-rustdoc/11018

## Guide-level explanation

This PR cleans up some of the sidebar navigation.

It makes the logo in the desktop sidebar a bit smaller, and puts the crate name and version next to it (either beside it, or below it, depending on if there's space), making it clearer what clicking on it does: click the crate name to open the crate's home page. It also removes the Rust logo from non-official-Rust crates, again to make the navigation and supply chain clearer (since the crate name has been added, the logo is no longer necessary for navigation).

It adds a bit more clarifying information for lateral navigation. On items that don't add their own sidebar items, it just shows its siblings directly below the crate name and logo, but for other items, it shows "In crate alloc" instead of just "In alloc". It also shows the lateral navigation tools on module pages, making modules consistent with every other item.

## Drawbacks

While this actually takes up less screen real estate than the old layout on desktop, it takes up more HTML. It's also a bit more visually complex.

## Rationale and alternatives

I could do what the Internals POC did and keep the vertically stacked layout all the time, instead of doing a horizontal stack where possible. It would take up more screen real estate, though.

## Prior art

This design is lifted almost verbatim from Hexdocs. It seems to work for them. [`opentelemetry_process_propagator`], for example, has a long application name.

[`opentelemetry_process_propagator`]: https://hexdocs.pm/opentelemetry_process_propagator/OpentelemetryProcessPropagator.html

## Unresolved questions

Maybe we should encourage crate authors to include their own logo more often? It certainly helps give people a better sense of "place." This seems to be blocked on coming up with an API to do it without requiring them to host the file somewhere.

## Future possibilities

Beyond this, plenty of other changes could be made to improve the layout, like

* Fix things so that clicking an item in the sidebar doesn't cause it to scroll back to the top.
  * The [Internals demo](https://utherii.github.io/new.html) does this right: clicking an item in the sidebar changes the content area, but the sidebar itself does not change. This is nice, because clicking is cheap and I can skim the opening few paragraphs while browsing.
  * The layout of the docs sidebar causes trouble to implement this, because it's different on different pages, but at least fix this on the file browser.
* Come up with a less cluttered way to do disclosure. There's a lot of `[-]` on the page.
  * We don't lack ideas to fix this one. We have *too many*.
* Do a better job of separating local navigation (vec::Vec links to vec::IntoIter) and the table of contents (vec::Vec links to vec::Vec::new).
  * A possibility: add a Back arrow next to the "In [module]" header?
    ![image](https://github.com/rust-lang/rust/assets/1593513/e969faf7-7722-457a-b8c6-8d962e9e1e23)
* Give readers more control of how much rustdoc shows them, and giving doc authors more control of how much it generates. Basically, https://github.com/rust-lang/rust/pull/115660 is great, let's do it too.

But those are mostly orthogonal, not future possibilities unlocked by this change.
This commit is contained in:
bors 2023-10-11 06:28:36 +00:00
commit 6d05c430d2
64 changed files with 433 additions and 99 deletions

View File

@ -11,6 +11,8 @@
html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
test(no_crate_inject, attr(deny(warnings)))
)]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![feature(core_intrinsics)]
#![feature(dropck_eyepatch)]
#![feature(new_uninit)]

View File

@ -8,6 +8,9 @@
html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
test(attr(deny(warnings)))
)]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![feature(associated_type_bounds)]
#![feature(box_patterns)]
#![feature(const_trait_impl)]

View File

@ -1,4 +1,7 @@
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![feature(associated_type_bounds)]
#![feature(box_patterns)]
#![feature(if_let_guard)]

View File

@ -5,6 +5,9 @@
//! This API is completely unstable and subject to change.
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![feature(lazy_cell)]
#![feature(decl_macro)]
#![feature(panic_update_hook)]

View File

@ -3,6 +3,8 @@
//! This module contains the code for creating and emitting diagnostics.
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![feature(array_windows)]
#![feature(extract_if)]
#![feature(if_let_guard)]

View File

@ -1,4 +1,7 @@
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![feature(proc_macro_diagnostic)]
#![feature(proc_macro_span)]
#![deny(rustc::untranslatable_diagnostic)]

View File

@ -273,6 +273,9 @@
html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
test(attr(allow(unused_variables), deny(warnings)))
)]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]

View File

@ -57,6 +57,9 @@ This API is completely unstable and subject to change.
#![allow(rustc::potential_query_instability)]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![feature(box_patterns)]
#![feature(control_flow_enum)]
#![feature(if_let_guard)]

View File

@ -2,6 +2,9 @@
#![deny(missing_docs)]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![feature(never_type)]
#![recursion_limit = "256"]
#![deny(rustc::untranslatable_diagnostic)]

View File

@ -13,6 +13,9 @@
//! This API is completely unstable and subject to change.
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![feature(associated_type_bounds)]
#![feature(box_patterns)]
#![feature(control_flow_enum)]

View File

@ -27,6 +27,8 @@
#![allow(rustc::potential_query_instability)]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![feature(array_windows)]
#![feature(box_patterns)]
#![feature(control_flow_enum)]

View File

@ -1,6 +1,9 @@
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![cfg_attr(not(bootstrap), allow(internal_features))]
// NOTE: This crate only exists to allow linking on mingw targets.

View File

@ -1,4 +1,7 @@
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![feature(decl_macro)]
#![feature(extract_if)]
#![feature(generators)]

View File

@ -23,6 +23,8 @@
//! This API is completely unstable and subject to change.
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![feature(allocator_api)]
#![feature(array_windows)]
#![feature(assert_matches)]

View File

@ -9,6 +9,9 @@
html_playground_url = "https://play.rust-lang.org/",
test(attr(deny(warnings)))
)]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
// We want to be able to build this crate with a stable compiler, so no

View File

@ -1108,6 +1108,7 @@ impl CheckAttrVisitor<'_> {
| sym::html_root_url
| sym::html_no_source
| sym::test
| sym::rust_logo
if !self.check_attr_crate_level(attr, meta, hir_id) =>
{
is_valid = false;
@ -1166,6 +1167,18 @@ impl CheckAttrVisitor<'_> {
| sym::plugins
| sym::fake_variadic => {}
sym::rust_logo => {
if !self.tcx.features().rustdoc_internals {
feature_err(
&self.tcx.sess.parse_sess,
sym::rustdoc_internals,
meta.span(),
"the `#[doc(rust_logo)]` attribute is used for Rust branding",
)
.emit();
}
}
sym::test => {
if !self.check_test_attr(meta, hir_id) {
is_valid = false;

View File

@ -6,6 +6,9 @@
#![allow(rustc::potential_query_instability)]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![feature(iter_intersperse)]
#![feature(let_chains)]
#![feature(map_try_insert)]

View File

@ -7,6 +7,9 @@
//! of the Unstable Book for some examples.
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![recursion_limit = "256"]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]

View File

@ -1,4 +1,7 @@
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![feature(associated_type_defaults)]
#![feature(rustc_private)]
#![feature(try_blocks)]

View File

@ -1,6 +1,8 @@
//! Support for serializing the dep-graph and reloading it.
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
// this shouldn't be necessary, but the check for `&mut _` is too naive and denies returning a function pointer that takes a mut ref
#![feature(const_mut_refs)]
#![feature(const_refs_to_cell)]

View File

@ -7,6 +7,8 @@
//! Type-relative name resolution (methods, fields, associated items) happens in `rustc_hir_analysis`.
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![feature(assert_matches)]
#![feature(box_patterns)]
#![feature(extract_if)]

View File

@ -5,6 +5,9 @@
html_playground_url = "https://play.rust-lang.org/",
test(attr(allow(unused_variables), deny(warnings)))
)]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![feature(allocator_api)]
#![feature(associated_type_bounds)]
#![feature(const_option)]

View File

@ -10,6 +10,9 @@
html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
test(attr(allow(unused_variables), deny(warnings)))
)]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![cfg_attr(not(bootstrap), allow(internal_features))]
pub mod rustc_internal;

View File

@ -14,6 +14,8 @@
//! This API is completely unstable and subject to change.
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![feature(array_windows)]
#![feature(if_let_guard)]
#![feature(negative_impls)]

View File

@ -1327,6 +1327,7 @@ symbols! {
rust_cold_cc,
rust_eh_catch_typeinfo,
rust_eh_personality,
rust_logo,
rustc,
rustc_abi,
rustc_allocator,

View File

@ -88,6 +88,9 @@
//! DefPaths which are much more robust in the face of changes to the code base.
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![feature(never_type)]
#![recursion_limit = "256"]
#![allow(rustc::potential_query_instability)]

View File

@ -8,6 +8,8 @@
//! LLVM.
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![feature(assert_matches)]
#![feature(associated_type_bounds)]
#![feature(exhaustive_patterns)]

View File

@ -11,6 +11,9 @@
//! This API is completely unstable and subject to change.
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![feature(associated_type_bounds)]
#![feature(box_patterns)]
#![feature(control_flow_enum)]

View File

@ -5,6 +5,9 @@
//! This API is completely unstable and subject to change.
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![feature(assert_matches)]
#![feature(iterator_try_collect)]
#![feature(let_chains)]

View File

@ -78,6 +78,8 @@
not(no_sync),
target_has_atomic = "ptr"
))]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![no_std]
#![needs_allocator]
// Lints:

View File

@ -68,6 +68,7 @@
test(no_crate_inject, attr(deny(warnings))),
test(attr(allow(dead_code, deprecated, unused_variables, unused_mut)))
)]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![doc(cfg_hide(
not(test),
any(not(feature = "miri-test-libstd"), test, doctest),

View File

@ -17,6 +17,8 @@
test(no_crate_inject, attr(deny(warnings))),
test(attr(allow(dead_code, deprecated, unused_variables, unused_mut)))
)]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
// This library is copied into rust-analyzer to allow loading rustc compiled proc macros.
// Please avoid unstable features where possible to minimize the amount of changes necessary
// to make it compile with rust-analyzer on stable.

View File

@ -227,6 +227,7 @@
test(no_crate_inject, attr(deny(warnings))),
test(attr(allow(dead_code, deprecated, unused_variables, unused_mut)))
)]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![doc(cfg_hide(
not(test),
not(any(test, bootstrap)),

View File

@ -16,6 +16,8 @@
#![unstable(feature = "test", issue = "50297")]
#![doc(test(attr(deny(warnings))))]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![feature(internal_output_capture)]
#![feature(staged_api)]
#![feature(process_exitcode_internals)]

View File

@ -207,6 +207,21 @@ To do so, the `#[doc(keyword = "...")]` attribute is used. Example:
mod empty_mod {}
```
### Use the Rust logo as the crate logo
This is for official Rust project use only.
Internal Rustdoc pages like settings.html and scrape-examples-help.html show the Rust logo.
This logo is tracked as a static resource. The attribute `#![doc(rust_logo)]` makes this same
built-in resource act as the main logo.
```rust
#![feature(rustdoc_internals)]
#![allow(internal_features)]
#![doc(rust_logo)]
//! This crate has the Rust(tm) branding on it.
```
## Effects of other nightly features
These nightly-only features are not primarily related to Rustdoc,

View File

@ -17,6 +17,7 @@ pub(crate) struct Layout {
pub(crate) external_html: ExternalHtml,
pub(crate) default_settings: FxHashMap<String, String>,
pub(crate) krate: String,
pub(crate) krate_version: String,
/// The given user css file which allow to customize the generated
/// documentation theme.
pub(crate) css_file_extension: Option<PathBuf>,
@ -31,6 +32,7 @@ pub(crate) struct Page<'a> {
pub(crate) static_root_path: Option<&'a str>,
pub(crate) description: &'a str,
pub(crate) resource_suffix: &'a str,
pub(crate) rust_logo: bool,
}
impl<'a> Page<'a> {
@ -54,9 +56,19 @@ struct PageLayout<'a> {
themes: Vec<String>,
sidebar: String,
content: String,
krate_with_trailing_slash: String,
rust_channel: &'static str,
pub(crate) rustdoc_version: &'a str,
// same as layout.krate, except on top-level pages like
// Settings, Help, All Crates, and About Scraped Examples,
// where these things instead give Rustdoc name and version.
//
// These are separate from the variables used for the search
// engine, because "Rustdoc" isn't necessarily a crate in
// the current workspace.
display_krate: &'a str,
display_krate_with_trailing_slash: String,
display_krate_version_number: &'a str,
display_krate_version_extra: &'a str,
}
pub(crate) fn render<T: Print, S: Print>(
@ -66,12 +78,26 @@ pub(crate) fn render<T: Print, S: Print>(
t: T,
style_files: &[StylePath],
) -> String {
let rustdoc_version = rustc_interface::util::version_str!().unwrap_or("unknown version");
let (display_krate, display_krate_version, display_krate_with_trailing_slash) =
if page.root_path == "./" {
// top level pages use Rust branding
("Rustdoc", rustdoc_version, String::new())
} else {
let display_krate_with_trailing_slash =
ensure_trailing_slash(&layout.krate).to_string();
(&layout.krate[..], &layout.krate_version[..], display_krate_with_trailing_slash)
};
let static_root_path = page.get_static_root_path();
let krate_with_trailing_slash = ensure_trailing_slash(&layout.krate).to_string();
// bootstrap passes in parts of the version separated by tabs, but other stuff might use spaces
let (display_krate_version_number, display_krate_version_extra) =
display_krate_version.split_once([' ', '\t']).unwrap_or((display_krate_version, ""));
let mut themes: Vec<String> = style_files.iter().map(|s| s.basename().unwrap()).collect();
themes.sort();
let rustdoc_version = rustc_interface::util::version_str!().unwrap_or("unknown version");
let content = Buffer::html().to_display(t); // Note: This must happen before making the sidebar.
let sidebar = Buffer::html().to_display(sidebar);
PageLayout {
@ -82,7 +108,10 @@ pub(crate) fn render<T: Print, S: Print>(
themes,
sidebar,
content,
krate_with_trailing_slash,
display_krate,
display_krate_with_trailing_slash,
display_krate_version_number,
display_krate_version_extra,
rust_channel: *crate::clean::utils::DOC_CHANNEL,
rustdoc_version,
}

View File

@ -24,6 +24,7 @@ use super::{
sidebar::{sidebar_module_like, Sidebar},
AllTypes, LinkFromSrc, StylePath,
};
use crate::clean::utils::has_doc_flag;
use crate::clean::{self, types::ExternalLocation, ExternalCrate, TypeAliasItem};
use crate::config::{ModuleSorting, RenderOptions};
use crate::docfs::{DocFS, PathError};
@ -277,6 +278,7 @@ impl<'tcx> Context<'tcx> {
title: &title,
description: &desc,
resource_suffix: &clone_shared.resource_suffix,
rust_logo: has_doc_flag(self.tcx(), LOCAL_CRATE.as_def_id(), sym::rust_logo),
};
let mut page_buffer = Buffer::html();
print_item(self, it, &mut page_buffer, &page);
@ -528,12 +530,14 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
if let Some(url) = playground_url {
playground = Some(markdown::Playground { crate_name: Some(krate.name(tcx)), url });
}
let krate_version = cache.crate_version.as_deref().unwrap_or_default();
let mut layout = layout::Layout {
logo: String::new(),
favicon: String::new(),
external_html,
default_settings,
krate: krate.name(tcx).to_string(),
krate_version: krate_version.to_string(),
css_file_extension: extension_css,
scrape_examples_extension: !call_locations.is_empty(),
};
@ -658,21 +662,22 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
let shared = Rc::clone(&self.shared);
let mut page = layout::Page {
title: "List of all items in this crate",
css_class: "mod",
css_class: "mod sys",
root_path: "../",
static_root_path: shared.static_root_path.as_deref(),
description: "List of all items in this crate",
resource_suffix: &shared.resource_suffix,
rust_logo: has_doc_flag(self.tcx(), LOCAL_CRATE.as_def_id(), sym::rust_logo),
};
let all = shared.all.replace(AllTypes::new());
let mut sidebar = Buffer::html();
let blocks = sidebar_module_like(all.item_sections());
let bar = Sidebar {
title_prefix: "Crate ",
title: crate_name.as_str(),
title_prefix: "",
title: "",
is_crate: false,
version: "",
is_mod: false,
blocks: vec![blocks],
path: String::new(),
};
@ -689,9 +694,10 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
shared.fs.write(final_file, v)?;
// Generating settings page.
page.title = "Rustdoc settings";
page.title = "Settings";
page.description = "Settings of Rustdoc";
page.root_path = "./";
page.rust_logo = true;
let sidebar = "<h2 class=\"location\">Settings</h2><div class=\"sidebar-elems\"></div>";
let v = layout::render(
@ -739,9 +745,10 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
shared.fs.write(settings_file, v)?;
// Generating help page.
page.title = "Rustdoc help";
page.title = "Help";
page.description = "Documentation for Rustdoc";
page.root_path = "./";
page.rust_logo = true;
let sidebar = "<h2 class=\"location\">Help</h2><div class=\"sidebar-elems\"></div>";
let v = layout::render(

View File

@ -2094,6 +2094,7 @@ impl ItemSection {
const ALL: &'static [Self] = {
use ItemSection::*;
// NOTE: The order here affects the order in the UI.
// Keep this synchronized with addSidebarItems in main.js
&[
Reexports,
PrimitiveTypes,

View File

@ -19,7 +19,7 @@ pub(super) struct Sidebar<'a> {
pub(super) title_prefix: &'static str,
pub(super) title: &'a str,
pub(super) is_crate: bool,
pub(super) version: &'a str,
pub(super) is_mod: bool,
pub(super) blocks: Vec<LinkBlock<'a>>,
pub(super) path: String,
}
@ -99,12 +99,12 @@ pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buf
|| it.is_primitive()
|| it.is_union()
|| it.is_enum()
|| it.is_mod()
// crate title is displayed as part of logo lockup
|| (it.is_mod() && !it.is_crate())
|| it.is_type_alias()
{
(
match *it.kind {
clean::ModuleItem(..) if it.is_crate() => "Crate ",
clean::ModuleItem(..) => "Module ",
_ => "",
},
@ -113,14 +113,22 @@ pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buf
} else {
("", "")
};
let version =
if it.is_crate() { cx.cache().crate_version.as_deref().unwrap_or_default() } else { "" };
let path: String = if !it.is_mod() {
cx.current.iter().map(|s| s.as_str()).intersperse("::").collect()
// need to show parent path header if:
// - it's a child module, instead of the crate root
// - there's a sidebar section for the item itself
//
// otherwise, the parent path header is redundant with the big crate
// branding area at the top of the sidebar
let sidebar_path =
if it.is_mod() { &cx.current[..cx.current.len() - 1] } else { &cx.current[..] };
let path: String = if sidebar_path.len() > 1 || !title.is_empty() {
let path = sidebar_path.iter().map(|s| s.as_str()).intersperse("::").collect();
if sidebar_path.len() == 1 { format!("crate {path}") } else { path }
} else {
"".into()
};
let sidebar = Sidebar { title_prefix, title, is_crate: it.is_crate(), version, blocks, path };
let sidebar =
Sidebar { title_prefix, title, is_mod: it.is_mod(), is_crate: it.is_crate(), blocks, path };
sidebar.render_into(buffer).unwrap();
}

View File

@ -336,11 +336,12 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex};
let dst = cx.dst.join("index.html");
let page = layout::Page {
title: "Index of crates",
css_class: "mod",
css_class: "mod sys",
root_path: "./",
static_root_path: shared.static_root_path.as_deref(),
description: "List of crates",
resource_suffix: &shared.resource_suffix,
rust_logo: true,
};
let content = format!(

View File

@ -1,4 +1,5 @@
use crate::clean;
use crate::clean::utils::has_doc_flag;
use crate::docfs::PathError;
use crate::error::Error;
use crate::html::format;
@ -13,6 +14,7 @@ use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
use rustc_span::source_map::FileName;
use rustc_span::sym;
use std::cell::RefCell;
use std::ffi::OsStr;
@ -231,6 +233,7 @@ impl SourceCollector<'_, '_> {
static_root_path: shared.static_root_path.as_deref(),
description: &desc,
resource_suffix: &shared.resource_suffix,
rust_logo: has_doc_flag(self.cx.tcx(), LOCAL_CRATE.as_def_id(), sym::rust_logo),
};
let v = layout::render(
&shared.layout,

View File

@ -461,19 +461,9 @@ img {
display: none !important;
}
.sidebar .logo-container {
margin-top: 10px;
margin-bottom: 10px;
text-align: center;
}
.version {
overflow-wrap: break-word;
}
.logo-container > img {
height: 100px;
width: 100px;
height: 48px;
width: 48px;
}
ul.block, .block li {
@ -502,6 +492,7 @@ ul.block, .block li {
}
.sidebar-elems,
.sidebar > .version,
.sidebar > h2 {
padding-left: 24px;
}
@ -510,6 +501,8 @@ ul.block, .block li {
color: var(--sidebar-link-color);
}
.sidebar .current,
.sidebar .current a,
.sidebar-crate a.logo-container:hover + h2 a,
.sidebar a:hover:not(.logo-container) {
background-color: var(--sidebar-current-link-background-color);
}
@ -524,6 +517,75 @@ ul.block, .block li {
overflow: hidden;
}
.sidebar-crate {
display: flex;
align-items: center;
justify-content: center;
/* there's a 10px padding at the top of <main>, and a 4px margin at the
top of the search form. To line them up, add them. */
margin: 14px 32px 1rem;
row-gap: 10px;
column-gap: 32px;
flex-wrap: wrap;
}
.sidebar-crate h2 {
flex-grow: 1;
/* This setup with the margins and row-gap is designed to make flex-wrap
work the way we want. If they're in the side-by-side lockup, there
should be a 16px margin to the left of the logo (visually the same as
the 24px one on everything else, which are not giant circles) and 8px
between it and the crate's name and version. When they're line wrapped,
the logo needs to have the same margin on both sides of itself (to
center properly) and the crate name and version need 24px on their
left margin. */
margin: 0 -8px;
/* To align this with the search bar, it should not be centered, even when
the logo is. */
align-self: start;
}
.sidebar-crate .logo-container {
/* The logo is expected to have 8px "slop" along its edges, so we can optically
center it. */
margin: 0 -16px 0 -16px;
text-align: center;
}
.sidebar-crate h2 a {
display: block;
margin: 0 calc(-24px + 0.25rem) 0 -0.5rem;
/* Align the sidebar crate link with the search bar, which have different
font sizes.
| | font-size | line-height | total line-height | padding-y | total |
|:-------|----------:|------------:|------------------:|----------:|-------------:|
| crate | 1.375rem | 1.25 | 1.72rem | x | 2x+1.72rem |
| search | 1rem | 1.15 | 1.15rem | 8px | 1.15rem+16px |
2x + 1.72rem = 1.15rem + 16px
2x = 1.15rem + 16px - 1.72rem
2x = 16px - 0.57rem
x = ( 16px - 0.57rem ) / 2
*/
padding: calc( ( 16px - 0.57rem ) / 2 ) 0.25rem;
padding-left: 0.5rem;
}
.sidebar-crate h2 .version {
display: block;
font-weight: normal;
font-size: 1rem;
overflow-wrap: break-word;
/* opposite of the link padding, cut in half again */
margin-top: calc( ( -16px + 0.57rem ) / 2 );
}
.sidebar-crate + .version {
margin-top: -1rem;
margin-bottom: 1rem;
}
.mobile-topbar {
display: none;
}

View File

@ -51,9 +51,14 @@ function setMobileTopbar() {
// but with the current code it's hard to get the right information in the right place.
const mobileTopbar = document.querySelector(".mobile-topbar");
const locationTitle = document.querySelector(".sidebar h2.location");
if (mobileTopbar && locationTitle) {
if (mobileTopbar) {
const mobileTitle = document.createElement("h2");
mobileTitle.innerHTML = locationTitle.innerHTML;
mobileTitle.className = "location";
if (hasClass(document.body, "crate")) {
mobileTitle.innerText = `Crate ${window.currentCrate}`;
} else if (locationTitle) {
mobileTitle.innerHTML = locationTitle.innerHTML;
}
mobileTopbar.appendChild(mobileTitle);
}
}
@ -480,22 +485,27 @@ function preLoadCss(cssUrl) {
return;
}
const modpath = hasClass(document.body, "mod") ? "../" : "";
const h3 = document.createElement("h3");
h3.innerHTML = `<a href="index.html#${id}">${longty}</a>`;
h3.innerHTML = `<a href="${modpath}index.html#${id}">${longty}</a>`;
const ul = document.createElement("ul");
ul.className = "block " + shortty;
for (const name of filtered) {
let path;
if (shortty === "mod") {
path = name + "/index.html";
path = `${modpath}${name}/index.html`;
} else {
path = shortty + "." + name + ".html";
path = `${modpath}${shortty}.${name}.html`;
}
let current_page = document.location.href.toString();
if (current_page.endsWith("/")) {
current_page += "index.html";
}
const current_page = document.location.href.split("/").pop();
const link = document.createElement("a");
link.href = path;
if (path === current_page) {
if (link.href === current_page) {
link.className = "current";
}
link.textContent = name;
@ -508,19 +518,33 @@ function preLoadCss(cssUrl) {
}
if (sidebar) {
// keep this synchronized with ItemSection::ALL in html/render/mod.rs
// Re-exports aren't shown here, because they don't have child pages
//block("reexport", "reexports", "Re-exports");
block("primitive", "primitives", "Primitive Types");
block("mod", "modules", "Modules");
block("macro", "macros", "Macros");
block("struct", "structs", "Structs");
block("enum", "enums", "Enums");
block("union", "unions", "Unions");
block("constant", "constants", "Constants");
block("static", "static", "Statics");
block("trait", "traits", "Traits");
block("fn", "functions", "Functions");
block("type", "types", "Type Aliases");
block("union", "unions", "Unions");
// No point, because these items don't appear in modules
//block("impl", "impls", "Implementations");
//block("tymethod", "tymethods", "Type Methods");
//block("method", "methods", "Methods");
//block("structfield", "fields", "Fields");
//block("variant", "variants", "Variants");
//block("associatedtype", "associated-types", "Associated Types");
//block("associatedconstant", "associated-consts", "Associated Constants");
block("foreigntype", "foreign-types", "Foreign Types");
block("keyword", "keywords", "Keywords");
block("opaque", "opaque-types", "Opaque Types");
block("attr", "attributes", "Attribute Macros");
block("derive", "derives", "Derive Macros");
block("traitalias", "trait-aliases", "Trait Aliases");
}
}

View File

@ -42,6 +42,8 @@
<script defer src="{{page.root_path|safe}}src-files{{page.resource_suffix}}.js"></script> {# #}
{% else if !page.css_class.contains("mod") %}
<script defer src="sidebar-items{{page.resource_suffix}}.js"></script> {# #}
{% else if !page.css_class.contains("sys") %}
<script defer src="../sidebar-items{{page.resource_suffix}}.js"></script> {# #}
{% endif %}
<script defer src="{{static_root_path|safe}}{{files.main_js}}"></script> {# #}
{% if layout.scrape_examples_extension %}
@ -77,36 +79,51 @@
{% if page.css_class != "src" %}
<nav class="mobile-topbar"> {# #}
<button class="sidebar-menu-toggle">&#9776;</button> {# #}
<a class="logo-container" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {# #}
{% if !layout.logo.is_empty() %}
<img src="{{layout.logo}}" alt="logo"> {# #}
{% else %}
<img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {# #}
{% if !layout.logo.is_empty() || page.rust_logo %}
<a class="logo-container" href="{{page.root_path|safe}}{{display_krate_with_trailing_slash|safe}}index.html"> {# #}
{% if page.rust_logo %}
<img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt=""> {# #}
{% else if !layout.logo.is_empty() %}
<img src="{{layout.logo}}" alt=""> {# #}
{% endif %}
</a> {# #}
{% endif %}
</nav>
{% endif %}
<nav class="sidebar"> {# #}
{% if page.css_class != "src" %}
<a class="logo-container" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {# #}
{% if !layout.logo.is_empty() %}
<img src="{{layout.logo}}" alt="logo"> {# #}
{% else %}
<img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {# #}
<div class="sidebar-crate">
{% if !layout.logo.is_empty() || page.rust_logo %}
<a class="logo-container" href="{{page.root_path|safe}}{{display_krate_with_trailing_slash|safe}}index.html"> {# #}
{% if page.rust_logo %}
<img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {# #}
{% else if !layout.logo.is_empty() %}
<img src="{{layout.logo}}" alt="logo"> {# #}
{% endif %}
</a> {# #}
{% endif %}
</a> {# #}
<h2> {# #}
<a href="{{page.root_path|safe}}{{display_krate_with_trailing_slash|safe}}index.html">{{display_krate}}</a> {# #}
{% if !display_krate_version_number.is_empty() %}
<span class="version">{{+ display_krate_version_number}}</span>
{% endif %}
</h2> {# #}
</div> {# #}
{% if !display_krate_version_extra.is_empty() %}
<div class="version">{{+ display_krate_version_extra}}</div> {# #}
{% endif %}
{% endif %}
{{ sidebar|safe }}
</nav> {# #}
<main> {# #}
{% if page.css_class != "src" %}<div class="width-limiter">{% endif %}
<nav class="sub"> {# #}
{% if page.css_class == "src" %}
<a class="sub-logo-container" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {# #}
{% if !layout.logo.is_empty() %}
<img src="{{layout.logo}}" alt="logo"> {# #}
{% else %}
<img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="logo"> {# #}
{% if page.css_class == "src" && (!layout.logo.is_empty() || page.rust_logo) %}
<a class="sub-logo-container" href="{{page.root_path|safe}}{{display_krate_with_trailing_slash|safe}}index.html"> {# #}
{% if page.rust_logo %}
<img class="rust-logo" src="{{static_root_path|safe}}{{files.rust_logo_svg}}" alt="{{display_krate}}"> {# #}
{% else if !layout.logo.is_empty() %}
<img src="{{layout.logo}}" alt="{{display_krate}}"> {# #}
{% endif %}
</a> {# #}
{% endif %}

View File

@ -6,9 +6,6 @@
<div class="sidebar-elems">
{% if is_crate %}
<ul class="block">
{% if !version.is_empty() %}
<li class="version">Version {{+ version}}</li>
{% endif %}
<li><a id="all-types" href="all.html">All Items</a></li> {# #}
</ul>
{% endif %}
@ -32,6 +29,6 @@
</section>
{% endif %}
{% if !path.is_empty() %}
<h2><a href="index.html">In {{+ path}}</a></h2>
<h2><a href="{% if is_mod %}../{% endif %}index.html">In {{+ path}}</a></h2>
{% endif %}
</div>

View File

@ -322,7 +322,15 @@ impl TestProps {
);
if let Some(flags) = config.parse_name_value_directive(ln, COMPILE_FLAGS) {
self.compile_flags.extend(flags.split_whitespace().map(|s| s.to_owned()));
self.compile_flags.extend(
flags
.split("'")
.enumerate()
.flat_map(|(i, f)| {
if i % 2 == 1 { vec![f] } else { f.split_whitespace().collect() }
})
.map(|s| s.to_owned()),
);
}
if config.parse_name_value_directive(ln, INCORRECT_COMPILER_FLAGS).is_some() {
panic!("`compiler-flags` directive should be spelled `compile-flags`");

View File

@ -56,7 +56,7 @@ define-function: (
assert-css: ("#top-doc-prose-title", {"color": |title_color|})
assert-css: (".sidebar a", {"color": |sidebar_link_color|})
assert-css: (".sidebar .block a", {"color": |sidebar_link_color|})
assert-css: (".main-heading h1 a", {"color": |title_color|})
// We move the cursor over the "Implementations" title so the anchor is displayed.

View File

@ -4,8 +4,8 @@ go-to: "file://" + |DOC_PATH| + "/huge_logo/index.html"
set-window-size: (1280, 1024)
// offsetWidth = width of sidebar
assert-property: (".sidebar .logo-container", {"offsetWidth": "200", "offsetHeight": 100})
assert-property: (".sidebar .logo-container img", {"offsetWidth": "100", "offsetHeight": 100})
assert-property: (".sidebar-crate .logo-container", {"offsetWidth": "48", "offsetHeight": 48})
assert-property: (".sidebar-crate .logo-container img", {"offsetWidth": "48", "offsetHeight": 48})
set-window-size: (400, 600)
// offset = size + margin

View File

@ -1,18 +1,18 @@
// This test ensures that the correct style is applied to the rust logo in the sidebar.
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
go-to: "file://" + |DOC_PATH| + "/staged_api/index.html"
define-function: (
"check-logo",
(theme, filter),
block {
// Going to the doc page.
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
go-to: "file://" + |DOC_PATH| + "/staged_api/index.html"
// Changing theme.
set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
reload:
assert-css: (".rust-logo", {"filter": |filter|})
// Going to the source code page.
go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html"
go-to: "file://" + |DOC_PATH| + "/src/staged_api/lib.rs.html"
// Changing theme (since it's local files, the local storage works by folder).
set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}
reload:
@ -26,6 +26,15 @@ define-function: (
assert-false: ".rust-logo"
// Check there is no filter.
assert-css: (".sidebar .logo-container img", {"filter": "none"})
// Now we check that this page has no logo at all
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
assert-false: ".rust-logo"
assert-false: ".logo-container"
assert-false: ".sub-logo-container"
go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html"
assert-false: ".rust-logo"
assert-false: ".logo-container"
assert-false: ".sub-logo-container"
},
)

View File

@ -26,7 +26,7 @@ assert-css: (".sidebar", {"left": "0px"})
// Make sure the "struct Foo" header is hidden, since the mobile topbar already does it.
assert-css: ("//nav[contains(@class, 'sidebar')]//h2/a[text()='Foo']/parent::h2", {"display": "none"})
// Make sure the global navigation is still here.
assert-css: ("//nav[contains(@class, 'sidebar')]//h2/a[text()='In test_docs']/parent::h2", {"display": "block"})
assert-css: ("//nav[contains(@class, 'sidebar')]//h2/a[text()='In crate test_docs']/parent::h2", {"display": "block"})
// Click elsewhere.
click: "body"
@ -50,7 +50,7 @@ assert-position: ("#method\.must_use", {"y": 46})
// Check that the bottom-most item on the sidebar menu can be scrolled fully into view.
click: ".sidebar-menu-toggle"
scroll-to: ".block.keyword li:nth-child(1)"
compare-elements-position-near: (".block.keyword li:nth-child(1)", ".mobile-topbar", {"y": 543.19})
compare-elements-position-near: (".block.keyword li:nth-child(1)", ".mobile-topbar", {"y": 544})
// Now checking the background color of the sidebar.
show-text: true

View File

@ -174,14 +174,14 @@ click: "#src-sidebar-toggle"
wait-for-css: (".sidebar", {"left": "-1000px"})
// We scroll to line 117 to change the scroll position.
scroll-to: '//*[@id="117"]'
assert-window-property: {"pageYOffset": "2542"}
assert-window-property: {"pageYOffset": "2516"}
// Expanding the sidebar...
click: "#src-sidebar-toggle"
wait-for-css: (".sidebar", {"left": "0px"})
click: "#src-sidebar-toggle"
wait-for-css: (".sidebar", {"left": "-1000px"})
// The "scrollTop" property should be the same.
assert-window-property: {"pageYOffset": "2542"}
assert-window-property: {"pageYOffset": "2516"}
// We now check that opening the sidebar and clicking a link will close it.
// The behavior here on mobile is different than the behavior on desktop,

View File

@ -50,9 +50,9 @@ set-local-storage: {"rustdoc-theme": "light"}
// We reload the page so the local storage settings are being used.
reload:
assert-text: (".sidebar > .location", "Crate test_docs")
// In modules, we only have one "location" element.
assert-count: (".sidebar .location", 1)
assert-text: (".sidebar > .sidebar-crate > h2 > a", "test_docs")
// Crate root has no "location" element
assert-count: (".sidebar .location", 0)
assert-count: (".sidebar h2", 1)
assert-text: ("#all-types", "All Items")
assert-css: ("#all-types", {"color": "#356da4"})
@ -74,8 +74,9 @@ assert-text: ("#structs + .item-table .item-name > a", "Foo")
click: "#structs + .item-table .item-name > a"
// PAGE: struct.Foo.html
assert-count: (".sidebar .sidebar-crate", 1)
assert-count: (".sidebar .location", 1)
assert-count: (".sidebar h2", 2)
assert-count: (".sidebar h2", 3)
// We check that there is no crate listed outside of the top level.
assert-false: ".sidebar-elems > .crate"
@ -94,7 +95,8 @@ click: ".sidebar-elems ul.crate > li:first-child > a"
// PAGE: lib2/index.html
go-to: "file://" + |DOC_PATH| + "/lib2/index.html"
assert-property: (".sidebar", {"clientWidth": "200"})
assert-text: (".sidebar > .location", "Crate lib2")
assert-text: (".sidebar > .sidebar-crate > h2 > a", "lib2")
assert-count: (".sidebar .location", 0)
// We check that we have the crates list and that the "current" on is now "lib2".
assert-text: (".sidebar-elems ul.crate > li > a.current", "lib2")
// We now go to the "foobar" function page.
@ -108,22 +110,39 @@ click: "#functions + .item-table .item-name > a"
// PAGE: fn.foobar.html
// In items containing no items (like functions or constants) and in modules, we have no
// "location" elements. Only the parent module h2.
// "location" elements. Only the crate and optional parent module.
// This page, being directly below the crate, only has its heading.
assert-text: (".sidebar > .sidebar-crate > h2 > a", "lib2")
assert-count: (".sidebar .location", 0)
assert-count: (".sidebar h2", 1)
assert-text: (".sidebar .sidebar-elems h2", "In lib2")
// We check that we don't have the crate list.
assert-false: ".sidebar-elems > .crate"
go-to: "./module/index.html"
assert-property: (".sidebar", {"clientWidth": "200"})
assert-text: (".sidebar > .sidebar-crate > h2 > a", "lib2")
assert-text: (".sidebar > .location", "Module module")
assert-count: (".sidebar .location", 1)
// Module page requires three headings:
// - Presistent crate branding (name and version)
// - Module name, followed by TOC for module headings
// - "In crate [name]" parent pointer, followed by sibling navigation
assert-count: (".sidebar h2", 3)
assert-text: (".sidebar > .sidebar-elems > h2", "In crate lib2")
assert-property: (".sidebar > .sidebar-elems > h2 > a", {
"href": "/lib2/index.html",
}, ENDS_WITH)
// We check that we don't have the crate list.
assert-false: ".sidebar-elems > .crate"
go-to: "./sub_module/sub_sub_module/index.html"
assert-property: (".sidebar", {"clientWidth": "200"})
assert-text: (".sidebar > .sidebar-crate > h2 > a", "lib2")
assert-text: (".sidebar > .location", "Module sub_sub_module")
assert-text: (".sidebar > .sidebar-elems > h2", "In lib2::module::sub_module")
assert-property: (".sidebar > .sidebar-elems > h2 > a", {
"href": "/module/sub_module/index.html",
}, ENDS_WITH)
// We check that we don't have the crate list.
assert-false: ".sidebar-elems .crate"
assert-text: (".sidebar-elems > section ul > li:nth-child(1)", "Functions")
@ -152,14 +171,14 @@ assert-property: (".sidebar", {"clientWidth": "200"})
// Checks that all.html and index.html have their sidebar link in the same place.
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
store-property: (".sidebar .location a", {
store-property: (".sidebar .sidebar-crate h2 a", {
"clientWidth": index_sidebar_width,
"clientHeight": index_sidebar_height,
"offsetTop": index_sidebar_y,
"offsetLeft": index_sidebar_x,
})
go-to: "file://" + |DOC_PATH| + "/test_docs/all.html"
assert-property: (".sidebar .location a", {
assert-property: (".sidebar .sidebar-crate h2 a", {
"clientWidth": |index_sidebar_width|,
"clientHeight": |index_sidebar_height|,
"offsetTop": |index_sidebar_y|,

View File

@ -8,13 +8,13 @@ set-window-size: (600, 800)
assert-property: ("html", {"scrollTop": "0"})
click: '//a[text() = "barbar" and @href="#5-7"]'
assert-property: ("html", {"scrollTop": "149"})
assert-property: ("html", {"scrollTop": "123"})
click: '//a[text() = "bar" and @href="#28-36"]'
assert-property: ("html", {"scrollTop": "180"})
assert-property: ("html", {"scrollTop": "154"})
click: '//a[text() = "sub_fn" and @href="#2-4"]'
assert-property: ("html", {"scrollTop": "77"})
assert-property: ("html", {"scrollTop": "51"})
// We now check that clicking on lines doesn't change the scroll
// Extra information: the "sub_fn" function header is on line 1.
click: '//*[@id="6"]'
assert-property: ("html", {"scrollTop": "77"})
assert-property: ("html", {"scrollTop": "51"})

View File

@ -89,9 +89,9 @@ assert-css: (".src-line-numbers", {"text-align": "right"})
// do anything (and certainly not add a `#NaN` to the URL!).
go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html"
// We use this assert-position to know where we will click.
assert-position: ("//*[@id='1']", {"x": 88, "y": 112})
assert-position: ("//*[@id='1']", {"x": 88, "y": 86})
// We click on the left of the "1" anchor but still in the "src-line-number" `<pre>`.
click: (87, 103)
click: (87, 77)
assert-document-property: ({"URL": "/lib.rs.html"}, ENDS_WITH)
// Checking the source code sidebar.
@ -163,16 +163,16 @@ assert-css: ("nav.sub", {"flex-direction": "row"})
// To check this, we maintain the invariant:
//
// offsetTop[nav.sub form] = offsetTop[#main-content] - offsetHeight[nav.sub form] - offsetTop[nav.sub form]
assert-property: ("nav.sub form", {"offsetTop": 28, "offsetHeight": 34})
assert-property: ("#main-content", {"offsetTop": 90})
// 28 = 90 - 34 - 28
assert-property: ("nav.sub form", {"offsetTop": 15, "offsetHeight": 34})
assert-property: ("#main-content", {"offsetTop": 64})
// 15 = 64 - 34 - 15
// Now do the same check on moderately-sized, tablet mobile.
set-window-size: (700, 700)
assert-css: ("nav.sub", {"flex-direction": "row"})
assert-property: ("nav.sub form", {"offsetTop": 21, "offsetHeight": 34})
assert-property: ("#main-content", {"offsetTop": 76})
// 21 = 76 - 34 - 21
assert-property: ("nav.sub form", {"offsetTop": 8, "offsetHeight": 34})
assert-property: ("#main-content", {"offsetTop": 50})
// 8 = 50 - 34 - 8
// Check the sidebar directory entries have a marker and spacing (tablet).
store-property: ("#src-sidebar > .title", {
@ -198,7 +198,12 @@ call-function: ("check-sidebar-dir-entry", {
"y": |source_sidebar_title_y| + |source_sidebar_title_height| + 6,
})
// The logo is not present on this page.
assert-false: ".sub-logo-container > img"
// Check the staged-api page instead, which does.
// Now we check that the logo has a bottom margin so it's not stuck to the search input.
go-to: "file://" + |DOC_PATH| + "/src/staged_api/lib.rs.html"
assert-css: (".sub-logo-container > img", {"margin-bottom": "8px"})
store-property: (".sub-logo-container", {"clientHeight": logo_height})
assert-position: (".search-form", {"y": |logo_height| + 8})

View File

@ -1,6 +1,8 @@
#![feature(staged_api)]
#![feature(rustdoc_internals)]
#![allow(internal_features)]
#![stable(feature = "some_feature", since = "1.3.5")]
#![doc(rust_logo)]
#[stable(feature = "some_feature", since = "1.3.5")]
pub struct Foo {}

View File

@ -2,4 +2,4 @@
#![crate_name = "foo"]
// @has 'foo/index.html' '//li[@class="version"]' 'Version <script>alert("hi")</script>'
// @has 'foo/index.html' '//*[@class="version"]' '<script>alert("hi")</script>'

View File

@ -0,0 +1,7 @@
// compile-flags: '--crate-version=1.3.37-nightly (203c57dbe 2023-09-17)'
#![crate_name="foo"]
// main version next to logo, extra version data below it
// @has 'foo/index.html' '//h2/span[@class="version"]' '1.3.37-nightly'
// @has 'foo/index.html' '//nav[@class="sidebar"]/div[@class="version"]' '(203c57dbe 2023-09-17)'

View File

@ -1,3 +1,3 @@
// compile-flags: --crate-version=1.3.37
// @has 'crate_version/index.html' '//*[@class="version"]' 'Version 1.3.37'
// @has 'crate_version/index.html' '//*[@class="version"]' '1.3.37'

View File

@ -1,4 +1,4 @@
// Note: this test is paired with logo-class.rs.
// @has logo_class_default/struct.SomeStruct.html '//*[@class="logo-container"]/img[@class="rust-logo"]' ''
// @has src/logo_class_default/logo-class-default.rs.html '//*[@class="sub-logo-container"]/img[@class="rust-logo"]' ''
// Note: this test is paired with logo-class.rs and logo-class-rust.rs.
// @!has logo_class_default/struct.SomeStruct.html '//*[@class="logo-container"]/img' ''
// @!has src/logo_class_default/logo-class-default.rs.html '//*[@class="sub-logo-container"]/img' ''
pub struct SomeStruct;

View File

@ -0,0 +1,7 @@
#![feature(rustdoc_internals)]
#![allow(internal_features)]
#![doc(rust_logo)]
// Note: this test is paired with logo-class.rs and logo-class-default.rs.
// @has logo_class_rust/struct.SomeStruct.html '//*[@class="logo-container"]/img[@class="rust-logo"]' ''
// @has src/logo_class_rust/logo-class-rust.rs.html '//*[@class="sub-logo-container"]/img[@class="rust-logo"]' ''
pub struct SomeStruct;

View File

@ -1,6 +1,6 @@
#![doc(html_logo_url =
"https://raw.githubusercontent.com/sagebind/isahc/master/media/isahc.svg.png")]
// Note: this test is paired with logo-class-default.rs.
// Note: this test is paired with logo-class-default.rs and logo-class-rust.rs.
// @has logo_class/struct.SomeStruct.html '//*[@class="logo-container"]/img[@src="https://raw.githubusercontent.com/sagebind/isahc/master/media/isahc.svg.png"]' ''
// @!has logo_class/struct.SomeStruct.html '//*[@class="logo-container"]/img[@class="rust-logo"]' ''

View File

@ -2,7 +2,8 @@
#![feature(rustc_attrs)]
// @matches 'foo/index.html' '//h1' 'Crate foo'
// @matches 'foo/index.html' '//h2[@class="location"]' 'Crate foo'
// @matches 'foo/index.html' '//div[@class="sidebar-crate"]/h2/a' 'foo'
// @count 'foo/index.html' '//h2[@class="location"]' 0
// @matches 'foo/foo_mod/index.html' '//h1' 'Module foo::foo_mod'
// @matches 'foo/foo_mod/index.html' '//h2[@class="location"]' 'Module foo_mod'

View File

@ -0,0 +1,5 @@
#![doc(rust_logo)]
//~^ ERROR the `#[doc(rust_logo)]` attribute is used for Rust branding
//! This is not an official rust crate
fn main() {}

View File

@ -0,0 +1,12 @@
error[E0658]: the `#[doc(rust_logo)]` attribute is used for Rust branding
--> $DIR/doc-rust-logo.rs:1:8
|
LL | #![doc(rust_logo)]
| ^^^^^^^^^
|
= note: see issue #90418 <https://github.com/rust-lang/rust/issues/90418> for more information
= help: add `#![feature(rustdoc_internals)]` to the crate attributes to enable
error: aborting due to previous error
For more information about this error, try `rustc --explain E0658`.