Skip to content
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
96 changes: 47 additions & 49 deletions src/librustdoc/html/static/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,32 @@ function preLoadCss(cssUrl) {
window.searchState = {
rustdocToolbar: document.querySelector("rustdoc-toolbar"),
loadingText: "Loading search results...",
searchLoaded: false,
loadSearch() {
// If you're browsing the nightly docs, the page might need to be refreshed for
// the search to work because the hash of the JS scripts might have changed.
Comment on lines +250 to +251

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: technically this doesn't just apply to nightly, it's just way more common there

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I copied comment from here. But yeah agreed.

function sendSearchForm() {
// @ts-expect-error
document.getElementsByClassName("search-form")[0].submit();
}
if (!window.searchState.searchLoaded) {
window.searchState.searchLoaded = true;
window.rr_ = data => {
window.searchIndex = data;
};
if (!window.StringdexOnload) {
window.StringdexOnload = [];
}
window.StringdexOnload.push(() => {
loadScript(
getVar("static-root-path") + getVar("search-js"),
sendSearchForm,
);
});
Comment on lines +261 to +269

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

the structure of this doesn't make a ton of sense to me in combination, shouldn't we just be initializing it to an array with a single element? otherwise we could hypothetically end up with an array of duplicate initializers?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Just moved existing code, didn't modify anything. I think it's because you can have multiple search indexes loaded in parallel.

loadScript(getVar("static-root-path") + getVar("stringdex-js"), sendSearchForm);
loadScript(resourcePath("search.index/root", ".js"), sendSearchForm);
}
},
inputElement: () => {
let el = document.getElementsByClassName("search-input")[0];
if (!el) {
Expand All @@ -269,6 +295,10 @@ function preLoadCss(cssUrl) {
</nav><div class="search-switcher"></div>`;
out.insertBefore(hdr, window.searchState.outputElement());
el = document.getElementsByClassName("search-input")[0];

el.addEventListener("focus", () => {
window.searchState.loadSearch();
});
}
if (el instanceof HTMLInputElement) {
return el;
Expand Down Expand Up @@ -391,41 +421,6 @@ function preLoadCss(cssUrl) {
return params;
},
setup: () => {
let searchLoaded = false;
const search_input = window.searchState.inputElement();
if (!search_input) {
return;
}
// If you're browsing the nightly docs, the page might need to be refreshed for the
// search to work because the hash of the JS scripts might have changed.
function sendSearchForm() {
// @ts-expect-error
document.getElementsByClassName("search-form")[0].submit();
}
function loadSearch() {
if (!searchLoaded) {
searchLoaded = true;
window.rr_ = data => {
window.searchIndex = data;
};
if (!window.StringdexOnload) {
window.StringdexOnload = [];
}
window.StringdexOnload.push(() => {
loadScript(
getVar("static-root-path") + getVar("search-js"),
sendSearchForm,
);
});
loadScript(getVar("static-root-path") + getVar("stringdex-js"), sendSearchForm);
loadScript(resourcePath("search.index/root", ".js"), sendSearchForm);
}
}

search_input.addEventListener("focus", () => {
loadSearch();
});

const btn = document.getElementById("search-button");
if (btn) {
btn.onclick = event => {
Expand All @@ -434,7 +429,7 @@ function preLoadCss(cssUrl) {
}
event.preventDefault();
window.searchState.toggle();
loadSearch();
window.searchState.loadSearch();
};
}

Expand All @@ -455,7 +450,7 @@ function preLoadCss(cssUrl) {
// previous state with nothing in the bar.
const inputElement = window.searchState.inputElement();
if (params.search !== undefined && inputElement !== null) {
loadSearch();
window.searchState.loadSearch();
inputElement.value = params.search;
// Some browsers fire "onpopstate" for every page load
// (Chrome), while others fire the event only when actually
Expand All @@ -482,29 +477,32 @@ function preLoadCss(cssUrl) {
// that try to sync state between the URL and the search input. To work around it,
// do a small amount of re-init on page show.
window.onpageshow = () => {
const inputElement = window.searchState.inputElement();
const qSearch = window.searchState.getQueryStringParams().search;
if (qSearch !== undefined && inputElement !== null) {
if (inputElement.value === "") {
inputElement.value = qSearch;
}
window.searchState.showResults();
if (qSearch === "") {
loadSearch();
window.searchState.focus();
if (qSearch !== undefined) {
const inputElement = window.searchState.inputElement();
if (inputElement !== null) {
if (inputElement.value === "") {
inputElement.value = qSearch;
}
window.searchState.showResults();
if (qSearch === "") {
window.searchState.loadSearch();
window.searchState.focus();
}
}
} else {
window.searchState.hideResults();
}
};

const params = window.searchState.getQueryStringParams();
if (params.search !== undefined) {
window.searchState.setLoadingSearch();
loadSearch();
window.searchState.loadSearch();
}
},
setLoadingSearch: () => {
// We set up the search input before adding the other search elements (like
// "search loading") in case it's not already there yet.
window.searchState.inputElement();
const search = window.searchState.outputElement();
nonnull(search).innerHTML = "<h3 class=\"search-loading\">" +
window.searchState.loadingText + "</h3>";
Expand Down
2 changes: 2 additions & 0 deletions src/librustdoc/html/static/js/rustdoc.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ declare namespace rustdoc {
loadDesc: function({descShard: SearchDescShard, descIndex: number}): Promise<string|null>;
loadedDescShard: function(string, number, string);
isDisplayed: function(): boolean;
searchLoaded: boolean;
loadSearch: function();
}

interface SearchDescShard {
Expand Down
2 changes: 2 additions & 0 deletions tests/rustdoc-gui/go-to-collapsed-elem.goml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// This test ensures that when clicking on a link which leads to an item inside a collapsed element,
// the collapsed element will be expanded.
go-to: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
include: "utils.goml"
// We check that the implementors block is expanded.
assert-property: ("#implementations-list .implementors-toggle", {"open": "true"})
// We now collapse the implementors block.
Expand All @@ -16,6 +17,7 @@ define-function: ("collapsed-from-search", [], block {
// Then we collapse the section again...
set-property: ("#implementations-list .implementors-toggle", {"open": "false"})
// Then we run the search.
call-function: ("open-search", {})

@GuillaumeGomez GuillaumeGomez Aug 6, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Ah that was my original "quest": looking at fixing the new flakyness which happened in #159593 (comment). ^^'

View changes since the review

write-into: (".search-input", "foo::must_use")
wait-for: "//*[@id='search']//a[@href='../test_docs/struct.Foo.html#method.must_use']"
click: "//*[@id='search']//a[@href='../test_docs/struct.Foo.html#method.must_use']"
Expand Down
15 changes: 15 additions & 0 deletions tests/rustdoc-gui/search-elements.goml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// This test ensures that the search elements are not created in the DOM before being needed.

go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
store-value: (search_selector, "#search")

// This selector is not supposed to exist yet.
assert-false: |search_selector|

// It should be generated when the search "begins".
click: "#search-button"
wait-for: |search_selector|

// When we arrive on a page with a search query parameter, the element should also be present.
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=a"
wait-for: |search_selector|
9 changes: 6 additions & 3 deletions tests/rustdoc-gui/sidebar-source-code-display.goml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ define-function: (
"#src-sidebar details[open] > .files a:not(.selected):focus",
{"color": |color_hover|, "background-color": |background_hover|},
)
focus: ".search-input"
// Focus another element to remove focus from the <a> element.
focus: "#search-button"
Comment thread
GuillaumeGomez marked this conversation as resolved.
// With hover.
move-cursor-to: "#src-sidebar details[open] > .files a:not(.selected)"
assert-css: (
Expand All @@ -72,7 +73,8 @@ define-function: (
"#src-sidebar .dir-entry summary:focus",
{"color": |color_hover|, "background-color": |background_hover|},
)
focus: ".search-input"
// Focus another element to remove focus from the <summary> element.
focus: "#search-button"
// With hover.
move-cursor-to: "#src-sidebar .dir-entry summary"
assert-css: (
Expand All @@ -91,7 +93,8 @@ define-function: (
"#src-sidebar details[open] > .folders > details > summary:focus",
{"color": |color_hover|, "background-color": |background_hover|},
)
focus: ".search-input"
// Focus another element to remove focus from the <summary> element.
focus: "#search-button"
// With hover.
move-cursor-to: "#src-sidebar details[open] > .folders > details > summary"
assert-css: (
Expand Down
Loading