Skip to content

Add per page TOC in the rustc book #140113

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 3 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
2 changes: 2 additions & 0 deletions src/doc/rustc/book.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ title = "The rustc book"
[output.html]
git-repository-url = "https://github.com/rust-lang/rust/tree/master/src/doc/rustc"
edit-url-template = "https://github.com/rust-lang/rust/edit/master/src/doc/rustc/{path}"
additional-css = ["theme/pagetoc.css"]
additional-js = ["theme/pagetoc.js"]

[output.html.search]
use-boolean-and = true
Expand Down
84 changes: 84 additions & 0 deletions src/doc/rustc/theme/pagetoc.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/* Inspired by https://github.com/JorelAli/mdBook-pagetoc/ */
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidtwco @wesleywiser, legal question here, pagetoc.css and pagetoc.js are both heavily inspired by https://github.com/JorelAli/mdBook-pagetoc/ which is licensed under the "WTFPL" license (Do What The F*ck You Want To Public License).

Is it okay for us to re-license those files under our dual license MIT/Apache-2?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will start a thread with the Foundation to see what they think.


: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(--sidebar-fg) !important;
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 src/doc/rustc/theme/pagetoc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Inspired by https://github.com/JorelAli/mdBook-pagetoc/

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());
}
Loading