1
0
silverbullet/plugs/core/search.ts

112 lines
2.9 KiB
TypeScript
Raw Normal View History

2022-10-14 13:11:33 +00:00
import { fulltext } from "$sb/plugos-syscall/mod.ts";
import { renderToText } from "$sb/lib/tree.ts";
2022-10-22 18:23:54 +00:00
import type { FileMeta } from "../../common/types.ts";
2022-10-14 13:11:33 +00:00
import { editor, index } from "$sb/silverbullet-syscall/mod.ts";
import { IndexTreeEvent, QueryProviderEvent } from "$sb/app_event.ts";
import { applyQuery, removeQueries } from "$sb/lib/query.ts";
2022-10-19 07:52:29 +00:00
import {
FileData,
FileEncoding,
} from "../../common/spaces/space_primitives.ts";
import { base64EncodedDataUrl } from "../../plugos/asset_bundle/base64.ts";
2022-05-16 13:09:36 +00:00
const searchPrefix = "🔍 ";
2022-10-14 13:11:33 +00:00
export async function pageIndex(data: IndexTreeEvent) {
2022-05-16 13:09:36 +00:00
removeQueries(data.tree);
2022-10-14 13:11:33 +00:00
const cleanText = renderToText(data.tree);
await fulltext.fullTextIndex(data.name, cleanText);
2022-05-16 13:09:36 +00:00
}
2022-10-14 13:11:33 +00:00
export async function pageUnindex(pageName: string) {
await fulltext.fullTextDelete(pageName);
2022-06-28 12:14:15 +00:00
}
2022-05-16 13:09:36 +00:00
export async function queryProvider({
query,
}: QueryProviderEvent): Promise<any[]> {
2022-10-14 13:11:33 +00:00
const phraseFilter = query.filter.find((f) => f.prop === "phrase");
2022-05-16 13:09:36 +00:00
if (!phraseFilter) {
throw Error("No 'phrase' filter specified, this is mandatory");
}
2022-10-22 18:23:54 +00:00
let results = await fulltext.fullTextSearch(phraseFilter.value, {
highlightEllipsis: "...",
limit: 100,
});
2022-05-16 13:09:36 +00:00
2022-10-14 13:11:33 +00:00
const allPageMap: Map<string, any> = new Map(
2022-10-12 09:47:13 +00:00
results.map((r: any) => [r.name, r]),
2022-05-17 06:32:33 +00:00
);
2022-10-14 13:11:33 +00:00
for (const { page, value } of await index.queryPrefix("meta:")) {
const p = allPageMap.get(page);
2022-05-16 13:09:36 +00:00
if (p) {
2022-10-15 17:02:56 +00:00
for (const [k, v] of Object.entries(value)) {
2022-05-16 13:09:36 +00:00
p[k] = v;
}
}
}
// Remove the "phrase" filter
query.filter.splice(query.filter.indexOf(phraseFilter), 1);
results = applyQuery(query, results);
return results;
}
2022-05-17 09:53:17 +00:00
export async function searchCommand() {
2022-10-19 07:52:29 +00:00
const phrase = await editor.prompt("Search for: ");
2022-05-17 09:53:17 +00:00
if (phrase) {
2022-10-14 13:11:33 +00:00
await editor.navigate(`${searchPrefix}${phrase}`);
2022-05-17 09:53:17 +00:00
}
}
2022-10-19 07:52:29 +00:00
export async function readFileSearch(
2022-10-12 09:47:13 +00:00
name: string,
2022-10-19 07:52:29 +00:00
encoding: FileEncoding,
): Promise<{ data: FileData; meta: FileMeta }> {
const phrase = name.substring(
searchPrefix.length,
name.length - ".md".length,
);
2022-10-22 18:23:54 +00:00
const results = await fulltext.fullTextSearch(phrase, {
highlightEllipsis: "...",
highlightPostfix: "==",
highlightPrefix: "==",
summaryMaxLength: 30,
limit: 100,
});
2022-10-12 09:47:13 +00:00
const text = `# Search results for "${phrase}"\n${
results
2022-10-22 18:23:54 +00:00
.map((r: any) =>
`[[${r.name}]]:\n> ${r.snippet.split("\n").join("\n> ")}`
)
.join("\n\n")
2022-10-12 09:47:13 +00:00
}
2022-05-17 09:53:17 +00:00
`;
2022-10-19 07:52:29 +00:00
2022-05-17 09:53:17 +00:00
return {
2022-10-19 07:52:29 +00:00
// encoding === "arraybuffer" is not an option, so either it's "string" or "dataurl"
data: encoding === "string" ? text : base64EncodedDataUrl(
"text/markdown",
new TextEncoder().encode(text),
),
2022-05-17 09:53:17 +00:00
meta: {
name,
2022-10-19 07:52:29 +00:00
contentType: "text/markdown",
size: text.length,
2022-05-17 09:53:17 +00:00
lastModified: 0,
perm: "ro",
},
};
}
2022-10-19 07:52:29 +00:00
export function getFileMetaSearch(name: string): FileMeta {
2022-05-17 09:53:17 +00:00
return {
name,
2022-10-19 07:52:29 +00:00
contentType: "text/markdown",
size: -1,
2022-05-17 09:53:17 +00:00
lastModified: 0,
perm: "ro",
};
}