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