More Lua template work
parent
21c17ac737
commit
fce78a22f8
|
@ -22,7 +22,7 @@ export abstract class CommonSystem {
|
|||
|
||||
// Hooks
|
||||
commandHook!: CommandHook;
|
||||
slashCommandHook!: SlashCommandHook;
|
||||
slashCommandHook?: SlashCommandHook;
|
||||
namespaceHook!: PlugNamespaceHook;
|
||||
codeWidgetHook!: CodeWidgetHook;
|
||||
panelWidgetHook!: PanelWidgetHook;
|
||||
|
@ -78,6 +78,10 @@ export abstract class CommonSystem {
|
|||
this.eventHook.scriptEnvironment = this.scriptEnv;
|
||||
|
||||
this.commandHook.throttledBuildAllCommands();
|
||||
if (this.slashCommandHook) {
|
||||
// Only on client
|
||||
this.slashCommandHook.throttledBuildAllCommands();
|
||||
}
|
||||
}
|
||||
|
||||
// Swap in the expanded function map
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { parse } from "$common/space_lua/parse.ts";
|
||||
import { parse, stripLuaComments } from "$common/space_lua/parse.ts";
|
||||
import { assertEquals } from "@std/assert/equals";
|
||||
|
||||
Deno.test("Test Lua parser", () => {
|
||||
// Basic block test
|
||||
|
@ -108,15 +109,21 @@ Deno.test("Test Lua parser", () => {
|
|||
});
|
||||
|
||||
Deno.test("Test comment handling", () => {
|
||||
parse(`
|
||||
-- Single line comment
|
||||
--[[ Multi
|
||||
line
|
||||
comment ]]
|
||||
f([[
|
||||
hello
|
||||
-- yo
|
||||
]])`);
|
||||
const code = `
|
||||
-- Single line comment
|
||||
--[[ Multi
|
||||
line
|
||||
comment ]]
|
||||
f([[
|
||||
hello
|
||||
-- yo
|
||||
]])`;
|
||||
const code2 = stripLuaComments(code);
|
||||
assertEquals(code2.length, code.length);
|
||||
console.log(code2);
|
||||
console.log(stripLuaComments(`e([==[
|
||||
--- Hello
|
||||
]==])`));
|
||||
});
|
||||
|
||||
Deno.test("Test query parsing", () => {
|
||||
|
|
|
@ -635,76 +635,103 @@ function parseTableField(t: ParseTree, ctx: ASTCtx): LuaTableField {
|
|||
throw new Error(`Unknown table field type: ${t.type}`);
|
||||
}
|
||||
}
|
||||
function stripLuaComments(s: string): string {
|
||||
// Strips Lua comments (single-line and multi-line) and replaces them with equivalent length whitespace
|
||||
|
||||
export function stripLuaComments(s: string): string {
|
||||
let result = "";
|
||||
let inString = false;
|
||||
let inMultilineString = false;
|
||||
let inComment = false;
|
||||
let inMultilineComment = false;
|
||||
let i = 0;
|
||||
|
||||
for (let i = 0; i < s.length; i++) {
|
||||
// Handle string detection for single-line strings (to avoid stripping comments inside strings)
|
||||
if (
|
||||
s[i] === '"' && !inComment && !inMultilineComment && !inMultilineString
|
||||
) {
|
||||
inString = !inString;
|
||||
}
|
||||
while (i < s.length) {
|
||||
// Check for long string
|
||||
if (s[i] === "[") {
|
||||
let j = i + 1;
|
||||
let equalsCount = 0;
|
||||
while (s[j] === "=") {
|
||||
equalsCount++;
|
||||
j++;
|
||||
}
|
||||
if (s[j] === "[") {
|
||||
// Found long string start
|
||||
const openBracket = s.substring(i, j + 1);
|
||||
const closeBracket = "]" + "=".repeat(equalsCount) + "]";
|
||||
result += openBracket;
|
||||
i = j + 1;
|
||||
|
||||
// Handle multi-line string literals (starting with "[[")
|
||||
if (
|
||||
!inString && !inComment && !inMultilineComment && s[i] === "[" &&
|
||||
s[i + 1] === "["
|
||||
) {
|
||||
inMultilineString = true;
|
||||
result += "[["; // Copy "[[" into result
|
||||
i += 1; // Skip over "[["
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle end of multi-line string literals (ending with "]]")
|
||||
if (inMultilineString && s[i] === "]" && s[i + 1] === "]") {
|
||||
inMultilineString = false;
|
||||
result += "]]"; // Copy "]]" into result
|
||||
i += 1; // Skip over "]]"
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle single-line comments (starting with "--")
|
||||
if (
|
||||
!inString && !inMultilineString && !inMultilineComment && s[i] === "-" &&
|
||||
s[i + 1] === "-"
|
||||
) {
|
||||
if (s[i + 2] === "[" && s[i + 3] === "[") {
|
||||
// Detect multi-line comment start "--[["
|
||||
inMultilineComment = true;
|
||||
i += 3; // Skip over "--[["
|
||||
result += " "; // Add equivalent length spaces for "--[["
|
||||
continue;
|
||||
} else {
|
||||
inComment = true;
|
||||
// Find matching closing bracket
|
||||
const content = s.substring(i);
|
||||
const closeIndex = content.indexOf(closeBracket);
|
||||
if (closeIndex !== -1) {
|
||||
// Copy string content verbatim, including any comment-like sequences
|
||||
result += content.substring(0, closeIndex) + closeBracket;
|
||||
i += closeIndex + closeBracket.length;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle end of single-line comment
|
||||
if (inComment && s[i] === "\n") {
|
||||
inComment = false;
|
||||
}
|
||||
|
||||
// Handle multi-line comment ending "]]"
|
||||
if (inMultilineComment && s[i] === "]" && s[i + 1] === "]") {
|
||||
inMultilineComment = false;
|
||||
i += 1; // Skip over "]]"
|
||||
result += " "; // Add equivalent length spaces for "]]"
|
||||
// Check for single quoted string
|
||||
if (s[i] === '"' || s[i] === "'") {
|
||||
const quote = s[i];
|
||||
result += quote;
|
||||
i++;
|
||||
while (i < s.length && s[i] !== quote) {
|
||||
if (s[i] === "\\") {
|
||||
result += s[i] + s[i + 1];
|
||||
i += 2;
|
||||
} else {
|
||||
result += s[i];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if (i < s.length) {
|
||||
result += s[i]; // closing quote
|
||||
i++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Replace comment content with spaces, or copy original content if not in comment or multi-line string
|
||||
if (inComment || inMultilineComment) {
|
||||
result += " "; // Replace comment characters with spaces
|
||||
} else {
|
||||
result += s[i];
|
||||
// Check for comments
|
||||
if (s[i] === "-" && s[i + 1] === "-") {
|
||||
// Replace the -- with spaces
|
||||
result += " ";
|
||||
i += 2;
|
||||
|
||||
// Check for long comment
|
||||
if (s[i] === "[") {
|
||||
let j = i + 1;
|
||||
let equalsCount = 0;
|
||||
while (s[j] === "=") {
|
||||
equalsCount++;
|
||||
j++;
|
||||
}
|
||||
if (s[j] === "[") {
|
||||
// Found long comment start
|
||||
const closeBracket = "]" + "=".repeat(equalsCount) + "]";
|
||||
// Replace opening bracket with spaces
|
||||
result += " ".repeat(j - i + 1);
|
||||
i = j + 1;
|
||||
|
||||
// Find matching closing bracket
|
||||
const content = s.substring(i);
|
||||
const closeIndex = content.indexOf(closeBracket);
|
||||
if (closeIndex !== -1) {
|
||||
// Replace comment content and closing bracket with spaces
|
||||
result += " ".repeat(closeIndex) + " ".repeat(closeBracket.length);
|
||||
i += closeIndex + closeBracket.length;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Single line comment - replace rest of line with spaces
|
||||
while (i < s.length && s[i] !== "\n") {
|
||||
result += " ";
|
||||
i++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
result += s[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
@ -80,6 +80,17 @@ export class LuaEnv implements ILuaSettable, ILuaGettable {
|
|||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
toJSON(omitKeys: string[] = []): Record<string, any> {
|
||||
const result: Record<string, any> = {};
|
||||
for (const key of this.keys()) {
|
||||
if (omitKeys.includes(key)) {
|
||||
continue;
|
||||
}
|
||||
result[key] = luaValueToJS(this.get(key));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export class LuaStackFrame {
|
||||
|
|
|
@ -39,13 +39,24 @@ function exposeSyscalls(env: LuaEnv, system: System<any>) {
|
|||
}
|
||||
}
|
||||
|
||||
export function buildThreadLocalEnv(_system: System<any>, globalEnv: LuaEnv) {
|
||||
export async function buildThreadLocalEnv(
|
||||
system: System<any>,
|
||||
globalEnv: LuaEnv,
|
||||
) {
|
||||
const tl = new LuaEnv();
|
||||
// const currentPageMeta = await system.localSyscall(
|
||||
// "editor.getCurrentPageMeta",
|
||||
// [],
|
||||
// );
|
||||
// tl.setLocal("pageMeta", currentPageMeta);
|
||||
if (system.registeredSyscalls.has("editor.getCurrentPageMeta")) {
|
||||
const currentPageMeta = await system.localSyscall(
|
||||
"editor.getCurrentPageMeta",
|
||||
[],
|
||||
);
|
||||
if (currentPageMeta) {
|
||||
tl.setLocal("currentPage", currentPageMeta);
|
||||
} else {
|
||||
tl.setLocal("currentPage", {
|
||||
name: await system.localSyscall("editor.getCurrentPage", []),
|
||||
});
|
||||
}
|
||||
}
|
||||
tl.setLocal("_GLOBAL", globalEnv);
|
||||
return Promise.resolve(tl);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import type { System } from "../lib/plugos/system.ts";
|
||||
import type { ParseTree } from "../plug-api/lib/tree.ts";
|
||||
import type { ScriptObject } from "../plugs/index/script.ts";
|
||||
import type { AppCommand, CommandDef } from "$lib/command.ts";
|
||||
import type { AppCommand, CommandDef, SlashCommand } from "$lib/command.ts";
|
||||
import { Intl, Temporal, toTemporalInstant } from "@js-temporal/polyfill";
|
||||
import * as syscalls from "@silverbulletmd/silverbullet/syscalls";
|
||||
import type { SlashCommandDef } from "$lib/manifest.ts";
|
||||
|
||||
// @ts-ignore: Temporal polyfill
|
||||
Date.prototype.toTemporalInstant = toTemporalInstant;
|
||||
|
@ -32,6 +33,7 @@ type AttributeExtractorCallback = (
|
|||
export class ScriptEnvironment {
|
||||
functions: Record<string, (...args: any[]) => any> = {};
|
||||
commands: Record<string, AppCommand> = {};
|
||||
slashCommands: Record<string, SlashCommand> = {};
|
||||
attributeExtractors: Record<string, AttributeExtractorCallback[]> = {};
|
||||
eventHandlers: Record<string, ((...args: any[]) => any)[]> = {};
|
||||
|
||||
|
@ -71,6 +73,16 @@ export class ScriptEnvironment {
|
|||
};
|
||||
}
|
||||
|
||||
registerSlashCommand(
|
||||
def: SlashCommandDef,
|
||||
fn: (...args: any[]) => any,
|
||||
) {
|
||||
this.slashCommands[def.name] = {
|
||||
slashCommand: def,
|
||||
run: fn,
|
||||
};
|
||||
}
|
||||
|
||||
registerAttributeExtractor(
|
||||
def: AttributeExtractorDef,
|
||||
callback: AttributeExtractorCallback,
|
||||
|
|
|
@ -46,5 +46,27 @@ export function commandSyscalls(
|
|||
},
|
||||
);
|
||||
},
|
||||
"slash_command.define": (
|
||||
_ctx,
|
||||
def: CallbackCommandDef,
|
||||
) => {
|
||||
commonSystem.scriptEnv.registerSlashCommand(
|
||||
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);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import {
|
|||
type LuaQueryCollection,
|
||||
} from "$common/space_lua/query_collection.ts";
|
||||
import {
|
||||
type LuaEnv,
|
||||
LuaEnv,
|
||||
LuaRuntimeError,
|
||||
type LuaStackFrame,
|
||||
luaValueToJS,
|
||||
|
@ -90,7 +90,12 @@ export function indexSyscalls(commonSystem: CommonSystem): SysCallMapping {
|
|||
const scopedVariables: Record<string, any> = {};
|
||||
for (const v of localVars) {
|
||||
try {
|
||||
const jsonValue = await luaValueToJS(env.get(v));
|
||||
let value = env.get(v);
|
||||
if (value instanceof LuaEnv) {
|
||||
// We don't want to include the global environment in the serialized value
|
||||
value = value.toJSON(["_GLOBAL"]);
|
||||
}
|
||||
const jsonValue = await luaValueToJS(value);
|
||||
// Ensure this is JSON serializable
|
||||
JSON.stringify(jsonValue);
|
||||
scopedVariables[v] = jsonValue;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import type { SlashCommandDef } from "$lib/manifest.ts";
|
||||
|
||||
export type CommandDef = {
|
||||
name: string;
|
||||
|
||||
|
@ -19,6 +21,11 @@ export type AppCommand = {
|
|||
run: (args?: any[]) => Promise<void>;
|
||||
};
|
||||
|
||||
export type SlashCommand = {
|
||||
slashCommand: SlashCommandDef;
|
||||
run: (args?: any[]) => Promise<void>;
|
||||
};
|
||||
|
||||
export type CommandHookEvents = {
|
||||
commandsUpdated(commandMap: Map<string, AppCommand>): void;
|
||||
};
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
Config library for defining and getting config values
|
||||
|
||||
```space-lua
|
||||
-- priority: 100
|
||||
-- priority: 10
|
||||
config = {}
|
||||
|
||||
local config_values = {}
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
Implements useful template functions
|
||||
|
||||
```space-lua
|
||||
-- priority: 100
|
||||
-- Template library for working with templates and iterables
|
||||
-- priority: 10
|
||||
-- Template API root table
|
||||
template = {}
|
||||
-- Template storage table
|
||||
templates = {}
|
||||
|
||||
-- Iterates over a table/array and applies a function to each element,
|
||||
-- concatenating the results
|
||||
|
@ -31,8 +33,4 @@ function template.new(template_str)
|
|||
return space_lua.interpolate(template_str, obj)
|
||||
end
|
||||
end
|
||||
|
||||
print("template loaded!!")
|
||||
|
||||
|
||||
```
|
||||
|
|
|
@ -2,7 +2,6 @@ import type { IndexTreeEvent } from "../../plug-api/types.ts";
|
|||
import { collectNodesOfType, findNodeOfType } from "../../plug-api/lib/tree.ts";
|
||||
import type { ObjectValue } from "../../plug-api/types.ts";
|
||||
import { indexObjects } from "./api.ts";
|
||||
import { space } from "@silverbulletmd/silverbullet/syscalls";
|
||||
export type ScriptObject = ObjectValue<{
|
||||
script: string;
|
||||
priority?: number;
|
||||
|
|
|
@ -55,14 +55,13 @@ export async function insertSnippetTemplate(
|
|||
const config = await system.getSpaceConfig();
|
||||
|
||||
const templateText = await space.readPage(slashCompletion.templatePage);
|
||||
let { renderedFrontmatter, text: replacementText, frontmatter } =
|
||||
const { renderedFrontmatter, text: replacementText, frontmatter } =
|
||||
await renderTemplate(
|
||||
templateText,
|
||||
pageObject,
|
||||
{ page: pageObject, config },
|
||||
);
|
||||
const snippetTemplate: SnippetConfig = frontmatter.hooks.snippet;
|
||||
|
||||
let cursorPos = await editor.getCursor();
|
||||
|
||||
if (renderedFrontmatter) {
|
||||
|
@ -108,8 +107,21 @@ export async function insertSnippetTemplate(
|
|||
cursorPos = await editor.getCursor();
|
||||
}
|
||||
|
||||
if (snippetTemplate.insertAt) {
|
||||
switch (snippetTemplate.insertAt) {
|
||||
await applySnippetTemplate(replacementText, snippetTemplate);
|
||||
}
|
||||
|
||||
export async function applySnippetTemplate(
|
||||
templateText: string,
|
||||
config: {
|
||||
insertAt?: string;
|
||||
match?: string;
|
||||
matchRegex?: string;
|
||||
},
|
||||
) {
|
||||
let cursorPos = await editor.getCursor();
|
||||
|
||||
if (config.insertAt) {
|
||||
switch (config.insertAt) {
|
||||
case "page-start":
|
||||
await editor.moveCursor(0);
|
||||
break;
|
||||
|
@ -141,12 +153,12 @@ export async function insertSnippetTemplate(
|
|||
|
||||
cursorPos = await editor.getCursor();
|
||||
|
||||
if (snippetTemplate.match || snippetTemplate.matchRegex) {
|
||||
if (config.match || config.matchRegex) {
|
||||
const pageText = await editor.getText();
|
||||
|
||||
// Regex matching mode
|
||||
const matchRegex = new RegExp(
|
||||
(snippetTemplate.match || snippetTemplate.matchRegex)!,
|
||||
(config.match || config.matchRegex)!,
|
||||
);
|
||||
|
||||
let startOfLine = cursorPos;
|
||||
|
@ -158,7 +170,7 @@ export async function insertSnippetTemplate(
|
|||
endOfLine++;
|
||||
}
|
||||
let currentLine = pageText.slice(startOfLine, endOfLine);
|
||||
const caretParts = replacementText.split("|^|");
|
||||
const caretParts = templateText.split("|^|");
|
||||
const emptyLine = !currentLine;
|
||||
currentLine = currentLine.replace(matchRegex, caretParts[0]);
|
||||
|
||||
|
@ -190,9 +202,9 @@ export async function insertSnippetTemplate(
|
|||
selection: newSelection,
|
||||
});
|
||||
} else {
|
||||
const carretPos = replacementText.indexOf("|^|");
|
||||
replacementText = replacementText.replace("|^|", "");
|
||||
await editor.insertAtCursor(replacementText);
|
||||
const carretPos = templateText.indexOf("|^|");
|
||||
templateText = templateText.replace("|^|", "");
|
||||
await editor.insertAtCursor(templateText);
|
||||
if (carretPos !== -1) {
|
||||
await editor.moveCursor(cursorPos + carretPos);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,10 @@ functions:
|
|||
instantiatePageTemplate:
|
||||
path: page.ts:instantiatePageTemplate
|
||||
|
||||
applySnippetTemplate:
|
||||
path: snippet.ts:applySnippetTemplate
|
||||
env: client
|
||||
|
||||
# Indexing
|
||||
indexTemplate:
|
||||
path: index.ts:indexTemplate
|
||||
|
|
|
@ -273,9 +273,7 @@ export class Client implements ConfigContainer {
|
|||
type: "config-loaded",
|
||||
config: this.config,
|
||||
});
|
||||
this.clientSystem.slashCommandHook.buildAllCommands(
|
||||
this.clientSystem.system,
|
||||
);
|
||||
this.clientSystem.slashCommandHook!.buildAllCommands();
|
||||
this.eventHook.dispatchEvent("config:loaded", this.config);
|
||||
}
|
||||
|
||||
|
|
|
@ -125,7 +125,7 @@ export class ClientSystem extends CommonSystem {
|
|||
this.system.addHook(this.commandHook);
|
||||
|
||||
// Slash command hook
|
||||
this.slashCommandHook = new SlashCommandHook(this.client);
|
||||
this.slashCommandHook = new SlashCommandHook(this.client, this);
|
||||
this.system.addHook(this.slashCommandHook);
|
||||
|
||||
this.eventHook.addLocalListener(
|
||||
|
@ -147,7 +147,7 @@ export class ClientSystem extends CommonSystem {
|
|||
|
||||
init() {
|
||||
// Slash command hook
|
||||
this.slashCommandHook = new SlashCommandHook(this.client);
|
||||
this.slashCommandHook = new SlashCommandHook(this.client, this);
|
||||
this.system.addHook(this.slashCommandHook);
|
||||
|
||||
// Syscalls available to all plugs
|
||||
|
|
|
@ -56,7 +56,7 @@ class InlineContentWidget extends WidgetType {
|
|||
return div;
|
||||
}
|
||||
|
||||
const url = encodeURIComponent(this.url)
|
||||
const url = encodeURIComponent(this.url);
|
||||
|
||||
if (mimeType.startsWith("image/")) {
|
||||
const img = document.createElement("img");
|
||||
|
|
|
@ -59,7 +59,11 @@ export function luaDirectivePlugin(client: Client) {
|
|||
.args[0];
|
||||
|
||||
const tl = new LuaEnv();
|
||||
tl.setLocal("pageMeta", currentPageMeta);
|
||||
tl.setLocal(
|
||||
"currentPage",
|
||||
currentPageMeta ||
|
||||
{ name: client.ui.viewState.currentPage },
|
||||
);
|
||||
tl.setLocal("_GLOBAL", client.clientSystem.spaceLuaEnv.env);
|
||||
const sf = new LuaStackFrame(tl, expr.ctx);
|
||||
const threadLocalizedEnv = new LuaEnv(
|
||||
|
|
|
@ -104,7 +104,10 @@ export function TopBar({
|
|||
<div className="sb-actions">
|
||||
{progressPerc !== undefined &&
|
||||
(
|
||||
<div className="progress-wrapper" title={`Sync Progress: ${progressPerc}%`}>
|
||||
<div
|
||||
className="progress-wrapper"
|
||||
title={`Sync Progress: ${progressPerc}%`}
|
||||
>
|
||||
<div
|
||||
className="progress-bar"
|
||||
style={`background: radial-gradient(closest-side, var(--top-background-color) 79%, transparent 80% 100%), conic-gradient(var(--button-color) ${progressPerc}%, var(--button-background-color) 0);`}
|
||||
|
|
|
@ -110,7 +110,7 @@ export function createEditorState(
|
|||
autocompletion({
|
||||
override: [
|
||||
client.editorComplete.bind(client),
|
||||
client.clientSystem.slashCommandHook.slashCommandCompleter.bind(
|
||||
client.clientSystem.slashCommandHook!.slashCommandCompleter.bind(
|
||||
client.clientSystem.slashCommandHook,
|
||||
),
|
||||
],
|
||||
|
|
|
@ -11,9 +11,10 @@ import type {
|
|||
SlashCompletionOption,
|
||||
SlashCompletions,
|
||||
} from "../../plug-api/types.ts";
|
||||
import { safeRun } from "$lib/async.ts";
|
||||
import { safeRun, throttle } from "$lib/async.ts";
|
||||
import type { SlashCommandDef, SlashCommandHookT } from "$lib/manifest.ts";
|
||||
import { parseCommand } from "$common/command.ts";
|
||||
import type { CommonSystem } from "$common/common_system.ts";
|
||||
|
||||
export type AppSlashCommand = {
|
||||
slashCommand: SlashCommandDef;
|
||||
|
@ -26,11 +27,17 @@ export class SlashCommandHook implements Hook<SlashCommandHookT> {
|
|||
slashCommands = new Map<string, AppSlashCommand>();
|
||||
private editor: Client;
|
||||
|
||||
constructor(editor: Client) {
|
||||
constructor(editor: Client, private commonSystem: CommonSystem) {
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
buildAllCommands(system: System<SlashCommandHookT>) {
|
||||
throttledBuildAllCommands = throttle(() => {
|
||||
this.buildAllCommands();
|
||||
}, 200);
|
||||
|
||||
buildAllCommands() {
|
||||
const system = this.commonSystem.system;
|
||||
|
||||
this.slashCommands.clear();
|
||||
for (const plug of system.loadedPlugs.values()) {
|
||||
for (
|
||||
|
@ -50,6 +57,15 @@ export class SlashCommandHook implements Hook<SlashCommandHookT> {
|
|||
});
|
||||
}
|
||||
}
|
||||
// Iterate over script defined slash commands
|
||||
for (
|
||||
const [name, command] of Object.entries(
|
||||
this.commonSystem.scriptEnv.slashCommands,
|
||||
)
|
||||
) {
|
||||
this.slashCommands.set(name, command);
|
||||
}
|
||||
// Iterate over all shortcuts
|
||||
if (this.editor.config?.shortcuts) {
|
||||
// Add slash commands for shortcuts that configure them
|
||||
for (const shortcut of this.editor.config.shortcuts) {
|
||||
|
@ -161,10 +177,10 @@ export class SlashCommandHook implements Hook<SlashCommandHookT> {
|
|||
}
|
||||
|
||||
apply(system: System<SlashCommandHookT>): void {
|
||||
this.buildAllCommands(system);
|
||||
this.buildAllCommands();
|
||||
system.on({
|
||||
plugLoaded: () => {
|
||||
this.buildAllCommands(system);
|
||||
this.buildAllCommands();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -137,7 +137,8 @@ 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.GLOBAL` providing access to the global scope
|
||||
* `_CTX.currentPage` providing access (in the client only) to the currently open page (PageMeta object)
|
||||
* `_CTX._GLOBAL` providing access to the global scope
|
||||
|
||||
# API
|
||||
Lua APIs, which should be (roughly) implemented according to the Lua standard.
|
||||
|
|
Loading…
Reference in New Issue