chore: Update doc-gen search js

main
Henrik Böving 2022-02-13 14:25:37 +01:00
parent 96c8a048e3
commit 4b9b7d77d5
4 changed files with 70 additions and 8 deletions

View File

@ -70,6 +70,7 @@ def htmlOutput (result : AnalyzerResult) (root : String) : IO Unit := do
FS.writeFile (basePath / "style.css") styleCss
FS.writeFile (basePath / "404.html") notFoundHtml.toString
FS.writeFile (basePath / "nav.js") navJs
FS.writeFile (basePath / "search.js") searchJs
for (module, content) in result.moduleInfo.toArray do
let moduleHtml := ReaderT.run (moduleToHtml content) config
let path := moduleNameToFile basePath module

View File

@ -48,6 +48,7 @@ def moduleNameToDirectory (basePath : FilePath) (n : Name) : FilePath :=
section Static
def styleCss : String := include_str "./static/style.css"
def navJs : String := include_str "./static/nav.js"
def searchJs : String := include_str "./static/search.js"
end Static
def declNameToLink (name : Name) : HtmlM String := do

View File

@ -106,14 +106,23 @@ if (tse != null) {
// Simple declaration search
// -------------------------
const searchWorkerURL = new URL(`${siteRoot}searchWorker.js`, window.location);
const declSearch = (q) => new Promise((resolve, reject) => {
const worker = new SharedWorker(searchWorkerURL);
worker.port.start();
worker.port.onmessage = ({data}) => resolve(data);
worker.port.onmessageerror = (e) => reject(e);
worker.port.postMessage({q});
});
const declURL = new URL(`${siteRoot}searchable_data.bmp`, window.location);
const getDecls = (() => {
let decls;
return () => {
if (!decls) decls = new Promise((resolve, reject) => {
const req = new XMLHttpRequest();
req.responseType = 'json';
req.addEventListener('load', () => resolve(loadDecls(req.response)));
req.addEventListener('error', () => reject());
req.open('GET', declURL);
req.send();
})
return decls;
}
})()
const declSearch = async (q) => getMatches(await getDecls(), q);
const srId = 'search_results';
document.getElementById('search_form')

51
static/search.js Normal file
View File

@ -0,0 +1,51 @@
function isSep(c) {
return c === '.' || c === '_';
}
function matchCaseSensitive(declName, lowerDeclName, pat) {
let i = 0, j = 0, err = 0, lastMatch = 0
while (i < declName.length && j < pat.length) {
if (pat[j] === declName[i] || pat[j] === lowerDeclName[i]) {
err += (isSep(pat[j]) ? 0.125 : 1) * (i - lastMatch);
if (pat[j] !== declName[i]) err += 0.5;
lastMatch = i + 1;
j++;
} else if (isSep(declName[i])) {
err += 0.125 * (i + 1 - lastMatch);
lastMatch = i + 1;
}
i++;
}
err += 0.125 * (declName.length - lastMatch);
if (j === pat.length) {
return err;
}
}
function loadDecls(searchableDataCnt) {
return searchableDataCnt.map(({name, description}) => [name, name.toLowerCase(), description.toLowerCase()])
}
function getMatches(decls, pat, maxResults = 30) {
const lowerPats = pat.toLowerCase().split(/\s/g);
const patNoSpaces = pat.replace(/\s/g, '');
const results = [];
for (const [decl, lowerDecl, lowerDoc] of decls) {
let err = matchCaseSensitive(decl, lowerDecl, patNoSpaces);
// match all words as substrings of docstring
if (!(err < 3) && pat.length > 3 && lowerPats.every(l => lowerDoc.indexOf(l) != -1)) {
err = 3;
}
if (err !== undefined) {
results.push({decl, err});
}
}
return results.sort(({err: a}, {err: b}) => a - b).slice(0, maxResults);
}
if (typeof process === 'object') { // NodeJS
const data = loadDecls(JSON.parse(require('fs').readFileSync('searchable_data.bmp').toString()));
console.log(getMatches(data, process.argv[2] || 'ltltle'));
}