@silverbulletmd/silverbullet 2.9.0 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -23
- package/client/plugos/hooks/syscall.ts +24 -10
- package/client/plugos/syscalls/asset.ts +61 -23
- package/client/plugos/syscalls/clientStore.ts +32 -6
- package/client/plugos/syscalls/client_code_widget.ts +9 -4
- package/client/plugos/syscalls/code_widget.ts +26 -11
- package/client/plugos/syscalls/config.ts +146 -30
- package/client/plugos/syscalls/datastore.ts +146 -40
- package/client/plugos/syscalls/editor.ts +1432 -711
- package/client/plugos/syscalls/event.ts +46 -13
- package/client/plugos/syscalls/fetch.ts +123 -82
- package/client/plugos/syscalls/index.ts +242 -116
- package/client/plugos/syscalls/jsonschema.ts +107 -8
- package/client/plugos/syscalls/language.ts +38 -12
- package/client/plugos/syscalls/markdown.ts +166 -50
- package/client/plugos/syscalls/mq.ts +93 -20
- package/client/plugos/syscalls/schema_introspection.ts +33 -0
- package/client/plugos/syscalls/service_registry.ts +74 -20
- package/client/plugos/syscalls/shell.ts +51 -20
- package/client/plugos/syscalls/space.ts +169 -113
- package/client/plugos/syscalls/sync.ts +50 -19
- package/client/plugos/syscalls/system.ts +236 -139
- package/client/plugos/system.ts +21 -5
- package/client/space_lua/api_documentation.ts +107 -0
- package/client/space_lua/ast.ts +13 -0
- package/client/space_lua/eval.ts +12 -4
- package/client/space_lua/parse.ts +243 -108
- package/client/space_lua/pretty_print.ts +128 -54
- package/client/space_lua/query_collection.ts +4 -4
- package/client/space_lua/render_widget.ts +156 -0
- package/client/space_lua/runtime.ts +83 -22
- package/client/space_lua/stdlib/crypto.ts +8 -3
- package/client/space_lua/stdlib/encoding.ts +27 -9
- package/client/space_lua/stdlib/js.ts +137 -22
- package/client/space_lua/stdlib/load.ts +24 -15
- package/client/space_lua/stdlib/math.ts +323 -118
- package/client/space_lua/stdlib/net.ts +57 -9
- package/client/space_lua/stdlib/os.ts +111 -44
- package/client/space_lua/stdlib/space_lua.ts +344 -20
- package/client/space_lua/stdlib/string.ts +282 -104
- package/client/space_lua/stdlib/string_pack.ts +58 -19
- package/client/space_lua/stdlib/table.ts +195 -40
- package/client/space_lua/stdlib.ts +335 -93
- package/client/space_lua/syscalls.ts +270 -0
- package/dist/plug-compile.js +1 -1
- package/package.json +3 -3
- package/plug-api/lib/ref.ts +96 -0
- package/plug-api/lib/shortcut.ts +2 -1
- package/plug-api/syscalls/config.ts +5 -3
- package/plug-api/syscalls/editor.ts +16 -0
- package/plug-api/syscalls/index.ts +17 -1
- package/plug-api/syscalls/jsonschema.ts +9 -0
- package/plug-api/syscalls/lua.ts +7 -0
- package/plug-api/syscalls/shell.ts +0 -1
- package/plug-api/syscalls/system.ts +7 -0
- package/plug-api/system_mock.ts +2 -2
- package/plug-api/types/index.ts +52 -1
- package/plug-api/types/manifest.ts +22 -4
- package/plug-api/ui/cx.ts +1 -3
- package/plug-api/ui/index.ts +3 -0
- package/plug-api/ui/panel_styles.ts +4 -3
- package/plug-api/ui/slugify.ts +44 -0
- package/client/plugos/syscalls/lua.ts +0 -81
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { SysCallMapping } from "../system.ts";
|
|
2
2
|
import type { Client } from "../../client.ts";
|
|
3
|
-
import { publicVersion } from "../../../
|
|
3
|
+
import { version as publicVersion } from "../../../version.json";
|
|
4
4
|
import type { CommandDef } from "@silverbulletmd/silverbullet/type/manifest";
|
|
5
5
|
import type { SyscallMeta } from "@silverbulletmd/silverbullet/type/index";
|
|
6
6
|
|
|
@@ -9,153 +9,250 @@ export function systemSyscalls(
|
|
|
9
9
|
readOnlyMode: boolean,
|
|
10
10
|
): SysCallMapping {
|
|
11
11
|
const api: SysCallMapping = {
|
|
12
|
-
"system.invokeFunction":
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
12
|
+
"system.invokeFunction": {
|
|
13
|
+
callback: (
|
|
14
|
+
_ctx,
|
|
15
|
+
fullName: string, // plug.function
|
|
16
|
+
...args: any[]
|
|
17
|
+
) => {
|
|
18
|
+
const [plugName, functionName] = fullName.split(".");
|
|
19
|
+
if (!plugName || !functionName) {
|
|
20
|
+
throw Error(`Invalid function name ${fullName}`);
|
|
21
|
+
}
|
|
22
|
+
const plug = client.clientSystem.system.loadedPlugs.get(plugName);
|
|
23
|
+
if (!plug) {
|
|
24
|
+
throw Error(`Plug ${plugName} not found`);
|
|
25
|
+
}
|
|
26
|
+
const functionDef = plug.manifest!.functions[functionName];
|
|
27
|
+
if (!functionDef) {
|
|
28
|
+
throw Error(`Function ${functionName} not found`);
|
|
29
|
+
}
|
|
30
|
+
return plug.invoke(functionName, args);
|
|
31
|
+
},
|
|
32
|
+
description: "Invokes a loaded plug function by its plug-qualified name.",
|
|
33
|
+
signatures: ["system.invokeFunction(name, ...)"],
|
|
34
|
+
},
|
|
35
|
+
"system.invokeFunctionOnServer": {
|
|
36
|
+
callback: (
|
|
37
|
+
ctx,
|
|
38
|
+
fullName: string, // plug.function
|
|
39
|
+
...args: any[]
|
|
40
|
+
) => {
|
|
41
|
+
console.warn(
|
|
42
|
+
"Calling deprecated system.invokeFunctionOnServer, use system.invokeFunction instead",
|
|
43
|
+
);
|
|
44
|
+
const registration = api["system.invokeFunction"];
|
|
45
|
+
const invokeFunction =
|
|
46
|
+
typeof registration === "function"
|
|
47
|
+
? registration
|
|
48
|
+
: registration.callback;
|
|
49
|
+
return invokeFunction(ctx, fullName, ...args);
|
|
50
|
+
},
|
|
51
|
+
description: "Deprecated alias for system.invokeFunction.",
|
|
52
|
+
deprecated: "Use system.invokeFunction instead.",
|
|
53
|
+
signatures: ["system.invokeFunctionOnServer(name, ...)"],
|
|
54
|
+
},
|
|
55
|
+
"system.serverSyscall": {
|
|
56
|
+
callback: (_ctx, name: string, ...args: any[]) => {
|
|
57
|
+
console.warn(
|
|
58
|
+
"Calling deprecated system.serverSyscall, use syscall instead",
|
|
59
|
+
);
|
|
60
|
+
return client.clientSystem.localSyscall(name, args);
|
|
61
|
+
},
|
|
62
|
+
description: "Deprecated helper for invoking a named syscall.",
|
|
63
|
+
deprecated: "Invoke the target syscall directly instead.",
|
|
64
|
+
signatures: ["system.serverSyscall(name, ...)"],
|
|
65
|
+
},
|
|
66
|
+
"system.invokeCommand": {
|
|
67
|
+
callback: (_ctx, name: string, args?: string[]) => {
|
|
68
|
+
console.warn("Deprecated, use editor.invokeCommand instead");
|
|
69
|
+
return client.runCommandByName(name, args);
|
|
70
|
+
},
|
|
71
|
+
description: "Deprecated alias for editor.invokeCommand.",
|
|
72
|
+
deprecated: "Use editor.invokeCommand instead.",
|
|
73
|
+
signatures: ["system.invokeCommand(name, args?)"],
|
|
74
|
+
},
|
|
75
|
+
"system.listCommands": {
|
|
76
|
+
callback: (): { [key: string]: CommandDef } => {
|
|
77
|
+
const commandHook = client.clientSystem.commandHook;
|
|
78
|
+
const allCommands: { [key: string]: CommandDef } = {};
|
|
79
|
+
for (const [cmd, def] of commandHook.buildAllCommands()) {
|
|
80
|
+
allCommands[cmd] = {
|
|
81
|
+
name: def.name,
|
|
82
|
+
contexts: def.contexts,
|
|
83
|
+
priority: def.priority,
|
|
84
|
+
key: def.key,
|
|
85
|
+
mac: def.mac,
|
|
86
|
+
hide: def.hide,
|
|
87
|
+
requireMode: def.requireMode,
|
|
88
|
+
requireEditor: def.requireEditor,
|
|
89
|
+
menu: def.menu,
|
|
90
|
+
menuMac: def.menuMac,
|
|
91
|
+
menuWindows: def.menuWindows,
|
|
92
|
+
menuLinux: def.menuLinux,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
return allCommands;
|
|
96
|
+
},
|
|
97
|
+
description:
|
|
98
|
+
"Returns a map of every currently available command definition.",
|
|
99
|
+
},
|
|
100
|
+
"system.listSyscalls": {
|
|
101
|
+
callback: (): SyscallMeta[] => {
|
|
102
|
+
const syscalls: SyscallMeta[] = [];
|
|
103
|
+
for (const [name, info] of client.clientSystem.system
|
|
104
|
+
.registeredSyscalls) {
|
|
105
|
+
const { callback, requiredPermissions, ...metadata } = info;
|
|
106
|
+
syscalls.push({
|
|
107
|
+
name,
|
|
108
|
+
requiredPermissions,
|
|
109
|
+
argCount: Math.max(0, callback.length - 1),
|
|
110
|
+
...metadata,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
return syscalls;
|
|
114
|
+
},
|
|
115
|
+
description:
|
|
116
|
+
"Lists registered syscalls with permissions, argument counts, and documentation metadata.",
|
|
117
|
+
},
|
|
118
|
+
"system.reloadPlugs": {
|
|
119
|
+
callback: () => {
|
|
120
|
+
if (!client) {
|
|
121
|
+
throw new Error("Not supported");
|
|
122
|
+
}
|
|
123
|
+
return client.loadPlugs();
|
|
124
|
+
},
|
|
125
|
+
description: "Reloads every plug available to the client.",
|
|
126
|
+
},
|
|
127
|
+
"system.reboot": {
|
|
128
|
+
callback: async () => {
|
|
129
|
+
await client.save(true);
|
|
130
|
+
await client.eventedSpacePrimitives.fetchFileListWhenIdle();
|
|
131
|
+
await client.mq.awaitEmptyQueue("indexQueue");
|
|
132
|
+
await client.clientSystem.reloadState();
|
|
133
|
+
},
|
|
134
|
+
description:
|
|
135
|
+
"Saves the current editor buffer, detects on-disk changes, waits for indexing, and reloads configuration, scripts, styles, and client state. Because the buffer is saved first, an external edit to the currently open page can be overwritten; edit that page through the editor or navigate away first.",
|
|
136
|
+
},
|
|
137
|
+
"system.loadPlug": {
|
|
138
|
+
callback: async (_ctx, path: string) => {
|
|
139
|
+
const meta = await client.space.spacePrimitives.getFileMeta(path);
|
|
140
|
+
await client.clientSystem.loadPlugFromPath(path, meta.lastModified);
|
|
141
|
+
await client.dispatchAppEvent("plugs:loaded");
|
|
142
|
+
},
|
|
143
|
+
description: "Loads or reloads one plug from a space file path.",
|
|
144
|
+
signatures: ["system.loadPlug(path)"],
|
|
145
|
+
},
|
|
146
|
+
"system.unloadPlug": {
|
|
147
|
+
callback: (_ctx, path: string) =>
|
|
148
|
+
client.clientSystem.system.unloadByPath(path),
|
|
149
|
+
description: "Unloads the plug loaded from a space file path.",
|
|
150
|
+
signatures: ["system.unloadPlug(path)"],
|
|
151
|
+
},
|
|
152
|
+
"system.reloadConfig": {
|
|
153
|
+
callback: (): Record<string, any> => {
|
|
154
|
+
console.warn("system.reloadConfig is deprecated, it's now a no-op");
|
|
155
|
+
return client.config.values;
|
|
156
|
+
},
|
|
157
|
+
description: "Deprecated no-op that returns the current configuration.",
|
|
158
|
+
deprecated:
|
|
159
|
+
"Configuration reloads automatically; use system.reboot when needed.",
|
|
160
|
+
},
|
|
161
|
+
"system.loadSpaceScripts": {
|
|
162
|
+
callback: async () => {
|
|
163
|
+
console.warn("DEPRECATED: used system.loadScripts instead");
|
|
164
|
+
await client.clientSystem.loadLuaScripts();
|
|
165
|
+
},
|
|
166
|
+
description: "Deprecated alias for system.loadScripts.",
|
|
167
|
+
deprecated: "Use system.loadScripts instead.",
|
|
168
|
+
},
|
|
169
|
+
"system.loadScripts": {
|
|
170
|
+
callback: async () => {
|
|
171
|
+
await client.clientSystem.loadLuaScripts();
|
|
172
|
+
},
|
|
173
|
+
description: "Reloads Space Lua scripts and configuration.",
|
|
174
|
+
},
|
|
175
|
+
"system.loadSpaceStyles": {
|
|
176
|
+
callback: async () => {
|
|
177
|
+
if (!client) {
|
|
178
|
+
throw new Error("Not supported on server");
|
|
179
|
+
}
|
|
180
|
+
await client.loadCustomStyles();
|
|
181
|
+
},
|
|
182
|
+
description: "Reloads custom Space Style definitions.",
|
|
183
|
+
},
|
|
184
|
+
"system.wipeClient": {
|
|
185
|
+
callback: async (_ctx, logout = false) => {
|
|
186
|
+
await client.wipeClient();
|
|
187
|
+
if (logout) {
|
|
188
|
+
location.href = ".logout";
|
|
189
|
+
} else {
|
|
190
|
+
alert("Client wiped, feel free to navigate elsewhere");
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
description:
|
|
194
|
+
"Wipes local client state, cached files, databases, and optionally the login session.",
|
|
195
|
+
signatures: ["system.wipeClient(logout?)"],
|
|
122
196
|
},
|
|
123
197
|
// DEPRECATED
|
|
124
|
-
"system.cleanDatabases":
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
198
|
+
"system.cleanDatabases": {
|
|
199
|
+
callback: (): boolean => {
|
|
200
|
+
console.warn(
|
|
201
|
+
"system.cleanDatabses is deprecated, use Client: Wipe instead",
|
|
202
|
+
);
|
|
203
|
+
return false;
|
|
204
|
+
},
|
|
205
|
+
description: "Deprecated no-op retained for compatibility.",
|
|
206
|
+
deprecated: "Use system.wipeClient or the Client: Wipe command instead.",
|
|
129
207
|
},
|
|
130
|
-
"system.getMode":
|
|
131
|
-
|
|
208
|
+
"system.getMode": {
|
|
209
|
+
callback: () => (readOnlyMode ? "ro" : "rw"),
|
|
210
|
+
description: "Returns rw for read-write mode or ro for read-only mode.",
|
|
132
211
|
},
|
|
133
|
-
"system.getURLPrefix":
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
212
|
+
"system.getURLPrefix": {
|
|
213
|
+
callback: () => {
|
|
214
|
+
const url = new URL(document.baseURI);
|
|
215
|
+
return url.pathname;
|
|
216
|
+
},
|
|
217
|
+
description:
|
|
218
|
+
"Returns the configured URL path prefix for this SilverBullet instance.",
|
|
137
219
|
},
|
|
138
|
-
"system.getBaseURI":
|
|
139
|
-
|
|
220
|
+
"system.getBaseURI": {
|
|
221
|
+
callback: () => document.baseURI,
|
|
222
|
+
description:
|
|
223
|
+
"Returns the browser base URI for this SilverBullet instance.",
|
|
140
224
|
},
|
|
141
|
-
"system.getVersion":
|
|
142
|
-
|
|
225
|
+
"system.getVersion": {
|
|
226
|
+
callback: () => publicVersion,
|
|
227
|
+
description: "Returns the running SilverBullet version.",
|
|
143
228
|
},
|
|
144
|
-
"system.getConfig":
|
|
145
|
-
|
|
229
|
+
"system.getConfig": {
|
|
230
|
+
callback: (_ctx, key: string, defaultValue: any = undefined) =>
|
|
231
|
+
client.config.get(key, defaultValue),
|
|
232
|
+
description: "Returns a configuration value, with an optional default.",
|
|
233
|
+
signatures: ["system.getConfig(key, defaultValue?)"],
|
|
146
234
|
},
|
|
147
235
|
// DEPRECATED
|
|
148
|
-
"system.getEnv":
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
236
|
+
"system.getEnv": {
|
|
237
|
+
callback: () => {
|
|
238
|
+
console.warn(
|
|
239
|
+
"system.getEnv is deprecated, you can assume the env to always be the client",
|
|
240
|
+
);
|
|
241
|
+
return null;
|
|
242
|
+
},
|
|
243
|
+
description: "Deprecated environment probe that always returns nil.",
|
|
244
|
+
deprecated: "The environment is always the client.",
|
|
245
|
+
},
|
|
246
|
+
"system.getSpaceConfig": {
|
|
247
|
+
callback: (_ctx, key, defaultValue?) => {
|
|
248
|
+
console.warn(
|
|
249
|
+
"system.getSpaceConfig is deprecated, use system.getConfig instead",
|
|
250
|
+
);
|
|
251
|
+
return client.config.get(key, defaultValue);
|
|
252
|
+
},
|
|
253
|
+
description: "Deprecated alias for system.getConfig.",
|
|
254
|
+
deprecated: "Use system.getConfig instead.",
|
|
255
|
+
signatures: ["system.getSpaceConfig(key, defaultValue?)"],
|
|
159
256
|
},
|
|
160
257
|
};
|
|
161
258
|
return api;
|
package/client/plugos/system.ts
CHANGED
|
@@ -7,9 +7,10 @@ import {
|
|
|
7
7
|
builtinPlugNames,
|
|
8
8
|
builtinPlugPaths,
|
|
9
9
|
} from "../../plugs/builtin_plugs.ts";
|
|
10
|
+
import type { LuaFunctionDocumentation } from "../../plug-api/types/index.ts";
|
|
10
11
|
|
|
11
12
|
export interface SysCallMapping {
|
|
12
|
-
[key: string]:
|
|
13
|
+
[key: string]: SyscallSignature | SyscallDefinition;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
export type SystemEvents<HookT> = {
|
|
@@ -26,10 +27,14 @@ export type SyscallContext = {
|
|
|
26
27
|
|
|
27
28
|
type SyscallSignature = (...args: any[]) => Promise<any> | any;
|
|
28
29
|
|
|
29
|
-
type
|
|
30
|
+
export type SyscallDefinition = {
|
|
31
|
+
callback: SyscallSignature;
|
|
32
|
+
} & LuaFunctionDocumentation;
|
|
33
|
+
|
|
34
|
+
type RegisteredSyscall = {
|
|
30
35
|
requiredPermissions: string[];
|
|
31
36
|
callback: SyscallSignature;
|
|
32
|
-
};
|
|
37
|
+
} & LuaFunctionDocumentation;
|
|
33
38
|
|
|
34
39
|
export type SystemOptions = {
|
|
35
40
|
manifestCache?: ManifestCache<any>;
|
|
@@ -37,7 +42,7 @@ export type SystemOptions = {
|
|
|
37
42
|
};
|
|
38
43
|
|
|
39
44
|
export class System<HookT> extends EventEmitter<SystemEvents<HookT>> {
|
|
40
|
-
registeredSyscalls = new Map<string,
|
|
45
|
+
registeredSyscalls = new Map<string, RegisteredSyscall>();
|
|
41
46
|
protected plugs = new Map<string, Plug<HookT>>();
|
|
42
47
|
protected enabledHooks = new Set<Hook<HookT>>();
|
|
43
48
|
|
|
@@ -68,10 +73,21 @@ export class System<HookT> extends EventEmitter<SystemEvents<HookT>> {
|
|
|
68
73
|
...registrationObjects: SysCallMapping[]
|
|
69
74
|
) {
|
|
70
75
|
for (const registrationObject of registrationObjects) {
|
|
71
|
-
for (const [name,
|
|
76
|
+
for (const [name, registration] of Object.entries(registrationObject)) {
|
|
77
|
+
const definition =
|
|
78
|
+
typeof registration === "function"
|
|
79
|
+
? { callback: registration }
|
|
80
|
+
: registration;
|
|
81
|
+
const cleanName = name.startsWith("lua:")
|
|
82
|
+
? name.slice("lua:".length)
|
|
83
|
+
: name;
|
|
84
|
+
const namespace = cleanName.split(".")[0];
|
|
85
|
+
const { callback, ...metadata } = definition;
|
|
72
86
|
this.registeredSyscalls.set(name, {
|
|
73
87
|
requiredPermissions: requiredCapabilities,
|
|
74
88
|
callback,
|
|
89
|
+
see: `API/${namespace}`,
|
|
90
|
+
...metadata,
|
|
75
91
|
});
|
|
76
92
|
}
|
|
77
93
|
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
LuaFunctionExampleDocumentation,
|
|
3
|
+
LuaFunctionInfo,
|
|
4
|
+
LuaFunctionParameterDocumentation,
|
|
5
|
+
LuaFunctionReturnDocumentation,
|
|
6
|
+
} from "../../plug-api/types/index.ts";
|
|
7
|
+
|
|
8
|
+
function compareNames(a: LuaFunctionInfo, b: LuaFunctionInfo): number {
|
|
9
|
+
const aName = a.name ?? "";
|
|
10
|
+
const bName = b.name ?? "";
|
|
11
|
+
return aName < bName ? -1 : aName > bName ? 1 : 0;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function signature(info: LuaFunctionInfo): string {
|
|
15
|
+
const parameters = (info.parameters ?? [])
|
|
16
|
+
.map((parameter) => `${parameter.name}${parameter.optional ? "?" : ""}`)
|
|
17
|
+
.join(", ");
|
|
18
|
+
return `${info.name ?? "<anonymous>"}(${parameters})`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function renderParameter(parameter: LuaFunctionParameterDocumentation): string {
|
|
22
|
+
const type = parameter.type ? ` (\`${parameter.type}\`)` : "";
|
|
23
|
+
const description = parameter.description
|
|
24
|
+
? ` — ${parameter.description}`
|
|
25
|
+
: "";
|
|
26
|
+
return `- \`${parameter.name}${parameter.optional ? "?" : ""}\`${type}${description}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function renderReturn(value: LuaFunctionReturnDocumentation): string {
|
|
30
|
+
const type = value.type ? `\`${value.type}\`` : "Value";
|
|
31
|
+
return `- ${type}${value.description ? ` — ${value.description}` : ""}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function codeFence(example: LuaFunctionExampleDocumentation): string[] {
|
|
35
|
+
const longestFence = Math.max(
|
|
36
|
+
2,
|
|
37
|
+
...[...example.code.matchAll(/`+/g)].map((match) => match[0].length),
|
|
38
|
+
);
|
|
39
|
+
const fence = "`".repeat(longestFence + 1);
|
|
40
|
+
return [`${fence}${example.language ?? "lua"}`, example.code, fence];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function renderFunction(
|
|
44
|
+
info: LuaFunctionInfo,
|
|
45
|
+
apiNamespace?: string,
|
|
46
|
+
): string[] {
|
|
47
|
+
const lines = [`## ${info.name ?? "<anonymous>"}`, ""];
|
|
48
|
+
const signatures = info.signatures?.length
|
|
49
|
+
? info.signatures
|
|
50
|
+
: [signature(info)];
|
|
51
|
+
for (const value of signatures) lines.push(`\`${value}\``);
|
|
52
|
+
|
|
53
|
+
if (info.deprecated) {
|
|
54
|
+
lines.push(
|
|
55
|
+
"",
|
|
56
|
+
`> **Deprecated:** ${
|
|
57
|
+
typeof info.deprecated === "string"
|
|
58
|
+
? info.deprecated
|
|
59
|
+
: "This function is deprecated."
|
|
60
|
+
}`,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
if (info.description) lines.push("", info.description);
|
|
64
|
+
if (info.parameters?.length) {
|
|
65
|
+
lines.push("", "**Parameters:**", "");
|
|
66
|
+
lines.push(...info.parameters.map(renderParameter));
|
|
67
|
+
}
|
|
68
|
+
if (info.returns?.length) {
|
|
69
|
+
lines.push("", "**Returns:**", "");
|
|
70
|
+
lines.push(...info.returns.map(renderReturn));
|
|
71
|
+
}
|
|
72
|
+
if (info.examples?.length) {
|
|
73
|
+
lines.push(
|
|
74
|
+
"",
|
|
75
|
+
info.examples.length === 1 ? "**Example:**" : "**Examples:**",
|
|
76
|
+
);
|
|
77
|
+
for (const example of info.examples) {
|
|
78
|
+
if (example.description) lines.push("", example.description);
|
|
79
|
+
lines.push("", ...codeFence(example));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const namespacePage = apiNamespace ? `API/${apiNamespace}` : undefined;
|
|
83
|
+
if (info.see && info.see !== namespacePage) {
|
|
84
|
+
lines.push("", `**See:** [[${info.see}]]`);
|
|
85
|
+
}
|
|
86
|
+
return lines;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Render function metadata as deterministic Markdown for live `${...}` directives. */
|
|
90
|
+
export function renderApiDocumentationMarkdown(
|
|
91
|
+
functions: LuaFunctionInfo[],
|
|
92
|
+
context?: string,
|
|
93
|
+
): string {
|
|
94
|
+
const documented = functions.filter((info) => info.name).sort(compareNames);
|
|
95
|
+
if (documented.length === 0) {
|
|
96
|
+
return context
|
|
97
|
+
? `_No documented API functions found for \`${context}\`._`
|
|
98
|
+
: "_No functions found._";
|
|
99
|
+
}
|
|
100
|
+
return documented
|
|
101
|
+
.flatMap((info, index) => [
|
|
102
|
+
...(index > 0 ? [""] : []),
|
|
103
|
+
...renderFunction(info, context),
|
|
104
|
+
])
|
|
105
|
+
.join("\n")
|
|
106
|
+
.trim();
|
|
107
|
+
}
|
package/client/space_lua/ast.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { LuaFunctionDocumentation } from "../../plug-api/types/index.ts";
|
|
2
|
+
|
|
1
3
|
type ASTContext = {
|
|
2
4
|
ctx: ASTCtx;
|
|
3
5
|
};
|
|
@@ -12,6 +14,8 @@ export type ASTCtx = {
|
|
|
12
14
|
export type LuaBlock = {
|
|
13
15
|
type: "Block";
|
|
14
16
|
statements: LuaStatement[];
|
|
17
|
+
// Comments lexically owned by this block, in source order.
|
|
18
|
+
comments?: LuaComment[];
|
|
15
19
|
hasLabel?: boolean;
|
|
16
20
|
hasGoto?: boolean;
|
|
17
21
|
// the first duplicated label is recorded here for later eval
|
|
@@ -26,6 +30,14 @@ export type LuaBlock = {
|
|
|
26
30
|
hasFunctionDef?: boolean;
|
|
27
31
|
} & ASTContext;
|
|
28
32
|
|
|
33
|
+
export type LuaComment = {
|
|
34
|
+
type: "Comment";
|
|
35
|
+
/** Exact source text, including delimiters. */
|
|
36
|
+
text: string;
|
|
37
|
+
kind: "line" | "long";
|
|
38
|
+
ctx: ASTCtx;
|
|
39
|
+
};
|
|
40
|
+
|
|
29
41
|
// STATEMENTS
|
|
30
42
|
export type LuaReturnStatement = {
|
|
31
43
|
type: "Return";
|
|
@@ -128,6 +140,7 @@ export type LuaFunctionBody = {
|
|
|
128
140
|
type: "FunctionBody";
|
|
129
141
|
parameters: string[];
|
|
130
142
|
block: LuaBlock;
|
|
143
|
+
documentation?: LuaFunctionDocumentation;
|
|
131
144
|
} & ASTContext;
|
|
132
145
|
|
|
133
146
|
export type LuaAssignmentStatement = {
|
package/client/space_lua/eval.ts
CHANGED
|
@@ -1015,7 +1015,8 @@ export function evalExpression(
|
|
|
1015
1015
|
case "Variable":
|
|
1016
1016
|
case "FunctionCall":
|
|
1017
1017
|
case "TableAccess":
|
|
1018
|
-
case "PropertyAccess":
|
|
1018
|
+
case "PropertyAccess":
|
|
1019
|
+
case "Parenthesized": {
|
|
1019
1020
|
return evalPrefixExpression(e, env, sf);
|
|
1020
1021
|
}
|
|
1021
1022
|
case "TableConstructor": {
|
|
@@ -1318,7 +1319,7 @@ function evalPrefixExpression(
|
|
|
1318
1319
|
|
|
1319
1320
|
case "Parenthesized": {
|
|
1320
1321
|
const p = asParenthesized(e);
|
|
1321
|
-
return evalExpression(p.expression, env, sf);
|
|
1322
|
+
return rpThen(evalExpression(p.expression, env, sf), singleResult);
|
|
1322
1323
|
}
|
|
1323
1324
|
|
|
1324
1325
|
// <<expr>>[<<expr>>]
|
|
@@ -2063,6 +2064,12 @@ export function evalStatement(
|
|
|
2063
2064
|
let errSf: LuaStackFrame | undefined;
|
|
2064
2065
|
const ps: Promise<any>[] = [];
|
|
2065
2066
|
for (let i = 0; i < lvalues.length; i++) {
|
|
2067
|
+
if (
|
|
2068
|
+
values[i] instanceof LuaFunction &&
|
|
2069
|
+
typeof lvalues[i].key === "string"
|
|
2070
|
+
) {
|
|
2071
|
+
values[i].info.name ??= lvalues[i].key;
|
|
2072
|
+
}
|
|
2066
2073
|
const r = luaSet(
|
|
2067
2074
|
lvalues[i].env,
|
|
2068
2075
|
lvalues[i].key,
|
|
@@ -2115,6 +2122,7 @@ export function evalStatement(
|
|
|
2115
2122
|
}
|
|
2116
2123
|
|
|
2117
2124
|
const bindOne = (name: any, v: LuaValue) => {
|
|
2125
|
+
if (v instanceof LuaFunction) v.info.name ??= name.name;
|
|
2118
2126
|
const isConst = name.attributes?.includes(LuaAttribute.Const) === true;
|
|
2119
2127
|
const isClose = name.attributes?.includes(LuaAttribute.Close) === true;
|
|
2120
2128
|
|
|
@@ -2386,13 +2394,13 @@ export function evalStatement(
|
|
|
2386
2394
|
}
|
|
2387
2395
|
(settable as any).set(
|
|
2388
2396
|
propNames[propNames.length - 1],
|
|
2389
|
-
new LuaFunction(body, env),
|
|
2397
|
+
new LuaFunction(body, env, { name: propNames.join(".") }),
|
|
2390
2398
|
);
|
|
2391
2399
|
return;
|
|
2392
2400
|
}
|
|
2393
2401
|
case "LocalFunction": {
|
|
2394
2402
|
const lf = asLocalFunction(s);
|
|
2395
|
-
env.setLocal(lf.name, new LuaFunction(lf.body, env));
|
|
2403
|
+
env.setLocal(lf.name, new LuaFunction(lf.body, env, { name: lf.name }));
|
|
2396
2404
|
return;
|
|
2397
2405
|
}
|
|
2398
2406
|
case "Return": {
|