Skip to content

make toc generation fully automatic #2509

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ jobs:
MDBOOK_VERSION: 0.4.48
MDBOOK_LINKCHECK2_VERSION: 0.9.1
MDBOOK_MERMAID_VERSION: 0.12.6
MDBOOK_TOC_VERSION: 0.11.2
MDBOOK_OUTPUT__LINKCHECK__FOLLOW_WEB_LINKS: ${{ github.event_name != 'pull_request' }}
DEPLOY_DIR: book/html
BASE_SHA: ${{ github.event.pull_request.base.sha }}
Expand All @@ -34,7 +33,7 @@ jobs:
with:
path: |
~/.cargo/bin
key: ${{ runner.os }}-${{ env.MDBOOK_VERSION }}--${{ env.MDBOOK_LINKCHECK2_VERSION }}--${{ env.MDBOOK_TOC_VERSION }}--${{ env.MDBOOK_MERMAID_VERSION }}
key: ${{ runner.os }}-${{ env.MDBOOK_VERSION }}--${{ env.MDBOOK_LINKCHECK2_VERSION }}--${{ env.MDBOOK_MERMAID_VERSION }}

- name: Restore cached Linkcheck
if: github.event_name == 'schedule'
Expand All @@ -57,7 +56,6 @@ jobs:
run: |
cargo install mdbook --version ${{ env.MDBOOK_VERSION }}
cargo install mdbook-linkcheck2 --version ${{ env.MDBOOK_LINKCHECK2_VERSION }}
cargo install mdbook-toc --version ${{ env.MDBOOK_TOC_VERSION }}
cargo install mdbook-mermaid --version ${{ env.MDBOOK_MERMAID_VERSION }}

- name: Check build
Expand Down
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ rustdocs][rustdocs].
To build a local static HTML site, install [`mdbook`](https://github.com/rust-lang/mdBook) with:

```
cargo install mdbook mdbook-linkcheck2 mdbook-toc mdbook-mermaid
cargo install mdbook mdbook-linkcheck2 mdbook-mermaid
```

and execute the following command in the root of the repository:
Expand All @@ -67,8 +67,7 @@ ENABLE_LINKCHECK=1 mdbook serve

### Table of Contents

We use `mdbook-toc` to auto-generate TOCs for long sections. You can invoke the preprocessor by
including the `<!-- toc -->` marker at the place where you want the TOC.
These are automatically generated by `pagetoc.css` and `pagetoc.js` files.

## Synchronizing josh subtree with rustc

Expand Down
11 changes: 6 additions & 5 deletions book.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@ description = "A guide to developing the Rust compiler (rustc)"
[build]
create-missing = false

[preprocessor.toc]
command = "mdbook-toc"
renderer = ["html"]

[preprocessor.mermaid]
command = "mdbook-mermaid"

[output.html]
git-repository-url = "https://github.com/rust-lang/rustc-dev-guide"
edit-url-template = "https://github.com/rust-lang/rustc-dev-guide/edit/master/{path}"
additional-js = ["mermaid.min.js", "mermaid-init.js"]
additional-js = [
"mermaid.min.js",
"mermaid-init.js",
"pagetoc.js",
]
additional-css = ["pagetoc.css"]

[output.html.search]
use-boolean-and = true
Expand Down
84 changes: 84 additions & 0 deletions pagetoc.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/* Inspired by https://github.com/JorelAli/mdBook-pagetoc/tree/98ee241 (under WTFPL) */

:root {
--toc-width: 270px;
--center-content-toc-shift: calc(-1 * var(--toc-width) / 2);
}

.nav-chapters {
/* adjust width of buttons that bring to the previous or the next page */
min-width: 50px;
}

@media only screen {
@media (max-width: 1179px) {
.sidebar-hidden #sidetoc {
display: none;
}
}

@media (max-width: 1439px) {
.sidebar-visible #sidetoc {
display: none;
}
}

@media (1180px <= width <= 1439px) {
.sidebar-hidden main {
position: relative;
left: var(--center-content-toc-shift);
}
}

@media (1440px <= width <= 1700px) {
.sidebar-visible main {
position: relative;
left: var(--center-content-toc-shift);
}
}

#sidetoc {
margin-left: calc(100% + 20px);
}
#pagetoc {
position: fixed;
/* adjust TOC width */
width: var(--toc-width);
height: calc(100vh - var(--menu-bar-height) - 0.67em * 4);
overflow: auto;
}
#pagetoc a {
border-left: 1px solid var(--sidebar-bg);
color: var(--fg);
display: block;
padding-bottom: 5px;
padding-top: 5px;
padding-left: 10px;
text-align: left;
text-decoration: none;
}
#pagetoc a:hover,
#pagetoc a.active {
background: var(--sidebar-bg);
color: var(--sidebar-active) !important;
}
#pagetoc .active {
background: var(--sidebar-bg);
color: var(--sidebar-active);
}
#pagetoc .pagetoc-H2 {
padding-left: 20px;
}
#pagetoc .pagetoc-H3 {
padding-left: 40px;
}
#pagetoc .pagetoc-H4 {
padding-left: 60px;
}
}

@media print {
#sidetoc {
display: none;
}
}
104 changes: 104 additions & 0 deletions pagetoc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Inspired by https://github.com/JorelAli/mdBook-pagetoc/tree/98ee241 (under WTFPL)

let activeHref = location.href;
function updatePageToc(elem = undefined) {
let selectedPageTocElem = elem;
const pagetoc = document.getElementById("pagetoc");

function getRect(element) {
return element.getBoundingClientRect();
}

function overflowTop(container, element) {
return getRect(container).top - getRect(element).top;
}

function overflowBottom(container, element) {
return getRect(container).bottom - getRect(element).bottom;
}

// We've not selected a heading to highlight, and the URL needs updating
// so we need to find a heading based on the URL
if (selectedPageTocElem === undefined && location.href !== activeHref) {
activeHref = location.href;
for (const pageTocElement of pagetoc.children) {
if (pageTocElement.href === activeHref) {
selectedPageTocElem = pageTocElement;
}
}
}

// We still don't have a selected heading, let's try and find the most
// suitable heading based on the scroll position
if (selectedPageTocElem === undefined) {
const margin = window.innerHeight / 3;

const headers = document.getElementsByClassName("header");
for (let i = 0; i < headers.length; i++) {
const header = headers[i];
if (selectedPageTocElem === undefined && getRect(header).top >= 0) {
if (getRect(header).top < margin) {
selectedPageTocElem = header;
} else {
selectedPageTocElem = headers[Math.max(0, i - 1)];
}
}
// a very long last section's heading is over the screen
if (selectedPageTocElem === undefined && i === headers.length - 1) {
selectedPageTocElem = header;
}
}
}

// Remove the active flag from all pagetoc elements
for (const pageTocElement of pagetoc.children) {
pageTocElement.classList.remove("active");
}

// If we have a selected heading, set it to active and scroll to it
if (selectedPageTocElem !== undefined) {
for (const pageTocElement of pagetoc.children) {
if (selectedPageTocElem.href.localeCompare(pageTocElement.href) === 0) {
pageTocElement.classList.add("active");
if (overflowTop(pagetoc, pageTocElement) > 0) {
pagetoc.scrollTop = pageTocElement.offsetTop;
}
if (overflowBottom(pagetoc, pageTocElement) < 0) {
pagetoc.scrollTop -= overflowBottom(pagetoc, pageTocElement);
}
}
}
}
}

if (document.getElementById("sidetoc") === null &&
document.getElementsByClassName("header").length > 0) {
// The sidetoc element doesn't exist yet, let's create it

// Create the empty sidetoc and pagetoc elements
const sidetoc = document.createElement("div");
const pagetoc = document.createElement("div");
sidetoc.id = "sidetoc";
pagetoc.id = "pagetoc";
sidetoc.appendChild(pagetoc);

// And append them to the current DOM
const main = document.querySelector('main');
main.insertBefore(sidetoc, main.firstChild);

// Populate sidebar on load
window.addEventListener("load", () => {
for (const header of document.getElementsByClassName("header")) {
const link = document.createElement("a");
link.innerHTML = header.innerHTML;
link.href = header.hash;
link.classList.add("pagetoc-" + header.parentElement.tagName);
document.getElementById("pagetoc").appendChild(link);
link.onclick = () => updatePageToc(link);
}
updatePageToc();
});

// Update page table of contents selected heading on scroll
window.addEventListener("scroll", () => updatePageToc());
}
2 changes: 0 additions & 2 deletions src/asm.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Inline assembly

<!-- toc -->

## Overview

Inline assembly in rustc mostly revolves around taking an `asm!` macro invocation and plumbing it
Expand Down
2 changes: 0 additions & 2 deletions src/backend/backend-agnostic.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Backend Agnostic Codegen

<!-- toc -->

[`rustc_codegen_ssa`]
provides an abstract interface for all backends to implement,
namely LLVM, [Cranelift], and [GCC].
Expand Down
2 changes: 0 additions & 2 deletions src/backend/implicit-caller-location.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Implicit caller location

<!-- toc -->

Approved in [RFC 2091], this feature enables the accurate reporting of caller location during panics
initiated from functions like `Option::unwrap`, `Result::expect`, and `Index::index`. This feature
adds the [`#[track_caller]`][attr-reference] attribute for functions, the
Expand Down
2 changes: 0 additions & 2 deletions src/backend/monomorph.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Monomorphization

<!-- toc -->

As you probably know, Rust has a very expressive type system that has extensive
support for generic types. But of course, assembly is not generic, so we need
to figure out the concrete types of all the generics before the code can
Expand Down
2 changes: 0 additions & 2 deletions src/backend/updating-llvm.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Updating LLVM

<!-- toc -->

<!-- date-check: Aug 2024 -->
Rust supports building against multiple LLVM versions:

Expand Down
2 changes: 0 additions & 2 deletions src/borrow_check/moves_and_initialization/move_paths.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Move paths

<!-- toc -->

In reality, it's not enough to track initialization at the granularity
of local variables. Rust also allows us to do moves and initialization
at the field granularity:
Expand Down
2 changes: 0 additions & 2 deletions src/borrow_check/region_inference.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Region inference (NLL)

<!-- toc -->

The MIR-based region checking code is located in [the `rustc_mir::borrow_check`
module][nll].

Expand Down
2 changes: 0 additions & 2 deletions src/borrow_check/region_inference/constraint_propagation.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Constraint propagation

<!-- toc -->

The main work of the region inference is **constraint propagation**,
which is done in the [`propagate_constraints`] function. There are
three sorts of constraints that are used in NLL, and we'll explain how
Expand Down
2 changes: 0 additions & 2 deletions src/borrow_check/region_inference/lifetime_parameters.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Universal regions

<!-- toc -->

"Universal regions" is the name that the code uses to refer to "named
lifetimes" -- e.g., lifetime parameters and `'static`. The name
derives from the fact that such lifetimes are "universally quantified"
Expand Down
2 changes: 0 additions & 2 deletions src/borrow_check/region_inference/member_constraints.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Member constraints

<!-- toc -->

A member constraint `'m member of ['c_1..'c_N]` expresses that the
region `'m` must be *equal* to some **choice regions** `'c_i` (for
some `i`). These constraints cannot be expressed by users, but they
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Placeholders and universes

<!-- toc -->

From time to time we have to reason about regions that we can't
concretely know. For example, consider this program:

Expand Down
2 changes: 0 additions & 2 deletions src/bug-fix-procedure.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Procedures for breaking changes

<!-- toc -->

This page defines the best practices procedure for making bug fixes or soundness
corrections in the compiler that can cause existing code to stop compiling. This
text is based on
Expand Down
2 changes: 0 additions & 2 deletions src/building/bootstrapping/what-bootstrapping-does.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# What Bootstrapping does

<!-- toc -->

[*Bootstrapping*][boot] is the process of using a compiler to compile itself.
More accurately, it means using an older compiler to compile a newer version of
the same compiler.
Expand Down
2 changes: 0 additions & 2 deletions src/building/how-to-build-and-run.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# How to build and run the compiler

<!-- toc -->

<div class="warning">

For `profile = "library"` users, or users who use `download-rustc = true | "if-unchanged"`, please be advised that
Expand Down
2 changes: 0 additions & 2 deletions src/building/new-target.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ relevant to your desired goal.

See also the associated documentation in the [target tier policy].

<!-- toc -->

[target tier policy]: https://doc.rust-lang.org/rustc/target-tier-policy.html#adding-a-new-target

## Specifying a new LLVM
Expand Down
2 changes: 0 additions & 2 deletions src/building/optimized-build.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Optimized build of the compiler

<!-- toc -->

There are multiple additional build configuration options and techniques that can be used to compile a
build of `rustc` that is as optimized as possible (for example when building `rustc` for a Linux
distribution). The status of these configuration options for various Rust targets is tracked [here].
Expand Down
2 changes: 0 additions & 2 deletions src/building/suggested.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
The full bootstrapping process takes quite a while. Here are some suggestions to
make your life easier.

<!-- toc -->

## Installing a pre-push hook

CI will automatically fail your build if it doesn't pass `tidy`, our internal
Expand Down
Loading