Find a file
Matthias Krüger e1da72c6e8
Rollup merge of #120736 - notriddle:notriddle/toc, r=t-rustdoc
rustdoc: add header map to the table of contents

## Summary

Add header sections to the sidebar TOC.

### Preview

![image](https://github.com/user-attachments/assets/eae4df02-86aa-4df4-8c61-a95685cd8829)

* http://notriddle.com/rustdoc-html-demo-9/toc/rust/std/index.html
* http://notriddle.com/rustdoc-html-demo-9/toc/rust-derive-builder/derive_builder/index.html

## Motivation

Some pages are very wordy, like these.

| crate | word count |
|--|--|
| [std::option](https://doc.rust-lang.org/stable/std/option/index.html) | 2,138
| [derive_builder](https://docs.rs/derive_builder/0.13.0/derive_builder/index.html) | 2,403
| [tracing](https://docs.rs/tracing/0.1.40/tracing/index.html) | 3,912
| [regex](https://docs.rs/regex/1.10.3/regex/index.html) | 8,412

This kind of very long document is more navigable with a table of contents, like Wikipedia's or the one [GitHub recently added](https://github.blog/changelog/2021-04-13-table-of-contents-support-in-markdown-files/) for READMEs.

In fact, the use case is so compelling, that it's been requested multiple times and implemented in an extension:

* https://github.com/rust-lang/rust/issues/80858
* https://github.com/rust-lang/rust/issues/28056
* https://github.com/rust-lang/rust/issues/14475
* https://rust.extension.sh/#show-table-of-content

(Some of these issues ask for more than this, so don’t close them.)

It's also been implemented by hand in some crates, because the author really thought it was needed. Protip: for a more exhaustive list, run [`site:docs.rs table of contents`](https://duckduckgo.com/?t=ffab&q=site%3Adocs.rs+table+of+contents&ia=web), though some of them are false positives.

* https://docs.rs/figment/0.10.14/figment/index.html#table-of-contents
* https://docs.rs/csv/1.3.0/csv/tutorial/index.html#table-of-contents
* https://docs.rs/axum/0.7.4/axum/response/index.html#table-of-contents
* https://docs.rs/regex-automata/0.4.5/regex_automata/index.html#table-of-contents

Unfortunately for these hand-built ToCs, because they're just part of the docs, there's no consistent way to turn them off if the reader doesn't want them. It's also more complicated to ensure they stay in sync with the docs they're supposed to describe, and they don't stay with you when you scroll like Wikipedia's [does now](https://uxdesign.cc/design-notes-on-the-2023-wikipedia-redesign-d6573b9af28d).

## Guide-level explanation

When writing docs for a top-level item, the first and second level of headers will be shown in an outline in the sidebar. In this context, "top level" means "not associated".

This means, if you're writing very long guides or explanations, and you want it to have a table of contents in the sidebar for its headings, the ideal place to attach it is usually the *module* or *crate*, because this page has fewer other things on it (and is the ideal place to describe "cross-cutting concerns" for its child items).

If you're reading documentation, and want to get rid of the table of contents, open the ![image](https://github.com/rust-lang/rust/assets/1593513/2ad82466-5fe3-4684-b1c2-6be4c99a8666) Settings panel and checkmark "Hide table of contents."

## Reference-level explanation

Top-level items have an outline generated. This works for potentially-malformed header trees by pairing a header with the nearest header with a higher level. For example:

```markdown
## A
# B
# C
## D
## E
```

A, B, and C are all siblings, and D and E are children of C.

Rustdoc only presents two layers of tree, but it tracks up to the full depth of 6 while preparing it.

That means that these two doc comment both generate the same outline:

```rust
/// # First
/// ## Second
struct One;
/// ## First
/// ### Second
struct Two;
```

## Drawbacks

The biggest drawback is adding more stuff to the sidebar.

My crawl through docs.rs shows this to, surprisingly, be less of a problem than I thought. The manually-built tables of contents, and the pages with dozens of headers, usually seem to be modules or crates, not types (where extreme scrolling would become a problem, since they already have methods to deal with).

The best example of a type with many headers is [vec::Vec](https://doc.rust-lang.org/1.75.0/std/vec/struct.Vec.html), which still only has five headers, not dozens like [axum::extract](https://docs.rs/axum/0.7.4/axum/extract/index.html).

## Rationale and alternatives

### Why in the existing sidebar?

The method links and the top-doc header links have more in common with each other than either of them do with the "In [parent module]" links, and should go together.

### Why limited to two levels?

The sidebar is pretty narrow, and I don't want too much space used by indentation. Making the sidebar wider, while it has some upsides, also takes up more space on middling-sized screens or tiled WMs.

### Why not line wrap?

That behaves strangely when resizing.

## Prior art

### Doc generators that have TOC for headers

https://hexdocs.pm/phoenix/Phoenix.Controller.html is very close, in the sense that it also has header sections directly alongside functions and types.

Another example, referenced as part of the [early sidebar discussion](https://github.com/rust-lang/rust/issues/37856) that added methods, Ruby will show a table of contents in the sidebar (for example, on the [ARGF](https://docs.ruby-lang.org/en/master/ARGF.html) class). According to their changelog, [they added it in 2013](06137bde8c/History.rdoc (400--2013-02-24-)).

Haskell seems to mix text and functions even more freely than Elixir. For example, this [Naming conventions](https://hackage.haskell.org/package/base-4.19.0.0/docs/Control-Monad.html#g:3) is plain text, and is immediately followed by functions. And the [Pandoc top level](https://hackage.haskell.org/package/pandoc-3.1.11.1/docs/Text-Pandoc.html) has items split up by function, rather than by kind. Their TOC matches exactly with the contents of the page.

### Doc generators that don't have header TOC, but still have headers

Elm, interestingly enough, seems to have the same setup that Rust used to have: sibling navigation between modules, and no index within a single page. [They keep Haskell's habit of named sections with machine-generated type signatures](https://package.elm-lang.org/packages/elm/browser/latest/Browser-Dom), though.

[PHP](https://www.php.net/manual/en/book.datetime.php), like elm, also has a right-hand sidebar with sibling navigation. However, PHP has a single page for a single method, unlike Rust's page for an entire "class." So even though these pages have headers, it's never more than ten at most. And when they have guides, those guides are also multi-page.

## Unresolved questions

* Writing recommendations for anyone who wants to take advantage of this.
* Right now, it does not line wrap. That might be a bad idea: a lot of these are getting truncated.
* Split sidebars, which I [tried implementing](https://rust-lang.zulipchat.com/#narrow/stream/266220-t-rustdoc/topic/Table.20of.20contents), are not required. The TOC can be turned off, if it's really a problem. Implemented in https://github.com/rust-lang/rust/pull/120818, but needs more, separate, discussion.

## Future possibilities

I would like to do a better job of distinguishing global navigation from local navigation. Rustdoc has a pretty reasonable information architecture, if only we did a better job of communicating it.

This PR aims, mostly, to help doc authors help their users by writing docs that can be more effectively skimmed. But it doesn't do anything to make it easier to tell the TOC and the Module Nav apart.
2024-09-05 03:47:40 +02:00
.github Update library/Cargo.toml in weekly job 2024-08-19 07:30:11 -05:00
compiler Rollup merge of #101339 - the8472:ci-randomize-debug, r=Mark-Simulacrum 2024-09-05 03:47:39 +02:00
library Rollup merge of #101339 - the8472:ci-randomize-debug, r=Mark-Simulacrum 2024-09-05 03:47:39 +02:00
LICENSES Include REUSE.toml in REUSE.toml. 2024-07-22 09:44:18 +01:00
src Rollup merge of #120736 - notriddle:notriddle/toc, r=t-rustdoc 2024-09-05 03:47:40 +02:00
tests Rollup merge of #120736 - notriddle:notriddle/toc, r=t-rustdoc 2024-09-05 03:47:40 +02:00
.clang-format Add .clang-format 2024-06-26 05:56:00 +08:00
.editorconfig Only use max_line_length = 100 for *.rs 2023-07-10 15:18:36 -07:00
.git-blame-ignore-revs Exclude the copy blessing from git blame 2024-08-18 16:09:03 -07:00
.gitattributes Rename config.toml.example to config.example.toml 2023-03-11 14:10:00 -08:00
.gitignore gitignore: ignore ICE reports regardless of directory 2024-08-25 14:19:05 +03:00
.gitmodules Update to LLVM 19 2024-07-30 10:22:48 +02:00
.ignore Add .ignore file to make config.toml searchable in vscode 2024-06-24 10:15:16 +02:00
.mailmap Rollup merge of #129906 - BoxyUwU:boxy_mailmap, r=lqd 2024-09-02 22:35:23 +02:00
Cargo.lock Rollup merge of #101339 - the8472:ci-randomize-debug, r=Mark-Simulacrum 2024-09-05 03:47:39 +02:00
Cargo.toml Switch to using the v2 resolver in most workspaces 2024-08-18 13:59:09 -05:00
CODE_OF_CONDUCT.md Remove the code of conduct; instead link https://www.rust-lang.org/conduct.html 2019-10-05 22:55:19 +02:00
config.example.toml Rollup merge of #101339 - the8472:ci-randomize-debug, r=Mark-Simulacrum 2024-09-05 03:47:39 +02:00
configure Ensure ./configure works when configure.py path contains spaces 2024-02-16 18:57:22 +00:00
CONTRIBUTING.md fix: Update CONTRIBUTING.md recommend -> recommended 2023-11-16 23:57:09 +05:30
COPYRIGHT Update COPYRIGHT file 2022-10-30 10:23:14 -04:00
INSTALL.md Rollup merge of #127434 - onur-ozkan:use-bootstrap-instead-of-rustbuild, r=Mark-Simulacrum 2024-07-13 20:19:45 -07:00
LICENSE-APACHE Remove appendix from LICENCE-APACHE 2019-12-30 14:25:53 +00:00
LICENSE-MIT LICENSE-MIT: Remove inaccurate (misattributed) copyright notice 2017-07-26 16:51:58 -07:00
README.md Use SVG logos in the README.md. 2024-04-03 19:48:20 +02:00
RELEASES.md include 1.80.1 release notes on master 2024-09-03 12:42:48 +02:00
REUSE.toml REUSE.toml: Copyright text isn't parsed as Markdown. 2024-08-06 11:04:55 +01:00
rust-bors.toml Increase timeout for new bors bot 2024-03-13 08:31:07 +01:00
rustfmt.toml Auto merge of #128083 - Mark-Simulacrum:bump-bootstrap, r=albertlarsan68 2024-07-30 17:49:08 +00:00
triagebot.toml Remove kobzol vacation status 2024-09-02 20:06:37 +02:00
x Make x capable of resolving symlinks 2023-10-14 17:53:33 +03:00
x.ps1 use & instead of start-process in x.ps1 2023-12-09 09:46:16 -05:00
x.py Fix recent python linting errors 2023-08-02 04:40:28 -04:00

This is the main source code repository for Rust. It contains the compiler, standard library, and documentation.

Why Rust?

  • Performance: Fast and memory-efficient, suitable for critical services, embedded devices, and easily integrate with other languages.

  • Reliability: Our rich type system and ownership model ensure memory and thread safety, reducing bugs at compile-time.

  • Productivity: Comprehensive documentation, a compiler committed to providing great diagnostics, and advanced tooling including package manager and build tool (Cargo), auto-formatter (rustfmt), linter (Clippy) and editor support (rust-analyzer).

Quick Start

Read "Installation" from The Book.

Installing from Source

If you really want to install from source (though this is not recommended), see INSTALL.md.

Getting Help

See https://www.rust-lang.org/community for a list of chat platforms and forums.

Contributing

See CONTRIBUTING.md.

License

Rust is primarily distributed under the terms of both the MIT license and the Apache License (Version 2.0), with portions covered by various BSD-like licenses.

See LICENSE-APACHE, LICENSE-MIT, and COPYRIGHT for details.

Trademark

The Rust Foundation owns and protects the Rust and Cargo trademarks and logos (the "Rust Trademarks").

If you want to use these names or brands, please read the media guide.

Third-party logos may be subject to third-party copyrights and trademarks. See Licenses for details.