Materialized query parser redo
This commit is contained in:
@@ -1,3 +1,23 @@
|
||||
syntax:
|
||||
HashTag:
|
||||
firstCharacters:
|
||||
- "#"
|
||||
regex: "#[A-Za-z\\.]+"
|
||||
styles:
|
||||
color: blue
|
||||
AtMention:
|
||||
firstCharacters:
|
||||
- "@"
|
||||
regex: "@[A-Za-z\\.]+"
|
||||
styles:
|
||||
color: blue
|
||||
URL:
|
||||
firstCharacters:
|
||||
- "h"
|
||||
regex: "https?:\\/\\/[-a-zA-Z0-9@:%._\\+~#=]{1,256}([-a-zA-Z0-9()@:%_\\+.~#?&=\\/]*)"
|
||||
styles:
|
||||
color: "#0330cb"
|
||||
textDecoration: underline
|
||||
functions:
|
||||
clearPageIndex:
|
||||
path: "./page.ts:clearPageIndex"
|
||||
@@ -55,13 +75,13 @@ functions:
|
||||
path: "./dates.ts:indexDates"
|
||||
events:
|
||||
- page:index
|
||||
updateMaterializedQueriesOnPage:
|
||||
path: ./materialized_queries.ts:updateMaterializedQueriesOnPage
|
||||
updateMaterializedQueriesCommand:
|
||||
path: ./materialized_queries.ts:updateMaterializedQueriesCommand
|
||||
parseServerCommand:
|
||||
path: ./page.ts:parseServerPageCommand
|
||||
command:
|
||||
name: "Materialized Queries: Update"
|
||||
parseCommand:
|
||||
name: "Debug: Parse Document on Server"
|
||||
parsePage:
|
||||
path: ./page.ts:parsePage
|
||||
parseCommand:
|
||||
path: ./page.ts:parsePageCommand
|
||||
command:
|
||||
name: Parse Document
|
||||
name: "Debug: Parse Document"
|
||||
|
||||
+7
-3
@@ -1,7 +1,7 @@
|
||||
import { insertAtCursor } from "plugos-silverbullet-syscall/editor";
|
||||
import { IndexEvent } from "../../webapp/app_event";
|
||||
import { batchSet } from "plugos-silverbullet-syscall";
|
||||
import { whiteOutQueries } from "./materialized_queries";
|
||||
import { whiteOutQueries } from "../query/materialized_queries";
|
||||
|
||||
const dateMatchRegex = /(\d{4}\-\d{2}\-\d{2})/g;
|
||||
|
||||
@@ -23,12 +23,16 @@ export async function indexDates({ name, text }: IndexEvent) {
|
||||
await batchSet(name, dates);
|
||||
}
|
||||
|
||||
export function niceDate(d: Date): string {
|
||||
return new Date().toISOString().split("T")[0];
|
||||
}
|
||||
|
||||
export async function insertToday() {
|
||||
await insertAtCursor(new Date().toISOString().split("T")[0]);
|
||||
await insertAtCursor(niceDate(new Date()));
|
||||
}
|
||||
|
||||
export async function insertTomorrow() {
|
||||
let d = new Date();
|
||||
d.setDate(d.getDate() + 1);
|
||||
await insertAtCursor(d.toISOString().split("T")[0]);
|
||||
await insertAtCursor(niceDate(d));
|
||||
}
|
||||
|
||||
+5
-5
@@ -1,9 +1,9 @@
|
||||
import { IndexEvent } from "../../webapp/app_event";
|
||||
import { whiteOutQueries } from "./materialized_queries";
|
||||
import { whiteOutQueries } from "../query/materialized_queries";
|
||||
|
||||
import { batchSet } from "plugos-silverbullet-syscall/index";
|
||||
import { parseMarkdown } from "plugos-silverbullet-syscall/markdown";
|
||||
import { collectNodesMatching, MarkdownTree, renderMarkdown } from "../lib/tree";
|
||||
import { collectNodesMatching, ParseTree, renderToText } from "../../common/tree";
|
||||
|
||||
type Item = {
|
||||
item: string;
|
||||
@@ -23,16 +23,16 @@ export async function indexItems({ name, text }: IndexEvent) {
|
||||
if (!n.children) {
|
||||
return;
|
||||
}
|
||||
let textNodes: MarkdownTree[] = [];
|
||||
let textNodes: ParseTree[] = [];
|
||||
let nested: string | undefined;
|
||||
for (let child of n.children!.slice(1)) {
|
||||
if (child.type === "OrderedList" || child.type === "BulletList") {
|
||||
nested = renderMarkdown(child);
|
||||
nested = renderToText(child);
|
||||
break;
|
||||
}
|
||||
textNodes.push(child);
|
||||
}
|
||||
let item = textNodes.map(renderMarkdown).join("").trim();
|
||||
let item = textNodes.map(renderToText).join("").trim();
|
||||
let value: Item = {
|
||||
item,
|
||||
};
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
import { flashNotification, getCurrentPage, reloadPage, save } from "plugos-silverbullet-syscall/editor";
|
||||
|
||||
import { listPages, readPage, writePage } from "plugos-silverbullet-syscall/space";
|
||||
import { invokeFunction } from "plugos-silverbullet-syscall/system";
|
||||
import { scanPrefixGlobal } from "plugos-silverbullet-syscall";
|
||||
|
||||
export const queryRegex =
|
||||
/(<!--\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;
|
||||
|
||||
export function whiteOutQueries(text: string): string {
|
||||
return text.replaceAll(queryRegex, (match) =>
|
||||
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() {
|
||||
const currentPage = await getCurrentPage();
|
||||
await save();
|
||||
await invokeFunction(
|
||||
"server",
|
||||
"updateMaterializedQueriesOnPage",
|
||||
currentPage
|
||||
);
|
||||
await reloadPage();
|
||||
await flashNotification("Updated materialized queries");
|
||||
}
|
||||
|
||||
// Called from client, running on server
|
||||
export async function updateMaterializedQueriesOnPage(pageName: string) {
|
||||
let { text } = await readPage(pageName);
|
||||
|
||||
text = await replaceAsync(text, queryRegex, async (match, ...args) => {
|
||||
let { table, filter, groupBy, limit, orderBy, orderDesc } =
|
||||
args[args.length - 1];
|
||||
const startQuery = args[0];
|
||||
const endQuery = args[args.length - 4];
|
||||
let results = [];
|
||||
switch (table) {
|
||||
case "page":
|
||||
let pages = await listPages();
|
||||
if (orderBy) {
|
||||
pages = pages.sort((a: any, b: any) => {
|
||||
if (a[orderBy] === b[orderBy]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
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}`;
|
||||
case "task":
|
||||
for (let {
|
||||
key,
|
||||
page,
|
||||
value: { task, complete, nested },
|
||||
} of await scanPrefixGlobal("task:")) {
|
||||
let [, pos] = key.split(":");
|
||||
if (!filter || (filter && task.includes(filter))) {
|
||||
results.push(
|
||||
`* [${complete ? "x" : " "}] [[${page}@${pos}]] ${task}` +
|
||||
(nested ? "\n " + nested : "")
|
||||
);
|
||||
}
|
||||
}
|
||||
return `${startQuery}\n${results.sort().join("\n")}\n${endQuery}`;
|
||||
case "link":
|
||||
let uniqueLinks = new Set<string>();
|
||||
console.log("Here!!");
|
||||
for (let { key, page, value: name } of await scanPrefixGlobal(
|
||||
`pl:${pageName}:`
|
||||
)) {
|
||||
console.log("Here!!");
|
||||
let [, pos] = key.split(":");
|
||||
if (!filter || (filter && name.includes(filter))) {
|
||||
uniqueLinks.add(name);
|
||||
}
|
||||
}
|
||||
console.log("Here!!");
|
||||
for (const uniqueResult of uniqueLinks) {
|
||||
results.push(`* [[${uniqueResult}]]`);
|
||||
}
|
||||
console.log("Here!!");
|
||||
return `${startQuery}\n${results.sort().join("\n")}\n${endQuery}`;
|
||||
case "item":
|
||||
for (let {
|
||||
key,
|
||||
page,
|
||||
value: { item, nested },
|
||||
} of await scanPrefixGlobal("it:")) {
|
||||
let [, pos] = key.split(":");
|
||||
if (!filter || (filter && item.includes(filter))) {
|
||||
results.push(
|
||||
`* [[${page}@${pos}]] ${item}` + (nested ? "\n " + nested : "")
|
||||
);
|
||||
}
|
||||
}
|
||||
return `${startQuery}\n${results.sort().join("\n")}\n${endQuery}`;
|
||||
default:
|
||||
return match;
|
||||
}
|
||||
});
|
||||
// console.log("New text", text);
|
||||
await writePage(pageName, text);
|
||||
}
|
||||
+7
-15
@@ -1,13 +1,11 @@
|
||||
import { ClickEvent } from "../../webapp/app_event";
|
||||
import { updateMaterializedQueriesCommand } from "./materialized_queries";
|
||||
import { getCursor, getText, navigate as navigateTo, openUrl } from "plugos-silverbullet-syscall/editor";
|
||||
import { taskToggleAtPos } from "../tasks/task";
|
||||
import { parseMarkdown } from "plugos-silverbullet-syscall/markdown";
|
||||
import { MarkdownTree, nodeAtPos } from "../lib/tree";
|
||||
import { nodeAtPos, ParseTree } from "../../common/tree";
|
||||
|
||||
const materializedQueryPrefix = /<!--\s*#query\s+/;
|
||||
|
||||
async function actionClickOrActionEnter(mdTree: MarkdownTree | null) {
|
||||
async function actionClickOrActionEnter(mdTree: ParseTree | null) {
|
||||
if (!mdTree) {
|
||||
return;
|
||||
}
|
||||
@@ -24,17 +22,9 @@ async function actionClickOrActionEnter(mdTree: MarkdownTree | null) {
|
||||
case "URL":
|
||||
await openUrl(mdTree.children![0].text!);
|
||||
break;
|
||||
case "CommentBlock":
|
||||
if (mdTree.children![0].text!.match(materializedQueryPrefix)) {
|
||||
await updateMaterializedQueriesCommand();
|
||||
}
|
||||
break;
|
||||
case "Link":
|
||||
await openUrl(mdTree.children![4].children![0].text!);
|
||||
break;
|
||||
case "TaskMarker":
|
||||
await taskToggleAtPos(mdTree.from! + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,9 +35,11 @@ export async function linkNavigate() {
|
||||
}
|
||||
|
||||
export async function clickNavigate(event: ClickEvent) {
|
||||
// Navigate by default, don't navigate when Ctrl or Cmd is held
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
let mdTree = await parseMarkdown(await getText());
|
||||
let newNode = nodeAtPos(mdTree, event.pos);
|
||||
await actionClickOrActionEnter(newNode);
|
||||
return;
|
||||
}
|
||||
let mdTree = await parseMarkdown(await getText());
|
||||
let newNode = nodeAtPos(mdTree, event.pos);
|
||||
await actionClickOrActionEnter(newNode);
|
||||
}
|
||||
|
||||
+15
-7
@@ -21,10 +21,10 @@ import { parseMarkdown } from "plugos-silverbullet-syscall/markdown";
|
||||
import {
|
||||
addParentPointers,
|
||||
collectNodesMatching,
|
||||
MarkdownTree,
|
||||
renderMarkdown,
|
||||
ParseTree,
|
||||
renderToText,
|
||||
replaceNodesMatching
|
||||
} from "../lib/tree";
|
||||
} from "../../common/tree";
|
||||
|
||||
export async function indexLinks({ name, text }: IndexEvent) {
|
||||
let backLinks: { key: string; value: string }[] = [];
|
||||
@@ -93,7 +93,7 @@ export async function renamePage() {
|
||||
}
|
||||
let mdTree = await parseMarkdown(text);
|
||||
addParentPointers(mdTree);
|
||||
replaceNodesMatching(mdTree, (n): MarkdownTree | undefined | null => {
|
||||
replaceNodesMatching(mdTree, (n): ParseTree | undefined | null => {
|
||||
if (n.type === "WikiLinkPage") {
|
||||
let pageName = n.children![0].text!;
|
||||
if (pageName === oldName) {
|
||||
@@ -110,7 +110,7 @@ export async function renamePage() {
|
||||
return;
|
||||
});
|
||||
// let newText = text.replaceAll(`[[${oldName}]]`, `[[${newName}]]`);
|
||||
let newText = renderMarkdown(mdTree);
|
||||
let newText = renderToText(mdTree);
|
||||
if (text !== newText) {
|
||||
console.log("Changes made, saving...");
|
||||
await writePage(pageToUpdate, newText);
|
||||
@@ -179,6 +179,14 @@ export async function clearPageIndex(page: string) {
|
||||
await clearPageIndexForPage(page);
|
||||
}
|
||||
|
||||
export async function parsePage() {
|
||||
console.log(await parseMarkdown(await getText()));
|
||||
export async function parseServerPageCommand() {
|
||||
console.log(await invokeFunction("server", "parsePage", await getText()));
|
||||
}
|
||||
|
||||
export async function parsePageCommand() {
|
||||
parsePage(await getText());
|
||||
}
|
||||
|
||||
export async function parsePage(text: string) {
|
||||
console.log("AST", JSON.stringify(await parseMarkdown(text), null, 2));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user