@neocompose/cli 0.89.1 → 0.89.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 CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.89.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 7095bfe: Fix collection paths assigned to reference sets being treated as single stored references. Preserve computed selections in nested initializers and field defaults, allow static collection defaults, and locate malformed reference selections at their source expression.
8
+ - db2d1d5: Preserve reference-set default types when selection literals contain computed entries. Method results, mixed stored and computed targets, and constructor parameters now prepare and export consistently while retaining target identity.
9
+ - 03bb210: Keep constructed objects in member receiver chains after initializer blocks. Restore completion, hover, navigation, and method signature help after expressions such as `new Box<Item> { Value=new() }.Value`.
10
+ - 68831cd: Complete generic type arguments in constructors and method calls, including nested types, incomplete expressions, scoped type parameters, and constrained method arguments. Keep ordinary argument commas in value completion.
11
+ - 5b8dac4: Stop offering named arguments for ordinary method and delegate calls, which require positional arguments. Keep typed argument completion and report unsupported named calls in initializer diagnostics before evaluation.
12
+ - cb282c1: Suggest the supported id and withProvenance arguments in typed Reference calls across initializers, functions, and decorator values. Preserve existing labels and positional value completion, and omit unsupported provenance arguments on file references and listener metadata.
13
+ - 5039d9b: Infer editor parameter types from a lambda's expected delegate type. Restore member completion, hover, and navigation in stored lambdas, including nested collection values, constructors, and tuple parameters.
14
+ - 582d9a7: Use delegate return types for stored lambda completion, including contextual enum values, booleans, and returned object initializers. Preserve local assignment and nested call context, constructor argument names, and variant discovery. Distinguish switch arm separators from lambda arrows.
15
+ - f2f83cb: Preserve expected result types in switch-arm completions, including target-typed constructors, initializer fields, and enum values nested in collections and constructor arguments.
16
+ - 82ce448: Complete target-typed construction fields and arguments within tuple elements. Preserve tuple element types through nested tuples, collections, constructor arguments, and grouping parentheses.
17
+
3
18
  ## 0.89.1
4
19
 
5
20
  ### Patch Changes
@@ -19377,7 +19377,7 @@ var init_registry = __esm({
19377
19377
  "../packages/manifest/schema-contract/registry.mjs"() {
19378
19378
  "use strict";
19379
19379
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
19380
- cliVersion: "0.89.1",
19380
+ cliVersion: "0.89.2",
19381
19381
  projectFileUploadBatchSize: 32,
19382
19382
  documentRecords: {
19383
19383
  member: {
@@ -62535,18 +62535,19 @@ function buildNeoScriptEditorIndex(lexed, kind) {
62535
62535
  lexed,
62536
62536
  diagnostics
62537
62537
  );
62538
+ const regionCalls = indexCalls(executable, executablePairs, lexed);
62538
62539
  const regionLocals = applySwitchSectionScopes(
62539
62540
  indexLocals(
62540
62541
  executable,
62541
62542
  executablePairs,
62542
62543
  lexed,
62543
62544
  diagnostics,
62544
- lambdaBodies
62545
+ lambdaBodies,
62546
+ regionCalls
62545
62547
  ),
62546
62548
  regionSwitches,
62547
62549
  lexed
62548
62550
  );
62549
- const regionCalls = indexCalls(executable, executablePairs, lexed);
62550
62551
  const declarations = new Set(
62551
62552
  regionLocals.map(
62552
62553
  (local) => `${local.nameRange.start.line}:${local.nameRange.start.character}`
@@ -62999,9 +63000,10 @@ function duplicateUnitDiagnostic(label, units, diagnostics) {
62999
63000
  ]
63000
63001
  });
63001
63002
  }
63002
- function indexLocals(tokens, pairs, lexed, diagnostics, lambdaBodies) {
63003
+ function indexLocals(tokens, pairs, lexed, diagnostics, lambdaBodies, calls) {
63003
63004
  const locals = [];
63004
63005
  const braceScope = braceScopeIndex(tokens, lexed.end);
63006
+ const switchArrows = switchExpressionArrowIndexes(tokens, calls);
63005
63007
  for (let index = 0; index < tokens.length; index++) {
63006
63008
  const caught = tryParseCatchParameter(tokens, index, pairs, lexed);
63007
63009
  if (caught) {
@@ -63022,7 +63024,13 @@ function indexLocals(tokens, pairs, lexed, diagnostics, lambdaBodies) {
63022
63024
  index = loop.resumeTokenIndex;
63023
63025
  continue;
63024
63026
  }
63025
- const lambda = tryParseLambdaParameters(tokens, index, pairs, lexed);
63027
+ const lambda = tryParseLambdaParameters(
63028
+ tokens,
63029
+ index,
63030
+ pairs,
63031
+ lexed,
63032
+ switchArrows
63033
+ );
63026
63034
  if (lambda) {
63027
63035
  locals.push(...lambda.locals);
63028
63036
  lambdaBodies.push(lambda.body);
@@ -63183,7 +63191,35 @@ function statementEndTokenIndex(tokens, start) {
63183
63191
  }
63184
63192
  return tokens.length;
63185
63193
  }
63186
- function tryParseLambdaParameters(tokens, start, pairs, lexed) {
63194
+ function switchExpressionArrowIndexes(tokens, calls) {
63195
+ const enclosing = enclosingOpenerIndex(tokens);
63196
+ const inResult = /* @__PURE__ */ new Set();
63197
+ const arrows = /* @__PURE__ */ new Set();
63198
+ const genericEnds = /* @__PURE__ */ new Map();
63199
+ for (const call of calls) {
63200
+ const first = call.typeArgumentTokens?.[0];
63201
+ if (first) genericEnds.set(first.start, call.openParenOffset);
63202
+ }
63203
+ for (let index = 0; index < tokens.length; index++) {
63204
+ const token = tokens[index];
63205
+ const genericEnd = genericEnds.get(token.start);
63206
+ if (genericEnd !== void 0) {
63207
+ while (tokens[index + 1] && tokens[index + 1].start < genericEnd)
63208
+ index++;
63209
+ continue;
63210
+ }
63211
+ const opener = enclosing[index];
63212
+ if (tokens[opener]?.text !== "{" || tokens[opener - 1]?.text !== "switch")
63213
+ continue;
63214
+ if (token.text === ",") inResult.delete(opener);
63215
+ else if (token.text === "=>" && !inResult.has(opener)) {
63216
+ arrows.add(index);
63217
+ inResult.add(opener);
63218
+ }
63219
+ }
63220
+ return arrows;
63221
+ }
63222
+ function tryParseLambdaParameters(tokens, start, pairs, lexed, switchArrows) {
63187
63223
  const open2 = tokens[start];
63188
63224
  if (!open2) return null;
63189
63225
  if (open2.kind === "identifier") {
@@ -63191,6 +63227,7 @@ function tryParseLambdaParameters(tokens, start, pairs, lexed) {
63191
63227
  if (!arrow2 || arrow2.kind !== "operator" || arrow2.text !== "=>") {
63192
63228
  return null;
63193
63229
  }
63230
+ if (switchArrows.has(start + 1)) return null;
63194
63231
  const body2 = lambdaBodySpan(tokens, start + 1, pairs, lexed);
63195
63232
  return {
63196
63233
  locals: [
@@ -63215,6 +63252,7 @@ function tryParseLambdaParameters(tokens, start, pairs, lexed) {
63215
63252
  if (closeIndex === void 0) return null;
63216
63253
  const arrow = tokens[closeIndex + 1];
63217
63254
  if (!arrow || arrow.kind !== "operator" || arrow.text !== "=>") return null;
63255
+ if (switchArrows.has(closeIndex + 1)) return null;
63218
63256
  const body = lambdaBodySpan(tokens, closeIndex + 1, pairs, lexed);
63219
63257
  const locals = [];
63220
63258
  for (const [parameterIndex, [from, to]] of patternSegments(
@@ -95922,6 +95960,8 @@ function validateOwnedStaticDefault(uri, member, ownerName, scope, environment,
95922
95960
  const initializer = member.initializer;
95923
95961
  if (initializer === void 0) return;
95924
95962
  if (member.type.name === "Reference") return;
95963
+ if (member.type.name === "Set" && member.type.typeArguments[0]?.name === "Reference")
95964
+ return;
95925
95965
  let expression;
95926
95966
  try {
95927
95967
  expression = parseExpression(initializer.text);
@@ -97044,6 +97084,21 @@ function knownGeneratorExpression(expression, scope, environment) {
97044
97084
  return candidates !== void 0 && candidates.length > 0 && candidates.every((member) => member.generator === true);
97045
97085
  }
97046
97086
  function validateExpression(expression, expected, scope, environment, uri, range2, diagnostics, anchor, storedValue = true) {
97087
+ if (anchor !== void 0 && expression.kind === "call") {
97088
+ const error = sourceCallBinding(expression, scope, environment)?.error;
97089
+ if (error?.message.includes(
97090
+ "Named arguments are not supported for this call."
97091
+ )) {
97092
+ pushDiagnostic(
97093
+ diagnostics,
97094
+ uri,
97095
+ anchorSpan(anchor, error, 1),
97096
+ "invalid-function-call",
97097
+ error.message.replace(/^\d+:\d+: /, "")
97098
+ );
97099
+ return;
97100
+ }
97101
+ }
97047
97102
  if (expression.kind === "new" && expression.typeArguments?.length) {
97048
97103
  const { context } = initializerResolver(
97049
97104
  environment,
@@ -123968,7 +124023,7 @@ function lowerFieldMember(context, ownerClass, declaration, id2, commonInput, ba
123968
124023
  storesSelectionArray,
123969
124024
  `${ownerClass.name}.${declaration.name}`,
123970
124025
  !declaration.modifiers.includes("static") && contextualEnumArgument(settings, "kind") === "Unordered",
123971
- reference2 !== null
124026
+ reference2 !== null ? fieldType : null
123972
124027
  )
123973
124028
  };
123974
124029
  if (fieldType.tupleElements) {
@@ -125246,7 +125301,7 @@ function spellsReference(expression) {
125246
125301
  }
125247
125302
  return expression.kind === "call" && expression.callee.kind === "ident" && expression.callee.name === "Reference";
125248
125303
  }
125249
- function lowerDefault(context, initializer, type2, ownerClass, base, memberId, storesSelectionArray, path, unorderedList = false, reference2 = false) {
125304
+ function lowerDefault(context, initializer, type2, ownerClass, base, memberId, storesSelectionArray, path, unorderedList = false, referenceType = null) {
125250
125305
  if (initializer === null) {
125251
125306
  return base?.defaultValue && "serverValueId" in base.defaultValue ? base.defaultValue : null;
125252
125307
  }
@@ -125268,9 +125323,14 @@ function lowerDefault(context, initializer, type2, ownerClass, base, memberId, s
125268
125323
  }
125269
125324
  if (base?.defaultValue != null && matchesStoredInitializer(base.defaultValue, initializer))
125270
125325
  return base.defaultValue;
125271
- const deferred = reference2 ? deferredReferenceDefault(context, expression) : null;
125326
+ const deferred = referenceType !== null && (referenceType.name !== "Set" || expression.kind === "litList") ? deferredReferenceDefault(context, expression) : null;
125272
125327
  if (deferred !== null) return { value: deferred, classId: null };
125273
- if (unorderedList && expression.kind === "litList" && expression.elements.length > 0 || constructsValue(expression) && slotForwardsTypeParameter(context, type2, ownerClass) || positionRequiresEvaluation(context, expression, type2, ownerClass)) {
125328
+ if (unorderedList && expression.kind === "litList" && expression.elements.length > 0 || constructsValue(expression) && slotForwardsTypeParameter(context, type2, ownerClass) || positionRequiresEvaluation(
125329
+ context,
125330
+ expression,
125331
+ referenceType ?? type2,
125332
+ ownerClass
125333
+ )) {
125274
125334
  return {
125275
125335
  init: {
125276
125336
  code: context.settleInitializerReferences(
@@ -125286,7 +125346,7 @@ function lowerDefault(context, initializer, type2, ownerClass, base, memberId, s
125286
125346
  if (context.rowBackedDefaultMemberIds.has(memberId) && base?.defaultValue && !("serverValueId" in base.defaultValue)) {
125287
125347
  return base.defaultValue;
125288
125348
  }
125289
- const slot = reference2 ? slotReferenceType(type2) : null;
125349
+ const slot = referenceType !== null ? slotReferenceType(type2) : null;
125290
125350
  if (slot !== null && !spellsReference(expression)) {
125291
125351
  throw new Error(
125292
125352
  `A ${formatManifestType(slot)} default names a stored ${formatManifestType(requiredTypeArgument(slot, 0))}, not a value. Wrap a member path in Reference(), or give a row id.`
@@ -162374,8 +162434,14 @@ function memberInitializerRequiresEvaluation(context, member, expression, source
162374
162434
  return false;
162375
162435
  }
162376
162436
  if (member.kind === "lookup" && expression.kind === "litList") {
162437
+ const elementMember = { ...member, multiselect: false };
162377
162438
  return expression.elements.some(
162378
- (element) => memberInitializerRequiresEvaluation(context, member, element, source)
162439
+ (element) => memberInitializerRequiresEvaluation(
162440
+ context,
162441
+ elementMember,
162442
+ element,
162443
+ source
162444
+ )
162379
162445
  );
162380
162446
  }
162381
162447
  const unorderedDefault = source.purpose === "memberDefault" && member.id === source.memberId && member.kind === "list" && member.listKind === "unordered" && expression.kind === "litList" && expression.elements.length > 0;
@@ -162416,6 +162482,7 @@ function deferredPathSpelling(context, expression, expected) {
162416
162482
  }
162417
162483
  function isDeferredPathReferenceValue(context, sourceExpression, expected) {
162418
162484
  const expression = annotatedValue(sourceExpression).expression;
162485
+ if (expected.multiselect && expression.kind !== "litList") return false;
162419
162486
  const elements = expression.kind === "litList" ? expression.elements : [expression];
162420
162487
  let indexPaths = 0;
162421
162488
  for (const element of elements) {
@@ -166292,9 +166359,13 @@ function lowerEnum2(context, member, expression) {
166292
166359
  });
166293
166360
  }
166294
166361
  function lowerReferences(context, member, expression, ownerValueId, source) {
166362
+ function refuse(message, at3 = expression) {
166363
+ context.loweringFailures.push({ message, site: referenceSite(source, at3) });
166364
+ throw new ReportedLoweringFailure(message);
166365
+ }
166295
166366
  const elements = member.multiselect ? expression.kind === "litList" ? expression.elements : null : [expression];
166296
166367
  if (elements === null) {
166297
- throw new Error(`Reference member ${member.name} requires [...].`);
166368
+ refuse(`Reference member ${member.name} requires [...].`);
166298
166369
  }
166299
166370
  const ids = [];
166300
166371
  for (const [index, entry] of elements.entries()) {
@@ -166312,7 +166383,7 @@ function lowerReferences(context, member, expression, ownerValueId, source) {
166312
166383
  }
166313
166384
  const target = referenceTargetSpelling(context, entry);
166314
166385
  if (target === null) {
166315
- throw new Error(`Reference member ${member.name} has an invalid target.`);
166386
+ refuse(`Reference member ${member.name} has an invalid target.`, entry);
166316
166387
  }
166317
166388
  if (target.spelling === "key" || target.spelling === "local") {
166318
166389
  ids.push(`${PENDING_ID_PREFIX}reference-key:${source.label}:${index}`);
@@ -166335,9 +166406,7 @@ function lowerReferences(context, member, expression, ownerValueId, source) {
166335
166406
  ids.push(target.spelling === "id" ? target.id : target.resolved.id);
166336
166407
  }
166337
166408
  if (new Set(ids).size !== ids.length) {
166338
- throw new Error(
166339
- `Reference member ${member.name} cannot contain duplicate targets.`
166340
- );
166409
+ refuse(`Reference member ${member.name} cannot contain duplicate targets.`);
166341
166410
  }
166342
166411
  return ids;
166343
166412
  }
@@ -185150,43 +185219,6 @@ var init_export_state = __esm({
185150
185219
  }
185151
185220
  });
185152
185221
 
185153
- // ../packages/neoscript-language/src/argument-name-completions.ts
185154
- function argumentNameCompletions(fields, tokens, start, prefixEnd, position) {
185155
- const edit = argumentNameCompletionEdit(tokens, start, prefixEnd, position);
185156
- if (!edit) return [];
185157
- const { range: range2, hasColon } = edit;
185158
- const items = [];
185159
- for (const field of fields) {
185160
- const insertText = hasColon ? field.name : `${field.name}: `;
185161
- items.push({
185162
- label: field.name,
185163
- kind: "property",
185164
- detail: field.defaultText === void 0 ? field.type : `[${field.type} = ${field.defaultText}]`,
185165
- insertText,
185166
- textEdit: { range: range2, newText: insertText }
185167
- });
185168
- }
185169
- return items;
185170
- }
185171
- function argumentNameCompletionEdit(tokens, start, prefixEnd, position) {
185172
- const first = tokens[start];
185173
- if (prefixEnd > start + 1) return null;
185174
- if (prefixEnd > start && (!first || !isCallArgumentLabelToken(first)))
185175
- return null;
185176
- const editingName = first !== void 0 && isCallArgumentLabelToken(first) && positionCompare(first.range.start, position) <= 0 && positionCompare(position, first.range.end) <= 0;
185177
- if (first && isCallArgumentLabelToken(first) && !editingName) return null;
185178
- const hasColon = editingName && tokens[start + 1]?.text === ":";
185179
- const range2 = editingName ? first.range : { start: position, end: position };
185180
- return { range: range2, hasColon };
185181
- }
185182
- var init_argument_name_completions = __esm({
185183
- "../packages/neoscript-language/src/argument-name-completions.ts"() {
185184
- "use strict";
185185
- init_executable_parser();
185186
- init_project_source_tokens();
185187
- }
185188
- });
185189
-
185190
185222
  // ../packages/neoscript-language/src/tuple-symbols.ts
185191
185223
  function tupleElementSymbol(element, index, alias = false) {
185192
185224
  const location3 = alias ? void 0 : element.labelLocation;
@@ -185528,15 +185560,7 @@ function complete(snapshot, position) {
185528
185560
  const tokens = significantTokensBefore(snapshot.lexed.tokens, word.start);
185529
185561
  const tail = tokens[tokens.length - 1];
185530
185562
  let candidates;
185531
- const genericMember = tail?.text === "<" ? resolveChain(
185532
- snapshot,
185533
- expressionTokensBefore(tokens, tokens.length - 1),
185534
- offset
185535
- )?.symbol : void 0;
185536
- const genericMemberId = genericMember?.id;
185537
- const firstTypeParameter = genericMember?.typeParameters?.[0]?.type;
185538
- const functionConstraint = firstTypeParameter?.kind === "typeParameter" && firstTypeParameter.constraint?.kind === "named" ? firstTypeParameter.constraint.typeId : void 0;
185539
- const nativeGenericClassId = genericMemberId === NEO_TILE_CONVERT_MEMBER_ID ? NEO_TILE_CLASS_ID : functionConstraint;
185563
+ const genericTypes = typeArgumentCompletionItems(snapshot, tokens, word);
185540
185564
  const catchFilterCandidates = catchFilterCompletionItems(
185541
185565
  snapshot,
185542
185566
  word.start,
@@ -185553,7 +185577,9 @@ function complete(snapshot, position) {
185553
185577
  prefix,
185554
185578
  word
185555
185579
  );
185556
- if (catchFilterCandidates) {
185580
+ if (genericTypes) {
185581
+ candidates = genericTypes;
185582
+ } else if (catchFilterCandidates) {
185557
185583
  candidates = catchFilterCandidates;
185558
185584
  } else if (catchClauseCandidates) {
185559
185585
  candidates = catchClauseCandidates;
@@ -185580,14 +185606,6 @@ function complete(snapshot, position) {
185580
185606
  word,
185581
185607
  expectedValueAt(snapshot, word.start)
185582
185608
  );
185583
- } else if (nativeGenericClassId) {
185584
- candidates = [...snapshot.project.typeByName.values()].filter(
185585
- (type2) => type2.kind === "class" && (genericMemberId !== NEO_TILE_CONVERT_MEMBER_ID || !type2.abstract && !type2.typeParameters?.length) && isNeoScriptTypeAssignable(
185586
- { kind: "named", typeId: type2.id },
185587
- { kind: "named", typeId: nativeGenericClassId },
185588
- snapshot.project
185589
- )
185590
- ).map((type2) => typeCompletion(type2, word, snapshot));
185591
185609
  } else if (isTypePosition(tokens)) {
185592
185610
  candidates = typeCompletionItems(snapshot, word);
185593
185611
  } else if (isLambdaParameterPosition(tokens)) {
@@ -185600,6 +185618,88 @@ function complete(snapshot, position) {
185600
185618
  items: rankAndDeduplicate(candidates, prefix)
185601
185619
  };
185602
185620
  }
185621
+ function completeTypeArgument(snapshot, position) {
185622
+ const offset = snapshot.source.offsetAt(position);
185623
+ if (insidePlainString(snapshot.lexed.tokens, offset)) return null;
185624
+ const word = {
185625
+ start: wordRangeAt(snapshot.source.text, offset).start,
185626
+ end: offset
185627
+ };
185628
+ const tokens = significantTokensBefore(snapshot.lexed.tokens, word.start);
185629
+ const items = typeArgumentCompletionItems(snapshot, tokens, word);
185630
+ return items === null ? null : {
185631
+ isIncomplete: hasUnclosedConstruct(snapshot.lexed.tokens, offset),
185632
+ items: rankAndDeduplicate(
185633
+ items,
185634
+ snapshot.source.text.slice(word.start, offset)
185635
+ )
185636
+ };
185637
+ }
185638
+ function typeArgumentCompletionItems(snapshot, tokens, word) {
185639
+ let depth = 0;
185640
+ let argumentIndex = 0;
185641
+ let tupleDepth = 0;
185642
+ let inTuple = false;
185643
+ for (let index = tokens.length - 1; index >= 0; index--) {
185644
+ const token = tokens[index];
185645
+ if (token.text === ")") tupleDepth++;
185646
+ else if (token.text === "(") {
185647
+ if (!["<", ",", "("].includes(tokens[index - 1]?.text ?? "")) return null;
185648
+ if (tupleDepth > 0) tupleDepth--;
185649
+ else inTuple = true;
185650
+ } else if (/^>+$/.test(token.text)) depth += token.text.length;
185651
+ else if (token.text === "<") {
185652
+ if (depth > 0) {
185653
+ depth--;
185654
+ continue;
185655
+ }
185656
+ const name = tokens[index - 1]?.text;
185657
+ const resolution = resolveChain(
185658
+ snapshot,
185659
+ expressionTokensBefore(tokens, index),
185660
+ word.start
185661
+ );
185662
+ const member = resolution?.symbol;
185663
+ const declaration = (tokens[index - 2]?.text === "new" || resolution === null) && name ? snapshot.project.typeByName.get(name) : resolution?.staticType;
185664
+ const parameters = member?.typeParameters ?? declaration?.typeParameters;
185665
+ const builtin = name !== void 0 && member === void 0 && tokens[index - 2]?.text !== "." && [
185666
+ "List",
185667
+ "Set",
185668
+ "Dictionary",
185669
+ "Reference",
185670
+ "Partial",
185671
+ "FunctionRef",
185672
+ "NeoDelegate",
185673
+ "NeoAction"
185674
+ ].includes(name);
185675
+ const tileConversion = member?.id === NEO_TILE_CONVERT_MEMBER_ID && argumentIndex === 0;
185676
+ if (!parameters?.length && !builtin && !tileConversion) return null;
185677
+ const parameter4 = inTuple ? void 0 : parameters?.[argumentIndex]?.type;
185678
+ const constraint = parameter4?.kind === "typeParameter" ? parameter4.constraint : void 0;
185679
+ const constraintId = constraint?.kind === "named" ? constraint.typeId : void 0;
185680
+ const classId = tileConversion ? NEO_TILE_CLASS_ID : constraintId;
185681
+ if (!classId) return typeCompletionItems(snapshot, word);
185682
+ const candidates = [...snapshot.project.typeByName.values()].filter(
185683
+ (type2) => type2.kind === "class" && (!tileConversion || !type2.abstract && !type2.typeParameters?.length) && isNeoScriptTypeAssignable(
185684
+ { kind: "named", typeId: type2.id },
185685
+ { kind: "named", typeId: classId },
185686
+ snapshot.project
185687
+ )
185688
+ ).map((type2) => typeCompletion(type2, word, snapshot));
185689
+ if (!tileConversion)
185690
+ candidates.push(
185691
+ ...typeParameterCompletionItems(snapshot, word, constraint)
185692
+ );
185693
+ return candidates;
185694
+ } else if (!isCallTypeArgumentToken(token)) return null;
185695
+ else if (depth === 0 && tupleDepth === 0 && token.text === ",")
185696
+ argumentIndex++;
185697
+ }
185698
+ return null;
185699
+ }
185700
+ function completeLambdaBody(snapshot, position) {
185701
+ return lambdaBodyAt(snapshot, snapshot.source.offsetAt(position)) ? complete(snapshot, position) : null;
185702
+ }
185603
185703
  function completeFunctionArgument(snapshot, position) {
185604
185704
  const argument2 = callArgumentAt(snapshot, snapshot.source.offsetAt(position));
185605
185705
  if (!argument2 || argument2.call.kind === "constructor" || !callSymbol(snapshot, argument2.call))
@@ -185608,76 +185708,7 @@ function completeFunctionArgument(snapshot, position) {
185608
185708
  for (let opener = innermostOpener(features, snapshot.source.offsetAt(position)); opener >= 0 && features.tokens[opener].start > argument2.call.openParenOffset; opener = features.enclosingOpener[opener]) {
185609
185709
  if (features.tokens[opener].text === "{") return null;
185610
185710
  }
185611
- const values = complete(snapshot, position);
185612
- const names2 = functionArgumentNames(snapshot, position);
185613
- if (!names2) return values;
185614
- const offset = snapshot.source.offsetAt(position);
185615
- const word = wordRangeAt(snapshot.source.text, offset);
185616
- return {
185617
- ...values,
185618
- items: rankAndDeduplicate(
185619
- names2.onlyNames ? names2.items : [...names2.items, ...values.items],
185620
- snapshot.source.text.slice(word.start, offset)
185621
- )
185622
- };
185623
- }
185624
- function functionArgumentNames(snapshot, position) {
185625
- const offset = snapshot.source.offsetAt(position);
185626
- if (lambdaBodyAt(snapshot, offset)) return null;
185627
- const argument2 = callArgumentAt(snapshot, offset);
185628
- if (argument2?.call.kind !== "function") return null;
185629
- const { call, index } = argument2;
185630
- const { tokens } = scriptFeatureIndex(snapshot);
185631
- const startOffset = call.argumentRanges[index] ? snapshot.source.offsetAt(call.argumentRanges[index].start) : offset;
185632
- const start = firstIndex(tokens, (token) => token.start >= startOffset);
185633
- const prefixEnd = firstIndex(tokens, (token) => token.start >= offset);
185634
- const edit = argumentNameCompletionEdit(tokens, start, prefixEnd, position);
185635
- if (!edit) return null;
185636
- const symbol = callSymbol(snapshot, call);
185637
- if (!symbol?.parameters) return null;
185638
- const labels = [...call.argumentNames];
185639
- labels[index] = null;
185640
- const bound = boundOverloadId(
185641
- snapshot,
185642
- snapshot.source.offsetAt(call.nameRange.start),
185643
- call.name
185644
- );
185645
- const overloads = callOverloads(snapshot, call, symbol, bound).map(
185646
- (overload) => ({ parameters: overload.parameters ?? [] })
185647
- );
185648
- const reachable = neoScriptApplicableSignatures(overloads, labels);
185649
- const fields = /* @__PURE__ */ new Map();
185650
- for (const { parameters } of reachable) {
185651
- const used = /* @__PURE__ */ new Set();
185652
- call.argumentNames.forEach((name, argumentIndex) => {
185653
- if (argumentIndex === index) return;
185654
- const parameter4 = neoScriptArgumentParameter(
185655
- parameters,
185656
- name,
185657
- argumentIndex
185658
- );
185659
- if (parameter4) used.add(parameter4.name.toLowerCase());
185660
- });
185661
- for (const parameter4 of parameters) {
185662
- const key = parameter4.name.toLowerCase();
185663
- if (used.has(key) || fields.has(key)) continue;
185664
- fields.set(key, {
185665
- name: parameter4.name,
185666
- type: formatType(parameter4.type, snapshot.project),
185667
- ...parameter4.defaultValue ? { defaultText: parameter4.defaultValue.displayText } : {}
185668
- });
185669
- }
185670
- }
185671
- return {
185672
- items: argumentNameCompletions(
185673
- [...fields.values()],
185674
- tokens,
185675
- start,
185676
- prefixEnd,
185677
- position
185678
- ),
185679
- onlyNames: edit.hasColon
185680
- };
185711
+ return complete(snapshot, position);
185681
185712
  }
185682
185713
  function hover(snapshot, position) {
185683
185714
  const offset = snapshot.source.offsetAt(position);
@@ -187071,6 +187102,32 @@ function initializerConstruction(snapshot, brace, constructor2) {
187071
187102
  }
187072
187103
  return construction;
187073
187104
  }
187105
+ function initializerKeyContextAt(snapshot, position) {
187106
+ const features = scriptFeatureIndex(snapshot);
187107
+ const offset = snapshot.source.offsetAt(position);
187108
+ const brace = innermostOpener(features, offset);
187109
+ const constructor2 = features.objectInitializers.get(brace);
187110
+ if (constructor2 === void 0) return null;
187111
+ const keys = [];
187112
+ let atKey = true;
187113
+ for (let index = brace + 1; index < features.tokens.length; index++) {
187114
+ const token = features.tokens[index];
187115
+ if (token.start >= offset) break;
187116
+ if (features.enclosingOpener[index] !== brace) continue;
187117
+ const call = features.calls.get(token.start);
187118
+ if (call?.typeArgumentTokens?.length) {
187119
+ index = (features.tokenIndexes.get(call.openParenOffset) ?? index + 1) - 1;
187120
+ continue;
187121
+ }
187122
+ if (token.text === "=") {
187123
+ keys.push(features.tokens[index - 1].text);
187124
+ atKey = false;
187125
+ } else if (token.text === ",") atKey = true;
187126
+ }
187127
+ if (!atKey) return null;
187128
+ const construction = initializerConstruction(snapshot, brace, constructor2);
187129
+ return construction ? { typeName: construction.declaration.name, keys } : null;
187130
+ }
187074
187131
  function initializerExpectedType(snapshot, offset, bodyStart) {
187075
187132
  const features = scriptFeatureIndex(snapshot);
187076
187133
  const { tokens, enclosingOpener, callOpenings, objectInitializers } = features;
@@ -187484,6 +187541,27 @@ function matchingCloseBraceIndex(tokens, openIndex) {
187484
187541
  }
187485
187542
  return null;
187486
187543
  }
187544
+ function typeParameterCompletionItems(snapshot, word, constraint) {
187545
+ const items = [];
187546
+ for (const parameter4 of [
187547
+ ...contextualTypeParameters(snapshot),
187548
+ ...snapshot.context.typeParameters ?? []
187549
+ ]) {
187550
+ if (constraint && !isNeoScriptTypeAssignable(parameter4.type, constraint, snapshot.project))
187551
+ continue;
187552
+ items.push(
187553
+ completion(
187554
+ parameter4.name,
187555
+ "typeParameter",
187556
+ parameter4.name,
187557
+ "Type parameter",
187558
+ word,
187559
+ snapshot
187560
+ )
187561
+ );
187562
+ }
187563
+ return items;
187564
+ }
187487
187565
  function typeCompletionItems(snapshot, word) {
187488
187566
  const items = NEOSCRIPT_PRIMITIVE_TYPES.map(
187489
187567
  (name) => completion(name, "keyword", name, "Primitive type", word, snapshot)
@@ -187499,8 +187577,9 @@ function typeCompletionItems(snapshot, word) {
187499
187577
  "snippet"
187500
187578
  )
187501
187579
  );
187580
+ items.push(...typeParameterCompletionItems(snapshot, word));
187502
187581
  for (const type2 of snapshot.project.typeByName.values()) {
187503
- if (type2.id === NEOSCRIPT_MATH_TYPE_ID || type2.id === "builtin:Debug")
187582
+ if (type2.kind === "builtin" && !ANNOTATION_TYPE_NAMES.has(type2.name))
187504
187583
  continue;
187505
187584
  items.push(typeCompletion(type2, word, snapshot));
187506
187585
  }
@@ -189301,6 +189380,18 @@ function inferLambdaParameterType(local, snapshot) {
189301
189380
  if (declarationIndex < 0) return UNKNOWN_TYPE;
189302
189381
  const arrow = tokens[declarationIndex + 1];
189303
189382
  let opener = enclosingOpener[declarationIndex];
189383
+ let lambdaStart = arrow?.text === "=>" ? declarationIndex : opener;
189384
+ for (let depth = 0; depth < (local.tuplePath?.length ?? 0); depth++)
189385
+ lambdaStart = enclosingOpener[lambdaStart] ?? -1;
189386
+ if (lambdaStart >= 0) {
189387
+ const offset = tokens[lambdaStart].start;
189388
+ const argument2 = resolvedCallArgumentAt(snapshot, offset);
189389
+ const expected = isCollectionCallbackArgument(argument2) ? null : expectedTypeAt(snapshot, offset, void 0, argument2);
189390
+ if (expected?.kind === "delegate") {
189391
+ const parameter4 = expected.parameterTypes[local.lambdaParameterIndex ?? 0];
189392
+ if (parameter4) return tuplePathType(parameter4, local.tuplePath);
189393
+ }
189394
+ }
189304
189395
  if (!(arrow?.kind === "operator" && arrow.text === "=>")) {
189305
189396
  if (opener < 0 || tokens[opener]?.text !== "(") return UNKNOWN_TYPE;
189306
189397
  opener = enclosingOpener[opener];
@@ -189571,17 +189662,20 @@ function expressionTokensBefore(tokens, endExclusive) {
189571
189662
  for (; start >= 0; start--) {
189572
189663
  const token = tokens[start];
189573
189664
  if (!token) continue;
189574
- if (token.kind === "punctuation" && [")", "]"].includes(token.text)) {
189665
+ if (token.kind === "punctuation" && [")", "]", "}"].includes(token.text)) {
189666
+ if (token.text === "}" && depth === 0 && start < endExclusive - 1 && ![".", "?.", "!"].includes(tokens[start + 1]?.text ?? ""))
189667
+ break;
189575
189668
  if (depth === 0) groupEnd = start;
189576
189669
  depth++;
189577
189670
  }
189578
- if (token.kind === "punctuation" && ["(", "["].includes(token.text)) {
189671
+ if (token.kind === "punctuation" && ["(", "[", "{"].includes(token.text)) {
189579
189672
  if (depth > 0) depth--;
189580
189673
  else break;
189581
189674
  if (depth === 0 && ["if", "while", "for", "foreach"].includes(
189582
189675
  tokens[start - 1]?.text ?? ""
189583
189676
  ))
189584
189677
  return tokens.slice(groupEnd + 1, endExclusive);
189678
+ continue;
189585
189679
  }
189586
189680
  if (depth === 0 && token.kind === "operator" && token.text === ">") {
189587
189681
  const name = genericCallNameIndex(tokens, start);
@@ -189598,8 +189692,7 @@ function expressionTokensBefore(tokens, endExclusive) {
189598
189692
  return tokens.slice(start + 1, endExclusive);
189599
189693
  }
189600
189694
  function genericCallNameIndex(tokens, close) {
189601
- if (tokens[close + 1]?.text !== "(" && tokens[close + 1]?.text !== ".")
189602
- return -1;
189695
+ if (!["(", ".", "{"].includes(tokens[close + 1]?.text ?? "")) return -1;
189603
189696
  let depth = 0;
189604
189697
  for (let cursor = close; cursor > 0; cursor--) {
189605
189698
  const token = tokens[cursor];
@@ -189607,7 +189700,7 @@ function genericCallNameIndex(tokens, close) {
189607
189700
  if (token.text === ">") depth++;
189608
189701
  else if (token.text === "<") {
189609
189702
  if (--depth === 0) {
189610
- const valid = tokens[close + 1]?.text === "." ? new Parser(tokens.slice(cursor - 1, close + 1)).typeEnd() === close - cursor + 2 : callOpenParenIndex(tokens, cursor - 1) === close + 1;
189703
+ const valid = tokens[close + 1]?.text !== "(" ? new Parser(tokens.slice(cursor - 1, close + 1)).typeEnd() === close - cursor + 2 : callOpenParenIndex(tokens, cursor - 1) === close + 1;
189611
189704
  return valid ? cursor - 1 : -1;
189612
189705
  }
189613
189706
  } else if (!isCallTypeArgumentToken(token)) return -1;
@@ -189633,13 +189726,6 @@ function wordRangeAt(text, offset) {
189633
189726
  function isTypePosition(tokens) {
189634
189727
  const tail = tokens[tokens.length - 1];
189635
189728
  if (!tail) return false;
189636
- if (tail.kind === "operator" && tail.text === "<") {
189637
- const previous = tokens[tokens.length - 2];
189638
- return previous?.text === "Dictionary" || previous?.text === "Set";
189639
- }
189640
- if (tail.kind === "punctuation" && tail.text === ",") {
189641
- return tokens.some((token) => token.text === "Dictionary");
189642
- }
189643
189729
  if (tail.kind === "keyword" && tail.text === "is") return true;
189644
189730
  if (tail.text === "not" && tokens.at(-2)?.text === "is") return true;
189645
189731
  return false;
@@ -189758,11 +189844,10 @@ function isTypeAnnotationReference(snapshot, reference2) {
189758
189844
  function isConstructorTypeReference(snapshot, reference2) {
189759
189845
  return scriptFeatureIndex(snapshot).calls.get(reference2.start)?.kind === "constructor";
189760
189846
  }
189761
- var SCRIPT_FEATURE_INDEX, assumedLambdaInputs, LIST_OPENERS, LIST_CLOSERS, TUPLE_LABEL_DECLARATIONS, NAMED_ARGUMENT_LABELS, NO_EXPECTED_VALUE, CONSTRUCTIONS, FLOW_PROBES, OVERLOADED_FUNCTION_NAMES, GENERIC_CALL_NAMES, LOCAL_SCOPE_INDEX, NAMED_ITEM_INDEX, COLLECTION_FUNCTION_DOCUMENTATION, LOCAL_SYMBOL_CACHE, CALL_COMMIT_CHARACTERS, GENERIC_CALL_COMMIT_CHARACTERS, SETTER_VALUE_ID_SUFFIX, LAMBDA_BODY_INDEX;
189847
+ var SCRIPT_FEATURE_INDEX, assumedLambdaInputs, LIST_OPENERS, LIST_CLOSERS, TUPLE_LABEL_DECLARATIONS, NAMED_ARGUMENT_LABELS, NO_EXPECTED_VALUE, CONSTRUCTIONS, ANNOTATION_TYPE_NAMES, FLOW_PROBES, OVERLOADED_FUNCTION_NAMES, GENERIC_CALL_NAMES, LOCAL_SCOPE_INDEX, NAMED_ITEM_INDEX, COLLECTION_FUNCTION_DOCUMENTATION, LOCAL_SYMBOL_CACHE, CALL_COMMIT_CHARACTERS, GENERIC_CALL_COMMIT_CHARACTERS, SETTER_VALUE_ID_SUFFIX, LAMBDA_BODY_INDEX;
189762
189848
  var init_analyzer = __esm({
189763
189849
  "../packages/neoscript-language/src/analyzer.ts"() {
189764
189850
  "use strict";
189765
- init_argument_name_completions();
189766
189851
  init_extension_members();
189767
189852
  init_random_contract();
189768
189853
  init_error_contract();
@@ -189790,6 +189875,9 @@ var init_analyzer = __esm({
189790
189875
  NAMED_ARGUMENT_LABELS = /* @__PURE__ */ new WeakMap();
189791
189876
  NO_EXPECTED_VALUE = { type: null };
189792
189877
  CONSTRUCTIONS = /* @__PURE__ */ new WeakMap();
189878
+ ANNOTATION_TYPE_NAMES = new Set(
189879
+ NEOSCRIPT_BUILTIN_ANNOTATION_TYPES
189880
+ );
189793
189881
  FLOW_PROBES = /* @__PURE__ */ new WeakMap();
189794
189882
  OVERLOADED_FUNCTION_NAMES = /* @__PURE__ */ new WeakMap();
189795
189883
  GENERIC_CALL_NAMES = /* @__PURE__ */ new WeakMap();
@@ -189853,6 +189941,43 @@ var init_parity_contract = __esm({
189853
189941
  }
189854
189942
  });
189855
189943
 
189944
+ // ../packages/neoscript-language/src/argument-name-completions.ts
189945
+ function argumentNameCompletions(fields, tokens, start, prefixEnd, position) {
189946
+ const edit = argumentNameCompletionEdit(tokens, start, prefixEnd, position);
189947
+ if (!edit) return [];
189948
+ const { range: range2, hasColon } = edit;
189949
+ const items = [];
189950
+ for (const field of fields) {
189951
+ const insertText = hasColon ? field.name : `${field.name}: `;
189952
+ items.push({
189953
+ label: field.name,
189954
+ kind: "property",
189955
+ detail: field.defaultText === void 0 ? field.type : `[${field.type} = ${field.defaultText}]`,
189956
+ insertText,
189957
+ textEdit: { range: range2, newText: insertText }
189958
+ });
189959
+ }
189960
+ return items;
189961
+ }
189962
+ function argumentNameCompletionEdit(tokens, start, prefixEnd, position) {
189963
+ const first = tokens[start];
189964
+ if (prefixEnd > start + 1) return null;
189965
+ if (prefixEnd > start && (!first || !isCallArgumentLabelToken(first)))
189966
+ return null;
189967
+ const editingName = first !== void 0 && isCallArgumentLabelToken(first) && positionCompare(first.range.start, position) <= 0 && positionCompare(position, first.range.end) <= 0;
189968
+ if (first && isCallArgumentLabelToken(first) && !editingName) return null;
189969
+ const hasColon = editingName && tokens[start + 1]?.text === ":";
189970
+ const range2 = editingName ? first.range : { start: position, end: position };
189971
+ return { range: range2, hasColon };
189972
+ }
189973
+ var init_argument_name_completions = __esm({
189974
+ "../packages/neoscript-language/src/argument-name-completions.ts"() {
189975
+ "use strict";
189976
+ init_executable_parser();
189977
+ init_project_source_tokens();
189978
+ }
189979
+ });
189980
+
189856
189981
  // ../packages/neoscript-language/src/project-listener-annotations.ts
189857
189982
  function createContext2(analysis, source) {
189858
189983
  const project = cachedProjectIndex(analysis.project);
@@ -189948,6 +190073,20 @@ function sourceTypeAst(type2) {
189948
190073
  column: type2.range.start.character + 1
189949
190074
  }
189950
190075
  };
190076
+ if (type2.tupleElements)
190077
+ return {
190078
+ ...common,
190079
+ kind: "tuple",
190080
+ elements: type2.tupleElements.map((element) => {
190081
+ const nested = sourceTypeAst(element.type);
190082
+ return {
190083
+ type: nested,
190084
+ pos: nested.pos,
190085
+ ...element.name ? { name: element.name } : {},
190086
+ ...element.nameRange ? { nameRange: element.nameRange } : {}
190087
+ };
190088
+ })
190089
+ };
189951
190090
  const args = type2.typeArguments;
189952
190091
  if ((type2.name === "List" || type2.name === "Set") && args[0])
189953
190092
  return {
@@ -190828,6 +190967,63 @@ function projectDiagnostics(analysis, uri) {
190828
190967
  }));
190829
190968
  }
190830
190969
  function projectCompletions(analysis, document, position) {
190970
+ const result = projectCompletionsAt(analysis, document, position);
190971
+ const referenceNames = projectReferenceArgumentCompletions(
190972
+ analysis,
190973
+ document,
190974
+ position
190975
+ );
190976
+ return referenceNames.length ? { ...result, items: [...referenceNames, ...result.items] } : result;
190977
+ }
190978
+ function projectReferenceArgumentCompletions(analysis, document, position) {
190979
+ if (projectTokenAt(document, position)?.kind === "comment") return [];
190980
+ const documentTokens = projectSignificantTokens(analysis, document);
190981
+ const tokens = tokensBeforePosition(documentTokens, position);
190982
+ const openIndex = activeCallOpenIndex(tokens, position);
190983
+ if (openIndex < 0) return [];
190984
+ const calleeIndex = projectCallCalleeIndex(tokens, openIndex);
190985
+ if (tokens[calleeIndex]?.text !== "Reference" || tokens[calleeIndex + 1]?.text !== "<")
190986
+ return [];
190987
+ if ([".", "?.", "new"].includes(tokens[calleeIndex - 1]?.text ?? ""))
190988
+ return [];
190989
+ const active = activeCallArgument(tokens, openIndex, position);
190990
+ if (active.label !== null) return [];
190991
+ if (tokens.length > active.start + 1) return [];
190992
+ const args = projectCallArguments(documentTokens, openIndex);
190993
+ let field;
190994
+ if (active.index === 0) field = { name: "id", type: "string" };
190995
+ else if (active.index === 1 && args[0]?.name === "id") {
190996
+ const source = analysis.documents.get(document.uri);
190997
+ if (source?.listenerStatements?.some(
190998
+ (statement) => rangeContains(statement.range, position)
190999
+ ))
191000
+ return [];
191001
+ const before = tokens[calleeIndex - 1];
191002
+ if (before?.text === ")" && !initializerRootAt(analysis, document, position) && !projectBodySnapshotAt(analysis, document, position)) {
191003
+ const annotationOpen = activeCallOpenIndex(
191004
+ tokens.slice(0, calleeIndex - 1),
191005
+ before.range.start
191006
+ );
191007
+ if (tokens[annotationOpen - 1]?.text === "listeners" && tokens[annotationOpen - 2]?.text === "@")
191008
+ return [];
191009
+ }
191010
+ const target = tokens[calleeIndex + 2]?.text;
191011
+ if (target === "NeoImage" || target === "NeoAudioClip") return [];
191012
+ field = { name: "withProvenance", type: "bool", defaultText: "false" };
191013
+ } else return [];
191014
+ if (args.some(
191015
+ (argument2) => argument2.index !== active.index && argument2.name === field.name
191016
+ ))
191017
+ return [];
191018
+ return argumentNameCompletions(
191019
+ [field],
191020
+ documentTokens,
191021
+ active.start,
191022
+ tokens.length,
191023
+ position
191024
+ );
191025
+ }
191026
+ function projectCompletionsAt(analysis, document, position) {
190831
191027
  if (projectTokenAt(document, position)?.kind === "comment") {
190832
191028
  return { isIncomplete: false, items: [] };
190833
191029
  }
@@ -190902,6 +191098,29 @@ function projectCompletions(analysis, document, position) {
190902
191098
  position,
190903
191099
  annotationSite
190904
191100
  );
191101
+ const lambda = expression && completeLambdaBody(expression.snapshot, position);
191102
+ if (lambda && expression) {
191103
+ const members3 = projectInitializerMemberCompletions(
191104
+ analysis,
191105
+ document,
191106
+ position,
191107
+ expression.snapshot
191108
+ );
191109
+ if (members3) return { ...lambda, items: members3 };
191110
+ const names2 = projectConstructorArgumentCompletions(
191111
+ analysis,
191112
+ document,
191113
+ position,
191114
+ expression.snapshot
191115
+ );
191116
+ const items = names2?.length ? [...names2, ...lambda.items] : lambda.items;
191117
+ return {
191118
+ ...lambda,
191119
+ items: variantScopeInBody ? [...items, variantScopeInBody] : items
191120
+ };
191121
+ }
191122
+ const typeArgument = expression && completeTypeArgument(expression.snapshot, position);
191123
+ if (typeArgument) return typeArgument;
190905
191124
  const argument2 = expression && completeFunctionArgument(expression.snapshot, position);
190906
191125
  if (argument2) return argument2;
190907
191126
  const builtinMembers = projectBuiltinStaticMemberCompletions(
@@ -191430,7 +191649,7 @@ function projectExpressionMemberCompletions(analysis, document, position, annota
191430
191649
  const dot = before.at(-1);
191431
191650
  if (dot?.text !== "." && dot?.text !== "?.") return null;
191432
191651
  const receiver = before.at(-2);
191433
- if (!receiver || !(receiver.kind === "identifier" || receiver.kind === "type" || [")", "]", ">", "!", "this", "base", "root"].includes(receiver.text)))
191652
+ if (!receiver || !(receiver.kind === "identifier" || receiver.kind === "type" || [")", "]", "}", ">", "!", "this", "base", "root"].includes(receiver.text)))
191434
191653
  return null;
191435
191654
  const expression = projectExpressionSnapshotAt(
191436
191655
  analysis,
@@ -191460,6 +191679,11 @@ function projectExpressionSnapshotAt(analysis, document, position, annotationSit
191460
191679
  const { thisClass: _thisClass, ...staticContext } = language.context;
191461
191680
  context = { ...staticContext, staticMember: true };
191462
191681
  }
191682
+ if (root && !argument2) {
191683
+ const expected = constructionSiteAt(analysis, document, root.range.start);
191684
+ if (expected?.expectedTypeRef)
191685
+ context = { ...context, returnType: expected.expectedTypeRef };
191686
+ }
191463
191687
  const key = range2 ?? context;
191464
191688
  let documents = PROJECT_EXPRESSION_SNAPSHOT_CACHE.get(analysis);
191465
191689
  if (!documents) {
@@ -191863,6 +192087,7 @@ function projectConstructorArgumentCompletions(analysis, document, position, bod
191863
192087
  if (!typeName) return null;
191864
192088
  const active = activeCallArgument(tokens, openIndex, position);
191865
192089
  if (active.label !== null) return null;
192090
+ if (tokens.length > active.start + 1) return null;
191866
192091
  const args = projectCallArguments(documentTokens, openIndex);
191867
192092
  const builtin = lookupVariantFolderArgumentFields(typeName) ?? graphConstructorNamedArguments(typeName);
191868
192093
  const fields = builtin ? unusedConstructorArguments(builtin, args, active.index) : declaredConstructorNamedArguments(analysis, typeName, args, active.index);
@@ -191946,18 +192171,16 @@ function parameterArgumentField(parameter4) {
191946
192171
  ...parameter4.defaultValue === void 0 ? {} : { defaultText: parameter4.defaultValue.text }
191947
192172
  };
191948
192173
  }
191949
- function projectInitializerMemberCompletions(analysis, document, position) {
191950
- const site = constructionSiteAt(analysis, document, position);
191951
- if (!site?.atInitializerKey) return null;
191952
- const typeName = site.initializerTypeName;
191953
- if (typeName === null) return null;
192174
+ function projectInitializerMemberCompletions(analysis, document, position, body) {
192175
+ const site = body ? null : constructionSiteAt(analysis, document, position);
192176
+ const context = body ? initializerKeyContextAt(body, position) : null;
192177
+ const typeName = body ? context?.typeName : site?.initializerTypeName;
192178
+ if (typeName == null) return null;
192179
+ if (!body && !site?.atInitializerKey) return null;
192180
+ const keys = context?.keys ?? site?.initializerKeys ?? [];
191954
192181
  const index = projectConstructionIndex(analysis);
191955
- const unsettled = unsettledRequiredMembers(
191956
- index,
191957
- typeName,
191958
- site.initializerKeys
191959
- );
191960
- const taken = new Set(site.initializerKeys.map((key) => key.toLowerCase()));
192182
+ const unsettled = unsettledRequiredMembers(index, typeName, keys);
192183
+ const taken = new Set(keys.map((key) => key.toLowerCase()));
191961
192184
  for (const member of unsettled) taken.add(member.name.toLowerCase());
191962
192185
  const items = unsettled.map((member) => ({
191963
192186
  label: member.name,
@@ -193635,6 +193858,7 @@ function indexConstructionSites(analysis, document, root) {
193635
193858
  end++;
193636
193859
  const tokens = allTokens.slice(start, end).filter((token) => token.kind !== "comment" && token.kind !== "eof");
193637
193860
  let delimiterEnds;
193861
+ const genericCallOpens = constructionGenericCallOpens(tokens);
193638
193862
  const language = listenerSourceContext(analysis, document, root.range.start);
193639
193863
  const sourceType = (type2) => resolveSourceType(type2, language.context, language.project);
193640
193864
  const fieldType = (owner, name) => {
@@ -193687,7 +193911,11 @@ function indexConstructionSites(analysis, document, root) {
193687
193911
  case "object":
193688
193912
  case "list":
193689
193913
  case "dictionary":
193914
+ case "tuple":
193915
+ case "group":
193690
193916
  return frame.slotId;
193917
+ case "switch":
193918
+ return frame.inResult ? frame.slotId : null;
193691
193919
  case "call":
193692
193920
  case "opaque":
193693
193921
  return null;
@@ -193699,7 +193927,12 @@ function indexConstructionSites(analysis, document, root) {
193699
193927
  switch (frame.kind) {
193700
193928
  case "object":
193701
193929
  case "dictionary":
193930
+ case "group":
193702
193931
  return frame.slotTypeRef;
193932
+ case "tuple":
193933
+ return frame.typeRef.elements[frame.elementIndex]?.type ?? null;
193934
+ case "switch":
193935
+ return frame.inResult ? frame.resultTypeRef : null;
193703
193936
  case "list":
193704
193937
  return frame.elementTypeRef;
193705
193938
  case "opaque":
@@ -193737,7 +193970,12 @@ function indexConstructionSites(analysis, document, root) {
193737
193970
  switch (frame.kind) {
193738
193971
  case "object":
193739
193972
  case "dictionary":
193973
+ case "group":
193740
193974
  return frame.slotType;
193975
+ case "tuple":
193976
+ return frame.type?.tupleElements?.[frame.elementIndex]?.type ?? null;
193977
+ case "switch":
193978
+ return frame.inResult ? frame.resultType : null;
193741
193979
  case "list":
193742
193980
  return frame.elementType;
193743
193981
  case "call":
@@ -193834,7 +194072,7 @@ function indexConstructionSites(analysis, document, root) {
193834
194072
  sawNew = true;
193835
194073
  newStart = index;
193836
194074
  pendingNewTypeRef = expectedTypeRef();
193837
- pendingNewTypeName = expectedType()?.name ?? null;
194075
+ pendingNewTypeName = pendingNewTypeRef?.kind === "named" ? language.project.typeById.get(pendingNewTypeRef.typeId)?.name ?? null : expectedType()?.name ?? null;
193838
194076
  closedConstructorTypeName = null;
193839
194077
  continue;
193840
194078
  }
@@ -193872,8 +194110,24 @@ function indexConstructionSites(analysis, document, root) {
193872
194110
  }
193873
194111
  continue;
193874
194112
  }
194113
+ if (!sawNew) {
194114
+ const call = genericCallOpens.get(index) ?? -1;
194115
+ if (call >= 0) {
194116
+ index = call - 1;
194117
+ closedConstructorTypeName = null;
194118
+ continue;
194119
+ }
194120
+ }
193875
194121
  if (token.text === "{") {
193876
- if (sawNew || closedConstructorTypeName !== null) {
194122
+ if (tokens[index - 1]?.text === "switch") {
194123
+ stack.push({
194124
+ kind: "switch",
194125
+ resultType: expectedType(),
194126
+ resultTypeRef: expectedTypeRef(),
194127
+ slotId: expectedSlotId(),
194128
+ inResult: false
194129
+ });
194130
+ } else if (sawNew || closedConstructorTypeName !== null) {
193877
194131
  const frame2 = {
193878
194132
  kind: "object",
193879
194133
  typeName: pendingNewTypeName ?? closedConstructorTypeName,
@@ -193934,7 +194188,33 @@ function indexConstructionSites(analysis, document, root) {
193934
194188
  sawNew = false;
193935
194189
  pendingNewTypeName = null;
193936
194190
  } else {
193937
- stack.push({ kind: "opaque" });
194191
+ const ends = delimiterEnds ??= projectDelimiterEnds(tokens);
194192
+ const expected = expectedTypeRef();
194193
+ const kind = constructionParenthesesKind(
194194
+ tokens,
194195
+ index,
194196
+ ends,
194197
+ genericCallOpens,
194198
+ expected?.kind === "tuple"
194199
+ );
194200
+ if (kind === "tuple" && expected?.kind === "tuple") {
194201
+ stack.push({
194202
+ kind: "tuple",
194203
+ type: expectedType(),
194204
+ typeRef: expected,
194205
+ slotId: expectedSlotId(),
194206
+ elementIndex: 0
194207
+ });
194208
+ } else if (kind === "group") {
194209
+ stack.push({
194210
+ kind: "group",
194211
+ slotType: expectedType(),
194212
+ slotTypeRef: expected,
194213
+ slotId: expectedSlotId()
194214
+ });
194215
+ } else {
194216
+ stack.push({ kind: "opaque" });
194217
+ }
193938
194218
  }
193939
194219
  closedConstructorTypeName = null;
193940
194220
  continue;
@@ -193987,6 +194267,11 @@ function indexConstructionSites(analysis, document, root) {
193987
194267
  frame.slotType = null;
193988
194268
  frame.slotTypeRef = null;
193989
194269
  }
194270
+ } else if (frame.kind === "tuple") {
194271
+ if (token.text === ",") frame.elementIndex++;
194272
+ } else if (frame.kind === "switch") {
194273
+ if (token.text === "=>") frame.inResult = true;
194274
+ else if (token.text === ",") frame.inResult = false;
193990
194275
  } else if (frame.kind === "call") {
193991
194276
  if (token.text === ",") {
193992
194277
  frame.argumentIndex++;
@@ -194005,6 +194290,43 @@ function indexConstructionSites(analysis, document, root) {
194005
194290
  sites.push({ position: previousPosition, site: snapshot() });
194006
194291
  return sites;
194007
194292
  }
194293
+ function constructionParenthesesKind(tokens, open2, ends, genericCallOpens, tupleExpected) {
194294
+ const previous = tokens[open2 - 1];
194295
+ const close = ends.get(open2);
194296
+ if (previous?.kind === "identifier" || previous?.kind === "type" || [")", "]", ">", "!"].includes(previous?.text ?? "") || close !== void 0 && tokens[close + 1]?.text === "=>")
194297
+ return null;
194298
+ for (let index = open2 + 1; index < (close ?? tokens.length); index++) {
194299
+ const token = tokens[index];
194300
+ if (token.text === ",") return "tuple";
194301
+ if (["(", "[", "{"].includes(token.text)) {
194302
+ const end = ends.get(index);
194303
+ if (end === void 0) return tupleExpected ? "tuple" : "group";
194304
+ index = end;
194305
+ } else if (token.text === "new" && tokens[index + 1]?.text !== "(") {
194306
+ const end = new Parser(tokens, index + 1).typeEnd();
194307
+ if (end > index + 1) index = end - 1;
194308
+ } else {
194309
+ const call = genericCallOpens.get(index) ?? -1;
194310
+ if (call >= 0) index = call - 1;
194311
+ }
194312
+ }
194313
+ return close === void 0 && tupleExpected ? "tuple" : "group";
194314
+ }
194315
+ function constructionGenericCallOpens(tokens) {
194316
+ const opens = [];
194317
+ const calls = /* @__PURE__ */ new Map();
194318
+ for (let index = 0; index < tokens.length; index++) {
194319
+ if (tokens[index].text === "<") opens.push(index);
194320
+ else if (tokens[index].text === ">") {
194321
+ const open2 = opens.pop();
194322
+ if (open2 === void 0 || open2 === 0 || tokens[index + 1]?.text !== "(")
194323
+ continue;
194324
+ const end = new Parser(tokens, open2 - 1).typeEnd();
194325
+ if (end === index + 1) calls.set(open2 - 1, end);
194326
+ }
194327
+ }
194328
+ return calls;
194329
+ }
194008
194330
  function collectionElementType2(type2) {
194009
194331
  if (!type2) return null;
194010
194332
  if (type2.name === "List" || type2.name === "Set") {
@@ -238168,7 +238490,7 @@ var releaseWorkspaceLock = () => void 0;
238168
238490
  async function main() {
238169
238491
  const args = parseArgs(process.argv.slice(2));
238170
238492
  if (args.command === "--version") {
238171
- console.log("0.89.1");
238493
+ console.log("0.89.2");
238172
238494
  return;
238173
238495
  }
238174
238496
  if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {
@@ -49949,18 +49949,19 @@ function buildNeoScriptEditorIndex(lexed, kind) {
49949
49949
  lexed,
49950
49950
  diagnostics
49951
49951
  );
49952
+ const regionCalls = indexCalls(executable, executablePairs, lexed);
49952
49953
  const regionLocals = applySwitchSectionScopes(
49953
49954
  indexLocals(
49954
49955
  executable,
49955
49956
  executablePairs,
49956
49957
  lexed,
49957
49958
  diagnostics,
49958
- lambdaBodies
49959
+ lambdaBodies,
49960
+ regionCalls
49959
49961
  ),
49960
49962
  regionSwitches,
49961
49963
  lexed
49962
49964
  );
49963
- const regionCalls = indexCalls(executable, executablePairs, lexed);
49964
49965
  const declarations = new Set(
49965
49966
  regionLocals.map(
49966
49967
  (local) => `${local.nameRange.start.line}:${local.nameRange.start.character}`
@@ -50413,9 +50414,10 @@ function duplicateUnitDiagnostic(label, units, diagnostics) {
50413
50414
  ]
50414
50415
  });
50415
50416
  }
50416
- function indexLocals(tokens, pairs, lexed, diagnostics, lambdaBodies) {
50417
+ function indexLocals(tokens, pairs, lexed, diagnostics, lambdaBodies, calls) {
50417
50418
  const locals = [];
50418
50419
  const braceScope = braceScopeIndex(tokens, lexed.end);
50420
+ const switchArrows = switchExpressionArrowIndexes(tokens, calls);
50419
50421
  for (let index = 0; index < tokens.length; index++) {
50420
50422
  const caught = tryParseCatchParameter(tokens, index, pairs, lexed);
50421
50423
  if (caught) {
@@ -50436,7 +50438,13 @@ function indexLocals(tokens, pairs, lexed, diagnostics, lambdaBodies) {
50436
50438
  index = loop.resumeTokenIndex;
50437
50439
  continue;
50438
50440
  }
50439
- const lambda = tryParseLambdaParameters(tokens, index, pairs, lexed);
50441
+ const lambda = tryParseLambdaParameters(
50442
+ tokens,
50443
+ index,
50444
+ pairs,
50445
+ lexed,
50446
+ switchArrows
50447
+ );
50440
50448
  if (lambda) {
50441
50449
  locals.push(...lambda.locals);
50442
50450
  lambdaBodies.push(lambda.body);
@@ -50604,7 +50612,35 @@ function statementEndTokenIndex(tokens, start) {
50604
50612
  }
50605
50613
  return tokens.length;
50606
50614
  }
50607
- function tryParseLambdaParameters(tokens, start, pairs, lexed) {
50615
+ function switchExpressionArrowIndexes(tokens, calls) {
50616
+ const enclosing = enclosingOpenerIndex(tokens);
50617
+ const inResult = /* @__PURE__ */ new Set();
50618
+ const arrows = /* @__PURE__ */ new Set();
50619
+ const genericEnds = /* @__PURE__ */ new Map();
50620
+ for (const call of calls) {
50621
+ const first = call.typeArgumentTokens?.[0];
50622
+ if (first) genericEnds.set(first.start, call.openParenOffset);
50623
+ }
50624
+ for (let index = 0; index < tokens.length; index++) {
50625
+ const token = tokens[index];
50626
+ const genericEnd = genericEnds.get(token.start);
50627
+ if (genericEnd !== void 0) {
50628
+ while (tokens[index + 1] && tokens[index + 1].start < genericEnd)
50629
+ index++;
50630
+ continue;
50631
+ }
50632
+ const opener = enclosing[index];
50633
+ if (tokens[opener]?.text !== "{" || tokens[opener - 1]?.text !== "switch")
50634
+ continue;
50635
+ if (token.text === ",") inResult.delete(opener);
50636
+ else if (token.text === "=>" && !inResult.has(opener)) {
50637
+ arrows.add(index);
50638
+ inResult.add(opener);
50639
+ }
50640
+ }
50641
+ return arrows;
50642
+ }
50643
+ function tryParseLambdaParameters(tokens, start, pairs, lexed, switchArrows) {
50608
50644
  const open = tokens[start];
50609
50645
  if (!open) return null;
50610
50646
  if (open.kind === "identifier") {
@@ -50612,6 +50648,7 @@ function tryParseLambdaParameters(tokens, start, pairs, lexed) {
50612
50648
  if (!arrow2 || arrow2.kind !== "operator" || arrow2.text !== "=>") {
50613
50649
  return null;
50614
50650
  }
50651
+ if (switchArrows.has(start + 1)) return null;
50615
50652
  const body2 = lambdaBodySpan(tokens, start + 1, pairs, lexed);
50616
50653
  return {
50617
50654
  locals: [
@@ -50636,6 +50673,7 @@ function tryParseLambdaParameters(tokens, start, pairs, lexed) {
50636
50673
  if (closeIndex === void 0) return null;
50637
50674
  const arrow = tokens[closeIndex + 1];
50638
50675
  if (!arrow || arrow.kind !== "operator" || arrow.text !== "=>") return null;
50676
+ if (switchArrows.has(closeIndex + 1)) return null;
50639
50677
  const body = lambdaBodySpan(tokens, closeIndex + 1, pairs, lexed);
50640
50678
  const locals = [];
50641
50679
  for (const [parameterIndex, [from, to]] of patternSegments(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.89.1",
3
+ "version": "0.89.2",
4
4
  "description": "Neo Compose native project-source CLI with bidirectional sync.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -9,7 +9,7 @@ description: >-
9
9
  `@neocompose/cli` or `node cli/bin/neo.mjs` in the neo-compose repository.
10
10
  ---
11
11
 
12
- <!-- reviewed-through-cli: 0.89.1 -->
12
+ <!-- reviewed-through-cli: 0.89.2 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -109,7 +109,7 @@ The marker near the top of `SKILL.md` must exactly match the package version,
109
109
  and this quoted copy is checked too:
110
110
 
111
111
  ```html
112
- <!-- reviewed-through-cli: 0.89.1 -->
112
+ <!-- reviewed-through-cli: 0.89.2 -->
113
113
  ```
114
114
 
115
115
  Keep this skill on current behavior. Upgrade steps, rollout prerequisites,
@@ -66,6 +66,13 @@ public Reference<NeoVariant<Tree>> Look = root.Garden.Felled;
66
66
  - `Set<Reference<T>>` selections can mix bare paths and explicit references.
67
67
  Computed selections deduplicate by target identity, not by current value.
68
68
  A literal selection of fixed targets must not repeat the same target.
69
+ A selection literal may also evaluate its entries, for example
70
+ `[root.Assets.Items.First(), selected]`. Those entries must resolve to
71
+ existing objects; the selection evaluates when its owner is constructed.
72
+ A compatible `Set<T>` path is a computed selection, including a set of a
73
+ derived class assigned to `Set<Reference<Base>>`. Each selected object keeps
74
+ its identity; the selection does not acquire the source collection's row.
75
+ Convert a `List<T>` to a set with `.ToSet()`.
69
76
  - A bare path to an existing reference copies its current target when the
70
77
  object is constructed. Later rebinding of that source reference does not
71
78
  rebind the copy.