diff --git a/server/types.ts b/common/types.ts similarity index 60% rename from server/types.ts rename to common/types.ts index 6ffc1a57..d782e6f4 100644 --- a/server/types.ts +++ b/common/types.ts @@ -1,5 +1,6 @@ export type PageMeta = { name: string; lastModified: number; - version?: number; + lastOpened?: number; + created?: boolean; }; diff --git a/plugos-silverbullet-syscall/editor.ts b/plugos-silverbullet-syscall/editor.ts new file mode 100644 index 00000000..1f6ca763 --- /dev/null +++ b/plugos-silverbullet-syscall/editor.ts @@ -0,0 +1,93 @@ +import { syscall } from "./syscall"; + +export function getCurrentPage(): Promise { + return syscall("editor.getCurrentPage"); +} + +export function getText(): Promise { + return syscall("editor.getText"); +} + +export function getCursor(): Promise { + return syscall("editor.getCursor"); +} + +export function save(): Promise { + return syscall("editor.save"); +} + +export function navigate(name: string, pos?: number): Promise { + return syscall("editor.navigate", name, pos); +} + +export function reloadPage(): Promise { + return syscall("editor.reloadPage"); +} + +export function openUrl(url: string): Promise { + return syscall("editor.openUrl", url); +} + +export function flashNotification(message: string): Promise { + return syscall("editor.flashNotification", message); +} + +export function showRhs(html: string): Promise { + return syscall("editor.showRhs", html); +} + +export function insertAtPos(text: string, pos: number): Promise { + return syscall("editor.insertAtPos", text, pos); +} + +export function replaceRange( + from: number, + to: number, + text: string +): Promise { + return syscall("editor.replaceRange", from, to, text); +} + +export function moveCursor(pos: number): Promise { + return syscall("editor.moveCursor", pos); +} + +export function insertAtCursor(text: string): Promise { + return syscall("editor.insertAtCursor", text); +} + +export type SyntaxNode = { + name: string; + text: string; + from: number; + to: number; +}; + +export function getSyntaxNodeUnderCursor(): Promise { + return syscall("editor.getSyntaxNodeUnderCursor"); +} + +export function getLineUnderCursor(): Promise { + return syscall("editor.getLineUnderCursor"); +} + +export function matchBefore( + re: string +): Promise<{ from: number; to: number; text: string } | null> { + return syscall("editor.matchBefore", re); +} + +export function getSyntaxNodeAtPos(pos: number): Promise { + return syscall("editor.getSyntaxNodeAtPos", pos); +} + +export function dispatch(change: any): Promise { + return syscall("editor.dispatch", change); +} + +export function prompt( + message: string, + defaultValue = "" +): Promise { + return syscall("editor.prompt", message, defaultValue); +} diff --git a/plugos-silverbullet-syscall/index.ts b/plugos-silverbullet-syscall/index.ts new file mode 100644 index 00000000..5f6ecda6 --- /dev/null +++ b/plugos-silverbullet-syscall/index.ts @@ -0,0 +1,54 @@ +import { syscall } from "./syscall"; + +export type KV = { + key: string; + value: any; +}; + +export async function set( + page: string, + key: string, + value: any +): Promise { + return syscall("index.set", page, key, value); +} + +export async function batchSet(page: string, kvs: KV[]): Promise { + return syscall("index.batchSet", page, kvs); +} + +export async function get(page: string, key: string): Promise { + return syscall("index.get", page, key); +} + +export async function del(page: string, key: string): Promise { + return syscall("index.delete", page, key); +} + +export async function scanPrefixForPage( + page: string, + prefix: string +): Promise<{ key: string; page: string; value: any }[]> { + return syscall("index.scanPrefixForPage", page, prefix); +} + +export async function scanPrefixGlobal( + prefix: string +): Promise<{ key: string; page: string; value: any }[]> { + return syscall("index.scanPrefixGlobal", prefix); +} + +export async function clearPageIndexForPage(page: string): Promise { + return syscall("index.clearPageIndexForPage", page); +} + +export async function deletePrefixForPage( + page: string, + prefix: string +): Promise { + return syscall("index.deletePrefixForPage", page, prefix); +} + +export async function clearPageIndex(): Promise { + return syscall("index.clearPageIndex"); +} diff --git a/plugos-silverbullet-syscall/package.json b/plugos-silverbullet-syscall/package.json new file mode 100644 index 00000000..2f933f0d --- /dev/null +++ b/plugos-silverbullet-syscall/package.json @@ -0,0 +1,4 @@ +{ + "name": "plugos-silverbullet-syscall", + "version": "1.0.0" +} diff --git a/plugos-silverbullet-syscall/space.ts b/plugos-silverbullet-syscall/space.ts new file mode 100644 index 00000000..f2de08ea --- /dev/null +++ b/plugos-silverbullet-syscall/space.ts @@ -0,0 +1,20 @@ +import { syscall } from "./syscall"; +import { PageMeta } from "../common/types"; + +export async function listPages(): Promise { + return syscall("space.listPages"); +} + +export async function readPage( + name: string +): Promise<{ text: string; meta: PageMeta }> { + return syscall("space.readPage", name); +} + +export async function writePage(name: string, text: string): Promise { + return syscall("space.writePage", name, text); +} + +export async function deletePage(name: string): Promise { + return syscall("space.deletePage", name); +} diff --git a/plugs/lib/syscall.ts b/plugos-silverbullet-syscall/syscall.ts similarity index 100% rename from plugs/lib/syscall.ts rename to plugos-silverbullet-syscall/syscall.ts diff --git a/plugos-silverbullet-syscall/system.ts b/plugos-silverbullet-syscall/system.ts new file mode 100644 index 00000000..3e2598cb --- /dev/null +++ b/plugos-silverbullet-syscall/system.ts @@ -0,0 +1,8 @@ +import { syscall } from "./syscall"; + +export async function invokeFunctionOnServer( + name: string, + ...args: any[] +): Promise { + return syscall("system.invokeFunctionOnServer", name, ...args); +} diff --git a/plugos-syscall/event.ts b/plugos-syscall/event.ts new file mode 100644 index 00000000..fa8114c0 --- /dev/null +++ b/plugos-syscall/event.ts @@ -0,0 +1,5 @@ +import { syscall } from "./syscall"; + +export async function dispatch(eventName: string, data: any): Promise { + return syscall("event.dispatch", eventName, data); +} diff --git a/plugos-syscall/fetch.ts b/plugos-syscall/fetch.ts new file mode 100644 index 00000000..766a1a65 --- /dev/null +++ b/plugos-syscall/fetch.ts @@ -0,0 +1,12 @@ +import { syscall } from "./syscall"; + +export async function json(url: RequestInfo, init: RequestInit): Promise { + return syscall("fetch.json", url, init); +} + +export async function text( + url: RequestInfo, + init: RequestInit +): Promise { + return syscall("fetch.text", url, init); +} diff --git a/plugos-syscall/package.json b/plugos-syscall/package.json new file mode 100644 index 00000000..ba9e47a9 --- /dev/null +++ b/plugos-syscall/package.json @@ -0,0 +1,4 @@ +{ + "name": "plugos-syscall", + "version": "1.0.0" +} diff --git a/plugos-syscall/shell.ts b/plugos-syscall/shell.ts new file mode 100644 index 00000000..df9fd374 --- /dev/null +++ b/plugos-syscall/shell.ts @@ -0,0 +1,8 @@ +import { syscall } from "./syscall"; + +export async function run( + cmd: string, + args: string[] +): Promise<{ stdout: string; stderr: string }> { + return syscall("shell.run", cmd, args); +} diff --git a/plugos-syscall/store.ts b/plugos-syscall/store.ts new file mode 100644 index 00000000..4d7a3c25 --- /dev/null +++ b/plugos-syscall/store.ts @@ -0,0 +1,40 @@ +import { syscall } from "./syscall"; + +export type KV = { + key: string; + value: any; +}; + +export async function set(key: string, value: any): Promise { + return syscall("store.set", key, value); +} + +export async function batchSet(kvs: KV[]): Promise { + return syscall("store.batchSet", kvs); +} + +export async function get(key: string): Promise { + return syscall("store.get", key); +} + +export async function del(key: string): Promise { + return syscall("store.delete", key); +} + +export async function batchDel(keys: string[]): Promise { + return syscall("store.batchDelete", keys); +} + +export async function queryPrefix( + prefix: string +): Promise<{ key: string; value: any }[]> { + return syscall("store.scanPrefix", prefix); +} + +export async function deletePrefix(prefix: string): Promise { + return syscall("store.deletePrefix", prefix); +} + +export async function deleteAll(): Promise { + return syscall("store.deleteAll"); +} diff --git a/plugos-syscall/syscall.ts b/plugos-syscall/syscall.ts new file mode 100644 index 00000000..b4d4e5ef --- /dev/null +++ b/plugos-syscall/syscall.ts @@ -0,0 +1,5 @@ +declare global { + function syscall(name: string, ...args: any[]): Promise; +} + +export const syscall = self.syscall; diff --git a/plugs/core/dates.ts b/plugs/core/dates.ts index bdc01440..9c356040 100644 --- a/plugs/core/dates.ts +++ b/plugs/core/dates.ts @@ -1,7 +1,6 @@ -import { syscall } from "../lib/syscall"; +import { insertAtCursor } from "plugos-silverbullet-syscall/editor"; export async function insertToday() { - console.log("Inserting date!"); let niceDate = new Date().toISOString().split("T")[0]; - await syscall("editor.insertAtCursor", niceDate); + await insertAtCursor(niceDate); } diff --git a/plugs/core/item.ts b/plugs/core/item.ts index c58b74eb..093955ca 100644 --- a/plugs/core/item.ts +++ b/plugs/core/item.ts @@ -1,6 +1,7 @@ import { IndexEvent } from "../../webapp/app_event"; import { whiteOutQueries } from "./materialized_queries"; -import { syscall } from "../lib/syscall"; + +import { batchSet } from "plugos-silverbullet-syscall/index"; type Item = { item: string; @@ -35,5 +36,5 @@ export async function indexItems({ name, text }: IndexEvent) { }); } console.log("Found", items.length, "item(s)"); - await syscall("index.batchSet", name, items); + await batchSet(name, items); } diff --git a/plugs/core/markdown.ts b/plugs/core/markdown.ts index 49ae0a93..804cc798 100644 --- a/plugs/core/markdown.ts +++ b/plugs/core/markdown.ts @@ -1,8 +1,8 @@ -import { syscall } from "../lib/syscall"; import mdParser from "../../webapp/parser"; +import { getText } from "plugos-silverbullet-syscall/editor"; export async function renderMD() { - let text = await syscall("editor.getText"); + let text = await getText(); let tree = mdParser.parser.parse(text); let slicesToRemove: [number, number][] = []; diff --git a/plugs/core/markup.ts b/plugs/core/markup.ts index 5e30cb2e..b6c7b4a1 100644 --- a/plugs/core/markup.ts +++ b/plugs/core/markup.ts @@ -1,4 +1,9 @@ -import { syscall } from "../lib/syscall"; +import { + getCursor, + getText, + insertAtPos, + replaceRange, +} from "plugos-silverbullet-syscall/editor"; export async function toggleH1() { await togglePrefix("# "); @@ -13,15 +18,15 @@ function lookBack(s: string, pos: number, backString: string): boolean { } async function togglePrefix(prefix: string) { - let text = (await syscall("editor.getText")) as string; - let pos = (await syscall("editor.getCursor")) as number; + let text = await getText(); + let pos = await getCursor(); if (text[pos] === "\n") { pos--; } while (pos > 0 && text[pos] !== "\n") { if (lookBack(text, pos, prefix)) { // Already has this prefix, let's flip it - await syscall("editor.replaceRange", pos - prefix.length, pos, ""); + await replaceRange(pos - prefix.length, pos, ""); return; } pos--; @@ -29,5 +34,5 @@ async function togglePrefix(prefix: string) { if (pos) { pos++; } - await syscall("editor.insertAtPos", prefix, pos); + await insertAtPos(prefix, pos); } diff --git a/plugs/core/materialized_queries.ts b/plugs/core/materialized_queries.ts index 9bab2b87..493c67e0 100644 --- a/plugs/core/materialized_queries.ts +++ b/plugs/core/materialized_queries.ts @@ -1,7 +1,16 @@ -import { syscall } from "../lib/syscall"; +import { + flashNotification, + getCurrentPage, + reloadPage, + save, +} from "plugos-silverbullet-syscall/editor"; + +import { readPage, writePage } from "plugos-silverbullet-syscall/space"; +import { invokeFunctionOnServer } from "plugos-silverbullet-syscall/system"; +import { scanPrefixGlobal } from "plugos-silverbullet-syscall"; export const queryRegex = - /()(.+?)()/gs; + /()(.+?)()/gs; export function whiteOutQueries(text: string): string { return text.replaceAll(queryRegex, (match) => @@ -25,18 +34,16 @@ async function replaceAsync( } export async function updateMaterializedQueriesCommand() { - await syscall( - "system.invokeFunctionOnServer", - "updateMaterializedQueriesOnPage", - await syscall("editor.getCurrentPage") - ); - await syscall("editor.reloadPage"); - await syscall("editor.flashNotification", "Updated materialized queries"); + const currentPage = await getCurrentPage(); + await save(); + await invokeFunctionOnServer("updateMaterializedQueriesOnPage", currentPage); + await reloadPage(); + await flashNotification("Updated materialized queries"); } // Called from client, running on server export async function updateMaterializedQueriesOnPage(pageName: string) { - let { text } = await syscall("space.readPage", pageName); + let { text } = await readPage(pageName); text = await replaceAsync(text, queryRegex, async (match, ...args) => { let { table, filter, groupBy } = args[args.length - 1]; const startQuery = args[0]; @@ -48,23 +55,20 @@ export async function updateMaterializedQueriesOnPage(pageName: string) { key, page, value: { task, complete, children }, - } of await syscall("index.scanPrefixGlobal", "task:")) { + } of await scanPrefixGlobal("task:")) { let [, pos] = key.split(":"); if (!filter || (filter && task.includes(filter))) { results.push( - `* [${complete ? "x" : " "}] [[${page}@${pos}]] ${task}` + `* [${complete ? "x" : " "}] [[${page}@${pos}]] ${task}` + + (children ? "\n" + children.join("\n") : "") ); - if (children) { - results.push(children.join("\n")); - } } } - return `${startQuery}\n${results.join("\n")}\n${endQuery}`; + return `${startQuery}\n${results.sort().join("\n")}\n${endQuery}`; case "link": let uniqueLinks = new Set(); - for (let {key, page, value: name} of await syscall( - "index.scanPrefixGlobal", - `pl:${pageName}:` + for (let { key, page, value: name } of await scanPrefixGlobal( + `pl:${pageName}:` )) { let [, pos] = key.split(":"); if (!filter || (filter && name.includes(filter))) { @@ -80,20 +84,20 @@ export async function updateMaterializedQueriesOnPage(pageName: string) { key, page, value: { item, children }, - }; of await syscall("index.scanPrefixGlobal", "it:")) { + } of await scanPrefixGlobal("it:")) { let [, pos] = key.split(":"); if (!filter || (filter && item.includes(filter))) { - results.push(`* [[${page}@${pos}]] ${item}`); - if (children) { - results.push(children.join("\n")); - } + results.push( + `* [[${page}@${pos}]] ${item}` + + (children ? "\n" + children.join("\n") : "") + ); } } - return `${startQuery}\n${results.join("\n")}\n${endQuery}`; + return `${startQuery}\n${results.sort().join("\n")}\n${endQuery}`; default: return match; } }); // console.log("New text", text); - await syscall("space.writePage", pageName, text); + await writePage(pageName, text); } diff --git a/plugs/core/navigate.ts b/plugs/core/navigate.ts index a4201f77..ca50f49e 100644 --- a/plugs/core/navigate.ts +++ b/plugs/core/navigate.ts @@ -1,6 +1,11 @@ import { ClickEvent } from "../../webapp/app_event"; -import { syscall } from "../lib/syscall"; import { updateMaterializedQueriesCommand } from "./materialized_queries"; +import { + getSyntaxNodeAtPos, + getSyntaxNodeUnderCursor, + navigate as navigateTo, + openUrl, +} from "plugos-silverbullet-syscall/editor"; const materializedQueryPrefix = /