Support ${lua expression} in templates

pull/1232/head
Zef Hemel 2025-02-06 11:58:27 +01:00
parent a169406d37
commit e1c997616c
8 changed files with 84 additions and 37 deletions

View File

@ -1,10 +1,29 @@
import type { SysCallMapping } from "$lib/plugos/system.ts";
import { parse } from "../space_lua/parse.ts";
import { evalExpression } from "$common/space_lua/eval.ts";
import { parse, parseExpressionString } from "../space_lua/parse.ts";
import type { CommonSystem } from "$common/common_system.ts";
import { LuaStackFrame, luaValueToJS } from "$common/space_lua/runtime.ts";
export function luaSyscalls(): SysCallMapping {
export function luaSyscalls(commonSystem: CommonSystem): SysCallMapping {
return {
"lua.parse": (_ctx, code: string) => {
return parse(code);
},
/**
* Evaluates a Lua expression and returns the result as a JavaScript value
* @param _ctx
* @param expression
* @returns
*/
"lua.evalExpression": (_ctx, expression: string) => {
const ast = parseExpressionString(expression);
return luaValueToJS(
evalExpression(
ast,
commonSystem.spaceLuaEnv.env,
LuaStackFrame.lostFrame,
),
);
},
};
}

View File

@ -2,8 +2,7 @@ import type { AST } from "../../plug-api/lib/tree.ts";
import { evalQueryExpression } from "@silverbulletmd/silverbullet/lib/query_expression";
import { expressionToKvQueryExpression } from "../../plug-api/lib/parse_query.ts";
import type { FunctionMap } from "../../plug-api/types.ts";
import { jsonToMDTable } from "../../plugs/template/util.ts";
import { LuaTable } from "$common/space_lua/runtime.ts";
import { renderExpressionResult } from "../../plugs/template/util.ts";
export async function renderTemplate(
ast: AST,
@ -83,35 +82,6 @@ async function renderExpressionDirective(
return renderExpressionResult(result);
}
export function renderExpressionResult(result: any): string {
if (result instanceof LuaTable) {
result = result.toJS();
}
if (
Array.isArray(result) && result.length > 0 && typeof result[0] === "object"
) {
// If result is an array of objects, render as a markdown table
try {
return jsonToMDTable(result);
} catch (e: any) {
console.error(
`Error rendering expression directive: ${e.message} for value ${
JSON.stringify(result)
}`,
);
return JSON.stringify(result);
}
} else if (typeof result === "object" && result.constructor === Object) {
// if result is a plain object, render as a markdown table
return jsonToMDTable([result]);
} else if (Array.isArray(result)) {
// Not-object array, let's render it as a markdown list
return result.map((item) => `- ${item}`).join("\n");
} else {
return "" + result;
}
}
async function renderEachVarDirective(
ast: AST,
value: any[],

View File

@ -6,3 +6,9 @@ export function parse(
): Promise<ParseTree> {
return syscall("lua.parse", code);
}
export function evalExpression(
expression: string,
): Promise<any> {
return syscall("lua.evalExpression", expression);
}

View File

@ -4,7 +4,7 @@ import {
renderToText,
replaceNodesMatchingAsync,
} from "../../plug-api/lib/tree.ts";
import { codeWidget } from "@silverbulletmd/silverbullet/syscalls";
import { codeWidget, lua } from "@silverbulletmd/silverbullet/syscalls";
import { parseMarkdown } from "../../plug-api/syscalls/markdown.ts";
import {
type MarkdownRenderOptions,
@ -12,6 +12,8 @@ import {
} from "./markdown_render.ts";
import { validatePageName } from "@silverbulletmd/silverbullet/lib/page_ref";
import { parsePageRef } from "@silverbulletmd/silverbullet/lib/page_ref";
import type { LuaExpression } from "$common/space_lua/ast.ts";
import { renderExpressionResult } from "../template/util.ts";
/**
* Finds code widgets, runs their plug code to render and inlines their content in the parse tree
@ -102,6 +104,26 @@ export async function expandCodeWidgets(
page,
);
}
} else if (n.type === "LuaDirective") {
const expr: LuaExpression | null = findNodeOfType(
n,
"LuaExpressionDirective",
) as LuaExpression | null;
if (!expr) {
return;
}
const exprText = renderToText(expr);
let result = await lua.evalExpression(exprText);
if (result.markdown) {
result = result.markdown;
}
const markdown = renderExpressionResult(result);
console.log("Expanding LuaDirective", exprText, result, markdown);
return await parseMarkdown(markdown);
}
});
return mdTree;

View File

@ -1,6 +1,7 @@
import type { PageMeta } from "../../plug-api/types.ts";
import { space, system, template } from "@silverbulletmd/silverbullet/syscalls";
import { cleanTemplate } from "./plug_api.ts";
import { LuaTable } from "$common/space_lua/runtime.ts";
export function defaultJsonTransformer(v: any): string {
if (v === undefined) {
@ -91,3 +92,32 @@ export async function renderQueryTemplate(
config,
});
}
export function renderExpressionResult(result: any): string {
if (result instanceof LuaTable) {
result = result.toJS();
}
if (
Array.isArray(result) && result.length > 0 && typeof result[0] === "object"
) {
// If result is an array of objects, render as a markdown table
try {
return jsonToMDTable(result);
} catch (e: any) {
console.error(
`Error rendering expression directive: ${e.message} for value ${
JSON.stringify(result)
}`,
);
return JSON.stringify(result);
}
} else if (typeof result === "object" && result.constructor === Object) {
// if result is a plain object, render as a markdown table
return jsonToMDTable([result]);
} else if (Array.isArray(result)) {
// Not-object array, let's render it as a markdown list
return result.map((item) => `- ${item}`).join("\n");
} else {
return "" + result;
}
}

View File

@ -139,7 +139,7 @@ export class ServerSystem extends CommonSystem {
jsonschemaSyscalls(),
indexSyscalls(this),
commandSyscalls(this),
luaSyscalls(),
luaSyscalls(this),
templateSyscalls(this.ds),
dataStoreReadSyscalls(this.ds, this),
codeWidgetSyscalls(codeWidgetHook),

View File

@ -168,7 +168,7 @@ export class ClientSystem extends CommonSystem {
jsonschemaSyscalls(),
indexSyscalls(this),
commandSyscalls(this),
luaSyscalls(),
luaSyscalls(this),
this.client.syncMode
// In sync mode handle locally
? mqSyscalls(this.mq)

View File

@ -11,7 +11,7 @@ import { extendedMarkdownLanguage } from "$common/markdown_parser/parser.ts";
import { renderToText } from "@silverbulletmd/silverbullet/lib/tree";
import { activeWidgets } from "./markdown_widget.ts";
import { attachWidgetEventHandlers } from "./widget_util.ts";
import { renderExpressionResult } from "$common/template/render.ts";
import { renderExpressionResult } from "../../plugs/template/util.ts";
export type LuaWidgetCallback = (
bodyText: string,