1
0
silverbullet/plugs/query/materialized_queries.ts

146 lines
4.7 KiB
TypeScript
Raw Normal View History

2022-04-05 15:02:17 +00:00
import { flashNotification, getCurrentPage, reloadPage, save } from "plugos-silverbullet-syscall/editor";
2022-04-01 15:07:08 +00:00
2022-04-05 15:02:17 +00:00
import { listPages, readPage, writePage } from "plugos-silverbullet-syscall/space";
import { invokeFunction } from "plugos-silverbullet-syscall/system";
import { scanPrefixGlobal } from "plugos-silverbullet-syscall";
2022-04-11 18:34:09 +00:00
import { niceDate } from "../core/dates";
2022-03-29 15:02:28 +00:00
export const queryRegex =
2022-04-04 13:25:07 +00:00
/(<!--\s*#query\s+(?<table>\w+)\s*(filter\s+["'](?<filter>[^"']+)["'])?\s*\s*(order by\s+(?<orderBy>\w+)(?<orderDesc>\s+desc)?)?(group by\s+(?<groupBy>\w+))?\s*(limit\s+(?<limit>\d+))?\s*-->)(.+?)(<!--\s*#end\s*-->)/gs;
2022-03-29 15:02:28 +00:00
2022-04-11 18:34:09 +00:00
export const newQueryRegex =
/<!--\s*#query\s+(.+?)(?=\s*-->)-->(.+?)<!--\s*#end\s*-->/gs;
2022-03-29 15:02:28 +00:00
export function whiteOutQueries(text: string): string {
2022-04-11 18:34:09 +00:00
return text.replaceAll(newQueryRegex, (match) =>
2022-03-29 15:02:28 +00:00
new Array(match.length + 1).join(" ")
);
}
async function replaceAsync(
str: string,
regex: RegExp,
asyncFn: (match: string, ...args: any[]) => Promise<string>
) {
const promises: Promise<string>[] = [];
str.replace(regex, (match: string, ...args: any[]): string => {
const promise = asyncFn(match, ...args);
promises.push(promise);
return "";
});
const data = await Promise.all(promises);
return str.replace(regex, () => data.shift()!);
}
export async function updateMaterializedQueriesCommand() {
2022-04-01 15:07:08 +00:00
const currentPage = await getCurrentPage();
await save();
2022-04-05 15:02:17 +00:00
await invokeFunction(
"server",
"updateMaterializedQueriesOnPage",
currentPage
);
2022-04-01 15:07:08 +00:00
await reloadPage();
await flashNotification("Updated materialized queries");
2022-03-29 15:02:28 +00:00
}
2022-04-11 18:34:09 +00:00
function replaceTemplateVars(s: string): string {
return s.replaceAll(/\{\{(\w+)\}\}/g, (match, v) => {
switch (v) {
case "today":
return niceDate(new Date());
break;
}
return match;
});
}
2022-03-29 15:02:28 +00:00
// Called from client, running on server
export async function updateMaterializedQueriesOnPage(pageName: string) {
2022-04-01 15:07:08 +00:00
let { text } = await readPage(pageName);
2022-03-29 15:02:28 +00:00
text = await replaceAsync(text, queryRegex, async (match, ...args) => {
2022-04-04 13:25:07 +00:00
let { table, filter, groupBy, limit, orderBy, orderDesc } =
args[args.length - 1];
2022-03-29 15:02:28 +00:00
const startQuery = args[0];
const endQuery = args[args.length - 4];
let results = [];
2022-04-11 18:34:09 +00:00
filter = filter && replaceTemplateVars(filter);
2022-03-29 15:02:28 +00:00
switch (table) {
2022-04-04 13:25:07 +00:00
case "page":
let pages = await listPages();
if (orderBy) {
pages = pages.sort((a: any, b: any) => {
if (a[orderBy] === b[orderBy]) {
return 0;
}
2022-04-04 13:25:07 +00:00
if (a[orderBy] < b[orderBy]) {
return !!orderDesc ? 1 : -1;
} else {
return !!orderDesc ? -1 : 1;
}
});
}
let matchCount = 0;
for (let pageMeta of pages) {
if (!filter || (filter && pageMeta.name.includes(filter))) {
matchCount++;
results.push(`* [[${pageMeta.name}]]`);
if (limit && matchCount === +limit) {
break;
}
}
}
return `${startQuery}\n${results.join("\n")}\n${endQuery}`;
2022-03-29 15:02:28 +00:00
case "task":
for (let {
key,
page,
2022-04-04 09:51:41 +00:00
value: { task, complete, nested },
2022-04-01 15:07:08 +00:00
} of await scanPrefixGlobal("task:")) {
2022-03-29 15:02:28 +00:00
let [, pos] = key.split(":");
if (!filter || (filter && task.includes(filter))) {
results.push(
2022-04-01 15:07:08 +00:00
`* [${complete ? "x" : " "}] [[${page}@${pos}]] ${task}` +
2022-04-04 09:51:41 +00:00
(nested ? "\n " + nested : "")
2022-03-29 15:02:28 +00:00
);
}
}
2022-04-01 15:07:08 +00:00
return `${startQuery}\n${results.sort().join("\n")}\n${endQuery}`;
2022-03-31 15:25:34 +00:00
case "link":
let uniqueLinks = new Set<string>();
2022-04-01 15:07:08 +00:00
for (let { key, page, value: name } of await scanPrefixGlobal(
`pl:${pageName}:`
2022-03-31 15:25:34 +00:00
)) {
let [, pos] = key.split(":");
if (!filter || (filter && name.includes(filter))) {
uniqueLinks.add(name);
}
}
for (const uniqueResult of uniqueLinks) {
results.push(`* [[${uniqueResult}]]`);
}
return `${startQuery}\n${results.sort().join("\n")}\n${endQuery}`;
2022-03-29 15:02:28 +00:00
case "item":
for (let {
key,
page,
2022-04-04 09:51:41 +00:00
value: { item, nested },
2022-04-01 15:07:08 +00:00
} of await scanPrefixGlobal("it:")) {
2022-03-29 15:02:28 +00:00
let [, pos] = key.split(":");
if (!filter || (filter && item.includes(filter))) {
2022-04-01 15:07:08 +00:00
results.push(
2022-04-04 09:51:41 +00:00
`* [[${page}@${pos}]] ${item}` + (nested ? "\n " + nested : "")
2022-04-01 15:07:08 +00:00
);
2022-03-29 15:02:28 +00:00
}
}
2022-04-01 15:07:08 +00:00
return `${startQuery}\n${results.sort().join("\n")}\n${endQuery}`;
2022-03-29 15:02:28 +00:00
default:
return match;
}
});
// console.log("New text", text);
2022-04-01 15:07:08 +00:00
await writePage(pageName, text);
2022-03-29 15:02:28 +00:00
}