@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,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 {
|
|
@@ -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
|
}
|
|
@@ -1585,112 +1801,13 @@ function parseTableField(t: ParseTree, ctx: ASTCtx): LuaTableField {
|
|
|
1585
1801
|
}
|
|
1586
1802
|
}
|
|
1587
1803
|
|
|
1588
|
-
export function stripLuaComments(s: string): string {
|
|
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
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 {
|