@neocompose/cli 0.5.3 → 0.5.4
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/CHANGELOG.md +10 -0
- package/dist/neo.mjs +566 -160
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.5.4] - 2026-07-19
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Stop reporting phantom member updates when a stored type info spells an
|
|
8
|
+
empty generic-binding map as `typeArguments: {}` or `null` instead of an
|
|
9
|
+
absent key. Convex round trips produce these spellings interchangeably;
|
|
10
|
+
nested type-info binding maps now normalize to absent at every depth
|
|
11
|
+
before semantic comparison.
|
|
12
|
+
|
|
3
13
|
## [0.5.3] - 2026-07-19
|
|
4
14
|
|
|
5
15
|
### Fixed
|
package/dist/neo.mjs
CHANGED
|
@@ -1185,9 +1185,29 @@ function normalizeDocumentFields(recordKind, value) {
|
|
|
1185
1185
|
}
|
|
1186
1186
|
if (current === void 0) normalized[field] = null;
|
|
1187
1187
|
}
|
|
1188
|
+
if (recordKind === "member") {
|
|
1189
|
+
normalizeTypeInfoBindings(normalized.returnTypeInfo);
|
|
1190
|
+
if (Array.isArray(normalized.argumentTypes)) {
|
|
1191
|
+
for (const argument2 of normalized.argumentTypes) {
|
|
1192
|
+
normalizeTypeInfoBindings(argument2);
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1188
1196
|
omitRedundantDeclarationOrder(recordKind, normalized);
|
|
1189
1197
|
return normalized;
|
|
1190
1198
|
}
|
|
1199
|
+
function normalizeTypeInfoBindings(value) {
|
|
1200
|
+
if (!isRecord(value)) return;
|
|
1201
|
+
const bindings = value.typeArguments;
|
|
1202
|
+
if (bindings === null || isRecord(bindings) && Object.keys(bindings).length === 0) {
|
|
1203
|
+
delete value.typeArguments;
|
|
1204
|
+
} else if (isRecord(bindings)) {
|
|
1205
|
+
for (const binding of Object.values(bindings)) {
|
|
1206
|
+
normalizeTypeInfoBindings(binding);
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
normalizeTypeInfoBindings(value.entryTypeInfo);
|
|
1210
|
+
}
|
|
1191
1211
|
function memberDefaultApplies(field, type) {
|
|
1192
1212
|
if (field === "isVirtual" || field === "isAbstract" || field === "locked" || field === "required") {
|
|
1193
1213
|
return true;
|
|
@@ -12341,6 +12361,151 @@ var init_project_source_ast = __esm({
|
|
|
12341
12361
|
}
|
|
12342
12362
|
});
|
|
12343
12363
|
|
|
12364
|
+
// ../packages/neoscript-language/src/project-source-tokens.ts
|
|
12365
|
+
function rangeContains(range2, position) {
|
|
12366
|
+
return positionCompare(range2.start, position) <= 0 && positionCompare(position, range2.end) <= 0;
|
|
12367
|
+
}
|
|
12368
|
+
function rangeSize(range2) {
|
|
12369
|
+
return (range2.end.line - range2.start.line) * 1e6 + range2.end.character - range2.start.character;
|
|
12370
|
+
}
|
|
12371
|
+
function documentPrefix(text, position) {
|
|
12372
|
+
const lines = text.split("\n");
|
|
12373
|
+
return [
|
|
12374
|
+
...lines.slice(0, position.line),
|
|
12375
|
+
(lines[position.line] ?? "").slice(0, position.character)
|
|
12376
|
+
].join("\n");
|
|
12377
|
+
}
|
|
12378
|
+
function projectTokenAt(document, position) {
|
|
12379
|
+
const offset = new SourceText(document.text).offsetAt(position);
|
|
12380
|
+
const tokens = projectTokens(document);
|
|
12381
|
+
const containing = tokens.find(
|
|
12382
|
+
(token) => token.start <= offset && offset < token.end
|
|
12383
|
+
);
|
|
12384
|
+
if (containing?.kind === "identifier") return containing;
|
|
12385
|
+
const touching = tokens.find(
|
|
12386
|
+
(token) => token.kind === "identifier" && token.end === offset
|
|
12387
|
+
);
|
|
12388
|
+
return touching ?? containing;
|
|
12389
|
+
}
|
|
12390
|
+
function projectTokens(document) {
|
|
12391
|
+
const cached = PROJECT_TOKEN_CACHE.get(document);
|
|
12392
|
+
if (cached) return cached;
|
|
12393
|
+
const source = new SourceText(document.text);
|
|
12394
|
+
const tokens = lex(document.text).tokens.flatMap(
|
|
12395
|
+
(token) => {
|
|
12396
|
+
if (token.kind !== "string" || !token.raw.startsWith('"""')) {
|
|
12397
|
+
return [token];
|
|
12398
|
+
}
|
|
12399
|
+
const result = [];
|
|
12400
|
+
const contentEnd = token.raw.endsWith('"""') ? token.end - 3 : token.end;
|
|
12401
|
+
let segmentStart = token.start;
|
|
12402
|
+
let cursor = token.start + 3;
|
|
12403
|
+
while (cursor < contentEnd) {
|
|
12404
|
+
if (document.text[cursor] !== "{" || document.text[cursor + 1] === "{") {
|
|
12405
|
+
cursor++;
|
|
12406
|
+
continue;
|
|
12407
|
+
}
|
|
12408
|
+
const close = tripleInterpolationEnd(
|
|
12409
|
+
document.text,
|
|
12410
|
+
cursor + 1,
|
|
12411
|
+
contentEnd
|
|
12412
|
+
);
|
|
12413
|
+
if (close === null) break;
|
|
12414
|
+
if (segmentStart < cursor + 1) {
|
|
12415
|
+
result.push(
|
|
12416
|
+
projectToken(
|
|
12417
|
+
"string",
|
|
12418
|
+
document.text,
|
|
12419
|
+
source,
|
|
12420
|
+
segmentStart,
|
|
12421
|
+
cursor + 1
|
|
12422
|
+
)
|
|
12423
|
+
);
|
|
12424
|
+
}
|
|
12425
|
+
const expressionStart = cursor + 1;
|
|
12426
|
+
const expression = document.text.slice(expressionStart, close);
|
|
12427
|
+
for (const nested of lex(expression).tokens) {
|
|
12428
|
+
if (nested.kind === "eof") continue;
|
|
12429
|
+
const start = expressionStart + nested.start;
|
|
12430
|
+
const end = expressionStart + nested.end;
|
|
12431
|
+
result.push({
|
|
12432
|
+
...nested,
|
|
12433
|
+
start,
|
|
12434
|
+
end,
|
|
12435
|
+
raw: document.text.slice(start, end),
|
|
12436
|
+
range: source.range(start, end)
|
|
12437
|
+
});
|
|
12438
|
+
}
|
|
12439
|
+
segmentStart = close;
|
|
12440
|
+
cursor = close + 1;
|
|
12441
|
+
}
|
|
12442
|
+
if (segmentStart < token.end) {
|
|
12443
|
+
result.push(
|
|
12444
|
+
projectToken(
|
|
12445
|
+
"string",
|
|
12446
|
+
document.text,
|
|
12447
|
+
source,
|
|
12448
|
+
segmentStart,
|
|
12449
|
+
token.end
|
|
12450
|
+
)
|
|
12451
|
+
);
|
|
12452
|
+
}
|
|
12453
|
+
return result;
|
|
12454
|
+
}
|
|
12455
|
+
);
|
|
12456
|
+
PROJECT_TOKEN_CACHE.set(document, tokens);
|
|
12457
|
+
return tokens;
|
|
12458
|
+
}
|
|
12459
|
+
function projectToken(kind, text, source, start, end) {
|
|
12460
|
+
const raw = text.slice(start, end);
|
|
12461
|
+
return { kind, text: raw, raw, start, end, range: source.range(start, end) };
|
|
12462
|
+
}
|
|
12463
|
+
function tripleInterpolationEnd(text, start, limit) {
|
|
12464
|
+
let depth = 0;
|
|
12465
|
+
let cursor = start;
|
|
12466
|
+
while (cursor < limit) {
|
|
12467
|
+
const char = text[cursor];
|
|
12468
|
+
const next = text[cursor + 1];
|
|
12469
|
+
if (char === '"') {
|
|
12470
|
+
cursor++;
|
|
12471
|
+
while (cursor < limit) {
|
|
12472
|
+
if (text[cursor] === "\\") cursor += 2;
|
|
12473
|
+
else if (text[cursor] === '"') {
|
|
12474
|
+
cursor++;
|
|
12475
|
+
break;
|
|
12476
|
+
} else cursor++;
|
|
12477
|
+
}
|
|
12478
|
+
continue;
|
|
12479
|
+
}
|
|
12480
|
+
if (char === "/" && next === "/") {
|
|
12481
|
+
while (cursor < limit && text[cursor] !== "\n") cursor++;
|
|
12482
|
+
continue;
|
|
12483
|
+
}
|
|
12484
|
+
if (char === "{") depth++;
|
|
12485
|
+
else if (char === "}") {
|
|
12486
|
+
if (depth === 0) return cursor;
|
|
12487
|
+
depth--;
|
|
12488
|
+
}
|
|
12489
|
+
cursor++;
|
|
12490
|
+
}
|
|
12491
|
+
return null;
|
|
12492
|
+
}
|
|
12493
|
+
function rangesEqual(left, right) {
|
|
12494
|
+
return positionCompare(left.start, right.start) === 0 && positionCompare(left.end, right.end) === 0;
|
|
12495
|
+
}
|
|
12496
|
+
function positionCompare(left, right) {
|
|
12497
|
+
return left.line === right.line ? left.character - right.character : left.line - right.line;
|
|
12498
|
+
}
|
|
12499
|
+
var PROJECT_TOKEN_CACHE;
|
|
12500
|
+
var init_project_source_tokens = __esm({
|
|
12501
|
+
"../packages/neoscript-language/src/project-source-tokens.ts"() {
|
|
12502
|
+
"use strict";
|
|
12503
|
+
init_lexer();
|
|
12504
|
+
init_source_text();
|
|
12505
|
+
PROJECT_TOKEN_CACHE = /* @__PURE__ */ new WeakMap();
|
|
12506
|
+
}
|
|
12507
|
+
});
|
|
12508
|
+
|
|
12344
12509
|
// ../packages/neoscript-language/src/project-source-parser.ts
|
|
12345
12510
|
function parseNeoProjectSource(sourceValue, kind) {
|
|
12346
12511
|
const lexed = lex(sourceValue);
|
|
@@ -18792,7 +18957,13 @@ function analyzeNeoProjectSources(inputs) {
|
|
|
18792
18957
|
const symbols = [];
|
|
18793
18958
|
for (const [uri, document] of documents) {
|
|
18794
18959
|
for (const declaration of document.declarations) {
|
|
18795
|
-
collectDeclarationSymbols(
|
|
18960
|
+
collectDeclarationSymbols(
|
|
18961
|
+
uri,
|
|
18962
|
+
document,
|
|
18963
|
+
declaration,
|
|
18964
|
+
symbols,
|
|
18965
|
+
diagnostics
|
|
18966
|
+
);
|
|
18796
18967
|
}
|
|
18797
18968
|
}
|
|
18798
18969
|
validateSymbolUniqueness(symbols, diagnostics);
|
|
@@ -18802,7 +18973,7 @@ function analyzeNeoProjectSources(inputs) {
|
|
|
18802
18973
|
diagnostics.push(...compileProjectSourceBodies(documents).diagnostics);
|
|
18803
18974
|
return { documents, symbols, diagnostics };
|
|
18804
18975
|
}
|
|
18805
|
-
function collectDeclarationSymbols(uri, declaration, symbols, diagnostics) {
|
|
18976
|
+
function collectDeclarationSymbols(uri, document, declaration, symbols, diagnostics) {
|
|
18806
18977
|
const declarationKind = declaration.kind;
|
|
18807
18978
|
collectSymbol(
|
|
18808
18979
|
uri,
|
|
@@ -18812,7 +18983,9 @@ function collectDeclarationSymbols(uri, declaration, symbols, diagnostics) {
|
|
|
18812
18983
|
declaration.annotations,
|
|
18813
18984
|
void 0,
|
|
18814
18985
|
symbols,
|
|
18815
|
-
diagnostics
|
|
18986
|
+
diagnostics,
|
|
18987
|
+
void 0,
|
|
18988
|
+
declaration.kind === "global" ? declaration.type.name : void 0
|
|
18816
18989
|
);
|
|
18817
18990
|
if (declaration.kind === "enum") {
|
|
18818
18991
|
for (const option of declaration.options) {
|
|
@@ -18829,7 +19002,10 @@ function collectDeclarationSymbols(uri, declaration, symbols, diagnostics) {
|
|
|
18829
19002
|
}
|
|
18830
19003
|
return;
|
|
18831
19004
|
}
|
|
18832
|
-
if (declaration.kind === "global")
|
|
19005
|
+
if (declaration.kind === "global") {
|
|
19006
|
+
collectGlobalChildSymbols(uri, document, declaration, symbols, diagnostics);
|
|
19007
|
+
return;
|
|
19008
|
+
}
|
|
18833
19009
|
if (declaration.kind === "class") {
|
|
18834
19010
|
for (const parameter3 of declaration.genericParameters) {
|
|
18835
19011
|
collectSymbol(
|
|
@@ -18886,6 +19062,104 @@ function collectDeclarationSymbols(uri, declaration, symbols, diagnostics) {
|
|
|
18886
19062
|
}
|
|
18887
19063
|
}
|
|
18888
19064
|
}
|
|
19065
|
+
function collectGlobalChildSymbols(uri, document, declaration, symbols, diagnostics) {
|
|
19066
|
+
if (declaration.type.name === "Root") return;
|
|
19067
|
+
const initializer = declaration.initializer;
|
|
19068
|
+
const tokens = lex(document.sourceText).tokens.filter(
|
|
19069
|
+
(token) => token.kind !== "comment" && token.kind !== "eof" && rangeWithin(initializer.range, token.range)
|
|
19070
|
+
);
|
|
19071
|
+
let braceDepth = 0;
|
|
19072
|
+
let groupDepth = 0;
|
|
19073
|
+
let pendingAnnotations = [];
|
|
19074
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
19075
|
+
const token = tokens[index];
|
|
19076
|
+
if (token.text === "{") {
|
|
19077
|
+
braceDepth++;
|
|
19078
|
+
continue;
|
|
19079
|
+
}
|
|
19080
|
+
if (token.text === "}") {
|
|
19081
|
+
braceDepth--;
|
|
19082
|
+
continue;
|
|
19083
|
+
}
|
|
19084
|
+
if (braceDepth !== 1) continue;
|
|
19085
|
+
if (token.text === "(" || token.text === "[") {
|
|
19086
|
+
groupDepth++;
|
|
19087
|
+
continue;
|
|
19088
|
+
}
|
|
19089
|
+
if (token.text === ")" || token.text === "]") {
|
|
19090
|
+
groupDepth--;
|
|
19091
|
+
continue;
|
|
19092
|
+
}
|
|
19093
|
+
if (groupDepth !== 0) continue;
|
|
19094
|
+
if (token.text === ";") {
|
|
19095
|
+
pendingAnnotations = [];
|
|
19096
|
+
continue;
|
|
19097
|
+
}
|
|
19098
|
+
if (token.text === "@") {
|
|
19099
|
+
const annotation2 = scanAnnotation(tokens, index);
|
|
19100
|
+
if (annotation2) {
|
|
19101
|
+
pendingAnnotations.push(annotation2.annotation);
|
|
19102
|
+
index = annotation2.endIndex;
|
|
19103
|
+
}
|
|
19104
|
+
continue;
|
|
19105
|
+
}
|
|
19106
|
+
const name = tokens[index + 1];
|
|
19107
|
+
if ((token.kind === "type" || token.kind === "identifier") && name?.kind === "identifier" && tokens[index + 2]?.text === "=" && tokens[index + 3]?.text !== "=" && // A pending annotation or statement boundary precedes a declaration;
|
|
19108
|
+
// `key: value` fields and expression member accesses never do.
|
|
19109
|
+
tokens[index - 1]?.text !== "." && tokens[index - 1]?.text !== ":") {
|
|
19110
|
+
collectSymbol(
|
|
19111
|
+
uri,
|
|
19112
|
+
"graphChild",
|
|
19113
|
+
name.text,
|
|
19114
|
+
name.range,
|
|
19115
|
+
pendingAnnotations,
|
|
19116
|
+
declaration.name,
|
|
19117
|
+
symbols,
|
|
19118
|
+
diagnostics,
|
|
19119
|
+
void 0,
|
|
19120
|
+
token.text
|
|
19121
|
+
);
|
|
19122
|
+
pendingAnnotations = [];
|
|
19123
|
+
index += 2;
|
|
19124
|
+
}
|
|
19125
|
+
}
|
|
19126
|
+
}
|
|
19127
|
+
function scanAnnotation(tokens, atIndex) {
|
|
19128
|
+
const marker = tokens[atIndex];
|
|
19129
|
+
const name = tokens[atIndex + 1];
|
|
19130
|
+
if (!name || name.kind !== "identifier" && name.kind !== "keyword") {
|
|
19131
|
+
return null;
|
|
19132
|
+
}
|
|
19133
|
+
const argumentsList2 = [];
|
|
19134
|
+
let endIndex = atIndex + 1;
|
|
19135
|
+
let endRange = name.range;
|
|
19136
|
+
if (tokens[atIndex + 2]?.text === "(") {
|
|
19137
|
+
let depth = 0;
|
|
19138
|
+
for (let index = atIndex + 2; index < tokens.length; index++) {
|
|
19139
|
+
const token = tokens[index];
|
|
19140
|
+
if (token.text === "(") depth++;
|
|
19141
|
+
else if (token.text === ")" && --depth === 0) {
|
|
19142
|
+
endIndex = index;
|
|
19143
|
+
endRange = token.range;
|
|
19144
|
+
break;
|
|
19145
|
+
} else if (depth === 1 && token.kind === "string") {
|
|
19146
|
+
argumentsList2.push({ text: token.raw, range: token.range });
|
|
19147
|
+
}
|
|
19148
|
+
}
|
|
19149
|
+
}
|
|
19150
|
+
return {
|
|
19151
|
+
annotation: {
|
|
19152
|
+
name: name.text,
|
|
19153
|
+
nameRange: name.range,
|
|
19154
|
+
arguments: argumentsList2,
|
|
19155
|
+
range: { start: marker.range.start, end: endRange.end }
|
|
19156
|
+
},
|
|
19157
|
+
endIndex
|
|
19158
|
+
};
|
|
19159
|
+
}
|
|
19160
|
+
function rangeWithin(outer, inner) {
|
|
19161
|
+
return positionCompare(outer.start, inner.start) <= 0 && positionCompare(inner.end, outer.end) <= 0;
|
|
19162
|
+
}
|
|
18889
19163
|
function collectFlowSymbols(uri, content, ownerName, scopeRange, symbols, diagnostics) {
|
|
18890
19164
|
for (const binding of content.bindings) {
|
|
18891
19165
|
collectSymbol(
|
|
@@ -19177,6 +19451,8 @@ var init_project_source_analysis = __esm({
|
|
|
19177
19451
|
"../packages/neoscript-language/src/project-source-analysis.ts"() {
|
|
19178
19452
|
"use strict";
|
|
19179
19453
|
init_language_spec();
|
|
19454
|
+
init_lexer();
|
|
19455
|
+
init_project_source_tokens();
|
|
19180
19456
|
init_project_source_parser();
|
|
19181
19457
|
init_project_source_semantics();
|
|
19182
19458
|
init_project_source_script_compiler();
|
|
@@ -21242,151 +21518,6 @@ var init_project_schema_manifest = __esm({
|
|
|
21242
21518
|
}
|
|
21243
21519
|
});
|
|
21244
21520
|
|
|
21245
|
-
// ../packages/neoscript-language/src/project-source-tokens.ts
|
|
21246
|
-
function rangeContains(range2, position) {
|
|
21247
|
-
return positionCompare(range2.start, position) <= 0 && positionCompare(position, range2.end) <= 0;
|
|
21248
|
-
}
|
|
21249
|
-
function rangeSize(range2) {
|
|
21250
|
-
return (range2.end.line - range2.start.line) * 1e6 + range2.end.character - range2.start.character;
|
|
21251
|
-
}
|
|
21252
|
-
function documentPrefix(text, position) {
|
|
21253
|
-
const lines = text.split("\n");
|
|
21254
|
-
return [
|
|
21255
|
-
...lines.slice(0, position.line),
|
|
21256
|
-
(lines[position.line] ?? "").slice(0, position.character)
|
|
21257
|
-
].join("\n");
|
|
21258
|
-
}
|
|
21259
|
-
function projectTokenAt(document, position) {
|
|
21260
|
-
const offset = new SourceText(document.text).offsetAt(position);
|
|
21261
|
-
const tokens = projectTokens(document);
|
|
21262
|
-
const containing = tokens.find(
|
|
21263
|
-
(token) => token.start <= offset && offset < token.end
|
|
21264
|
-
);
|
|
21265
|
-
if (containing?.kind === "identifier") return containing;
|
|
21266
|
-
const touching = tokens.find(
|
|
21267
|
-
(token) => token.kind === "identifier" && token.end === offset
|
|
21268
|
-
);
|
|
21269
|
-
return touching ?? containing;
|
|
21270
|
-
}
|
|
21271
|
-
function projectTokens(document) {
|
|
21272
|
-
const cached = PROJECT_TOKEN_CACHE.get(document);
|
|
21273
|
-
if (cached) return cached;
|
|
21274
|
-
const source = new SourceText(document.text);
|
|
21275
|
-
const tokens = lex(document.text).tokens.flatMap(
|
|
21276
|
-
(token) => {
|
|
21277
|
-
if (token.kind !== "string" || !token.raw.startsWith('"""')) {
|
|
21278
|
-
return [token];
|
|
21279
|
-
}
|
|
21280
|
-
const result = [];
|
|
21281
|
-
const contentEnd = token.raw.endsWith('"""') ? token.end - 3 : token.end;
|
|
21282
|
-
let segmentStart = token.start;
|
|
21283
|
-
let cursor = token.start + 3;
|
|
21284
|
-
while (cursor < contentEnd) {
|
|
21285
|
-
if (document.text[cursor] !== "{" || document.text[cursor + 1] === "{") {
|
|
21286
|
-
cursor++;
|
|
21287
|
-
continue;
|
|
21288
|
-
}
|
|
21289
|
-
const close = tripleInterpolationEnd(
|
|
21290
|
-
document.text,
|
|
21291
|
-
cursor + 1,
|
|
21292
|
-
contentEnd
|
|
21293
|
-
);
|
|
21294
|
-
if (close === null) break;
|
|
21295
|
-
if (segmentStart < cursor + 1) {
|
|
21296
|
-
result.push(
|
|
21297
|
-
projectToken(
|
|
21298
|
-
"string",
|
|
21299
|
-
document.text,
|
|
21300
|
-
source,
|
|
21301
|
-
segmentStart,
|
|
21302
|
-
cursor + 1
|
|
21303
|
-
)
|
|
21304
|
-
);
|
|
21305
|
-
}
|
|
21306
|
-
const expressionStart = cursor + 1;
|
|
21307
|
-
const expression = document.text.slice(expressionStart, close);
|
|
21308
|
-
for (const nested of lex(expression).tokens) {
|
|
21309
|
-
if (nested.kind === "eof") continue;
|
|
21310
|
-
const start = expressionStart + nested.start;
|
|
21311
|
-
const end = expressionStart + nested.end;
|
|
21312
|
-
result.push({
|
|
21313
|
-
...nested,
|
|
21314
|
-
start,
|
|
21315
|
-
end,
|
|
21316
|
-
raw: document.text.slice(start, end),
|
|
21317
|
-
range: source.range(start, end)
|
|
21318
|
-
});
|
|
21319
|
-
}
|
|
21320
|
-
segmentStart = close;
|
|
21321
|
-
cursor = close + 1;
|
|
21322
|
-
}
|
|
21323
|
-
if (segmentStart < token.end) {
|
|
21324
|
-
result.push(
|
|
21325
|
-
projectToken(
|
|
21326
|
-
"string",
|
|
21327
|
-
document.text,
|
|
21328
|
-
source,
|
|
21329
|
-
segmentStart,
|
|
21330
|
-
token.end
|
|
21331
|
-
)
|
|
21332
|
-
);
|
|
21333
|
-
}
|
|
21334
|
-
return result;
|
|
21335
|
-
}
|
|
21336
|
-
);
|
|
21337
|
-
PROJECT_TOKEN_CACHE.set(document, tokens);
|
|
21338
|
-
return tokens;
|
|
21339
|
-
}
|
|
21340
|
-
function projectToken(kind, text, source, start, end) {
|
|
21341
|
-
const raw = text.slice(start, end);
|
|
21342
|
-
return { kind, text: raw, raw, start, end, range: source.range(start, end) };
|
|
21343
|
-
}
|
|
21344
|
-
function tripleInterpolationEnd(text, start, limit) {
|
|
21345
|
-
let depth = 0;
|
|
21346
|
-
let cursor = start;
|
|
21347
|
-
while (cursor < limit) {
|
|
21348
|
-
const char = text[cursor];
|
|
21349
|
-
const next = text[cursor + 1];
|
|
21350
|
-
if (char === '"') {
|
|
21351
|
-
cursor++;
|
|
21352
|
-
while (cursor < limit) {
|
|
21353
|
-
if (text[cursor] === "\\") cursor += 2;
|
|
21354
|
-
else if (text[cursor] === '"') {
|
|
21355
|
-
cursor++;
|
|
21356
|
-
break;
|
|
21357
|
-
} else cursor++;
|
|
21358
|
-
}
|
|
21359
|
-
continue;
|
|
21360
|
-
}
|
|
21361
|
-
if (char === "/" && next === "/") {
|
|
21362
|
-
while (cursor < limit && text[cursor] !== "\n") cursor++;
|
|
21363
|
-
continue;
|
|
21364
|
-
}
|
|
21365
|
-
if (char === "{") depth++;
|
|
21366
|
-
else if (char === "}") {
|
|
21367
|
-
if (depth === 0) return cursor;
|
|
21368
|
-
depth--;
|
|
21369
|
-
}
|
|
21370
|
-
cursor++;
|
|
21371
|
-
}
|
|
21372
|
-
return null;
|
|
21373
|
-
}
|
|
21374
|
-
function rangesEqual(left, right) {
|
|
21375
|
-
return positionCompare(left.start, right.start) === 0 && positionCompare(left.end, right.end) === 0;
|
|
21376
|
-
}
|
|
21377
|
-
function positionCompare(left, right) {
|
|
21378
|
-
return left.line === right.line ? left.character - right.character : left.line - right.line;
|
|
21379
|
-
}
|
|
21380
|
-
var PROJECT_TOKEN_CACHE;
|
|
21381
|
-
var init_project_source_tokens = __esm({
|
|
21382
|
-
"../packages/neoscript-language/src/project-source-tokens.ts"() {
|
|
21383
|
-
"use strict";
|
|
21384
|
-
init_lexer();
|
|
21385
|
-
init_source_text();
|
|
21386
|
-
PROJECT_TOKEN_CACHE = /* @__PURE__ */ new WeakMap();
|
|
21387
|
-
}
|
|
21388
|
-
});
|
|
21389
|
-
|
|
21390
21521
|
// ../packages/neoscript-language/src/quick-fixes.ts
|
|
21391
21522
|
function quickFixFor(document, diagnostic) {
|
|
21392
21523
|
if (diagnostic.code === "missing-semicolon") {
|
|
@@ -21703,11 +21834,17 @@ function projectMemberCompletions(analysis, document, position) {
|
|
|
21703
21834
|
if (!owner || owner.kind !== "identifier") return null;
|
|
21704
21835
|
const resolvedOwner = projectSymbolAt(analysis, document, owner.range.start);
|
|
21705
21836
|
if (!resolvedOwner) return null;
|
|
21837
|
+
const ownerNames = /* @__PURE__ */ new Set();
|
|
21706
21838
|
const typeName = projectSymbolTypeName(resolvedOwner.symbol);
|
|
21707
|
-
if (
|
|
21708
|
-
|
|
21709
|
-
(
|
|
21839
|
+
if (typeName) ownerNames.add(typeName);
|
|
21840
|
+
if (resolvedOwner.symbol.kind === "global") {
|
|
21841
|
+
ownerNames.add(resolvedOwner.symbol.name);
|
|
21842
|
+
}
|
|
21843
|
+
if (ownerNames.size === 0) return null;
|
|
21844
|
+
const members = analysis.symbols.filter(
|
|
21845
|
+
(symbol) => (symbol.kind === "member" || symbol.kind === "graphChild") && symbol.ownerName !== void 0 && ownerNames.has(symbol.ownerName)
|
|
21710
21846
|
);
|
|
21847
|
+
return members.length > 0 ? members : null;
|
|
21711
21848
|
}
|
|
21712
21849
|
function projectConstructorArgumentCompletions(analysis, document, position) {
|
|
21713
21850
|
const tokens = lex(document.text).tokens.filter(
|
|
@@ -21799,6 +21936,15 @@ function graphConstructorNamedArguments(typeName) {
|
|
|
21799
21936
|
{ name: "reason", type: "string" },
|
|
21800
21937
|
{ name: "duration", type: "decimal?" }
|
|
21801
21938
|
];
|
|
21939
|
+
case "Vector2":
|
|
21940
|
+
case "Vector2Int":
|
|
21941
|
+
case "Vector3":
|
|
21942
|
+
case "Vector3Int":
|
|
21943
|
+
case "Color":
|
|
21944
|
+
return graphConstructorParameters(typeName).map((parameter3) => ({
|
|
21945
|
+
name: parameter3.name,
|
|
21946
|
+
type: parameter3.type
|
|
21947
|
+
}));
|
|
21802
21948
|
default:
|
|
21803
21949
|
return null;
|
|
21804
21950
|
}
|
|
@@ -22234,6 +22380,19 @@ function indexedProjectSymbolAt(analysis, document, position, index) {
|
|
|
22234
22380
|
return { token, symbol: visibleCandidates[0] };
|
|
22235
22381
|
}
|
|
22236
22382
|
if (visibleCandidates.length === 0) return null;
|
|
22383
|
+
const sourceTokens = projectTokens(document);
|
|
22384
|
+
const tokenIndex = sourceTokens.findIndex(
|
|
22385
|
+
(candidate) => candidate.start === token.start && candidate.end === token.end
|
|
22386
|
+
);
|
|
22387
|
+
if (sourceTokens[tokenIndex + 1]?.text === "=" && sourceTokens[tokenIndex + 2]?.text !== "=") {
|
|
22388
|
+
const constructed = constructionMemberSymbol(
|
|
22389
|
+
analysis,
|
|
22390
|
+
document,
|
|
22391
|
+
token,
|
|
22392
|
+
visibleCandidates
|
|
22393
|
+
);
|
|
22394
|
+
if (constructed) return { token, symbol: constructed };
|
|
22395
|
+
}
|
|
22237
22396
|
const source = analysis.documents.get(document.uri);
|
|
22238
22397
|
const owner = source ? declarationContaining(source, token.range.start)?.name : void 0;
|
|
22239
22398
|
if (owner) {
|
|
@@ -22242,10 +22401,6 @@ function indexedProjectSymbolAt(analysis, document, position, index) {
|
|
|
22242
22401
|
);
|
|
22243
22402
|
if (owned.length === 1) return { token, symbol: owned[0] };
|
|
22244
22403
|
}
|
|
22245
|
-
const sourceTokens = projectTokens(document);
|
|
22246
|
-
const tokenIndex = sourceTokens.findIndex(
|
|
22247
|
-
(candidate) => candidate.start === token.start && candidate.end === token.end
|
|
22248
|
-
);
|
|
22249
22404
|
const dotOwner = sourceTokens[tokenIndex - 1]?.text === "." ? sourceTokens[tokenIndex - 2] : void 0;
|
|
22250
22405
|
if (dotOwner) {
|
|
22251
22406
|
const resolvedOwner = indexedProjectSymbolAt(
|
|
@@ -22254,15 +22409,39 @@ function indexedProjectSymbolAt(analysis, document, position, index) {
|
|
|
22254
22409
|
dotOwner.range.start,
|
|
22255
22410
|
index
|
|
22256
22411
|
);
|
|
22257
|
-
const
|
|
22412
|
+
const ownerNames = /* @__PURE__ */ new Set();
|
|
22413
|
+
if (resolvedOwner) {
|
|
22414
|
+
const typeName = projectSymbolTypeName(resolvedOwner.symbol);
|
|
22415
|
+
if (typeName) ownerNames.add(typeName);
|
|
22416
|
+
if (resolvedOwner.symbol.kind === "global") {
|
|
22417
|
+
ownerNames.add(resolvedOwner.symbol.name);
|
|
22418
|
+
}
|
|
22419
|
+
} else {
|
|
22420
|
+
ownerNames.add(dotOwner.text);
|
|
22421
|
+
}
|
|
22258
22422
|
const qualified = visibleCandidates.filter(
|
|
22259
|
-
(symbol) => symbol.ownerName
|
|
22423
|
+
(symbol) => symbol.ownerName !== void 0 && ownerNames.has(symbol.ownerName)
|
|
22260
22424
|
);
|
|
22261
22425
|
if (qualified.length === 1) return { token, symbol: qualified[0] };
|
|
22262
22426
|
}
|
|
22263
22427
|
const projectLevel = visibleCandidates.filter((symbol) => !symbol.ownerName);
|
|
22264
22428
|
return projectLevel.length === 1 ? { token, symbol: projectLevel[0] } : null;
|
|
22265
22429
|
}
|
|
22430
|
+
function constructionMemberSymbol(analysis, document, token, candidates) {
|
|
22431
|
+
const site = constructionSiteAt(analysis, document, token.range.start);
|
|
22432
|
+
let current = site?.enclosingTypeName ?? null;
|
|
22433
|
+
const visited = /* @__PURE__ */ new Set();
|
|
22434
|
+
while (current !== null && !visited.has(current)) {
|
|
22435
|
+
visited.add(current);
|
|
22436
|
+
const owned = candidates.find(
|
|
22437
|
+
(symbol) => symbol.kind === "member" && symbol.ownerName === current
|
|
22438
|
+
);
|
|
22439
|
+
if (owned) return owned;
|
|
22440
|
+
const declaration = findTypeDeclaration(analysis, current);
|
|
22441
|
+
current = declaration?.kind === "class" ? resolvableBaseClassName(analysis, declaration) : null;
|
|
22442
|
+
}
|
|
22443
|
+
return null;
|
|
22444
|
+
}
|
|
22266
22445
|
function projectSymbolTypeName(symbol) {
|
|
22267
22446
|
switch (symbol.kind) {
|
|
22268
22447
|
case "class":
|
|
@@ -22360,21 +22539,219 @@ function signatureResult(name, parameters, activeParameter) {
|
|
|
22360
22539
|
};
|
|
22361
22540
|
}
|
|
22362
22541
|
function contextualConstructorType(analysis, document, position) {
|
|
22542
|
+
return constructionSiteAt(analysis, document, position)?.expectedTypeName ?? null;
|
|
22543
|
+
}
|
|
22544
|
+
function initializerRootAt(analysis, document, position) {
|
|
22363
22545
|
const source = analysis.documents.get(document.uri);
|
|
22364
22546
|
if (!source) return null;
|
|
22365
22547
|
for (const declaration of source.declarations) {
|
|
22366
22548
|
if (declaration.kind === "global") {
|
|
22367
22549
|
if (rangeContains(declaration.initializer.range, position)) {
|
|
22368
|
-
return
|
|
22550
|
+
return {
|
|
22551
|
+
type: declaration.type,
|
|
22552
|
+
range: declaration.initializer.range
|
|
22553
|
+
};
|
|
22369
22554
|
}
|
|
22370
22555
|
continue;
|
|
22371
22556
|
}
|
|
22372
22557
|
if (declaration.kind === "enum") continue;
|
|
22373
22558
|
for (const member of declaration.members) {
|
|
22374
22559
|
if (member.kind === "field" && member.initializer && rangeContains(member.initializer.range, position)) {
|
|
22375
|
-
return member.type.
|
|
22560
|
+
return { type: member.type, range: member.initializer.range };
|
|
22561
|
+
}
|
|
22562
|
+
}
|
|
22563
|
+
}
|
|
22564
|
+
return null;
|
|
22565
|
+
}
|
|
22566
|
+
function constructionSiteAt(analysis, document, position) {
|
|
22567
|
+
const root = initializerRootAt(analysis, document, position);
|
|
22568
|
+
if (!root) return null;
|
|
22569
|
+
const tokens = projectTokens(document).filter(
|
|
22570
|
+
(token) => token.kind !== "comment" && token.kind !== "eof" && rangeContains(root.range, token.range.start) && positionCompare(token.range.start, position) < 0
|
|
22571
|
+
);
|
|
22572
|
+
const stack = [];
|
|
22573
|
+
const expectedType = () => {
|
|
22574
|
+
const frame = stack.at(-1);
|
|
22575
|
+
if (!frame) return root.type;
|
|
22576
|
+
switch (frame.kind) {
|
|
22577
|
+
case "object":
|
|
22578
|
+
case "dictionary":
|
|
22579
|
+
return frame.slotType;
|
|
22580
|
+
case "list":
|
|
22581
|
+
return frame.elementType;
|
|
22582
|
+
case "call":
|
|
22583
|
+
return constructorArgumentType(analysis, frame);
|
|
22584
|
+
case "opaque":
|
|
22585
|
+
return null;
|
|
22586
|
+
}
|
|
22587
|
+
};
|
|
22588
|
+
let sawNew = false;
|
|
22589
|
+
let pendingNewTypeName = null;
|
|
22590
|
+
let closedConstructorTypeName = null;
|
|
22591
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
22592
|
+
const token = tokens[index];
|
|
22593
|
+
if (token.text === "@") {
|
|
22594
|
+
if ((tokens[index + 1]?.kind === "identifier" || tokens[index + 1]?.kind === "keyword") && tokens[index + 2]?.text === "(") {
|
|
22595
|
+
let depth = 0;
|
|
22596
|
+
index += 2;
|
|
22597
|
+
for (; index < tokens.length; index++) {
|
|
22598
|
+
if (tokens[index].text === "(") depth++;
|
|
22599
|
+
else if (tokens[index].text === ")" && --depth === 0) break;
|
|
22600
|
+
}
|
|
22601
|
+
} else if (tokens[index + 1]?.kind === "identifier") {
|
|
22602
|
+
index += 1;
|
|
22603
|
+
}
|
|
22604
|
+
continue;
|
|
22605
|
+
}
|
|
22606
|
+
if (token.text === "new") {
|
|
22607
|
+
sawNew = true;
|
|
22608
|
+
pendingNewTypeName = expectedType()?.name ?? null;
|
|
22609
|
+
closedConstructorTypeName = null;
|
|
22610
|
+
continue;
|
|
22611
|
+
}
|
|
22612
|
+
if (sawNew && (token.kind === "identifier" || token.kind === "type")) {
|
|
22613
|
+
pendingNewTypeName = token.text;
|
|
22614
|
+
continue;
|
|
22615
|
+
}
|
|
22616
|
+
if (sawNew && token.text === "<") {
|
|
22617
|
+
let depth = 0;
|
|
22618
|
+
for (; index < tokens.length; index++) {
|
|
22619
|
+
if (tokens[index].text === "<") depth++;
|
|
22620
|
+
else if (tokens[index].text === ">" && --depth === 0) break;
|
|
22621
|
+
}
|
|
22622
|
+
continue;
|
|
22623
|
+
}
|
|
22624
|
+
if (token.text === "{") {
|
|
22625
|
+
if (sawNew || closedConstructorTypeName !== null) {
|
|
22626
|
+
stack.push({
|
|
22627
|
+
kind: "object",
|
|
22628
|
+
typeName: pendingNewTypeName ?? closedConstructorTypeName,
|
|
22629
|
+
slotType: null
|
|
22630
|
+
});
|
|
22631
|
+
sawNew = false;
|
|
22632
|
+
pendingNewTypeName = null;
|
|
22633
|
+
} else {
|
|
22634
|
+
const expected = expectedType();
|
|
22635
|
+
if (expected?.name === "Dictionary") {
|
|
22636
|
+
stack.push({
|
|
22637
|
+
kind: "dictionary",
|
|
22638
|
+
valueType: expected.typeArguments[1] ?? null,
|
|
22639
|
+
slotType: null
|
|
22640
|
+
});
|
|
22641
|
+
} else {
|
|
22642
|
+
stack.push({ kind: "opaque" });
|
|
22643
|
+
}
|
|
22376
22644
|
}
|
|
22645
|
+
closedConstructorTypeName = null;
|
|
22646
|
+
continue;
|
|
22647
|
+
}
|
|
22648
|
+
if (token.text === "(") {
|
|
22649
|
+
if (sawNew) {
|
|
22650
|
+
stack.push({
|
|
22651
|
+
kind: "call",
|
|
22652
|
+
typeName: pendingNewTypeName,
|
|
22653
|
+
argumentIndex: 0,
|
|
22654
|
+
argumentName: null
|
|
22655
|
+
});
|
|
22656
|
+
sawNew = false;
|
|
22657
|
+
pendingNewTypeName = null;
|
|
22658
|
+
} else {
|
|
22659
|
+
stack.push({ kind: "opaque" });
|
|
22660
|
+
}
|
|
22661
|
+
closedConstructorTypeName = null;
|
|
22662
|
+
continue;
|
|
22663
|
+
}
|
|
22664
|
+
if (token.text === "[") {
|
|
22665
|
+
stack.push({
|
|
22666
|
+
kind: "list",
|
|
22667
|
+
elementType: collectionElementType2(expectedType())
|
|
22668
|
+
});
|
|
22669
|
+
closedConstructorTypeName = null;
|
|
22670
|
+
continue;
|
|
22671
|
+
}
|
|
22672
|
+
if (token.text === ")") {
|
|
22673
|
+
const frame2 = stack.pop();
|
|
22674
|
+
closedConstructorTypeName = frame2?.kind === "call" ? frame2.typeName : null;
|
|
22675
|
+
continue;
|
|
22676
|
+
}
|
|
22677
|
+
if (token.text === "}" || token.text === "]") {
|
|
22678
|
+
stack.pop();
|
|
22679
|
+
closedConstructorTypeName = null;
|
|
22680
|
+
continue;
|
|
22681
|
+
}
|
|
22682
|
+
closedConstructorTypeName = null;
|
|
22683
|
+
const frame = stack.at(-1);
|
|
22684
|
+
if (!frame) continue;
|
|
22685
|
+
if (frame.kind === "object") {
|
|
22686
|
+
if (token.text === "=" && tokens[index + 1]?.text !== "=") {
|
|
22687
|
+
const nameToken = tokens[index - 1];
|
|
22688
|
+
frame.slotType = nameToken && (nameToken.kind === "identifier" || nameToken.kind === "type") ? declaredFieldType(analysis, frame.typeName, nameToken.text) : null;
|
|
22689
|
+
} else if (token.text === ",") {
|
|
22690
|
+
frame.slotType = null;
|
|
22691
|
+
}
|
|
22692
|
+
} else if (frame.kind === "dictionary") {
|
|
22693
|
+
if (token.text === ":") frame.slotType = frame.valueType;
|
|
22694
|
+
else if (token.text === ",") frame.slotType = null;
|
|
22695
|
+
} else if (frame.kind === "call") {
|
|
22696
|
+
if (token.text === ",") {
|
|
22697
|
+
frame.argumentIndex++;
|
|
22698
|
+
frame.argumentName = null;
|
|
22699
|
+
} else if (token.text === ":") {
|
|
22700
|
+
const nameToken = tokens[index - 1];
|
|
22701
|
+
frame.argumentName = nameToken?.kind === "identifier" ? nameToken.text : null;
|
|
22702
|
+
}
|
|
22703
|
+
}
|
|
22704
|
+
}
|
|
22705
|
+
const enclosing = [...stack].reverse().find(
|
|
22706
|
+
(frame) => frame.kind === "object"
|
|
22707
|
+
);
|
|
22708
|
+
return {
|
|
22709
|
+
expectedTypeName: expectedType()?.name ?? null,
|
|
22710
|
+
enclosingTypeName: enclosing?.typeName ?? null
|
|
22711
|
+
};
|
|
22712
|
+
}
|
|
22713
|
+
function collectionElementType2(type) {
|
|
22714
|
+
if (!type) return null;
|
|
22715
|
+
if (type.name === "List" || type.name === "Set") {
|
|
22716
|
+
return type.typeArguments[0] ?? null;
|
|
22717
|
+
}
|
|
22718
|
+
return null;
|
|
22719
|
+
}
|
|
22720
|
+
function constructorArgumentType(analysis, frame) {
|
|
22721
|
+
if (!frame.typeName) return null;
|
|
22722
|
+
const declaration = findTypeDeclaration(analysis, frame.typeName);
|
|
22723
|
+
if (!declaration || declaration.kind === "global" || declaration.kind === "enum") {
|
|
22724
|
+
return null;
|
|
22725
|
+
}
|
|
22726
|
+
const fields = declaration.members.filter(
|
|
22727
|
+
(member) => member.kind === "field" && !member.modifiers.includes("static")
|
|
22728
|
+
);
|
|
22729
|
+
if (frame.argumentName !== null) {
|
|
22730
|
+
return fields.find((field) => field.name === frame.argumentName)?.type ?? null;
|
|
22731
|
+
}
|
|
22732
|
+
return fields[frame.argumentIndex]?.type ?? null;
|
|
22733
|
+
}
|
|
22734
|
+
function declaredFieldType(analysis, typeName, fieldName) {
|
|
22735
|
+
let current = typeName;
|
|
22736
|
+
const visited = /* @__PURE__ */ new Set();
|
|
22737
|
+
while (current !== null && !visited.has(current)) {
|
|
22738
|
+
visited.add(current);
|
|
22739
|
+
const declaration = findTypeDeclaration(analysis, current);
|
|
22740
|
+
if (!declaration || declaration.kind === "global" || declaration.kind === "enum") {
|
|
22741
|
+
return null;
|
|
22377
22742
|
}
|
|
22743
|
+
const member = declaration.members.find(
|
|
22744
|
+
(candidate) => candidate.kind === "field" && candidate.name === fieldName
|
|
22745
|
+
);
|
|
22746
|
+
if (member) return member.type;
|
|
22747
|
+
current = declaration.kind === "class" ? resolvableBaseClassName(analysis, declaration) : null;
|
|
22748
|
+
}
|
|
22749
|
+
return null;
|
|
22750
|
+
}
|
|
22751
|
+
function resolvableBaseClassName(analysis, declaration) {
|
|
22752
|
+
for (const base of declaration.baseTypes) {
|
|
22753
|
+
const resolved = findTypeDeclaration(analysis, base.name);
|
|
22754
|
+
if (resolved?.kind === "class") return base.name;
|
|
22378
22755
|
}
|
|
22379
22756
|
return null;
|
|
22380
22757
|
}
|
|
@@ -22448,6 +22825,35 @@ function graphConstructorParameters(typeName) {
|
|
|
22448
22825
|
{ type: "string", name: "reason" },
|
|
22449
22826
|
{ type: "decimal?", name: "duration" }
|
|
22450
22827
|
];
|
|
22828
|
+
case "Vector2":
|
|
22829
|
+
return [
|
|
22830
|
+
{ type: "float", name: "x" },
|
|
22831
|
+
{ type: "float", name: "y" }
|
|
22832
|
+
];
|
|
22833
|
+
case "Vector2Int":
|
|
22834
|
+
return [
|
|
22835
|
+
{ type: "int", name: "x" },
|
|
22836
|
+
{ type: "int", name: "y" }
|
|
22837
|
+
];
|
|
22838
|
+
case "Vector3":
|
|
22839
|
+
return [
|
|
22840
|
+
{ type: "float", name: "x" },
|
|
22841
|
+
{ type: "float", name: "y" },
|
|
22842
|
+
{ type: "float", name: "z" }
|
|
22843
|
+
];
|
|
22844
|
+
case "Vector3Int":
|
|
22845
|
+
return [
|
|
22846
|
+
{ type: "int", name: "x" },
|
|
22847
|
+
{ type: "int", name: "y" },
|
|
22848
|
+
{ type: "int", name: "z" }
|
|
22849
|
+
];
|
|
22850
|
+
case "Color":
|
|
22851
|
+
return [
|
|
22852
|
+
{ type: "float", name: "r" },
|
|
22853
|
+
{ type: "float", name: "g" },
|
|
22854
|
+
{ type: "float", name: "b" },
|
|
22855
|
+
{ type: "float", name: "a" }
|
|
22856
|
+
];
|
|
22451
22857
|
default:
|
|
22452
22858
|
return [];
|
|
22453
22859
|
}
|