Breaking Lua API changes:
* tpl -> template.new * define_command -> command.define * define_event_listener -> event.listen * tag -> index.tag Updated in the docspull/1224/head
parent
72b4ecdc36
commit
6078452a6c
|
@ -62,7 +62,7 @@ export abstract class CommonSystem {
|
|||
Object.keys(this.scriptEnv.eventHandlers).length,
|
||||
"event handlers from space-script",
|
||||
);
|
||||
await this.spaceLuaEnv.reload(this.system, this.scriptEnv);
|
||||
await this.spaceLuaEnv.reload(this.system);
|
||||
} catch (e: any) {
|
||||
console.error("Error loading space-script:", e.message);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import {
|
|||
type PageRef,
|
||||
parsePageRef,
|
||||
} from "@silverbulletmd/silverbullet/lib/page_ref";
|
||||
import type { ScriptEnvironment } from "$common/space_script.ts";
|
||||
import type { ASTCtx } from "$common/space_lua/ast.ts";
|
||||
import { buildLuaEnv } from "$common/space_lua_api.ts";
|
||||
|
||||
|
@ -24,7 +23,6 @@ export class SpaceLuaEnvironment {
|
|||
*/
|
||||
async reload(
|
||||
system: System<any>,
|
||||
scriptEnv: ScriptEnvironment,
|
||||
) {
|
||||
const allScripts: ScriptObject[] = await system.invokeFunction(
|
||||
"index.queryObjects",
|
||||
|
@ -36,7 +34,7 @@ export class SpaceLuaEnvironment {
|
|||
}],
|
||||
);
|
||||
try {
|
||||
this.env = buildLuaEnv(system, scriptEnv);
|
||||
this.env = buildLuaEnv(system);
|
||||
const tl = new LuaEnv();
|
||||
tl.setLocal("_GLOBAL", this.env);
|
||||
for (const script of allScripts) {
|
||||
|
|
|
@ -267,6 +267,7 @@ export function evalExpression(
|
|||
sf.withCtx(e.ctx),
|
||||
);
|
||||
}
|
||||
collection = luaValueToJS(collection);
|
||||
// Check if collection is a queryable collection
|
||||
if (!collection.query) {
|
||||
// If not, try to convert it to JS and see if it's an array
|
||||
|
|
|
@ -104,10 +104,12 @@ Deno.test("Test comment handling", () => {
|
|||
});
|
||||
|
||||
Deno.test("Test query parsing", () => {
|
||||
parse(`_(query[[from p = tag("page") where p.name == "John" limit 10, 3]])`);
|
||||
parse(`_(query[[from tag("page") select {name="hello", age=10}]])`);
|
||||
parse(
|
||||
`_(query[[from p = tag("page") order by p.lastModified desc, p.name]])`,
|
||||
`_(query[[from p = index.tag("page") where p.name == "John" limit 10, 3]])`,
|
||||
);
|
||||
parse(`_(query[[from p = tag("page") order by p.lastModified]])`);
|
||||
parse(`_(query[[from index.tag("page") select {name="hello", age=10}]])`);
|
||||
parse(
|
||||
`_(query[[from p = index.tag("page") order by p.lastModified desc, p.name]])`,
|
||||
);
|
||||
parse(`_(query[[from p = index.tag("page") order by p.lastModified]])`);
|
||||
});
|
||||
|
|
|
@ -1,31 +1,21 @@
|
|||
import {
|
||||
type ILuaFunction,
|
||||
jsToLuaValue,
|
||||
LuaBuiltinFunction,
|
||||
luaCall,
|
||||
LuaEnv,
|
||||
luaGet,
|
||||
LuaMultiRes,
|
||||
LuaRuntimeError,
|
||||
LuaTable,
|
||||
type LuaTable,
|
||||
luaToString,
|
||||
luaTypeOf,
|
||||
type LuaValue,
|
||||
luaValueToJS,
|
||||
} from "$common/space_lua/runtime.ts";
|
||||
import { stringApi } from "$common/space_lua/stdlib/string.ts";
|
||||
import { tableApi } from "$common/space_lua/stdlib/table.ts";
|
||||
import { osApi } from "$common/space_lua/stdlib/os.ts";
|
||||
import { jsApi } from "$common/space_lua/stdlib/js.ts";
|
||||
import {
|
||||
interpolateLuaString,
|
||||
spaceLuaApi,
|
||||
} from "$common/space_lua/stdlib/space_lua.ts";
|
||||
import {
|
||||
findAllQueryVariables,
|
||||
type LuaCollectionQuery,
|
||||
type LuaQueryCollection,
|
||||
} from "$common/space_lua/query_collection.ts";
|
||||
import { spaceLuaApi } from "$common/space_lua/stdlib/space_lua.ts";
|
||||
import { templateApi } from "$common/space_lua/stdlib/template.ts";
|
||||
import { mathApi } from "$common/space_lua/stdlib/math.ts";
|
||||
|
||||
|
@ -149,68 +139,6 @@ const getmetatableFunction = new LuaBuiltinFunction((_sf, table: LuaTable) => {
|
|||
return table.metatable;
|
||||
});
|
||||
|
||||
// Non-standard
|
||||
const tagFunction = new LuaBuiltinFunction(
|
||||
(sf, tagName: LuaValue): LuaQueryCollection => {
|
||||
const global = sf.threadLocal.get("_GLOBAL");
|
||||
if (!global) {
|
||||
throw new LuaRuntimeError("Global not found", sf);
|
||||
}
|
||||
return {
|
||||
query: async (query: LuaCollectionQuery, env: LuaEnv): Promise<any[]> => {
|
||||
const localVars = findAllQueryVariables(query).filter((v) =>
|
||||
!global.has(v) && v !== "_"
|
||||
);
|
||||
const scopedVariables: Record<string, any> = {};
|
||||
for (const v of localVars) {
|
||||
try {
|
||||
const jsonValue = await luaValueToJS(env.get(v));
|
||||
// Ensure this is JSON serializable
|
||||
JSON.stringify(jsonValue);
|
||||
scopedVariables[v] = jsonValue;
|
||||
} catch (e: any) {
|
||||
console.error(
|
||||
"Failed to JSON serialize variable",
|
||||
v,
|
||||
e,
|
||||
);
|
||||
throw new LuaRuntimeError(
|
||||
`Failed to JSON serialize variable ${v} in query`,
|
||||
sf,
|
||||
);
|
||||
}
|
||||
}
|
||||
return (await global.get("datastore").get("query_lua").call(
|
||||
sf,
|
||||
[
|
||||
"idx",
|
||||
tagName,
|
||||
],
|
||||
query,
|
||||
scopedVariables,
|
||||
)).toJSArray();
|
||||
},
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
const tplFunction = new LuaBuiltinFunction(
|
||||
(_sf, template: string): ILuaFunction => {
|
||||
const lines = template.split("\n").map((line) =>
|
||||
line.replace(/^\s{4}/, "")
|
||||
);
|
||||
const processed = lines.join("\n");
|
||||
return new LuaBuiltinFunction(
|
||||
async (sf, env: LuaTable | any) => {
|
||||
if (!(env instanceof LuaTable)) {
|
||||
env = jsToLuaValue(env);
|
||||
}
|
||||
return await interpolateLuaString(sf, processed, env);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export function luaBuildStandardEnv() {
|
||||
const env = new LuaEnv();
|
||||
// Top-level builtins
|
||||
|
@ -231,9 +159,6 @@ export function luaBuildStandardEnv() {
|
|||
env.set("error", errorFunction);
|
||||
env.set("pcall", pcallFunction);
|
||||
env.set("xpcall", xpcallFunction);
|
||||
// Non-standard
|
||||
env.set("tag", tagFunction);
|
||||
env.set("tpl", tplFunction);
|
||||
// APIs
|
||||
env.set("string", stringApi);
|
||||
env.set("table", tableApi);
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import {
|
||||
type ILuaFunction,
|
||||
jsToLuaValue,
|
||||
LuaBuiltinFunction,
|
||||
LuaTable,
|
||||
} from "$common/space_lua/runtime.ts";
|
||||
import { interpolateLuaString } from "$common/space_lua/stdlib/space_lua.ts";
|
||||
|
||||
export const templateApi = new LuaTable({
|
||||
each: new LuaBuiltinFunction(
|
||||
|
@ -17,4 +19,20 @@ export const templateApi = new LuaTable({
|
|||
return result.join("");
|
||||
},
|
||||
),
|
||||
new: new LuaBuiltinFunction(
|
||||
(_sf, template: string): ILuaFunction => {
|
||||
const lines = template.split("\n").map((line) =>
|
||||
line.replace(/^\s{4}/, "")
|
||||
);
|
||||
const processed = lines.join("\n");
|
||||
return new LuaBuiltinFunction(
|
||||
async (sf, env: LuaTable | any) => {
|
||||
if (!(env instanceof LuaTable)) {
|
||||
env = jsToLuaValue(env);
|
||||
}
|
||||
return await interpolateLuaString(sf, processed, env);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
});
|
||||
|
|
|
@ -1,24 +1,18 @@
|
|||
import { luaBuildStandardEnv } from "$common/space_lua/stdlib.ts";
|
||||
import { parsePageRef } from "@silverbulletmd/silverbullet/lib/page_ref";
|
||||
import {
|
||||
jsToLuaValue,
|
||||
LuaBuiltinFunction,
|
||||
LuaEnv,
|
||||
LuaNativeJSFunction,
|
||||
LuaStackFrame,
|
||||
LuaTable,
|
||||
} from "$common/space_lua/runtime.ts";
|
||||
import type { System } from "$lib/plugos/system.ts";
|
||||
import type { ScriptEnvironment } from "$common/space_script.ts";
|
||||
import type { CommandDef } from "$lib/command.ts";
|
||||
|
||||
export function buildLuaEnv(system: System<any>, scriptEnv: ScriptEnvironment) {
|
||||
export function buildLuaEnv(system: System<any>) {
|
||||
const env = new LuaEnv(luaBuildStandardEnv());
|
||||
|
||||
// Expose all syscalls to Lua
|
||||
exposeSyscalls(env, system);
|
||||
// Support defining commands and subscriptions from Lua
|
||||
exposeDefinitions(env, system, scriptEnv);
|
||||
|
||||
return env;
|
||||
}
|
||||
|
@ -45,82 +39,7 @@ function exposeSyscalls(env: LuaEnv, system: System<any>) {
|
|||
}
|
||||
}
|
||||
|
||||
function exposeDefinitions(
|
||||
env: LuaEnv,
|
||||
system: System<any>,
|
||||
scriptEnv: ScriptEnvironment,
|
||||
) {
|
||||
// Expose the command registration function to Lua via define_command({name="foo", function() ... end})
|
||||
env.set(
|
||||
"define_command",
|
||||
new LuaBuiltinFunction(
|
||||
(_sf, def: LuaTable) => {
|
||||
if (def.get(1) === undefined) {
|
||||
throw new Error("Callback is required");
|
||||
}
|
||||
if (!def.get("name")) {
|
||||
throw new Error("Name is required");
|
||||
}
|
||||
const fn = def.get(1);
|
||||
console.log(
|
||||
`[Lua] Registering command '${
|
||||
def.get("name")
|
||||
}' (source: ${fn.body.ctx.ref})`,
|
||||
);
|
||||
scriptEnv.registerCommand(
|
||||
{
|
||||
name: def.get("name"),
|
||||
key: def.get("key"),
|
||||
mac: def.get("mac"),
|
||||
priority: def.get("priority"),
|
||||
requireMode: def.get("require_mode"),
|
||||
hide: def.get("hide"),
|
||||
} as CommandDef,
|
||||
async (...args: any[]) => {
|
||||
const tl = await buildThreadLocalEnv(system, env);
|
||||
const sf = new LuaStackFrame(tl, null);
|
||||
try {
|
||||
return await fn.call(sf, ...args.map(jsToLuaValue));
|
||||
} catch (e: any) {
|
||||
await handleLuaError(e, system);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
env.set(
|
||||
"define_event_listener",
|
||||
new LuaBuiltinFunction((_sf, def: LuaTable) => {
|
||||
if (def.get(1) === undefined) {
|
||||
throw new Error("Callback is required");
|
||||
}
|
||||
if (!def.get("event")) {
|
||||
throw new Error("Event is required");
|
||||
}
|
||||
const fn = def.get(1);
|
||||
console.log(
|
||||
`[Lua] Subscribing to event '${
|
||||
def.get("event")
|
||||
}' (source: ${fn.body.ctx.ref})`,
|
||||
);
|
||||
scriptEnv.registerEventListener(
|
||||
{ name: def.get("event") },
|
||||
async (...args: any[]) => {
|
||||
const tl = await buildThreadLocalEnv(system, env);
|
||||
const sf = new LuaStackFrame(tl, null);
|
||||
try {
|
||||
return await fn.call(sf, ...args.map(jsToLuaValue));
|
||||
} catch (e: any) {
|
||||
await handleLuaError(e, system);
|
||||
}
|
||||
},
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
function buildThreadLocalEnv(_system: System<any>, globalEnv: LuaEnv) {
|
||||
export function buildThreadLocalEnv(_system: System<any>, globalEnv: LuaEnv) {
|
||||
const tl = new LuaEnv();
|
||||
// const currentPageMeta = await system.localSyscall(
|
||||
// "editor.getCurrentPageMeta",
|
||||
|
@ -131,7 +50,7 @@ function buildThreadLocalEnv(_system: System<any>, globalEnv: LuaEnv) {
|
|||
return Promise.resolve(tl);
|
||||
}
|
||||
|
||||
async function handleLuaError(e: any, system: System<any>) {
|
||||
export async function handleLuaError(e: any, system: System<any>) {
|
||||
console.error(
|
||||
"Lua eval exception",
|
||||
e.message,
|
||||
|
|
|
@ -20,7 +20,7 @@ type AttributeExtractorDef = {
|
|||
tags: string[];
|
||||
};
|
||||
|
||||
type EventListenerDef = {
|
||||
export type EventListenerDef = {
|
||||
name: string;
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
import type { SysCallMapping } from "$lib/plugos/system.ts";
|
||||
import type { CommandDef } from "$lib/command.ts";
|
||||
import { buildThreadLocalEnv, handleLuaError } from "$common/space_lua_api.ts";
|
||||
import {
|
||||
type ILuaFunction,
|
||||
jsToLuaValue,
|
||||
luaCall,
|
||||
LuaStackFrame,
|
||||
luaValueToJS,
|
||||
} from "$common/space_lua/runtime.ts";
|
||||
import type { CommonSystem } from "$common/common_system.ts";
|
||||
|
||||
export type CallbackCommandDef = CommandDef & {
|
||||
run: ILuaFunction;
|
||||
};
|
||||
|
||||
export function commandSyscalls(
|
||||
commonSystem: CommonSystem,
|
||||
): SysCallMapping {
|
||||
return {
|
||||
/**
|
||||
* Define a Lua command
|
||||
* @param def - The command definition
|
||||
* @param luaCallback - The Lua callback
|
||||
*/
|
||||
"command.define": (
|
||||
_ctx,
|
||||
def: CallbackCommandDef,
|
||||
) => {
|
||||
console.log("Registering Lua command: ", def.name);
|
||||
commonSystem.scriptEnv.registerCommand(
|
||||
def,
|
||||
async (...args: any[]) => {
|
||||
const tl = await buildThreadLocalEnv(
|
||||
commonSystem.system,
|
||||
commonSystem.spaceLuaEnv.env,
|
||||
);
|
||||
const sf = new LuaStackFrame(tl, null);
|
||||
try {
|
||||
return luaValueToJS(
|
||||
await luaCall(def.run, args.map(jsToLuaValue), sf),
|
||||
);
|
||||
} catch (e: any) {
|
||||
await handleLuaError(e, commonSystem.system);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
import type { SysCallMapping } from "$lib/plugos/system.ts";
|
||||
import type { EventListenerDef } from "$common/space_script.ts";
|
||||
import { buildThreadLocalEnv, handleLuaError } from "$common/space_lua_api.ts";
|
||||
import {
|
||||
type ILuaFunction,
|
||||
jsToLuaValue,
|
||||
luaCall,
|
||||
LuaStackFrame,
|
||||
luaValueToJS,
|
||||
} from "$common/space_lua/runtime.ts";
|
||||
import type { CommonSystem } from "$common/common_system.ts";
|
||||
|
||||
export type CallbackEventListener = EventListenerDef & {
|
||||
run: ILuaFunction;
|
||||
};
|
||||
|
||||
export function eventListenerSyscalls(
|
||||
commonSystem: CommonSystem,
|
||||
): SysCallMapping {
|
||||
return {
|
||||
/**
|
||||
* Define a Lua event listener
|
||||
*/
|
||||
"event.listen": (
|
||||
_ctx,
|
||||
def: CallbackEventListener,
|
||||
) => {
|
||||
console.log("Registering Lua event listener: ", def.name);
|
||||
commonSystem.scriptEnv.registerEventListener(
|
||||
def,
|
||||
async (...args: any[]) => {
|
||||
const tl = await buildThreadLocalEnv(
|
||||
commonSystem.system,
|
||||
commonSystem.spaceLuaEnv.env,
|
||||
);
|
||||
const sf = new LuaStackFrame(tl, null);
|
||||
try {
|
||||
return luaValueToJS(
|
||||
await luaCall(def.run, args.map(jsToLuaValue), sf),
|
||||
);
|
||||
} catch (e: any) {
|
||||
await handleLuaError(e, commonSystem.system);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
|
@ -3,15 +3,26 @@ import type {
|
|||
ObjectQuery,
|
||||
ObjectValue,
|
||||
} from "@silverbulletmd/silverbullet/types";
|
||||
import type { SysCallMapping, System } from "$lib/plugos/system.ts";
|
||||
import type { LuaCollectionQuery } from "$common/space_lua/query_collection.ts";
|
||||
import type { SysCallMapping } from "$lib/plugos/system.ts";
|
||||
import {
|
||||
findAllQueryVariables,
|
||||
type LuaCollectionQuery,
|
||||
type LuaQueryCollection,
|
||||
} from "$common/space_lua/query_collection.ts";
|
||||
import {
|
||||
type LuaEnv,
|
||||
LuaRuntimeError,
|
||||
type LuaStackFrame,
|
||||
luaValueToJS,
|
||||
} from "$common/space_lua/runtime.ts";
|
||||
import type { CommonSystem } from "$common/common_system.ts";
|
||||
|
||||
// These are just wrappers around the system.invokeFunction calls, but they make it easier to use the index
|
||||
|
||||
export function indexSyscalls(system: System<any>): SysCallMapping {
|
||||
export function indexSyscalls(commonSystem: CommonSystem): SysCallMapping {
|
||||
return {
|
||||
"index.indexObjects": (ctx, page: string, objects: ObjectValue<any>[]) => {
|
||||
return system.syscall(ctx, "system.invokeFunction", [
|
||||
return commonSystem.system.syscall(ctx, "system.invokeFunction", [
|
||||
"index.indexObjects",
|
||||
page,
|
||||
objects,
|
||||
|
@ -23,7 +34,7 @@ export function indexSyscalls(system: System<any>): SysCallMapping {
|
|||
query: ObjectQuery,
|
||||
ttlSecs?: number,
|
||||
) => {
|
||||
return system.syscall(ctx, "system.invokeFunction", [
|
||||
return commonSystem.system.syscall(ctx, "system.invokeFunction", [
|
||||
"index.queryObjects",
|
||||
tag,
|
||||
query,
|
||||
|
@ -36,7 +47,7 @@ export function indexSyscalls(system: System<any>): SysCallMapping {
|
|||
query: LuaCollectionQuery,
|
||||
scopedVariables?: Record<string, any>,
|
||||
) => {
|
||||
return system.syscall(ctx, "system.invokeFunction", [
|
||||
return commonSystem.system.syscall(ctx, "system.invokeFunction", [
|
||||
"index.queryLuaObjects",
|
||||
tag,
|
||||
query,
|
||||
|
@ -44,26 +55,68 @@ export function indexSyscalls(system: System<any>): SysCallMapping {
|
|||
]);
|
||||
},
|
||||
"index.queryDeleteObjects": (ctx, tag: string, query: ObjectQuery) => {
|
||||
return system.syscall(ctx, "system.invokeFunction", [
|
||||
return commonSystem.system.syscall(ctx, "system.invokeFunction", [
|
||||
"index.queryDeleteObjects",
|
||||
tag,
|
||||
query,
|
||||
]);
|
||||
},
|
||||
"index.query": (ctx, query: KvQuery, variables?: Record<string, any>) => {
|
||||
return system.syscall(ctx, "system.invokeFunction", [
|
||||
return commonSystem.system.syscall(ctx, "system.invokeFunction", [
|
||||
"index.query",
|
||||
query,
|
||||
variables,
|
||||
]);
|
||||
},
|
||||
"index.getObjectByRef": (ctx, page: string, tag: string, ref: string) => {
|
||||
return system.syscall(ctx, "system.invokeFunction", [
|
||||
return commonSystem.system.syscall(ctx, "system.invokeFunction", [
|
||||
"index.getObjectByRef",
|
||||
page,
|
||||
tag,
|
||||
ref,
|
||||
]);
|
||||
},
|
||||
"index.tag": (_ctx, tagName: string): LuaQueryCollection => {
|
||||
return {
|
||||
query: async (
|
||||
query: LuaCollectionQuery,
|
||||
env: LuaEnv,
|
||||
sf: LuaStackFrame,
|
||||
): Promise<any[]> => {
|
||||
const global = commonSystem.spaceLuaEnv.env;
|
||||
const localVars = findAllQueryVariables(query).filter((v) =>
|
||||
!global.has(v) && v !== "_"
|
||||
);
|
||||
const scopedVariables: Record<string, any> = {};
|
||||
for (const v of localVars) {
|
||||
try {
|
||||
const jsonValue = await luaValueToJS(env.get(v));
|
||||
// Ensure this is JSON serializable
|
||||
JSON.stringify(jsonValue);
|
||||
scopedVariables[v] = jsonValue;
|
||||
} catch (e: any) {
|
||||
console.error(
|
||||
"Failed to JSON serialize variable",
|
||||
v,
|
||||
e,
|
||||
);
|
||||
throw new LuaRuntimeError(
|
||||
`Failed to JSON serialize variable ${v} in query`,
|
||||
sf,
|
||||
);
|
||||
}
|
||||
}
|
||||
return (await global.get("datastore").get("query_lua").call(
|
||||
sf,
|
||||
[
|
||||
"idx",
|
||||
tagName,
|
||||
],
|
||||
query,
|
||||
scopedVariables,
|
||||
)).toJSArray();
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -42,6 +42,8 @@ import { plugPrefix } from "$common/spaces/constants.ts";
|
|||
import { base64EncodedDataUrl } from "$lib/crypto.ts";
|
||||
import type { ConfigContainer } from "../type/config.ts";
|
||||
import { indexSyscalls } from "$common/syscalls/index.ts";
|
||||
import { commandSyscalls } from "$common/syscalls/command.ts";
|
||||
import { eventListenerSyscalls } from "$common/syscalls/event.ts";
|
||||
|
||||
const fileListInterval = 30 * 1000; // 30s
|
||||
|
||||
|
@ -122,6 +124,7 @@ export class ServerSystem extends CommonSystem {
|
|||
this.system.registerSyscalls(
|
||||
[],
|
||||
eventSyscalls(this.eventHook),
|
||||
eventListenerSyscalls(this),
|
||||
spaceReadSyscalls(space, this.allKnownFiles),
|
||||
assetSyscalls(this.system),
|
||||
yamlSyscalls(),
|
||||
|
@ -134,7 +137,8 @@ export class ServerSystem extends CommonSystem {
|
|||
mqSyscalls(this.mq),
|
||||
languageSyscalls(),
|
||||
jsonschemaSyscalls(),
|
||||
indexSyscalls(this.system),
|
||||
indexSyscalls(this),
|
||||
commandSyscalls(this),
|
||||
luaSyscalls(),
|
||||
templateSyscalls(this.ds),
|
||||
dataStoreReadSyscalls(this.ds, this),
|
||||
|
|
|
@ -45,6 +45,8 @@ import { plugPrefix } from "$common/spaces/constants.ts";
|
|||
import { jsonschemaSyscalls } from "$common/syscalls/jsonschema.ts";
|
||||
import { luaSyscalls } from "$common/syscalls/lua.ts";
|
||||
import { indexSyscalls } from "$common/syscalls/index.ts";
|
||||
import { commandSyscalls } from "$common/syscalls/command.ts";
|
||||
import { eventListenerSyscalls } from "$common/syscalls/event.ts";
|
||||
|
||||
const plugNameExtractRegex = /\/(.+)\.plug\.js$/;
|
||||
|
||||
|
@ -152,6 +154,7 @@ export class ClientSystem extends CommonSystem {
|
|||
this.system.registerSyscalls(
|
||||
[],
|
||||
eventSyscalls(this.eventHook),
|
||||
eventListenerSyscalls(this),
|
||||
editorSyscalls(this.client),
|
||||
spaceReadSyscalls(this.client),
|
||||
systemSyscalls(this.system, false, this, this.client, this.client),
|
||||
|
@ -163,7 +166,8 @@ export class ClientSystem extends CommonSystem {
|
|||
clientCodeWidgetSyscalls(),
|
||||
languageSyscalls(),
|
||||
jsonschemaSyscalls(),
|
||||
indexSyscalls(this.system),
|
||||
indexSyscalls(this),
|
||||
commandSyscalls(this),
|
||||
luaSyscalls(),
|
||||
this.client.syncMode
|
||||
// In sync mode handle locally
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
This describes the APIs available in [[Space Lua]]
|
||||
|
||||
${template.each(query[[
|
||||
from tag("page") where string.startswith(name, "API/")
|
||||
from index.tag("page") where string.startswith(name, "API/")
|
||||
]], render.page)}
|
|
@ -0,0 +1,14 @@
|
|||
APIs related to editor commands
|
||||
|
||||
### command.define(command_def)
|
||||
Registers a command.
|
||||
|
||||
Example:
|
||||
```lua
|
||||
command.define {
|
||||
name = "My custom command",
|
||||
run = function()
|
||||
editor.flash_notification "Triggered my custom command"
|
||||
end
|
||||
}
|
||||
```
|
|
@ -4,13 +4,25 @@ The Event API provides functions for working with SilverBullet's event bus syste
|
|||
|
||||
## Event Operations
|
||||
|
||||
### event.dispatch_event(event_name, data, timeout)
|
||||
### event.listen(listener_def)
|
||||
Register an event listener.
|
||||
|
||||
```lua
|
||||
event.listen {
|
||||
name = "my-event",
|
||||
run = function(e)
|
||||
print("Data", e.data)
|
||||
end
|
||||
}
|
||||
```
|
||||
|
||||
### event.dispatch(event_name, data, timeout)
|
||||
Triggers an event on the SilverBullet event bus. Event handlers can return values, which are accumulated and returned to the caller.
|
||||
|
||||
Example:
|
||||
```lua
|
||||
-- Simple event dispatch
|
||||
event.dispatch_event("custom.event", {message = "Hello"})
|
||||
event.dispatch("custom.event", {message = "Hello"})
|
||||
|
||||
-- Event dispatch with timeout and response handling
|
||||
local responses = event.dispatch_event("data.request", {id = 123}, 5000)
|
||||
|
|
|
@ -201,23 +201,3 @@ rawset(t, "foo", "bar") -- bypasses the metamethod
|
|||
print(t.foo) -- prints: "bar"
|
||||
```
|
||||
|
||||
# Space Lua specific
|
||||
## tag(name)
|
||||
Returns a given [[Objects#Tags]] as a query collection, to be queried using [[Space Lua/Lua Integrated Query]].
|
||||
|
||||
Example:
|
||||
|
||||
${query[[from tag("page") limit 1]]}
|
||||
|
||||
## tpl(template)
|
||||
Returns a template function that can be used to render a template. Conventionally, a template string is put between `[==[` and `]==]` as string delimiters.
|
||||
|
||||
Example:
|
||||
|
||||
```space-lua
|
||||
examples = examples or {}
|
||||
|
||||
examples.say_hello = tpl[==[Hello ${name}!]==]
|
||||
```
|
||||
|
||||
And its use: ${examples.say_hello {name="Pete"}}
|
||||
|
|
|
@ -2,7 +2,14 @@ The `index` API provides functions for interacting with SilverBullet's [[Objects
|
|||
|
||||
## Object Operations
|
||||
|
||||
### index.index_objects(page, objects)
|
||||
## index.tag(name)
|
||||
Returns a given [[Objects#Tags]] as a query collection, to be queried using [[Space Lua/Lua Integrated Query]].
|
||||
|
||||
Example:
|
||||
|
||||
${query[[from index.tag("page") limit 1]]}
|
||||
|
||||
## index.index_objects(page, objects)
|
||||
Indexes an array of objects for a specific page.
|
||||
|
||||
Example:
|
||||
|
@ -14,7 +21,7 @@ local objects = {
|
|||
index.index_objects("my page", objects)
|
||||
```
|
||||
|
||||
### index.query_lua_objects(tag, query, scoped_variables?)
|
||||
## index.query_lua_objects(tag, query, scoped_variables?)
|
||||
Queries objects using a Lua-based collection query.
|
||||
|
||||
Example:
|
||||
|
@ -22,7 +29,7 @@ Example:
|
|||
local tasks = index.query_lua_objects("mytask", {limit=3})
|
||||
```
|
||||
|
||||
### index.get_object_by_ref(page, tag, ref)
|
||||
## index.get_object_by_ref(page, tag, ref)
|
||||
Retrieves a specific object by its reference.
|
||||
|
||||
Example:
|
||||
|
|
|
@ -1,10 +1,23 @@
|
|||
Template functions that use the [[API/global#tpl(template)]] function.
|
||||
Template functions that use the [[API/template#template.new(template)]] function.
|
||||
|
||||
## template.new(template)
|
||||
Returns a template function that can be used to render a template. Conventionally, a template string is put between `[==[` and `]==]` as string delimiters.
|
||||
|
||||
Example:
|
||||
|
||||
```space-lua
|
||||
examples = examples or {}
|
||||
|
||||
examples.say_hello = template.new[==[Hello ${name}!]==]
|
||||
```
|
||||
|
||||
And its use: ${examples.say_hello {name="Pete"}}
|
||||
|
||||
## template.each(collection, template)
|
||||
Iterates over a collection and renders a template for each item.
|
||||
|
||||
Example:
|
||||
|
||||
${template.each(query[[from tag "page" limit 3]], tpl[==[
|
||||
${template.each(query[[from index.tag "page" limit 3]], template.new[==[
|
||||
* ${name}
|
||||
]==])}
|
|
@ -1,8 +0,0 @@
|
|||
Defines some core useful templates for use in [[Space Lua]]
|
||||
```space-lua
|
||||
render = render or {}
|
||||
|
||||
render.page = tpl[==[
|
||||
* [[${name}]]
|
||||
]==]
|
||||
```
|
|
@ -60,7 +60,7 @@ For example: 10 + 2 = ${adder(10, 2)} (Alt-click, or select to see the expressio
|
|||
Space Lua has a feature called [[Space Lua/Lua Integrated Query]], which integrate SQL-like queries into Lua. By using this feature, you can easily replicate [[Live Queries]]. More detail in [[Space Lua/Lua Integrated Query]], but here’s a small example querying the last 3 modifies pages:
|
||||
|
||||
${query[[
|
||||
from tag "page"
|
||||
from index.tag "page"
|
||||
order by lastModified desc
|
||||
select name
|
||||
limit 3
|
||||
|
@ -101,12 +101,12 @@ ${marquee "Finally, marqeeeeeeee!"}
|
|||
Oh boy, the times we live in!
|
||||
|
||||
## Commands
|
||||
Custom commands can be defined using `define_command`:
|
||||
Custom commands can be defined using `command.define`:
|
||||
|
||||
```space-lua
|
||||
define_command {
|
||||
name = "Hello World";
|
||||
function()
|
||||
command.define {
|
||||
name = "Hello World",
|
||||
run = function()
|
||||
editor.flash_notification "Hello world!"
|
||||
event.dispatch("my-custom-event", {name="Pete"})
|
||||
end
|
||||
|
@ -116,15 +116,14 @@ define_command {
|
|||
Try it: {[Hello World]}
|
||||
|
||||
## Event listeners
|
||||
You can listen to events using `define_event_listener`:
|
||||
You can listen to events using `event.listen`:
|
||||
|
||||
```space-lua
|
||||
define_event_listener {
|
||||
event = "my-custom-event";
|
||||
function(e)
|
||||
event.listen {
|
||||
name = "my-custom-event";
|
||||
run = function(e)
|
||||
editor.flash_notification("Custom triggered: "
|
||||
.. e.data.name
|
||||
.. " on page " .. _CTX.pageMeta.name)
|
||||
.. e.data.name)
|
||||
end
|
||||
}
|
||||
```
|
||||
|
@ -138,7 +137,6 @@ Space Lua currently introduces a few new features on top core Lua:
|
|||
## Thread locals
|
||||
There’s a magic `_CTX` global variable available from which you can access useful context-specific values. Currently the following keys are available:
|
||||
|
||||
* `_CTX.pageMeta` contains a reference to the loaded page metadata (can be `nil` when not yet loaded)
|
||||
* `_CTX.GLOBAL` providing access to the global scope
|
||||
|
||||
# API
|
||||
|
|
|
@ -19,10 +19,10 @@ Unlike [[Query Language]] which operates on [[Objects]] only, LIQ can operate on
|
|||
For instance, to sort a list of numbers in descending order:
|
||||
${query[[from n = {1, 2, 3} order by n desc]]}
|
||||
|
||||
However, in most cases you’ll use it in conjunction with [[../API/global#tag(name)]]. Here’s an example querying the 3 pages that were last modified:
|
||||
However, in most cases you’ll use it in conjunction with [[API/index#index.tag(name)]]. Here’s an example querying the 3 pages that were last modified:
|
||||
|
||||
${query[[
|
||||
from p = tag "page"
|
||||
from p = index.tag "page"
|
||||
order by p.lastModified desc
|
||||
select p.name
|
||||
limit 3
|
||||
|
@ -52,8 +52,8 @@ ${query[[from {1, 2, 3} select _]]}
|
|||
With variable binding:
|
||||
${query[[from n = {1, 2, 3} select n]]}
|
||||
|
||||
A more realistic example using `tag`:
|
||||
${query[[from tag "page" order by lastModified select name limit 3]]}
|
||||
A more realistic example using `index.tag`:
|
||||
${query[[from index.tag "page" order by lastModified select name limit 3]]}
|
||||
|
||||
## `where <expression>`
|
||||
The `where` clause allows you to filter data. When the expression evaluated to a truthy value, the item is included in the result.
|
||||
|
@ -64,14 +64,14 @@ ${query[[from {1, 2, 3, 4, 5} where _ > 2]]}
|
|||
|
||||
Or to select all pages tagged with `#meta`:
|
||||
|
||||
${query[[from tag "page" where table.includes(tags, "meta")]]}
|
||||
${query[[from index.tag "page" where table.includes(tags, "meta")]]}
|
||||
|
||||
## `order by <expression> [desc]`
|
||||
The `order by` clause allows you to sort data, when `desc` is specified it reverts the sort order.
|
||||
|
||||
As an example, the last 3 modified pages:
|
||||
${query[[
|
||||
from tag "page"
|
||||
from index.tag "page"
|
||||
order by lastModified desc
|
||||
select name
|
||||
limit 3
|
||||
|
@ -97,11 +97,11 @@ Double each number:
|
|||
${query[[from {1, 2, 3} select _ * 2]]}
|
||||
|
||||
Extract just the name from pages:
|
||||
${query[[from tag "page" select _.name limit 3]]}
|
||||
${query[[from index.tag "page" select _.name limit 3]]}
|
||||
|
||||
You can also return tables or other complex values:
|
||||
${query[[
|
||||
from p = tag "page"
|
||||
from p = index.tag "page"
|
||||
select {
|
||||
name = p.name,
|
||||
modified = p.lastModified
|
||||
|
|
Loading…
Reference in New Issue