Skip to content

Commit

Permalink
Merge pull request #7189 from jfkonecn/search-by-signature
Browse files Browse the repository at this point in the history
Added search by signature in docs
  • Loading branch information
lukewilliamboswell authored Oct 27, 2024
2 parents 8903d63 + 3974c9a commit 589ec1d
Show file tree
Hide file tree
Showing 4 changed files with 243 additions and 77 deletions.
70 changes: 70 additions & 0 deletions crates/docs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ pub fn generate_docs_html(root_file: PathBuf, build_dir: &Path) {
.replace(
"<!-- Module links -->",
render_sidebar(exposed_module_docs.iter().map(|(_, docs)| docs)).as_str(),
)
.replace(
"<!-- Search Type Ahead -->",
render_search_type_ahead(exposed_module_docs.iter().map(|(_, docs)| docs)).as_str(),
);

let all_exposed_symbols = {
Expand Down Expand Up @@ -469,6 +473,72 @@ fn render_sidebar<'a, I: Iterator<Item = &'a ModuleDocumentation>>(modules: I) -
buf
}

fn render_search_type_ahead<'a, I: Iterator<Item = &'a ModuleDocumentation>>(modules: I) -> String {
let mut buf = String::new();
for module in modules {
let module_name = module.name.as_str();
for entry in &module.entries {
if let DocEntry::DocDef(doc_def) = entry {
if module.exposed_symbols.contains(&doc_def.symbol) {
let mut entry_contents_buf = String::new();
push_html(
&mut entry_contents_buf,
"p",
vec![("class", "type-ahead-def-name")],
doc_def.name.as_str(),
);

let mut entry_signature_buf = String::new();
type_annotation_to_html(
0,
&mut entry_signature_buf,
&doc_def.type_annotation,
false,
);

push_html(
&mut entry_contents_buf,
"p",
vec![("class", "type-ahead-signature")],
entry_signature_buf.as_str(),
);

push_html(
&mut entry_contents_buf,
"p",
vec![("class", "type-ahead-doc-path")],
format!("{} > {}", module_name, doc_def.name),
);

let mut entry_href = String::new();

entry_href.push_str(module_name);
entry_href.push('#');
entry_href.push_str(doc_def.name.as_str());

let mut anchor_buf = String::new();

push_html(
&mut anchor_buf,
"a",
vec![("href", entry_href.as_str()), ("class", "type-ahead-link")],
entry_contents_buf.as_str(),
);

push_html(
&mut buf,
"li",
vec![("role", "option")],
anchor_buf.as_str(),
);
}
}
}
}

buf
}

pub fn load_module_for_docs(filename: PathBuf) -> LoadedModule {
let arena = Bump::new();
let load_config = LoadConfig {
Expand Down
20 changes: 17 additions & 3 deletions crates/docs/src/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@

<body>
<nav id="sidebar-nav">
<input id="module-search" aria-labelledby="search-link" type="text" placeholder="Search" />
<label for="module-search" id="search-link"><span id="search-link-text">Search</span> <span
id="search-link-hint">(press <span id="search-shortcut-key">s</span>)</span></label>
<div class="module-links">
<!-- Module links -->
</div>
Expand All @@ -41,6 +38,23 @@
</a>
<!-- Package Name -->
</div>
<form id="module-search-form">
<input
id="module-search"
aria-labelledby="search-link"
type="text"
placeholder="Search"
role="combobox"
aria-autocomplete="list"
aria-expanded="false"
aria-controls="search-type-ahead"
/>
<label for="module-search" id="search-link">Search (press s)</label>
<span id="search-shortcut-key" aria-hidden="true">s</span>
<ul id="search-type-ahead" role="listbox" aria-label="Search Results" class="hidden">
<!-- Search Type Ahead -->
</ul>
</form>
<div class="top-header-triangle">
<!-- if the window gets big, this extends the purple bar on the top header to the left edge of the window -->
</div>
Expand Down
118 changes: 72 additions & 46 deletions crates/docs/src/static/search.js
Original file line number Diff line number Diff line change
@@ -1,69 +1,88 @@
(() => {

let sidebar = document.getElementById("sidebar-nav");
// Un-hide everything
sidebar
.querySelectorAll(".sidebar-entry a")
.forEach((entry) => entry.classList.remove("hidden"));

// Re-hide all the sub-entries except for those of the current module
let currentModuleName = document.querySelector(".module-name").textContent;

sidebar.querySelectorAll(".sidebar-entry").forEach((entry) => {
let entryName = entry.querySelector(".sidebar-module-link").textContent;
if (currentModuleName === entryName) {
entry.firstChild.classList.add("active");
return;
}
entry
.querySelectorAll(".sidebar-sub-entries a")
.forEach((subEntry) => subEntry.classList.add("hidden"));
});

let searchTypeAhead = document.getElementById("search-type-ahead");
let searchBox = document.getElementById("module-search");
let searchForm = document.getElementById("module-search-form");
let topSearchResultListItem = undefined;

if (searchBox != null) {
function search() {
topSearchResultListItem = undefined;
let text = searchBox.value.toLowerCase(); // Search is case-insensitive.

if (text === "") {
// Un-hide everything
sidebar
.querySelectorAll(".sidebar-entry a")
.forEach((entry) => entry.classList.remove("hidden"));

// Re-hide all the sub-entries except for those of the current module
let currentModuleName =
document.querySelector(".module-name").textContent;

sidebar.querySelectorAll(".sidebar-entry").forEach((entry) => {
let entryName = entry.querySelector(
".sidebar-module-link"
).textContent;
if (currentModuleName === entryName) {
entry.firstChild.classList.add("active");
return;
}
entry
.querySelectorAll(".sidebar-sub-entries a")
.forEach((subEntry) =>
subEntry.classList.add("hidden")
);
});
} else {
// First, show/hide all the sub-entries within each module (top-level functions etc.)
sidebar
.querySelectorAll(".sidebar-sub-entries a")
.forEach((entry) => {
if (entry.textContent.toLowerCase().includes(text)) {
entry.classList.remove("hidden");
} else {
entry.classList.add("hidden");
}
});
if (text === "") {
searchTypeAhead.classList.add("hidden");
} else {
let totalResults = 0;
// Firsttype-ahead-signature", show/hide all the sub-entries within each module (top-level functions etc.)
searchTypeAhead.querySelectorAll("li").forEach((entry) => {
const entryName = entry
.querySelector(".type-ahead-def-name")
.textContent.toLowerCase();
const entrySignature = entry
.querySelector(".type-ahead-signature")
.textContent.toLowerCase()
.replace(/\s+/g, "");

// Then, show/hide modules based on whether they match, or any of their sub-entries matched
sidebar
.querySelectorAll(".sidebar-module-link")
.forEach((entry) => {
if (
entry.textContent.toLowerCase().includes(text) ||
entry.parentNode.querySelectorAll(
".sidebar-sub-entries a:not(.hidden)"
).length > 0
totalResults < 5 &&
(entryName.includes(text) ||
entrySignature.includes(text.replace(/\s+/g, "")))
) {
totalResults++;
entry.classList.remove("hidden");
if (topSearchResultListItem === undefined) {
topSearchResultListItem = entry;
}
} else {
entry.classList.add("hidden");
}
});
}
if (totalResults < 1) {
searchTypeAhead.classList.add("hidden");
} else {
searchTypeAhead.classList.remove("hidden");
}
}
}

searchBox.addEventListener("input", search);

search();

function searchSubmit(e) {
// pick the top result if the user submits search form
e.preventDefault();
if (topSearchResultListItem !== undefined) {
let topSearchResultListItemAnchor =
topSearchResultListItem.querySelector("a");
if (topSearchResultListItemAnchor !== null) {
topSearchResultListItemAnchor.click();
}
}
}
searchForm.addEventListener("submit", searchSubmit);

// Capture '/' keypress for quick search
window.addEventListener("keyup", (e) => {
if (e.key === "s" && document.activeElement !== searchBox) {
Expand All @@ -77,13 +96,20 @@

// De-focus input box
searchBox.blur();
} else if (
e.key === "Escape" &&
searchTypeAhead.contains(document.activeElement)
) {
e.preventDefault;

// Reset sidebar state
search();
// De-focus type ahead
searchBox.focus();
searchBox.blur();
}
});
}


const isTouchSupported = () => {
try {
document.createEvent("TouchEvent");
Expand Down
Loading

0 comments on commit 589ec1d

Please sign in to comment.