Rewrote some navigation stuff based on new parser
This commit is contained in:
parent
16bf0d866d
commit
07453d638b
@ -3,13 +3,13 @@ import {MarkdownTree, nodeAtPos, parse, render} from "../tree";
|
||||
|
||||
export function markdownSyscalls(): SysCallMapping {
|
||||
return {
|
||||
parse(ctx, text: string): MarkdownTree {
|
||||
"markdown.parse": (ctx, text: string): MarkdownTree => {
|
||||
return parse(text);
|
||||
},
|
||||
nodeAtPos(ctx, mdTree: MarkdownTree, pos: number): MarkdownTree | null {
|
||||
"markdown.nodeAtPos": (ctx, mdTree: MarkdownTree, pos: number): MarkdownTree | null => {
|
||||
return nodeAtPos(mdTree, pos);
|
||||
},
|
||||
render(ctx, mdTree: MarkdownTree): string {
|
||||
"markdown.render": (ctx, mdTree: MarkdownTree): string => {
|
||||
return render(mdTree);
|
||||
},
|
||||
};
|
||||
|
@ -8,7 +8,7 @@ export async function parse(text: string): Promise<MarkdownTree> {
|
||||
export async function nodeAtPos(
|
||||
mdTree: MarkdownTree,
|
||||
pos: number
|
||||
): Promise<any | null> {
|
||||
): Promise<MarkdownTree | null> {
|
||||
return syscall("markdown.nodeAtPos", mdTree, pos);
|
||||
}
|
||||
|
||||
|
@ -10,11 +10,7 @@ import {System} from "../system";
|
||||
import {EndpointHook, EndpointHookT} from "../hooks/endpoint";
|
||||
import {safeRun} from "../util";
|
||||
import knex from "knex";
|
||||
import {
|
||||
ensureTable,
|
||||
storeReadSyscalls,
|
||||
storeWriteSyscalls,
|
||||
} from "../syscalls/store.knex_node";
|
||||
import {ensureTable, storeSyscalls} from "../syscalls/store.knex_node";
|
||||
import {fetchSyscalls} from "../syscalls/fetch.node";
|
||||
import {EventHook, EventHookT} from "../hooks/event";
|
||||
import {eventSyscalls} from "../syscalls/event";
|
||||
@ -55,16 +51,11 @@ safeRun(async () => {
|
||||
system.addHook(new NodeCronHook());
|
||||
let eventHook = new EventHook();
|
||||
system.addHook(eventHook);
|
||||
system.registerSyscalls("event", [], eventSyscalls(eventHook));
|
||||
system.registerSyscalls([], eventSyscalls(eventHook));
|
||||
system.addHook(new EndpointHook(app, ""));
|
||||
system.registerSyscalls("shell", [], shellSyscalls("."));
|
||||
system.registerSyscalls("fetch", [], fetchSyscalls());
|
||||
system.registerSyscalls(
|
||||
"store",
|
||||
[],
|
||||
storeWriteSyscalls(db, "item"),
|
||||
storeReadSyscalls(db, "item")
|
||||
);
|
||||
system.registerSyscalls([], shellSyscalls("."));
|
||||
system.registerSyscalls([], fetchSyscalls());
|
||||
system.registerSyscalls([], storeSyscalls(db, "item"));
|
||||
app.listen(args.port, () => {
|
||||
console.log(`Plugbox server listening on port ${args.port}`);
|
||||
});
|
||||
|
@ -4,7 +4,7 @@ import { System } from "./system";
|
||||
|
||||
test("Run a Node sandbox", async () => {
|
||||
let system = new System("server");
|
||||
system.registerSyscalls("", [], {
|
||||
system.registerSyscalls([], {
|
||||
addNumbers: (ctx, a, b) => {
|
||||
return a + b;
|
||||
},
|
||||
@ -12,12 +12,12 @@ test("Run a Node sandbox", async () => {
|
||||
throw new Error("#fail");
|
||||
},
|
||||
});
|
||||
system.registerSyscalls("", ["restricted"], {
|
||||
system.registerSyscalls(["restricted"], {
|
||||
restrictedSyscall: () => {
|
||||
return "restricted";
|
||||
},
|
||||
});
|
||||
system.registerSyscalls("", ["dangerous"], {
|
||||
system.registerSyscalls(["dangerous"], {
|
||||
dangerousSyscall: () => {
|
||||
return "yay";
|
||||
},
|
||||
|
@ -3,7 +3,7 @@ import { EventHook } from "../hooks/event";
|
||||
|
||||
export function eventSyscalls(eventHook: EventHook): SysCallMapping {
|
||||
return {
|
||||
async dispatch(ctx, eventName: string, data: any) {
|
||||
"event.dispatch": async(ctx, eventName: string, data: any) => {
|
||||
return eventHook.dispatchEvent(eventName, data);
|
||||
},
|
||||
};
|
||||
|
@ -3,11 +3,11 @@ import { SysCallMapping } from "../system";
|
||||
|
||||
export function fetchSyscalls(): SysCallMapping {
|
||||
return {
|
||||
async json(ctx, url: RequestInfo, init: RequestInit) {
|
||||
"fetch.json": async (ctx, url: RequestInfo, init: RequestInit) => {
|
||||
let resp = await fetch(url, init);
|
||||
return resp.json();
|
||||
},
|
||||
async text(ctx, url: RequestInfo, init: RequestInit) {
|
||||
"fetch.text": async(ctx, url: RequestInfo, init: RequestInit) => {
|
||||
let resp = await fetch(url, init);
|
||||
return resp.text();
|
||||
},
|
||||
|
@ -6,7 +6,7 @@ const execFilePromise = promisify(execFile);
|
||||
|
||||
export default function (cwd: string): SysCallMapping {
|
||||
return {
|
||||
run: async (
|
||||
"shell.run": async (
|
||||
ctx,
|
||||
cmd: string,
|
||||
args: string[]
|
||||
|
@ -8,7 +8,7 @@ require("fake-indexeddb/auto");
|
||||
|
||||
test("Test store", async () => {
|
||||
let system = new System("server");
|
||||
system.registerSyscalls("store", [], storeSyscalls("test", "test"));
|
||||
system.registerSyscalls([], storeSyscalls("test", "test"));
|
||||
let plug = await system.load(
|
||||
"test",
|
||||
{
|
||||
|
@ -17,26 +17,26 @@ export function storeSyscalls(
|
||||
const items = db.table(tableName);
|
||||
|
||||
return {
|
||||
async delete(ctx, key: string) {
|
||||
"store.delete": async (ctx, key: string) => {
|
||||
await items.delete(key);
|
||||
},
|
||||
|
||||
async deletePrefix(ctx, prefix: string) {
|
||||
"store.deletePrefix": async (ctx, prefix: string) => {
|
||||
await items.where("key").startsWith(prefix).delete();
|
||||
},
|
||||
|
||||
async deleteAll() {
|
||||
"store.deleteAll": async () => {
|
||||
await items.clear();
|
||||
},
|
||||
|
||||
async set(ctx, key: string, value: any) {
|
||||
"store.set": async (ctx, key: string, value: any) => {
|
||||
await items.put({
|
||||
key,
|
||||
value,
|
||||
});
|
||||
},
|
||||
|
||||
async batchSet(ctx, kvs: KV[]) {
|
||||
"store.batchSet": async (ctx, kvs: KV[]) => {
|
||||
await items.bulkPut(
|
||||
kvs.map(({ key, value }) => ({
|
||||
key,
|
||||
@ -45,17 +45,17 @@ export function storeSyscalls(
|
||||
);
|
||||
},
|
||||
|
||||
async get(ctx, key: string): Promise<any | null> {
|
||||
"store.get": async (ctx, key: string): Promise<any | null> => {
|
||||
let result = await items.get({
|
||||
key,
|
||||
});
|
||||
return result ? result.value : null;
|
||||
},
|
||||
|
||||
async queryPrefix(
|
||||
"store.queryPrefix": async (
|
||||
ctx,
|
||||
keyPrefix: string
|
||||
): Promise<{ key: string; value: any }[]> {
|
||||
): Promise<{ key: string; value: any }[]> => {
|
||||
let results = await items.where("key").startsWith(keyPrefix).toArray();
|
||||
return results.map((result) => ({
|
||||
key: result.key,
|
||||
|
@ -1,11 +1,7 @@
|
||||
import {createSandbox} from "../environments/node_sandbox";
|
||||
import {expect, test} from "@jest/globals";
|
||||
import {System} from "../system";
|
||||
import {
|
||||
ensureTable,
|
||||
storeReadSyscalls,
|
||||
storeWriteSyscalls,
|
||||
} from "./store.knex_node";
|
||||
import {ensureTable, storeSyscalls} from "./store.knex_node";
|
||||
import knex from "knex";
|
||||
import fs from "fs/promises";
|
||||
|
||||
@ -19,12 +15,7 @@ test("Test store", async () => {
|
||||
});
|
||||
await ensureTable(db, "test_table");
|
||||
let system = new System("server");
|
||||
system.registerSyscalls(
|
||||
"store",
|
||||
[],
|
||||
storeWriteSyscalls(db, "test_table"),
|
||||
storeReadSyscalls(db, "test_table")
|
||||
);
|
||||
system.registerSyscalls([], storeSyscalls(db, "test_table"));
|
||||
let plug = await system.load(
|
||||
"test",
|
||||
{
|
||||
|
@ -23,21 +23,21 @@ export async function ensureTable(db: Knex<any, unknown>, tableName: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export function storeWriteSyscalls(
|
||||
export function storeSyscalls(
|
||||
db: Knex<any, unknown>,
|
||||
tableName: string
|
||||
): SysCallMapping {
|
||||
const apiObj: SysCallMapping = {
|
||||
delete: async (ctx, key: string) => {
|
||||
"store.delete": async (ctx, key: string) => {
|
||||
await db<Item>(tableName).where({ key }).del();
|
||||
},
|
||||
deletePrefix: async (ctx, prefix: string) => {
|
||||
"store.deletePrefix": async (ctx, prefix: string) => {
|
||||
return db<Item>(tableName).andWhereLike("key", `${prefix}%`).del();
|
||||
},
|
||||
deleteAll: async (ctx) => {
|
||||
"store.deleteAll": async (ctx) => {
|
||||
await db<Item>(tableName).del();
|
||||
},
|
||||
set: async (ctx, key: string, value: any) => {
|
||||
"store.set": async (ctx, key: string, value: any) => {
|
||||
let changed = await db<Item>(tableName)
|
||||
.where({ key })
|
||||
.update("value", JSON.stringify(value));
|
||||
@ -49,26 +49,17 @@ export function storeWriteSyscalls(
|
||||
}
|
||||
},
|
||||
// TODO: Optimize
|
||||
batchSet: async (ctx, kvs: KV[]) => {
|
||||
"store.batchSet": async (ctx, kvs: KV[]) => {
|
||||
for (let { key, value } of kvs) {
|
||||
await apiObj.set(ctx, key, value);
|
||||
await apiObj["store.set"](ctx, key, value);
|
||||
}
|
||||
},
|
||||
batchDelete: async (ctx, keys: string[]) => {
|
||||
"store.batchDelete": async (ctx, keys: string[]) => {
|
||||
for (let key of keys) {
|
||||
await apiObj.delete(ctx, key);
|
||||
await apiObj["store.delete"](ctx, key);
|
||||
}
|
||||
},
|
||||
};
|
||||
return apiObj;
|
||||
}
|
||||
|
||||
export function storeReadSyscalls(
|
||||
db: Knex<any, unknown>,
|
||||
tableName: string
|
||||
): SysCallMapping {
|
||||
return {
|
||||
get: async (ctx, key: string): Promise<any | null> => {
|
||||
"store.get": async (ctx, key: string): Promise<any | null> => {
|
||||
let result = await db<Item>(tableName).where({ key }).select("value");
|
||||
if (result.length) {
|
||||
return JSON.parse(result[0].value);
|
||||
@ -76,7 +67,7 @@ export function storeReadSyscalls(
|
||||
return null;
|
||||
}
|
||||
},
|
||||
queryPrefix: async (ctx, prefix: string) => {
|
||||
"store.queryPrefix": async (ctx, prefix: string) => {
|
||||
return (
|
||||
await db<Item>(tableName)
|
||||
.andWhereLike("key", `${prefix}%`)
|
||||
@ -87,4 +78,5 @@ export function storeReadSyscalls(
|
||||
}));
|
||||
},
|
||||
};
|
||||
return apiObj;
|
||||
}
|
||||
|
@ -29,31 +29,32 @@ type Syscall = {
|
||||
};
|
||||
|
||||
export class System<HookT> extends EventEmitter<SystemEvents<HookT>> {
|
||||
readonly runtimeEnv: RuntimeEnvironment;
|
||||
protected plugs = new Map<string, Plug<HookT>>();
|
||||
protected registeredSyscalls = new Map<string, Syscall>();
|
||||
protected enabledHooks = new Set<Hook<HookT>>();
|
||||
|
||||
readonly runtimeEnv: RuntimeEnvironment;
|
||||
|
||||
constructor(env: RuntimeEnvironment) {
|
||||
super();
|
||||
this.runtimeEnv = env;
|
||||
}
|
||||
|
||||
get loadedPlugs(): Map<string, Plug<HookT>> {
|
||||
return this.plugs;
|
||||
}
|
||||
|
||||
addHook(feature: Hook<HookT>) {
|
||||
this.enabledHooks.add(feature);
|
||||
feature.apply(this);
|
||||
}
|
||||
|
||||
registerSyscalls(
|
||||
namespace: string,
|
||||
requiredCapabilities: string[],
|
||||
...registrationObjects: SysCallMapping[]
|
||||
) {
|
||||
for (const registrationObject of registrationObjects) {
|
||||
for (let [name, callback] of Object.entries(registrationObject)) {
|
||||
const callName = namespace ? `${namespace}.${name}` : name;
|
||||
this.registeredSyscalls.set(callName, {
|
||||
this.registeredSyscalls.set(name, {
|
||||
requiredPermissions: requiredCapabilities,
|
||||
callback,
|
||||
});
|
||||
@ -116,10 +117,6 @@ export class System<HookT> extends EventEmitter<SystemEvents<HookT>> {
|
||||
this.plugs.delete(name);
|
||||
}
|
||||
|
||||
get loadedPlugs(): Map<string, Plug<HookT>> {
|
||||
return this.plugs;
|
||||
}
|
||||
|
||||
toJSON(): SystemJSON<HookT> {
|
||||
let plugJSON: { [key: string]: Manifest<HookT> } = {};
|
||||
for (let [name, plug] of this.plugs) {
|
||||
|
@ -1,62 +1,54 @@
|
||||
import {ClickEvent} from "../../webapp/app_event";
|
||||
import {updateMaterializedQueriesCommand} from "./materialized_queries";
|
||||
import {
|
||||
getSyntaxNodeAtPos,
|
||||
getSyntaxNodeUnderCursor,
|
||||
getText,
|
||||
navigate as navigateTo,
|
||||
openUrl,
|
||||
} from "plugos-silverbullet-syscall/editor";
|
||||
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 type {MarkdownTree} from "../../common/tree";
|
||||
|
||||
const materializedQueryPrefix = /<!--\s*#query\s+/;
|
||||
|
||||
async function actionClickOrActionEnter(syntaxNode: any) {
|
||||
if (!syntaxNode) {
|
||||
async function actionClickOrActionEnter(mdTree: MarkdownTree | null) {
|
||||
if (!mdTree) {
|
||||
return;
|
||||
}
|
||||
console.log("Attempting to navigate based on syntax node", syntaxNode);
|
||||
switch (syntaxNode.name) {
|
||||
console.log("Attempting to navigate based on syntax node", mdTree);
|
||||
switch (mdTree.type) {
|
||||
case "WikiLinkPage":
|
||||
let pageLink = syntaxNode.text;
|
||||
let pos = 0;
|
||||
let pageLink = mdTree.children![0].text!;
|
||||
let pos = "0";
|
||||
if (pageLink.includes("@")) {
|
||||
[pageLink, pos] = syntaxNode.text.split("@");
|
||||
[pageLink, pos] = pageLink.split("@");
|
||||
}
|
||||
await navigateTo(pageLink, +pos);
|
||||
break;
|
||||
case "URL":
|
||||
await openUrl(syntaxNode.text);
|
||||
await openUrl(mdTree.children![0].text!);
|
||||
break;
|
||||
case "CommentBlock":
|
||||
if (syntaxNode.text.match(materializedQueryPrefix)) {
|
||||
if (mdTree.children![0].text!.match(materializedQueryPrefix)) {
|
||||
await updateMaterializedQueriesCommand();
|
||||
}
|
||||
break;
|
||||
case "Link":
|
||||
// Markdown link: [bla](URLHERE) needs extraction
|
||||
let match = /\[[^\\]+\]\(([^\)]+)\)/.exec(syntaxNode.text);
|
||||
if (match) {
|
||||
await openUrl(match[1]);
|
||||
}
|
||||
await openUrl(mdTree.children![4].children![0].text!);
|
||||
break;
|
||||
case "TaskMarker":
|
||||
await taskToggleAtPos(syntaxNode.from + 1);
|
||||
await taskToggleAtPos(mdTree.from + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
export async function linkNavigate() {
|
||||
await actionClickOrActionEnter(await getSyntaxNodeUnderCursor());
|
||||
let mdTree = await parse(await getText());
|
||||
let newNode = await nodeAtPos(mdTree, await getCursor());
|
||||
await actionClickOrActionEnter(newNode);
|
||||
}
|
||||
|
||||
export async function clickNavigate(event: ClickEvent) {
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
let syntaxNode = await getSyntaxNodeAtPos(event.pos);
|
||||
let mdTree = await parse(await getText());
|
||||
let newNode = await nodeAtPos(mdTree, event.pos);
|
||||
console.log("New node", newNode);
|
||||
await actionClickOrActionEnter(syntaxNode);
|
||||
await actionClickOrActionEnter(newNode);
|
||||
}
|
||||
}
|
||||
|
@ -51,13 +51,13 @@ export class ExpressServer {
|
||||
useNullAsDefault: true,
|
||||
});
|
||||
|
||||
system.registerSyscalls("shell", ["shell"], shellSyscalls(rootPath));
|
||||
system.registerSyscalls(["shell"], shellSyscalls(rootPath));
|
||||
system.addHook(new NodeCronHook());
|
||||
|
||||
system.registerSyscalls("index", [], pageIndexSyscalls(this.db));
|
||||
system.registerSyscalls("space", [], spaceSyscalls(this.storage));
|
||||
system.registerSyscalls("event", [], eventSyscalls(this.eventHook));
|
||||
system.registerSyscalls("markdown", [], markdownSyscalls());
|
||||
system.registerSyscalls([], pageIndexSyscalls(this.db));
|
||||
system.registerSyscalls([], spaceSyscalls(this.storage));
|
||||
system.registerSyscalls([], eventSyscalls(this.eventHook));
|
||||
system.registerSyscalls([], markdownSyscalls());
|
||||
system.addHook(new EndpointHook(app, "/_"));
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,7 @@
|
||||
import {Knex} from "knex";
|
||||
import {SysCallMapping} from "../../plugos/system";
|
||||
|
||||
import {
|
||||
ensureTable,
|
||||
storeReadSyscalls,
|
||||
storeWriteSyscalls,
|
||||
} from "../../plugos/syscalls/store.knex_node";
|
||||
import {ensureTable, storeSyscalls,} from "../../plugos/syscalls/store.knex_node";
|
||||
|
||||
type IndexItem = {
|
||||
page: string;
|
||||
@ -52,39 +48,38 @@ export async function ensurePageIndexTable(db: Knex<any, unknown>) {
|
||||
}
|
||||
|
||||
export function pageIndexSyscalls(db: Knex<any, unknown>): SysCallMapping {
|
||||
const readCalls = storeReadSyscalls(db, "page_index");
|
||||
const writeCalls = storeWriteSyscalls(db, "page_index");
|
||||
const storeCalls = storeSyscalls(db, "page_index");
|
||||
const apiObj: SysCallMapping = {
|
||||
set: async (ctx, page: string, key: string, value: any) => {
|
||||
await writeCalls.set(ctx, pageKey(page, key), value);
|
||||
await writeCalls.set(ctx, globalKey(page, key), value);
|
||||
"index.set": async (ctx, page: string, key: string, value: any) => {
|
||||
await storeCalls["store.set"](ctx, pageKey(page, key), value);
|
||||
await storeCalls["store.set"](ctx, globalKey(page, key), value);
|
||||
},
|
||||
batchSet: async (ctx, page: string, kvs: KV[]) => {
|
||||
"index.batchSet": async (ctx, page: string, kvs: KV[]) => {
|
||||
for (let { key, value } of kvs) {
|
||||
await apiObj.set(ctx, page, key, value);
|
||||
await apiObj["index.set"](ctx, page, key, value);
|
||||
}
|
||||
},
|
||||
delete: async (ctx, page: string, key: string) => {
|
||||
await writeCalls.delete(ctx, pageKey(page, key));
|
||||
await writeCalls.delete(ctx, globalKey(page, key));
|
||||
"index.delete": async (ctx, page: string, key: string) => {
|
||||
await storeCalls["store.delete"](ctx, pageKey(page, key));
|
||||
await storeCalls["store.delete"](ctx, globalKey(page, key));
|
||||
},
|
||||
get: async (ctx, page: string, key: string) => {
|
||||
return readCalls.get(ctx, pageKey(page, key));
|
||||
"index.get": async (ctx, page: string, key: string) => {
|
||||
return storeCalls["store.get"](ctx, pageKey(page, key));
|
||||
},
|
||||
scanPrefixForPage: async (ctx, page: string, prefix: string) => {
|
||||
return (await readCalls.queryPrefix(ctx, pageKey(page, prefix))).map(
|
||||
({ key, value }: { key: string; value: any }) => {
|
||||
"index.scanPrefixForPage": async (ctx, page: string, prefix: string) => {
|
||||
return (
|
||||
await storeCalls["store.queryPrefix"](ctx, pageKey(page, prefix))
|
||||
).map(({ key, value }: { key: string; value: any }) => {
|
||||
const { key: pageKey } = unpackPageKey(key);
|
||||
return {
|
||||
page,
|
||||
key: pageKey,
|
||||
value,
|
||||
};
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
scanPrefixGlobal: async (ctx, prefix: string) => {
|
||||
return (await readCalls.queryPrefix(ctx, `k~${prefix}`)).map(
|
||||
"index.scanPrefixGlobal": async (ctx, prefix: string) => {
|
||||
return (await storeCalls["store.queryPrefix"](ctx, `k~${prefix}`)).map(
|
||||
({ key, value }: { key: string; value: any }) => {
|
||||
const { page, key: pageKey } = unpackGlobalKey(key);
|
||||
return {
|
||||
@ -95,22 +90,22 @@ export function pageIndexSyscalls(db: Knex<any, unknown>): SysCallMapping {
|
||||
}
|
||||
);
|
||||
},
|
||||
clearPageIndexForPage: async (ctx, page: string) => {
|
||||
await apiObj.deletePrefixForPage(ctx, page, "");
|
||||
"index.clearPageIndexForPage": async (ctx, page: string) => {
|
||||
await apiObj["index.deletePrefixForPage"](ctx, page, "");
|
||||
},
|
||||
deletePrefixForPage: async (ctx, page: string, prefix: string) => {
|
||||
"index.deletePrefixForPage": async (ctx, page: string, prefix: string) => {
|
||||
// Collect all global keys for this page to delete
|
||||
let keysToDelete = (
|
||||
await readCalls.queryPrefix(ctx, pageKey(page, prefix))
|
||||
await storeCalls["store.queryPrefix"](ctx, pageKey(page, prefix))
|
||||
).map(({ key }: { key: string; value: string }) =>
|
||||
globalKey(page, unpackPageKey(key).key)
|
||||
);
|
||||
// Delete all page keys
|
||||
await writeCalls.deletePrefix(ctx, pageKey(page, prefix));
|
||||
await writeCalls.batchDelete(ctx, keysToDelete);
|
||||
await storeCalls["store.deletePrefix"](ctx, pageKey(page, prefix));
|
||||
await storeCalls["store.batchDelete"](ctx, keysToDelete);
|
||||
},
|
||||
clearPageIndex: async (ctx) => {
|
||||
await writeCalls.deleteAll(ctx);
|
||||
"index.clearPageIndex": async (ctx) => {
|
||||
await storeCalls["store.deleteAll"](ctx);
|
||||
},
|
||||
};
|
||||
return apiObj;
|
||||
|
@ -4,19 +4,23 @@ import { Storage } from "../disk_storage";
|
||||
|
||||
export default (storage: Storage): SysCallMapping => {
|
||||
return {
|
||||
listPages: (ctx): Promise<PageMeta[]> => {
|
||||
"space.listPages": (ctx): Promise<PageMeta[]> => {
|
||||
return storage.listPages();
|
||||
},
|
||||
readPage: async (
|
||||
"space.readPage": async (
|
||||
ctx,
|
||||
name: string
|
||||
): Promise<{ text: string; meta: PageMeta }> => {
|
||||
return storage.readPage(name);
|
||||
},
|
||||
writePage: async (ctx, name: string, text: string): Promise<PageMeta> => {
|
||||
"space.writePage": async (
|
||||
ctx,
|
||||
name: string,
|
||||
text: string
|
||||
): Promise<PageMeta> => {
|
||||
return storage.writePage(name, text);
|
||||
},
|
||||
deletePage: async (ctx, name: string) => {
|
||||
"space.deletePage": async (ctx, name: string) => {
|
||||
return storage.deletePage(name);
|
||||
},
|
||||
};
|
||||
|
@ -47,7 +47,6 @@ import {CompleterHook} from "./hooks/completer";
|
||||
import {pasteLinkExtension} from "./editor_paste";
|
||||
import {markdownSyscalls} from "../common/syscalls/markdown";
|
||||
|
||||
|
||||
class PageState {
|
||||
scrollTop: number;
|
||||
selection: EditorSelection;
|
||||
@ -61,11 +60,9 @@ class PageState {
|
||||
const saveInterval = 2000;
|
||||
|
||||
export class Editor implements AppEventDispatcher {
|
||||
private system = new System<SilverBulletHooks>("client");
|
||||
readonly commandHook: CommandHook;
|
||||
readonly slashCommandHook: SlashCommandHook;
|
||||
readonly completerHook: CompleterHook;
|
||||
|
||||
openPages = new Map<string, PageState>();
|
||||
editorView?: EditorView;
|
||||
viewState: AppViewState;
|
||||
@ -73,6 +70,8 @@ export class Editor implements AppEventDispatcher {
|
||||
space: Space;
|
||||
pageNavigator: PathPageNavigator;
|
||||
eventHook: EventHook;
|
||||
saveTimeout: any;
|
||||
private system = new System<SilverBulletHooks>("client");
|
||||
|
||||
constructor(space: Space, parent: Element) {
|
||||
this.space = space;
|
||||
@ -110,11 +109,15 @@ export class Editor implements AppEventDispatcher {
|
||||
});
|
||||
this.pageNavigator = new PathPageNavigator();
|
||||
|
||||
this.system.registerSyscalls("editor", [], editorSyscalls(this));
|
||||
this.system.registerSyscalls("space", [], spaceSyscalls(this));
|
||||
this.system.registerSyscalls("index", [], indexerSyscalls(this.space));
|
||||
this.system.registerSyscalls("system", [], systemSyscalls(this.space));
|
||||
this.system.registerSyscalls("markdown", [], markdownSyscalls());
|
||||
this.system.registerSyscalls([], editorSyscalls(this));
|
||||
this.system.registerSyscalls([], spaceSyscalls(this));
|
||||
this.system.registerSyscalls([], indexerSyscalls(this.space));
|
||||
this.system.registerSyscalls([], systemSyscalls(this.space));
|
||||
this.system.registerSyscalls([], markdownSyscalls());
|
||||
}
|
||||
|
||||
get currentPage(): string | undefined {
|
||||
return this.viewState.currentPage;
|
||||
}
|
||||
|
||||
async init() {
|
||||
@ -180,8 +183,6 @@ export class Editor implements AppEventDispatcher {
|
||||
}
|
||||
}
|
||||
|
||||
saveTimeout: any;
|
||||
|
||||
async save(immediate: boolean = false): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.viewState.unsavedChanges) {
|
||||
@ -236,10 +237,6 @@ export class Editor implements AppEventDispatcher {
|
||||
return this.eventHook.dispatchEvent(name, data);
|
||||
}
|
||||
|
||||
get currentPage(): string | undefined {
|
||||
return this.viewState.currentPage;
|
||||
}
|
||||
|
||||
createEditorState(pageName: string, text: string): EditorState {
|
||||
let commandKeyBindings: KeyBinding[] = [];
|
||||
for (let def of this.commandHook.editorCommands.values()) {
|
||||
|
@ -28,37 +28,37 @@ function ensureAnchor(expr: any, start: boolean) {
|
||||
|
||||
export function editorSyscalls(editor: Editor): SysCallMapping {
|
||||
return {
|
||||
getCurrentPage: (): string => {
|
||||
"editor.getCurrentPage": (): string => {
|
||||
return editor.currentPage!;
|
||||
},
|
||||
getText: () => {
|
||||
"editor.getText": () => {
|
||||
return editor.editorView?.state.sliceDoc();
|
||||
},
|
||||
getCursor: (): number => {
|
||||
"editor.getCursor": (): number => {
|
||||
return editor.editorView!.state.selection.main.from;
|
||||
},
|
||||
save: async () => {
|
||||
"editor.save": async () => {
|
||||
return editor.save(true);
|
||||
},
|
||||
navigate: async (ctx, name: string, pos: number) => {
|
||||
"editor.navigate": async (ctx, name: string, pos: number) => {
|
||||
await editor.navigate(name, pos);
|
||||
},
|
||||
reloadPage: async (ctx) => {
|
||||
"editor.reloadPage": async (ctx) => {
|
||||
await editor.reloadPage();
|
||||
},
|
||||
openUrl: async (ctx, url: string) => {
|
||||
"editor.openUrl": async (ctx, url: string) => {
|
||||
window.open(url, "_blank")!.focus();
|
||||
},
|
||||
flashNotification: (ctx, message: string) => {
|
||||
"editor.flashNotification": (ctx, message: string) => {
|
||||
editor.flashNotification(message);
|
||||
},
|
||||
showRhs: (ctx, html: string) => {
|
||||
"editor.showRhs": (ctx, html: string) => {
|
||||
editor.viewDispatch({
|
||||
type: "show-rhs",
|
||||
html: html,
|
||||
});
|
||||
},
|
||||
insertAtPos: (ctx, text: string, pos: number) => {
|
||||
"editor.insertAtPos": (ctx, text: string, pos: number) => {
|
||||
editor.editorView!.dispatch({
|
||||
changes: {
|
||||
insert: text,
|
||||
@ -66,7 +66,7 @@ export function editorSyscalls(editor: Editor): SysCallMapping {
|
||||
},
|
||||
});
|
||||
},
|
||||
replaceRange: (ctx, from: number, to: number, text: string) => {
|
||||
"editor.replaceRange": (ctx, from: number, to: number, text: string) => {
|
||||
editor.editorView!.dispatch({
|
||||
changes: {
|
||||
insert: text,
|
||||
@ -75,14 +75,14 @@ export function editorSyscalls(editor: Editor): SysCallMapping {
|
||||
},
|
||||
});
|
||||
},
|
||||
moveCursor: (ctx, pos: number) => {
|
||||
"editor.moveCursor": (ctx, pos: number) => {
|
||||
editor.editorView!.dispatch({
|
||||
selection: {
|
||||
anchor: pos,
|
||||
},
|
||||
});
|
||||
},
|
||||
insertAtCursor: (ctx, text: string) => {
|
||||
"editor.insertAtCursor": (ctx, text: string) => {
|
||||
let editorView = editor.editorView!;
|
||||
let from = editorView.state.selection.main.from;
|
||||
editorView.dispatch({
|
||||
@ -95,7 +95,7 @@ export function editorSyscalls(editor: Editor): SysCallMapping {
|
||||
},
|
||||
});
|
||||
},
|
||||
getSyntaxNodeUnderCursor: (): SyntaxNode | undefined => {
|
||||
"editor.getSyntaxNodeUnderCursor": (): SyntaxNode | undefined => {
|
||||
const editorState = editor.editorView!.state;
|
||||
let selection = editorState.selection.main;
|
||||
if (selection.empty) {
|
||||
@ -110,13 +110,13 @@ export function editorSyscalls(editor: Editor): SysCallMapping {
|
||||
}
|
||||
}
|
||||
},
|
||||
getLineUnderCursor: (): string => {
|
||||
"editor.getLineUnderCursor": (): string => {
|
||||
const editorState = editor.editorView!.state;
|
||||
let selection = editorState.selection.main;
|
||||
let line = editorState.doc.lineAt(selection.from);
|
||||
return editorState.sliceDoc(line.from, line.to);
|
||||
},
|
||||
matchBefore: (
|
||||
"editor.matchBefore": (
|
||||
ctx,
|
||||
regexp: string
|
||||
): { from: number; to: number; text: string } | null => {
|
||||
@ -135,7 +135,7 @@ export function editorSyscalls(editor: Editor): SysCallMapping {
|
||||
}
|
||||
return null;
|
||||
},
|
||||
getSyntaxNodeAtPos: (ctx, pos: number): SyntaxNode | undefined => {
|
||||
"editor.getSyntaxNodeAtPos": (ctx, pos: number): SyntaxNode | undefined => {
|
||||
const editorState = editor.editorView!.state;
|
||||
let node = syntaxTree(editorState).resolveInner(pos);
|
||||
if (node) {
|
||||
@ -147,10 +147,14 @@ export function editorSyscalls(editor: Editor): SysCallMapping {
|
||||
};
|
||||
}
|
||||
},
|
||||
dispatch: (ctx, change: Transaction) => {
|
||||
"editor.dispatch": (ctx, change: Transaction) => {
|
||||
editor.editorView!.dispatch(change);
|
||||
},
|
||||
prompt: (ctx, message: string, defaultValue = ""): string | null => {
|
||||
"editor.prompt": (
|
||||
ctx,
|
||||
message: string,
|
||||
defaultValue = ""
|
||||
): string | null => {
|
||||
return prompt(message, defaultValue);
|
||||
},
|
||||
};
|
||||
|
@ -5,13 +5,13 @@ import {transportSyscalls} from "../../plugos/syscalls/transport";
|
||||
export function indexerSyscalls(space: Space): SysCallMapping {
|
||||
return transportSyscalls(
|
||||
[
|
||||
"scanPrefixForPage",
|
||||
"scanPrefixGlobal",
|
||||
"get",
|
||||
"set",
|
||||
"batchSet",
|
||||
"delete",
|
||||
"index.scanPrefixForPage",
|
||||
"index.scanPrefixGlobal",
|
||||
"index.get",
|
||||
"index.set",
|
||||
"index.batchSet",
|
||||
"index.delete",
|
||||
],
|
||||
(ctx, name, ...args) => space.remoteSyscall(ctx.plug, `index.${name}`, args)
|
||||
(ctx, name, ...args) => space.remoteSyscall(ctx.plug, name, args)
|
||||
);
|
||||
}
|
||||
|
@ -4,19 +4,23 @@ import {PageMeta} from "../../common/types";
|
||||
|
||||
export function spaceSyscalls(editor: Editor): SysCallMapping {
|
||||
return {
|
||||
listPages: async (): Promise<PageMeta[]> => {
|
||||
"space.listPages": async (): Promise<PageMeta[]> => {
|
||||
return [...(await editor.space.listPages())];
|
||||
},
|
||||
readPage: async (
|
||||
"space.readPage": async (
|
||||
ctx,
|
||||
name: string
|
||||
): Promise<{ text: string; meta: PageMeta }> => {
|
||||
return await editor.space.readPage(name);
|
||||
},
|
||||
writePage: async (ctx, name: string, text: string): Promise<PageMeta> => {
|
||||
"space.writePage": async (
|
||||
ctx,
|
||||
name: string,
|
||||
text: string
|
||||
): Promise<PageMeta> => {
|
||||
return await editor.space.writePage(name, text);
|
||||
},
|
||||
deletePage: async (ctx, name: string) => {
|
||||
"space.deletePage": async (ctx, name: string) => {
|
||||
// If we're deleting the current page, navigate to the start page
|
||||
if (editor.currentPage === name) {
|
||||
await editor.navigate("start");
|
||||
|
@ -3,7 +3,11 @@ import { Space } from "../space";
|
||||
|
||||
export function systemSyscalls(space: Space): SysCallMapping {
|
||||
return {
|
||||
async invokeFunctionOnServer(ctx, name: string, ...args: any[]) {
|
||||
"system.invokeFunctionOnServer": async (
|
||||
ctx,
|
||||
name: string,
|
||||
...args: any[]
|
||||
) => {
|
||||
if (!ctx.plug) {
|
||||
throw Error("No plug associated with context");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user