From 16577c8ea20db09bde263723a18163514fbc7f29 Mon Sep 17 00:00:00 2001 From: Zef Hemel Date: Mon, 4 Apr 2022 11:51:41 +0200 Subject: [PATCH] Rewrote all plugs using MarkdownTree --- common/syscalls/markdown.ts | 18 +-- common/tree.test.ts | 26 ----- common/tree.ts | 148 ++++++++---------------- package.json | 2 +- plugos-silverbullet-syscall/markdown.ts | 15 +-- plugos/hooks/event.ts | 10 +- plugos/syscalls/event.ts | 2 +- plugos/syscalls/fetch.node.ts | 2 +- plugs/core/item.ts | 49 ++++---- plugs/core/materialized_queries.ts | 9 +- plugs/core/navigate.ts | 10 +- plugs/lib/tree.test.ts | 52 +++++++++ plugs/lib/tree.ts | 101 ++++++++++++++++ plugs/package.json | 1 + plugs/tasks/task.ts | 105 +++++++++-------- server/disk_storage.ts | 21 ++-- webapp/parser.ts | 16 +-- 17 files changed, 332 insertions(+), 255 deletions(-) delete mode 100644 common/tree.test.ts create mode 100644 plugs/lib/tree.test.ts create mode 100644 plugs/lib/tree.ts diff --git a/common/syscalls/markdown.ts b/common/syscalls/markdown.ts index 047bf00f..f18c3e49 100644 --- a/common/syscalls/markdown.ts +++ b/common/syscalls/markdown.ts @@ -1,16 +1,10 @@ import {SysCallMapping} from "../../plugos/system"; -import {MarkdownTree, nodeAtPos, parse, render} from "../tree"; +import {MarkdownTree, parse} from "../tree"; export function markdownSyscalls(): SysCallMapping { - return { - "markdown.parse": (ctx, text: string): MarkdownTree => { - return parse(text); - }, - "markdown.nodeAtPos": (ctx, mdTree: MarkdownTree, pos: number): MarkdownTree | null => { - return nodeAtPos(mdTree, pos); - }, - "markdown.render": (ctx, mdTree: MarkdownTree): string => { - return render(mdTree); - }, - }; + return { + "markdown.parseMarkdown": (ctx, text: string): MarkdownTree => { + return parse(text); + }, + }; } diff --git a/common/tree.test.ts b/common/tree.test.ts deleted file mode 100644 index 77d1648b..00000000 --- a/common/tree.test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {expect, test} from "@jest/globals"; -import {nodeAtPos, parse, render} from "./tree"; - -const mdTest1 = ` -# Heading -## Sub _heading_ cool - -Hello, this is some **bold** text and *italic*. And [a link](http://zef.me). - -- This is a list -- With another item -- TODOs: - - [ ] A task that's not yet done - - [x] Hello -- And a _third_ one [[Wiki Page]] yo -`; - -test("Run a Node sandbox", async () => { - let mdTree = parse(mdTest1); - console.log(JSON.stringify(mdTree, null, 2)); - expect(nodeAtPos(mdTree, 4)!.type).toBe("ATXHeading1"); - expect(nodeAtPos(mdTree, mdTest1.indexOf("Wiki Page"))!.type).toBe( - "WikiLink" - ); - expect(render(mdTree)).toBe(mdTest1); -}); diff --git a/common/tree.ts b/common/tree.ts index 8ba2073c..d1504d15 100644 --- a/common/tree.ts +++ b/common/tree.ts @@ -2,114 +2,66 @@ import {SyntaxNode} from "@lezer/common"; import wikiMarkdownLang from "../webapp/parser"; export type MarkdownTree = { - type?: string; // undefined === text node - from: number; - to: number; - text?: string; - children?: MarkdownTree[]; - parent?: MarkdownTree; + type?: string; // undefined === text node + from: number; + to: number; + text?: string; + children?: MarkdownTree[]; }; function treeToAST(text: string, n: SyntaxNode): MarkdownTree { - let children: MarkdownTree[] = []; - let nodeText: string | undefined; - let child = n.firstChild; - while (child) { - children.push(treeToAST(text, child)); - child = child.nextSibling; - } + let children: MarkdownTree[] = []; + let nodeText: string | undefined; + let child = n.firstChild; + while (child) { + children.push(treeToAST(text, child)); + child = child.nextSibling; + } - if (children.length === 0) { - children = [ - { - from: n.from, - to: n.to, - text: text.substring(n.from, n.to), - }, - ]; - } else { - let newChildren: MarkdownTree[] | string = []; - let index = n.from; - for (let child of children) { - let s = text.substring(index, child.from); - if (s) { - newChildren.push({ - from: index, - to: child.from, - text: s, - }); - } - newChildren.push(child); - index = child.to; - } - let s = text.substring(index, n.to); - if (s) { - newChildren.push({ from: index, to: n.to, text: s }); - } - children = newChildren; - } - - let result: MarkdownTree = { - type: n.name, + if (children.length === 0) { + children = [ + { from: n.from, to: n.to, - }; - if (children.length > 0) { - result.children = children; + text: text.substring(n.from, n.to), + }, + ]; + } else { + let newChildren: MarkdownTree[] | string = []; + let index = n.from; + for (let child of children) { + let s = text.substring(index, child.from); + if (s) { + newChildren.push({ + from: index, + to: child.from, + text: s, + }); + } + newChildren.push(child); + index = child.to; } - if (nodeText) { - result.text = nodeText; + let s = text.substring(index, n.to); + if (s) { + newChildren.push({ from: index, to: n.to, text: s }); } - return result; -} + children = newChildren; + } -// Currently unused -function addParentPointers(mdTree: MarkdownTree) { - if (!mdTree.children) { - return; - } - for (let child of mdTree.children) { - child.parent = mdTree; - addParentPointers(child); - } -} - -// Finds non-text node at position -export function nodeAtPos( - mdTree: MarkdownTree, - pos: number -): MarkdownTree | null { - if (pos < mdTree.from || pos > mdTree.to) { - return null; - } - if (!mdTree.children) { - return mdTree; - } - for (let child of mdTree.children) { - let n = nodeAtPos(child, pos); - if (n && n.text) { - // Got a text node, let's return its parent - return mdTree; - } else if (n) { - // Got it - return n; - } - } - return null; -} - -// Turn MarkdownTree back into regular markdown text -export function render(mdTree: MarkdownTree): string { - let pieces: string[] = []; - if (mdTree.text) { - return mdTree.text; - } - for (let child of mdTree.children!) { - pieces.push(render(child)); - } - return pieces.join(""); + let result: MarkdownTree = { + type: n.name, + from: n.from, + to: n.to, + }; + if (children.length > 0) { + result.children = children; + } + if (nodeText) { + result.text = nodeText; + } + return result; } export function parse(text: string): MarkdownTree { - return treeToAST(text, wikiMarkdownLang.parser.parse(text).topNode); + return treeToAST(text, wikiMarkdownLang.parser.parse(text).topNode); } diff --git a/package.json b/package.json index 1c19d7ab..dbe2c155 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "context": "node" }, "test": { - "source": ["common/tree.test.ts"], + "source": ["plugs/lib/tree.test.ts"], "outputFormat": "commonjs", "isLibrary": true, "context": "node" diff --git a/plugos-silverbullet-syscall/markdown.ts b/plugos-silverbullet-syscall/markdown.ts index eaa05e15..88b98bad 100644 --- a/plugos-silverbullet-syscall/markdown.ts +++ b/plugos-silverbullet-syscall/markdown.ts @@ -1,17 +1,6 @@ import {syscall} from "./syscall"; import type {MarkdownTree} from "../common/tree"; -export async function parse(text: string): Promise { - return syscall("markdown.parse", text); -} - -export async function nodeAtPos( - mdTree: MarkdownTree, - pos: number -): Promise { - return syscall("markdown.nodeAtPos", mdTree, pos); -} - -export async function render(mdTree: MarkdownTree): Promise { - return syscall("markdown.render", mdTree); +export async function parseMarkdown(text: string): Promise { + return syscall("markdown.parseMarkdown", text); } diff --git a/plugos/hooks/event.ts b/plugos/hooks/event.ts index fb9424e9..34c178a4 100644 --- a/plugos/hooks/event.ts +++ b/plugos/hooks/event.ts @@ -1,6 +1,6 @@ -import { Hook, Manifest } from "../types"; -import { System } from "../system"; -import { safeRun } from "../util"; +import {Hook, Manifest} from "../types"; +import {System} from "../system"; +import {safeRun} from "../util"; // System events: // - plug:load (plugName: string) @@ -16,7 +16,6 @@ export class EventHook implements Hook { if (!this.system) { throw new Error("Event hook is not initialized"); } - let promises: Promise[] = []; for (const plug of this.system.loadedPlugs.values()) { for (const [name, functionDef] of Object.entries( plug.manifest!.functions @@ -24,12 +23,11 @@ export class EventHook implements Hook { if (functionDef.events && functionDef.events.includes(eventName)) { // Only dispatch functions that can run in this environment if (plug.canInvoke(name)) { - promises.push(plug.invoke(name, [data])); + await plug.invoke(name, [data]); } } } } - await Promise.all(promises); } apply(system: System): void { diff --git a/plugos/syscalls/event.ts b/plugos/syscalls/event.ts index 243d5e9a..2492ea7d 100644 --- a/plugos/syscalls/event.ts +++ b/plugos/syscalls/event.ts @@ -3,7 +3,7 @@ import {EventHook} from "../hooks/event"; export function eventSyscalls(eventHook: EventHook): SysCallMapping { return { - "event.dispatch": async(ctx, eventName: string, data: any) => { + "event.dispatch": async (ctx, eventName: string, data: any) => { return eventHook.dispatchEvent(eventName, data); }, }; diff --git a/plugos/syscalls/fetch.node.ts b/plugos/syscalls/fetch.node.ts index 3ca4f700..805e9f3f 100644 --- a/plugos/syscalls/fetch.node.ts +++ b/plugos/syscalls/fetch.node.ts @@ -7,7 +7,7 @@ export function fetchSyscalls(): SysCallMapping { let resp = await fetch(url, init); return resp.json(); }, - "fetch.text": async(ctx, url: RequestInfo, init: RequestInit) => { + "fetch.text": async (ctx, url: RequestInfo, init: RequestInit) => { let resp = await fetch(url, init); return resp.text(); }, diff --git a/plugs/core/item.ts b/plugs/core/item.ts index 093955ca..c0eaa0c4 100644 --- a/plugs/core/item.ts +++ b/plugs/core/item.ts @@ -1,40 +1,49 @@ -import { IndexEvent } from "../../webapp/app_event"; -import { whiteOutQueries } from "./materialized_queries"; +import {IndexEvent} from "../../webapp/app_event"; +import {whiteOutQueries} from "./materialized_queries"; -import { batchSet } from "plugos-silverbullet-syscall/index"; +import {batchSet} from "plugos-silverbullet-syscall/index"; +import {parseMarkdown} from "plugos-silverbullet-syscall/markdown"; +import {collectNodesMatching, MarkdownTree, render} from "../lib/tree"; type Item = { item: string; - children?: string[]; + nested?: string; }; -const pageRefRe = /\[\[[^\]]+@\d+\]\]/; -const itemFullRe = - /(?[\t ]*)[\-\*]\s*([^\n]+)(\n\k\s+[\-\*][^\n]+)*/g; - export async function indexItems({ name, text }: IndexEvent) { let items: { key: string; value: Item }[] = []; text = whiteOutQueries(text); - for (let match of text.matchAll(itemFullRe)) { - let entire = match[0]; - let item = match[2]; - if (item.match(pageRefRe)) { - continue; - } - let pos = match.index!; - let lines = entire.split("\n"); + console.log("Indexing items", name); + let mdTree = await parseMarkdown(text); + + let coll = collectNodesMatching(mdTree, (n) => n.type === "ListItem"); + + coll.forEach((n) => { + if (!n.children) { + return; + } + let textNodes: MarkdownTree[] = []; + let nested: string | undefined; + for (let child of n.children!.slice(1)) { + if (child.type === "OrderedList" || child.type === "BulletList") { + nested = render(child); + break; + } + textNodes.push(child); + } + let item = textNodes.map(render).join("").trim(); let value: Item = { item, }; - if (lines.length > 1) { - value.children = lines.slice(1); + if (nested) { + value.nested = nested; } items.push({ - key: `it:${pos}`, + key: `it:${n.from}`, value, }); - } + }); console.log("Found", items.length, "item(s)"); await batchSet(name, items); } diff --git a/plugs/core/materialized_queries.ts b/plugs/core/materialized_queries.ts index 383d6a2f..d9e7c583 100644 --- a/plugs/core/materialized_queries.ts +++ b/plugs/core/materialized_queries.ts @@ -49,13 +49,13 @@ export async function updateMaterializedQueriesOnPage(pageName: string) { for (let { key, page, - value: { task, complete, children }, + 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}` + - (children ? "\n" + children.join("\n") : "") + (nested ? "\n " + nested : "") ); } } @@ -78,13 +78,12 @@ export async function updateMaterializedQueriesOnPage(pageName: string) { for (let { key, page, - value: { item, children }, + value: { item, nested }, } of await scanPrefixGlobal("it:")) { let [, pos] = key.split(":"); if (!filter || (filter && item.includes(filter))) { results.push( - `* [[${page}@${pos}]] ${item}` + - (children ? "\n" + children.join("\n") : "") + `* [[${page}@${pos}]] ${item}` + (nested ? "\n " + nested : "") ); } } diff --git a/plugs/core/navigate.ts b/plugs/core/navigate.ts index 3ae68a3b..1cf36351 100644 --- a/plugs/core/navigate.ts +++ b/plugs/core/navigate.ts @@ -2,7 +2,8 @@ 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 {nodeAtPos, parse} from "plugos-silverbullet-syscall/markdown"; +import {parseMarkdown} from "plugos-silverbullet-syscall/markdown"; +import {nodeAtPos} from "../lib/tree"; import type {MarkdownTree} from "../../common/tree"; const materializedQueryPrefix = /