@neocompose/cli 0.31.8 → 0.31.9
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
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.31.9] - 2026-08-17
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Surface `SpriteInfo.Empty` in project-source and NeoScript completion and
|
|
8
|
+
hover with documentation for its canonical empty-sprite behavior.
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Accept the exact `SpriteInfo.Empty` sentinel as the non-null result of a
|
|
13
|
+
required Sprite function while continuing to reject malformed empty sprites
|
|
14
|
+
and empty Audio values.
|
|
15
|
+
|
|
3
16
|
## [0.31.8] - 2026-08-17
|
|
4
17
|
|
|
5
18
|
### Fixed
|
package/dist/neo.mjs
CHANGED
|
@@ -1663,13 +1663,14 @@ var init_language_spec = __esm({
|
|
|
1663
1663
|
id: "builtin:SpriteInfo",
|
|
1664
1664
|
name: "SpriteInfo",
|
|
1665
1665
|
kind: "builtin",
|
|
1666
|
+
documentation: "A sprite asset reference and slice index. Use SpriteInfo.Empty when a required sprite should render no image.",
|
|
1666
1667
|
members: [
|
|
1667
1668
|
{
|
|
1668
1669
|
...property(
|
|
1669
1670
|
"SpriteInfo",
|
|
1670
1671
|
"Empty",
|
|
1671
1672
|
SPRITE_INFO_TYPE,
|
|
1672
|
-
"
|
|
1673
|
+
"The canonical non-null empty sprite. Use it when a required SpriteInfo should render no image; it has no backing file and uses slice index 0."
|
|
1673
1674
|
),
|
|
1674
1675
|
static: true
|
|
1675
1676
|
},
|
|
@@ -2319,7 +2320,11 @@ function complete(snapshot, position) {
|
|
|
2319
2320
|
word
|
|
2320
2321
|
);
|
|
2321
2322
|
} else {
|
|
2322
|
-
const
|
|
2323
|
+
const directStaticType = receiverTokens.length === 1 && receiverTokens[0]?.kind === "type" ? snapshot.project.typeByName.get(receiverTokens[0].text) : void 0;
|
|
2324
|
+
const resolved = directStaticType ? {
|
|
2325
|
+
type: { kind: "named", typeId: directStaticType.id },
|
|
2326
|
+
staticType: directStaticType
|
|
2327
|
+
} : resolveChain(snapshot, receiverTokens, offset);
|
|
2323
2328
|
candidates = completionItemsForResolution(snapshot, resolved, word);
|
|
2324
2329
|
}
|
|
2325
2330
|
} else if (tail?.kind === "identifier" && tail.text === NEOSCRIPT_CONSTRUCTOR_KEYWORD) {
|
|
@@ -2343,27 +2348,14 @@ function complete(snapshot, position) {
|
|
|
2343
2348
|
function hover(snapshot, position) {
|
|
2344
2349
|
const offset = snapshot.source.offsetAt(position);
|
|
2345
2350
|
const reference2 = referenceAt(snapshot.parsed.references, offset);
|
|
2346
|
-
if (!reference2) return
|
|
2351
|
+
if (!reference2) return builtinStaticMemberHoverAt(snapshot, offset);
|
|
2347
2352
|
const resolution = resolveReference(snapshot, reference2);
|
|
2348
|
-
if (!resolution?.symbol && !resolution?.staticType)
|
|
2353
|
+
if (!resolution?.symbol && !resolution?.staticType) {
|
|
2354
|
+
return builtinStaticMemberHoverAt(snapshot, offset);
|
|
2355
|
+
}
|
|
2349
2356
|
const symbol = resolution.symbol;
|
|
2350
2357
|
if (symbol) {
|
|
2351
|
-
|
|
2352
|
-
const origin = symbol.inheritedFrom ? `
|
|
2353
|
-
|
|
2354
|
-
Inherited from \`${symbol.inheritedFrom.typeName}\`.` : "";
|
|
2355
|
-
const identity2 = symbol.id.startsWith("local:") || symbol.id.startsWith("parameter:") ? "" : `
|
|
2356
|
-
|
|
2357
|
-
Schema id: \`${symbol.id}\``;
|
|
2358
|
-
const docs2 = symbol.documentation ? `
|
|
2359
|
-
|
|
2360
|
-
${symbol.documentation}` : "";
|
|
2361
|
-
return {
|
|
2362
|
-
range: reference2.range,
|
|
2363
|
-
markdown: `**${symbol.name}** _${displaySymbolKind(symbol)}_
|
|
2364
|
-
|
|
2365
|
-
\`${signature}\`${docs2}${origin}${identity2}`
|
|
2366
|
-
};
|
|
2358
|
+
return symbolHover(symbol, reference2.range, snapshot.project);
|
|
2367
2359
|
}
|
|
2368
2360
|
const type = resolution.staticType;
|
|
2369
2361
|
if (!type) return null;
|
|
@@ -2380,6 +2372,39 @@ ${type.documentation}` : "";
|
|
|
2380
2372
|
Schema id: \`${type.id}\`${docs}`
|
|
2381
2373
|
};
|
|
2382
2374
|
}
|
|
2375
|
+
function symbolHover(symbol, range2, project) {
|
|
2376
|
+
const signature = formatSymbolSignature(symbol, project);
|
|
2377
|
+
const origin = symbol.inheritedFrom ? `
|
|
2378
|
+
|
|
2379
|
+
Inherited from \`${symbol.inheritedFrom.typeName}\`.` : "";
|
|
2380
|
+
const identity2 = symbol.id.startsWith("local:") || symbol.id.startsWith("parameter:") ? "" : `
|
|
2381
|
+
|
|
2382
|
+
Schema id: \`${symbol.id}\``;
|
|
2383
|
+
const docs = symbol.documentation ? `
|
|
2384
|
+
|
|
2385
|
+
${symbol.documentation}` : "";
|
|
2386
|
+
return {
|
|
2387
|
+
range: range2,
|
|
2388
|
+
markdown: `**${symbol.name}** _${displaySymbolKind(symbol)}_
|
|
2389
|
+
|
|
2390
|
+
\`${signature}\`${docs}${origin}${identity2}`
|
|
2391
|
+
};
|
|
2392
|
+
}
|
|
2393
|
+
function builtinStaticMemberHoverAt(snapshot, offset) {
|
|
2394
|
+
const tokens = snapshot.lexed.tokens;
|
|
2395
|
+
const memberIndex = tokens.findIndex(
|
|
2396
|
+
(token) => token.kind === "identifier" && token.start <= offset && offset <= token.end
|
|
2397
|
+
);
|
|
2398
|
+
if (memberIndex < 2 || tokens[memberIndex - 1]?.text !== ".") return null;
|
|
2399
|
+
const owner = tokens[memberIndex - 2];
|
|
2400
|
+
const memberToken = tokens[memberIndex];
|
|
2401
|
+
if (owner?.kind !== "type" || !memberToken) return null;
|
|
2402
|
+
const type = snapshot.project.typeByName.get(owner.text);
|
|
2403
|
+
const member = type?.members.find(
|
|
2404
|
+
(candidate) => candidate.static === true && candidate.name === memberToken.text
|
|
2405
|
+
);
|
|
2406
|
+
return member ? symbolHover(member, memberToken.range, snapshot.project) : null;
|
|
2407
|
+
}
|
|
2383
2408
|
function definition(snapshot, position) {
|
|
2384
2409
|
const offset = snapshot.source.offsetAt(position);
|
|
2385
2410
|
const reference2 = referenceAt(snapshot.parsed.references, offset);
|
|
@@ -27300,6 +27325,16 @@ function projectCompletions(analysis, document, position) {
|
|
|
27300
27325
|
position
|
|
27301
27326
|
);
|
|
27302
27327
|
if (annotations) return { isIncomplete: false, items: annotations };
|
|
27328
|
+
const builtinMembers = projectBuiltinStaticMemberCompletions(
|
|
27329
|
+
document,
|
|
27330
|
+
position
|
|
27331
|
+
);
|
|
27332
|
+
if (builtinMembers) {
|
|
27333
|
+
return {
|
|
27334
|
+
isIncomplete: false,
|
|
27335
|
+
items: builtinMembers.map(projectBuiltinCompletionItem)
|
|
27336
|
+
};
|
|
27337
|
+
}
|
|
27303
27338
|
const members = projectMemberCompletions(analysis, document, position);
|
|
27304
27339
|
const variantScope = variantScopeInBody;
|
|
27305
27340
|
if (members || variantScope) {
|
|
@@ -27800,20 +27835,8 @@ function sourceTypeAt(document, position) {
|
|
|
27800
27835
|
return null;
|
|
27801
27836
|
}
|
|
27802
27837
|
function projectMemberCompletions(analysis, document, position) {
|
|
27803
|
-
const
|
|
27804
|
-
|
|
27805
|
-
// yet for completion purposes. Including a semicolon that starts at the
|
|
27806
|
-
// cursor makes `Device.|;` lose the dot it is completing.
|
|
27807
|
-
positionCompare2(token.range.start, position) < 0
|
|
27808
|
-
);
|
|
27809
|
-
let cursor = tokens.length - 1;
|
|
27810
|
-
const current = tokens[cursor];
|
|
27811
|
-
if (current?.kind === "identifier" && current.range.start.line === position.line) {
|
|
27812
|
-
cursor--;
|
|
27813
|
-
}
|
|
27814
|
-
if (tokens[cursor]?.text !== ".") return null;
|
|
27815
|
-
const owner = tokens[cursor - 1];
|
|
27816
|
-
if (!owner || owner.kind !== "identifier") return null;
|
|
27838
|
+
const owner = projectMemberCompletionOwnerToken(document, position);
|
|
27839
|
+
if (!owner) return null;
|
|
27817
27840
|
const resolvedOwner = projectSymbolAt(analysis, document, owner.range.start);
|
|
27818
27841
|
if (!resolvedOwner) return null;
|
|
27819
27842
|
const ownerNames = projectReceiverOwnerNames(analysis, resolvedOwner.symbol);
|
|
@@ -27831,6 +27854,71 @@ function projectMemberCompletions(analysis, document, position) {
|
|
|
27831
27854
|
}
|
|
27832
27855
|
return members.length > 0 ? members : null;
|
|
27833
27856
|
}
|
|
27857
|
+
function projectMemberCompletionOwnerToken(document, position) {
|
|
27858
|
+
const tokens = projectTokens(document).filter(
|
|
27859
|
+
(token) => token.kind !== "eof" && token.kind !== "comment" && // Auto-inserted/future punctuation at the cursor has not been authored
|
|
27860
|
+
// yet for completion purposes. Including a semicolon that starts at the
|
|
27861
|
+
// cursor makes `Device.|;` lose the dot it is completing.
|
|
27862
|
+
positionCompare2(token.range.start, position) < 0
|
|
27863
|
+
);
|
|
27864
|
+
let cursor = tokens.length - 1;
|
|
27865
|
+
const current = tokens[cursor];
|
|
27866
|
+
if (current?.kind === "identifier" && current.range.start.line === position.line) {
|
|
27867
|
+
cursor--;
|
|
27868
|
+
}
|
|
27869
|
+
if (tokens[cursor]?.text !== ".") return null;
|
|
27870
|
+
const owner = tokens[cursor - 1];
|
|
27871
|
+
if (!owner || owner.kind !== "identifier" && owner.kind !== "type") {
|
|
27872
|
+
return null;
|
|
27873
|
+
}
|
|
27874
|
+
return owner;
|
|
27875
|
+
}
|
|
27876
|
+
function projectBuiltinStaticMemberCompletions(document, position) {
|
|
27877
|
+
const owner = projectMemberCompletionOwnerToken(document, position);
|
|
27878
|
+
if (!owner) return null;
|
|
27879
|
+
const type = NEOSCRIPT_BUILTIN_TYPE_SYMBOLS.find(
|
|
27880
|
+
(candidate) => candidate.name === owner.text
|
|
27881
|
+
);
|
|
27882
|
+
if (!type) return null;
|
|
27883
|
+
const members = type.members.filter((member) => member.static === true);
|
|
27884
|
+
return members.length > 0 ? members : null;
|
|
27885
|
+
}
|
|
27886
|
+
function projectBuiltinCompletionItem(symbol) {
|
|
27887
|
+
return {
|
|
27888
|
+
label: symbol.name,
|
|
27889
|
+
kind: projectBuiltinCompletionKind(symbol),
|
|
27890
|
+
detail: projectBuiltinSymbolSignature(symbol),
|
|
27891
|
+
...symbol.documentation ? { documentation: symbol.documentation } : {},
|
|
27892
|
+
insertText: symbol.name,
|
|
27893
|
+
symbolId: symbol.id
|
|
27894
|
+
};
|
|
27895
|
+
}
|
|
27896
|
+
function projectBuiltinCompletionKind(symbol) {
|
|
27897
|
+
switch (symbol.kind) {
|
|
27898
|
+
case "property":
|
|
27899
|
+
case "field":
|
|
27900
|
+
return "property";
|
|
27901
|
+
case "method":
|
|
27902
|
+
return "method";
|
|
27903
|
+
case "function":
|
|
27904
|
+
return "function";
|
|
27905
|
+
case "enumMember":
|
|
27906
|
+
return "enumMember";
|
|
27907
|
+
default:
|
|
27908
|
+
return "value";
|
|
27909
|
+
}
|
|
27910
|
+
}
|
|
27911
|
+
function projectBuiltinSymbolSignature(symbol) {
|
|
27912
|
+
if (symbol.parameters !== void 0) {
|
|
27913
|
+
const parameters = symbol.parameters.map((parameter4) => `${formatType(parameter4.type)} ${parameter4.name}`).join(", ");
|
|
27914
|
+
return `${formatType(symbol.returnType ?? symbol.type)} ${symbol.name}(${parameters})`;
|
|
27915
|
+
}
|
|
27916
|
+
if (symbol.kind === "property" || symbol.kind === "field") {
|
|
27917
|
+
const setter = symbol.writable ? " set;" : "";
|
|
27918
|
+
return `${formatType(symbol.type)} ${symbol.name} { get;${setter} }`;
|
|
27919
|
+
}
|
|
27920
|
+
return `${formatType(symbol.type)} ${symbol.name}`;
|
|
27921
|
+
}
|
|
27834
27922
|
function projectConstructorArgumentCompletions(analysis, document, position) {
|
|
27835
27923
|
const tokens = tokensBeforePosition(document.text, position);
|
|
27836
27924
|
const openIndex = activeCallOpenIndex(tokens, position);
|
|
@@ -28202,6 +28290,31 @@ function projectHover(analysis, document, position) {
|
|
|
28202
28290
|
}
|
|
28203
28291
|
const body = projectBodySnapshotAt(analysis, document, position);
|
|
28204
28292
|
if (body) return hover(body, position);
|
|
28293
|
+
const builtin = projectBuiltinSymbolAt(document, position);
|
|
28294
|
+
if (builtin) {
|
|
28295
|
+
if (builtin.symbol) {
|
|
28296
|
+
const documentation3 = builtin.symbol.documentation ? `
|
|
28297
|
+
|
|
28298
|
+
${builtin.symbol.documentation}` : "";
|
|
28299
|
+
return {
|
|
28300
|
+
range: builtin.token.range,
|
|
28301
|
+
markdown: `**${builtin.symbol.name}** _${builtin.symbol.kind}_
|
|
28302
|
+
|
|
28303
|
+
\`${projectBuiltinSymbolSignature(builtin.symbol)}\`${documentation3}
|
|
28304
|
+
|
|
28305
|
+
Schema id: \`${builtin.symbol.id}\``
|
|
28306
|
+
};
|
|
28307
|
+
}
|
|
28308
|
+
const documentation2 = builtin.type.documentation ? `
|
|
28309
|
+
|
|
28310
|
+
${builtin.type.documentation}` : "";
|
|
28311
|
+
return {
|
|
28312
|
+
range: builtin.token.range,
|
|
28313
|
+
markdown: `**${builtin.type.name}** _${builtin.type.kind}_${documentation2}
|
|
28314
|
+
|
|
28315
|
+
Schema id: \`${builtin.type.id}\``
|
|
28316
|
+
};
|
|
28317
|
+
}
|
|
28205
28318
|
const resolved = projectSymbolAt(analysis, document, position);
|
|
28206
28319
|
if (!resolved) return null;
|
|
28207
28320
|
const owner = resolved.symbol.ownerName ? ` on \`${resolved.symbol.ownerName}\`` : "";
|
|
@@ -28218,6 +28331,30 @@ ID: \`${resolved.symbol.id}\`` : "\n\nPending ID";
|
|
|
28218
28331
|
markdown: `**${resolved.symbol.kind}** \`${resolved.symbol.name}\`${detail}${owner}${documentation}${contract}${id2}`
|
|
28219
28332
|
};
|
|
28220
28333
|
}
|
|
28334
|
+
function projectBuiltinSymbolAt(document, position) {
|
|
28335
|
+
const token = projectTokenAt(document, position);
|
|
28336
|
+
if (!token || token.kind !== "identifier" && token.kind !== "type") {
|
|
28337
|
+
return null;
|
|
28338
|
+
}
|
|
28339
|
+
const tokens = projectTokens(document);
|
|
28340
|
+
const tokenIndex = tokens.findIndex(
|
|
28341
|
+
(candidate) => candidate.start === token.start && candidate.end === token.end
|
|
28342
|
+
);
|
|
28343
|
+
const owner = tokens[tokenIndex - 1]?.text === "." ? tokens[tokenIndex - 2] : void 0;
|
|
28344
|
+
if (owner?.kind === "identifier" || owner?.kind === "type") {
|
|
28345
|
+
const type2 = NEOSCRIPT_BUILTIN_TYPE_SYMBOLS.find(
|
|
28346
|
+
(candidate) => candidate.name === owner.text
|
|
28347
|
+
);
|
|
28348
|
+
const symbol = type2?.members.find(
|
|
28349
|
+
(candidate) => candidate.static === true && candidate.name === token.text
|
|
28350
|
+
);
|
|
28351
|
+
if (type2 && symbol) return { token, type: type2, symbol };
|
|
28352
|
+
}
|
|
28353
|
+
const type = NEOSCRIPT_BUILTIN_TYPE_SYMBOLS.find(
|
|
28354
|
+
(candidate) => candidate.name === token.text
|
|
28355
|
+
);
|
|
28356
|
+
return type ? { token, type } : null;
|
|
28357
|
+
}
|
|
28221
28358
|
function projectConstructionContractMarkdown(analysis, className) {
|
|
28222
28359
|
const index = projectConstructionIndex(analysis);
|
|
28223
28360
|
const contract = classConstructionContract(index, className);
|
|
@@ -29373,6 +29510,7 @@ var init_project_source_language_features = __esm({
|
|
|
29373
29510
|
init_language_spec();
|
|
29374
29511
|
init_project_source_semantics();
|
|
29375
29512
|
init_project_source_variants();
|
|
29513
|
+
init_project();
|
|
29376
29514
|
init_project_source_semantics();
|
|
29377
29515
|
init_project_source_settlement();
|
|
29378
29516
|
init_project_source_tokens();
|
|
@@ -67158,6 +67296,7 @@ function isRuntimeFileValue(value) {
|
|
|
67158
67296
|
return typeof fileId === "string" && fileId.length > 0;
|
|
67159
67297
|
}
|
|
67160
67298
|
function isRuntimeSpriteValue(value) {
|
|
67299
|
+
if (isEmptySpriteValue(value)) return true;
|
|
67161
67300
|
if (!isRuntimeFileValue(value)) return false;
|
|
67162
67301
|
const sliceIndex = value.sliceIndex;
|
|
67163
67302
|
return typeof sliceIndex === "number" && Number.isInteger(sliceIndex) && sliceIndex >= 0;
|
|
@@ -111276,7 +111415,7 @@ var init_registry2 = __esm({
|
|
|
111276
111415
|
PROJECT_SCHEMA_CONTRACT = Object.freeze({
|
|
111277
111416
|
formatVersion: 3,
|
|
111278
111417
|
contractVersion: "3.13",
|
|
111279
|
-
cliVersion: "0.31.
|
|
111418
|
+
cliVersion: "0.31.9",
|
|
111280
111419
|
projectFileUploadBatchSize: 32,
|
|
111281
111420
|
documentRecords: {
|
|
111282
111421
|
member: {
|
|
@@ -117871,7 +118010,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
|
|
|
117871
118010
|
async function main() {
|
|
117872
118011
|
const args = parseArgs(process.argv.slice(2));
|
|
117873
118012
|
if (args.command === "--version") {
|
|
117874
|
-
console.log("0.31.
|
|
118013
|
+
console.log("0.31.9");
|
|
117875
118014
|
return;
|
|
117876
118015
|
}
|
|
117877
118016
|
if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {
|
package/package.json
CHANGED
|
@@ -83,7 +83,7 @@ wrappers.
|
|
|
83
83
|
The marker near the top of `SKILL.md` must exactly match the package version:
|
|
84
84
|
|
|
85
85
|
```html
|
|
86
|
-
<!-- reviewed-through-cli: 0.31.
|
|
86
|
+
<!-- reviewed-through-cli: 0.31.9 -->
|
|
87
87
|
```
|
|
88
88
|
|
|
89
89
|
The quoted version above is checked too, so this instruction cannot go stale
|
|
@@ -56,6 +56,10 @@ or narrowing guard before the next access.
|
|
|
56
56
|
|
|
57
57
|
Treat lookup-return values with the same nullable narrowing rules.
|
|
58
58
|
|
|
59
|
+
Use `SpriteInfo.Empty` when a required sprite should intentionally render no
|
|
60
|
+
image. It is the canonical non-null empty sprite: it has no backing file and
|
|
61
|
+
uses slice index zero.
|
|
62
|
+
|
|
59
63
|
Let runtime ownership—not body kind—decide whether a write target is Immutable,
|
|
60
64
|
Save, Session, Setter, or otherwise writable. A constructor writes only its
|
|
61
65
|
`this` instance. A writable structured-leaf field still inherits the receiver's
|