@neocompose/cli 0.35.1 → 0.36.1
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 +39 -0
- package/dist/neo.mjs +266 -62
- package/package.json +1 -1
- package/skills/neocompose-cli/SKILL.md +1 -1
- package/skills/neocompose-cli/references/cli-development.md +1 -1
- package/skills/neocompose-cli/references/declarations-and-construction.md +6 -0
- package/skills/neocompose-cli/references/neoscript.md +17 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.36.1] - 2026-08-19
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Preserve authored function parameter defaults in live project-source symbols,
|
|
8
|
+
so calls may omit a defaulted trailing argument and hover/signature help show
|
|
9
|
+
the declared `= value`.
|
|
10
|
+
- Store direct field and property reads, including static getters such as
|
|
11
|
+
`FeatureFlags.StartInventorySize`, as executable member initializers instead
|
|
12
|
+
of rejecting them as unresolved persisted literals.
|
|
13
|
+
|
|
14
|
+
## [0.36.0] - 2026-08-19
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- Add collection `Count` property access, optional-predicate `Count(...)`, and
|
|
19
|
+
list `IndexOf(value)` across NeoScript compilation, IntelliSense, web/CLI
|
|
20
|
+
evaluation, Unity export validation, and the matching Unity runtime.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- **Breaking:** Unity export schema version is now 26 and the NeoScript
|
|
25
|
+
compiler revision is now 13. Exports containing `indexOf` or predicate
|
|
26
|
+
`count` IR require the matching Unity SDK.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- Prevent VS Code completion from replacing an existing identifier when an
|
|
31
|
+
author inserts `this.` before it.
|
|
32
|
+
- Restrict action invocation and `NeoAction.Clear()` to the declaring class
|
|
33
|
+
and its derived classes. Public external callers may still subscribe and
|
|
34
|
+
unsubscribe with `+=` and `-=`.
|
|
35
|
+
- Keep declaration-pattern bindings for indexed expressions such as
|
|
36
|
+
`items[index] is Item item`, so the bound local remains available inside the
|
|
37
|
+
successful branch without incorrectly treating the indexed expression as a
|
|
38
|
+
stable dotted narrowing path.
|
|
39
|
+
- Diagnose `NeoAction<void>` with the canonical zero-argument spelling,
|
|
40
|
+
`NeoAction`, instead of later reporting the misleading invocation arity.
|
|
41
|
+
|
|
3
42
|
## [0.35.1] - 2026-08-19
|
|
4
43
|
|
|
5
44
|
### Fixed
|
package/dist/neo.mjs
CHANGED
|
@@ -2313,7 +2313,7 @@ function formatType(type, project) {
|
|
|
2313
2313
|
)
|
|
2314
2314
|
].join(", ")}>${suffix}`;
|
|
2315
2315
|
case "action":
|
|
2316
|
-
return `NeoAction<${type.parameterTypes.map((parameter4) => formatType(parameter4, project)).join(", ")}
|
|
2316
|
+
return `${type.parameterTypes.length === 0 ? "NeoAction" : `NeoAction<${type.parameterTypes.map((parameter4) => formatType(parameter4, project)).join(", ")}>`}${suffix}`;
|
|
2317
2317
|
}
|
|
2318
2318
|
}
|
|
2319
2319
|
var UNKNOWN_TYPE;
|
|
@@ -2416,7 +2416,8 @@ function complete(snapshot, position) {
|
|
|
2416
2416
|
if (insidePlainString(snapshot.lexed.tokens, offset)) {
|
|
2417
2417
|
return { isIncomplete: false, items: [] };
|
|
2418
2418
|
}
|
|
2419
|
-
const
|
|
2419
|
+
const wordAtCursor = wordRangeAt(snapshot.source.text, offset);
|
|
2420
|
+
const word = { start: wordAtCursor.start, end: offset };
|
|
2420
2421
|
const prefix = snapshot.source.text.slice(word.start, offset);
|
|
2421
2422
|
const tokens = significantTokensBefore(snapshot.lexed.tokens, word.start);
|
|
2422
2423
|
const tail = tokens[tokens.length - 1];
|
|
@@ -4129,10 +4130,20 @@ function collectionSymbols(kind, type) {
|
|
|
4129
4130
|
method(
|
|
4130
4131
|
"Count",
|
|
4131
4132
|
{ kind: "primitive", name: "int" },
|
|
4132
|
-
[],
|
|
4133
|
-
"Return the entry count."
|
|
4133
|
+
[optionalCollectionPredicate(kind, entryType, keyType)],
|
|
4134
|
+
"Return the entry count, optionally restricted by a predicate. It may also be read as a property."
|
|
4134
4135
|
)
|
|
4135
4136
|
];
|
|
4137
|
+
if (kind === "list") {
|
|
4138
|
+
symbols.push(
|
|
4139
|
+
method(
|
|
4140
|
+
"IndexOf",
|
|
4141
|
+
{ kind: "primitive", name: "int" },
|
|
4142
|
+
[parameter2("value", entryType)],
|
|
4143
|
+
"Return the first matching index, or -1."
|
|
4144
|
+
)
|
|
4145
|
+
);
|
|
4146
|
+
}
|
|
4136
4147
|
if (!canMutate) return symbols;
|
|
4137
4148
|
if (kind === "dictionary") {
|
|
4138
4149
|
symbols.push(
|
|
@@ -4192,6 +4203,20 @@ function collectionSymbols(kind, type) {
|
|
|
4192
4203
|
function lambdaParameters(kind, entry, key) {
|
|
4193
4204
|
return kind === "dictionary" ? [parameter2("key", key), parameter2("value", entry)] : [parameter2("item", entry)];
|
|
4194
4205
|
}
|
|
4206
|
+
function optionalCollectionPredicate(kind, entry, key) {
|
|
4207
|
+
return {
|
|
4208
|
+
name: "predicate",
|
|
4209
|
+
type: {
|
|
4210
|
+
kind: "delegate",
|
|
4211
|
+
returnType: { kind: "primitive", name: "bool" },
|
|
4212
|
+
parameterTypes: lambdaParameters(kind, entry, key).map(
|
|
4213
|
+
(candidate) => candidate.type
|
|
4214
|
+
),
|
|
4215
|
+
nullable: true
|
|
4216
|
+
},
|
|
4217
|
+
defaultValue: { displayText: "null", value: null }
|
|
4218
|
+
};
|
|
4219
|
+
}
|
|
4195
4220
|
function method(name, returnType, parameters, documentation) {
|
|
4196
4221
|
return {
|
|
4197
4222
|
id: `builtin:collection:${name}`,
|
|
@@ -4311,7 +4336,13 @@ function inferLambdaParameterType(local, snapshot) {
|
|
|
4311
4336
|
(token) => token.start === snapshot.source.offsetAt(local.nameRange.start)
|
|
4312
4337
|
);
|
|
4313
4338
|
if (declarationIndex < 0) return UNKNOWN_TYPE;
|
|
4314
|
-
const lambdaMethods = /* @__PURE__ */ new Set([
|
|
4339
|
+
const lambdaMethods = /* @__PURE__ */ new Set([
|
|
4340
|
+
"Where",
|
|
4341
|
+
"First",
|
|
4342
|
+
"FirstOrDefault",
|
|
4343
|
+
"Select",
|
|
4344
|
+
"Count"
|
|
4345
|
+
]);
|
|
4315
4346
|
for (let cursor = declarationIndex - 1; cursor >= 1; cursor--) {
|
|
4316
4347
|
const open = tokens[cursor];
|
|
4317
4348
|
const method2 = tokens[cursor - 1];
|
|
@@ -4354,7 +4385,10 @@ function symbolCompletion(symbol, word, snapshot) {
|
|
|
4354
4385
|
range: snapshot.source.range(word.start, word.end),
|
|
4355
4386
|
newText: insertText
|
|
4356
4387
|
},
|
|
4357
|
-
|
|
4388
|
+
// `.` is a completion trigger, not a commit character. Treating it as
|
|
4389
|
+
// both lets VS Code accept a stale selection while an author prefixes an
|
|
4390
|
+
// existing identifier (for example, typing `this.` before a member).
|
|
4391
|
+
...callable2 ? { commitCharacters: ["("] } : {},
|
|
4358
4392
|
symbolId: symbol.id
|
|
4359
4393
|
};
|
|
4360
4394
|
}
|
|
@@ -4370,7 +4404,7 @@ function typeCompletion(type, word, snapshot) {
|
|
|
4370
4404
|
range: snapshot.source.range(word.start, word.end),
|
|
4371
4405
|
newText: type.name
|
|
4372
4406
|
},
|
|
4373
|
-
commitCharacters: ["
|
|
4407
|
+
commitCharacters: ["<"],
|
|
4374
4408
|
symbolId: type.id
|
|
4375
4409
|
};
|
|
4376
4410
|
}
|
|
@@ -4386,7 +4420,7 @@ function indexCompletion(index, word, snapshot) {
|
|
|
4386
4420
|
range: snapshot.source.range(word.start, word.end),
|
|
4387
4421
|
newText: index.name
|
|
4388
4422
|
},
|
|
4389
|
-
commitCharacters: ["
|
|
4423
|
+
commitCharacters: ["["],
|
|
4390
4424
|
symbolId: index.id
|
|
4391
4425
|
};
|
|
4392
4426
|
}
|
|
@@ -8510,7 +8544,7 @@ var NEOSCRIPT_COMPILER_REVISION;
|
|
|
8510
8544
|
var init_strict_ir = __esm({
|
|
8511
8545
|
"../packages/neoscript-language/src/strict-ir.ts"() {
|
|
8512
8546
|
"use strict";
|
|
8513
|
-
NEOSCRIPT_COMPILER_REVISION =
|
|
8547
|
+
NEOSCRIPT_COMPILER_REVISION = 13;
|
|
8514
8548
|
}
|
|
8515
8549
|
});
|
|
8516
8550
|
|
|
@@ -8535,8 +8569,10 @@ function pathRoot(path) {
|
|
|
8535
8569
|
}
|
|
8536
8570
|
function factsFromIsCheck(expression, whenTrue) {
|
|
8537
8571
|
const path = canonicalPath(expression.operand);
|
|
8538
|
-
if (!path) return [];
|
|
8539
8572
|
const nullCheck = expression.type.kind === "primitive" && expression.type.name === "null";
|
|
8573
|
+
if (path === null && (!whenTrue || nullCheck || !expression.bindingName)) {
|
|
8574
|
+
return [];
|
|
8575
|
+
}
|
|
8540
8576
|
if (whenTrue) {
|
|
8541
8577
|
return nullCheck ? [{ expression: expression.operand, path, nullValue: true }] : [
|
|
8542
8578
|
{
|
|
@@ -12156,10 +12192,10 @@ var init_strict_resolver = __esm({
|
|
|
12156
12192
|
applyFactsToScope(outerScope, targetScope, facts, mode = "all") {
|
|
12157
12193
|
for (const fact of facts) {
|
|
12158
12194
|
if (mode === "bindings-only" && !fact.bindingName) continue;
|
|
12159
|
-
const rootEntry = outerScope.lookup(pathRoot(fact.path));
|
|
12160
|
-
if (!rootEntry) continue;
|
|
12161
12195
|
const natural = this.resolveExpression(fact.expression, outerScope);
|
|
12196
|
+
const rootEntry = fact.path === null ? null : outerScope.lookup(pathRoot(fact.path));
|
|
12162
12197
|
if (fact.nullValue) {
|
|
12198
|
+
if (fact.path === null || rootEntry === null) continue;
|
|
12163
12199
|
if (fact.loose === true && natural.type.kind === "primitive" && (natural.type.name === "string" || natural.type.name === "int" || natural.type.name === "float" || natural.type.name === "bool")) {
|
|
12164
12200
|
continue;
|
|
12165
12201
|
}
|
|
@@ -12176,7 +12212,7 @@ var init_strict_resolver = __esm({
|
|
|
12176
12212
|
const naturalDefinition = natural.type.kind === "named" ? this.project.typeById.get(natural.type.typeId) : void 0;
|
|
12177
12213
|
const retainConcreteClass = !fact.bindingName && naturalDefinition?.kind === "class" && targetDefinition?.kind === "interface";
|
|
12178
12214
|
const narrowed = retainConcreteClass ? { ...natural.type, nullable: false } : { ...target, nullable: false };
|
|
12179
|
-
if (mode === "all") {
|
|
12215
|
+
if (mode === "all" && fact.path !== null && rootEntry !== null) {
|
|
12180
12216
|
targetScope.narrow(fact.path, rootEntry, narrowed);
|
|
12181
12217
|
}
|
|
12182
12218
|
if (fact.bindingName) {
|
|
@@ -12193,6 +12229,7 @@ var init_strict_resolver = __esm({
|
|
|
12193
12229
|
}
|
|
12194
12230
|
continue;
|
|
12195
12231
|
}
|
|
12232
|
+
if (fact.path === null || rootEntry === null) continue;
|
|
12196
12233
|
if (!isNullable(natural.type)) continue;
|
|
12197
12234
|
targetScope.narrow(fact.path, rootEntry, {
|
|
12198
12235
|
...natural.type,
|
|
@@ -12650,6 +12687,19 @@ var init_strict_resolver = __esm({
|
|
|
12650
12687
|
indexSurface: receiver.type
|
|
12651
12688
|
};
|
|
12652
12689
|
}
|
|
12690
|
+
if (name === "Count" && collectionPartsFor(receiver) !== null) {
|
|
12691
|
+
if (nullPropagating) {
|
|
12692
|
+
throw new CompileError(
|
|
12693
|
+
"Null-propagating collection Count access is not supported; coalesce or narrow the collection first.",
|
|
12694
|
+
pos
|
|
12695
|
+
);
|
|
12696
|
+
}
|
|
12697
|
+
return intrinsic(
|
|
12698
|
+
"count" /* Count */,
|
|
12699
|
+
{ collectionPointer: receiver.pointer },
|
|
12700
|
+
{ kind: "primitive", name: "int" }
|
|
12701
|
+
);
|
|
12702
|
+
}
|
|
12653
12703
|
const primitiveMember = resolvePrimitiveMember(receiver.type, name);
|
|
12654
12704
|
if (primitiveMember) {
|
|
12655
12705
|
if (isPrimitive(receiver.type, "DialogueRef")) {
|
|
@@ -14395,10 +14445,50 @@ var init_strict_resolver = __esm({
|
|
|
14395
14445
|
const collection = collectionPartsFor(receiver);
|
|
14396
14446
|
if (!collection) return null;
|
|
14397
14447
|
if (name === "Count") {
|
|
14398
|
-
|
|
14448
|
+
if (args.length > 1) {
|
|
14449
|
+
throw new CompileError(
|
|
14450
|
+
`Count expects zero or one predicate, got ${args.length}.`,
|
|
14451
|
+
pos
|
|
14452
|
+
);
|
|
14453
|
+
}
|
|
14454
|
+
const fn = args[0] ? this.resolveCollectionLambda(
|
|
14455
|
+
args[0],
|
|
14456
|
+
collection,
|
|
14457
|
+
{ kind: "primitive", name: "bool" },
|
|
14458
|
+
scope,
|
|
14459
|
+
pos
|
|
14460
|
+
) : void 0;
|
|
14399
14461
|
return intrinsic(
|
|
14400
14462
|
"count" /* Count */,
|
|
14401
|
-
{
|
|
14463
|
+
{
|
|
14464
|
+
collectionPointer: receiver.pointer,
|
|
14465
|
+
...fn ? { function: fn } : {}
|
|
14466
|
+
},
|
|
14467
|
+
{ kind: "primitive", name: "int" }
|
|
14468
|
+
);
|
|
14469
|
+
}
|
|
14470
|
+
if (name === "IndexOf" && collection.kind === "list") {
|
|
14471
|
+
requireArgCount(name, args, 1, pos);
|
|
14472
|
+
const expectedValueType = collectionOperationValueType(
|
|
14473
|
+
receiver,
|
|
14474
|
+
collection
|
|
14475
|
+
);
|
|
14476
|
+
const value = this.resolveExpression(
|
|
14477
|
+
requiredAt(args, 0),
|
|
14478
|
+
scope,
|
|
14479
|
+
expectedValueType
|
|
14480
|
+
);
|
|
14481
|
+
this.requireCollectionOperationAssignable(
|
|
14482
|
+
value.type,
|
|
14483
|
+
expectedValueType,
|
|
14484
|
+
receiver,
|
|
14485
|
+
collection,
|
|
14486
|
+
pos,
|
|
14487
|
+
"IndexOf argument"
|
|
14488
|
+
);
|
|
14489
|
+
return intrinsic(
|
|
14490
|
+
"indexOf" /* IndexOf */,
|
|
14491
|
+
{ collectionPointer: receiver.pointer, valuePointer: value.pointer },
|
|
14402
14492
|
{ kind: "primitive", name: "int" }
|
|
14403
14493
|
);
|
|
14404
14494
|
}
|
|
@@ -14790,6 +14880,7 @@ var init_strict_resolver = __esm({
|
|
|
14790
14880
|
* return and no optional form to carry.
|
|
14791
14881
|
*/
|
|
14792
14882
|
resolveActionCall(action, actionType, expression, scope) {
|
|
14883
|
+
this.assertActionControlAccessible(action, "invoke", expression.pos);
|
|
14793
14884
|
if (expression.callee.kind === "member" && expression.callee.optional) {
|
|
14794
14885
|
throw new CompileError(
|
|
14795
14886
|
`${this.describe(actionType)} is never null, so optional-chained invocation is not supported.`,
|
|
@@ -15047,6 +15138,7 @@ var init_strict_resolver = __esm({
|
|
|
15047
15138
|
}
|
|
15048
15139
|
const receiver = this.resolveExpression(expression.callee.receiver, scope);
|
|
15049
15140
|
if (receiver.type.kind !== "action") return null;
|
|
15141
|
+
this.assertActionControlAccessible(receiver, "clear", pos);
|
|
15050
15142
|
if (expression.callee.optional) {
|
|
15051
15143
|
throw new CompileError(
|
|
15052
15144
|
`${this.describe(receiver.type)} is never null, so optional-chained Clear is not supported.`,
|
|
@@ -15109,6 +15201,34 @@ var init_strict_resolver = __esm({
|
|
|
15109
15201
|
}
|
|
15110
15202
|
};
|
|
15111
15203
|
}
|
|
15204
|
+
/**
|
|
15205
|
+
* An action's declared accessibility controls who may subscribe. Invoking
|
|
15206
|
+
* or clearing its listener set is a separate, protected-like capability:
|
|
15207
|
+
* the declaring class and its subclasses may raise/reset the action, while
|
|
15208
|
+
* public callers remain limited to `+=` and `-=`.
|
|
15209
|
+
*/
|
|
15210
|
+
assertActionControlAccessible(action, operation, pos) {
|
|
15211
|
+
const symbol = action.symbol;
|
|
15212
|
+
const owner = symbol === void 0 ? void 0 : symbol.inheritedFrom ? this.project.typeById.get(symbol.inheritedFrom.typeId) : [...this.project.typeById.values()].find(
|
|
15213
|
+
(type) => type.members.some(
|
|
15214
|
+
(candidate) => candidate.id === symbol.id && candidate.inheritedFrom === void 0
|
|
15215
|
+
)
|
|
15216
|
+
);
|
|
15217
|
+
if (symbol !== void 0 && owner !== void 0 && memberAccessFailure({
|
|
15218
|
+
project: this.project,
|
|
15219
|
+
context: this.context,
|
|
15220
|
+
owner,
|
|
15221
|
+
symbol: { ...symbol, accessModifier: "protected" }
|
|
15222
|
+
}) === null) {
|
|
15223
|
+
return;
|
|
15224
|
+
}
|
|
15225
|
+
const verb = operation === "invoke" ? "invoked" : "cleared";
|
|
15226
|
+
const actionName = symbol === void 0 ? this.describe(action.type) : `'${owner?.name ?? symbol.inheritedFrom?.typeName ?? "Unknown"}.${symbol.name}'`;
|
|
15227
|
+
throw new CompileError(
|
|
15228
|
+
`Action ${actionName} can only be ${verb} from its declaring class or a derived class; external callers may only subscribe with '+=' or '-='.`,
|
|
15229
|
+
pos
|
|
15230
|
+
);
|
|
15231
|
+
}
|
|
15112
15232
|
resolveConditionalExpression(expression, scope, expected) {
|
|
15113
15233
|
const condition = this.toBool(
|
|
15114
15234
|
this.resolveExpression(expression.condition, scope),
|
|
@@ -25170,13 +25290,14 @@ function compileProjectSourceBodies(documents) {
|
|
|
25170
25290
|
implicitMemberAccess: true,
|
|
25171
25291
|
staticMember: false,
|
|
25172
25292
|
kind: "constructor",
|
|
25173
|
-
parameters: constructor2.parameters.map(
|
|
25174
|
-
|
|
25175
|
-
|
|
25176
|
-
|
|
25177
|
-
|
|
25178
|
-
|
|
25179
|
-
|
|
25293
|
+
parameters: constructor2.parameters.map(
|
|
25294
|
+
(parameter4, index) => sourceParameter(
|
|
25295
|
+
parameter4,
|
|
25296
|
+
owner,
|
|
25297
|
+
graph.typeIdsByName,
|
|
25298
|
+
`${declaredConstructorId(owner, declaration, constructor2)}:argument:${index}`
|
|
25299
|
+
)
|
|
25300
|
+
),
|
|
25180
25301
|
functionName: constructor2.name
|
|
25181
25302
|
},
|
|
25182
25303
|
projectIndex,
|
|
@@ -25205,13 +25326,12 @@ function compileProjectSourceBodies(documents) {
|
|
|
25205
25326
|
staticMember: false,
|
|
25206
25327
|
kind: "constructor",
|
|
25207
25328
|
parameters: (declaration.headerParameters ?? []).map(
|
|
25208
|
-
(parameter4, index) => (
|
|
25209
|
-
|
|
25210
|
-
|
|
25211
|
-
|
|
25212
|
-
|
|
25213
|
-
|
|
25214
|
-
})
|
|
25329
|
+
(parameter4, index) => sourceParameter(
|
|
25330
|
+
parameter4,
|
|
25331
|
+
owner,
|
|
25332
|
+
graph.typeIdsByName,
|
|
25333
|
+
`${initId}:argument:${index}`
|
|
25334
|
+
)
|
|
25215
25335
|
),
|
|
25216
25336
|
functionName: "init"
|
|
25217
25337
|
},
|
|
@@ -25248,13 +25368,14 @@ function compileProjectSourceBodies(documents) {
|
|
|
25248
25368
|
context: {
|
|
25249
25369
|
...baseContext2,
|
|
25250
25370
|
kind: "function",
|
|
25251
|
-
parameters: member.parameters.map(
|
|
25252
|
-
|
|
25253
|
-
|
|
25254
|
-
|
|
25255
|
-
|
|
25256
|
-
|
|
25257
|
-
|
|
25371
|
+
parameters: member.parameters.map(
|
|
25372
|
+
(parameter4, index) => sourceParameter(
|
|
25373
|
+
parameter4,
|
|
25374
|
+
owner,
|
|
25375
|
+
graph.typeIdsByName,
|
|
25376
|
+
`${info.symbol.id}:argument:${index}`
|
|
25377
|
+
)
|
|
25378
|
+
),
|
|
25258
25379
|
deferred: member.modifiers.includes("async"),
|
|
25259
25380
|
functionName: member.name
|
|
25260
25381
|
},
|
|
@@ -25621,13 +25742,9 @@ function sourceMemberSymbol(member, owner, typeIds, source) {
|
|
|
25621
25742
|
...common,
|
|
25622
25743
|
kind: "function",
|
|
25623
25744
|
returnType: type,
|
|
25624
|
-
parameters: member.parameters.map(
|
|
25625
|
-
|
|
25626
|
-
|
|
25627
|
-
type: sourceTypeRef(parameter4.type, owner, typeIds),
|
|
25628
|
-
location: location(owner.uri, parameter4.range),
|
|
25629
|
-
selectionLocation: location(owner.uri, parameter4.nameRange)
|
|
25630
|
-
})),
|
|
25745
|
+
parameters: member.parameters.map(
|
|
25746
|
+
(parameter4, index) => sourceParameter(parameter4, owner, typeIds, `${id2}:argument:${index}`)
|
|
25747
|
+
),
|
|
25631
25748
|
deferred: member.modifiers.includes("async"),
|
|
25632
25749
|
...member.modifiers.includes("native") ? { native: true } : {}
|
|
25633
25750
|
};
|
|
@@ -25699,8 +25816,7 @@ function requiredConstructorSymbol(owner, declaration, typeIds) {
|
|
|
25699
25816
|
return {
|
|
25700
25817
|
id: requiredConstructorBodyId(owner),
|
|
25701
25818
|
parameters: headerParameters.map((parameter4) => ({
|
|
25702
|
-
|
|
25703
|
-
type: sourceTypeRef(parameter4.type, owner, typeIds),
|
|
25819
|
+
...sourceParameter(parameter4, owner, typeIds),
|
|
25704
25820
|
required: !parameter4.type.nullable
|
|
25705
25821
|
})),
|
|
25706
25822
|
required: true,
|
|
@@ -25711,14 +25827,23 @@ function declaredConstructorSymbol(owner, declaration, constructor2, typeIds) {
|
|
|
25711
25827
|
return {
|
|
25712
25828
|
id: declaredConstructorId(owner, declaration, constructor2),
|
|
25713
25829
|
parameters: constructor2.parameters.map((parameter4) => ({
|
|
25714
|
-
|
|
25715
|
-
type: sourceTypeRef(parameter4.type, owner, typeIds),
|
|
25830
|
+
...sourceParameter(parameter4, owner, typeIds),
|
|
25716
25831
|
required: !parameter4.type.nullable
|
|
25717
25832
|
})),
|
|
25718
25833
|
...constructor2.docsText ? { documentation: constructor2.docsText } : {},
|
|
25719
25834
|
location: location(owner.uri, constructor2.range)
|
|
25720
25835
|
};
|
|
25721
25836
|
}
|
|
25837
|
+
function sourceParameter(parameter4, owner, typeIds, id2) {
|
|
25838
|
+
return {
|
|
25839
|
+
...id2 === void 0 ? {} : { id: id2 },
|
|
25840
|
+
name: parameter4.name,
|
|
25841
|
+
type: sourceTypeRef(parameter4.type, owner, typeIds),
|
|
25842
|
+
...parameter4.defaultValue === void 0 ? {} : { defaultValue: { displayText: parameter4.defaultValue.text } },
|
|
25843
|
+
location: location(owner.uri, parameter4.range),
|
|
25844
|
+
selectionLocation: location(owner.uri, parameter4.nameRange)
|
|
25845
|
+
};
|
|
25846
|
+
}
|
|
25722
25847
|
function substituteInheritedSourceMember(member, baseInfo, resolvedBaseType) {
|
|
25723
25848
|
if (baseInfo.declaration.kind !== "class" || resolvedBaseType.kind !== "named") {
|
|
25724
25849
|
return {
|
|
@@ -26556,6 +26681,15 @@ function validateTypeApplication(uri, type, genericParameters, environment, diag
|
|
|
26556
26681
|
return;
|
|
26557
26682
|
}
|
|
26558
26683
|
if (type.name === "NeoAction") {
|
|
26684
|
+
if (type.typeArguments.some((argument2) => argument2.name === "void")) {
|
|
26685
|
+
diagnose(
|
|
26686
|
+
diagnostics,
|
|
26687
|
+
uri,
|
|
26688
|
+
type.range,
|
|
26689
|
+
"invalid-member-type",
|
|
26690
|
+
"Type 'NeoAction' cannot use 'void' as a parameter type; spell a zero-argument action as bare 'NeoAction'."
|
|
26691
|
+
);
|
|
26692
|
+
}
|
|
26559
26693
|
if (type.typeArguments.length > 16) {
|
|
26560
26694
|
diagnose(
|
|
26561
26695
|
diagnostics,
|
|
@@ -29087,10 +29221,8 @@ function maskOutsideProjectBody(text, start, end) {
|
|
|
29087
29221
|
}
|
|
29088
29222
|
function projectWordRange(text, offset) {
|
|
29089
29223
|
let start = offset;
|
|
29090
|
-
let end = offset;
|
|
29091
29224
|
while (start > 0 && /[A-Za-z0-9_]/.test(text[start - 1] ?? "")) start--;
|
|
29092
|
-
|
|
29093
|
-
return { start, end };
|
|
29225
|
+
return { start, end: offset };
|
|
29094
29226
|
}
|
|
29095
29227
|
function isContextualDot(text, wordStart) {
|
|
29096
29228
|
if (text[wordStart - 1] !== ".") return false;
|
|
@@ -42957,6 +43089,13 @@ function isNSFunctionContains(value) {
|
|
|
42957
43089
|
if (typeof info !== "object" || info === null) return false;
|
|
42958
43090
|
return isNSPointer(info.collectionPointer) && isNSPointer(info.valuePointer);
|
|
42959
43091
|
}
|
|
43092
|
+
function isNSFunctionIndexOf(value) {
|
|
43093
|
+
const v = value;
|
|
43094
|
+
if (v?.type !== "indexOf" /* indexOf */) return false;
|
|
43095
|
+
const info = v.info;
|
|
43096
|
+
if (typeof info !== "object" || info === null) return false;
|
|
43097
|
+
return isNSPointer(info.collectionPointer) && isNSPointer(info.valuePointer);
|
|
43098
|
+
}
|
|
42960
43099
|
function isNSFunctionStringOp(value) {
|
|
42961
43100
|
const v = value;
|
|
42962
43101
|
if (v?.type !== "stringOp" /* stringOp */) return false;
|
|
@@ -43010,12 +43149,7 @@ function isNSFunctionListRepeat(value) {
|
|
|
43010
43149
|
}
|
|
43011
43150
|
function isNSFunctionCount(value) {
|
|
43012
43151
|
const v = value;
|
|
43013
|
-
|
|
43014
|
-
const info = v.info;
|
|
43015
|
-
if (typeof info !== "object" || info === null) return false;
|
|
43016
|
-
return isNSPointer(
|
|
43017
|
-
info.collectionPointer
|
|
43018
|
-
);
|
|
43152
|
+
return v?.type === "count" /* count */ && isNSCollectionOptionalBoolFunction(v.info);
|
|
43019
43153
|
}
|
|
43020
43154
|
function isNSDialogueMemoryFunctionInfo(value) {
|
|
43021
43155
|
if (typeof value !== "object" || value === null) return false;
|
|
@@ -43052,7 +43186,7 @@ function isNSFunctionImageSlice(value) {
|
|
|
43052
43186
|
return isNSPointer(info.sliceIndexPointer);
|
|
43053
43187
|
}
|
|
43054
43188
|
function isNSFunction(value) {
|
|
43055
|
-
return isNSFunctionClassConstructor(value) || isNSFunctionClassClone(value) || isNSFunctionListIndex(value) || isNSFunctionSelect(value) || isNSFunctionFirst(value) || isNSFunctionFirstOrDefault(value) || isNSFunctionWhere(value) || isNSFunctionContains(value) || // stringOp was missing from this chain since its introduction — a
|
|
43189
|
+
return isNSFunctionClassConstructor(value) || isNSFunctionClassClone(value) || isNSFunctionListIndex(value) || isNSFunctionSelect(value) || isNSFunctionFirst(value) || isNSFunctionFirstOrDefault(value) || isNSFunctionWhere(value) || isNSFunctionContains(value) || isNSFunctionIndexOf(value) || // stringOp was missing from this chain since its introduction — a
|
|
43056
43190
|
// compiled getter using ToLower()/StartsWith() would fail pointer
|
|
43057
43191
|
// validation anywhere isNSFunction gates. Fixed alongside decimalOp.
|
|
43058
43192
|
isNSFunctionStringOp(value) || isNSFunctionDecimalOp(value) || isNSFunctionMathOp(value) || isNSFunctionListRepeat(value) || isNSFunctionCount(value) || isNSFunctionVisitCount(value) || isNSFunctionHasVisited(value) || isNSFunctionVectorConstructor(value) || isNSFunctionImageSlice(value) || isNSFunctionVariantInitialize(value) || isNSFunctionVariantApply(value) || isNSFunctionDeclaredConstructor(value);
|
|
@@ -43277,7 +43411,12 @@ function minimumNeoScriptCompilerRevisionForIR(node, visited = /* @__PURE__ */ n
|
|
|
43277
43411
|
visited.add(node);
|
|
43278
43412
|
const discriminatorRevision = "type" in node && typeof node.type === "string" ? MINIMUM_REVISION_BY_IR_DISCRIMINATOR.get(node.type) ?? 1 : 1;
|
|
43279
43413
|
const fallbackRevision = "missingMemberFallback" in node && node.missingMemberFallback === "valueEquality" ? 12 : 1;
|
|
43280
|
-
|
|
43414
|
+
const countPredicateRevision = "type" in node && node.type === "count" /* count */ && "info" in node && typeof node.info === "object" && node.info !== null && "function" in node.info && node.info.function !== void 0 && node.info.function !== null ? 13 : 1;
|
|
43415
|
+
let minimum = Math.max(
|
|
43416
|
+
discriminatorRevision,
|
|
43417
|
+
fallbackRevision,
|
|
43418
|
+
countPredicateRevision
|
|
43419
|
+
);
|
|
43281
43420
|
const isLiteralValue = "typeInfo" in node && "value" in node && !("type" in node);
|
|
43282
43421
|
for (const [key, child] of Object.entries(node)) {
|
|
43283
43422
|
if (isLiteralValue && key === "value") continue;
|
|
@@ -43379,7 +43518,8 @@ var init_neoscript_guards = __esm({
|
|
|
43379
43518
|
["removeActionListener" /* removeActionListener */, 8],
|
|
43380
43519
|
["callAction" /* callAction */, 8],
|
|
43381
43520
|
["conditional" /* conditional */, 12],
|
|
43382
|
-
["delegateClosure" /* delegateClosure */, 12]
|
|
43521
|
+
["delegateClosure" /* delegateClosure */, 12],
|
|
43522
|
+
["indexOf" /* indexOf */, 13]
|
|
43383
43523
|
]);
|
|
43384
43524
|
}
|
|
43385
43525
|
});
|
|
@@ -59595,6 +59735,7 @@ function lowerExpressionValue(context, expression, declaredExpected, ownerClass,
|
|
|
59595
59735
|
function positionRequiresEvaluation(context, expression, expected, ownerClass) {
|
|
59596
59736
|
if (isStoredDelegateLiteral(expected, expression)) return false;
|
|
59597
59737
|
if (isStoredActionLiteral(expected, expression)) return false;
|
|
59738
|
+
if (isSourceMemberValueRead(context, expression, ownerClass)) return true;
|
|
59598
59739
|
return initializerRequiresEvaluation(
|
|
59599
59740
|
context.declaredConstructors,
|
|
59600
59741
|
expression,
|
|
@@ -59605,6 +59746,36 @@ function positionRequiresEvaluation(context, expression, expected, ownerClass) {
|
|
|
59605
59746
|
)
|
|
59606
59747
|
);
|
|
59607
59748
|
}
|
|
59749
|
+
function isSourceMemberValueRead(context, expression, ownerClass) {
|
|
59750
|
+
let current = expression;
|
|
59751
|
+
while (current.kind === "annotated") current = current.expression;
|
|
59752
|
+
const path = expressionMemberPath(current);
|
|
59753
|
+
if (path === null) return false;
|
|
59754
|
+
const parts = path.split(".");
|
|
59755
|
+
let classId;
|
|
59756
|
+
let memberName;
|
|
59757
|
+
let requiresStatic = false;
|
|
59758
|
+
if (parts.length === 1 || parts.length === 2 && parts[0] === "this") {
|
|
59759
|
+
classId = materializedId(ownerClass, "class", ownerClass.name);
|
|
59760
|
+
memberName = parts.at(-1);
|
|
59761
|
+
} else if (parts.length === 2) {
|
|
59762
|
+
classId = context.classIdsByName.get(parts[0]);
|
|
59763
|
+
memberName = parts[1];
|
|
59764
|
+
requiresStatic = true;
|
|
59765
|
+
} else {
|
|
59766
|
+
const receiverPath = parts.slice(0, -1).join(".");
|
|
59767
|
+
classId = context.rootValueTargetsByPath.get(receiverPath)?.classId;
|
|
59768
|
+
memberName = parts.at(-1);
|
|
59769
|
+
}
|
|
59770
|
+
if (classId === void 0 || memberName === void 0) return false;
|
|
59771
|
+
const memberId = effectiveMemberIdByName(context, classId, memberName);
|
|
59772
|
+
if (memberId === null) return false;
|
|
59773
|
+
const source = context.sourceMembersById.get(memberId)?.member;
|
|
59774
|
+
if (source?.kind === "function") return false;
|
|
59775
|
+
const schema = context.loweredMembers.get(memberId) ?? context.baseMembers.get(memberId);
|
|
59776
|
+
const isStatic = source?.modifiers.includes("static") ?? schema?.isStatic ?? false;
|
|
59777
|
+
return !requiresStatic || isStatic;
|
|
59778
|
+
}
|
|
59608
59779
|
function isStoredDelegateLiteral(type, expression) {
|
|
59609
59780
|
if (type.name !== "NeoDelegate") return false;
|
|
59610
59781
|
let current = expression;
|
|
@@ -70420,7 +70591,23 @@ function evalFunction(fn, scope, ctx) {
|
|
|
70420
70591
|
}
|
|
70421
70592
|
case "count" /* count */: {
|
|
70422
70593
|
const c = evalPointer(fn.info.collectionPointer, scope, ctx);
|
|
70423
|
-
|
|
70594
|
+
const innerFn = fn.info.function ?? null;
|
|
70595
|
+
if (innerFn === null) return collectionLength(c);
|
|
70596
|
+
const isList = isListCollection(c);
|
|
70597
|
+
let count = 0;
|
|
70598
|
+
const callback = prepareCollectionCallback(
|
|
70599
|
+
innerFn,
|
|
70600
|
+
scope,
|
|
70601
|
+
ctx,
|
|
70602
|
+
isList,
|
|
70603
|
+
"predicate",
|
|
70604
|
+
() => {
|
|
70605
|
+
count += 1;
|
|
70606
|
+
return 0 /* Continue */;
|
|
70607
|
+
}
|
|
70608
|
+
);
|
|
70609
|
+
iterateCollection(c, ctx, callback);
|
|
70610
|
+
return count;
|
|
70424
70611
|
}
|
|
70425
70612
|
case "contains" /* contains */: {
|
|
70426
70613
|
const c = evalPointer(fn.info.collectionPointer, scope, ctx);
|
|
@@ -70444,6 +70631,23 @@ function evalFunction(fn, scope, ctx) {
|
|
|
70444
70631
|
});
|
|
70445
70632
|
return containsResolvedEntry;
|
|
70446
70633
|
}
|
|
70634
|
+
case "indexOf" /* indexOf */: {
|
|
70635
|
+
const collection = listArg(
|
|
70636
|
+
evalPointer(fn.info.collectionPointer, scope, ctx),
|
|
70637
|
+
"IndexOf receiver"
|
|
70638
|
+
);
|
|
70639
|
+
const target = evalPointer(fn.info.valuePointer, scope, ctx);
|
|
70640
|
+
const targetReferenceId = typeof target === "string" ? target : findKnownRowIdByValueReference(target, ctx);
|
|
70641
|
+
let found = -1;
|
|
70642
|
+
iterateCollection(collection, ctx, (entry, key, valueId) => {
|
|
70643
|
+
if (valueId !== null && valueId === targetReferenceId || jsEqual(entry, target)) {
|
|
70644
|
+
found = numberArg(key, "IndexOf index");
|
|
70645
|
+
return 1 /* Break */;
|
|
70646
|
+
}
|
|
70647
|
+
return 0 /* Continue */;
|
|
70648
|
+
});
|
|
70649
|
+
return found;
|
|
70650
|
+
}
|
|
70447
70651
|
case "decimalOp" /* decimalOp */: {
|
|
70448
70652
|
const info = fn.info;
|
|
70449
70653
|
const receiverRaw = evalPointer(info.receiverPointer, scope, ctx);
|
|
@@ -113627,7 +113831,7 @@ var init_registry2 = __esm({
|
|
|
113627
113831
|
PROJECT_SCHEMA_CONTRACT = Object.freeze({
|
|
113628
113832
|
formatVersion: 3,
|
|
113629
113833
|
contractVersion: "3.14",
|
|
113630
|
-
cliVersion: "0.
|
|
113834
|
+
cliVersion: "0.36.1",
|
|
113631
113835
|
projectFileUploadBatchSize: 32,
|
|
113632
113836
|
documentRecords: {
|
|
113633
113837
|
member: {
|
|
@@ -120229,7 +120433,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
|
|
|
120229
120433
|
async function main() {
|
|
120230
120434
|
const args = parseArgs(process.argv.slice(2));
|
|
120231
120435
|
if (args.command === "--version") {
|
|
120232
|
-
console.log("0.
|
|
120436
|
+
console.log("0.36.1");
|
|
120233
120437
|
return;
|
|
120234
120438
|
}
|
|
120235
120439
|
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.
|
|
86
|
+
<!-- reviewed-through-cli: 0.36.1 -->
|
|
87
87
|
```
|
|
88
88
|
|
|
89
89
|
The quoted version above is checked too, so this instruction cannot go stale
|
|
@@ -154,6 +154,12 @@ components must be numeric literals. When a computed list or dictionary entry
|
|
|
154
154
|
carries an authored `@id`, parenthesize the expression — `@id("row")
|
|
155
155
|
(A.B / A.C)` — so the annotation names the row rather than its left operand.
|
|
156
156
|
|
|
157
|
+
A direct field or property read is computed too, whether it is local,
|
|
158
|
+
qualified static, or reached through a stable root path. For example,
|
|
159
|
+
`protected int Slots = FeatureFlags.StartInventorySize;` evaluates the current
|
|
160
|
+
static field or getter whenever a new instance is constructed; it is not baked
|
|
161
|
+
into a persisted literal during push.
|
|
162
|
+
|
|
157
163
|
Declare overloadable constructors as members when each constructor body owns
|
|
158
164
|
its parameter scope:
|
|
159
165
|
|
|
@@ -80,9 +80,14 @@ Prefer `--json` for automation.
|
|
|
80
80
|
|
|
81
81
|
Inside an inline Class body, unqualified names resolve against that Class;
|
|
82
82
|
invoke delegate and action members directly with `Selector()` or `OnChanged()`.
|
|
83
|
+
Spell a zero-argument action as bare `NeoAction`; type arguments name action
|
|
84
|
+
parameters, so `NeoAction<void>` is invalid rather than a zero-argument alias.
|
|
83
85
|
Clear every subscribed listener from a stored action with
|
|
84
86
|
`this.OnChanged.Clear()`. `Clear()` is a mutating statement and follows the
|
|
85
|
-
action member's effective storage and writability.
|
|
87
|
+
action member's effective storage and writability. Only the action's declaring
|
|
88
|
+
class and derived classes may invoke it or call `Clear()`; public external
|
|
89
|
+
callers may subscribe and unsubscribe with `+=` and `-=` but cannot raise or
|
|
90
|
+
reset the action.
|
|
86
91
|
|
|
87
92
|
Inline `NeoDelegate` lambdas may be returned or stored and may read surrounding
|
|
88
93
|
locals and parameters. Those values are captured when the lambda is created,
|
|
@@ -172,7 +177,17 @@ enum, parameter, or local may shadow it. Qualified members use their owner's
|
|
|
172
177
|
namespace and may be named `Math`; access one as `this.Math` or
|
|
173
178
|
`SomeValue.Math` without colliding with the builtin qualifier.
|
|
174
179
|
|
|
175
|
-
##
|
|
180
|
+
## Collection builtins
|
|
181
|
+
|
|
182
|
+
Lists expose their size as either `items.Count` or `items.Count()`. Use
|
|
183
|
+
`items.Count((item) => predicate)` to count only matching entries without
|
|
184
|
+
materializing a filtered list. The same Count forms work on Dictionary and Set
|
|
185
|
+
collections; dictionary predicates may accept `(key, value)` just like the
|
|
186
|
+
other collection callbacks.
|
|
187
|
+
|
|
188
|
+
Use `items.IndexOf(value)` to return the zero-based index of the first equal
|
|
189
|
+
list entry, or `-1` when the value is absent. `IndexOf` is list-only and uses
|
|
190
|
+
the same value/reference equality as `Contains`.
|
|
176
191
|
|
|
177
192
|
Use `List.Repeat(value, count)` to build a list of `count` copies of a value
|
|
178
193
|
instead of hand-counting a literal or looping around `Add`:
|