163 lines
4.3 KiB
TypeScript
163 lines
4.3 KiB
TypeScript
import { afterEach, expect, test } from "vitest";
|
|
import type {
|
|
CompleteEvent,
|
|
EnrichedClickEvent,
|
|
} from "@silverbulletmd/silverbullet/type/client";
|
|
import { luaComplete, luaNavigate } from "./lua.ts";
|
|
|
|
const originalSyscall = globalThis.syscall;
|
|
|
|
afterEach(() => {
|
|
globalThis.syscall = originalSyscall;
|
|
});
|
|
|
|
function completionEvent(
|
|
linePrefix: string,
|
|
luaCode = linePrefix,
|
|
): CompleteEvent {
|
|
return {
|
|
pageName: "Test",
|
|
linePrefix,
|
|
pos: luaCode.length,
|
|
parentNodes: [`FencedCode:space-lua\n${luaCode}`],
|
|
};
|
|
}
|
|
|
|
function clickEvent(pos: number, parentNodes: string[]): EnrichedClickEvent {
|
|
return {
|
|
page: "Test",
|
|
pos,
|
|
metaKey: false,
|
|
ctrlKey: true,
|
|
altKey: false,
|
|
parentNodes,
|
|
};
|
|
}
|
|
|
|
test("luaComplete returns documented function snippets", async () => {
|
|
globalThis.syscall = async (name: string, ...args: any[]) => {
|
|
expect(name).toBe("lua.inspect");
|
|
expect(args).toEqual([["string"]]);
|
|
return {
|
|
type: "table",
|
|
properties: [
|
|
{
|
|
key: "gmatch",
|
|
type: "function",
|
|
functionInfo: {
|
|
kind: "builtin",
|
|
name: "string.gmatch",
|
|
description: "Iterates over matches in a string.",
|
|
parameters: [
|
|
{ name: "s", type: "string" },
|
|
{ name: "pattern", type: "string" },
|
|
],
|
|
},
|
|
},
|
|
],
|
|
};
|
|
};
|
|
|
|
expect(await luaComplete(completionEvent("string.gm"))).toEqual({
|
|
from: "string.".length,
|
|
options: [
|
|
expect.objectContaining({
|
|
label: "gmatch(s, pattern)",
|
|
snippet: "gmatch(${})",
|
|
detail: "Iterates over matches in a string.",
|
|
documentation: expect.stringContaining("## string.gmatch"),
|
|
}),
|
|
],
|
|
});
|
|
});
|
|
|
|
test("luaComplete includes properties from schema-derived locals", async () => {
|
|
globalThis.syscall = async (name: string, ...args: any[]) => {
|
|
if (name === "config.get") {
|
|
expect(args[0]).toEqual(["tags", "person", "schema"]);
|
|
return {
|
|
type: "object",
|
|
properties: {
|
|
name: { type: "string" },
|
|
age: { type: "number" },
|
|
},
|
|
};
|
|
}
|
|
throw new Error(`Unexpected syscall: ${name}`);
|
|
};
|
|
|
|
const luaCode = "local person = tags.person\nperson.na";
|
|
expect(await luaComplete(completionEvent("person.na", luaCode))).toEqual({
|
|
from: luaCode.length - 2,
|
|
options: [
|
|
{
|
|
label: "name",
|
|
detail: "string",
|
|
},
|
|
],
|
|
});
|
|
});
|
|
|
|
test("luaNavigate opens Lua-defined function sources", async () => {
|
|
const calls: [string, ...any[]][] = [];
|
|
globalThis.syscall = async (name: string, ...args: any[]) => {
|
|
calls.push([name, ...args]);
|
|
if (name === "editor.getText") return "demo.greet()";
|
|
if (name === "lua.inspect") {
|
|
expect(args).toEqual([["demo", "greet"]]);
|
|
return {
|
|
type: "function",
|
|
functionInfo: { kind: "lua", name: "demo.greet" },
|
|
definition: "Library/Test@123",
|
|
properties: [],
|
|
};
|
|
}
|
|
if (name === "editor.navigate") return;
|
|
throw new Error(`Unexpected syscall: ${name}`);
|
|
};
|
|
|
|
await luaNavigate(clickEvent(7, ["FencedCode:space-lua\ndemo.greet()"]));
|
|
|
|
expect(calls).toContainEqual([
|
|
"editor.navigate",
|
|
"Library/Test@123",
|
|
false,
|
|
false,
|
|
]);
|
|
});
|
|
|
|
test("luaNavigate leaves Markdown links to regular navigation", async () => {
|
|
globalThis.syscall = async (name: string) => {
|
|
throw new Error(`Unexpected syscall: ${name}`);
|
|
};
|
|
|
|
await luaNavigate(
|
|
clickEvent(7, ["WikiLink", "FencedCode:space-lua\nprint([[Some Page]])"]),
|
|
);
|
|
});
|
|
|
|
test("luaNavigate reports values without Lua definitions", async () => {
|
|
const calls: [string, ...any[]][] = [];
|
|
globalThis.syscall = async (name: string, ...args: any[]) => {
|
|
calls.push([name, ...args]);
|
|
if (name === "editor.getText") return "string.gmatch()";
|
|
if (name === "lua.inspect") {
|
|
return {
|
|
type: "function",
|
|
functionInfo: { kind: "builtin", name: "string.gmatch" },
|
|
properties: [],
|
|
};
|
|
}
|
|
if (name === "editor.flashNotification") return;
|
|
throw new Error(`Unexpected syscall: ${name}`);
|
|
};
|
|
|
|
await luaNavigate(clickEvent(8, ["FencedCode:space-lua\nstring.gmatch()"]));
|
|
|
|
expect(calls).toContainEqual([
|
|
"editor.flashNotification",
|
|
"Cannot navigate to definition; not defined in Lua.",
|
|
"info",
|
|
]);
|
|
});
|