diff --git a/DocGen4/Output.lean b/DocGen4/Output.lean
index 9d79088..ef495f5 100644
--- a/DocGen4/Output.lean
+++ b/DocGen4/Output.lean
@@ -12,6 +12,7 @@ import DocGen4.Output.Module
import DocGen4.Output.NotFound
import DocGen4.Output.Find
import DocGen4.Output.SourceLinker
+import DocGen4.Output.Search
import DocGen4.Output.ToJson
import DocGen4.Output.FoundationalTypes
import DocGen4.LeanInk.Process
@@ -35,11 +36,13 @@ def htmlOutputSetup (config : SiteBaseContext) : IO Unit := do
let notFoundHtml := ReaderT.run notFound config |>.toString
let foundationalTypesHtml := ReaderT.run foundationalTypes config |>.toString
let navbarHtml := ReaderT.run navbar config |>.toString
+ let searchHtml := ReaderT.run search config |>.toString
let docGenStatic := #[
("style.css", styleCss),
("declaration-data.js", declarationDataCenterJs),
("nav.js", navJs),
("how-about.js", howAboutJs),
+ ("search.html", searchHtml),
("search.js", searchJs),
("mathjax-config.js", mathjaxConfigJs),
("instances.js", instancesJs),
diff --git a/DocGen4/Output/Search.lean b/DocGen4/Output/Search.lean
new file mode 100644
index 0000000..b748c7e
--- /dev/null
+++ b/DocGen4/Output/Search.lean
@@ -0,0 +1,28 @@
+/-
+Copyright (c) 2023 Jeremy Salwen. All rights reserved.
+Released under Apache 2.0 license as described in the file LICENSE.
+Authors: Jeremy Salwen
+-/
+import DocGen4.Output.ToHtmlFormat
+import DocGen4.Output.Template
+
+namespace DocGen4
+namespace Output
+
+open scoped DocGen4.Jsx
+
+def search : BaseHtmlM Html := do templateExtends (baseHtml "Search") <|
+ pure <|
+
+
+ Search Results
+
+
+
+
+
+
+end Output
+end DocGen4
diff --git a/DocGen4/Output/Template.lean b/DocGen4/Output/Template.lean
index 2c7ea8a..f2b7409 100644
--- a/DocGen4/Output/Template.lean
+++ b/DocGen4/Output/Template.lean
@@ -45,10 +45,10 @@ def baseHtmlGenerator (title : String) (site : Array Html) : BaseHtmlM Html := d
Documentation
- -- TODO: Replace this form with our own search
diff --git a/DocGen4/Process/Hierarchy.lean b/DocGen4/Process/Hierarchy.lean
index 01b3ba0..86d2be0 100644
--- a/DocGen4/Process/Hierarchy.lean
+++ b/DocGen4/Process/Hierarchy.lean
@@ -89,6 +89,7 @@ def baseDirBlackList : HashSet String :=
"find",
"how-about.js",
"index.html",
+ "search.html",
"foundational_types.html",
"mathjax-config.js",
"navbar.html",
diff --git a/lakefile.lean b/lakefile.lean
index 9a8b32f..f558b8b 100644
--- a/lakefile.lean
+++ b/lakefile.lean
@@ -94,6 +94,7 @@ library_facet docs (lib) : FilePath := do
basePath / "index.html",
basePath / "404.html",
basePath / "navbar.html",
+ basePath / "search.html",
basePath / "find" / "index.html",
basePath / "find" / "find.js",
basePath / "src" / "alectryon.css",
diff --git a/static/declaration-data.js b/static/declaration-data.js
index 953edf2..8c4e42c 100644
--- a/static/declaration-data.js
+++ b/static/declaration-data.js
@@ -67,7 +67,7 @@ export class DeclarationDataCenter {
* Search for a declaration.
* @returns {Array}
*/
- search(pattern, strict = true) {
+ search(pattern, strict = true, maxResults=undefined) {
if (!pattern) {
return [];
}
@@ -75,7 +75,7 @@ export class DeclarationDataCenter {
let decl = this.declarationData.declarations[pattern];
return decl ? [decl] : [];
} else {
- return getMatches(this.declarationData.declarations, pattern);
+ return getMatches(this.declarationData.declarations, pattern, maxResults);
}
}
@@ -159,7 +159,7 @@ function matchCaseSensitive(declName, lowerDeclName, pattern) {
}
}
-function getMatches(declarations, pattern, maxResults = 30) {
+function getMatches(declarations, pattern, maxResults = undefined) {
const lowerPats = pattern.toLowerCase().split(/\s/g);
const patNoSpaces = pattern.replace(/\s/g, "");
const results = [];
diff --git a/static/search.js b/static/search.js
index 074d8bc..835bfca 100644
--- a/static/search.js
+++ b/static/search.js
@@ -4,27 +4,36 @@
import { DeclarationDataCenter } from "./declaration-data.js";
+// Search form and input in the upper right toolbar
const SEARCH_FORM = document.querySelector("#search_form");
const SEARCH_INPUT = SEARCH_FORM.querySelector("input[name=q]");
-// Create an `div#search_results` to hold all search results.
-let sr = document.createElement("div");
-sr.id = "search_results";
-SEARCH_FORM.appendChild(sr);
+// Search form on the /search.html_page. These may be null.
+const SEARCH_PAGE_INPUT = document.querySelector("#search_page_query")
+const SEARCH_RESULTS = document.querySelector("#search_results")
+
+// Max results to show for autocomplete or /search.html page.
+const AC_MAX_RESULTS = 30
+const SEARCH_PAGE_MAX_RESULTS = undefined
+
+// Create an `div#autocomplete_results` to hold all autocomplete results.
+let ac_results = document.createElement("div");
+ac_results.id = "autocomplete_results";
+SEARCH_FORM.appendChild(ac_results);
/**
- * Attach `selected` class to the the selected search result.
+ * Attach `selected` class to the the selected autocomplete result.
*/
function handleSearchCursorUpDown(down) {
- const sel = sr.querySelector(`.selected`);
+ const sel = ac_results.querySelector(`.selected`);
if (sel) {
sel.classList.remove("selected");
const toSelect = down
- ? sel.nextSibling || sr.firstChild
- : sel.previousSibling || sr.lastChild;
+ ? sel.nextSibling || ac_results.firstChild
+ : sel.previousSibling || ac_results.lastChild;
toSelect && toSelect.classList.add("selected");
} else {
- const toSelect = down ? sr.firstChild : sr.lastChild;
+ const toSelect = down ? ac_results.firstChild : ac_results.lastChild;
toSelect && toSelect.classList.add("selected");
}
}
@@ -33,12 +42,12 @@ function handleSearchCursorUpDown(down) {
* Perform search (when enter is pressed).
*/
function handleSearchEnter() {
- const sel = sr.querySelector(`.selected`) || sr.firstChild;
+ const sel = ac_results.querySelector(`.selected`) || ac_results.firstChild;
sel.click();
}
/**
- * Allow user to navigate search results with up/down arrow keys, and choose with enter.
+ * Allow user to navigate autocomplete results with up/down arrow keys, and choose with enter.
*/
SEARCH_INPUT.addEventListener("keydown", (ev) => {
switch (ev.key) {
@@ -71,7 +80,7 @@ function removeAllChildren(node) {
/**
* Handle user input and perform search.
*/
-function handleSearch(dataCenter, err, ev) {
+function handleSearch(dataCenter, err, ev, sr, maxResults) {
const text = ev.target.value;
// If no input clear all.
@@ -85,12 +94,12 @@ function handleSearch(dataCenter, err, ev) {
sr.setAttribute("state", "loading");
if (dataCenter) {
- const result = dataCenter.search(text, false);
+ const result = dataCenter.search(text, false, maxResults);
// in case user has updated the input.
if (ev.target.value != text) return;
- // update search results
+ // update autocomplete results
removeAllChildren(sr);
for (const { name, docLink } of result) {
const d = sr.appendChild(document.createElement("a"));
@@ -111,8 +120,15 @@ function handleSearch(dataCenter, err, ev) {
DeclarationDataCenter.init()
.then((dataCenter) => {
// Search autocompletion.
- SEARCH_INPUT.addEventListener("input", ev => handleSearch(dataCenter, null, ev));
+ SEARCH_INPUT.addEventListener("input", ev => handleSearch(dataCenter, null, ev, ac_results, AC_MAX_RESULTS));
+ if(SEARCH_PAGE_INPUT) {
+ SEARCH_PAGE_INPUT.addEventListener("input", ev => handleSearch(dataCenter, null, ev, SEARCH_RESULTS, SEARCH_PAGE_MAX_RESULTS))
+ SEARCH_PAGE_INPUT.dispatchEvent(new Event("input"))
+ }
})
.catch(e => {
- SEARCH_INPUT.addEventListener("input", ev => handleSearch(null, e, ev));
+ SEARCH_INPUT.addEventListener("input", ev => handleSearch(null, e, ev, ac_results, AC_MAX_RESULTS));
+ if(SEARCH_PAGE_INPUT) {
+ SEARCH_PAGE_INPUT.addEventListener("input", ev => handleSearch(null, e, ev, SEARCH_RESULTS, SEARCH_PAGE_MAX_RESULTS));
+ }
});
diff --git a/static/style.css b/static/style.css
index 2944e99..0701500 100644
--- a/static/style.css
+++ b/static/style.css
@@ -116,7 +116,7 @@ header {
:root { --header-side-padding: 1ex; }
#search_form button { display: none; }
#search_form input { width: 100%; }
- header #search_results {
+ header #autocomplete_results {
left: 1ex;
right: 1ex;
width: inherit;
@@ -146,7 +146,7 @@ header header_filename {
}
/* inserted by nav.js */
-#search_results {
+#autocomplete_results {
position: absolute;
top: var(--header-height);
right: calc(var(--header-side-padding));
@@ -160,15 +160,15 @@ header header_filename {
max-height: calc(100vh - var(--header-height));
}
-#search_results:empty {
+#autocomplete_results:empty {
display: none;
}
-#search_results[state="loading"]:empty {
+#autocomplete_results[state="loading"]:empty {
display: block;
cursor: progress;
}
-#search_results[state="loading"]:empty::before {
+#autocomplete_results[state="loading"]:empty::before {
display: block;
content: ' 🐙 🐙 🐙 🐙 🐙 🐙 🐙 🐙 🐙 🐙 🐙 🐙 🐙 🐙 🐙 ';
padding: 1ex;
@@ -179,17 +179,17 @@ header header_filename {
100% { transform: translate(-100%, 0); }
}
-#search_results[state="done"]:empty {
+#autocomplete_results[state="done"]:empty {
display: block;
text-align: center;
padding: 1ex;
}
-#search_results[state="done"]:empty::before {
+#autocomplete_results[state="done"]:empty::before {
content: '(no results)';
font-style: italic;
}
-#search_results a {
+#autocomplete_results a {
display: block;
color: inherit;
padding: 1ex;
@@ -197,11 +197,20 @@ header header_filename {
padding-left: 0.5ex;
cursor: pointer;
}
-#search_results .selected {
+#autocomplete_results .selected {
background: white;
border-color: #f0a202;
}
+#search_results a {
+ display: block;
+}
+
+#search_results[state="done"]:empty::before {
+ content: '(no results)';
+ font-style: italic;
+}
+
main, nav {
margin-top: calc(var(--header-height) + 1em);
}
@@ -476,7 +485,7 @@ main h2, main h3, main h4, main h5, main h6 {
}
.imports li, code, .decl_header, .attributes, .structure_field_info,
- .constructor, .instances li, .equation, #search_results div,
+ .constructor, .instances li, .equation, #autocomplete_results div,
.structure_ext_ctor {
font-family: 'Source Code Pro', monospace;
}