Lua: tweaks and fixes

pull/1224/head
Zef Hemel 2025-01-25 08:29:11 +01:00
parent 6f3d021da7
commit 236b2a7fdd
9 changed files with 76 additions and 23 deletions

View File

@ -315,6 +315,11 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
return this.arrayPart.length; return this.arrayPart.length;
} }
hasKeys(): boolean {
return !!(Object.keys(this.stringKeys).length > 0 ||
this.arrayPart.length > 0 || (this.otherKeys && this.otherKeys.size > 0));
}
keys(): any[] { keys(): any[] {
const keys: any[] = Object.keys(this.stringKeys); const keys: any[] = Object.keys(this.stringKeys);
for (let i = 0; i < this.arrayPart.length; i++) { for (let i = 0; i < this.arrayPart.length; i++) {
@ -695,7 +700,7 @@ export function luaTruthy(value: any): boolean {
return false; return false;
} }
if (value instanceof LuaTable) { if (value instanceof LuaTable) {
return value.length > 0; return value.hasKeys();
} }
return true; return true;
} }
@ -773,6 +778,17 @@ export function jsToLuaValue(value: any): any {
return value; return value;
} else if (value instanceof Uint8Array || value instanceof ArrayBuffer) { } else if (value instanceof Uint8Array || value instanceof ArrayBuffer) {
return value; return value;
} else if (Array.isArray(value) && "index" in value && "input" in value) {
// This is a RegExpMatchArray
const regexMatch = value as RegExpMatchArray;
const regexMatchTable = new LuaTable();
for (let i = 0; i < regexMatch.length; i++) {
regexMatchTable.set(i + 1, regexMatch[i]);
}
regexMatchTable.set("index", regexMatch.index);
regexMatchTable.set("input", regexMatch.input);
regexMatchTable.set("groups", regexMatch.groups);
return regexMatchTable;
} else if (Array.isArray(value)) { } else if (Array.isArray(value)) {
const table = new LuaTable(); const table = new LuaTable();
for (let i = 0; i < value.length; i++) { for (let i = 0; i < value.length; i++) {

View File

@ -1,4 +1,5 @@
import { import {
jsToLuaValue,
LuaBuiltinFunction, LuaBuiltinFunction,
luaCall, luaCall,
LuaMultiRes, LuaMultiRes,
@ -213,4 +214,13 @@ export const stringApi = new LuaTable({
trim_end: new LuaBuiltinFunction((_sf, s: string) => { trim_end: new LuaBuiltinFunction((_sf, s: string) => {
return s.trimEnd(); return s.trimEnd();
}), }),
match_regex: new LuaBuiltinFunction((_sf, s: string, pattern: string) => {
const regex = new RegExp(pattern);
const result = s.match(regex);
return jsToLuaValue(result);
// if (!result) {
// return new LuaMultiRes([]);
// }
// return new LuaMultiRes(result.slice(1));
}),
}); });

View File

@ -1,6 +1,7 @@
import { import {
type ILuaFunction, type ILuaFunction,
LuaBuiltinFunction, LuaBuiltinFunction,
type LuaEnv,
luaEquals, luaEquals,
LuaRuntimeError, LuaRuntimeError,
LuaTable, LuaTable,
@ -17,7 +18,10 @@ export const tableApi = new LuaTable({
* @returns The concatenated string. * @returns The concatenated string.
*/ */
concat: new LuaBuiltinFunction( concat: new LuaBuiltinFunction(
(_sf, tbl: LuaTable, sep?: string, i?: number, j?: number) => { (_sf, tbl: LuaTable | any[], sep?: string, i?: number, j?: number) => {
if (Array.isArray(tbl)) {
return tbl.join(sep);
}
sep = sep ?? ""; sep = sep ?? "";
i = i ?? 1; i = i ?? 1;
j = j ?? tbl.length; j = j ?? tbl.length;
@ -70,7 +74,8 @@ export const tableApi = new LuaTable({
* @param tbl - The table to get the keys from. * @param tbl - The table to get the keys from.
* @returns The keys of the table. * @returns The keys of the table.
*/ */
keys: new LuaBuiltinFunction((_sf, tbl: LuaTable) => { keys: new LuaBuiltinFunction((_sf, tbl: LuaTable | LuaEnv) => {
console.log("Keys", tbl);
return tbl.keys(); return tbl.keys();
}), }),
/** /**

View File

@ -38,7 +38,7 @@ export function commandSyscalls(
const sf = new LuaStackFrame(tl, null); const sf = new LuaStackFrame(tl, null);
try { try {
return luaValueToJS( return luaValueToJS(
await luaCall(def.run, args.map(jsToLuaValue), sf), await luaCall(def.run, args.map(jsToLuaValue), {}, sf),
); );
} catch (e: any) { } catch (e: any) {
await handleLuaError(e, commonSystem.system); await handleLuaError(e, commonSystem.system);
@ -60,7 +60,7 @@ export function commandSyscalls(
const sf = new LuaStackFrame(tl, null); const sf = new LuaStackFrame(tl, null);
try { try {
return luaValueToJS( return luaValueToJS(
await luaCall(def.run, args.map(jsToLuaValue), sf), await luaCall(def.run, args.map(jsToLuaValue), {}, sf),
); );
} catch (e: any) { } catch (e: any) {
await handleLuaError(e, commonSystem.system); await handleLuaError(e, commonSystem.system);

View File

@ -35,9 +35,10 @@ export function eventListenerSyscalls(
); );
const sf = new LuaStackFrame(tl, null); const sf = new LuaStackFrame(tl, null);
try { try {
return luaValueToJS( const val = luaValueToJS(
await luaCall(def.run, args.map(jsToLuaValue), sf), await luaCall(def.run, args.map(jsToLuaValue), {}, sf),
); );
return val;
} catch (e: any) { } catch (e: any) {
await handleLuaError(e, commonSystem.system); await handleLuaError(e, commonSystem.system);
} }

View File

@ -53,6 +53,8 @@ export type SlashCommandDef = {
name: string; name: string;
description?: string; description?: string;
boost?: number; boost?: number;
// Parent AST nodes in which this slash command is available
contexts?: string[];
}; };
export type SlashCommandHookT = { export type SlashCommandHookT = {
slashCommand?: SlashCommandDef; slashCommand?: SlashCommandDef;

View File

@ -19,7 +19,7 @@ function config.set(key, value)
error("Config key not defined: " .. key) error("Config key not defined: " .. key)
end end
if schema != true then if schema != true then
result = jsonschema.validate_object(schema, value) local result = jsonschema.validate_object(schema, value)
if result != nil then if result != nil then
error("Validation error (" .. key .. "): " .. result) error("Validation error (" .. key .. "): " .. result)
end end

View File

@ -2,7 +2,7 @@ import type {
CompletionContext, CompletionContext,
CompletionResult, CompletionResult,
} from "@codemirror/autocomplete"; } from "@codemirror/autocomplete";
import type { Compartment } from "@codemirror/state"; import type { Compartment, EditorState } from "@codemirror/state";
import { EditorView } from "@codemirror/view"; import { EditorView } from "@codemirror/view";
import { syntaxTree } from "@codemirror/language"; import { syntaxTree } from "@codemirror/language";
import { compile as gitIgnoreCompiler } from "gitignore-parser"; import { compile as gitIgnoreCompiler } from "gitignore-parser";
@ -892,21 +892,13 @@ export class Client implements ConfigContainer {
const linePrefix = line.text.slice(0, selection.from - line.from); const linePrefix = line.text.slice(0, selection.from - line.from);
// Build up list of parent nodes, some completions need this // Build up list of parent nodes, some completions need this
const parentNodes: string[] = [];
const sTree = syntaxTree(editorState); const sTree = syntaxTree(editorState);
const currentNode = sTree.resolveInner(selection.from); const currentNode = sTree.resolveInner(editorState.selection.main.from);
if (currentNode) {
let node: SyntaxNode | null = currentNode; const parentNodes: string[] = this.extractParentNodes(
do { editorState,
if (node.name === "FencedCode" || node.name === "FrontMatter") { currentNode,
const body = editorState.sliceDoc(node.from + 3, node.to - 3); );
parentNodes.push(`${node.name}:${body}`);
} else {
parentNodes.push(node.name);
}
node = node.parent;
} while (node);
}
// Dispatch the event // Dispatch the event
const results = await this.dispatchAppEvent(eventName, { const results = await this.dispatchAppEvent(eventName, {
@ -949,6 +941,23 @@ export class Client implements ConfigContainer {
return currentResult; return currentResult;
} }
public extractParentNodes(editorState: EditorState, currentNode: SyntaxNode) {
const parentNodes: string[] = [];
if (currentNode) {
let node: SyntaxNode | null = currentNode;
do {
if (node.name === "FencedCode" || node.name === "FrontMatter") {
const body = editorState.sliceDoc(node.from + 3, node.to - 3);
parentNodes.push(`${node.name}:${body}`);
} else {
parentNodes.push(node.name);
}
node = node.parent;
} while (node);
}
return parentNodes;
}
editorComplete( editorComplete(
context: CompletionContext, context: CompletionContext,
): Promise<CompletionResult | null> { ): Promise<CompletionResult | null> {

View File

@ -108,7 +108,17 @@ export class SlashCommandHook implements Hook<SlashCommandHookT> {
return null; return null;
} }
// Check if the slash command is available in the current context
const parentNodes = this.editor.extractParentNodes(ctx.state, currentNode);
// console.log("Parent nodes", parentNodes);
for (const def of this.slashCommands.values()) { for (const def of this.slashCommands.values()) {
if (
def.slashCommand.contexts && !def.slashCommand.contexts.some(
(context) => parentNodes.some((node) => node.startsWith(context)),
)
) {
continue;
}
options.push({ options.push({
label: def.slashCommand.name, label: def.slashCommand.name,
detail: def.slashCommand.description, detail: def.slashCommand.description,