diff --git a/common/common_system.ts b/common/common_system.ts index 6222f4ea..4aa08a46 100644 --- a/common/common_system.ts +++ b/common/common_system.ts @@ -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); } diff --git a/common/space_lua.ts b/common/space_lua.ts index 66c76a20..61f07af9 100644 --- a/common/space_lua.ts +++ b/common/space_lua.ts @@ -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, - 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) { diff --git a/common/space_lua/eval.ts b/common/space_lua/eval.ts index 0bc7c4ca..acfd4ea1 100644 --- a/common/space_lua/eval.ts +++ b/common/space_lua/eval.ts @@ -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 diff --git a/common/space_lua/parse.test.ts b/common/space_lua/parse.test.ts index 49606e3e..7e2609df 100644 --- a/common/space_lua/parse.test.ts +++ b/common/space_lua/parse.test.ts @@ -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]])`); }); diff --git a/common/space_lua/stdlib.ts b/common/space_lua/stdlib.ts index 5ed15ea5..2b9b17cb 100644 --- a/common/space_lua/stdlib.ts +++ b/common/space_lua/stdlib.ts @@ -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 => { - const localVars = findAllQueryVariables(query).filter((v) => - !global.has(v) && v !== "_" - ); - const scopedVariables: Record = {}; - 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); diff --git a/common/space_lua/stdlib/template.ts b/common/space_lua/stdlib/template.ts index c6e2cce7..f655b70b 100644 --- a/common/space_lua/stdlib/template.ts +++ b/common/space_lua/stdlib/template.ts @@ -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); + }, + ); + }, + ), }); diff --git a/common/space_lua_api.ts b/common/space_lua_api.ts index a2ae0349..8ed8d6ff 100644 --- a/common/space_lua_api.ts +++ b/common/space_lua_api.ts @@ -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, scriptEnv: ScriptEnvironment) { +export function buildLuaEnv(system: System) { 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) { } } -function exposeDefinitions( - env: LuaEnv, - system: System, - 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, globalEnv: LuaEnv) { +export function buildThreadLocalEnv(_system: System, globalEnv: LuaEnv) { const tl = new LuaEnv(); // const currentPageMeta = await system.localSyscall( // "editor.getCurrentPageMeta", @@ -131,7 +50,7 @@ function buildThreadLocalEnv(_system: System, globalEnv: LuaEnv) { return Promise.resolve(tl); } -async function handleLuaError(e: any, system: System) { +export async function handleLuaError(e: any, system: System) { console.error( "Lua eval exception", e.message, diff --git a/common/space_script.ts b/common/space_script.ts index d7b0d6a5..db44c8a3 100644 --- a/common/space_script.ts +++ b/common/space_script.ts @@ -20,7 +20,7 @@ type AttributeExtractorDef = { tags: string[]; }; -type EventListenerDef = { +export type EventListenerDef = { name: string; }; diff --git a/common/syscalls/command.ts b/common/syscalls/command.ts new file mode 100644 index 00000000..565009da --- /dev/null +++ b/common/syscalls/command.ts @@ -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); + } + }, + ); + }, + }; +} diff --git a/common/syscalls/event.ts b/common/syscalls/event.ts new file mode 100644 index 00000000..e474f202 --- /dev/null +++ b/common/syscalls/event.ts @@ -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); + } + }, + ); + }, + }; +} diff --git a/common/syscalls/index.ts b/common/syscalls/index.ts index 4c97e1d1..387f2423 100644 --- a/common/syscalls/index.ts +++ b/common/syscalls/index.ts @@ -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): SysCallMapping { +export function indexSyscalls(commonSystem: CommonSystem): SysCallMapping { return { "index.indexObjects": (ctx, page: string, objects: ObjectValue[]) => { - 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): 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): SysCallMapping { query: LuaCollectionQuery, scopedVariables?: Record, ) => { - 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): 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) => { - 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 => { + const global = commonSystem.spaceLuaEnv.env; + const localVars = findAllQueryVariables(query).filter((v) => + !global.has(v) && v !== "_" + ); + const scopedVariables: Record = {}; + 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(); + }, + }; + }, }; } diff --git a/server/server_system.ts b/server/server_system.ts index 3a6bf389..fb0ea486 100644 --- a/server/server_system.ts +++ b/server/server_system.ts @@ -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), diff --git a/web/client_system.ts b/web/client_system.ts index 5d983e14..ba6a99ef 100644 --- a/web/client_system.ts +++ b/web/client_system.ts @@ -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 diff --git a/website/API.md b/website/API.md index 636dffc4..cd8d5f1c 100644 --- a/website/API.md +++ b/website/API.md @@ -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)} \ No newline at end of file diff --git a/website/API/command.md b/website/API/command.md new file mode 100644 index 00000000..334d537c --- /dev/null +++ b/website/API/command.md @@ -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 +} +``` diff --git a/website/API/event.md b/website/API/event.md index 9b6dc45e..a8801fbf 100644 --- a/website/API/event.md +++ b/website/API/event.md @@ -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) diff --git a/website/API/global.md b/website/API/global.md index c415e917..43de354f 100644 --- a/website/API/global.md +++ b/website/API/global.md @@ -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"}} diff --git a/website/API/index.md b/website/API/index.md index 1c3580c5..c71108d9 100644 --- a/website/API/index.md +++ b/website/API/index.md @@ -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: diff --git a/website/API/template.md b/website/API/template.md index 84d4a668..9dbab9b0 100644 --- a/website/API/template.md +++ b/website/API/template.md @@ -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} ]==])} \ No newline at end of file diff --git a/website/Library/Lua Core/Templates.md b/website/Library/Lua Core/Templates.md deleted file mode 100644 index 634e5325..00000000 --- a/website/Library/Lua Core/Templates.md +++ /dev/null @@ -1,8 +0,0 @@ -Defines some core useful templates for use in [[Space Lua]] -```space-lua -render = render or {} - -render.page = tpl[==[ -* [[${name}]] -]==] -``` diff --git a/website/Space Lua.md b/website/Space Lua.md index fa75357e..7f26ae17 100644 --- a/website/Space Lua.md +++ b/website/Space Lua.md @@ -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 diff --git a/website/Space Lua/Lua Integrated Query.md b/website/Space Lua/Lua Integrated Query.md index 932b586b..f7019724 100644 --- a/website/Space Lua/Lua Integrated Query.md +++ b/website/Space Lua/Lua Integrated Query.md @@ -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 ` 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 [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