@silverbulletmd/silverbullet 2.8.1 → 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 +42 -25
- 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 -700
- package/client/plugos/syscalls/event.ts +46 -13
- package/client/plugos/syscalls/fetch.ts +123 -82
- package/client/plugos/syscalls/index.ts +243 -114
- 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 +254 -119
- package/client/space_lua/pretty_print.ts +465 -0
- package/client/space_lua/query_collection.ts +3 -3
- 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 +26 -17
- 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 +397 -13
- 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 +336 -94
- package/client/space_lua/syscalls.ts +270 -0
- package/dist/plug-compile.js +1 -1
- package/package.json +12 -4
- 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 +23 -2
- package/plug-api/syscalls/index.ts +24 -1
- package/plug-api/syscalls/jsonschema.ts +9 -0
- package/plug-api/syscalls/lua.ts +28 -1
- 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/config.ts +1 -1
- package/plug-api/types/index.ts +52 -1
- package/plug-api/types/manifest.ts +22 -4
- package/plug-api/ui/cx.ts +4 -0
- package/plug-api/ui/index.ts +22 -0
- package/plug-api/ui/panel_styles.ts +33 -0
- package/plug-api/ui/slugify.ts +44 -0
- package/plugs/builtin_plugs.ts +1 -0
- package/client/plugos/syscalls/lua.ts +0 -58
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { lezerToParseTree } from "../../client/markdown_parser/parse_tree.ts";
|
|
2
2
|
import type { SyntaxNode } from "@lezer/common";
|
|
3
3
|
import {
|
|
4
|
-
cleanTree,
|
|
5
4
|
type ParseTree,
|
|
5
|
+
renderToText,
|
|
6
6
|
} from "@silverbulletmd/silverbullet/lib/tree";
|
|
7
7
|
// @ts-expect-error - Local generated JavaScript file without type definitions
|
|
8
8
|
import { parser } from "./parse-lua.js";
|
|
@@ -12,6 +12,7 @@ import type {
|
|
|
12
12
|
ASTCtx,
|
|
13
13
|
LuaAttName,
|
|
14
14
|
LuaBlock,
|
|
15
|
+
LuaComment,
|
|
15
16
|
LuaExpression,
|
|
16
17
|
LuaFunctionBody,
|
|
17
18
|
LuaFunctionCallExpression,
|
|
@@ -25,6 +26,7 @@ import type {
|
|
|
25
26
|
LuaStatement,
|
|
26
27
|
LuaTableField,
|
|
27
28
|
} from "./ast.ts";
|
|
29
|
+
import type { LuaFunctionDocumentation } from "../../plug-api/types/index.ts";
|
|
28
30
|
import { LuaAttribute } from "./ast.ts";
|
|
29
31
|
import { getBlockGotoMeta } from "./labels.ts";
|
|
30
32
|
import { LuaRuntimeError, LuaStackFrame } from "./runtime.ts";
|
|
@@ -67,7 +69,217 @@ function parseChunk(t: ParseTree, ctx: ASTCtx): LuaBlock {
|
|
|
67
69
|
if (t.type !== "Chunk") {
|
|
68
70
|
throw new Error(`Expected Chunk, got ${t.type}`);
|
|
69
71
|
}
|
|
70
|
-
|
|
72
|
+
const block = t.children?.find((child) => child.type === "Block");
|
|
73
|
+
if (!block) {
|
|
74
|
+
return { type: "Block", statements: [], ctx: context(t, ctx) };
|
|
75
|
+
}
|
|
76
|
+
return parseBlockNode(block, ctx);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function collectLuaComments(t: ParseTree, ctx: ASTCtx): LuaComment[] {
|
|
80
|
+
const comments: LuaComment[] = [];
|
|
81
|
+
const visit = (node: ParseTree) => {
|
|
82
|
+
if (node.type === "Comment") {
|
|
83
|
+
const text = renderToText(node);
|
|
84
|
+
comments.push({
|
|
85
|
+
type: "Comment",
|
|
86
|
+
text,
|
|
87
|
+
kind: /^--\[(=*)\[/.test(text) ? "long" : "line",
|
|
88
|
+
ctx: context(node, ctx),
|
|
89
|
+
});
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
for (const child of node.children ?? []) visit(child);
|
|
93
|
+
};
|
|
94
|
+
visit(t);
|
|
95
|
+
return comments.sort((a, b) => (a.ctx.from ?? 0) - (b.ctx.from ?? 0));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function withoutComments(t: ParseTree): ParseTree {
|
|
99
|
+
if (!t.children) return t;
|
|
100
|
+
return {
|
|
101
|
+
...t,
|
|
102
|
+
children: t.children
|
|
103
|
+
.filter((child) => child.type !== "Comment")
|
|
104
|
+
.map(withoutComments),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function assignCommentsToBlocks(
|
|
109
|
+
root: LuaBlock,
|
|
110
|
+
comments: LuaComment[],
|
|
111
|
+
source: string,
|
|
112
|
+
): void {
|
|
113
|
+
const blocks: LuaBlock[] = [];
|
|
114
|
+
const seen = new WeakSet<object>();
|
|
115
|
+
const visit = (value: unknown, key?: string) => {
|
|
116
|
+
if (!value || typeof value !== "object" || key === "ctx") return;
|
|
117
|
+
if (seen.has(value)) return;
|
|
118
|
+
seen.add(value);
|
|
119
|
+
if ((value as { type?: string }).type === "Block") {
|
|
120
|
+
blocks.push(value as LuaBlock);
|
|
121
|
+
}
|
|
122
|
+
for (const [childKey, child] of Object.entries(value)) {
|
|
123
|
+
if (
|
|
124
|
+
childKey !== "ctx" &&
|
|
125
|
+
childKey !== "comments" &&
|
|
126
|
+
childKey !== "documentation"
|
|
127
|
+
) {
|
|
128
|
+
visit(child, childKey);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
visit(root);
|
|
133
|
+
|
|
134
|
+
for (const comment of comments) {
|
|
135
|
+
const from = comment.ctx.from ?? 0;
|
|
136
|
+
const to = comment.ctx.to ?? from;
|
|
137
|
+
let owner = root;
|
|
138
|
+
let ownerSpan = Number.POSITIVE_INFINITY;
|
|
139
|
+
for (const block of blocks) {
|
|
140
|
+
if (block === root) continue;
|
|
141
|
+
const blockFrom = block.ctx.from;
|
|
142
|
+
const blockTo = block.ctx.to;
|
|
143
|
+
if (blockFrom === undefined || blockTo === undefined) continue;
|
|
144
|
+
const contains = blockFrom <= from && blockTo >= to;
|
|
145
|
+
const immediatelyBefore =
|
|
146
|
+
to <= blockFrom && !source.slice(to, blockFrom).trim();
|
|
147
|
+
const immediatelyAfter =
|
|
148
|
+
blockTo <= from && !source.slice(blockTo, from).trim();
|
|
149
|
+
if (
|
|
150
|
+
(contains || immediatelyBefore || immediatelyAfter) &&
|
|
151
|
+
blockTo - blockFrom < ownerSpan
|
|
152
|
+
) {
|
|
153
|
+
owner = block;
|
|
154
|
+
ownerSpan = blockTo - blockFrom;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
(owner.comments ??= []).push(comment);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function parseFunctionDocumentation(
|
|
162
|
+
comments: LuaComment[],
|
|
163
|
+
): LuaFunctionDocumentation | undefined {
|
|
164
|
+
const description: string[] = [];
|
|
165
|
+
const parameters: NonNullable<LuaFunctionDocumentation["parameters"]> = [];
|
|
166
|
+
const returns: NonNullable<LuaFunctionDocumentation["returns"]> = [];
|
|
167
|
+
let deprecated: string | boolean | undefined;
|
|
168
|
+
let see: string | undefined;
|
|
169
|
+
|
|
170
|
+
for (const comment of comments) {
|
|
171
|
+
const line = comment.text.slice(3).replace(/^\s?/, "");
|
|
172
|
+
const param = /^@param\s+(\S+)\s+(\S+)(?:\s+(.*))?$/.exec(line);
|
|
173
|
+
if (param) {
|
|
174
|
+
const optional = param[1].endsWith("?");
|
|
175
|
+
parameters.push({
|
|
176
|
+
name: optional ? param[1].slice(0, -1) : param[1],
|
|
177
|
+
type: param[2],
|
|
178
|
+
description: param[3] || undefined,
|
|
179
|
+
optional,
|
|
180
|
+
});
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const returnDoc = /^@return\s+(\S+)(?:\s+(.*))?$/.exec(line);
|
|
184
|
+
if (returnDoc) {
|
|
185
|
+
returns.push({
|
|
186
|
+
type: returnDoc[1],
|
|
187
|
+
description: returnDoc[2] || undefined,
|
|
188
|
+
});
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
const deprecatedDoc = /^@deprecated(?:\s+(.*))?$/.exec(line);
|
|
192
|
+
if (deprecatedDoc) {
|
|
193
|
+
deprecated = deprecatedDoc[1] || true;
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
const seeDoc = /^@see\s+(.+)$/.exec(line);
|
|
197
|
+
if (seeDoc) {
|
|
198
|
+
see = seeDoc[1];
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (!line.startsWith("@")) description.push(line);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const docs: LuaFunctionDocumentation = {};
|
|
205
|
+
const descriptionText = description.join("\n").trim();
|
|
206
|
+
if (descriptionText) docs.description = descriptionText;
|
|
207
|
+
if (parameters.length) docs.parameters = parameters;
|
|
208
|
+
if (returns.length) docs.returns = returns;
|
|
209
|
+
if (deprecated !== undefined) docs.deprecated = deprecated;
|
|
210
|
+
if (see) docs.see = see;
|
|
211
|
+
return Object.keys(docs).length ? docs : undefined;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function attachFunctionDocumentation(
|
|
215
|
+
root: LuaBlock,
|
|
216
|
+
comments: LuaComment[],
|
|
217
|
+
source: string,
|
|
218
|
+
): void {
|
|
219
|
+
const candidates: { from: number; body: LuaFunctionBody }[] = [];
|
|
220
|
+
const seen = new WeakSet<object>();
|
|
221
|
+
const claimedBodies = new WeakSet<LuaFunctionBody>();
|
|
222
|
+
|
|
223
|
+
const addCandidate = (from: number | undefined, body: LuaFunctionBody) => {
|
|
224
|
+
if (from === undefined || claimedBodies.has(body)) return;
|
|
225
|
+
claimedBodies.add(body);
|
|
226
|
+
candidates.push({ from, body });
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const visit = (value: unknown, key?: string) => {
|
|
230
|
+
if (!value || typeof value !== "object" || key === "ctx") return;
|
|
231
|
+
if (seen.has(value)) return;
|
|
232
|
+
seen.add(value);
|
|
233
|
+
const node = value as Record<string, any>;
|
|
234
|
+
if (node.type === "Function" || node.type === "LocalFunction") {
|
|
235
|
+
addCandidate(node.ctx?.from, node.body);
|
|
236
|
+
} else if (
|
|
237
|
+
(node.type === "Local" || node.type === "Assignment") &&
|
|
238
|
+
node.expressions?.length === 1 &&
|
|
239
|
+
node.expressions[0]?.type === "FunctionDefinition"
|
|
240
|
+
) {
|
|
241
|
+
addCandidate(node.ctx?.from, node.expressions[0].body);
|
|
242
|
+
} else if (node.type === "FunctionDefinition") {
|
|
243
|
+
addCandidate(node.ctx?.from, node.body);
|
|
244
|
+
}
|
|
245
|
+
for (const [childKey, child] of Object.entries(node)) {
|
|
246
|
+
if (
|
|
247
|
+
childKey !== "ctx" &&
|
|
248
|
+
childKey !== "comments" &&
|
|
249
|
+
childKey !== "documentation"
|
|
250
|
+
) {
|
|
251
|
+
visit(child, childKey);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
visit(root);
|
|
256
|
+
|
|
257
|
+
const docComments = comments.filter(
|
|
258
|
+
(comment) => comment.kind === "line" && comment.text.startsWith("---"),
|
|
259
|
+
);
|
|
260
|
+
for (const candidate of candidates) {
|
|
261
|
+
const group: LuaComment[] = [];
|
|
262
|
+
let boundary = candidate.from;
|
|
263
|
+
for (let i = docComments.length - 1; i >= 0; i--) {
|
|
264
|
+
const comment = docComments[i];
|
|
265
|
+
const from = comment.ctx.from ?? 0;
|
|
266
|
+
const to = comment.ctx.to ?? from;
|
|
267
|
+
if (to > boundary) continue;
|
|
268
|
+
const gap = source.slice(to, boundary);
|
|
269
|
+
const lineStart = source.lastIndexOf("\n", from - 1) + 1;
|
|
270
|
+
if (
|
|
271
|
+
gap.trim() ||
|
|
272
|
+
/\r?\n[ \t]*\r?\n/.test(gap) ||
|
|
273
|
+
source.slice(lineStart, from).trim()
|
|
274
|
+
) {
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
group.unshift(comment);
|
|
278
|
+
boundary = from;
|
|
279
|
+
}
|
|
280
|
+
const documentation = parseFunctionDocumentation(group);
|
|
281
|
+
if (documentation) candidate.body.documentation = documentation;
|
|
282
|
+
}
|
|
71
283
|
}
|
|
72
284
|
|
|
73
285
|
function hasCloseLocal(names: LuaAttName[] | undefined): boolean {
|
|
@@ -636,7 +848,7 @@ function exprCapturesNames(e: LuaExpression, names: Set<string>): boolean {
|
|
|
636
848
|
}
|
|
637
849
|
}
|
|
638
850
|
|
|
639
|
-
function
|
|
851
|
+
function parseBlockNode(t: ParseTree, ctx: ASTCtx): LuaBlock {
|
|
640
852
|
if (t.type !== "Block") {
|
|
641
853
|
throw new Error(`Expected Block, got ${t.type}`);
|
|
642
854
|
}
|
|
@@ -846,20 +1058,20 @@ function parseStatement(t: ParseTree, ctx: ASTCtx): LuaStatement {
|
|
|
846
1058
|
ctx: context(t, ctx),
|
|
847
1059
|
};
|
|
848
1060
|
case "Scope":
|
|
849
|
-
return
|
|
1061
|
+
return parseBlockNode(t.children![1], ctx);
|
|
850
1062
|
case ";":
|
|
851
1063
|
return { type: "Semicolon", ctx: context(t, ctx) };
|
|
852
1064
|
case "WhileStatement":
|
|
853
1065
|
return {
|
|
854
1066
|
type: "While",
|
|
855
1067
|
condition: parseExpression(t.children![1], ctx),
|
|
856
|
-
block:
|
|
1068
|
+
block: parseBlockNode(t.children![3], ctx),
|
|
857
1069
|
ctx: context(t, ctx),
|
|
858
1070
|
};
|
|
859
1071
|
case "RepeatStatement":
|
|
860
1072
|
return {
|
|
861
1073
|
type: "Repeat",
|
|
862
|
-
block:
|
|
1074
|
+
block: parseBlockNode(t.children![1], ctx),
|
|
863
1075
|
condition: parseExpression(t.children![3], ctx),
|
|
864
1076
|
ctx: context(t, ctx),
|
|
865
1077
|
};
|
|
@@ -880,12 +1092,12 @@ function parseStatement(t: ParseTree, ctx: ASTCtx): LuaStatement {
|
|
|
880
1092
|
if (token === "if" || token === "elseif") {
|
|
881
1093
|
conditions.push({
|
|
882
1094
|
condition: parseExpression(t.children![i + 1], ctx),
|
|
883
|
-
block:
|
|
1095
|
+
block: parseBlockNode(t.children![i + 3], ctx),
|
|
884
1096
|
from: child.from,
|
|
885
1097
|
to: child.to,
|
|
886
1098
|
});
|
|
887
1099
|
} else if (token === "else") {
|
|
888
|
-
elseBlock =
|
|
1100
|
+
elseBlock = parseBlockNode(t.children![i + 1], ctx);
|
|
889
1101
|
} else if (token === "end") {
|
|
890
1102
|
break;
|
|
891
1103
|
} else {
|
|
@@ -903,7 +1115,7 @@ function parseStatement(t: ParseTree, ctx: ASTCtx): LuaStatement {
|
|
|
903
1115
|
if (t.children![1].type === "ForNumeric") {
|
|
904
1116
|
const forNumeric = t.children![1];
|
|
905
1117
|
const name = forNumeric.children![0].children![0].text!;
|
|
906
|
-
const block =
|
|
1118
|
+
const block = parseBlockNode(t.children![3], ctx);
|
|
907
1119
|
const node: LuaStatement = {
|
|
908
1120
|
type: "For",
|
|
909
1121
|
name,
|
|
@@ -923,7 +1135,7 @@ function parseStatement(t: ParseTree, ctx: ASTCtx): LuaStatement {
|
|
|
923
1135
|
}
|
|
924
1136
|
const forGeneric = t.children![1];
|
|
925
1137
|
const names = parseNameList(forGeneric.children![0]);
|
|
926
|
-
const block =
|
|
1138
|
+
const block = parseBlockNode(t.children![3], ctx);
|
|
927
1139
|
const node: LuaStatement = {
|
|
928
1140
|
type: "ForIn",
|
|
929
1141
|
names,
|
|
@@ -1269,7 +1481,11 @@ function parseExpression(t: ParseTree, ctx: ASTCtx): LuaExpression {
|
|
|
1269
1481
|
};
|
|
1270
1482
|
|
|
1271
1483
|
case "Parens":
|
|
1272
|
-
return
|
|
1484
|
+
return {
|
|
1485
|
+
type: "Parenthesized",
|
|
1486
|
+
expression: parseExpression(t.children![1], ctx),
|
|
1487
|
+
ctx: context(t, ctx),
|
|
1488
|
+
};
|
|
1273
1489
|
case "FunctionCall": {
|
|
1274
1490
|
return parseFunctionCall(t, ctx);
|
|
1275
1491
|
}
|
|
@@ -1512,7 +1728,7 @@ function parseFunctionBody(t: ParseTree, ctx: ASTCtx): LuaFunctionBody {
|
|
|
1512
1728
|
(c) => c.type && ["Name", "Ellipsis"].includes(c.type),
|
|
1513
1729
|
)
|
|
1514
1730
|
.map((c) => c.children![0].text!),
|
|
1515
|
-
block:
|
|
1731
|
+
block: parseBlockNode(t.children![3], ctx),
|
|
1516
1732
|
ctx: context(t, ctx),
|
|
1517
1733
|
};
|
|
1518
1734
|
}
|
|
@@ -1585,112 +1801,13 @@ function parseTableField(t: ParseTree, ctx: ASTCtx): LuaTableField {
|
|
|
1585
1801
|
}
|
|
1586
1802
|
}
|
|
1587
1803
|
|
|
1588
|
-
export function
|
|
1589
|
-
let result = "";
|
|
1590
|
-
let i = 0;
|
|
1591
|
-
|
|
1592
|
-
while (i < s.length) {
|
|
1593
|
-
// Check for long string
|
|
1594
|
-
if (s[i] === "[") {
|
|
1595
|
-
let j = i + 1;
|
|
1596
|
-
let equalsCount = 0;
|
|
1597
|
-
while (s[j] === "=") {
|
|
1598
|
-
equalsCount++;
|
|
1599
|
-
j++;
|
|
1600
|
-
}
|
|
1601
|
-
if (s[j] === "[") {
|
|
1602
|
-
// Found long string start
|
|
1603
|
-
const openBracket = s.substring(i, j + 1);
|
|
1604
|
-
const closeBracket = `]${"=".repeat(equalsCount)}]`;
|
|
1605
|
-
result += openBracket;
|
|
1606
|
-
i = j + 1;
|
|
1607
|
-
|
|
1608
|
-
// Find matching closing bracket
|
|
1609
|
-
const content = s.substring(i);
|
|
1610
|
-
const closeIndex = content.indexOf(closeBracket);
|
|
1611
|
-
if (closeIndex !== -1) {
|
|
1612
|
-
// Copy string content verbatim, including any comment-like sequences
|
|
1613
|
-
result += content.substring(0, closeIndex) + closeBracket;
|
|
1614
|
-
i += closeIndex + closeBracket.length;
|
|
1615
|
-
continue;
|
|
1616
|
-
}
|
|
1617
|
-
}
|
|
1618
|
-
}
|
|
1619
|
-
|
|
1620
|
-
// Check for single quoted string
|
|
1621
|
-
if (s[i] === '"' || s[i] === "'") {
|
|
1622
|
-
const quote = s[i];
|
|
1623
|
-
result += quote;
|
|
1624
|
-
i++;
|
|
1625
|
-
while (i < s.length && s[i] !== quote) {
|
|
1626
|
-
if (s[i] === "\\") {
|
|
1627
|
-
result += s[i] + s[i + 1];
|
|
1628
|
-
i += 2;
|
|
1629
|
-
} else {
|
|
1630
|
-
result += s[i];
|
|
1631
|
-
i++;
|
|
1632
|
-
}
|
|
1633
|
-
}
|
|
1634
|
-
if (i < s.length) {
|
|
1635
|
-
result += s[i]; // closing quote
|
|
1636
|
-
i++;
|
|
1637
|
-
}
|
|
1638
|
-
continue;
|
|
1639
|
-
}
|
|
1640
|
-
|
|
1641
|
-
// Check for comments
|
|
1642
|
-
if (s[i] === "-" && s[i + 1] === "-") {
|
|
1643
|
-
// Replace the -- with spaces
|
|
1644
|
-
result += " ";
|
|
1645
|
-
i += 2;
|
|
1646
|
-
|
|
1647
|
-
// Check for long comment
|
|
1648
|
-
if (s[i] === "[") {
|
|
1649
|
-
let j = i + 1;
|
|
1650
|
-
let equalsCount = 0;
|
|
1651
|
-
while (s[j] === "=") {
|
|
1652
|
-
equalsCount++;
|
|
1653
|
-
j++;
|
|
1654
|
-
}
|
|
1655
|
-
if (s[j] === "[") {
|
|
1656
|
-
// Found long comment start
|
|
1657
|
-
const closeBracket = `]${"=".repeat(equalsCount)}]`;
|
|
1658
|
-
// Replace opening bracket with spaces
|
|
1659
|
-
result += " ".repeat(j - i + 1);
|
|
1660
|
-
i = j + 1;
|
|
1661
|
-
|
|
1662
|
-
// Find matching closing bracket
|
|
1663
|
-
const content = s.substring(i);
|
|
1664
|
-
const closeIndex = content.indexOf(closeBracket);
|
|
1665
|
-
if (closeIndex !== -1) {
|
|
1666
|
-
// Replace comment content and closing bracket with spaces
|
|
1667
|
-
result += " ".repeat(closeIndex) + " ".repeat(closeBracket.length);
|
|
1668
|
-
i += closeIndex + closeBracket.length;
|
|
1669
|
-
continue;
|
|
1670
|
-
}
|
|
1671
|
-
}
|
|
1672
|
-
}
|
|
1673
|
-
|
|
1674
|
-
// Single line comment - replace rest of line with spaces
|
|
1675
|
-
while (i < s.length && s[i] !== "\n") {
|
|
1676
|
-
result += " ";
|
|
1677
|
-
i++;
|
|
1678
|
-
}
|
|
1679
|
-
continue;
|
|
1680
|
-
}
|
|
1681
|
-
|
|
1682
|
-
result += s[i];
|
|
1683
|
-
i++;
|
|
1684
|
-
}
|
|
1685
|
-
|
|
1686
|
-
return result;
|
|
1687
|
-
}
|
|
1688
|
-
|
|
1689
|
-
export function parse(s: string, ctx: ASTCtx = {}): LuaBlock {
|
|
1804
|
+
export function parseBlock(s: string, ctx: ASTCtx = {}): LuaBlock {
|
|
1690
1805
|
try {
|
|
1691
|
-
const
|
|
1692
|
-
|
|
1693
|
-
const result = parseChunk(
|
|
1806
|
+
const concreteTree = parseToAST(s);
|
|
1807
|
+
const comments = collectLuaComments(concreteTree, ctx);
|
|
1808
|
+
const result = parseChunk(withoutComments(concreteTree), ctx);
|
|
1809
|
+
assignCommentsToBlocks(result, comments, s);
|
|
1810
|
+
attachFunctionDocumentation(result, comments, s);
|
|
1694
1811
|
// console.log("Parsed AST", JSON.stringify(result, null, 2));
|
|
1695
1812
|
getBlockGotoMeta(result);
|
|
1696
1813
|
return result;
|
|
@@ -1716,7 +1833,25 @@ export function parseToAST(t: string): ParseTree {
|
|
|
1716
1833
|
}
|
|
1717
1834
|
|
|
1718
1835
|
const n = lezerToParseTree(t, tree.topNode);
|
|
1719
|
-
return
|
|
1836
|
+
return cleanLuaTree(n);
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
function cleanLuaTree(tree: ParseTree): ParseTree {
|
|
1840
|
+
if (tree.type === "⚠") {
|
|
1841
|
+
throw new Error(`Parse error at pos ${tree.from}`);
|
|
1842
|
+
}
|
|
1843
|
+
if (tree.text !== undefined) return tree;
|
|
1844
|
+
const result: ParseTree = {
|
|
1845
|
+
type: tree.type,
|
|
1846
|
+
children: [],
|
|
1847
|
+
from: tree.from,
|
|
1848
|
+
to: tree.to,
|
|
1849
|
+
};
|
|
1850
|
+
for (const node of tree.children ?? []) {
|
|
1851
|
+
if (node.type) result.children!.push(cleanLuaTree(node));
|
|
1852
|
+
if (node.text?.trim()) result.children!.push(node);
|
|
1853
|
+
}
|
|
1854
|
+
return result;
|
|
1720
1855
|
}
|
|
1721
1856
|
|
|
1722
1857
|
function findFirstParseError(node: SyntaxNode): SyntaxNode | null {
|
|
@@ -1743,6 +1878,6 @@ function luaUnexpectedSymbolMessage(src: string, from: number): string {
|
|
|
1743
1878
|
* Helper function to parse a Lua expression string
|
|
1744
1879
|
*/
|
|
1745
1880
|
export function parseExpressionString(expr: string): LuaExpression {
|
|
1746
|
-
const parsedLua =
|
|
1881
|
+
const parsedLua = parseBlock(`_(${expr})`) as LuaBlock;
|
|
1747
1882
|
return (parsedLua.statements[0] as LuaFunctionCallStatement).call.args[0];
|
|
1748
1883
|
}
|