From 53ef53aed9cb43e6719f6f31aec82e05f236cd57 Mon Sep 17 00:00:00 2001 From: binarycat Date: Wed, 2 Apr 2025 12:39:58 -0500 Subject: [PATCH 01/10] settings.js: begin typechecking migration this also makes `changeSetting` more robust in case it somehow gets called before `main.js` has finished loading. --- src/librustdoc/html/static/js/main.js | 3 --- src/librustdoc/html/static/js/rustdoc.d.ts | 2 ++ src/librustdoc/html/static/js/settings.js | 21 +++++++++++++++++---- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 4150c5609a97e..d6f4c2e0fa842 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -1101,7 +1101,6 @@ function preLoadCss(cssUrl) { }); }()); - // @ts-expect-error window.rustdoc_add_line_numbers_to_examples = () => { // @ts-expect-error function generateLine(nb) { @@ -1123,7 +1122,6 @@ function preLoadCss(cssUrl) { }); }; - // @ts-expect-error window.rustdoc_remove_line_numbers_from_examples = () => { onEachLazy( document.querySelectorAll(".rustdoc:not(.src) :not(.scraped-example) > .example-wrap"), @@ -1132,7 +1130,6 @@ function preLoadCss(cssUrl) { }; if (getSettingValue("line-numbers") === "true") { - // @ts-expect-error window.rustdoc_add_line_numbers_to_examples(); } diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index 91a58fab86eff..0465866e3671a 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -78,6 +78,8 @@ declare global { pending_implementors?: rustdoc.Implementors, register_type_impls?: function(rustdoc.TypeImpls): void, pending_type_impls?: rustdoc.TypeImpls, + rustdoc_add_line_numbers_to_examples?: function(), + rustdoc_remove_line_numbers_from_examples?: function(), } interface HTMLElement { /** Used by the popover tooltip code. */ diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index 5f1bbd27328cb..ff21c828611a0 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -4,19 +4,26 @@ /* global MAIN_ID, getVar, getSettingsButton, getHelpButton */ // Eventually fix this. -// @ts-nocheck "use strict"; (function() { const isSettingsPage = window.location.pathname.endsWith("/settings.html"); + /** + * @overload {"theme"|"preferred-dark-theme"|"preferred-light-theme"} + * @param {string} settingName + * @param {string} value + * @returns + * @param {string} settingName + * @param {string|boolean} value + */ function changeSetting(settingName, value) { if (settingName === "theme") { const useSystem = value === "system preference" ? "true" : "false"; updateLocalStorage("use-system-theme", useSystem); } - updateLocalStorage(settingName, value); + updateLocalStorage(settingName, "" + value); switch (settingName) { case "theme": @@ -27,9 +34,15 @@ break; case "line-numbers": if (value === true) { - window.rustdoc_add_line_numbers_to_examples(); + const f = window.rustdoc_add_line_numbers_to_examples; + if (f !== undefined) { + f(); + } } else { - window.rustdoc_remove_line_numbers_from_examples(); + const f = window.rustdoc_remove_line_numbers_from_examples; + if (f !== undefined) { + f(); + } } break; case "hide-sidebar": From 6ca7bd041b2da04274d019f4dd001f5cfc51e78a Mon Sep 17 00:00:00 2001 From: binarycat Date: Wed, 2 Apr 2025 13:06:17 -0500 Subject: [PATCH 02/10] settings.js: add new rustdoc.Setting interface --- src/librustdoc/html/static/js/rustdoc.d.ts | 10 +++++ src/librustdoc/html/static/js/settings.js | 43 +++++++++++++--------- 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index 0465866e3671a..1b28fcb19defc 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -479,4 +479,14 @@ declare namespace rustdoc { * is a tuple of (filename, subdirs, filenames). */ type Dir = [string, rustdoc.Dir[], string[]] + + /** + * Indivitual setting object, used in `settings.js` + */ + interface Setting { + js_name: string, + name: string, + options?: string[], + default: string | boolean, + } } diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index ff21c828611a0..694a26cdac200 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -102,6 +102,9 @@ } } + /** + * @param {HTMLElement} settingsElement + */ function setEvents(settingsElement) { updateLightAndDark(); onEachLazy(settingsElement.querySelectorAll("input[type=\"checkbox\"]"), toggle => { @@ -114,23 +117,27 @@ changeSetting(toggle.id, toggle.checked); }; }); - onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"), elem => { - const settingId = elem.name; - let settingValue = getSettingValue(settingId); - if (settingId === "theme") { - const useSystem = getSettingValue("use-system-theme"); - if (useSystem === "true" || settingValue === null) { - // "light" is the default theme - settingValue = useSystem === "false" ? "light" : "system preference"; + onEachLazy( + settingsElement.querySelectorAll("input[type=\"radio\"]"), + /** @param {HTMLInputElement} elem */ + elem => { + const settingId = elem.name; + let settingValue = getSettingValue(settingId); + if (settingId === "theme") { + const useSystem = getSettingValue("use-system-theme"); + if (useSystem === "true" || settingValue === null) { + // "light" is the default theme + settingValue = useSystem === "false" ? "light" : "system preference"; + } } + if (settingValue !== null && settingValue !== "null") { + elem.checked = settingValue === elem.value; + } + elem.addEventListener("change", ev => { + changeSetting(elem.name, elem.value); + }); } - if (settingValue !== null && settingValue !== "null") { - elem.checked = settingValue === elem.value; - } - elem.addEventListener("change", ev => { - changeSetting(ev.target.name, ev.target.value); - }); - }); + ); } /** @@ -138,7 +145,7 @@ * as argument which describes each setting and how to render it. It returns a string * representing the raw HTML. * - * @param {Array} settings + * @param {Array} settings * * @return {string} */ @@ -195,7 +202,9 @@ * @return {HTMLElement} */ function buildSettingsPage() { - const theme_names = getVar("themes").split(",").filter(t => t); + const theme_list = getVar("themes") + const theme_names = (theme_list === null ? "" : theme_list) + .split(",").filter(t => t); theme_names.push("light", "dark", "ayu"); const settings = [ From f05683639d64dc26e51f63d97a0ba3cd812dc374 Mon Sep 17 00:00:00 2001 From: binarycat Date: Wed, 2 Apr 2025 13:08:05 -0500 Subject: [PATCH 03/10] settings.js: remove dead code for adding
to settings page --- src/librustdoc/html/static/js/settings.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index 694a26cdac200..5e706e284928d 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -153,11 +153,6 @@ let output = ""; for (const setting of settings) { - if (setting === "hr") { - output += "
"; - continue; - } - const js_data_name = setting["js_name"]; const setting_name = setting["name"]; From 9f8d3d0bc9057ecc9de715a5cf65d97dc67da766 Mon Sep 17 00:00:00 2001 From: binarycat Date: Wed, 2 Apr 2025 13:09:49 -0500 Subject: [PATCH 04/10] settings.js: refactor settingsBlurHandler changes: * Add type signature * Add null checks * getHelpButton and getSettingsButton are only called once, which should marginally improve performance due to less queries. unfortunatly 2 @ts-expect-error was needed, as typescript is unaware the EventTarget is likely an Element. --- src/librustdoc/html/static/js/settings.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index 5e706e284928d..f61673c9088ea 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -310,12 +310,21 @@ }); } + /** + * @param {MouseEvent} event + */ function settingsBlurHandler(event) { - if (!getHelpButton().contains(document.activeElement) && - !getHelpButton().contains(event.relatedTarget) && - !getSettingsButton().contains(document.activeElement) && - !getSettingsButton().contains(event.relatedTarget) - ) { + const helpBtn = getHelpButton(); + const settingsBtn = getSettingsButton(); + const helpUnfocused = helpBtn === null || + (!helpBtn.contains(document.activeElement) && + // @ts-expect-error + !helpBtn.contains(event.relatedTarget)); + const settingsUnfocused = settingsBtn === null || + (!settingsBtn.contains(document.activeElement) && + // @ts-expect-error + !settingsBtn.contains(event.relatedTarget)); + if (helpUnfocused && settingsUnfocused) { window.hidePopoverMenus(); } } From d2ff26bbe696e3aa8f6c92de787d8de4c68d7a5e Mon Sep 17 00:00:00 2001 From: binarycat Date: Wed, 2 Apr 2025 13:24:55 -0500 Subject: [PATCH 05/10] settings.js: add some null checks to buildSettingsPage --- src/librustdoc/html/static/js/settings.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index f61673c9088ea..ea02e01dccf7b 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -289,10 +289,16 @@ el.innerHTML = innerHTML; if (isSettingsPage) { - document.getElementById(MAIN_ID).appendChild(el); + const mainElem = document.getElementById(MAIN_ID); + if (mainElem !== null) { + mainElem.appendChild(el); + } } else { el.setAttribute("tabindex", "-1"); - getSettingsButton().appendChild(el); + const settingsBtn = getSettingsButton(); + if (settingsBtn !== null) { + settingsBtn.appendChild(el); + } } return el; } From a805f5253e96dd1746cd929fd172a3e8391c6517 Mon Sep 17 00:00:00 2001 From: binarycat Date: Wed, 2 Apr 2025 13:45:35 -0500 Subject: [PATCH 06/10] settings.js: make top-level code typecheck With this, almost the entire file is fully typechecked, the only exception being the Element.contains(EventTarget) pattern that is used several times, those are annotated with @ts-expect-error --- src/librustdoc/html/static/js/main.js | 2 +- src/librustdoc/html/static/js/rustdoc.d.ts | 2 ++ src/librustdoc/html/static/js/settings.js | 18 ++++++++++-------- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index d6f4c2e0fa842..754eb90b4723a 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -1,6 +1,6 @@ // Local js definitions: /* global addClass, getSettingValue, hasClass, updateLocalStorage */ -/* global onEachLazy, removeClass, getVar */ +/* global onEachLazy, removeClass, getVar, nonnull */ "use strict"; diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index 1b28fcb19defc..0d2e19e019f34 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -30,6 +30,8 @@ declare global { currentCrate: string|null; /** * Hide popovers, tooltips, or the mobile sidebar. + * + * Pass `true` to reset focus for tooltip popovers. */ hideAllModals: function(boolean), /** diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index ea02e01dccf7b..4facb45ae38ef 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -1,9 +1,7 @@ // Local js definitions: /* global getSettingValue, updateLocalStorage, updateTheme */ /* global addClass, removeClass, onEach, onEachLazy */ -/* global MAIN_ID, getVar, getSettingsButton, getHelpButton */ - -// Eventually fix this. +/* global MAIN_ID, getVar, getSettingsButton, getHelpButton, nonnull */ "use strict"; @@ -317,7 +315,7 @@ } /** - * @param {MouseEvent} event + * @param {FocusEvent} event */ function settingsBlurHandler(event) { const helpBtn = getHelpButton(); @@ -337,22 +335,26 @@ if (!isSettingsPage) { // We replace the existing "onclick" callback. - const settingsButton = getSettingsButton(); - const settingsMenu = document.getElementById("settings"); + // These elements must exist, as (outside of the settings page) + // `settings.js` is only loaded after the settings button is clicked. + const settingsButton = nonnull(getSettingsButton()); + const settingsMenu = nonnull(document.getElementById("settings")); settingsButton.onclick = event => { + // @ts-expect-error if (settingsMenu.contains(event.target)) { return; } event.preventDefault(); const shouldDisplaySettings = settingsMenu.style.display === "none"; - window.hideAllModals(); + window.hideAllModals(false); if (shouldDisplaySettings) { displaySettings(); } }; settingsButton.onblur = settingsBlurHandler; - settingsButton.querySelector("a").onblur = settingsBlurHandler; + // the settings button should always have a link in it + nonnull(settingsButton.querySelector("a")).onblur = settingsBlurHandler; onEachLazy(settingsMenu.querySelectorAll("input"), el => { el.onblur = settingsBlurHandler; }); From 8a341298ec1c6a029bda1cc9f0ec696e9444caa9 Mon Sep 17 00:00:00 2001 From: binarycat Date: Wed, 2 Apr 2025 13:48:05 -0500 Subject: [PATCH 07/10] main.js: make hideAllModals docs clearer "true" looks like a string value, `true` makes it more clear that we are talking about the actual boolean value. --- src/librustdoc/html/static/js/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 754eb90b4723a..9902918fe5e70 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -1593,7 +1593,7 @@ function preLoadCss(cssUrl) { /** * Hide popover menus, clickable tooltips, and the sidebar (if applicable). * - * Pass "true" to reset focus for tooltip popovers. + * Pass `true` to reset focus for tooltip popovers. */ window.hideAllModals = switchFocus => { hideSidebar(); From 9f54910f4ba9d1183c5246ec16f0a35183772db2 Mon Sep 17 00:00:00 2001 From: binarycat Date: Wed, 2 Apr 2025 13:56:31 -0500 Subject: [PATCH 08/10] settings.js: add elemContainsTarget utility function this allows us to eliminate the last 3 @ts-expect-error in this file. --- src/librustdoc/html/static/js/settings.js | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index 4facb45ae38ef..d82f5f5463345 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -8,6 +8,18 @@ (function() { const isSettingsPage = window.location.pathname.endsWith("/settings.html"); + /** + * @param {Element} elem + * @param {EventTarget|null} target + */ + function elemContainsTarget(elem, target) { + if (target instanceof Node) { + return elem.contains(target); + } else { + return false; + } + } + /** * @overload {"theme"|"preferred-dark-theme"|"preferred-light-theme"} * @param {string} settingName @@ -322,12 +334,10 @@ const settingsBtn = getSettingsButton(); const helpUnfocused = helpBtn === null || (!helpBtn.contains(document.activeElement) && - // @ts-expect-error - !helpBtn.contains(event.relatedTarget)); + !elemContainsTarget(helpBtn, event.relatedTarget)); const settingsUnfocused = settingsBtn === null || (!settingsBtn.contains(document.activeElement) && - // @ts-expect-error - !settingsBtn.contains(event.relatedTarget)); + !elemContainsTarget(settingsBtn, event.relatedTarget)); if (helpUnfocused && settingsUnfocused) { window.hidePopoverMenus(); } @@ -340,8 +350,7 @@ const settingsButton = nonnull(getSettingsButton()); const settingsMenu = nonnull(document.getElementById("settings")); settingsButton.onclick = event => { - // @ts-expect-error - if (settingsMenu.contains(event.target)) { + if (elemContainsTarget(settingsMenu, event.target)) { return; } event.preventDefault(); From ef1827b97452f697c28d028f94148463606b2fd8 Mon Sep 17 00:00:00 2001 From: binarycat Date: Wed, 2 Apr 2025 15:24:38 -0500 Subject: [PATCH 09/10] settings.js: fix whitespace --- src/librustdoc/html/static/js/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index d82f5f5463345..2c051d0025cb5 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -19,7 +19,7 @@ return false; } } - + /** * @overload {"theme"|"preferred-dark-theme"|"preferred-light-theme"} * @param {string} settingName From 4435c999db7a4affa81662d1d90a75595da94b78 Mon Sep 17 00:00:00 2001 From: binarycat Date: Wed, 2 Apr 2025 16:36:08 -0500 Subject: [PATCH 10/10] rustdoc js: satisfy eslint --- src/librustdoc/html/static/js/main.js | 2 +- src/librustdoc/html/static/js/settings.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 9902918fe5e70..a7ce2bf9048bf 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -1,6 +1,6 @@ // Local js definitions: /* global addClass, getSettingValue, hasClass, updateLocalStorage */ -/* global onEachLazy, removeClass, getVar, nonnull */ +/* global onEachLazy, removeClass, getVar */ "use strict"; diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index 2c051d0025cb5..2430b5829b2ba 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -143,10 +143,10 @@ if (settingValue !== null && settingValue !== "null") { elem.checked = settingValue === elem.value; } - elem.addEventListener("change", ev => { + elem.addEventListener("change", () => { changeSetting(elem.name, elem.value); }); - } + }, ); } @@ -207,7 +207,7 @@ * @return {HTMLElement} */ function buildSettingsPage() { - const theme_list = getVar("themes") + const theme_list = getVar("themes"); const theme_names = (theme_list === null ? "" : theme_list) .split(",").filter(t => t); theme_names.push("light", "dark", "ayu");