@neocompose/cli 0.26.1 → 0.26.2
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 +13 -0
- package/README.md +12 -4
- package/dist/neo.mjs +1118 -198
- package/package.json +1 -1
- package/skills/neocompose-cli/SKILL.md +1 -1
- package/skills/neocompose-cli/references/cli-development.md +1 -1
package/dist/neo.mjs
CHANGED
|
@@ -2143,16 +2143,28 @@ function complete(snapshot, position) {
|
|
|
2143
2143
|
candidates = switchCaseCandidates;
|
|
2144
2144
|
} else if (tail && (tail.kind === "punctuation" && tail.text === "." || tail.kind === "operator" && tail.text === "?.")) {
|
|
2145
2145
|
const receiverTokens = expressionTokensBefore(tokens, tokens.length - 1);
|
|
2146
|
-
|
|
2147
|
-
|
|
2146
|
+
if (receiverTokens.length === 0 || isContextualCompletionDot(snapshot.source.text, word.start)) {
|
|
2147
|
+
candidates = contextualEnumCompletionItems(
|
|
2148
|
+
snapshot,
|
|
2149
|
+
expectedTypeAt(snapshot, word.start),
|
|
2150
|
+
word
|
|
2151
|
+
);
|
|
2152
|
+
} else {
|
|
2153
|
+
const resolved = resolveChain(snapshot, receiverTokens, offset);
|
|
2154
|
+
candidates = completionItemsForResolution(snapshot, resolved, word);
|
|
2155
|
+
}
|
|
2148
2156
|
} else if (tail?.kind === "identifier" && tail.text === NEOSCRIPT_CONSTRUCTOR_KEYWORD) {
|
|
2149
|
-
candidates = constructorCompletionItems(
|
|
2157
|
+
candidates = constructorCompletionItems(
|
|
2158
|
+
snapshot,
|
|
2159
|
+
word,
|
|
2160
|
+
expectedTypeAt(snapshot, word.start)
|
|
2161
|
+
);
|
|
2150
2162
|
} else if (isTypePosition(tokens)) {
|
|
2151
2163
|
candidates = typeCompletionItems(snapshot, word);
|
|
2152
2164
|
} else if (isLambdaParameterPosition(tokens)) {
|
|
2153
2165
|
candidates = [];
|
|
2154
2166
|
} else {
|
|
2155
|
-
candidates =
|
|
2167
|
+
candidates = contextualCompletionItems(snapshot, offset, word);
|
|
2156
2168
|
}
|
|
2157
2169
|
return {
|
|
2158
2170
|
isIncomplete: hasUnclosedConstruct(snapshot.lexed.tokens, offset),
|
|
@@ -2230,12 +2242,33 @@ function signatureHelp(snapshot, position) {
|
|
|
2230
2242
|
if (call.kind === "constructor") {
|
|
2231
2243
|
const type = resolution?.staticType;
|
|
2232
2244
|
const constructor2 = type?.constructorSignature;
|
|
2233
|
-
|
|
2245
|
+
const declared = type?.declaredConstructors ?? [];
|
|
2246
|
+
if (!type) return null;
|
|
2234
2247
|
const activeParameter2 = countTopLevelCommas(
|
|
2235
2248
|
snapshot.source.text,
|
|
2236
2249
|
call.openParenOffset + 1,
|
|
2237
2250
|
offset
|
|
2238
2251
|
);
|
|
2252
|
+
if (declared.length > 0) {
|
|
2253
|
+
return {
|
|
2254
|
+
signatures: declared.map((candidate) => ({
|
|
2255
|
+
label: `${type.name}(${candidate.parameters.map(
|
|
2256
|
+
(parameter4) => `${formatType(parameter4.type, snapshot.project)} ${parameter4.name}`
|
|
2257
|
+
).join(", ")})`,
|
|
2258
|
+
...candidate.documentation ? { documentation: candidate.documentation } : {},
|
|
2259
|
+
parameters: candidate.parameters.map((parameter4) => ({
|
|
2260
|
+
label: `${formatType(parameter4.type, snapshot.project)} ${parameter4.name}`,
|
|
2261
|
+
...parameter4.documentation ? { documentation: parameter4.documentation } : {}
|
|
2262
|
+
}))
|
|
2263
|
+
})),
|
|
2264
|
+
activeSignature: 0,
|
|
2265
|
+
activeParameter: Math.min(
|
|
2266
|
+
activeParameter2,
|
|
2267
|
+
Math.max(0, (declared[0]?.parameters.length ?? 1) - 1)
|
|
2268
|
+
)
|
|
2269
|
+
};
|
|
2270
|
+
}
|
|
2271
|
+
if (!constructor2) return null;
|
|
2239
2272
|
return {
|
|
2240
2273
|
signatures: [
|
|
2241
2274
|
{
|
|
@@ -2283,19 +2316,18 @@ function inlayHints(snapshot, range2) {
|
|
|
2283
2316
|
const hints = [];
|
|
2284
2317
|
for (const call of snapshot.parsed.calls) {
|
|
2285
2318
|
if (call.kind !== "constructor") continue;
|
|
2286
|
-
const
|
|
2287
|
-
|
|
2288
|
-
)
|
|
2289
|
-
if (!constructor2) continue;
|
|
2319
|
+
const type = snapshot.project.typeByName.get(call.name);
|
|
2320
|
+
const parameterNames = constructorParameterNames(type);
|
|
2321
|
+
if (!parameterNames) continue;
|
|
2290
2322
|
for (let index = 0; index < call.argumentRanges.length; index++) {
|
|
2291
2323
|
const argumentRange = call.argumentRanges[index];
|
|
2292
|
-
const
|
|
2293
|
-
if (!argumentRange || !
|
|
2324
|
+
const parameterName = parameterNames[index];
|
|
2325
|
+
if (!argumentRange || !parameterName) continue;
|
|
2294
2326
|
const position = firstNonWhitespacePosition(snapshot, argumentRange);
|
|
2295
2327
|
if (range2 && !positionInRange(position, range2)) continue;
|
|
2296
2328
|
hints.push({
|
|
2297
2329
|
position,
|
|
2298
|
-
label: `${
|
|
2330
|
+
label: `${parameterName}:`,
|
|
2299
2331
|
kind: "parameter",
|
|
2300
2332
|
paddingRight: true
|
|
2301
2333
|
});
|
|
@@ -2303,6 +2335,22 @@ function inlayHints(snapshot, range2) {
|
|
|
2303
2335
|
}
|
|
2304
2336
|
return hints;
|
|
2305
2337
|
}
|
|
2338
|
+
function constructorParameterNames(type) {
|
|
2339
|
+
const declared = type?.declaredConstructors ?? [];
|
|
2340
|
+
if (declared.length > 0) {
|
|
2341
|
+
const arity = Math.max(
|
|
2342
|
+
...declared.map((constructor2) => constructor2.parameters.length)
|
|
2343
|
+
);
|
|
2344
|
+
return Array.from({ length: arity }, (_, index) => {
|
|
2345
|
+
const names = new Set(
|
|
2346
|
+
declared.map((constructor2) => constructor2.parameters[index]?.name)
|
|
2347
|
+
);
|
|
2348
|
+
const [name] = names;
|
|
2349
|
+
return names.size === 1 && name ? name : null;
|
|
2350
|
+
});
|
|
2351
|
+
}
|
|
2352
|
+
return type?.constructorSignature?.parameters.map((parameter4) => parameter4.name) ?? null;
|
|
2353
|
+
}
|
|
2306
2354
|
function documentSymbols(snapshot) {
|
|
2307
2355
|
const unitSymbols = snapshot.parsed.units.map((unit) => ({
|
|
2308
2356
|
name: unit.kind,
|
|
@@ -2439,8 +2487,9 @@ function isValidNeoIdentifier(name) {
|
|
|
2439
2487
|
function isValidRenameIdentifier(name) {
|
|
2440
2488
|
return isValidNeoIdentifier(name);
|
|
2441
2489
|
}
|
|
2442
|
-
function
|
|
2490
|
+
function contextualCompletionItems(snapshot, offset, word) {
|
|
2443
2491
|
const items = [];
|
|
2492
|
+
const expectedType = expectedTypeAt(snapshot, word.start);
|
|
2444
2493
|
if (insideCatchBody(snapshot, offset)) {
|
|
2445
2494
|
items.push(
|
|
2446
2495
|
completion(
|
|
@@ -2454,7 +2503,12 @@ function topLevelCompletionItems(snapshot, offset, word) {
|
|
|
2454
2503
|
)
|
|
2455
2504
|
);
|
|
2456
2505
|
}
|
|
2506
|
+
const statementStart = isStatementStart(snapshot.lexed.tokens, word.start);
|
|
2457
2507
|
for (const keyword of NEOSCRIPT_KEYWORDS) {
|
|
2508
|
+
if (!statementStart && statementOnlyKeyword(keyword)) continue;
|
|
2509
|
+
if (!statementStart && expectedType !== null && !literalKeywordMatchesExpectedType(keyword, expectedType)) {
|
|
2510
|
+
continue;
|
|
2511
|
+
}
|
|
2458
2512
|
items.push(
|
|
2459
2513
|
completion(
|
|
2460
2514
|
keyword,
|
|
@@ -2467,6 +2521,7 @@ function topLevelCompletionItems(snapshot, offset, word) {
|
|
|
2467
2521
|
);
|
|
2468
2522
|
}
|
|
2469
2523
|
for (const snippet of NEOSCRIPT_STATEMENT_SNIPPETS) {
|
|
2524
|
+
if (!statementStart) continue;
|
|
2470
2525
|
items.push(
|
|
2471
2526
|
completion(
|
|
2472
2527
|
snippet.label,
|
|
@@ -2479,60 +2534,282 @@ function topLevelCompletionItems(snapshot, offset, word) {
|
|
|
2479
2534
|
)
|
|
2480
2535
|
);
|
|
2481
2536
|
}
|
|
2482
|
-
|
|
2483
|
-
completion(
|
|
2484
|
-
NEOSCRIPT_INFERRED_LOCAL_KEYWORD,
|
|
2485
|
-
"snippet",
|
|
2486
|
-
"var ${1:name} = ${0:value};",
|
|
2487
|
-
"Inferred local declaration",
|
|
2488
|
-
word,
|
|
2489
|
-
snapshot,
|
|
2490
|
-
"snippet"
|
|
2491
|
-
)
|
|
2492
|
-
);
|
|
2493
|
-
items.push(
|
|
2494
|
-
completion(
|
|
2495
|
-
NEOSCRIPT_CONSTRUCTOR_KEYWORD,
|
|
2496
|
-
"snippet",
|
|
2497
|
-
"new ${1:ClassName}(${0})",
|
|
2498
|
-
"Construct a Class value",
|
|
2499
|
-
word,
|
|
2500
|
-
snapshot,
|
|
2501
|
-
"snippet"
|
|
2502
|
-
)
|
|
2503
|
-
);
|
|
2504
|
-
for (const primitive3 of NEOSCRIPT_PRIMITIVE_TYPES) {
|
|
2537
|
+
if (statementStart) {
|
|
2505
2538
|
items.push(
|
|
2506
2539
|
completion(
|
|
2507
|
-
|
|
2508
|
-
"
|
|
2509
|
-
|
|
2510
|
-
"
|
|
2540
|
+
NEOSCRIPT_INFERRED_LOCAL_KEYWORD,
|
|
2541
|
+
"snippet",
|
|
2542
|
+
"var ${1:name} = ${0:value};",
|
|
2543
|
+
"Inferred local declaration",
|
|
2511
2544
|
word,
|
|
2512
|
-
snapshot
|
|
2545
|
+
snapshot,
|
|
2546
|
+
"snippet"
|
|
2547
|
+
)
|
|
2548
|
+
);
|
|
2549
|
+
}
|
|
2550
|
+
const expectedNamed = expectedType?.kind === "named" ? snapshot.project.typeById.get(expectedType.typeId) : void 0;
|
|
2551
|
+
if (expectedType === null || expectedNamed?.kind === "class" || expectedNamed?.kind === "interface") {
|
|
2552
|
+
items.push(
|
|
2553
|
+
completion(
|
|
2554
|
+
NEOSCRIPT_CONSTRUCTOR_KEYWORD,
|
|
2555
|
+
"snippet",
|
|
2556
|
+
"new ${1:ClassName}(${0})",
|
|
2557
|
+
"Construct a Class value",
|
|
2558
|
+
word,
|
|
2559
|
+
snapshot,
|
|
2560
|
+
"snippet"
|
|
2561
|
+
)
|
|
2562
|
+
);
|
|
2563
|
+
}
|
|
2564
|
+
if (statementStart && expectedType === null) {
|
|
2565
|
+
for (const primitive3 of NEOSCRIPT_PRIMITIVE_TYPES) {
|
|
2566
|
+
items.push(
|
|
2567
|
+
completion(
|
|
2568
|
+
primitive3,
|
|
2569
|
+
"keyword",
|
|
2570
|
+
primitive3,
|
|
2571
|
+
"Primitive type",
|
|
2572
|
+
word,
|
|
2573
|
+
snapshot
|
|
2574
|
+
)
|
|
2575
|
+
);
|
|
2576
|
+
}
|
|
2577
|
+
items.push(
|
|
2578
|
+
completion(
|
|
2579
|
+
"Dictionary",
|
|
2580
|
+
"snippet",
|
|
2581
|
+
"Dictionary<string, ${1:T}>",
|
|
2582
|
+
"Dictionary type",
|
|
2583
|
+
word,
|
|
2584
|
+
snapshot,
|
|
2585
|
+
"snippet"
|
|
2513
2586
|
)
|
|
2514
2587
|
);
|
|
2515
2588
|
}
|
|
2516
|
-
items.push(
|
|
2517
|
-
completion(
|
|
2518
|
-
"Dictionary",
|
|
2519
|
-
"snippet",
|
|
2520
|
-
"Dictionary<string, ${1:T}>",
|
|
2521
|
-
"Dictionary type",
|
|
2522
|
-
word,
|
|
2523
|
-
snapshot,
|
|
2524
|
-
"snippet"
|
|
2525
|
-
)
|
|
2526
|
-
);
|
|
2527
2589
|
for (const symbol of scopeAt(snapshot, offset)) {
|
|
2590
|
+
if (expectedType !== null && !isNeoScriptTypeAssignable(
|
|
2591
|
+
symbol.returnType ?? symbol.type,
|
|
2592
|
+
expectedType,
|
|
2593
|
+
snapshot.project
|
|
2594
|
+
)) {
|
|
2595
|
+
continue;
|
|
2596
|
+
}
|
|
2528
2597
|
items.push(symbolCompletion(symbol, word, snapshot));
|
|
2529
2598
|
}
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2599
|
+
if (statementStart && expectedType === null) {
|
|
2600
|
+
for (const type of snapshot.project.typeByName.values()) {
|
|
2601
|
+
if (type.kind === "builtin" && type.name.startsWith("__")) continue;
|
|
2602
|
+
items.push(typeCompletion(type, word, snapshot));
|
|
2603
|
+
}
|
|
2533
2604
|
}
|
|
2534
2605
|
return items;
|
|
2535
2606
|
}
|
|
2607
|
+
function contextualEnumCompletionItems(snapshot, expected, word) {
|
|
2608
|
+
if (expected?.kind !== "named") return [];
|
|
2609
|
+
const type = snapshot.project.typeById.get(expected.typeId);
|
|
2610
|
+
if (type?.kind !== "enum") return [];
|
|
2611
|
+
return type.members.filter((member) => member.kind === "enumMember").map((member) => {
|
|
2612
|
+
const item = symbolCompletion(member, word, snapshot);
|
|
2613
|
+
return {
|
|
2614
|
+
...item,
|
|
2615
|
+
label: `.${member.name}`,
|
|
2616
|
+
insertText: member.name,
|
|
2617
|
+
textEdit: {
|
|
2618
|
+
range: snapshot.source.range(word.start, word.end),
|
|
2619
|
+
newText: member.name
|
|
2620
|
+
}
|
|
2621
|
+
};
|
|
2622
|
+
});
|
|
2623
|
+
}
|
|
2624
|
+
function isContextualCompletionDot(text, wordStart) {
|
|
2625
|
+
if (text[wordStart - 1] !== ".") return false;
|
|
2626
|
+
let cursor = wordStart - 2;
|
|
2627
|
+
while (cursor >= 0 && /\s/.test(text[cursor] ?? "")) cursor--;
|
|
2628
|
+
const beforeDot = text[cursor];
|
|
2629
|
+
if (beforeDot !== void 0 && /[A-Za-z0-9_]/.test(beforeDot)) {
|
|
2630
|
+
const precedingWord = /[A-Za-z_][A-Za-z0-9_]*$/.exec(
|
|
2631
|
+
text.slice(0, cursor + 1)
|
|
2632
|
+
)?.[0];
|
|
2633
|
+
return precedingWord === "return" || precedingWord === "case";
|
|
2634
|
+
}
|
|
2635
|
+
return beforeDot === void 0 || !/[A-Za-z0-9_\])}]/.test(beforeDot);
|
|
2636
|
+
}
|
|
2637
|
+
function expectedTypeAt(snapshot, offset) {
|
|
2638
|
+
const call = [...snapshot.parsed.calls].filter(
|
|
2639
|
+
(candidate) => candidate.openParenOffset < offset && (candidate.closeParenOffset === void 0 || candidate.closeParenOffset >= offset)
|
|
2640
|
+
).sort((left, right) => right.openParenOffset - left.openParenOffset)[0];
|
|
2641
|
+
if (call) {
|
|
2642
|
+
const reference2 = snapshot.parsed.references.find(
|
|
2643
|
+
(candidate) => candidate.range.start.line === call.nameRange.start.line && candidate.range.start.character === call.nameRange.start.character
|
|
2644
|
+
);
|
|
2645
|
+
const resolution = reference2 ? resolveReference(snapshot, reference2) : null;
|
|
2646
|
+
const index = countTopLevelCommas(
|
|
2647
|
+
snapshot.source.text,
|
|
2648
|
+
call.openParenOffset + 1,
|
|
2649
|
+
offset
|
|
2650
|
+
);
|
|
2651
|
+
const parameterType = call.kind === "constructor" ? constructorParameterTypeAt(resolution?.staticType, index, snapshot) : resolution?.symbol?.parameters?.[index]?.type;
|
|
2652
|
+
if (parameterType) {
|
|
2653
|
+
return expectedCollectionElementType(
|
|
2654
|
+
parameterType,
|
|
2655
|
+
snapshot.lexed.tokens,
|
|
2656
|
+
call.openParenOffset + 1,
|
|
2657
|
+
offset
|
|
2658
|
+
);
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2661
|
+
const local = snapshot.parsed.locals.filter((candidate) => {
|
|
2662
|
+
const first = candidate.initializerTokens?.[0];
|
|
2663
|
+
return !candidate.inferred && first !== void 0 && first.start <= offset && offset <= snapshot.source.offsetAt(candidate.declarationRange.end);
|
|
2664
|
+
}).sort((left, right) => right.scopeStart - left.scopeStart)[0];
|
|
2665
|
+
if (local) {
|
|
2666
|
+
const first = local.initializerTokens?.[0];
|
|
2667
|
+
return expectedCollectionElementType(
|
|
2668
|
+
resolveDeclaredType(local, snapshot),
|
|
2669
|
+
snapshot.lexed.tokens,
|
|
2670
|
+
first?.start ?? offset,
|
|
2671
|
+
offset
|
|
2672
|
+
);
|
|
2673
|
+
}
|
|
2674
|
+
const tokens = significantTokensBefore(snapshot.lexed.tokens, offset);
|
|
2675
|
+
const boundary = lastStatementBoundary(tokens);
|
|
2676
|
+
for (let index = tokens.length - 1; index > boundary; index--) {
|
|
2677
|
+
const token = tokens[index];
|
|
2678
|
+
if (token?.kind !== "operator" || token.text !== "=") continue;
|
|
2679
|
+
const left = expressionTokensBefore(tokens, index);
|
|
2680
|
+
const resolution = resolveChain(snapshot, left, offset);
|
|
2681
|
+
if (resolution) {
|
|
2682
|
+
return expectedCollectionElementType(
|
|
2683
|
+
resolution.type,
|
|
2684
|
+
tokens,
|
|
2685
|
+
token.end,
|
|
2686
|
+
offset
|
|
2687
|
+
);
|
|
2688
|
+
}
|
|
2689
|
+
break;
|
|
2690
|
+
}
|
|
2691
|
+
for (let index = tokens.length - 1; index > boundary; index--) {
|
|
2692
|
+
const token = tokens[index];
|
|
2693
|
+
if (token?.kind !== "operator" || token.text !== "==" && token.text !== "!=") {
|
|
2694
|
+
continue;
|
|
2695
|
+
}
|
|
2696
|
+
const left = expressionTokensBefore(tokens, index);
|
|
2697
|
+
const type = resolveChain(snapshot, left, offset)?.type ?? inferExpressionType(left, snapshot, left[0]?.start ?? offset);
|
|
2698
|
+
if (!isUnknownExpectedType(type)) return type;
|
|
2699
|
+
}
|
|
2700
|
+
const futureComparison = expectedTypeFromComparisonRight(snapshot, offset);
|
|
2701
|
+
if (futureComparison) return futureComparison;
|
|
2702
|
+
const tail = tokens.at(-1);
|
|
2703
|
+
if (tail?.kind === "operator" && ["!", "&&", "||"].includes(tail.text) || tail?.kind === "punctuation" && tail.text === "(" && tokens.at(-2)?.kind === "keyword" && ["if", "while"].includes(tokens.at(-2)?.text ?? "")) {
|
|
2704
|
+
return { kind: "primitive", name: "bool" };
|
|
2705
|
+
}
|
|
2706
|
+
const throwToken = tokens.slice(boundary + 1).find((token) => token.kind === "keyword" && token.text === "throw");
|
|
2707
|
+
if (throwToken) return { kind: "primitive", name: "string" };
|
|
2708
|
+
const returnToken = tokens.slice(boundary + 1).find((token) => token.kind === "keyword" && token.text === "return");
|
|
2709
|
+
if (returnToken && snapshot.context.returnType) {
|
|
2710
|
+
return expectedCollectionElementType(
|
|
2711
|
+
snapshot.context.returnType,
|
|
2712
|
+
tokens,
|
|
2713
|
+
returnToken.end,
|
|
2714
|
+
offset
|
|
2715
|
+
);
|
|
2716
|
+
}
|
|
2717
|
+
return null;
|
|
2718
|
+
}
|
|
2719
|
+
function expectedTypeFromComparisonRight(snapshot, offset) {
|
|
2720
|
+
const tokens = snapshot.lexed.tokens.filter(
|
|
2721
|
+
(token) => token.kind !== "comment" && token.kind !== "eof"
|
|
2722
|
+
);
|
|
2723
|
+
let operatorIndex = -1;
|
|
2724
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
2725
|
+
const token = tokens[index];
|
|
2726
|
+
if (!token || token.start < offset) continue;
|
|
2727
|
+
if (token.kind === "punctuation" && [";", "{", "}"].includes(token.text)) {
|
|
2728
|
+
break;
|
|
2729
|
+
}
|
|
2730
|
+
if (token.kind === "operator" && (token.text === "==" || token.text === "!=")) {
|
|
2731
|
+
operatorIndex = index;
|
|
2732
|
+
break;
|
|
2733
|
+
}
|
|
2734
|
+
}
|
|
2735
|
+
if (operatorIndex < 0) return null;
|
|
2736
|
+
const right = [];
|
|
2737
|
+
let depth = 0;
|
|
2738
|
+
for (let index = operatorIndex + 1; index < tokens.length; index++) {
|
|
2739
|
+
const token = tokens[index];
|
|
2740
|
+
if (!token) continue;
|
|
2741
|
+
if (token.kind === "punctuation" && ["(", "["].includes(token.text)) {
|
|
2742
|
+
depth++;
|
|
2743
|
+
} else if (token.kind === "punctuation" && [")", "]"].includes(token.text)) {
|
|
2744
|
+
if (depth === 0) break;
|
|
2745
|
+
depth--;
|
|
2746
|
+
}
|
|
2747
|
+
if (depth === 0 && token.kind === "punctuation" && [";", ",", "{"].includes(token.text)) {
|
|
2748
|
+
break;
|
|
2749
|
+
}
|
|
2750
|
+
right.push(token);
|
|
2751
|
+
}
|
|
2752
|
+
const type = inferExpressionType(right, snapshot, right[0]?.start ?? offset);
|
|
2753
|
+
return isUnknownExpectedType(type) ? null : type;
|
|
2754
|
+
}
|
|
2755
|
+
function isUnknownExpectedType(type) {
|
|
2756
|
+
return type.kind === "primitive" && type.name === "unknown";
|
|
2757
|
+
}
|
|
2758
|
+
function constructorParameterTypeAt(type, index, snapshot) {
|
|
2759
|
+
const declared = (type?.declaredConstructors ?? []).map((constructor2) => constructor2.parameters[index]?.type).filter(
|
|
2760
|
+
(candidate) => candidate !== void 0
|
|
2761
|
+
);
|
|
2762
|
+
if ((type?.declaredConstructors?.length ?? 0) === 0) {
|
|
2763
|
+
return type?.constructorSignature?.parameters[index]?.parameterType;
|
|
2764
|
+
}
|
|
2765
|
+
if (declared.length === 0) return void 0;
|
|
2766
|
+
const first = declared[0];
|
|
2767
|
+
return declared.every(
|
|
2768
|
+
(candidate) => isNeoScriptTypeAssignable(candidate, first, snapshot.project) && isNeoScriptTypeAssignable(first, candidate, snapshot.project)
|
|
2769
|
+
) ? first : void 0;
|
|
2770
|
+
}
|
|
2771
|
+
function expectedCollectionElementType(initial, tokens, start, end) {
|
|
2772
|
+
let expected = initial;
|
|
2773
|
+
const stack = [];
|
|
2774
|
+
for (const token of tokens) {
|
|
2775
|
+
if (token.start < start || token.end > end || token.kind !== "punctuation") {
|
|
2776
|
+
continue;
|
|
2777
|
+
}
|
|
2778
|
+
if (token.text === "[") {
|
|
2779
|
+
stack.push(expected);
|
|
2780
|
+
if (expected.kind === "list" || expected.kind === "set") {
|
|
2781
|
+
expected = expected.elementType;
|
|
2782
|
+
}
|
|
2783
|
+
} else if (token.text === "]") {
|
|
2784
|
+
expected = stack.pop() ?? expected;
|
|
2785
|
+
}
|
|
2786
|
+
}
|
|
2787
|
+
return expected;
|
|
2788
|
+
}
|
|
2789
|
+
function lastStatementBoundary(tokens) {
|
|
2790
|
+
for (let index = tokens.length - 1; index >= 0; index--) {
|
|
2791
|
+
const token = tokens[index];
|
|
2792
|
+
if (token?.kind === "punctuation" && (token.text === ";" || token.text === "{" || token.text === "}")) {
|
|
2793
|
+
return index;
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
return -1;
|
|
2797
|
+
}
|
|
2798
|
+
function isStatementStart(tokens, offset) {
|
|
2799
|
+
const significant = significantTokensBefore(tokens, offset);
|
|
2800
|
+
const tail = significant.at(-1);
|
|
2801
|
+
return tail === void 0 || tail.kind === "punctuation" && (tail.text === ";" || tail.text === "{" || tail.text === "}") || tail.kind === "punctuation" && tail.text === ":";
|
|
2802
|
+
}
|
|
2803
|
+
function statementOnlyKeyword(keyword) {
|
|
2804
|
+
return !["true", "false", "null", "is"].includes(keyword);
|
|
2805
|
+
}
|
|
2806
|
+
function literalKeywordMatchesExpectedType(keyword, expected) {
|
|
2807
|
+
if (keyword === "true" || keyword === "false") {
|
|
2808
|
+
return expected.kind === "primitive" && expected.name === "bool";
|
|
2809
|
+
}
|
|
2810
|
+
if (keyword === "null") return expected.nullable === true;
|
|
2811
|
+
return false;
|
|
2812
|
+
}
|
|
2536
2813
|
function catchFilterCompletionItems(snapshot, offset, word) {
|
|
2537
2814
|
const prefix = snapshot.source.text.slice(0, offset);
|
|
2538
2815
|
if (!/\bcatch\s*\(\s*string\s+[A-Za-z_][A-Za-z0-9_]*\s*\)\s*$/.test(prefix)) {
|
|
@@ -2682,16 +2959,24 @@ function typeCompletionItems(snapshot, word) {
|
|
|
2682
2959
|
}
|
|
2683
2960
|
return items;
|
|
2684
2961
|
}
|
|
2685
|
-
function constructorCompletionItems(snapshot, word) {
|
|
2962
|
+
function constructorCompletionItems(snapshot, word, expected = null) {
|
|
2686
2963
|
return [...snapshot.project.typeByName.values()].flatMap((type) => {
|
|
2687
2964
|
const signature = type.constructorSignature;
|
|
2688
|
-
|
|
2965
|
+
const declared = type.declaredConstructors ?? [];
|
|
2966
|
+
if (!signature && declared.length === 0) return [];
|
|
2967
|
+
if (expected !== null && !isNeoScriptTypeAssignable(
|
|
2968
|
+
{ kind: "named", typeId: type.id },
|
|
2969
|
+
expected,
|
|
2970
|
+
snapshot.project
|
|
2971
|
+
)) {
|
|
2972
|
+
return [];
|
|
2973
|
+
}
|
|
2689
2974
|
const insertText = `${type.name}(`;
|
|
2690
2975
|
return [
|
|
2691
2976
|
{
|
|
2692
2977
|
label: type.name,
|
|
2693
2978
|
kind: "class",
|
|
2694
|
-
detail: formatConstructorSignature(type.name, signature, snapshot),
|
|
2979
|
+
detail: declared.length > 0 ? `${type.name} \u2014 ${declared.length} declared constructor${declared.length === 1 ? "" : "s"}` : signature ? formatConstructorSignature(type.name, signature, snapshot) : type.name,
|
|
2695
2980
|
...type.documentation ? { documentation: type.documentation } : {},
|
|
2696
2981
|
insertText,
|
|
2697
2982
|
textEdit: {
|
|
@@ -2764,6 +3049,22 @@ function membersForType(snapshot, type) {
|
|
|
2764
3049
|
return [];
|
|
2765
3050
|
}
|
|
2766
3051
|
function resolveReference(snapshot, reference2) {
|
|
3052
|
+
if (!reference2.member && (isTypeAnnotationReference(snapshot, reference2) || isConstructorTypeReference(snapshot, reference2))) {
|
|
3053
|
+
const type = snapshot.project.typeByName.get(reference2.name);
|
|
3054
|
+
if (type) {
|
|
3055
|
+
return {
|
|
3056
|
+
type: { kind: "named", typeId: type.id },
|
|
3057
|
+
staticType: type
|
|
3058
|
+
};
|
|
3059
|
+
}
|
|
3060
|
+
}
|
|
3061
|
+
if (reference2.member && isContextualCompletionDot(snapshot.source.text, reference2.start)) {
|
|
3062
|
+
const contextual = resolveContextualExpectedEnumReference(
|
|
3063
|
+
snapshot,
|
|
3064
|
+
reference2
|
|
3065
|
+
);
|
|
3066
|
+
if (contextual) return contextual;
|
|
3067
|
+
}
|
|
2767
3068
|
const tokens = snapshot.lexed.tokens.filter(
|
|
2768
3069
|
(token) => token.kind !== "comment" && token.kind !== "eof" && token.end <= reference2.end
|
|
2769
3070
|
);
|
|
@@ -2814,6 +3115,16 @@ function resolveContextualSwitchEnumReference(snapshot, reference2) {
|
|
|
2814
3115
|
);
|
|
2815
3116
|
return symbol ? { type: symbol.type, symbol } : null;
|
|
2816
3117
|
}
|
|
3118
|
+
function resolveContextualExpectedEnumReference(snapshot, reference2) {
|
|
3119
|
+
const expected = expectedTypeAt(snapshot, reference2.start);
|
|
3120
|
+
if (expected?.kind !== "named") return null;
|
|
3121
|
+
const type = snapshot.project.typeById.get(expected.typeId);
|
|
3122
|
+
if (type?.kind !== "enum") return null;
|
|
3123
|
+
const symbol = type.members.find(
|
|
3124
|
+
(member) => member.kind === "enumMember" && member.name === reference2.name
|
|
3125
|
+
);
|
|
3126
|
+
return symbol ? { type: symbol.type, symbol } : null;
|
|
3127
|
+
}
|
|
2817
3128
|
function switchCaseEnumContext(snapshot, offset) {
|
|
2818
3129
|
for (const statement of snapshot.parsed.switches) {
|
|
2819
3130
|
for (const section of statement.sections) {
|
|
@@ -3067,6 +3378,19 @@ function scopeAt(snapshot, offset) {
|
|
|
3067
3378
|
)
|
|
3068
3379
|
);
|
|
3069
3380
|
}
|
|
3381
|
+
const declaringType = snapshot.context.declaringType;
|
|
3382
|
+
if (snapshot.context.implicitMemberAccess === true && declaringType?.kind === "named") {
|
|
3383
|
+
const owner = snapshot.project.typeById.get(declaringType.typeId);
|
|
3384
|
+
if (owner) {
|
|
3385
|
+
for (const member of owner.members) {
|
|
3386
|
+
if (snapshot.context.staticMember === true && member.static !== true) {
|
|
3387
|
+
continue;
|
|
3388
|
+
}
|
|
3389
|
+
if (!isMemberCompletionAccessible(snapshot, owner, member)) continue;
|
|
3390
|
+
result.push(withScope(member, 0, snapshot.source.text.length));
|
|
3391
|
+
}
|
|
3392
|
+
}
|
|
3393
|
+
}
|
|
3070
3394
|
if (effectiveDocumentKind(snapshot, offset) === "setter" && snapshot.context.returnType) {
|
|
3071
3395
|
result.push(
|
|
3072
3396
|
withScope(
|
|
@@ -3903,6 +4227,11 @@ function isTypeAnnotationReference(snapshot, reference2) {
|
|
|
3903
4227
|
(local) => local.typeTokens.some((token) => token.start === reference2.start)
|
|
3904
4228
|
);
|
|
3905
4229
|
}
|
|
4230
|
+
function isConstructorTypeReference(snapshot, reference2) {
|
|
4231
|
+
return snapshot.parsed.calls.some(
|
|
4232
|
+
(call) => call.kind === "constructor" && call.nameRange.start.line === reference2.range.start.line && call.nameRange.start.character === reference2.range.start.character
|
|
4233
|
+
);
|
|
4234
|
+
}
|
|
3906
4235
|
var IDENTIFIER_PATTERN, RESERVED_NAMES;
|
|
3907
4236
|
var init_analyzer = __esm({
|
|
3908
4237
|
"../packages/neoscript-language/src/analyzer.ts"() {
|
|
@@ -21592,6 +21921,7 @@ function compileProjectSourceBodies(documents) {
|
|
|
21592
21921
|
const graph = buildProjectGraph(documents);
|
|
21593
21922
|
const projectIndex = createProjectIndex(graph.project);
|
|
21594
21923
|
const bodies = [];
|
|
21924
|
+
const contexts = [];
|
|
21595
21925
|
const diagnostics = [];
|
|
21596
21926
|
for (const [uri, document] of documents) {
|
|
21597
21927
|
if (document.kind !== "definition") continue;
|
|
@@ -21636,6 +21966,7 @@ function compileProjectSourceBodies(documents) {
|
|
|
21636
21966
|
},
|
|
21637
21967
|
projectIndex,
|
|
21638
21968
|
bodies,
|
|
21969
|
+
contexts,
|
|
21639
21970
|
diagnostics
|
|
21640
21971
|
});
|
|
21641
21972
|
}
|
|
@@ -21671,6 +22002,7 @@ function compileProjectSourceBodies(documents) {
|
|
|
21671
22002
|
},
|
|
21672
22003
|
projectIndex,
|
|
21673
22004
|
bodies,
|
|
22005
|
+
contexts,
|
|
21674
22006
|
diagnostics
|
|
21675
22007
|
});
|
|
21676
22008
|
}
|
|
@@ -21711,6 +22043,7 @@ function compileProjectSourceBodies(documents) {
|
|
|
21711
22043
|
},
|
|
21712
22044
|
projectIndex,
|
|
21713
22045
|
bodies,
|
|
22046
|
+
contexts,
|
|
21714
22047
|
diagnostics
|
|
21715
22048
|
});
|
|
21716
22049
|
continue;
|
|
@@ -21735,15 +22068,24 @@ function compileProjectSourceBodies(documents) {
|
|
|
21735
22068
|
context: { ...baseContext2, kind: unit },
|
|
21736
22069
|
projectIndex,
|
|
21737
22070
|
bodies,
|
|
22071
|
+
contexts,
|
|
21738
22072
|
diagnostics
|
|
21739
22073
|
});
|
|
21740
22074
|
}
|
|
21741
22075
|
}
|
|
21742
22076
|
}
|
|
21743
22077
|
}
|
|
21744
|
-
return { project: graph.project, bodies, diagnostics };
|
|
22078
|
+
return { project: graph.project, bodies, contexts, diagnostics };
|
|
21745
22079
|
}
|
|
21746
22080
|
function compileBody(args) {
|
|
22081
|
+
args.contexts.push({
|
|
22082
|
+
uri: args.uri,
|
|
22083
|
+
ownerName: args.owner.declaration.name,
|
|
22084
|
+
memberName: args.memberName,
|
|
22085
|
+
unit: args.unit,
|
|
22086
|
+
range: args.range,
|
|
22087
|
+
context: args.context
|
|
22088
|
+
});
|
|
21747
22089
|
const source = new SourceText(args.document.sourceText);
|
|
21748
22090
|
const start = source.offsetAt(args.range.start);
|
|
21749
22091
|
const end = source.offsetAt(args.range.end);
|
|
@@ -24063,6 +24405,7 @@ function analyzeNeoProjectSources(inputs, parsedDocuments = /* @__PURE__ */ new
|
|
|
24063
24405
|
symbols,
|
|
24064
24406
|
project: bodyCompilation.project,
|
|
24065
24407
|
compiledBodies: bodyCompilation.bodies,
|
|
24408
|
+
bodyContexts: bodyCompilation.contexts,
|
|
24066
24409
|
diagnostics
|
|
24067
24410
|
};
|
|
24068
24411
|
}
|
|
@@ -24890,9 +25233,25 @@ var init_quick_fixes = __esm({
|
|
|
24890
25233
|
|
|
24891
25234
|
// ../packages/neoscript-language/src/project-source-language-features.ts
|
|
24892
25235
|
function projectDiagnostics(analysis, uri) {
|
|
24893
|
-
return analysis.diagnostics.filter((diagnostic) => diagnostic.uri === uri).map((
|
|
25236
|
+
return analysis.diagnostics.filter((diagnostic) => diagnostic.uri === uri).map((diagnostic) => ({
|
|
25237
|
+
range: diagnostic.range,
|
|
25238
|
+
severity: diagnostic.severity,
|
|
25239
|
+
message: diagnostic.message,
|
|
25240
|
+
...diagnostic.code === void 0 ? {} : { code: diagnostic.code },
|
|
25241
|
+
...diagnostic.source === void 0 ? {} : { source: diagnostic.source },
|
|
25242
|
+
...diagnostic.relatedInformation === void 0 ? {} : { relatedInformation: diagnostic.relatedInformation },
|
|
25243
|
+
...diagnostic.suggestions === void 0 ? {} : { suggestions: diagnostic.suggestions }
|
|
25244
|
+
}));
|
|
24894
25245
|
}
|
|
24895
25246
|
function projectCompletions(analysis, document, position) {
|
|
25247
|
+
const body = projectBodySnapshotAt(analysis, document, position);
|
|
25248
|
+
if (body) return complete(body, position);
|
|
25249
|
+
const annotations = projectAnnotationCompletions(
|
|
25250
|
+
analysis,
|
|
25251
|
+
document,
|
|
25252
|
+
position
|
|
25253
|
+
);
|
|
25254
|
+
if (annotations) return { isIncomplete: false, items: annotations };
|
|
24896
25255
|
const members = projectMemberCompletions(analysis, document, position);
|
|
24897
25256
|
if (members) {
|
|
24898
25257
|
return {
|
|
@@ -24977,13 +25336,126 @@ function projectCompletions(analysis, document, position) {
|
|
|
24977
25336
|
if (initializerMembers) {
|
|
24978
25337
|
return { isIncomplete: false, items: initializerMembers };
|
|
24979
25338
|
}
|
|
25339
|
+
const contextualEnum = projectContextualEnumCompletions(
|
|
25340
|
+
analysis,
|
|
25341
|
+
document,
|
|
25342
|
+
position
|
|
25343
|
+
);
|
|
25344
|
+
if (contextualEnum) {
|
|
25345
|
+
return { isIncomplete: false, items: contextualEnum };
|
|
25346
|
+
}
|
|
25347
|
+
return projectFallbackCompletions(analysis, document, position);
|
|
25348
|
+
}
|
|
25349
|
+
function projectAnnotationCompletions(analysis, document, position) {
|
|
25350
|
+
const source = new SourceText(document.text);
|
|
25351
|
+
const offset = source.offsetAt(position);
|
|
25352
|
+
const word = projectWordRange(document.text, offset);
|
|
25353
|
+
if (document.text[word.start - 1] !== "@") return null;
|
|
25354
|
+
if (initializerRootAt(analysis, document, position)) return [];
|
|
25355
|
+
const names = projectAnnotationNamesAt(analysis, document, position);
|
|
25356
|
+
return names.map((name) => ({
|
|
25357
|
+
label: `@${name}`,
|
|
25358
|
+
kind: "snippet",
|
|
25359
|
+
insertText: name,
|
|
25360
|
+
textEdit: {
|
|
25361
|
+
range: source.range(word.start, word.end),
|
|
25362
|
+
newText: name
|
|
25363
|
+
}
|
|
25364
|
+
}));
|
|
25365
|
+
}
|
|
25366
|
+
function projectAnnotationNamesAt(analysis, document, position) {
|
|
25367
|
+
const source = analysis.documents.get(document.uri);
|
|
25368
|
+
if (!source) return [];
|
|
25369
|
+
if (document.languageId === "neoflow") {
|
|
25370
|
+
return ["id", "primary", "incomplete"];
|
|
25371
|
+
}
|
|
25372
|
+
const offset = new SourceText(document.text).offsetAt(position);
|
|
25373
|
+
const followingDeclaration = /^\s*(class|interface|enum)\b([^{}]*)/.exec(
|
|
25374
|
+
document.text.slice(offset)
|
|
25375
|
+
);
|
|
25376
|
+
if (followingDeclaration?.[1] === "class") {
|
|
25377
|
+
return [
|
|
25378
|
+
"id",
|
|
25379
|
+
"hidden",
|
|
25380
|
+
"settings",
|
|
25381
|
+
"storage",
|
|
25382
|
+
"relations",
|
|
25383
|
+
.../\bDialogue\b/.test(followingDeclaration[2] ?? "") ? ["incomplete"] : []
|
|
25384
|
+
];
|
|
25385
|
+
}
|
|
25386
|
+
if (followingDeclaration) return ["id"];
|
|
25387
|
+
const containing = declarationContaining(source, position);
|
|
25388
|
+
if (containing?.kind === "enum") return ["id"];
|
|
25389
|
+
if (containing?.kind === "class" || containing?.kind === "interface") {
|
|
25390
|
+
if (positionCompare(position, containing.nameRange.start) < 0) {
|
|
25391
|
+
return projectDeclarationAnnotationNames(containing);
|
|
25392
|
+
}
|
|
25393
|
+
const member = containing.members.find(
|
|
25394
|
+
(candidate) => rangeContains(candidate.range, position) || positionCompare(position, candidate.range.start) <= 0
|
|
25395
|
+
);
|
|
25396
|
+
return projectMemberAnnotationNames(member);
|
|
25397
|
+
}
|
|
25398
|
+
const following = source.declarations.find(
|
|
25399
|
+
(declaration) => positionCompare(position, declaration.range.start) <= 0
|
|
25400
|
+
);
|
|
25401
|
+
if (following) return projectDeclarationAnnotationNames(following);
|
|
25402
|
+
return ["id"];
|
|
25403
|
+
}
|
|
25404
|
+
function projectDeclarationAnnotationNames(declaration) {
|
|
25405
|
+
if (declaration.kind !== "class") return ["id"];
|
|
25406
|
+
return [
|
|
25407
|
+
"id",
|
|
25408
|
+
"hidden",
|
|
25409
|
+
"settings",
|
|
25410
|
+
"storage",
|
|
25411
|
+
"relations",
|
|
25412
|
+
...declaration.baseTypes.some((type) => type.name === "Dialogue") ? ["incomplete"] : []
|
|
25413
|
+
];
|
|
25414
|
+
}
|
|
25415
|
+
function projectMemberAnnotationNames(member) {
|
|
25416
|
+
const names = ["id", "settings", "storage", "locked"];
|
|
25417
|
+
if (member?.type.name === "List") names.push("index", "column");
|
|
25418
|
+
return names;
|
|
25419
|
+
}
|
|
25420
|
+
function projectContextualEnumCompletions(analysis, document, position) {
|
|
25421
|
+
const source = new SourceText(document.text);
|
|
25422
|
+
const offset = source.offsetAt(position);
|
|
25423
|
+
const word = projectWordRange(document.text, offset);
|
|
25424
|
+
if (!isContextualDot(document.text, word.start)) return null;
|
|
25425
|
+
const typeName = constructionSiteAt(
|
|
25426
|
+
analysis,
|
|
25427
|
+
document,
|
|
25428
|
+
position
|
|
25429
|
+
)?.expectedTypeName;
|
|
25430
|
+
if (!typeName) return null;
|
|
25431
|
+
const type = analysis.project.types.find(
|
|
25432
|
+
(candidate) => candidate.kind === "enum" && candidate.name === typeName
|
|
25433
|
+
);
|
|
25434
|
+
if (!type) return null;
|
|
25435
|
+
return type.members.filter((member) => member.kind === "enumMember").map((member) => ({
|
|
25436
|
+
label: `.${member.name}`,
|
|
25437
|
+
kind: "enumMember",
|
|
25438
|
+
detail: type.name,
|
|
25439
|
+
insertText: member.name,
|
|
25440
|
+
textEdit: {
|
|
25441
|
+
range: source.range(word.start, word.end),
|
|
25442
|
+
newText: member.name
|
|
25443
|
+
},
|
|
25444
|
+
symbolId: member.id
|
|
25445
|
+
}));
|
|
25446
|
+
}
|
|
25447
|
+
function projectFallbackCompletions(analysis, document, position) {
|
|
24980
25448
|
const items = /* @__PURE__ */ new Map();
|
|
24981
|
-
|
|
24982
|
-
|
|
24983
|
-
|
|
24984
|
-
|
|
24985
|
-
|
|
24986
|
-
|
|
25449
|
+
const construction = constructionSiteAt(analysis, document, position);
|
|
25450
|
+
const recoveredInitializerType = projectInitializerExpectedTypeNameAt(
|
|
25451
|
+
document,
|
|
25452
|
+
position
|
|
25453
|
+
);
|
|
25454
|
+
const expression = construction !== null || recoveredInitializerType !== null;
|
|
25455
|
+
const expectedTypeName = construction?.expectedTypeName ?? recoveredInitializerType;
|
|
25456
|
+
const afterNew = expression && projectAfterNewAt(document, position);
|
|
25457
|
+
const typePosition = projectTypePositionAt(analysis, document, position);
|
|
25458
|
+
const keywords = expression ? afterNew ? [] : ["new"] : typePosition ? [] : [
|
|
24987
25459
|
"class",
|
|
24988
25460
|
"interface",
|
|
24989
25461
|
"enum",
|
|
@@ -24993,16 +25465,20 @@ function projectCompletions(analysis, document, position) {
|
|
|
24993
25465
|
"override",
|
|
24994
25466
|
"readonly",
|
|
24995
25467
|
"virtual",
|
|
24996
|
-
"static"
|
|
24997
|
-
|
|
24998
|
-
|
|
25468
|
+
"static"
|
|
25469
|
+
];
|
|
25470
|
+
for (const keyword of keywords) {
|
|
24999
25471
|
items.set(keyword, {
|
|
25000
25472
|
label: keyword,
|
|
25001
|
-
kind:
|
|
25473
|
+
kind: "keyword",
|
|
25002
25474
|
insertText: keyword
|
|
25003
25475
|
});
|
|
25004
25476
|
}
|
|
25005
|
-
for (const typeName of
|
|
25477
|
+
for (const typeName of [
|
|
25478
|
+
...NEOSCRIPT_PRIMITIVE_TYPES,
|
|
25479
|
+
...NEOSCRIPT_BUILTIN_TYPES
|
|
25480
|
+
]) {
|
|
25481
|
+
if (expression) continue;
|
|
25006
25482
|
items.set(typeName, {
|
|
25007
25483
|
label: typeName,
|
|
25008
25484
|
kind: "class",
|
|
@@ -25010,10 +25486,31 @@ function projectCompletions(analysis, document, position) {
|
|
|
25010
25486
|
insertText: typeName
|
|
25011
25487
|
});
|
|
25012
25488
|
}
|
|
25489
|
+
const source = analysis.documents.get(document.uri);
|
|
25490
|
+
const owner = source ? declarationContaining(source, position)?.name : void 0;
|
|
25013
25491
|
for (const symbol of analysis.symbols) {
|
|
25014
25492
|
if (symbol.scopeRange && (symbol.location.uri !== document.uri || !rangeContains(symbol.scopeRange, position))) {
|
|
25015
25493
|
continue;
|
|
25016
25494
|
}
|
|
25495
|
+
const isType = symbol.kind === "class" || symbol.kind === "interface" || symbol.kind === "enum" || symbol.kind === "genericParameter";
|
|
25496
|
+
if (typePosition && !isType) continue;
|
|
25497
|
+
if (expression) {
|
|
25498
|
+
if (afterNew) {
|
|
25499
|
+
if (!isType || symbol.kind !== "class" || expectedTypeName !== null && !projectTypeNameAssignable(analysis, symbol.name, expectedTypeName)) {
|
|
25500
|
+
continue;
|
|
25501
|
+
}
|
|
25502
|
+
} else {
|
|
25503
|
+
const isValue = symbol.kind === "parameter" || symbol.kind === "flowBinding" || symbol.kind === "graphChild" || symbol.kind === "global" || symbol.kind === "member" && symbol.ownerName === owner;
|
|
25504
|
+
if (!isValue) continue;
|
|
25505
|
+
if (expectedTypeName !== null && (symbol.detail === void 0 || !projectTypeNameAssignable(
|
|
25506
|
+
analysis,
|
|
25507
|
+
symbol.detail,
|
|
25508
|
+
expectedTypeName
|
|
25509
|
+
))) {
|
|
25510
|
+
continue;
|
|
25511
|
+
}
|
|
25512
|
+
}
|
|
25513
|
+
}
|
|
25017
25514
|
const kind = projectCompletionKind(symbol);
|
|
25018
25515
|
items.set(symbol.name, {
|
|
25019
25516
|
label: symbol.name,
|
|
@@ -25025,6 +25522,168 @@ function projectCompletions(analysis, document, position) {
|
|
|
25025
25522
|
}
|
|
25026
25523
|
return { isIncomplete: false, items: [...items.values()] };
|
|
25027
25524
|
}
|
|
25525
|
+
function projectInitializerExpectedTypeNameAt(document, position) {
|
|
25526
|
+
const prefix = documentPrefix(document.text, position);
|
|
25527
|
+
const line = prefix.slice(prefix.lastIndexOf("\n") + 1);
|
|
25528
|
+
return /^(?:\s*@\w+(?:\([^)]*\))?)*\s*(?:(?:public|protected|private|abstract|async|native|override|readonly|sealed|static|virtual)\s+)*([A-Za-z_][A-Za-z0-9_]*)(?:\s*<[^>]*>)?\s+[A-Za-z_][A-Za-z0-9_]*\s*=\s*[^;]*$/.exec(
|
|
25529
|
+
line
|
|
25530
|
+
)?.[1] ?? null;
|
|
25531
|
+
}
|
|
25532
|
+
function projectAfterNewAt(document, position) {
|
|
25533
|
+
const tokens = tokensBeforePosition(document.text, position);
|
|
25534
|
+
let cursor = tokens.length - 1;
|
|
25535
|
+
const current = tokens[cursor];
|
|
25536
|
+
const offset = new SourceText(document.text).offsetAt(position);
|
|
25537
|
+
if (current?.kind === "identifier" && current.text !== "new" && /[A-Za-z0-9_]$/.test(document.text.slice(0, offset))) {
|
|
25538
|
+
cursor--;
|
|
25539
|
+
}
|
|
25540
|
+
return tokens[cursor]?.text === "new";
|
|
25541
|
+
}
|
|
25542
|
+
function projectTypeNameAssignable(analysis, sourceName, targetName) {
|
|
25543
|
+
if (sourceName === targetName) return true;
|
|
25544
|
+
if (sourceName === "int" && (targetName === "float" || targetName === "decimal")) {
|
|
25545
|
+
return true;
|
|
25546
|
+
}
|
|
25547
|
+
const source = analysis.project.types.find(
|
|
25548
|
+
(type) => type.name === sourceName
|
|
25549
|
+
);
|
|
25550
|
+
const target = analysis.project.types.find(
|
|
25551
|
+
(type) => type.name === targetName
|
|
25552
|
+
);
|
|
25553
|
+
if (!source || !target) return false;
|
|
25554
|
+
const visited = /* @__PURE__ */ new Set();
|
|
25555
|
+
const derivesFrom = (typeId) => {
|
|
25556
|
+
if (typeId === target.id) return true;
|
|
25557
|
+
if (visited.has(typeId)) return false;
|
|
25558
|
+
visited.add(typeId);
|
|
25559
|
+
const type = analysis.project.types.find(
|
|
25560
|
+
(candidate) => candidate.id === typeId
|
|
25561
|
+
);
|
|
25562
|
+
return [
|
|
25563
|
+
...type?.baseTypeIds ?? [],
|
|
25564
|
+
...type?.interfaceTypeIds ?? []
|
|
25565
|
+
].some(derivesFrom);
|
|
25566
|
+
};
|
|
25567
|
+
return derivesFrom(source.id);
|
|
25568
|
+
}
|
|
25569
|
+
function projectBodySnapshotAt(analysis, document, position) {
|
|
25570
|
+
const body = analysis.bodyContexts.find(
|
|
25571
|
+
(candidate) => candidate.uri === document.uri && rangeContains(candidate.range, position)
|
|
25572
|
+
);
|
|
25573
|
+
return body ? projectBodySnapshot(analysis, document, body) : null;
|
|
25574
|
+
}
|
|
25575
|
+
function projectBodySnapshot(analysis, document, body) {
|
|
25576
|
+
let snapshots = PROJECT_BODY_SNAPSHOT_CACHE.get(analysis);
|
|
25577
|
+
if (!snapshots) {
|
|
25578
|
+
snapshots = /* @__PURE__ */ new Map();
|
|
25579
|
+
PROJECT_BODY_SNAPSHOT_CACHE.set(analysis, snapshots);
|
|
25580
|
+
}
|
|
25581
|
+
const cached = snapshots.get(body);
|
|
25582
|
+
if (cached) return cached;
|
|
25583
|
+
const source = new SourceText(document.text);
|
|
25584
|
+
const start = source.offsetAt(body.range.start);
|
|
25585
|
+
const end = source.offsetAt(body.range.end);
|
|
25586
|
+
const text = maskOutsideProjectBody(document.text, start, end);
|
|
25587
|
+
const syntax = analyzeNeoScriptSyntax(text, body.context.kind);
|
|
25588
|
+
const context = { ...body.context, project: analysis.project };
|
|
25589
|
+
const snapshot = {
|
|
25590
|
+
uri: document.uri,
|
|
25591
|
+
source: syntax.lexed.source,
|
|
25592
|
+
lexed: syntax.lexed,
|
|
25593
|
+
parsed: syntax.parsed,
|
|
25594
|
+
context,
|
|
25595
|
+
project: buildProjectIndexWithBuiltins(context)
|
|
25596
|
+
};
|
|
25597
|
+
snapshots.set(body, snapshot);
|
|
25598
|
+
return snapshot;
|
|
25599
|
+
}
|
|
25600
|
+
function maskOutsideProjectBody(text, start, end) {
|
|
25601
|
+
let masked = "";
|
|
25602
|
+
for (let index = 0; index < text.length; index++) {
|
|
25603
|
+
const character = text[index] ?? "";
|
|
25604
|
+
masked += index >= start && index < end || character === "\n" || character === "\r" ? character : " ";
|
|
25605
|
+
}
|
|
25606
|
+
return masked;
|
|
25607
|
+
}
|
|
25608
|
+
function projectWordRange(text, offset) {
|
|
25609
|
+
let start = offset;
|
|
25610
|
+
let end = offset;
|
|
25611
|
+
while (start > 0 && /[A-Za-z0-9_]/.test(text[start - 1] ?? "")) start--;
|
|
25612
|
+
while (end < text.length && /[A-Za-z0-9_]/.test(text[end] ?? "")) end++;
|
|
25613
|
+
return { start, end };
|
|
25614
|
+
}
|
|
25615
|
+
function isContextualDot(text, wordStart) {
|
|
25616
|
+
if (text[wordStart - 1] !== ".") return false;
|
|
25617
|
+
let cursor = wordStart - 2;
|
|
25618
|
+
while (cursor >= 0 && /\s/.test(text[cursor] ?? "")) cursor--;
|
|
25619
|
+
const beforeDot = text[cursor];
|
|
25620
|
+
if (beforeDot !== void 0 && /[A-Za-z0-9_]/.test(beforeDot)) {
|
|
25621
|
+
const precedingWord = /[A-Za-z_][A-Za-z0-9_]*$/.exec(
|
|
25622
|
+
text.slice(0, cursor + 1)
|
|
25623
|
+
)?.[0];
|
|
25624
|
+
return precedingWord === "return" || precedingWord === "case";
|
|
25625
|
+
}
|
|
25626
|
+
return beforeDot === void 0 || !/[A-Za-z0-9_\])}]/.test(beforeDot);
|
|
25627
|
+
}
|
|
25628
|
+
function projectTypePositionAt(analysis, document, position) {
|
|
25629
|
+
const source = analysis.documents.get(document.uri);
|
|
25630
|
+
if (source && sourceTypeAt(source, position)) return true;
|
|
25631
|
+
const prefix = documentPrefix(document.text, position);
|
|
25632
|
+
const line = prefix.slice(prefix.lastIndexOf("\n") + 1);
|
|
25633
|
+
return /(?:^|[{:;,])\s*(?:(?:public|protected|private|abstract|async|native|override|readonly|sealed|static|virtual)\s+)*[A-Za-z_][A-Za-z0-9_]*(?:\s*<[^;={}()]*)?$/.test(
|
|
25634
|
+
line
|
|
25635
|
+
);
|
|
25636
|
+
}
|
|
25637
|
+
function sourceTypeAt(document, position) {
|
|
25638
|
+
const contains2 = (type) => {
|
|
25639
|
+
for (const argument2 of type.typeArguments) {
|
|
25640
|
+
const nested = contains2(argument2);
|
|
25641
|
+
if (nested) return nested;
|
|
25642
|
+
}
|
|
25643
|
+
return rangeContains(type.range, position) ? type : null;
|
|
25644
|
+
};
|
|
25645
|
+
const parameterType = (parameters) => {
|
|
25646
|
+
for (const parameter4 of parameters ?? []) {
|
|
25647
|
+
const match = contains2(parameter4.type);
|
|
25648
|
+
if (match) return match;
|
|
25649
|
+
}
|
|
25650
|
+
return null;
|
|
25651
|
+
};
|
|
25652
|
+
for (const declaration of document.declarations) {
|
|
25653
|
+
if (declaration.kind === "global") {
|
|
25654
|
+
const match = contains2(declaration.type);
|
|
25655
|
+
if (match) return match;
|
|
25656
|
+
continue;
|
|
25657
|
+
}
|
|
25658
|
+
if (declaration.kind === "enum") continue;
|
|
25659
|
+
for (const base of declaration.baseTypes) {
|
|
25660
|
+
const match = contains2(base);
|
|
25661
|
+
if (match) return match;
|
|
25662
|
+
}
|
|
25663
|
+
if (declaration.kind === "class") {
|
|
25664
|
+
const header = parameterType(declaration.headerParameters);
|
|
25665
|
+
if (header) return header;
|
|
25666
|
+
for (const generic of declaration.genericParameters) {
|
|
25667
|
+
if (!generic.constraint) continue;
|
|
25668
|
+
const match = contains2(generic.constraint);
|
|
25669
|
+
if (match) return match;
|
|
25670
|
+
}
|
|
25671
|
+
for (const constructor2 of declaration.constructors) {
|
|
25672
|
+
const match = parameterType(constructor2.parameters);
|
|
25673
|
+
if (match) return match;
|
|
25674
|
+
}
|
|
25675
|
+
}
|
|
25676
|
+
for (const member of declaration.members) {
|
|
25677
|
+
const memberType2 = contains2(member.type);
|
|
25678
|
+
if (memberType2) return memberType2;
|
|
25679
|
+
if (member.kind === "function") {
|
|
25680
|
+
const match = parameterType(member.parameters);
|
|
25681
|
+
if (match) return match;
|
|
25682
|
+
}
|
|
25683
|
+
}
|
|
25684
|
+
}
|
|
25685
|
+
return null;
|
|
25686
|
+
}
|
|
25028
25687
|
function projectMemberCompletions(analysis, document, position) {
|
|
25029
25688
|
const tokens = projectTokens(document).filter(
|
|
25030
25689
|
(token) => token.kind !== "eof" && token.kind !== "comment" && positionCompare(token.range.start, position) <= 0
|
|
@@ -25262,6 +25921,8 @@ function activeNamedArgument(text, position) {
|
|
|
25262
25921
|
return /([A-Za-z_][A-Za-z0-9_]*)\s*:\s*\.[A-Za-z0-9_]*$/.exec(prefix)?.[1] ?? null;
|
|
25263
25922
|
}
|
|
25264
25923
|
function projectHover(analysis, document, position) {
|
|
25924
|
+
const body = projectBodySnapshotAt(analysis, document, position);
|
|
25925
|
+
if (body) return hover(body, position);
|
|
25265
25926
|
const resolved = projectSymbolAt(analysis, document, position);
|
|
25266
25927
|
if (!resolved) return null;
|
|
25267
25928
|
const owner = resolved.symbol.ownerName ? ` on \`${resolved.symbol.ownerName}\`` : "";
|
|
@@ -25309,6 +25970,8 @@ Construct with \`new(${parameters})\` \u2014 this class declares a required cons
|
|
|
25309
25970
|
${lines.join("\n\n")}`;
|
|
25310
25971
|
}
|
|
25311
25972
|
function projectDefinition(analysis, document, position) {
|
|
25973
|
+
const body = projectBodySnapshotAt(analysis, document, position);
|
|
25974
|
+
if (body) return definition(body, position);
|
|
25312
25975
|
const resolved = projectSymbolAt(analysis, document, position);
|
|
25313
25976
|
return resolved ? [resolved.symbol.location] : [];
|
|
25314
25977
|
}
|
|
@@ -25375,6 +26038,8 @@ function requiredDestinationEdit(document, initializer, destination) {
|
|
|
25375
26038
|
return open && close ? trailingBlockReturnEdit(document.text, open, close, destination) : null;
|
|
25376
26039
|
}
|
|
25377
26040
|
function projectSignatureHelp(analysis, document, position) {
|
|
26041
|
+
const body = projectBodySnapshotAt(analysis, document, position);
|
|
26042
|
+
if (body) return signatureHelp(body, position);
|
|
25378
26043
|
const tokens = tokensBeforePosition(document.text, position);
|
|
25379
26044
|
const openIndex = activeCallOpenIndex(tokens, position);
|
|
25380
26045
|
if (openIndex < 0) return null;
|
|
@@ -25436,6 +26101,9 @@ function projectSignatureHelp(analysis, document, position) {
|
|
|
25436
26101
|
};
|
|
25437
26102
|
}
|
|
25438
26103
|
function projectInlayHints(analysis, document, range2) {
|
|
26104
|
+
const bodyContexts = analysis.bodyContexts.filter(
|
|
26105
|
+
(body) => body.uri === document.uri
|
|
26106
|
+
);
|
|
25439
26107
|
const tokens = lex(document.text).tokens.filter(
|
|
25440
26108
|
(token) => token.kind !== "comment" && token.kind !== "eof"
|
|
25441
26109
|
);
|
|
@@ -25450,6 +26118,9 @@ function projectInlayHints(analysis, document, range2) {
|
|
|
25450
26118
|
if (!callee || callee.kind !== "identifier" && callee.kind !== "type") {
|
|
25451
26119
|
continue;
|
|
25452
26120
|
}
|
|
26121
|
+
if (bodyContexts.some((body) => rangeContains(body.range, callee.range.start))) {
|
|
26122
|
+
continue;
|
|
26123
|
+
}
|
|
25453
26124
|
if (!open || open.text !== "(") continue;
|
|
25454
26125
|
const beforeCallee = tokens[index - 1];
|
|
25455
26126
|
if (beforeCallee?.text === "@") continue;
|
|
@@ -25476,6 +26147,14 @@ function projectInlayHints(analysis, document, range2) {
|
|
|
25476
26147
|
});
|
|
25477
26148
|
}
|
|
25478
26149
|
}
|
|
26150
|
+
for (const body of bodyContexts) {
|
|
26151
|
+
const snapshot = projectBodySnapshot(analysis, document, body);
|
|
26152
|
+
hints.push(
|
|
26153
|
+
...inlayHints(snapshot, range2).filter(
|
|
26154
|
+
(hint) => rangeContains(body.range, hint.position)
|
|
26155
|
+
)
|
|
26156
|
+
);
|
|
26157
|
+
}
|
|
25479
26158
|
return hints;
|
|
25480
26159
|
}
|
|
25481
26160
|
function projectCallParameterNames(analysis, document, callee, beforeCallee) {
|
|
@@ -25677,8 +26356,14 @@ function symbolsDeclaredAt(index, uri, range2) {
|
|
|
25677
26356
|
function projectSemanticTokens(document, analysis) {
|
|
25678
26357
|
const index = buildProjectSymbolIndex(analysis.symbols);
|
|
25679
26358
|
const tokens = projectTokens(document);
|
|
25680
|
-
|
|
26359
|
+
const bodyContexts = analysis.bodyContexts.filter(
|
|
26360
|
+
(body) => body.uri === document.uri
|
|
26361
|
+
);
|
|
26362
|
+
const result = tokens.flatMap((token, tokenIndex) => {
|
|
25681
26363
|
if (token.kind === "eof" || token.kind === "error") return [];
|
|
26364
|
+
if (bodyContexts.some((body) => rangeContains(body.range, token.range.start))) {
|
|
26365
|
+
return [];
|
|
26366
|
+
}
|
|
25682
26367
|
const declaration = symbolsDeclaredAt(index, document.uri, token.range)[0];
|
|
25683
26368
|
const resolved = token.kind === "identifier" ? indexedProjectSymbolAt(analysis, document, token.range.start, index)?.symbol : void 0;
|
|
25684
26369
|
let type;
|
|
@@ -25705,6 +26390,17 @@ function projectSemanticTokens(document, analysis) {
|
|
|
25705
26390
|
}
|
|
25706
26391
|
];
|
|
25707
26392
|
});
|
|
26393
|
+
for (const body of bodyContexts) {
|
|
26394
|
+
const snapshot = projectBodySnapshot(analysis, document, body);
|
|
26395
|
+
result.push(
|
|
26396
|
+
...semanticTokens(snapshot).filter(
|
|
26397
|
+
(token) => rangeContains(body.range, token.range.start)
|
|
26398
|
+
)
|
|
26399
|
+
);
|
|
26400
|
+
}
|
|
26401
|
+
return result.sort(
|
|
26402
|
+
(left, right) => positionCompare(left.range.start, right.range.start)
|
|
26403
|
+
);
|
|
25708
26404
|
}
|
|
25709
26405
|
function isProjectContextualKeyword(tokens, tokenIndex) {
|
|
25710
26406
|
const token = tokens[tokenIndex];
|
|
@@ -25729,6 +26425,17 @@ function indexedProjectSymbolAt(analysis, document, position, index) {
|
|
|
25729
26425
|
if (exact.length === 1) return { token, symbol: exact[0] };
|
|
25730
26426
|
const candidates = index.byName.get(token.text) ?? [];
|
|
25731
26427
|
if (candidates.length === 0) return null;
|
|
26428
|
+
const sourceTokens = projectTokens(document);
|
|
26429
|
+
const tokenIndex = sourceTokens.findIndex(
|
|
26430
|
+
(candidate) => candidate.start === token.start && candidate.end === token.end
|
|
26431
|
+
);
|
|
26432
|
+
const source = analysis.documents.get(document.uri);
|
|
26433
|
+
if (source && sourceTypeAt(source, token.range.start) || sourceTokens[tokenIndex - 1]?.text === "new") {
|
|
26434
|
+
const types = candidates.filter(
|
|
26435
|
+
(symbol) => symbol.kind === "class" || symbol.kind === "interface" || symbol.kind === "enum" || symbol.kind === "genericParameter"
|
|
26436
|
+
);
|
|
26437
|
+
if (types.length === 1) return { token, symbol: types[0] };
|
|
26438
|
+
}
|
|
25732
26439
|
const scoped = candidates.filter(
|
|
25733
26440
|
(symbol) => symbol.scopeRange && symbol.location.uri === document.uri && rangeContains(symbol.scopeRange, token.range.start)
|
|
25734
26441
|
).sort(
|
|
@@ -25740,10 +26447,6 @@ function indexedProjectSymbolAt(analysis, document, position, index) {
|
|
|
25740
26447
|
return { token, symbol: visibleCandidates[0] };
|
|
25741
26448
|
}
|
|
25742
26449
|
if (visibleCandidates.length === 0) return null;
|
|
25743
|
-
const sourceTokens = projectTokens(document);
|
|
25744
|
-
const tokenIndex = sourceTokens.findIndex(
|
|
25745
|
-
(candidate) => candidate.start === token.start && candidate.end === token.end
|
|
25746
|
-
);
|
|
25747
26450
|
if (sourceTokens[tokenIndex + 1]?.text === "=" && sourceTokens[tokenIndex + 2]?.text !== "=") {
|
|
25748
26451
|
const constructed = constructionMemberSymbol(
|
|
25749
26452
|
analysis,
|
|
@@ -25753,7 +26456,6 @@ function indexedProjectSymbolAt(analysis, document, position, index) {
|
|
|
25753
26456
|
);
|
|
25754
26457
|
if (constructed) return { token, symbol: constructed };
|
|
25755
26458
|
}
|
|
25756
|
-
const source = analysis.documents.get(document.uri);
|
|
25757
26459
|
const owner = source ? declarationContaining(source, token.range.start)?.name : void 0;
|
|
25758
26460
|
if (owner) {
|
|
25759
26461
|
const owned = visibleCandidates.filter(
|
|
@@ -26272,10 +26974,11 @@ function projectFunctionsNamed(analysis, name) {
|
|
|
26272
26974
|
}
|
|
26273
26975
|
return result;
|
|
26274
26976
|
}
|
|
26275
|
-
var CONSTRUCTION_INDEX_CACHE;
|
|
26977
|
+
var PROJECT_BODY_SNAPSHOT_CACHE, CONSTRUCTION_INDEX_CACHE;
|
|
26276
26978
|
var init_project_source_language_features = __esm({
|
|
26277
26979
|
"../packages/neoscript-language/src/project-source-language-features.ts"() {
|
|
26278
26980
|
"use strict";
|
|
26981
|
+
init_analyzer();
|
|
26279
26982
|
init_lexer();
|
|
26280
26983
|
init_language_spec();
|
|
26281
26984
|
init_project_source_semantics();
|
|
@@ -26283,6 +26986,8 @@ var init_project_source_language_features = __esm({
|
|
|
26283
26986
|
init_project_source_tokens();
|
|
26284
26987
|
init_quick_fixes();
|
|
26285
26988
|
init_source_text();
|
|
26989
|
+
init_syntax();
|
|
26990
|
+
PROJECT_BODY_SNAPSHOT_CACHE = /* @__PURE__ */ new WeakMap();
|
|
26286
26991
|
CONSTRUCTION_INDEX_CACHE = /* @__PURE__ */ new WeakMap();
|
|
26287
26992
|
}
|
|
26288
26993
|
});
|
|
@@ -50456,15 +51161,14 @@ function commentEndIndex(source, index) {
|
|
|
50456
51161
|
return null;
|
|
50457
51162
|
}
|
|
50458
51163
|
function stripInitializerComments(source) {
|
|
50459
|
-
const lines = [{
|
|
51164
|
+
const lines = [{ parts: [], removedComment: false }];
|
|
50460
51165
|
let quote6 = null;
|
|
50461
51166
|
let escaped = false;
|
|
50462
51167
|
let index = 0;
|
|
51168
|
+
let segmentStart = 0;
|
|
50463
51169
|
while (index < source.length) {
|
|
50464
51170
|
const character = source[index];
|
|
50465
|
-
const line = lines[lines.length - 1];
|
|
50466
51171
|
if (quote6 !== null) {
|
|
50467
|
-
line.text += character;
|
|
50468
51172
|
if (escaped) escaped = false;
|
|
50469
51173
|
else if (character === "\\") escaped = true;
|
|
50470
51174
|
else if (character === quote6) quote6 = null;
|
|
@@ -50473,20 +51177,34 @@ function stripInitializerComments(source) {
|
|
|
50473
51177
|
}
|
|
50474
51178
|
const afterComment = commentEndIndex(source, index);
|
|
50475
51179
|
if (afterComment !== null) {
|
|
51180
|
+
const line = lines[lines.length - 1];
|
|
51181
|
+
if (segmentStart < index) {
|
|
51182
|
+
line.parts.push(source.slice(segmentStart, index));
|
|
51183
|
+
}
|
|
50476
51184
|
line.removedComment = true;
|
|
50477
51185
|
index = afterComment;
|
|
51186
|
+
segmentStart = index;
|
|
50478
51187
|
continue;
|
|
50479
51188
|
}
|
|
50480
51189
|
if (character === "\n") {
|
|
50481
|
-
|
|
51190
|
+
if (segmentStart < index) {
|
|
51191
|
+
lines[lines.length - 1].parts.push(source.slice(segmentStart, index));
|
|
51192
|
+
}
|
|
51193
|
+
lines.push({ parts: [], removedComment: false });
|
|
50482
51194
|
index += 1;
|
|
51195
|
+
segmentStart = index;
|
|
50483
51196
|
continue;
|
|
50484
51197
|
}
|
|
50485
51198
|
if (character === '"' || character === "'") quote6 = character;
|
|
50486
|
-
line.text += character;
|
|
50487
51199
|
index += 1;
|
|
50488
51200
|
}
|
|
50489
|
-
|
|
51201
|
+
if (segmentStart < source.length) {
|
|
51202
|
+
lines[lines.length - 1].parts.push(source.slice(segmentStart));
|
|
51203
|
+
}
|
|
51204
|
+
return lines.map((line) => ({
|
|
51205
|
+
text: line.parts.join("").trimEnd(),
|
|
51206
|
+
removedComment: line.removedComment
|
|
51207
|
+
})).filter((line) => !line.removedComment || line.text.trim().length > 0).map((line) => line.text).join("\n").trim();
|
|
50490
51208
|
}
|
|
50491
51209
|
function normalizeBodySource(source) {
|
|
50492
51210
|
const trimmed = source.trim();
|
|
@@ -56658,6 +57376,12 @@ var init_NeoScriptScope = __esm({
|
|
|
56658
57376
|
setLocal(bindingId, value) {
|
|
56659
57377
|
this.#bindings.set(bindingId, value);
|
|
56660
57378
|
}
|
|
57379
|
+
resetInvocationLocals(parameterCount) {
|
|
57380
|
+
if (this.#bindings.size > parameterCount) this.#bindings.clear();
|
|
57381
|
+
if (this.#readonlyBindingErrors.size > 0) {
|
|
57382
|
+
this.#readonlyBindingErrors.clear();
|
|
57383
|
+
}
|
|
57384
|
+
}
|
|
56661
57385
|
*keys() {
|
|
56662
57386
|
const inherited = /* @__PURE__ */ new Set();
|
|
56663
57387
|
if (this.parent !== null) {
|
|
@@ -61887,23 +62611,30 @@ function evalFunction(fn, scope, ctx) {
|
|
|
61887
62611
|
const innerFn = fn.info.function;
|
|
61888
62612
|
const isList = Array.isArray(c);
|
|
61889
62613
|
const out = isList ? [] : {};
|
|
62614
|
+
const useFreshCallbackScope = ctx.__collectionCallbackStrategy === "fresh";
|
|
62615
|
+
const callbackScope = useFreshCallbackScope ? null : createChildScope(scope);
|
|
62616
|
+
const callbackOptions = useFreshCallbackScope ? null : evaluationOptions(ctx, false);
|
|
62617
|
+
const callbackParameterCount = innerFn.parameters.length === 1 || innerFn.parameters.length === 2 ? innerFn.parameters.length : 0;
|
|
61890
62618
|
iterateCollection(c, ctx, (entry, key, valueId) => {
|
|
61891
62619
|
consumeBudget(ctx, "workUnits", 1, "work unit");
|
|
61892
|
-
const innerScope = pushParams(
|
|
61893
|
-
|
|
61894
|
-
|
|
61895
|
-
|
|
61896
|
-
|
|
61897
|
-
|
|
61898
|
-
|
|
61899
|
-
|
|
61900
|
-
|
|
61901
|
-
|
|
62620
|
+
const innerScope = callbackScope ?? pushParams(scope, innerFn.parameters, [key, entry], isList);
|
|
62621
|
+
if (callbackScope !== null) {
|
|
62622
|
+
callbackScope.resetInvocationLocals(callbackParameterCount);
|
|
62623
|
+
if (callbackParameterCount === 1) {
|
|
62624
|
+
callbackScope.setLocal(innerFn.parameters[0].id, entry);
|
|
62625
|
+
} else if (callbackParameterCount === 2) {
|
|
62626
|
+
callbackScope.setLocal(
|
|
62627
|
+
innerFn.parameters[0].id,
|
|
62628
|
+
isList ? Number(key) : String(key)
|
|
62629
|
+
);
|
|
62630
|
+
callbackScope.setLocal(innerFn.parameters[1].id, entry);
|
|
62631
|
+
}
|
|
62632
|
+
}
|
|
61902
62633
|
const result = evalInstructions(
|
|
61903
62634
|
innerFn.instructions,
|
|
61904
62635
|
innerScope,
|
|
61905
62636
|
ctx,
|
|
61906
|
-
evaluationOptions(ctx, false)
|
|
62637
|
+
callbackOptions ?? evaluationOptions(ctx, false)
|
|
61907
62638
|
);
|
|
61908
62639
|
if (result.kind === "return" && result.value === true) {
|
|
61909
62640
|
consumeBudget(
|
|
@@ -61928,24 +62659,35 @@ function evalFunction(fn, scope, ctx) {
|
|
|
61928
62659
|
const innerFn = fn.info.function ?? null;
|
|
61929
62660
|
const isList = Array.isArray(c);
|
|
61930
62661
|
const sentinel = /* @__PURE__ */ Symbol("not-found");
|
|
62662
|
+
const useFreshCallbackScope = ctx.__collectionCallbackStrategy === "fresh";
|
|
62663
|
+
const callbackScope = innerFn === null || useFreshCallbackScope ? null : createChildScope(scope);
|
|
62664
|
+
const callbackOptions = innerFn === null || useFreshCallbackScope ? null : evaluationOptions(ctx, false);
|
|
62665
|
+
const callbackParameterCount = innerFn !== null && (innerFn.parameters.length === 1 || innerFn.parameters.length === 2) ? innerFn.parameters.length : 0;
|
|
61931
62666
|
let found = sentinel;
|
|
61932
62667
|
iterateCollection(c, ctx, (entry, key) => {
|
|
61933
|
-
if (
|
|
62668
|
+
if (innerFn === null) {
|
|
61934
62669
|
found = entry;
|
|
61935
62670
|
return 1 /* Break */;
|
|
61936
62671
|
}
|
|
61937
62672
|
consumeBudget(ctx, "workUnits", 1, "work unit");
|
|
61938
|
-
const innerScope = pushParams(
|
|
61939
|
-
|
|
61940
|
-
|
|
61941
|
-
|
|
61942
|
-
|
|
61943
|
-
|
|
62673
|
+
const innerScope = callbackScope ?? pushParams(scope, innerFn.parameters, [key, entry], isList);
|
|
62674
|
+
if (callbackScope !== null) {
|
|
62675
|
+
callbackScope.resetInvocationLocals(callbackParameterCount);
|
|
62676
|
+
if (callbackParameterCount === 1) {
|
|
62677
|
+
callbackScope.setLocal(innerFn.parameters[0].id, entry);
|
|
62678
|
+
} else if (callbackParameterCount === 2) {
|
|
62679
|
+
callbackScope.setLocal(
|
|
62680
|
+
innerFn.parameters[0].id,
|
|
62681
|
+
isList ? Number(key) : String(key)
|
|
62682
|
+
);
|
|
62683
|
+
callbackScope.setLocal(innerFn.parameters[1].id, entry);
|
|
62684
|
+
}
|
|
62685
|
+
}
|
|
61944
62686
|
const result = evalInstructions(
|
|
61945
62687
|
innerFn.instructions,
|
|
61946
62688
|
innerScope,
|
|
61947
62689
|
ctx,
|
|
61948
|
-
evaluationOptions(ctx, false)
|
|
62690
|
+
callbackOptions ?? evaluationOptions(ctx, false)
|
|
61949
62691
|
);
|
|
61950
62692
|
if (result.kind === "return" && result.value === true) {
|
|
61951
62693
|
found = entry;
|
|
@@ -61963,22 +62705,33 @@ function evalFunction(fn, scope, ctx) {
|
|
|
61963
62705
|
}
|
|
61964
62706
|
case "select" /* select */: {
|
|
61965
62707
|
const c = evalPointer(fn.info.collectionPointer, scope, ctx);
|
|
62708
|
+
const useFreshCallbackScope = ctx.__collectionCallbackStrategy === "fresh";
|
|
62709
|
+
const callbackScope = useFreshCallbackScope ? null : createChildScope(scope);
|
|
62710
|
+
const callbackOptions = useFreshCallbackScope ? null : evaluationOptions(ctx, false);
|
|
61966
62711
|
const innerFn = fn.info.function;
|
|
62712
|
+
const callbackParameterCount = innerFn.parameters.length === 1 || innerFn.parameters.length === 2 ? innerFn.parameters.length : 0;
|
|
61967
62713
|
const isList = Array.isArray(c);
|
|
61968
62714
|
const out = [];
|
|
61969
62715
|
iterateCollection(c, ctx, (entry, key) => {
|
|
61970
62716
|
consumeBudget(ctx, "workUnits", 1, "work unit");
|
|
61971
|
-
const innerScope = pushParams(
|
|
61972
|
-
|
|
61973
|
-
|
|
61974
|
-
|
|
61975
|
-
|
|
61976
|
-
|
|
62717
|
+
const innerScope = callbackScope ?? pushParams(scope, innerFn.parameters, [key, entry], isList);
|
|
62718
|
+
if (callbackScope !== null) {
|
|
62719
|
+
callbackScope.resetInvocationLocals(callbackParameterCount);
|
|
62720
|
+
if (callbackParameterCount === 1) {
|
|
62721
|
+
callbackScope.setLocal(innerFn.parameters[0].id, entry);
|
|
62722
|
+
} else if (callbackParameterCount === 2) {
|
|
62723
|
+
callbackScope.setLocal(
|
|
62724
|
+
innerFn.parameters[0].id,
|
|
62725
|
+
isList ? Number(key) : String(key)
|
|
62726
|
+
);
|
|
62727
|
+
callbackScope.setLocal(innerFn.parameters[1].id, entry);
|
|
62728
|
+
}
|
|
62729
|
+
}
|
|
61977
62730
|
const result = evalInstructions(
|
|
61978
62731
|
innerFn.instructions,
|
|
61979
62732
|
innerScope,
|
|
61980
62733
|
ctx,
|
|
61981
|
-
evaluationOptions(ctx, false)
|
|
62734
|
+
callbackOptions ?? evaluationOptions(ctx, false)
|
|
61982
62735
|
);
|
|
61983
62736
|
if (result.kind === "return") {
|
|
61984
62737
|
consumeBudget(
|
|
@@ -63324,7 +64077,7 @@ function encodeRequiredConstructorArgument(args) {
|
|
|
63324
64077
|
return adoptTracked(trackedId, "root");
|
|
63325
64078
|
}
|
|
63326
64079
|
if (args.typeInfo.type !== 6 /* List */ && args.typeInfo.type !== 5 /* Dictionary */) {
|
|
63327
|
-
return args.value;
|
|
64080
|
+
return cloneConstructorLiteralValue(args.value);
|
|
63328
64081
|
}
|
|
63329
64082
|
if (args.value === null || args.value === void 0) return null;
|
|
63330
64083
|
if (trackedId !== null) return adoptTracked(trackedId, "root");
|
|
@@ -63368,7 +64121,7 @@ function encodeRequiredConstructorArgument(args) {
|
|
|
63368
64121
|
}
|
|
63369
64122
|
return nested;
|
|
63370
64123
|
}
|
|
63371
|
-
return registerRow(value).id;
|
|
64124
|
+
return registerRow(cloneConstructorLiteralValue(value)).id;
|
|
63372
64125
|
};
|
|
63373
64126
|
if (collectionType.type === 6 /* List */) {
|
|
63374
64127
|
if (!Array.isArray(args.value)) {
|
|
@@ -63962,10 +64715,10 @@ function cloneConstructorArgumentGraph(args) {
|
|
|
63962
64715
|
return cloneRow(args.source, args.member, args.genericEnv);
|
|
63963
64716
|
}
|
|
63964
64717
|
function cloneConstructorLiteralValue(value) {
|
|
64718
|
+
if (typeof value !== "object" || value === null) return value;
|
|
63965
64719
|
if (Array.isArray(value)) {
|
|
63966
64720
|
return value.map((entry) => cloneConstructorLiteralValue(entry));
|
|
63967
64721
|
}
|
|
63968
|
-
if (typeof value !== "object" || value === null) return value;
|
|
63969
64722
|
return Object.fromEntries(
|
|
63970
64723
|
Object.entries(value).map(([key, entry]) => [
|
|
63971
64724
|
key,
|
|
@@ -66503,6 +67256,7 @@ var init_project_document_read = __esm({
|
|
|
66503
67256
|
});
|
|
66504
67257
|
|
|
66505
67258
|
// src/workspace.ts
|
|
67259
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
66506
67260
|
import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2 } from "node:fs";
|
|
66507
67261
|
import { dirname, join as join2, resolve } from "node:path";
|
|
66508
67262
|
function recordStateKey(recordKind, recordId) {
|
|
@@ -66735,9 +67489,12 @@ function matchUnityScalarField(content, field) {
|
|
|
66735
67489
|
function readWorkspaceState(root, options = {}) {
|
|
66736
67490
|
const statePath = join2(root, NEO_STATE_DIR, NEO_STATE_FILE);
|
|
66737
67491
|
if (!existsSync2(statePath)) {
|
|
67492
|
+
options.onSourceRead?.(null);
|
|
66738
67493
|
return { records: {} };
|
|
66739
67494
|
}
|
|
66740
|
-
const
|
|
67495
|
+
const source = readFileSync2(statePath, "utf8");
|
|
67496
|
+
options.onSourceRead?.(source);
|
|
67497
|
+
const parsed = JSON.parse(source);
|
|
66741
67498
|
if (typeof parsed !== "object" || parsed === null) {
|
|
66742
67499
|
throw new Error(`"${statePath}" must contain a JSON object.`);
|
|
66743
67500
|
}
|
|
@@ -66780,10 +67537,20 @@ function loadWorkspace(startDir, options = {}) {
|
|
|
66780
67537
|
`No "${NEO_CONFIG_FILE}" found in "${startDir}" or any parent directory. Run "neo init" first.`
|
|
66781
67538
|
);
|
|
66782
67539
|
}
|
|
67540
|
+
let stateSourceSha256;
|
|
67541
|
+
const state = readWorkspaceState(root, {
|
|
67542
|
+
discardLegacyFormat2State: options.discardLegacyFormat2State,
|
|
67543
|
+
...options.fingerprintStateSource === true ? {
|
|
67544
|
+
onSourceRead: (source) => {
|
|
67545
|
+
stateSourceSha256 = createHash2("sha256").update(source ?? "<missing>").digest("hex");
|
|
67546
|
+
}
|
|
67547
|
+
} : {}
|
|
67548
|
+
});
|
|
66783
67549
|
return {
|
|
66784
67550
|
root,
|
|
66785
67551
|
config: readWorkspaceConfig(root),
|
|
66786
|
-
state
|
|
67552
|
+
state,
|
|
67553
|
+
...stateSourceSha256 === void 0 ? {} : { stateSourceSha256 }
|
|
66787
67554
|
};
|
|
66788
67555
|
}
|
|
66789
67556
|
var NEO_CONFIG_FILE, NEO_STATE_DIR, NEO_STATE_FILE, CURRENT_FORMAT_VERSION;
|
|
@@ -72142,7 +72909,7 @@ var init_animation_clips = __esm({
|
|
|
72142
72909
|
|
|
72143
72910
|
// src/project-source/materialized-construction-cache.ts
|
|
72144
72911
|
import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
72145
|
-
import { createHash as
|
|
72912
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
72146
72913
|
import { dirname as dirname2, join as join3 } from "node:path";
|
|
72147
72914
|
function createMaterializedConstructionExpressionsV1(args) {
|
|
72148
72915
|
const warm = args.useBuildCaches ? readMaterializedConstructionBuildCacheV1(args.root, args.state) : null;
|
|
@@ -72234,7 +73001,7 @@ function writeMaterializedConstructionBuildCacheV1(root, state, expressions) {
|
|
|
72234
73001
|
renameSync(temporary, file);
|
|
72235
73002
|
}
|
|
72236
73003
|
function materializedConstructionStateFingerprint(state) {
|
|
72237
|
-
const hash =
|
|
73004
|
+
const hash = createHash3("sha256");
|
|
72238
73005
|
hash.update(String(MATERIALIZED_CONSTRUCTION_BUILD_CACHE_REVISION));
|
|
72239
73006
|
for (const [key, record3] of Object.entries(state.records).sort(
|
|
72240
73007
|
([left], [right]) => left.localeCompare(right)
|
|
@@ -73060,10 +73827,10 @@ var init_neo_script_recompile_scope = __esm({
|
|
|
73060
73827
|
});
|
|
73061
73828
|
|
|
73062
73829
|
// ../src/database/project-content-hash.ts
|
|
73063
|
-
import { createHash as
|
|
73830
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
73064
73831
|
function hashCanonicalJson(value) {
|
|
73065
73832
|
const canonicalJson = canonicalJsonStringify(value);
|
|
73066
|
-
return
|
|
73833
|
+
return createHash4("sha256").update(canonicalJson).digest("hex");
|
|
73067
73834
|
}
|
|
73068
73835
|
var init_project_content_hash = __esm({
|
|
73069
73836
|
"../src/database/project-content-hash.ts"() {
|
|
@@ -73130,7 +73897,7 @@ var init_project_fingerprint = __esm({
|
|
|
73130
73897
|
|
|
73131
73898
|
// src/project-source/neoscript-build-cache.ts
|
|
73132
73899
|
import { mkdirSync as mkdirSync4, readFileSync as readFileSync4, renameSync as renameSync2, writeFileSync as writeFileSync4 } from "node:fs";
|
|
73133
|
-
import { createHash as
|
|
73900
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
73134
73901
|
import { dirname as dirname3, join as join4 } from "node:path";
|
|
73135
73902
|
function loadOrBuildNeoScriptProjectV1(root, document) {
|
|
73136
73903
|
const fingerprint = neoScriptProjectFingerprint(document);
|
|
@@ -73157,7 +73924,7 @@ function loadOrBuildNeoScriptProjectV1(root, document) {
|
|
|
73157
73924
|
return project;
|
|
73158
73925
|
}
|
|
73159
73926
|
function neoScriptProjectFingerprint(document) {
|
|
73160
|
-
return
|
|
73927
|
+
return createHash5("sha256").update(
|
|
73161
73928
|
canonicalJsonStringify(neoScriptCompilationProjectContract(document))
|
|
73162
73929
|
).digest("hex");
|
|
73163
73930
|
}
|
|
@@ -92280,7 +93047,7 @@ var init_project_documents = __esm({
|
|
|
92280
93047
|
|
|
92281
93048
|
// src/project-source/project-document-cache.ts
|
|
92282
93049
|
import { mkdirSync as mkdirSync5, readFileSync as readFileSync5, renameSync as renameSync3, writeFileSync as writeFileSync5 } from "node:fs";
|
|
92283
|
-
import { createHash as
|
|
93050
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
92284
93051
|
import { dirname as dirname4, join as join5 } from "node:path";
|
|
92285
93052
|
function readProjectSourceAnalysisBuildCacheV4(root, sources) {
|
|
92286
93053
|
try {
|
|
@@ -92404,7 +93171,7 @@ function writeProjectSourceDocumentBuildCacheV1(root, sources, documents) {
|
|
|
92404
93171
|
renameSync3(temporary, file);
|
|
92405
93172
|
}
|
|
92406
93173
|
function projectSourceFileFingerprint(source) {
|
|
92407
|
-
return
|
|
93174
|
+
return createHash6("sha256").update(String(PROJECT_SOURCE_DOCUMENT_BUILD_CACHE_REVISION)).update("\0").update(String(NEOSCRIPT_COMPILER_REVISION)).update("\0").update(source.kind).update("\0").update(source.text).digest("hex");
|
|
92408
93175
|
}
|
|
92409
93176
|
function isCachedProjectSourceDocument(value, source) {
|
|
92410
93177
|
if (value === null || typeof value !== "object") return false;
|
|
@@ -92412,7 +93179,7 @@ function isCachedProjectSourceDocument(value, source) {
|
|
|
92412
93179
|
return document.kind === source.kind && document.sourceText === source.text && Array.isArray(document.declarations) && Array.isArray(document.diagnostics);
|
|
92413
93180
|
}
|
|
92414
93181
|
function projectSourceFingerprint(sources) {
|
|
92415
|
-
const hash =
|
|
93182
|
+
const hash = createHash6("sha256");
|
|
92416
93183
|
hash.update(String(PROJECT_SOURCE_BUILD_CACHE_REVISION));
|
|
92417
93184
|
hash.update("\0");
|
|
92418
93185
|
hash.update(String(NEOSCRIPT_COMPILER_REVISION));
|
|
@@ -92440,7 +93207,7 @@ var init_project_document_cache = __esm({
|
|
|
92440
93207
|
});
|
|
92441
93208
|
|
|
92442
93209
|
// src/project-source/project-files.ts
|
|
92443
|
-
import { createHash as
|
|
93210
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
92444
93211
|
import {
|
|
92445
93212
|
existsSync as existsSync3,
|
|
92446
93213
|
mkdirSync as mkdirSync6,
|
|
@@ -92687,7 +93454,7 @@ function discoverProjectBinariesV4(root, explicit, ignoredPaths = []) {
|
|
|
92687
93454
|
}).sort((left, right) => compareCodePoints(left.path, right.path));
|
|
92688
93455
|
}
|
|
92689
93456
|
function sha256Bytes(bytes) {
|
|
92690
|
-
return
|
|
93457
|
+
return createHash7("sha256").update(bytes).digest("hex");
|
|
92691
93458
|
}
|
|
92692
93459
|
function sha256File(path) {
|
|
92693
93460
|
return sha256Bytes(readFileSync6(path));
|
|
@@ -98140,7 +98907,7 @@ var init_script = __esm({
|
|
|
98140
98907
|
});
|
|
98141
98908
|
|
|
98142
98909
|
// ../src/database/project-source-identity.ts
|
|
98143
|
-
import { createHash as
|
|
98910
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
98144
98911
|
function hashProjectSourceFiles(inputFiles) {
|
|
98145
98912
|
const files = normalizeSourceFiles(inputFiles);
|
|
98146
98913
|
const bytes = Buffer.from(JSON.stringify({ version: 1, files }), "utf8");
|
|
@@ -98149,7 +98916,7 @@ function hashProjectSourceFiles(inputFiles) {
|
|
|
98149
98916
|
`Project source identity is ${bytes.byteLength} bytes; the limit is ${MAX_SOURCE_BYTES} bytes.`
|
|
98150
98917
|
);
|
|
98151
98918
|
}
|
|
98152
|
-
return
|
|
98919
|
+
return createHash8("sha256").update(bytes).digest("hex");
|
|
98153
98920
|
}
|
|
98154
98921
|
function normalizeSourceFiles(inputFiles) {
|
|
98155
98922
|
if (inputFiles.length > MAX_SOURCE_FILES) {
|
|
@@ -98241,14 +99008,14 @@ var init_project_source_identity = __esm({
|
|
|
98241
99008
|
|
|
98242
99009
|
// src/push-hook.ts
|
|
98243
99010
|
import { spawn } from "node:child_process";
|
|
98244
|
-
import { createHash as
|
|
99011
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
98245
99012
|
import { existsSync as existsSync10, readFileSync as readFileSync13, readdirSync as readdirSync4 } from "node:fs";
|
|
98246
99013
|
import { join as join13, relative as relative3, sep as sep3 } from "node:path";
|
|
98247
|
-
function fingerprintPushInputs(workspace) {
|
|
99014
|
+
function fingerprintPushInputs(workspace, options = {}) {
|
|
98248
99015
|
const paths = /* @__PURE__ */ new Set([
|
|
98249
99016
|
join13(workspace.root, "neo.json"),
|
|
98250
99017
|
...listProjectSourceFilesV4(workspace.root),
|
|
98251
|
-
...listProjectTestFilesV1(workspace.root),
|
|
99018
|
+
...options.includeTests === false ? [] : listProjectTestFilesV1(workspace.root),
|
|
98252
99019
|
...workspace.config.unityConfigPath === void 0 ? [] : [join13(workspace.root, workspace.config.unityConfigPath)]
|
|
98253
99020
|
]);
|
|
98254
99021
|
const visitManaged = (directory) => {
|
|
@@ -98262,7 +99029,7 @@ function fingerprintPushInputs(workspace) {
|
|
|
98262
99029
|
};
|
|
98263
99030
|
visitManaged(join13(workspace.root, "Files", "Images"));
|
|
98264
99031
|
visitManaged(join13(workspace.root, "Files", "AudioClips"));
|
|
98265
|
-
const hash =
|
|
99032
|
+
const hash = createHash9("sha256");
|
|
98266
99033
|
for (const path of [...paths].sort()) {
|
|
98267
99034
|
const name = relative3(workspace.root, path).split(sep3).join("/");
|
|
98268
99035
|
hash.update(name);
|
|
@@ -98919,7 +99686,7 @@ __export(push_exports, {
|
|
|
98919
99686
|
runPush: () => runPush,
|
|
98920
99687
|
stripServerDerivedNeoScript: () => stripServerDerivedNeoScript
|
|
98921
99688
|
});
|
|
98922
|
-
import { createHash as
|
|
99689
|
+
import { createHash as createHash10, randomUUID as randomUUID2 } from "node:crypto";
|
|
98923
99690
|
import {
|
|
98924
99691
|
mkdirSync as mkdirSync10,
|
|
98925
99692
|
writeFileSync as writeFileSync10,
|
|
@@ -99702,7 +100469,7 @@ async function runPush(workspace, options, preparationOverride) {
|
|
|
99702
100469
|
version: 1,
|
|
99703
100470
|
projectFingerprint: `sha256:${preparedLocal.source.sourceHash}`,
|
|
99704
100471
|
inputFingerprint: preparedInputFingerprint,
|
|
99705
|
-
documentSha256:
|
|
100472
|
+
documentSha256: createHash10("sha256").update(documentJson).digest("hex"),
|
|
99706
100473
|
document: candidate.document
|
|
99707
100474
|
})}
|
|
99708
100475
|
`,
|
|
@@ -101711,7 +102478,7 @@ var init_registry2 = __esm({
|
|
|
101711
102478
|
PROJECT_SCHEMA_CONTRACT = Object.freeze({
|
|
101712
102479
|
formatVersion: 3,
|
|
101713
102480
|
contractVersion: "3.9",
|
|
101714
|
-
cliVersion: "0.26.
|
|
102481
|
+
cliVersion: "0.26.2",
|
|
101715
102482
|
projectFileUploadBatchSize: 32,
|
|
101716
102483
|
documentRecords: {
|
|
101717
102484
|
member: {
|
|
@@ -103004,7 +103771,7 @@ __export(test_exports, {
|
|
|
103004
103771
|
maintainNeoTestBuildCache: () => maintainNeoTestBuildCache,
|
|
103005
103772
|
runTest: () => runTest
|
|
103006
103773
|
});
|
|
103007
|
-
import { createHash as
|
|
103774
|
+
import { createHash as createHash11, randomUUID as randomUUID3 } from "node:crypto";
|
|
103008
103775
|
import {
|
|
103009
103776
|
existsSync as existsSync12,
|
|
103010
103777
|
mkdirSync as mkdirSync11,
|
|
@@ -103118,10 +103885,11 @@ function errorMessage2(error) {
|
|
|
103118
103885
|
function findDelegateTargetMemberId(value) {
|
|
103119
103886
|
return isRecord10(value) && typeof value.memberId === "string" ? value.memberId : null;
|
|
103120
103887
|
}
|
|
103121
|
-
function
|
|
103122
|
-
const
|
|
103123
|
-
|
|
103124
|
-
const
|
|
103888
|
+
function sharedEvaluatorBase(rawDocument) {
|
|
103889
|
+
const cached = SHARED_EVALUATOR_BASES.get(rawDocument);
|
|
103890
|
+
if (cached !== void 0) return cached;
|
|
103891
|
+
const document = readDocumentArrays(rawDocument);
|
|
103892
|
+
const constructors = Array.isArray(rawDocument.constructors) ? rawDocument.constructors.filter(isRecord10) : [];
|
|
103125
103893
|
const vm = {
|
|
103126
103894
|
project: document.project,
|
|
103127
103895
|
members: document.members,
|
|
@@ -103146,10 +103914,19 @@ function makeEvaluatorContext(rawDocument, interceptor, documentAlreadyIsolated
|
|
|
103146
103914
|
}
|
|
103147
103915
|
)
|
|
103148
103916
|
};
|
|
103149
|
-
|
|
103917
|
+
const created = {
|
|
103150
103918
|
vm,
|
|
103919
|
+
rootValue: buildRootValue(document)
|
|
103920
|
+
};
|
|
103921
|
+
SHARED_EVALUATOR_BASES.set(rawDocument, created);
|
|
103922
|
+
return created;
|
|
103923
|
+
}
|
|
103924
|
+
function makeEvaluatorContext(rawDocument, interceptor) {
|
|
103925
|
+
const base = sharedEvaluatorBase(rawDocument);
|
|
103926
|
+
return createNeoScriptEvaluationRuntime({
|
|
103927
|
+
vm: base.vm,
|
|
103151
103928
|
thisValue: null,
|
|
103152
|
-
rootValue:
|
|
103929
|
+
rootValue: base.rootValue,
|
|
103153
103930
|
dialogueContext: null,
|
|
103154
103931
|
callInterceptor: interceptor,
|
|
103155
103932
|
nativeFunctionErrorCheckBehavior: "strict"
|
|
@@ -103216,14 +103993,15 @@ function preparedHookCandidate(workspace) {
|
|
|
103216
103993
|
);
|
|
103217
103994
|
}
|
|
103218
103995
|
const documentJson = JSON.stringify(parsed.document);
|
|
103219
|
-
if (
|
|
103996
|
+
if (createHash11("sha256").update(documentJson).digest("hex") !== parsed.documentSha256) {
|
|
103220
103997
|
throw new NeoTestPreparedCandidateError(
|
|
103221
103998
|
"Configured push hook candidate project document failed checksum verification."
|
|
103222
103999
|
);
|
|
103223
104000
|
}
|
|
103224
104001
|
return {
|
|
103225
104002
|
document: parsed.document,
|
|
103226
|
-
sourceHash: configuredFingerprint.replace(/^sha256:/u, "")
|
|
104003
|
+
sourceHash: configuredFingerprint.replace(/^sha256:/u, ""),
|
|
104004
|
+
documentSha256: parsed.documentSha256
|
|
103227
104005
|
};
|
|
103228
104006
|
} catch (error) {
|
|
103229
104007
|
if (error instanceof NeoTestPreparedCandidateError) throw error;
|
|
@@ -103232,11 +104010,79 @@ function preparedHookCandidate(workspace) {
|
|
|
103232
104010
|
);
|
|
103233
104011
|
}
|
|
103234
104012
|
}
|
|
104013
|
+
function fingerprintTestCandidateInputs(workspace) {
|
|
104014
|
+
return createHash11("sha256").update("neo-test-candidate\0").update(String(TEST_CANDIDATE_CACHE_REVISION)).update("\0").update(PROJECT_SCHEMA_CONTRACT.cliVersion).update("\0").update(String(NEOSCRIPT_COMPILER_REVISION)).update("\0").update(fingerprintPushInputs(workspace, { includeTests: false })).update("\0").update(
|
|
104015
|
+
workspace.stateSourceSha256 === void 0 ? `object\0${JSON.stringify(workspace.state)}` : `source\0${workspace.stateSourceSha256}`
|
|
104016
|
+
).digest("hex");
|
|
104017
|
+
}
|
|
104018
|
+
function testCandidateCachePath(workspace, inputFingerprint) {
|
|
104019
|
+
const cacheKey = createHash11("sha256").update(inputFingerprint).digest("hex");
|
|
104020
|
+
return join16(
|
|
104021
|
+
workspace.root,
|
|
104022
|
+
".neo",
|
|
104023
|
+
"test-build",
|
|
104024
|
+
"v1",
|
|
104025
|
+
"project",
|
|
104026
|
+
cacheKey,
|
|
104027
|
+
"candidate.json"
|
|
104028
|
+
);
|
|
104029
|
+
}
|
|
104030
|
+
function cachedTestCandidate(workspace, inputFingerprint) {
|
|
104031
|
+
try {
|
|
104032
|
+
const candidatePath = testCandidateCachePath(workspace, inputFingerprint);
|
|
104033
|
+
const parsed = JSON.parse(readFileSync16(candidatePath, "utf8"));
|
|
104034
|
+
if (!isRecord10(parsed)) return null;
|
|
104035
|
+
if (parsed.version !== 1) return null;
|
|
104036
|
+
if (parsed.candidateRevision !== TEST_CANDIDATE_CACHE_REVISION) return null;
|
|
104037
|
+
if (parsed.cliVersion !== PROJECT_SCHEMA_CONTRACT.cliVersion) return null;
|
|
104038
|
+
if (parsed.compilerRevision !== NEOSCRIPT_COMPILER_REVISION) return null;
|
|
104039
|
+
if (parsed.inputFingerprint !== inputFingerprint) return null;
|
|
104040
|
+
if (typeof parsed.sourceHash !== "string" || parsed.sourceHash.length === 0) {
|
|
104041
|
+
return null;
|
|
104042
|
+
}
|
|
104043
|
+
if (typeof parsed.documentSha256 !== "string") return null;
|
|
104044
|
+
const documentJson = readFileSync16(
|
|
104045
|
+
join16(dirname9(candidatePath), "document.json"),
|
|
104046
|
+
"utf8"
|
|
104047
|
+
);
|
|
104048
|
+
if (createHash11("sha256").update(documentJson).digest("hex") !== parsed.documentSha256) {
|
|
104049
|
+
return null;
|
|
104050
|
+
}
|
|
104051
|
+
const document = JSON.parse(documentJson);
|
|
104052
|
+
if (!isRecord10(document)) return null;
|
|
104053
|
+
return {
|
|
104054
|
+
document,
|
|
104055
|
+
sourceHash: parsed.sourceHash,
|
|
104056
|
+
documentSha256: parsed.documentSha256
|
|
104057
|
+
};
|
|
104058
|
+
} catch {
|
|
104059
|
+
return null;
|
|
104060
|
+
}
|
|
104061
|
+
}
|
|
104062
|
+
function cacheTestCandidate(workspace, inputFingerprint, candidate) {
|
|
104063
|
+
const documentJson = JSON.stringify(candidate.document);
|
|
104064
|
+
const documentSha256 = createHash11("sha256").update(documentJson).digest("hex");
|
|
104065
|
+
const candidatePath = testCandidateCachePath(workspace, inputFingerprint);
|
|
104066
|
+
atomicWrite(join16(dirname9(candidatePath), "document.json"), documentJson);
|
|
104067
|
+
atomicWrite(
|
|
104068
|
+
candidatePath,
|
|
104069
|
+
JSON.stringify({
|
|
104070
|
+
version: 1,
|
|
104071
|
+
candidateRevision: TEST_CANDIDATE_CACHE_REVISION,
|
|
104072
|
+
cliVersion: PROJECT_SCHEMA_CONTRACT.cliVersion,
|
|
104073
|
+
compilerRevision: NEOSCRIPT_COMPILER_REVISION,
|
|
104074
|
+
inputFingerprint,
|
|
104075
|
+
sourceHash: candidate.sourceHash,
|
|
104076
|
+
documentSha256
|
|
104077
|
+
}) + "\n"
|
|
104078
|
+
);
|
|
104079
|
+
return { ...candidate, documentSha256 };
|
|
104080
|
+
}
|
|
103235
104081
|
function compileSpec(workspace, document, absolutePath, projectCompilationHash2) {
|
|
103236
104082
|
const scriptDocument = readDocumentArrays(document);
|
|
103237
104083
|
const path = relative5(workspace.root, absolutePath).split(sep5).join("/");
|
|
103238
104084
|
const source = readFileSync16(absolutePath, "utf8");
|
|
103239
|
-
const sourceHash =
|
|
104085
|
+
const sourceHash = createHash11("sha256").update(source).digest("hex");
|
|
103240
104086
|
const artifactPath = join16(
|
|
103241
104087
|
workspace.root,
|
|
103242
104088
|
".neo",
|
|
@@ -103247,7 +104093,7 @@ function compileSpec(workspace, document, absolutePath, projectCompilationHash2)
|
|
|
103247
104093
|
);
|
|
103248
104094
|
try {
|
|
103249
104095
|
const cached = JSON.parse(readFileSync16(artifactPath, "utf8"));
|
|
103250
|
-
if (isRecord10(cached) && cached.version === 1 && cached.compilerRevision === NEOSCRIPT_COMPILER_REVISION && cached.projectCompilationHash === projectCompilationHash2 && cached.sourceHash === sourceHash && isRecord10(cached.action) && typeof cached.artifactSha256 === "string" &&
|
|
104096
|
+
if (isRecord10(cached) && cached.version === 1 && cached.compilerRevision === NEOSCRIPT_COMPILER_REVISION && cached.projectCompilationHash === projectCompilationHash2 && cached.sourceHash === sourceHash && isRecord10(cached.action) && typeof cached.artifactSha256 === "string" && createHash11("sha256").update(JSON.stringify(cached.action)).digest("hex") === cached.artifactSha256 && cached.action.compilerRevision === NEOSCRIPT_COMPILER_REVISION && Array.isArray(cached.action.parameters) && Array.isArray(cached.action.instructions) && isRecord10(cached.action.typeInfo)) {
|
|
103251
104097
|
return {
|
|
103252
104098
|
path,
|
|
103253
104099
|
source,
|
|
@@ -103279,15 +104125,16 @@ function compileSpec(workspace, document, absolutePath, projectCompilationHash2)
|
|
|
103279
104125
|
compilerRevision: NEOSCRIPT_COMPILER_REVISION,
|
|
103280
104126
|
projectCompilationHash: projectCompilationHash2,
|
|
103281
104127
|
sourceHash,
|
|
103282
|
-
artifactSha256:
|
|
104128
|
+
artifactSha256: createHash11("sha256").update(JSON.stringify(compiled.action)).digest("hex"),
|
|
103283
104129
|
action: compiled.action
|
|
103284
104130
|
})}
|
|
103285
104131
|
`
|
|
103286
104132
|
);
|
|
103287
104133
|
return compiled;
|
|
103288
104134
|
}
|
|
103289
|
-
function projectCompilationHash(projectSourceHash, document) {
|
|
103290
|
-
|
|
104135
|
+
function projectCompilationHash(projectSourceHash, document, knownDocumentSha256) {
|
|
104136
|
+
const documentSha256 = knownDocumentSha256 ?? createHash11("sha256").update(JSON.stringify(document)).digest("hex");
|
|
104137
|
+
return createHash11("sha256").update(projectSourceHash).update("\0").update(documentSha256).digest("hex");
|
|
103291
104138
|
}
|
|
103292
104139
|
function selectedSpecPaths(workspace, selectors) {
|
|
103293
104140
|
const all = listProjectTestFilesV1(workspace.root);
|
|
@@ -103520,7 +104367,7 @@ function registerSpec(spec, document) {
|
|
|
103520
104367
|
}
|
|
103521
104368
|
names.add(fullName);
|
|
103522
104369
|
const test = {
|
|
103523
|
-
id: `sha256:${
|
|
104370
|
+
id: `sha256:${createHash11("sha256").update(`${spec.path}\0${namePath}`).digest("hex")}`,
|
|
103524
104371
|
name: namePath,
|
|
103525
104372
|
fullName,
|
|
103526
104373
|
body: portableDelegate(call.args[1]),
|
|
@@ -103871,24 +104718,32 @@ function interceptTestCall(environment, call) {
|
|
|
103871
104718
|
throw error;
|
|
103872
104719
|
}
|
|
103873
104720
|
}
|
|
103874
|
-
function createTestEnvironment(document
|
|
104721
|
+
function createTestEnvironment(document) {
|
|
103875
104722
|
const environment = {
|
|
103876
104723
|
baseDocument: document,
|
|
103877
104724
|
context: void 0,
|
|
103878
104725
|
mocks: /* @__PURE__ */ new Map(),
|
|
103879
104726
|
mocksByMember: /* @__PURE__ */ new Map(),
|
|
103880
|
-
nextMockId: 1
|
|
104727
|
+
nextMockId: 1,
|
|
104728
|
+
hasStateChanges: false
|
|
103881
104729
|
};
|
|
103882
104730
|
environment.context = makeEvaluatorContext(
|
|
103883
104731
|
document,
|
|
103884
|
-
(call) => interceptTestCall(environment, call)
|
|
103885
|
-
documentAlreadyIsolated
|
|
104732
|
+
(call) => interceptTestCall(environment, call)
|
|
103886
104733
|
);
|
|
103887
104734
|
return environment;
|
|
103888
104735
|
}
|
|
103889
104736
|
function snapshotEnvironmentDocument(environment) {
|
|
103890
|
-
const
|
|
103891
|
-
const
|
|
104737
|
+
const overlay = environment.context.__valueOverlay;
|
|
104738
|
+
const runtime = environment.context.__runtimeSessionValues;
|
|
104739
|
+
const bindings = new Map([
|
|
104740
|
+
...environment.context.__saveStaticBindings ?? [],
|
|
104741
|
+
...environment.context.__sessionStaticBindings ?? []
|
|
104742
|
+
]);
|
|
104743
|
+
if (!environment.hasStateChanges) return environment.baseDocument;
|
|
104744
|
+
const snapshot = { ...environment.baseDocument };
|
|
104745
|
+
const baseValues = Array.isArray(snapshot.values) ? snapshot.values : [];
|
|
104746
|
+
const values = [...baseValues];
|
|
103892
104747
|
const byId = /* @__PURE__ */ new Map();
|
|
103893
104748
|
values.forEach((value, index) => {
|
|
103894
104749
|
if (isRecord10(value) && typeof value.id === "string")
|
|
@@ -103905,20 +104760,17 @@ function snapshotEnvironmentDocument(environment) {
|
|
|
103905
104760
|
values[index] = cloned;
|
|
103906
104761
|
}
|
|
103907
104762
|
};
|
|
103908
|
-
for (const row of
|
|
103909
|
-
|
|
103910
|
-
for (const row of environment.context.__runtimeSessionValues?.values() ?? [])
|
|
103911
|
-
mergeRow(row);
|
|
104763
|
+
for (const row of overlay?.values() ?? []) mergeRow(row);
|
|
104764
|
+
for (const row of runtime?.values() ?? []) mergeRow(row);
|
|
103912
104765
|
snapshot.values = values;
|
|
103913
|
-
|
|
103914
|
-
|
|
103915
|
-
|
|
103916
|
-
|
|
103917
|
-
|
|
103918
|
-
|
|
103919
|
-
|
|
103920
|
-
|
|
103921
|
-
member.valueId = bindings.get(member.id) ?? null;
|
|
104766
|
+
if (bindings.size > 0) {
|
|
104767
|
+
const baseMembers = Array.isArray(snapshot.members) ? snapshot.members : [];
|
|
104768
|
+
snapshot.members = baseMembers.map((member) => {
|
|
104769
|
+
if (!isRecord10(member) || typeof member.id !== "string" || !bindings.has(member.id)) {
|
|
104770
|
+
return member;
|
|
104771
|
+
}
|
|
104772
|
+
return { ...member, valueId: bindings.get(member.id) ?? null };
|
|
104773
|
+
});
|
|
103922
104774
|
}
|
|
103923
104775
|
return snapshot;
|
|
103924
104776
|
}
|
|
@@ -103939,10 +104791,7 @@ function copyMocks(source, target) {
|
|
|
103939
104791
|
}
|
|
103940
104792
|
}
|
|
103941
104793
|
function cloneTestEnvironment(source) {
|
|
103942
|
-
const cloned = createTestEnvironment(
|
|
103943
|
-
snapshotEnvironmentDocument(source),
|
|
103944
|
-
true
|
|
103945
|
-
);
|
|
104794
|
+
const cloned = createTestEnvironment(snapshotEnvironmentDocument(source));
|
|
103946
104795
|
copyMocks(source, cloned);
|
|
103947
104796
|
return cloned;
|
|
103948
104797
|
}
|
|
@@ -103953,10 +104802,14 @@ function runCallback(environment, delegate, deadlineMs) {
|
|
|
103953
104802
|
__indexes: void 0,
|
|
103954
104803
|
wallClockDeadlineMs: deadlineMs
|
|
103955
104804
|
});
|
|
103956
|
-
|
|
103957
|
-
|
|
103958
|
-
|
|
103959
|
-
|
|
104805
|
+
try {
|
|
104806
|
+
evaluateNSDelegate(
|
|
104807
|
+
bindNeoScriptDelegateToContext(delegate, environment.context),
|
|
104808
|
+
environment.context
|
|
104809
|
+
);
|
|
104810
|
+
} finally {
|
|
104811
|
+
environment.hasStateChanges ||= (environment.context.__executionState?.writes.length ?? 0) > 0 || (environment.context.__runtimeSessionValues?.size ?? 0) > 0 || (environment.context.__saveStaticBindings?.size ?? 0) > 0 || (environment.context.__sessionStaticBindings?.size ?? 0) > 0;
|
|
104812
|
+
}
|
|
103960
104813
|
}
|
|
103961
104814
|
function failureFor(error, file, position, member = null) {
|
|
103962
104815
|
const timeout = error instanceof NeoScriptWallClockTimeoutError;
|
|
@@ -103982,18 +104835,22 @@ function suiteHasSelectedTests(suite, selected2) {
|
|
|
103982
104835
|
async function executeRegisteredSpec(registered, document, selected2, timeoutMs, interrupted) {
|
|
103983
104836
|
const results = [];
|
|
103984
104837
|
const fileFailures = [];
|
|
104838
|
+
let testDurationMs = 0;
|
|
103985
104839
|
const executeSuite = async (suite, parentEnvironment, inheritedFailures) => {
|
|
103986
104840
|
if (!suiteHasSelectedTests(suite, selected2)) return;
|
|
103987
104841
|
const fixture = cloneTestEnvironment(parentEnvironment);
|
|
103988
104842
|
const suiteFailures = [...inheritedFailures];
|
|
103989
104843
|
const suiteDeadline = Date.now() + timeoutMs;
|
|
103990
104844
|
for (const hook of suite.beforeAll) {
|
|
104845
|
+
const hookStarted = performance.now();
|
|
103991
104846
|
try {
|
|
103992
104847
|
runCallback(fixture, hook, suiteDeadline);
|
|
103993
104848
|
} catch (error) {
|
|
103994
104849
|
suiteFailures.push(
|
|
103995
104850
|
failureFor(error, registered.spec.path, suite, "beforeAll")
|
|
103996
104851
|
);
|
|
104852
|
+
} finally {
|
|
104853
|
+
testDurationMs += performance.now() - hookStarted;
|
|
103997
104854
|
}
|
|
103998
104855
|
}
|
|
103999
104856
|
for (const test of suite.tests) {
|
|
@@ -104001,6 +104858,8 @@ async function executeRegisteredSpec(registered, document, selected2, timeoutMs,
|
|
|
104001
104858
|
const started = performance.now();
|
|
104002
104859
|
const deadline = Date.now() + timeoutMs;
|
|
104003
104860
|
const environment = cloneTestEnvironment(fixture);
|
|
104861
|
+
const startupBeforeTestMs = performance.now() - started;
|
|
104862
|
+
const testStarted = performance.now();
|
|
104004
104863
|
const failures = [...suiteFailures];
|
|
104005
104864
|
let setupFailed = failures.length > 0;
|
|
104006
104865
|
if (!setupFailed) {
|
|
@@ -104037,19 +104896,24 @@ async function executeRegisteredSpec(registered, document, selected2, timeoutMs,
|
|
|
104037
104896
|
}
|
|
104038
104897
|
}
|
|
104039
104898
|
}
|
|
104040
|
-
const
|
|
104899
|
+
const currentTestDurationMs = performance.now() - testStarted;
|
|
104900
|
+
const schedulerStarted = performance.now();
|
|
104901
|
+
await new Promise((resolvePromise) => {
|
|
104902
|
+
setImmediate(resolvePromise);
|
|
104903
|
+
});
|
|
104904
|
+
const startupDurationMs = startupBeforeTestMs + performance.now() - schedulerStarted;
|
|
104905
|
+
testDurationMs += currentTestDurationMs;
|
|
104041
104906
|
results.push({
|
|
104042
104907
|
id: test.id,
|
|
104043
104908
|
file: registered.spec.path,
|
|
104044
104909
|
name: test.name,
|
|
104045
104910
|
fullName: test.fullName,
|
|
104046
104911
|
status: failures.length === 0 ? "passed" : "failed",
|
|
104047
|
-
durationMs,
|
|
104912
|
+
durationMs: startupDurationMs + currentTestDurationMs,
|
|
104913
|
+
startupDurationMs,
|
|
104914
|
+
testDurationMs: currentTestDurationMs,
|
|
104048
104915
|
failures
|
|
104049
104916
|
});
|
|
104050
|
-
await new Promise((resolvePromise) => {
|
|
104051
|
-
setImmediate(resolvePromise);
|
|
104052
|
-
});
|
|
104053
104917
|
if (interrupted()) break;
|
|
104054
104918
|
}
|
|
104055
104919
|
for (const child of suite.suites) {
|
|
@@ -104057,12 +104921,15 @@ async function executeRegisteredSpec(registered, document, selected2, timeoutMs,
|
|
|
104057
104921
|
await executeSuite(child, fixture, suiteFailures);
|
|
104058
104922
|
}
|
|
104059
104923
|
for (const hook of suite.afterAll) {
|
|
104924
|
+
const hookStarted = performance.now();
|
|
104060
104925
|
try {
|
|
104061
104926
|
runCallback(fixture, hook, Date.now() + timeoutMs);
|
|
104062
104927
|
} catch (error) {
|
|
104063
104928
|
fileFailures.push(
|
|
104064
104929
|
failureFor(error, registered.spec.path, suite, "afterAll")
|
|
104065
104930
|
);
|
|
104931
|
+
} finally {
|
|
104932
|
+
testDurationMs += performance.now() - hookStarted;
|
|
104066
104933
|
}
|
|
104067
104934
|
}
|
|
104068
104935
|
};
|
|
@@ -104073,7 +104940,7 @@ async function executeRegisteredSpec(registered, document, selected2, timeoutMs,
|
|
|
104073
104940
|
results.sort(
|
|
104074
104941
|
(left, right) => (registrationOrder.get(left.id) ?? Number.MAX_SAFE_INTEGER) - (registrationOrder.get(right.id) ?? Number.MAX_SAFE_INTEGER)
|
|
104075
104942
|
);
|
|
104076
|
-
return { tests: results, failures: fileFailures };
|
|
104943
|
+
return { tests: results, failures: fileFailures, testDurationMs };
|
|
104077
104944
|
}
|
|
104078
104945
|
function atomicWrite(path, content) {
|
|
104079
104946
|
mkdirSync11(dirname9(path), { recursive: true });
|
|
@@ -104126,8 +104993,12 @@ function maintainNeoTestBuildCache(root, maxBytes = TEST_BUILD_CACHE_LIMIT_BYTES
|
|
|
104126
104993
|
total -= file.size;
|
|
104127
104994
|
}
|
|
104128
104995
|
}
|
|
104996
|
+
function formatTestDuration(durationMs) {
|
|
104997
|
+
return `${durationMs < 10 ? durationMs.toFixed(2) : durationMs.toFixed(1)}ms`;
|
|
104998
|
+
}
|
|
104129
104999
|
async function runTest(workspace, options, dependencies = {}) {
|
|
104130
105000
|
const started = Date.now();
|
|
105001
|
+
const performanceStarted = performance.now();
|
|
104131
105002
|
const testBuildRoot = join16(workspace.root, ".neo", "test-build");
|
|
104132
105003
|
maintainNeoTestBuildCache(testBuildRoot);
|
|
104133
105004
|
const startedAt = new Date(started).toISOString();
|
|
@@ -104140,6 +105011,7 @@ async function runTest(workspace, options, dependencies = {}) {
|
|
|
104140
105011
|
const selectedTestCountByFile = /* @__PURE__ */ new Map();
|
|
104141
105012
|
const fileFailuresByPath = /* @__PURE__ */ new Map();
|
|
104142
105013
|
const diagnostics = [];
|
|
105014
|
+
let testDurationMs = 0;
|
|
104143
105015
|
let exitCode = 0;
|
|
104144
105016
|
let interrupted = false;
|
|
104145
105017
|
const handleSigint = () => {
|
|
@@ -104155,18 +105027,47 @@ async function runTest(workspace, options, dependencies = {}) {
|
|
|
104155
105027
|
process.on("SIGINT", handleSigint);
|
|
104156
105028
|
let phase = "prepare";
|
|
104157
105029
|
try {
|
|
104158
|
-
|
|
105030
|
+
let candidate = dependencies.prepareLocalCandidate === void 0 ? preparedHookCandidate(workspace) : null;
|
|
105031
|
+
let candidateInputFingerprint = null;
|
|
105032
|
+
let shouldCacheCandidate = false;
|
|
105033
|
+
if (candidate === null) {
|
|
105034
|
+
const canUseCandidateCache = dependencies.prepareLocalCandidate === void 0 || dependencies.fingerprintCandidateInputs !== void 0;
|
|
105035
|
+
if (canUseCandidateCache) {
|
|
105036
|
+
candidateInputFingerprint = dependencies.fingerprintCandidateInputs?.(workspace) ?? fingerprintTestCandidateInputs(workspace);
|
|
105037
|
+
candidate = cachedTestCandidate(workspace, candidateInputFingerprint);
|
|
105038
|
+
}
|
|
105039
|
+
if (candidate === null) {
|
|
105040
|
+
const prepared = dependencies.prepareLocalCandidate === void 0 ? await prepareLocalCandidateV4(workspace) : await dependencies.prepareLocalCandidate(workspace);
|
|
105041
|
+
if (prepared.document === null) {
|
|
105042
|
+
throw new NeoTestReportWriteError(
|
|
105043
|
+
"Local test preparation did not produce a project document."
|
|
105044
|
+
);
|
|
105045
|
+
}
|
|
105046
|
+
candidate = {
|
|
105047
|
+
document: prepared.document,
|
|
105048
|
+
sourceHash: prepared.sourceHash
|
|
105049
|
+
};
|
|
105050
|
+
shouldCacheCandidate = canUseCandidateCache;
|
|
105051
|
+
}
|
|
105052
|
+
}
|
|
104159
105053
|
assertNotInterrupted();
|
|
104160
|
-
if (candidate
|
|
105054
|
+
if (candidate === null) {
|
|
104161
105055
|
throw new NeoTestReportWriteError(
|
|
104162
|
-
"Local test preparation
|
|
105056
|
+
"Local test preparation produced no reusable candidate."
|
|
104163
105057
|
);
|
|
104164
105058
|
}
|
|
105059
|
+
if (shouldCacheCandidate && candidateInputFingerprint !== null) {
|
|
105060
|
+
candidate = cacheTestCandidate(workspace, candidateInputFingerprint, {
|
|
105061
|
+
document: candidate.document,
|
|
105062
|
+
sourceHash: candidate.sourceHash
|
|
105063
|
+
});
|
|
105064
|
+
}
|
|
104165
105065
|
projectFingerprint = `sha256:${candidate.sourceHash}`;
|
|
104166
105066
|
const rawDocument = documentRecord(candidate.document);
|
|
104167
105067
|
const compilationHash = projectCompilationHash(
|
|
104168
105068
|
candidate.sourceHash,
|
|
104169
|
-
rawDocument
|
|
105069
|
+
rawDocument,
|
|
105070
|
+
candidate.documentSha256
|
|
104170
105071
|
);
|
|
104171
105072
|
phase = "select";
|
|
104172
105073
|
let selectedPaths;
|
|
@@ -104225,6 +105126,7 @@ async function runTest(workspace, options, dependencies = {}) {
|
|
|
104225
105126
|
() => interrupted
|
|
104226
105127
|
);
|
|
104227
105128
|
results.push(...executed.tests);
|
|
105129
|
+
testDurationMs += executed.testDurationMs;
|
|
104228
105130
|
fileFailuresByPath.set(entry.spec.path, executed.failures);
|
|
104229
105131
|
assertNotInterrupted();
|
|
104230
105132
|
}
|
|
@@ -104238,14 +105140,14 @@ async function runTest(workspace, options, dependencies = {}) {
|
|
|
104238
105140
|
compilerRevision: NEOSCRIPT_COMPILER_REVISION,
|
|
104239
105141
|
evaluatorRevision: 4,
|
|
104240
105142
|
projectFingerprint,
|
|
104241
|
-
configurationSha256:
|
|
105143
|
+
configurationSha256: createHash11("sha256").update(JSON.stringify(workspace.config.test ?? {})).digest("hex"),
|
|
104242
105144
|
dependencyGraph: Object.fromEntries(
|
|
104243
105145
|
specs.map((spec) => [spec.path, [projectFingerprint]])
|
|
104244
105146
|
),
|
|
104245
105147
|
artifacts: specs.map((spec) => ({
|
|
104246
105148
|
path: spec.path,
|
|
104247
|
-
sourceSha256:
|
|
104248
|
-
artifactSha256:
|
|
105149
|
+
sourceSha256: createHash11("sha256").update(spec.source).digest("hex"),
|
|
105150
|
+
artifactSha256: createHash11("sha256").update(JSON.stringify(spec.action)).digest("hex")
|
|
104249
105151
|
}))
|
|
104250
105152
|
};
|
|
104251
105153
|
atomicWrite(
|
|
@@ -104273,7 +105175,16 @@ async function runTest(workspace, options, dependencies = {}) {
|
|
|
104273
105175
|
0
|
|
104274
105176
|
);
|
|
104275
105177
|
const skipped = Math.max(0, selectedTestTotal - results.length);
|
|
104276
|
-
const durationMs =
|
|
105178
|
+
const durationMs = performance.now() - performanceStarted;
|
|
105179
|
+
const startupDurationMs = Math.max(0, durationMs - testDurationMs);
|
|
105180
|
+
const perTestStartupDurationMs = results.reduce(
|
|
105181
|
+
(sum, result) => sum + result.startupDurationMs,
|
|
105182
|
+
0
|
|
105183
|
+
);
|
|
105184
|
+
const sharedStartupDurationMs = Math.max(
|
|
105185
|
+
0,
|
|
105186
|
+
startupDurationMs - perTestStartupDurationMs
|
|
105187
|
+
);
|
|
104277
105188
|
const testFiles = specs.map((spec) => {
|
|
104278
105189
|
const tests = results.filter((result) => result.file === spec.path);
|
|
104279
105190
|
const failures = fileFailuresByPath.get(spec.path) ?? [];
|
|
@@ -104290,7 +105201,7 @@ async function runTest(workspace, options, dependencies = {}) {
|
|
|
104290
105201
|
success: exitCode === 0,
|
|
104291
105202
|
projectFingerprint,
|
|
104292
105203
|
seed: Number.parseInt(
|
|
104293
|
-
|
|
105204
|
+
createHash11("sha256").update(projectFingerprint ?? "neo-test:no-project").digest("hex").slice(0, 8),
|
|
104294
105205
|
16
|
|
104295
105206
|
),
|
|
104296
105207
|
selection: {
|
|
@@ -104305,7 +105216,10 @@ async function runTest(workspace, options, dependencies = {}) {
|
|
|
104305
105216
|
failed,
|
|
104306
105217
|
skipped,
|
|
104307
105218
|
todo: 0,
|
|
104308
|
-
durationMs
|
|
105219
|
+
durationMs,
|
|
105220
|
+
startupDurationMs,
|
|
105221
|
+
sharedStartupDurationMs,
|
|
105222
|
+
testDurationMs
|
|
104309
105223
|
},
|
|
104310
105224
|
testFiles,
|
|
104311
105225
|
diagnostics,
|
|
@@ -104330,9 +105244,12 @@ async function runTest(workspace, options, dependencies = {}) {
|
|
|
104330
105244
|
if (options.reporter === "json") {
|
|
104331
105245
|
if (options.outputFile === null) process.stdout.write(serialized);
|
|
104332
105246
|
} else {
|
|
105247
|
+
console.log(
|
|
105248
|
+
`\u21BB shared startup (${formatTestDuration(report.summary.sharedStartupDurationMs)})`
|
|
105249
|
+
);
|
|
104333
105250
|
for (const result of results) {
|
|
104334
105251
|
console.log(
|
|
104335
|
-
`${result.status === "passed" ? "\u2713" : "\u2717"} ${result.file} > ${result.name} (${result.durationMs.
|
|
105252
|
+
`${result.status === "passed" ? "\u2713" : "\u2717"} ${result.file} > ${result.name} (total ${formatTestDuration(result.durationMs)}; test ${formatTestDuration(result.testDurationMs)}; startup ${formatTestDuration(result.startupDurationMs)})`
|
|
104336
105253
|
);
|
|
104337
105254
|
for (const failure of result.failures)
|
|
104338
105255
|
console.log(` ${failure.message}`);
|
|
@@ -104353,7 +105270,7 @@ async function runTest(workspace, options, dependencies = {}) {
|
|
|
104353
105270
|
for (const diagnostic of diagnostics)
|
|
104354
105271
|
console.log(`\u2717 ${diagnostic.message}`);
|
|
104355
105272
|
console.log(
|
|
104356
|
-
`${report.summary.passed} passed, ${report.summary.failed} failed (${report.summary.durationMs}
|
|
105273
|
+
`${report.summary.passed} passed, ${report.summary.failed} failed (total ${formatTestDuration(report.summary.durationMs)}; test ${formatTestDuration(report.summary.testDurationMs)}; startup ${formatTestDuration(report.summary.startupDurationMs)})`
|
|
104357
105274
|
);
|
|
104358
105275
|
}
|
|
104359
105276
|
if (!report.success) process.exitCode = exitCode;
|
|
@@ -104400,7 +105317,7 @@ async function inspectNeoTestCompilation(workspace, dependencies = {}) {
|
|
|
104400
105317
|
errors
|
|
104401
105318
|
};
|
|
104402
105319
|
}
|
|
104403
|
-
var TEST_BUILD_CACHE_LIMIT_BYTES, ABANDONED_TEMP_MAX_AGE_MS, NeoTestUsageError, NeoTestNoTestsError, NeoTestRegistrationError, NeoTestReportWriteError, NeoTestPreparedCandidateError, NeoTestInterruptedError, NeoTestAssertionError, ERROR_SOURCE_POSITIONS, REGISTRATION_IDS;
|
|
105320
|
+
var TEST_BUILD_CACHE_LIMIT_BYTES, ABANDONED_TEMP_MAX_AGE_MS, TEST_CANDIDATE_CACHE_REVISION, NeoTestUsageError, NeoTestNoTestsError, NeoTestRegistrationError, NeoTestReportWriteError, NeoTestPreparedCandidateError, NeoTestInterruptedError, NeoTestAssertionError, ERROR_SOURCE_POSITIONS, SHARED_EVALUATOR_BASES, REGISTRATION_IDS;
|
|
104404
105321
|
var init_test = __esm({
|
|
104405
105322
|
"src/commands/test.ts"() {
|
|
104406
105323
|
"use strict";
|
|
@@ -104415,6 +105332,7 @@ var init_test = __esm({
|
|
|
104415
105332
|
init_push_hook();
|
|
104416
105333
|
TEST_BUILD_CACHE_LIMIT_BYTES = 512 * 1024 * 1024;
|
|
104417
105334
|
ABANDONED_TEMP_MAX_AGE_MS = 60 * 60 * 1e3;
|
|
105335
|
+
TEST_CANDIDATE_CACHE_REVISION = 1;
|
|
104418
105336
|
NeoTestUsageError = class extends Error {
|
|
104419
105337
|
name = "NeoTestUsageError";
|
|
104420
105338
|
};
|
|
@@ -104446,6 +105364,7 @@ var init_test = __esm({
|
|
|
104446
105364
|
received;
|
|
104447
105365
|
};
|
|
104448
105366
|
ERROR_SOURCE_POSITIONS = /* @__PURE__ */ new WeakMap();
|
|
105367
|
+
SHARED_EVALUATOR_BASES = /* @__PURE__ */ new WeakMap();
|
|
104449
105368
|
REGISTRATION_IDS = /* @__PURE__ */ new Set([
|
|
104450
105369
|
NEO_TEST_IDS.describe,
|
|
104451
105370
|
NEO_TEST_IDS.test,
|
|
@@ -107841,7 +108760,8 @@ function loadWorkspaceForCommand(args) {
|
|
|
107841
108760
|
// A reset reconstructs the working copy from the server and may therefore
|
|
107842
108761
|
// discard an unsupported pre-cutover cache. No other command gets this
|
|
107843
108762
|
// exception, so format 2 never becomes an active compatibility read path.
|
|
107844
|
-
discardLegacyFormat2State: args.command === "pull" && boolFlag(args, "reset")
|
|
108763
|
+
discardLegacyFormat2State: args.command === "pull" && boolFlag(args, "reset"),
|
|
108764
|
+
fingerprintStateSource: args.command === "test"
|
|
107845
108765
|
});
|
|
107846
108766
|
const apiOverride = stringFlag(args, "api");
|
|
107847
108767
|
if (apiOverride !== null) {
|
|
@@ -108019,7 +108939,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
|
|
|
108019
108939
|
async function main() {
|
|
108020
108940
|
const args = parseArgs(process.argv.slice(2));
|
|
108021
108941
|
if (args.command === "--version") {
|
|
108022
|
-
console.log("0.26.
|
|
108942
|
+
console.log("0.26.2");
|
|
108023
108943
|
return;
|
|
108024
108944
|
}
|
|
108025
108945
|
if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {
|