@neocompose/cli 0.61.4 → 0.62.0

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/dist/neo.mjs CHANGED
@@ -119,6 +119,92 @@ var init_workspace_source_path = __esm({
119
119
  }
120
120
  });
121
121
 
122
+ // ../packages/neoscript-language/src/local-scope-index.ts
123
+ var LocalScopeIndex;
124
+ var init_local_scope_index = __esm({
125
+ "../packages/neoscript-language/src/local-scope-index.ts"() {
126
+ "use strict";
127
+ LocalScopeIndex = class {
128
+ constructor(locals) {
129
+ this.locals = locals;
130
+ for (let index = 0; index < locals.length; index++) {
131
+ const local = locals[index];
132
+ if (local.scopeStart > local.scopeEnd) continue;
133
+ const named = this.byName.get(local.name);
134
+ if (named) named.push(index);
135
+ else this.byName.set(local.name, [index]);
136
+ }
137
+ }
138
+ locals;
139
+ byName = /* @__PURE__ */ new Map();
140
+ segments = /* @__PURE__ */ new Map();
141
+ at(name, offset) {
142
+ const named = this.byName.get(name);
143
+ if (!named) return void 0;
144
+ let segments = this.segments.get(name);
145
+ if (!segments) {
146
+ segments = this.buildSegments(named);
147
+ this.segments.set(name, segments);
148
+ }
149
+ let low = 0;
150
+ let high = segments.length;
151
+ while (low < high) {
152
+ const middle = low + high >>> 1;
153
+ if (segments[middle].start <= offset) low = middle + 1;
154
+ else high = middle;
155
+ }
156
+ const index = segments[low - 1]?.localIndex;
157
+ return index === void 0 ? void 0 : this.locals[index];
158
+ }
159
+ buildSegments(indices) {
160
+ const events = indices.flatMap((index) => [
161
+ { offset: this.locals[index].scopeStart, index },
162
+ // Scope ends are inclusive in the editor's recovery model.
163
+ { offset: this.locals[index].scopeEnd + 1, index: void 0 }
164
+ ]);
165
+ events.sort((a, b) => a.offset - b.offset);
166
+ const active = [];
167
+ const result = [];
168
+ for (let eventIndex = 0; eventIndex < events.length; ) {
169
+ const offset = events[eventIndex].offset;
170
+ while (events[eventIndex]?.offset === offset) {
171
+ const index = events[eventIndex++].index;
172
+ if (index === void 0) continue;
173
+ let child = active.length;
174
+ active.push(index);
175
+ while (child > 0) {
176
+ const parent = child - 1 >>> 1;
177
+ if (active[parent] >= index) break;
178
+ active[child] = active[parent];
179
+ child = parent;
180
+ }
181
+ active[child] = index;
182
+ }
183
+ while (active.length && this.locals[active[0]].scopeEnd < offset) {
184
+ const last = active.pop();
185
+ if (!active.length) break;
186
+ let parent = 0;
187
+ while (parent * 2 + 1 < active.length) {
188
+ let child = parent * 2 + 1;
189
+ if (child + 1 < active.length && active[child + 1] > active[child])
190
+ child++;
191
+ if (active[child] <= last) break;
192
+ active[parent] = active[child];
193
+ parent = child;
194
+ }
195
+ active[parent] = last;
196
+ }
197
+ const localIndex = active[0];
198
+ if (!result.length || result.at(-1).localIndex !== localIndex) {
199
+ result.push({ start: offset, localIndex });
200
+ }
201
+ }
202
+ return result;
203
+ }
204
+ };
205
+ }
206
+ });
207
+
122
208
  // ../packages/neoscript-language/src/cell-pattern.ts
123
209
  var NEO_CELL_PATTERN_CLASS_ID, NEO_CELL_PATTERN_CONSTRUCTOR_ID, NEO_CELL_PATTERN_EXCLUDING_ENUM_ID, NEO_CELL_PATTERN_EXCLUDING_OPTION_IDS, NEO_CELL_PATTERN_MEMBER_IDS, NEO_OBJECT_GRID_MEMBER_IDS, NEO_CELL_PATTERN_PURE_NATIVE_MEMBER_IDS, NEO_OBJECT_CLASS_ID;
124
210
  var init_cell_pattern = __esm({
@@ -8385,7 +8471,7 @@ var init_strict_resolver = __esm({
8385
8471
  };
8386
8472
  }
8387
8473
  if (expression.kind !== "member") return null;
8388
- if (expression.receiver.kind === "ident" && !scope.lookup(expression.receiver.name)) {
8474
+ if (expression.receiver.kind === "ident" && !this.hasValueReceiver(expression.receiver.name, scope)) {
8389
8475
  const owner2 = this.project.typeByName.get(expression.receiver.name);
8390
8476
  const symbol2 = owner2?.members.find(
8391
8477
  (candidate) => candidate.name === expression.name
@@ -9015,7 +9101,7 @@ var init_strict_resolver = __esm({
9015
9101
  expression: this.resolveIdentifier("this", scope, expression.pos)
9016
9102
  };
9017
9103
  } else if (expression.kind === "member") {
9018
- if (expression.receiver.kind === "ident" && !scope.lookup(expression.receiver.name)) {
9104
+ if (expression.receiver.kind === "ident" && !this.hasValueReceiver(expression.receiver.name, scope)) {
9019
9105
  owner = this.project.typeByName.get(expression.receiver.name);
9020
9106
  symbol = owner?.members.find(
9021
9107
  (candidate) => candidate.name === expression.name && (candidate.kind === "function" || candidate.kind === "method")
@@ -9100,7 +9186,7 @@ var init_strict_resolver = __esm({
9100
9186
  owner = implicit.owner;
9101
9187
  receiverType = this.context.thisClass;
9102
9188
  } else if (expression.kind === "member") {
9103
- if (expression.receiver.kind === "ident" && !scope.lookup(expression.receiver.name)) {
9189
+ if (expression.receiver.kind === "ident" && !this.hasValueReceiver(expression.receiver.name, scope)) {
9104
9190
  owner = this.project.typeByName.get(expression.receiver.name);
9105
9191
  symbol = owner?.members.find(
9106
9192
  (candidate) => candidate.name === expression.name && (candidate.kind === "function" || candidate.kind === "method")
@@ -9320,10 +9406,17 @@ var init_strict_resolver = __esm({
9320
9406
  if (global) return this.symbolValue(global);
9321
9407
  const implicit = this.implicitMember(name);
9322
9408
  if (implicit && implicit.symbol.kind !== "function" && implicit.symbol.kind !== "method") {
9409
+ if (implicit.symbol.static === true) {
9410
+ return this.resolveStaticMemberValue(
9411
+ implicit.owner,
9412
+ implicit.symbol,
9413
+ pos
9414
+ );
9415
+ }
9323
9416
  return this.resolveMember(
9324
9417
  {
9325
9418
  kind: "ident",
9326
- name: implicit.symbol.static === true ? implicit.owner.name : "this",
9419
+ name: "this",
9327
9420
  pos
9328
9421
  },
9329
9422
  name,
@@ -9545,7 +9638,7 @@ var init_strict_resolver = __esm({
9545
9638
  if (path[1] !== NEO_VARIANT_SCOPE_NAME) return null;
9546
9639
  const className = path[0];
9547
9640
  if (className === void 0) return null;
9548
- if (scope.lookup(className)) return null;
9641
+ if (this.hasValueReceiver(className, scope)) return null;
9549
9642
  const classType = this.project.typeByName.get(className);
9550
9643
  if (classType === void 0 || classType.kind !== "class") return null;
9551
9644
  const rest = path.slice(2);
@@ -9649,7 +9742,7 @@ var init_strict_resolver = __esm({
9649
9742
  const isInitialize = callee.name === VARIANT_INITIALIZE_NAME;
9650
9743
  const isApply = callee.name === VARIANT_APPLY_NAME;
9651
9744
  if (!isInitialize && !isApply) return null;
9652
- if (callee.receiver.kind === "ident" && !scope.lookup(callee.receiver.name) && this.project.typeByName.has(callee.receiver.name)) {
9745
+ if (callee.receiver.kind === "ident" && !this.hasValueReceiver(callee.receiver.name, scope) && this.project.typeByName.has(callee.receiver.name)) {
9653
9746
  return null;
9654
9747
  }
9655
9748
  const receiver = this.resolveExpression(callee.receiver, scope);
@@ -9804,6 +9897,49 @@ var init_strict_resolver = __esm({
9804
9897
  }
9805
9898
  return resolved;
9806
9899
  }
9900
+ resolveStaticMemberValue(staticType, member, pos) {
9901
+ this.assertMemberAccessible(staticType, member, pos);
9902
+ if (member.static !== true) {
9903
+ throw new CompileError(
9904
+ `Member '${staticType.name}.${member.name}' requires an instance receiver.`,
9905
+ pos
9906
+ );
9907
+ }
9908
+ if (member.kind === "function" || member.kind === "method") {
9909
+ throw new CompileError(
9910
+ `Static Function '${staticType.name}.${member.name}' must be called with \`()\`; it cannot be read as a value.`,
9911
+ pos
9912
+ );
9913
+ }
9914
+ if (member.id === NEOSCRIPT_SPRITE_INFO_EMPTY_MEMBER_ID) {
9915
+ return literal(member.type, {
9916
+ ...NEOSCRIPT_SPRITE_INFO_EMPTY_VALUE
9917
+ });
9918
+ }
9919
+ if (member.computed === true) {
9920
+ return {
9921
+ pointer: {
9922
+ type: "callGetter" /* CallGetter */,
9923
+ memberId: member.id,
9924
+ receiver: { kind: "static", memberId: member.id }
9925
+ },
9926
+ type: member.type,
9927
+ symbol: member,
9928
+ ...member.writable ? {
9929
+ writability: member.writability ? toWritability(member.writability) : "setter" /* Setter */
9930
+ } : {}
9931
+ };
9932
+ }
9933
+ return {
9934
+ pointer: {
9935
+ type: "staticMember" /* StaticMember */,
9936
+ memberId: member.id
9937
+ },
9938
+ type: member.type,
9939
+ symbol: member,
9940
+ ...member.writability ? { writability: toWritability(member.writability) } : {}
9941
+ };
9942
+ }
9807
9943
  resolveMemberNatural(receiverAst, name, scope, pos, optional2) {
9808
9944
  if (receiverAst.kind === "ident" && receiverAst.name === "base") {
9809
9945
  const { owner, symbol, receiver: receiver2 } = this.resolveBaseMember(
@@ -9844,7 +9980,7 @@ var init_strict_resolver = __esm({
9844
9980
  }
9845
9981
  return variantPath2;
9846
9982
  }
9847
- if (receiverAst.kind === "ident" && !scope.lookup(receiverAst.name)) {
9983
+ if (receiverAst.kind === "ident" && !this.hasValueReceiver(receiverAst.name, scope)) {
9848
9984
  if (receiverAst.name === "SpriteInfo" && name === "Empty") {
9849
9985
  return literal(
9850
9986
  { kind: "primitive", name: "SpriteInfo" },
@@ -9886,47 +10022,7 @@ var init_strict_resolver = __esm({
9886
10022
  pos
9887
10023
  );
9888
10024
  }
9889
- this.assertMemberAccessible(staticType, member, pos);
9890
- if (member.static !== true) {
9891
- throw new CompileError(
9892
- `Member '${staticType.name}.${name}' requires an instance receiver.`,
9893
- pos
9894
- );
9895
- }
9896
- if (member.kind === "function" || member.kind === "method") {
9897
- throw new CompileError(
9898
- `Static Function '${staticType.name}.${name}' must be called with \`()\`; it cannot be read as a value.`,
9899
- pos
9900
- );
9901
- }
9902
- if (member.id === NEOSCRIPT_SPRITE_INFO_EMPTY_MEMBER_ID) {
9903
- return literal(member.type, {
9904
- ...NEOSCRIPT_SPRITE_INFO_EMPTY_VALUE
9905
- });
9906
- }
9907
- if (member.computed === true) {
9908
- return {
9909
- pointer: {
9910
- type: "callGetter" /* CallGetter */,
9911
- memberId: member.id,
9912
- receiver: { kind: "static", memberId: member.id }
9913
- },
9914
- type: member.type,
9915
- symbol: member,
9916
- ...member.writable ? {
9917
- writability: member.writability ? toWritability(member.writability) : "setter" /* Setter */
9918
- } : {}
9919
- };
9920
- }
9921
- return {
9922
- pointer: {
9923
- type: "staticMember" /* StaticMember */,
9924
- memberId: member.id
9925
- },
9926
- type: member.type,
9927
- symbol: member,
9928
- ...member.writability ? { writability: toWritability(member.writability) } : {}
9929
- };
10025
+ return this.resolveStaticMemberValue(staticType, member, pos);
9930
10026
  }
9931
10027
  }
9932
10028
  const resolvedReceiver = this.resolveExpression(receiverAst, scope);
@@ -10716,7 +10812,7 @@ var init_strict_resolver = __esm({
10716
10812
  pos
10717
10813
  );
10718
10814
  }
10719
- if (callee.receiver.kind === "ident" && !scope.lookup(callee.receiver.name)) {
10815
+ if (callee.receiver.kind === "ident" && !this.hasValueReceiver(callee.receiver.name, scope)) {
10720
10816
  if (callee.receiver.name === MATH_NAMESPACE_NAME) {
10721
10817
  if (callee.optional) {
10722
10818
  throw new CompileError(
@@ -11967,7 +12063,7 @@ var init_strict_resolver = __esm({
11967
12063
  if (implicit?.symbol.static !== true)
11968
12064
  receiverType = this.context.thisClass;
11969
12065
  } else if (expression.kind === "member") {
11970
- const isStatic = expression.receiver.kind === "ident" && !scope.lookup(expression.receiver.name) && this.project.typeByName.has(expression.receiver.name);
12066
+ const isStatic = expression.receiver.kind === "ident" && !this.hasValueReceiver(expression.receiver.name, scope) && this.project.typeByName.has(expression.receiver.name);
11971
12067
  if (!isStatic) {
11972
12068
  receiverType = this.resolveExpression(expression.receiver, scope).type;
11973
12069
  }
@@ -12673,7 +12769,7 @@ var init_strict_resolver = __esm({
12673
12769
  }
12674
12770
  if (expression.callee.kind !== "member") return null;
12675
12771
  const memberName = expression.callee.name;
12676
- if (expression.callee.receiver.kind === "ident" && !scope.lookup(expression.callee.receiver.name)) {
12772
+ if (expression.callee.receiver.kind === "ident" && !this.hasValueReceiver(expression.callee.receiver.name, scope)) {
12677
12773
  const staticType = this.project.typeByName.get(
12678
12774
  expression.callee.receiver.name
12679
12775
  );
@@ -13384,6 +13480,9 @@ var init_strict_resolver = __esm({
13384
13480
  }
13385
13481
  return literal(symbol.type, null);
13386
13482
  }
13483
+ hasValueReceiver(name, scope) {
13484
+ return scope.lookup(name) !== null || this.implicitMember(name) !== null;
13485
+ }
13387
13486
  implicitMember(name) {
13388
13487
  if (this.context.implicitMemberAccess !== true) return null;
13389
13488
  const declaring = this.context.declaringType ?? this.context.thisClass;
@@ -13425,6 +13524,53 @@ var init_strict_resolver = __esm({
13425
13524
  });
13426
13525
 
13427
13526
  // ../packages/neoscript-language/src/analyzer.ts
13527
+ function scriptFeatureIndex(snapshot) {
13528
+ const cached = SCRIPT_FEATURE_INDEX.get(snapshot);
13529
+ if (cached) return cached;
13530
+ const references = /* @__PURE__ */ new Map();
13531
+ const locals = /* @__PURE__ */ new Map();
13532
+ const calls = /* @__PURE__ */ new Map();
13533
+ const contextualKeywords = /* @__PURE__ */ new Set();
13534
+ const typeAnnotations = /* @__PURE__ */ new Set();
13535
+ for (const reference2 of snapshot.parsed.references) {
13536
+ if (!references.has(reference2.start))
13537
+ references.set(reference2.start, reference2);
13538
+ }
13539
+ for (const local of snapshot.parsed.locals) {
13540
+ const start = snapshot.source.offsetAt(local.nameRange.start);
13541
+ if (!locals.has(start)) locals.set(start, local);
13542
+ if (local.contextualKeywordRange)
13543
+ contextualKeywords.add(
13544
+ snapshot.source.offsetAt(local.contextualKeywordRange.start)
13545
+ );
13546
+ for (const token of local.typeTokens) typeAnnotations.add(token.start);
13547
+ }
13548
+ for (const call of snapshot.parsed.calls) {
13549
+ const start = snapshot.source.offsetAt(call.nameRange.start);
13550
+ if (!calls.has(start)) calls.set(start, call);
13551
+ if (call.kind === "constructor" && call.contextualKeywordRange)
13552
+ contextualKeywords.add(
13553
+ snapshot.source.offsetAt(call.contextualKeywordRange.start)
13554
+ );
13555
+ }
13556
+ const tokens = snapshot.lexed.tokens.filter(
13557
+ (token) => token.kind !== "comment" && token.kind !== "eof"
13558
+ );
13559
+ const tokenIndexes = new Map(
13560
+ tokens.map((token, index2) => [token.start, index2])
13561
+ );
13562
+ const index = {
13563
+ references,
13564
+ locals,
13565
+ calls,
13566
+ contextualKeywords,
13567
+ typeAnnotations,
13568
+ tokens,
13569
+ tokenIndexes
13570
+ };
13571
+ SCRIPT_FEATURE_INDEX.set(snapshot, index);
13572
+ return index;
13573
+ }
13428
13574
  function buildProjectIndexWithBuiltins(context) {
13429
13575
  const typeIds = new Set(context.project.types.map((type) => type.id));
13430
13576
  const globalIds3 = new Set(context.project.globals.map((symbol) => symbol.id));
@@ -13684,8 +13830,12 @@ function signatureHelp(snapshot, position) {
13684
13830
  function inlayHints(snapshot, range2) {
13685
13831
  const hints = [];
13686
13832
  for (const call of snapshot.parsed.calls) {
13687
- const reference2 = snapshot.parsed.references.find(
13688
- (candidate) => candidate.range.start.line === call.nameRange.start.line && candidate.range.start.character === call.nameRange.start.character
13833
+ if (range2 && !call.argumentRanges.some(
13834
+ (argument2) => comparePositions(argument2.end, range2.start) >= 0 && comparePositions(argument2.start, range2.end) <= 0
13835
+ ))
13836
+ continue;
13837
+ const reference2 = scriptFeatureIndex(snapshot).references.get(
13838
+ snapshot.source.offsetAt(call.nameRange.start)
13689
13839
  );
13690
13840
  const resolution = reference2 ? resolveReference(snapshot, reference2) : null;
13691
13841
  const parameterNames = call.kind === "constructor" ? constructorParameterNames(resolution?.staticType) : resolution?.symbol?.parameters?.map((parameter4) => parameter4.name);
@@ -13698,6 +13848,7 @@ function inlayHints(snapshot, range2) {
13698
13848
  continue;
13699
13849
  }
13700
13850
  const position = firstNonWhitespacePosition(snapshot, argumentRange);
13851
+ if (comparePositions(position, argumentRange.end) >= 0) continue;
13701
13852
  if (range2 && !positionInRange(position, range2)) continue;
13702
13853
  hints.push({
13703
13854
  position,
@@ -13709,10 +13860,10 @@ function inlayHints(snapshot, range2) {
13709
13860
  }
13710
13861
  for (const local of snapshot.parsed.locals) {
13711
13862
  if (!local.inferred || !local.contextualKeywordRange) continue;
13712
- const type = resolveDeclaredType(local, snapshot);
13713
- if (type.kind === "primitive" && type.name === "unknown") continue;
13714
13863
  const position = local.nameRange.end;
13715
13864
  if (range2 && !positionInRange(position, range2)) continue;
13865
+ const type = localSymbol(local, snapshot).type;
13866
+ if (type.kind === "primitive" && type.name === "unknown") continue;
13716
13867
  hints.push({
13717
13868
  position,
13718
13869
  label: `: ${formatType(type, snapshot.project)}`,
@@ -13806,6 +13957,7 @@ function documentSymbols(snapshot) {
13806
13957
  }
13807
13958
  function semanticTokens(snapshot) {
13808
13959
  const result = [];
13960
+ const index = scriptFeatureIndex(snapshot);
13809
13961
  for (const token of snapshot.lexed.tokens) {
13810
13962
  if (token.kind === "eof" || token.kind === "error") continue;
13811
13963
  if (token.kind === "keyword" || token.kind === "comment" || token.kind === "string") {
@@ -13829,17 +13981,11 @@ function semanticTokens(snapshot) {
13829
13981
  continue;
13830
13982
  }
13831
13983
  if (token.kind !== "identifier") continue;
13832
- if (snapshot.parsed.locals.some(
13833
- (local) => local.contextualKeywordRange?.start.line === token.range.start.line && local.contextualKeywordRange.start.character === token.range.start.character
13834
- ) || snapshot.parsed.calls.some(
13835
- (call) => call.kind === "constructor" && call.contextualKeywordRange?.start.line === token.range.start.line && call.contextualKeywordRange.start.character === token.range.start.character
13836
- )) {
13984
+ if (index.contextualKeywords.has(token.start)) {
13837
13985
  result.push({ range: token.range, type: "keyword", modifiers: [] });
13838
13986
  continue;
13839
13987
  }
13840
- const declaredLocal = snapshot.parsed.locals.find(
13841
- (local) => local.nameRange.start.line === token.range.start.line && local.nameRange.start.character === token.range.start.character
13842
- );
13988
+ const declaredLocal = index.locals.get(token.start);
13843
13989
  if (declaredLocal) {
13844
13990
  const symbol2 = localSymbol(declaredLocal, snapshot);
13845
13991
  result.push({
@@ -13852,9 +13998,7 @@ function semanticTokens(snapshot) {
13852
13998
  });
13853
13999
  continue;
13854
14000
  }
13855
- const reference2 = snapshot.parsed.references.find(
13856
- (candidate) => candidate.start === token.start
13857
- );
14001
+ const reference2 = index.references.get(token.start);
13858
14002
  const resolved = reference2 ? resolveReference(snapshot, reference2) : null;
13859
14003
  if (resolved?.staticType) {
13860
14004
  result.push({
@@ -13906,6 +14050,16 @@ function referenceAtPosition(snapshot, position) {
13906
14050
  snapshot.source.offsetAt(position)
13907
14051
  );
13908
14052
  }
14053
+ function localSymbolAtPosition(snapshot, position) {
14054
+ const offset = snapshot.source.offsetAt(position);
14055
+ const declaration = snapshot.parsed.locals.find(
14056
+ (local) => snapshot.source.offsetAt(local.nameRange.start) <= offset && offset <= snapshot.source.offsetAt(local.nameRange.end)
14057
+ );
14058
+ if (declaration) return localSymbol(declaration, snapshot);
14059
+ const reference2 = referenceAtPosition(snapshot, position);
14060
+ const symbol = reference2 ? resolveReference(snapshot, reference2)?.symbol : null;
14061
+ return symbol?.id.startsWith("local:") ? symbol : null;
14062
+ }
13909
14063
  function contextualCompletionItems(snapshot, offset, word) {
13910
14064
  const items = [];
13911
14065
  const expectedType = expectedTypeAt(snapshot, word.start);
@@ -14148,6 +14302,14 @@ function expectedTypeAt(snapshot, offset) {
14148
14302
  offset
14149
14303
  );
14150
14304
  }
14305
+ if (snapshot.expressionRange && snapshot.context.returnType) {
14306
+ return expectedCollectionElementType(
14307
+ snapshot.context.returnType,
14308
+ tokens,
14309
+ snapshot.source.offsetAt(snapshot.expressionRange.start),
14310
+ offset
14311
+ );
14312
+ }
14151
14313
  return null;
14152
14314
  }
14153
14315
  function expectedTypeFromComparisonRight(snapshot, offset) {
@@ -14559,16 +14721,12 @@ function resolveReference(snapshot, reference2) {
14559
14721
  );
14560
14722
  if (contextual) return contextual;
14561
14723
  }
14562
- const tokens = snapshot.lexed.tokens.filter(
14563
- (token) => token.kind !== "comment" && token.kind !== "eof" && token.end <= reference2.end
14564
- );
14565
- const tokenIndex = tokens.findIndex(
14566
- (token) => token.start === reference2.start
14567
- );
14568
- if (tokenIndex < 0) return null;
14569
- if (!reference2.member) {
14724
+ if (!reference2.member)
14570
14725
  return resolveRoot(snapshot, reference2.name, reference2.start);
14571
- }
14726
+ const index = scriptFeatureIndex(snapshot);
14727
+ const tokens = index.tokens;
14728
+ const tokenIndex = index.tokenIndexes.get(reference2.start);
14729
+ if (tokenIndex === void 0) return null;
14572
14730
  const dotIndex = tokenIndex - 1;
14573
14731
  const chainTokens = expressionTokensBefore(tokens, dotIndex);
14574
14732
  const receiver = resolveChain(snapshot, chainTokens, reference2.start);
@@ -14576,9 +14734,7 @@ function resolveReference(snapshot, reference2) {
14576
14734
  return resolveContextualSwitchEnumReference(snapshot, reference2);
14577
14735
  }
14578
14736
  const member = resolveMember(snapshot, receiver, reference2.name);
14579
- const call = snapshot.parsed.calls.find(
14580
- (call2) => call2.nameRange.start.line === reference2.range.start.line && call2.nameRange.start.character === reference2.range.start.character
14581
- );
14737
+ const call = index.calls.get(reference2.start);
14582
14738
  return specializeGridQuery(snapshot, member, call?.typeArgumentTokens);
14583
14739
  }
14584
14740
  function specializeGridQuery(snapshot, resolved, typeArguments) {
@@ -14786,9 +14942,7 @@ function selectLambdaResultTokens(body) {
14786
14942
  return semicolon < 0 ? tail : tail.slice(0, semicolon);
14787
14943
  }
14788
14944
  function resolveRoot(snapshot, name, offset) {
14789
- const symbol = scopeAt(snapshot, offset).find(
14790
- (candidate) => candidate.name === name
14791
- );
14945
+ const symbol = scopeAt(snapshot, offset, name)[0];
14792
14946
  if (symbol) return { type: symbol.returnType ?? symbol.type, symbol };
14793
14947
  const typeParameter2 = contextualTypeParameters(snapshot).find(
14794
14948
  (candidate) => candidate.name === name
@@ -14842,18 +14996,18 @@ function resolveMember(snapshot, receiver, name) {
14842
14996
  symbol: member
14843
14997
  };
14844
14998
  }
14845
- function scopeAt(snapshot, offset) {
14999
+ function scopeAt(snapshot, offset, name) {
14846
15000
  const result = [];
14847
- for (const root of snapshot.context.project.roots) {
15001
+ for (const root of namedItems(snapshot.context.project.roots, name)) {
14848
15002
  result.push(withScope(root, 0, snapshot.source.text.length));
14849
15003
  }
14850
15004
  for (const global of [
14851
- ...snapshot.context.project.globals,
14852
- ...NEOSCRIPT_GLOBALS
15005
+ ...namedItems(snapshot.context.project.globals, name),
15006
+ ...namedItems(NEOSCRIPT_GLOBALS, name)
14853
15007
  ]) {
14854
15008
  result.push(withScope(global, 0, snapshot.source.text.length));
14855
15009
  }
14856
- if (snapshot.context.thisClass) {
15010
+ if (snapshot.context.thisClass && (name === void 0 || name === "this")) {
14857
15011
  result.push(
14858
15012
  withScope(
14859
15013
  {
@@ -14868,7 +15022,7 @@ function scopeAt(snapshot, offset) {
14868
15022
  )
14869
15023
  );
14870
15024
  }
14871
- if (snapshot.context.baseClass) {
15025
+ if (snapshot.context.baseClass && (name === void 0 || name === "base")) {
14872
15026
  result.push(
14873
15027
  withScope(
14874
15028
  {
@@ -14910,7 +15064,7 @@ function scopeAt(snapshot, offset) {
14910
15064
  )
14911
15065
  );
14912
15066
  }
14913
- if (snapshot.context.dialogueContextType) {
15067
+ if (snapshot.context.dialogueContextType && (name === void 0 || name === "context")) {
14914
15068
  result.push(
14915
15069
  withScope(
14916
15070
  {
@@ -14925,7 +15079,7 @@ function scopeAt(snapshot, offset) {
14925
15079
  )
14926
15080
  );
14927
15081
  }
14928
- for (const alias of snapshot.context.valueAliases ?? []) {
15082
+ for (const alias of namedItems(snapshot.context.valueAliases ?? [], name)) {
14929
15083
  result.push(
14930
15084
  withScope(
14931
15085
  {
@@ -14944,7 +15098,7 @@ function scopeAt(snapshot, offset) {
14944
15098
  if (snapshot.context.implicitMemberAccess === true && declaringType?.kind === "named") {
14945
15099
  const owner = snapshot.project.typeById.get(declaringType.typeId);
14946
15100
  if (owner) {
14947
- for (const member of owner.members) {
15101
+ for (const member of namedItems(owner.members, name)) {
14948
15102
  if (snapshot.context.staticMember === true && member.static !== true) {
14949
15103
  continue;
14950
15104
  }
@@ -14953,7 +15107,7 @@ function scopeAt(snapshot, offset) {
14953
15107
  }
14954
15108
  }
14955
15109
  }
14956
- if (effectiveDocumentKind(snapshot, offset) === "setter" && snapshot.context.returnType) {
15110
+ if ((name === void 0 || name === "value") && effectiveDocumentKind(snapshot, offset) === "setter" && snapshot.context.returnType) {
14957
15111
  result.push(
14958
15112
  withScope(
14959
15113
  {
@@ -14968,19 +15122,47 @@ function scopeAt(snapshot, offset) {
14968
15122
  )
14969
15123
  );
14970
15124
  }
14971
- for (const parameter4 of snapshot.context.parameters ?? []) {
15125
+ for (const parameter4 of namedItems(snapshot.context.parameters ?? [], name)) {
14972
15126
  result.push(
14973
15127
  parameterSymbol(parameter4, snapshot.uri, snapshot.source.text.length)
14974
15128
  );
14975
15129
  }
14976
- for (const local of snapshot.parsed.locals) {
14977
- if (offset < local.scopeStart || offset > local.scopeEnd) continue;
14978
- result.push(localSymbol(local, snapshot));
15130
+ if (name === void 0) {
15131
+ for (const local of snapshot.parsed.locals) {
15132
+ if (offset < local.scopeStart || offset > local.scopeEnd) continue;
15133
+ result.push(localSymbol(local, snapshot));
15134
+ }
15135
+ } else {
15136
+ let index = LOCAL_SCOPE_INDEX.get(snapshot.parsed.locals);
15137
+ if (!index) {
15138
+ index = new LocalScopeIndex(snapshot.parsed.locals);
15139
+ LOCAL_SCOPE_INDEX.set(snapshot.parsed.locals, index);
15140
+ }
15141
+ const local = index.at(name, offset);
15142
+ if (local) result.push(localSymbol(local, snapshot));
14979
15143
  }
14980
15144
  const byName = /* @__PURE__ */ new Map();
14981
- for (const symbol of result) byName.set(symbol.name, symbol);
15145
+ for (const symbol of result) {
15146
+ if (name === void 0 || symbol.name === name)
15147
+ byName.set(symbol.name, symbol);
15148
+ }
14982
15149
  return [...byName.values()];
14983
15150
  }
15151
+ function namedItems(items, name) {
15152
+ if (name === void 0) return items;
15153
+ let index = NAMED_ITEM_INDEX.get(items);
15154
+ if (!index) {
15155
+ index = /* @__PURE__ */ new Map();
15156
+ for (let i2 = 0; i2 < items.length; i2++) {
15157
+ const key = items[i2].name;
15158
+ const matches = index.get(key);
15159
+ if (matches) matches.push(i2);
15160
+ else index.set(key, [i2]);
15161
+ }
15162
+ NAMED_ITEM_INDEX.set(items, index);
15163
+ }
15164
+ return (index.get(name) ?? []).map((i2) => items[i2]);
15165
+ }
14984
15166
  function resolveDeclaredType(local, snapshot) {
14985
15167
  if (local.inferred) return inferLocalType(local, snapshot);
14986
15168
  const tokens = local.typeTokens;
@@ -15474,7 +15656,14 @@ function parameterSymbol(value, uri, documentEnd) {
15474
15656
  );
15475
15657
  }
15476
15658
  function localSymbol(local, snapshot) {
15477
- return withScope(
15659
+ let symbols = LOCAL_SYMBOL_CACHE.get(snapshot);
15660
+ if (!symbols) {
15661
+ symbols = /* @__PURE__ */ new Map();
15662
+ LOCAL_SYMBOL_CACHE.set(snapshot, symbols);
15663
+ }
15664
+ const cached = symbols.get(local);
15665
+ if (cached) return cached;
15666
+ const symbol = withScope(
15478
15667
  {
15479
15668
  id: `local:${snapshot.uri}:${local.nameRange.start.line}:${local.nameRange.start.character}`,
15480
15669
  name: local.name,
@@ -15486,6 +15675,8 @@ function localSymbol(local, snapshot) {
15486
15675
  local.scopeStart,
15487
15676
  local.scopeEnd
15488
15677
  );
15678
+ symbols.set(local, symbol);
15679
+ return symbol;
15489
15680
  }
15490
15681
  function inferLambdaParameterType(local, snapshot) {
15491
15682
  const explicit = resolveDeclaredType(local, snapshot);
@@ -15850,18 +16041,16 @@ function comparePositions(left, right) {
15850
16041
  return left.line === right.line ? left.character - right.character : left.line - right.line;
15851
16042
  }
15852
16043
  function isTypeAnnotationReference(snapshot, reference2) {
15853
- return snapshot.parsed.locals.some(
15854
- (local) => local.typeTokens.some((token) => token.start === reference2.start)
15855
- );
16044
+ return scriptFeatureIndex(snapshot).typeAnnotations.has(reference2.start);
15856
16045
  }
15857
16046
  function isConstructorTypeReference(snapshot, reference2) {
15858
- return snapshot.parsed.calls.some(
15859
- (call) => call.kind === "constructor" && call.nameRange.start.line === reference2.range.start.line && call.nameRange.start.character === reference2.range.start.character
15860
- );
16047
+ return scriptFeatureIndex(snapshot).calls.get(reference2.start)?.kind === "constructor";
15861
16048
  }
16049
+ var SCRIPT_FEATURE_INDEX, LOCAL_SCOPE_INDEX, NAMED_ITEM_INDEX, LOCAL_SYMBOL_CACHE;
15862
16050
  var init_analyzer = __esm({
15863
16051
  "../packages/neoscript-language/src/analyzer.ts"() {
15864
16052
  "use strict";
16053
+ init_local_scope_index();
15865
16054
  init_cell_pattern();
15866
16055
  init_tile_conversion();
15867
16056
  init_syntax_index();
@@ -15870,6 +16059,10 @@ var init_analyzer = __esm({
15870
16059
  init_executable_parser();
15871
16060
  init_strict_resolver();
15872
16061
  init_project();
16062
+ SCRIPT_FEATURE_INDEX = /* @__PURE__ */ new WeakMap();
16063
+ LOCAL_SCOPE_INDEX = /* @__PURE__ */ new WeakMap();
16064
+ NAMED_ITEM_INDEX = /* @__PURE__ */ new WeakMap();
16065
+ LOCAL_SYMBOL_CACHE = /* @__PURE__ */ new WeakMap();
15873
16066
  }
15874
16067
  });
15875
16068
 
@@ -19131,16 +19324,34 @@ function documentPrefix(text, position) {
19131
19324
  ].join("\n");
19132
19325
  }
19133
19326
  function projectTokenAt(document, position) {
19134
- const offset = new SourceText(document.text).offsetAt(position);
19327
+ return projectTokens(document)[projectTokenIndexAt(document, position)];
19328
+ }
19329
+ function projectTokenIndexAt(document, position) {
19330
+ const offset = projectSourceText(document).offsetAt(position);
19135
19331
  const tokens = projectTokens(document);
19136
- const containing = tokens.find(
19137
- (token) => token.start <= offset && offset < token.end
19138
- );
19139
- if (isProjectNameToken(containing)) return containing;
19140
- const touching = tokens.find(
19141
- (token) => isProjectNameToken(token) && token.end === offset
19142
- );
19143
- return touching ?? containing;
19332
+ let low = 0;
19333
+ let high = tokens.length;
19334
+ while (low < high) {
19335
+ const middle = low + high >>> 1;
19336
+ if (tokens[middle].start <= offset) low = middle + 1;
19337
+ else high = middle;
19338
+ }
19339
+ const candidate = tokens[low - 1];
19340
+ const containing = candidate && offset < candidate.end ? low - 1 : -1;
19341
+ if (isProjectNameToken(tokens[containing])) return containing;
19342
+ const touching = isProjectNameToken(candidate) && candidate.end === offset ? low - 1 : low - 2;
19343
+ if (isProjectNameToken(tokens[touching]) && tokens[touching].end === offset) {
19344
+ return touching;
19345
+ }
19346
+ return containing;
19347
+ }
19348
+ function projectSourceText(document) {
19349
+ let source = PROJECT_SOURCE_CACHE.get(document);
19350
+ if (!source) {
19351
+ source = new SourceText(document.text);
19352
+ PROJECT_SOURCE_CACHE.set(document, source);
19353
+ }
19354
+ return source;
19144
19355
  }
19145
19356
  function isProjectNameToken(token) {
19146
19357
  return token !== void 0 && (token.kind === "identifier" || token.kind === "type" || token.kind === "keyword");
@@ -19148,7 +19359,7 @@ function isProjectNameToken(token) {
19148
19359
  function projectTokens(document) {
19149
19360
  const cached = PROJECT_TOKEN_CACHE.get(document);
19150
19361
  if (cached) return cached;
19151
- const source = new SourceText(document.text);
19362
+ const source = projectSourceText(document);
19152
19363
  const tokens = lex(document.text).tokens.flatMap(
19153
19364
  (token) => {
19154
19365
  if (token.kind !== "string" || !token.raw.startsWith('"""')) {
@@ -19248,18 +19459,16 @@ function tripleInterpolationEnd(text, start, limit) {
19248
19459
  }
19249
19460
  return null;
19250
19461
  }
19251
- function rangesEqual(left, right) {
19252
- return positionCompare2(left.start, right.start) === 0 && positionCompare2(left.end, right.end) === 0;
19253
- }
19254
19462
  function positionCompare2(left, right) {
19255
19463
  return left.line === right.line ? left.character - right.character : left.line - right.line;
19256
19464
  }
19257
- var PROJECT_TOKEN_CACHE;
19465
+ var PROJECT_SOURCE_CACHE, PROJECT_TOKEN_CACHE;
19258
19466
  var init_project_source_tokens = __esm({
19259
19467
  "../packages/neoscript-language/src/project-source-tokens.ts"() {
19260
19468
  "use strict";
19261
19469
  init_lexer();
19262
19470
  init_source_text();
19471
+ PROJECT_SOURCE_CACHE = /* @__PURE__ */ new WeakMap();
19263
19472
  PROJECT_TOKEN_CACHE = /* @__PURE__ */ new WeakMap();
19264
19473
  }
19265
19474
  });
@@ -25783,18 +25992,25 @@ var init_project_source_semantics = __esm({
25783
25992
  });
25784
25993
 
25785
25994
  // ../packages/neoscript-language/src/project-source-accessors.ts
25786
- function findProjectSourceAccessorBody(source, keyword, containingRange) {
25787
- if (source.length === 0) return null;
25995
+ function projectSourceAccessors(source, containingRange) {
25788
25996
  const full = new SourceText(source);
25789
25997
  const range2 = containingRange ?? full.fullRange();
25790
25998
  const bodyStart = full.offsetAt(range2.start);
25791
- const bodyEnd = full.offsetAt(range2.end);
25792
- const body = source.slice(bodyStart, bodyEnd);
25793
- const tokens = lex(body).roots.filter((token) => token.kind !== "comment");
25999
+ const tokens = lex(
26000
+ source.slice(bodyStart, full.offsetAt(range2.end))
26001
+ ).roots.filter((token) => token.kind !== "comment");
25794
26002
  if (isToken(tokens[0], "operator", "=>")) {
25795
- if (keyword !== "get") return null;
25796
- return expressionAccessor(source, full, tokens, bodyStart, 1);
26003
+ return [
26004
+ {
26005
+ keyword: "get",
26006
+ form: "expression",
26007
+ range: range2,
26008
+ parameters: null,
26009
+ body: expressionAccessor(source, full, tokens, bodyStart, 1)
26010
+ }
26011
+ ];
25797
26012
  }
26013
+ const accessors = [];
25798
26014
  let depth = 0;
25799
26015
  for (let index = 0; index < tokens.length; index++) {
25800
26016
  const token = tokens[index];
@@ -25806,44 +26022,66 @@ function findProjectSourceAccessorBody(source, keyword, containingRange) {
25806
26022
  depth--;
25807
26023
  continue;
25808
26024
  }
25809
- if (depth !== 1 || token.kind !== "identifier" && token.kind !== "keyword" || token.text !== keyword) {
26025
+ if (depth !== 1 || token.kind !== "identifier" && token.kind !== "keyword" || token.text !== "get" && token.text !== "set")
25810
26026
  continue;
25811
- }
25812
26027
  let openIndex = index + 1;
26028
+ let parameters = null;
25813
26029
  if (isToken(tokens[openIndex], "punctuation", "(")) {
26030
+ const firstParameter = ++openIndex;
25814
26031
  let parameterDepth = 1;
25815
- for (let cursor = openIndex + 1; cursor < tokens.length; cursor++) {
25816
- const candidate = tokens[cursor];
25817
- if (isToken(candidate, "punctuation", "(")) parameterDepth++;
25818
- if (isToken(candidate, "punctuation", ")")) parameterDepth--;
25819
- if (parameterDepth !== 0) continue;
25820
- openIndex = cursor + 1;
25821
- break;
26032
+ for (; openIndex < tokens.length; openIndex++) {
26033
+ if (isToken(tokens[openIndex], "punctuation", "(")) parameterDepth++;
26034
+ if (isToken(tokens[openIndex], "punctuation", ")")) parameterDepth--;
26035
+ if (parameterDepth === 0) break;
25822
26036
  }
25823
- if (parameterDepth !== 0) return null;
26037
+ parameters = tokens.slice(firstParameter, openIndex);
26038
+ openIndex++;
25824
26039
  }
25825
26040
  const open = tokens[openIndex];
26041
+ let body = null;
26042
+ let form = isToken(open, "punctuation", ";") ? "contract" : "invalid";
25826
26043
  if (isToken(open, "operator", "=>")) {
25827
- return expressionAccessor(source, full, tokens, bodyStart, openIndex + 1);
25828
- }
25829
- if (open === void 0 || !isToken(open, "punctuation", "{")) return null;
25830
- let accessorDepth = 1;
25831
- for (let cursor = openIndex + 1; cursor < tokens.length; cursor++) {
25832
- const candidate = tokens[cursor];
25833
- if (isToken(candidate, "punctuation", "{")) accessorDepth++;
25834
- if (isToken(candidate, "punctuation", "}")) accessorDepth--;
25835
- if (accessorDepth !== 0) continue;
25836
- const start = bodyStart + open.end;
25837
- const end = bodyStart + candidate.start;
25838
- return {
25839
- range: full.range(start, end),
25840
- source: source.slice(start, end),
25841
- form: "block"
25842
- };
26044
+ form = "expression";
26045
+ body = expressionAccessor(source, full, tokens, bodyStart, openIndex + 1);
26046
+ if (body) {
26047
+ const end = full.offsetAt(body.range.end) - bodyStart;
26048
+ while (openIndex + 1 < tokens.length && tokens[openIndex + 1].start < end)
26049
+ openIndex++;
26050
+ }
26051
+ } else if (isToken(open, "punctuation", "{")) {
26052
+ form = "block";
26053
+ let accessorDepth = 1;
26054
+ for (let cursor = openIndex + 1; cursor < tokens.length; cursor++) {
26055
+ const candidate = tokens[cursor];
26056
+ if (isToken(candidate, "punctuation", "{")) accessorDepth++;
26057
+ if (isToken(candidate, "punctuation", "}")) accessorDepth--;
26058
+ if (accessorDepth !== 0) continue;
26059
+ const start = bodyStart + open.end;
26060
+ const end = bodyStart + candidate.start;
26061
+ body = {
26062
+ range: full.range(start, end),
26063
+ source: source.slice(start, end),
26064
+ form: "block"
26065
+ };
26066
+ openIndex = cursor;
26067
+ break;
26068
+ }
25843
26069
  }
25844
- return null;
26070
+ accessors.push({
26071
+ keyword: token.text,
26072
+ form,
26073
+ range: full.range(bodyStart + token.start, bodyStart + token.end),
26074
+ parameters,
26075
+ body
26076
+ });
26077
+ index = Math.max(index, openIndex);
25845
26078
  }
25846
- return null;
26079
+ return accessors;
26080
+ }
26081
+ function findProjectSourceAccessorBody(source, keyword, containingRange) {
26082
+ return projectSourceAccessors(source, containingRange).find(
26083
+ (accessor) => accessor.keyword === keyword
26084
+ )?.body ?? null;
25847
26085
  }
25848
26086
  function isToken(token, kind, text) {
25849
26087
  return token?.kind === kind && token.text === text;
@@ -26078,9 +26316,10 @@ function compileBody(args) {
26078
26316
  memberName: args.memberName,
26079
26317
  unit: args.unit,
26080
26318
  range: args.range,
26081
- context: args.context
26319
+ context: args.context,
26320
+ ...args.bodyMode ? { bodyMode: args.bodyMode } : {}
26082
26321
  });
26083
- const source = new SourceText(args.document.sourceText);
26322
+ const source = args.document.syntax.source;
26084
26323
  const start = source.offsetAt(args.range.start);
26085
26324
  const end = source.offsetAt(args.range.end);
26086
26325
  const result = compileNeoScriptStrict(
@@ -26435,7 +26674,9 @@ function sourceMemberSymbol(member, owner, typeIds, source) {
26435
26674
  };
26436
26675
  }
26437
26676
  if (member.kind === "property") {
26438
- const hasSetter = findProjectSourceAccessorBody(source, "set", member.body.range) !== null || /\bset\s*;/.test(member.body.text);
26677
+ const hasSetter = projectSourceAccessors(source, member.body.range).some(
26678
+ (accessor) => accessor.keyword === "set"
26679
+ );
26439
26680
  return {
26440
26681
  ...common,
26441
26682
  kind: "property",
@@ -27463,6 +27704,8 @@ function validateMemberTypeKind(uri, type, context, name, diagnostics) {
27463
27704
  function validateMembers(environment, diagnostics) {
27464
27705
  for (const ref of environment.interfaces.values()) {
27465
27706
  for (const member of ref.declaration.members) {
27707
+ if (member.kind === "property")
27708
+ validatePropertyAccessors(ref, member, diagnostics);
27466
27709
  if (member.kind === "field") {
27467
27710
  diagnose(
27468
27711
  diagnostics,
@@ -27723,6 +27966,8 @@ function validateClassMemberModifiers(ref, environment, diagnostics) {
27723
27966
  related(ref.location, "The containing class is declared here.")
27724
27967
  );
27725
27968
  }
27969
+ if (member.kind === "property")
27970
+ validatePropertyAccessors(ref, member, diagnostics);
27726
27971
  if (modifiers.has("abstract") && member.kind === "field" && member.initializer) {
27727
27972
  diagnose(
27728
27973
  diagnostics,
@@ -27732,17 +27977,85 @@ function validateClassMemberModifiers(ref, environment, diagnostics) {
27732
27977
  `Abstract member '${member.name}' cannot declare a default initializer.`
27733
27978
  );
27734
27979
  }
27735
- if (modifiers.has("abstract") && member.kind === "property" && (member.body.expression !== void 0 || /\b(?:get|set)\s*\{/.test(member.body.text))) {
27980
+ }
27981
+ }
27982
+ function validatePropertyAccessors(ref, member, diagnostics) {
27983
+ const accessors = projectSourceAccessors(member.body.text);
27984
+ const isInterface = ref.declaration.kind === "interface";
27985
+ const isAbstract = member.modifiers.includes("abstract");
27986
+ const contract = isInterface || isAbstract;
27987
+ const seen = /* @__PURE__ */ new Set();
27988
+ for (const accessor of accessors) {
27989
+ if (seen.has(accessor.keyword)) {
27990
+ diagnose(
27991
+ diagnostics,
27992
+ ref.uri,
27993
+ member.nameRange,
27994
+ "duplicate-property-accessor",
27995
+ `Property '${ref.declaration.name}.${member.name}' declares more than one '${accessor.keyword}' accessor.`
27996
+ );
27997
+ }
27998
+ seen.add(accessor.keyword);
27999
+ if (accessor.form === "invalid") {
28000
+ diagnose(
28001
+ diagnostics,
28002
+ ref.uri,
28003
+ member.nameRange,
28004
+ "invalid-property-accessor",
28005
+ `Property '${ref.declaration.name}.${member.name}' accessor '${accessor.keyword}' requires a body or a semicolon for an abstract/interface contract.`
28006
+ );
28007
+ }
28008
+ const parameters = accessor.parameters;
28009
+ if (parameters !== null) {
28010
+ const actual = parameters.map((token) => token.text);
28011
+ const expected = accessor.keyword === "get" ? "" : `${accessorTypeName(member.type)} value`;
28012
+ const expectedTokens = lex(expected).roots.filter((token) => token.kind !== "eof").map((token) => token.text);
28013
+ if (actual.length !== expectedTokens.length || actual.some((token, index) => token !== expectedTokens[index])) {
28014
+ const signature = accessor.keyword === "get" ? "get" : `set(${accessorTypeName(member.type)} value)`;
28015
+ diagnose(
28016
+ diagnostics,
28017
+ ref.uri,
28018
+ member.nameRange,
28019
+ "property-accessor-signature",
28020
+ `Property '${ref.declaration.name}.${member.name}' requires '${signature}'; accessor parameters must match the property type and use the name 'value' for a setter.`
28021
+ );
28022
+ }
28023
+ }
28024
+ if (contract && (accessor.form === "block" || accessor.form === "expression")) {
27736
28025
  diagnose(
27737
28026
  diagnostics,
27738
28027
  ref.uri,
27739
28028
  member.body.range,
27740
- "abstract-member-body",
27741
- `Abstract property '${member.name}' can declare accessor contracts but cannot implement an accessor body.`
28029
+ isInterface ? "interface-property-body" : "abstract-member-body",
28030
+ `${isInterface ? "Interface" : "Abstract"} property '${member.name}' can declare accessor contracts but cannot implement an accessor body.`
27742
28031
  );
27743
28032
  }
28033
+ if (!contract && accessor.keyword === "set" && accessor.body === null) {
28034
+ diagnose(
28035
+ diagnostics,
28036
+ ref.uri,
28037
+ member.nameRange,
28038
+ "concrete-property-setter-body",
28039
+ `Property '${ref.declaration.name}.${member.name}' requires a setter body. Implement 'set(${accessorTypeName(member.type)} value) { ... }', or remove the setter for a read-only property.`
28040
+ );
28041
+ }
28042
+ }
28043
+ if (!contract && !accessors.some(
28044
+ (accessor) => accessor.keyword === "get" && accessor.body !== null
28045
+ )) {
28046
+ diagnose(
28047
+ diagnostics,
28048
+ ref.uri,
28049
+ member.nameRange,
28050
+ "concrete-property-getter-body",
28051
+ `Property '${ref.declaration.name}.${member.name}' requires a getter body. Implement 'get { return ...; }' or 'get => ...;', declare the property abstract in an abstract class, or use a field with an initializer for stored data.`
28052
+ );
27744
28053
  }
27745
28054
  }
28055
+ function accessorTypeName(type) {
28056
+ const argumentsText = type.typeArguments.length ? `<${type.typeArguments.map(accessorTypeName).join(",")}>` : "";
28057
+ return `${type.name}${argumentsText}${type.nullable ? "?" : ""}`;
28058
+ }
27746
28059
  function validateClassOverrides(ref, environment, diagnostics) {
27747
28060
  const inherited = inheritedClassMembers(ref, environment);
27748
28061
  for (const member of ref.declaration.members) {
@@ -28135,10 +28448,10 @@ function valueAccessors(member, environment) {
28135
28448
  };
28136
28449
  }
28137
28450
  if (member.kind === "function") return { get: false, set: false };
28138
- if (member.body.expression) return { get: true, set: false };
28451
+ const accessors = projectSourceAccessors(member.body.text);
28139
28452
  return {
28140
- get: /\bget\b/.test(member.body.text),
28141
- set: /\bset\b/.test(member.body.text)
28453
+ get: accessors.some((accessor) => accessor.keyword === "get"),
28454
+ set: accessors.some((accessor) => accessor.keyword === "set")
28142
28455
  };
28143
28456
  }
28144
28457
  function resolvedSourceMemberStorage(owner, member, environment, seen = /* @__PURE__ */ new Set()) {
@@ -28318,6 +28631,8 @@ var init_project_source_type_checker = __esm({
28318
28631
  "use strict";
28319
28632
  init_language_spec();
28320
28633
  init_project_source_ast();
28634
+ init_lexer();
28635
+ init_project_source_accessors();
28321
28636
  init_project_source_identifiers();
28322
28637
  BUILTIN_ARITY = /* @__PURE__ */ new Map([
28323
28638
  ["List", 1],
@@ -29648,7 +29963,7 @@ function projectCompletions(analysis, document, position) {
29648
29963
  return projectFallbackCompletions(analysis, document, position);
29649
29964
  }
29650
29965
  function projectAnnotationCompletions(analysis, document, position) {
29651
- const source = new SourceText(document.text);
29966
+ const source = projectSourceText(document);
29652
29967
  const offset = source.offsetAt(position);
29653
29968
  const word = projectWordRange(document.text, offset);
29654
29969
  if (document.text[word.start - 1] !== "@") return null;
@@ -29676,7 +29991,7 @@ function projectAnnotationNamesAt(analysis, document, position) {
29676
29991
  if (document.languageId === "neoflow") {
29677
29992
  return ["id", "primary", "incomplete"];
29678
29993
  }
29679
- const offset = new SourceText(document.text).offsetAt(position);
29994
+ const offset = projectSourceText(document).offsetAt(position);
29680
29995
  const followingDeclaration = /^\s*(class|interface|enum)\b([^{}]*)/.exec(
29681
29996
  document.text.slice(offset)
29682
29997
  );
@@ -29728,7 +30043,7 @@ function projectMemberAnnotationNames(member) {
29728
30043
  return names;
29729
30044
  }
29730
30045
  function projectContextualEnumCompletions(analysis, document, position) {
29731
- const source = new SourceText(document.text);
30046
+ const source = projectSourceText(document);
29732
30047
  const offset = source.offsetAt(position);
29733
30048
  const word = projectWordRange(document.text, offset);
29734
30049
  if (!isContextualDot(document.text, word.start)) return null;
@@ -29856,7 +30171,7 @@ function projectAfterNewAt(analysis, document, position) {
29856
30171
  );
29857
30172
  let cursor = tokens.length - 1;
29858
30173
  const current = tokens[cursor];
29859
- const offset = new SourceText(document.text).offsetAt(position);
30174
+ const offset = projectSourceText(document).offsetAt(position);
29860
30175
  if (current?.kind === "identifier" && current.text !== "new" && /[A-Za-z0-9_]$/.test(document.text.slice(0, offset))) {
29861
30176
  cursor--;
29862
30177
  }
@@ -29889,10 +30204,35 @@ function projectTypeNameAssignable(analysis, sourceName, targetName) {
29889
30204
  };
29890
30205
  return derivesFrom(source.id);
29891
30206
  }
30207
+ function projectBodies(analysis, uri) {
30208
+ let byUri = PROJECT_BODIES.get(analysis);
30209
+ if (!byUri) {
30210
+ byUri = /* @__PURE__ */ new Map();
30211
+ for (const body of analysis.bodyContexts) {
30212
+ const bodies = byUri.get(body.uri);
30213
+ if (bodies) bodies.push(body);
30214
+ else byUri.set(body.uri, [body]);
30215
+ }
30216
+ for (const bodies of byUri.values())
30217
+ bodies.sort((a, b) => positionCompare2(a.range.end, b.range.end));
30218
+ PROJECT_BODIES.set(analysis, byUri);
30219
+ }
30220
+ return byUri.get(uri) ?? [];
30221
+ }
30222
+ function rangeItemAt(items, position) {
30223
+ let low = 0;
30224
+ let high = items.length;
30225
+ while (low < high) {
30226
+ const middle = low + high >>> 1;
30227
+ if (positionCompare2(items[middle].range.end, position) < 0)
30228
+ low = middle + 1;
30229
+ else high = middle;
30230
+ }
30231
+ const item = items[low];
30232
+ return item && rangeContains(item.range, position) ? item : void 0;
30233
+ }
29892
30234
  function projectBodySnapshotAt(analysis, document, position) {
29893
- const body = analysis.bodyContexts.find(
29894
- (candidate) => candidate.uri === document.uri && rangeContains(candidate.range, position)
29895
- );
30235
+ const body = rangeItemAt(projectBodies(analysis, document.uri), position);
29896
30236
  return body ? projectBodySnapshot(analysis, document, body) : null;
29897
30237
  }
29898
30238
  function projectBodySnapshot(analysis, document, body) {
@@ -29903,30 +30243,35 @@ function projectBodySnapshot(analysis, document, body) {
29903
30243
  }
29904
30244
  const cached = snapshots.get(body);
29905
30245
  if (cached) return cached;
29906
- const source = new SourceText(document.text);
30246
+ const source = projectSourceText(document);
29907
30247
  const start = source.offsetAt(body.range.start);
29908
30248
  const end = source.offsetAt(body.range.end);
29909
- const text = maskOutsideProjectBody(document.text, start, end);
29910
- const syntax = analyzeNeoScriptSyntax(text, body.context.kind);
30249
+ const lexed = analysis.documents.get(document.uri)?.syntax ?? lex(document.text);
30250
+ const syntax = analyzeNeoScriptSyntax(
30251
+ document.text,
30252
+ body.context.kind,
30253
+ sliceLexResult(lexed, start, end)
30254
+ );
29911
30255
  const context = { ...body.context, project: analysis.project };
29912
30256
  const snapshot = {
29913
30257
  uri: document.uri,
30258
+ ...body.bodyMode === "expression" ? { expressionRange: body.range } : {},
29914
30259
  source: syntax.lexed.source,
29915
30260
  lexed: syntax.lexed,
29916
30261
  parsed: syntax.parsed,
29917
30262
  context,
29918
- project: buildProjectIndexWithBuiltins(context)
30263
+ project: projectBodyIndex(analysis, context)
29919
30264
  };
29920
30265
  snapshots.set(body, snapshot);
29921
30266
  return snapshot;
29922
30267
  }
29923
- function maskOutsideProjectBody(text, start, end) {
29924
- let masked = "";
29925
- for (let index = 0; index < text.length; index++) {
29926
- const character = text[index] ?? "";
29927
- masked += index >= start && index < end || character === "\n" || character === "\r" ? character : " ";
30268
+ function projectBodyIndex(analysis, context) {
30269
+ let index = PROJECT_BODY_INDEX_CACHE.get(analysis);
30270
+ if (!index) {
30271
+ index = buildProjectIndexWithBuiltins(context);
30272
+ PROJECT_BODY_INDEX_CACHE.set(analysis, index);
29928
30273
  }
29929
- return masked;
30274
+ return index;
29930
30275
  }
29931
30276
  function projectWordRange(text, offset) {
29932
30277
  let start = offset;
@@ -29939,9 +30284,9 @@ function isContextualDot(text, wordStart) {
29939
30284
  while (cursor >= 0 && /\s/.test(text[cursor] ?? "")) cursor--;
29940
30285
  const beforeDot = text[cursor];
29941
30286
  if (beforeDot !== void 0 && /[A-Za-z0-9_]/.test(beforeDot)) {
29942
- const precedingWord = /[A-Za-z_][A-Za-z0-9_]*$/.exec(
29943
- text.slice(0, cursor + 1)
29944
- )?.[0];
30287
+ let start = cursor;
30288
+ while (start > 0 && /[A-Za-z0-9_]/.test(text[start - 1])) start--;
30289
+ const precedingWord = text.slice(start, cursor + 1);
29945
30290
  return precedingWord === "return" || precedingWord === "case";
29946
30291
  }
29947
30292
  return beforeDot === void 0 || !/[A-Za-z0-9_\])}]/.test(beforeDot);
@@ -30002,54 +30347,44 @@ function projectClassBasePositionAt(analysis, document, position) {
30002
30347
  return true;
30003
30348
  }
30004
30349
  function sourceTypeAt(document, position) {
30005
- const contains2 = (type) => {
30006
- for (const argument2 of type.typeArguments) {
30350
+ const contains2 = (type2) => {
30351
+ for (const argument2 of type2.typeArguments) {
30007
30352
  const nested = contains2(argument2);
30008
30353
  if (nested) return nested;
30009
30354
  }
30010
- return rangeContains(type.range, position) ? type : null;
30011
- };
30012
- const parameterType = (parameters) => {
30013
- for (const parameter4 of parameters ?? []) {
30014
- const match = contains2(parameter4.type);
30015
- if (match) return match;
30016
- }
30017
- return null;
30355
+ return rangeContains(type2.range, position) ? type2 : null;
30018
30356
  };
30019
- for (const declaration of document.declarations) {
30020
- if (declaration.kind === "global") {
30021
- const match = contains2(declaration.type);
30022
- if (match) return match;
30023
- continue;
30024
- }
30025
- if (declaration.kind === "enum") continue;
30026
- for (const base of declaration.baseTypes) {
30027
- const match = contains2(base);
30028
- if (match) return match;
30029
- }
30030
- if (declaration.kind === "class") {
30031
- const header = parameterType(declaration.headerParameters);
30032
- if (header) return header;
30033
- for (const generic of declaration.genericParameters) {
30034
- if (!generic.constraint) continue;
30035
- const match = contains2(generic.constraint);
30036
- if (match) return match;
30357
+ let types = SOURCE_TYPES.get(document);
30358
+ if (!types) {
30359
+ types = [];
30360
+ const parameters = (items) => {
30361
+ for (const parameter4 of items ?? []) types.push(parameter4.type);
30362
+ };
30363
+ for (const declaration of document.declarations) {
30364
+ if (declaration.kind === "global") {
30365
+ types.push(declaration.type);
30366
+ continue;
30037
30367
  }
30038
- for (const constructor2 of declaration.constructors) {
30039
- const match = parameterType(constructor2.parameters);
30040
- if (match) return match;
30368
+ if (declaration.kind === "enum") continue;
30369
+ types.push(...declaration.baseTypes);
30370
+ if (declaration.kind === "class") {
30371
+ parameters(declaration.headerParameters);
30372
+ for (const generic of declaration.genericParameters) {
30373
+ if (generic.constraint) types.push(generic.constraint);
30374
+ }
30375
+ for (const constructor2 of declaration.constructors)
30376
+ parameters(constructor2.parameters);
30041
30377
  }
30042
- }
30043
- for (const member of declaration.members) {
30044
- const memberType2 = contains2(member.type);
30045
- if (memberType2) return memberType2;
30046
- if (member.kind === "function") {
30047
- const match = parameterType(member.parameters);
30048
- if (match) return match;
30378
+ for (const member of declaration.members) {
30379
+ types.push(member.type);
30380
+ if (member.kind === "function") parameters(member.parameters);
30049
30381
  }
30050
30382
  }
30383
+ types.sort((a, b) => positionCompare2(a.range.end, b.range.end));
30384
+ SOURCE_TYPES.set(document, types);
30051
30385
  }
30052
- return null;
30386
+ const type = rangeItemAt(types, position);
30387
+ return type ? contains2(type) : null;
30053
30388
  }
30054
30389
  function projectMemberCompletions(analysis, document, position) {
30055
30390
  const owner = projectMemberCompletionOwnerToken(document, position);
@@ -30426,6 +30761,7 @@ function projectCompletionKind(symbol) {
30426
30761
  case "constructor":
30427
30762
  return "method";
30428
30763
  case "parameter":
30764
+ case "local":
30429
30765
  case "flowBinding":
30430
30766
  case "graphChild":
30431
30767
  case "global":
@@ -30667,7 +31003,7 @@ function requiredDestinationEdit(sourceDocument, document, initializer, destinat
30667
31003
  };
30668
31004
  }
30669
31005
  if (initializer.body.kind !== "block") return null;
30670
- const source = new SourceText(document.text);
31006
+ const source = projectSourceText(document);
30671
31007
  const start = source.offsetAt(initializer.body.range.start);
30672
31008
  const end = source.offsetAt(initializer.body.range.end);
30673
31009
  const tokens = sourceDocument.syntax.tokens;
@@ -30746,9 +31082,7 @@ function projectSignatureHelp(analysis, document, position) {
30746
31082
  };
30747
31083
  }
30748
31084
  function projectInlayHints(analysis, document, range2) {
30749
- const bodyContexts = analysis.bodyContexts.filter(
30750
- (body) => body.uri === document.uri
30751
- );
31085
+ const bodyContexts = projectBodies(analysis, document.uri);
30752
31086
  const tokens = projectDocumentTokens(analysis, document).filter(
30753
31087
  (token) => token.kind !== "comment" && token.kind !== "eof"
30754
31088
  );
@@ -30757,19 +31091,28 @@ function projectInlayHints(analysis, document, range2) {
30757
31091
  document
30758
31092
  );
30759
31093
  const hints = [];
31094
+ const delimiterEnds = projectDelimiterEnds(tokens);
30760
31095
  for (let index = 0; index < tokens.length - 1; index++) {
30761
31096
  const callee = tokens[index];
30762
31097
  const open = tokens[index + 1];
30763
31098
  if (!callee || callee.kind !== "identifier" && callee.kind !== "type") {
30764
31099
  continue;
30765
31100
  }
30766
- if (bodyContexts.some((body) => rangeContains(body.range, callee.range.start))) {
31101
+ if (rangeItemAt(bodyContexts, callee.range.start)) {
30767
31102
  continue;
30768
31103
  }
30769
31104
  if (!open || open.text !== "(") continue;
30770
31105
  const beforeCallee = tokens[index - 1];
30771
31106
  if (beforeCallee?.text === "@") continue;
30772
31107
  if (declarationNames.has(positionKey(callee.range.start))) continue;
31108
+ if (range2 && positionCompare2(callee.range.start, range2.end) > 0) break;
31109
+ const callArguments2 = projectCallArguments(
31110
+ tokens,
31111
+ index + 1,
31112
+ delimiterEnds
31113
+ );
31114
+ if (range2 && !callArguments2.some((argument2) => rangeContains(range2, argument2.start)))
31115
+ continue;
30773
31116
  const parameterNames = projectCallParameterNames(
30774
31117
  analysis,
30775
31118
  document,
@@ -30777,7 +31120,6 @@ function projectInlayHints(analysis, document, range2) {
30777
31120
  beforeCallee
30778
31121
  );
30779
31122
  if (!parameterNames) continue;
30780
- const callArguments2 = projectCallArguments(tokens, index + 1);
30781
31123
  for (let argument2 = 0; argument2 < callArguments2.length; argument2++) {
30782
31124
  const argumentToken = callArguments2[argument2];
30783
31125
  if (!argumentToken || argumentToken.named) continue;
@@ -30796,6 +31138,8 @@ function projectInlayHints(analysis, document, range2) {
30796
31138
  }
30797
31139
  }
30798
31140
  for (const body of bodyContexts) {
31141
+ if (range2 && (positionCompare2(body.range.end, range2.start) < 0 || positionCompare2(body.range.start, range2.end) > 0))
31142
+ continue;
30799
31143
  const snapshot = projectBodySnapshot(analysis, document, body);
30800
31144
  hints.push(
30801
31145
  ...inlayHints(snapshot, range2).filter(
@@ -30840,19 +31184,31 @@ function projectCallParameterNames(analysis, document, callee, beforeCallee) {
30840
31184
  return names.size === 1 && name ? name : null;
30841
31185
  });
30842
31186
  }
30843
- function projectCallArguments(tokens, openIndex) {
31187
+ function projectDelimiterEnds(tokens) {
31188
+ const ends = /* @__PURE__ */ new Map();
31189
+ const stack = [];
31190
+ for (let index = 0; index < tokens.length; index++) {
31191
+ const text = tokens[index].text;
31192
+ if (text === "(" || text === "[" || text === "{") stack.push(index);
31193
+ else if (text === ")" || text === "]" || text === "}") {
31194
+ const open = stack.pop();
31195
+ if (open !== void 0) ends.set(open, index);
31196
+ }
31197
+ }
31198
+ return ends;
31199
+ }
31200
+ function projectCallArguments(tokens, openIndex, delimiterEnds) {
30844
31201
  const result = [];
30845
- let depth = 0;
30846
31202
  let atArgumentStart = true;
30847
31203
  for (let index = openIndex + 1; index < tokens.length; index++) {
30848
31204
  const token = tokens[index];
30849
31205
  if (!token) break;
30850
- if (depth === 0 && token.text === ")") break;
30851
- if (depth === 0 && token.text === ",") {
31206
+ if (token.text === ")") break;
31207
+ if (token.text === ",") {
30852
31208
  atArgumentStart = true;
30853
31209
  continue;
30854
31210
  }
30855
- if (depth === 0 && atArgumentStart) {
31211
+ if (atArgumentStart) {
30856
31212
  result.push({
30857
31213
  start: token.range.start,
30858
31214
  named: token.kind === "identifier" && tokens[index + 1]?.text === ":",
@@ -30861,9 +31217,7 @@ function projectCallArguments(tokens, openIndex) {
30861
31217
  atArgumentStart = false;
30862
31218
  }
30863
31219
  if (token.text === "(" || token.text === "[" || token.text === "{") {
30864
- depth++;
30865
- } else if (token.text === ")" || token.text === "]" || token.text === "}") {
30866
- if (depth > 0) depth--;
31220
+ index = delimiterEnds.get(index) ?? tokens.length;
30867
31221
  }
30868
31222
  }
30869
31223
  return result;
@@ -30978,6 +31332,7 @@ function projectSemanticTokenType(symbol) {
30978
31332
  return symbol.callable ? "method" : "property";
30979
31333
  case "constructor":
30980
31334
  return "method";
31335
+ case "local":
30981
31336
  case "graphChild":
30982
31337
  case "flowBinding":
30983
31338
  case "global":
@@ -30988,9 +31343,13 @@ function symbolRangeKey(range2) {
30988
31343
  return `${range2.start.line}:${range2.start.character}-${range2.end.line}:${range2.end.character}`;
30989
31344
  }
30990
31345
  function buildProjectSymbolIndex(symbols) {
31346
+ const cached = PROJECT_SYMBOL_INDEX_CACHE.get(symbols);
31347
+ if (cached) return cached;
30991
31348
  const byLocation = /* @__PURE__ */ new Map();
30992
31349
  const byName = /* @__PURE__ */ new Map();
31350
+ const byId = /* @__PURE__ */ new Map();
30993
31351
  for (const symbol of symbols) {
31352
+ if (symbol.id !== null) byId.set(symbol.id, symbol);
30994
31353
  let uriBucket = byLocation.get(symbol.location.uri);
30995
31354
  if (!uriBucket) {
30996
31355
  uriBucket = /* @__PURE__ */ new Map();
@@ -31004,7 +31363,9 @@ function buildProjectSymbolIndex(symbols) {
31004
31363
  if (named) named.push(symbol);
31005
31364
  else byName.set(symbol.name, [symbol]);
31006
31365
  }
31007
- return { byLocation, byName };
31366
+ const index = { byLocation, byName, byId };
31367
+ PROJECT_SYMBOL_INDEX_CACHE.set(symbols, index);
31368
+ return index;
31008
31369
  }
31009
31370
  function symbolsDeclaredAt(index, uri, range2) {
31010
31371
  return index.byLocation.get(uri)?.get(symbolRangeKey(range2)) ?? [];
@@ -31012,16 +31373,14 @@ function symbolsDeclaredAt(index, uri, range2) {
31012
31373
  function projectSemanticTokens(document, analysis) {
31013
31374
  const index = buildProjectSymbolIndex(analysis.symbols);
31014
31375
  const tokens = projectTokens(document);
31015
- const bodyContexts = analysis.bodyContexts.filter(
31016
- (body) => body.uri === document.uri
31017
- );
31376
+ const bodyContexts = projectBodies(analysis, document.uri);
31018
31377
  const result = tokens.flatMap((token, tokenIndex) => {
31019
31378
  if (token.kind === "eof" || token.kind === "error") return [];
31020
- if (bodyContexts.some((body) => rangeContains(body.range, token.range.start))) {
31379
+ if (rangeItemAt(bodyContexts, token.range.start)) {
31021
31380
  return [];
31022
31381
  }
31023
31382
  const declaration = symbolsDeclaredAt(index, document.uri, token.range)[0];
31024
- const resolved = isProjectNameToken(token) ? indexedProjectSymbolAt(analysis, document, token.range.start, index)?.symbol : void 0;
31383
+ const resolved = !declaration && isProjectNameToken(token) ? indexedProjectSymbolAt(analysis, document, token.range.start, index)?.symbol : void 0;
31025
31384
  let type;
31026
31385
  if (declaration) {
31027
31386
  type = projectSemanticTokenType(declaration);
@@ -31068,32 +31427,40 @@ function isProjectContextualKeyword(tokens, tokenIndex) {
31068
31427
  if (token?.text !== "new") return false;
31069
31428
  return next?.text === "(" || next?.text === "{" || next?.kind === "identifier" || next?.kind === "type";
31070
31429
  }
31071
- function projectSymbolAt(analysis, document, position) {
31072
- return indexedProjectSymbolAt(
31073
- analysis,
31074
- document,
31075
- position,
31076
- buildProjectSymbolIndex(analysis.symbols)
31077
- );
31430
+ function projectSymbolAt(analysis, document, position, index = buildProjectSymbolIndex(analysis.symbols)) {
31431
+ return indexedProjectSymbolAt(analysis, document, position, index);
31078
31432
  }
31079
31433
  function indexedProjectSymbolAt(analysis, document, position, index) {
31080
- const token = projectTokenAt(document, position);
31434
+ const tokenIndex = projectTokenIndexAt(document, position);
31435
+ const sourceTokens = projectTokens(document);
31436
+ const token = sourceTokens[tokenIndex];
31081
31437
  if (!isProjectNameToken(token)) return null;
31082
31438
  const body = projectBodySnapshotAt(analysis, document, position);
31083
31439
  if (body) {
31440
+ const local = localSymbolAtPosition(body, position);
31441
+ if (local?.location) {
31442
+ return {
31443
+ token,
31444
+ symbol: {
31445
+ kind: "local",
31446
+ id: local.id,
31447
+ name: local.name,
31448
+ location: local.location
31449
+ }
31450
+ };
31451
+ }
31084
31452
  const reference2 = referenceAtPosition(body, position);
31085
31453
  const symbolId = reference2 ? resolveReferenceSymbolId(body, reference2) : null;
31086
- const symbol = symbolId ? analysis.symbols.find((candidate) => candidate.id === symbolId) : void 0;
31454
+ const executable = symbolId ? body.project.symbolById.get(symbolId) : void 0;
31455
+ const location3 = executable?.selectionLocation ?? executable?.location;
31456
+ const located = location3 ? symbolsDeclaredAt(index, location3.uri, location3.range)[0] : void 0;
31457
+ const symbol = located ?? (symbolId ? index.byId.get(symbolId) : void 0);
31087
31458
  if (symbol) return { token, symbol };
31088
31459
  }
31089
31460
  const exact = symbolsDeclaredAt(index, document.uri, token.range);
31090
31461
  if (exact.length === 1) return { token, symbol: exact[0] };
31091
31462
  const candidates = index.byName.get(token.text) ?? [];
31092
31463
  if (candidates.length === 0) return null;
31093
- const sourceTokens = projectTokens(document);
31094
- const tokenIndex = sourceTokens.findIndex(
31095
- (candidate) => candidate.start === token.start && candidate.end === token.end
31096
- );
31097
31464
  const source = analysis.documents.get(document.uri);
31098
31465
  const typePosition = source && sourceTypeAt(source, token.range.start);
31099
31466
  if (typePosition || sourceTokens[tokenIndex - 1]?.text === "new") {
@@ -31265,6 +31632,7 @@ function projectSymbolTypeName(symbol) {
31265
31632
  return symbol.name;
31266
31633
  case "member":
31267
31634
  case "parameter":
31635
+ case "local":
31268
31636
  case "flowBinding":
31269
31637
  case "global":
31270
31638
  return symbol.detail ?? null;
@@ -31281,13 +31649,6 @@ function declarationContaining(document, position) {
31281
31649
  (declaration) => positionCompare2(declaration.range.start, position) <= 0 && positionCompare2(position, declaration.range.end) <= 0
31282
31650
  );
31283
31651
  }
31284
- function sameProjectSymbol(left, right) {
31285
- if (left.kind !== right.kind) return false;
31286
- if (left.id && right.id) {
31287
- return left.id === right.id && (left.kind !== "enumOption" || left.ownerName === right.ownerName);
31288
- }
31289
- return left.location.uri === right.location.uri && rangesEqual(left.location.range, right.location.range);
31290
- }
31291
31652
  function tokensBeforePosition(tokens, position) {
31292
31653
  return tokens.filter(
31293
31654
  (token) => token.kind !== "comment" && token.kind !== "eof" && positionCompare2(token.range.start, position) < 0
@@ -31381,31 +31742,67 @@ function contextualConstructorType(analysis, document, position) {
31381
31742
  function initializerRootAt(analysis, document, position) {
31382
31743
  const source = analysis.documents.get(document.uri);
31383
31744
  if (!source) return null;
31384
- for (const declaration of source.declarations) {
31385
- if (declaration.kind === "global") {
31386
- if (rangeContains(declaration.initializer.range, position)) {
31387
- return {
31745
+ let roots = INITIALIZER_ROOTS.get(source);
31746
+ if (!roots) {
31747
+ roots = [];
31748
+ for (const declaration of source.declarations) {
31749
+ if (declaration.kind === "global") {
31750
+ roots.push({
31388
31751
  type: declaration.type,
31389
31752
  range: declaration.initializer.range
31390
- };
31753
+ });
31754
+ continue;
31391
31755
  }
31392
- continue;
31393
- }
31394
- if (declaration.kind === "enum") continue;
31395
- for (const member of declaration.members) {
31396
- if (member.kind === "field" && member.initializer && rangeContains(member.initializer.range, position)) {
31397
- return { type: member.type, range: member.initializer.range };
31756
+ if (declaration.kind === "enum") continue;
31757
+ for (const member of declaration.members) {
31758
+ if (member.kind === "field" && member.initializer) {
31759
+ roots.push({ type: member.type, range: member.initializer.range });
31760
+ }
31398
31761
  }
31399
31762
  }
31763
+ roots.sort((a, b) => positionCompare2(a.range.end, b.range.end));
31764
+ INITIALIZER_ROOTS.set(source, roots);
31400
31765
  }
31401
- return null;
31766
+ return rangeItemAt(roots, position) ?? null;
31402
31767
  }
31403
31768
  function constructionSiteAt(analysis, document, position) {
31404
31769
  const root = initializerRootAt(analysis, document, position);
31405
31770
  if (!root) return null;
31406
- const tokens = projectTokens(document).filter(
31407
- (token) => token.kind !== "comment" && token.kind !== "eof" && rangeContains(root.range, token.range.start) && positionCompare2(token.range.start, position) < 0
31408
- );
31771
+ let documents = CONSTRUCTION_SITE_CACHE.get(analysis);
31772
+ if (!documents) {
31773
+ documents = /* @__PURE__ */ new WeakMap();
31774
+ CONSTRUCTION_SITE_CACHE.set(analysis, documents);
31775
+ }
31776
+ let roots = documents.get(document);
31777
+ if (!roots) {
31778
+ roots = /* @__PURE__ */ new Map();
31779
+ documents.set(document, roots);
31780
+ }
31781
+ const key = positionKey(root.range.start);
31782
+ let sites = roots.get(key);
31783
+ if (!sites) {
31784
+ sites = indexConstructionSites(analysis, document, root);
31785
+ roots.set(key, sites);
31786
+ }
31787
+ let low = 0;
31788
+ let high = sites.length;
31789
+ while (low < high) {
31790
+ const middle = low + high >>> 1;
31791
+ if (positionCompare2(sites[middle].position, position) < 0)
31792
+ low = middle + 1;
31793
+ else high = middle;
31794
+ }
31795
+ return sites[Math.max(0, low - 1)].site;
31796
+ }
31797
+ function indexConstructionSites(analysis, document, root) {
31798
+ const allTokens = projectTokens(document);
31799
+ let start = Math.max(0, projectTokenIndexAt(document, root.range.start));
31800
+ while (start < allTokens.length && positionCompare2(allTokens[start].range.start, root.range.start) < 0)
31801
+ start++;
31802
+ let end = start;
31803
+ while (end < allTokens.length && positionCompare2(allTokens[end].range.start, root.range.end) <= 0)
31804
+ end++;
31805
+ const tokens = allTokens.slice(start, end).filter((token) => token.kind !== "comment" && token.kind !== "eof");
31409
31806
  const stack = [];
31410
31807
  const expectedType = () => {
31411
31808
  const frame = stack.at(-1);
@@ -31422,11 +31819,38 @@ function constructionSiteAt(analysis, document, position) {
31422
31819
  return null;
31423
31820
  }
31424
31821
  };
31822
+ const objects = [];
31823
+ const snapshot = () => {
31824
+ const expected = expectedType();
31825
+ const innermost = stack.at(-1);
31826
+ const directly = innermost?.kind === "object" ? innermost : null;
31827
+ const keys = directly?.keys ?? [];
31828
+ const keyCount = keys.length;
31829
+ return {
31830
+ expectedType: expected,
31831
+ expectedTypeName: expected?.name ?? null,
31832
+ enclosingTypeName: objects.at(-1)?.typeName ?? null,
31833
+ initializerTypeName: directly?.typeName ?? null,
31834
+ get initializerKeys() {
31835
+ return keys.slice(0, keyCount);
31836
+ },
31837
+ atInitializerKey: directly?.atKey ?? false
31838
+ };
31839
+ };
31840
+ const sites = [];
31841
+ let previousPosition = root.range.start;
31842
+ const pop = () => {
31843
+ const frame = stack.pop();
31844
+ if (frame?.kind === "object") objects.pop();
31845
+ return frame;
31846
+ };
31425
31847
  let sawNew = false;
31426
31848
  let pendingNewTypeName = null;
31427
31849
  let closedConstructorTypeName = null;
31428
31850
  for (let index = 0; index < tokens.length; index++) {
31851
+ sites.push({ position: previousPosition, site: snapshot() });
31429
31852
  const token = tokens[index];
31853
+ previousPosition = token.range.start;
31430
31854
  if (token.text === "@") {
31431
31855
  if ((tokens[index + 1]?.kind === "identifier" || tokens[index + 1]?.kind === "keyword") && tokens[index + 2]?.text === "(") {
31432
31856
  let depth = 0;
@@ -31460,13 +31884,15 @@ function constructionSiteAt(analysis, document, position) {
31460
31884
  }
31461
31885
  if (token.text === "{") {
31462
31886
  if (sawNew || closedConstructorTypeName !== null) {
31463
- stack.push({
31887
+ const frame2 = {
31464
31888
  kind: "object",
31465
31889
  typeName: pendingNewTypeName ?? closedConstructorTypeName,
31466
31890
  slotType: null,
31467
31891
  keys: [],
31468
31892
  atKey: true
31469
- });
31893
+ };
31894
+ stack.push(frame2);
31895
+ objects.push(frame2);
31470
31896
  sawNew = false;
31471
31897
  pendingNewTypeName = null;
31472
31898
  } else {
@@ -31509,12 +31935,12 @@ function constructionSiteAt(analysis, document, position) {
31509
31935
  continue;
31510
31936
  }
31511
31937
  if (token.text === ")") {
31512
- const frame2 = stack.pop();
31938
+ const frame2 = pop();
31513
31939
  closedConstructorTypeName = frame2?.kind === "call" ? frame2.typeName : null;
31514
31940
  continue;
31515
31941
  }
31516
31942
  if (token.text === "}" || token.text === "]") {
31517
- stack.pop();
31943
+ pop();
31518
31944
  closedConstructorTypeName = null;
31519
31945
  continue;
31520
31946
  }
@@ -31545,19 +31971,8 @@ function constructionSiteAt(analysis, document, position) {
31545
31971
  }
31546
31972
  }
31547
31973
  }
31548
- const enclosing = [...stack].reverse().find(
31549
- (frame) => frame.kind === "object"
31550
- );
31551
- const innermost = stack.at(-1);
31552
- const directly = innermost?.kind === "object" ? innermost : null;
31553
- return {
31554
- expectedType: expectedType(),
31555
- expectedTypeName: expectedType()?.name ?? null,
31556
- enclosingTypeName: enclosing?.typeName ?? null,
31557
- initializerTypeName: directly?.typeName ?? null,
31558
- initializerKeys: directly?.keys ?? [],
31559
- atInitializerKey: directly?.atKey ?? false
31560
- };
31974
+ sites.push({ position: previousPosition, site: snapshot() });
31975
+ return sites;
31561
31976
  }
31562
31977
  function collectionElementType(type) {
31563
31978
  if (!type) return null;
@@ -31572,13 +31987,31 @@ function constructorArgumentType(analysis, frame) {
31572
31987
  if (!declaration || declaration.kind === "global" || declaration.kind === "enum") {
31573
31988
  return null;
31574
31989
  }
31575
- const fields = declaration.members.filter(
31576
- (member) => member.kind === "field" && !member.modifiers.includes("static") && !member.modifiers.includes("readonly")
31577
- );
31990
+ const fields = declarationFields(declaration);
31578
31991
  if (frame.argumentName !== null) {
31579
- return fields.find((field) => field.name === frame.argumentName)?.type ?? null;
31992
+ return fields.assignableByName.get(frame.argumentName)?.type ?? null;
31580
31993
  }
31581
- return fields[frame.argumentIndex]?.type ?? null;
31994
+ return fields.assignable[frame.argumentIndex]?.type ?? null;
31995
+ }
31996
+ function declarationFields(declaration) {
31997
+ let fields = DECLARATION_FIELDS.get(declaration);
31998
+ if (!fields) {
31999
+ const byName = /* @__PURE__ */ new Map();
32000
+ const assignable = [];
32001
+ const assignableByName = /* @__PURE__ */ new Map();
32002
+ for (const member of declaration.members) {
32003
+ if (member.kind !== "field") continue;
32004
+ if (!byName.has(member.name)) byName.set(member.name, member);
32005
+ if (member.modifiers.includes("static") || member.modifiers.includes("readonly"))
32006
+ continue;
32007
+ assignable.push(member);
32008
+ if (!assignableByName.has(member.name))
32009
+ assignableByName.set(member.name, member);
32010
+ }
32011
+ fields = { byName, assignable, assignableByName };
32012
+ DECLARATION_FIELDS.set(declaration, fields);
32013
+ }
32014
+ return fields;
31582
32015
  }
31583
32016
  function declaredFieldType(analysis, typeName, fieldName) {
31584
32017
  let current = typeName;
@@ -31589,9 +32022,7 @@ function declaredFieldType(analysis, typeName, fieldName) {
31589
32022
  if (!declaration || declaration.kind === "global" || declaration.kind === "enum") {
31590
32023
  return null;
31591
32024
  }
31592
- const member = declaration.members.find(
31593
- (candidate) => candidate.kind === "field" && candidate.name === fieldName
31594
- );
32025
+ const member = declarationFields(declaration).byName.get(fieldName);
31595
32026
  if (member) return member.type;
31596
32027
  current = declaration.kind === "class" ? resolvableBaseClassName(analysis, declaration) : null;
31597
32028
  }
@@ -31605,13 +32036,19 @@ function resolvableBaseClassName(analysis, declaration) {
31605
32036
  return null;
31606
32037
  }
31607
32038
  function findTypeDeclaration(analysis, name) {
31608
- for (const document of analysis.documents.values()) {
31609
- const declaration = document.declarations.find(
31610
- (candidate) => candidate.kind !== "global" && candidate.name === name
31611
- );
31612
- if (declaration) return declaration;
32039
+ let declarations = PROJECT_TYPE_DECLARATIONS.get(analysis);
32040
+ if (!declarations) {
32041
+ declarations = /* @__PURE__ */ new Map();
32042
+ for (const document of analysis.documents.values()) {
32043
+ for (const declaration of document.declarations) {
32044
+ if (declaration.kind !== "global" && !declarations.has(declaration.name)) {
32045
+ declarations.set(declaration.name, declaration);
32046
+ }
32047
+ }
32048
+ }
32049
+ PROJECT_TYPE_DECLARATIONS.set(analysis, declarations);
31613
32050
  }
31614
- return void 0;
32051
+ return declarations.get(name);
31615
32052
  }
31616
32053
  function constructorParameterLabel(parameter4) {
31617
32054
  return `${parameter4.type} ${parameter4.name}${parameterDefaultSuffix2(parameter4)}`;
@@ -31728,21 +32165,27 @@ function graphConstructorParameters(typeName) {
31728
32165
  }
31729
32166
  }
31730
32167
  function projectFunctionsNamed(analysis, name) {
31731
- const result = [];
31732
- for (const document of analysis.documents.values()) {
31733
- for (const declaration of document.declarations) {
31734
- if (declaration.kind === "global" || declaration.kind === "enum")
31735
- continue;
31736
- for (const member of declaration.members) {
31737
- if (member.kind === "function" && member.name === name) {
31738
- result.push({ owner: declaration.name, member });
32168
+ let byName = PROJECT_FUNCTIONS.get(analysis);
32169
+ if (!byName) {
32170
+ byName = /* @__PURE__ */ new Map();
32171
+ for (const document of analysis.documents.values()) {
32172
+ for (const declaration of document.declarations) {
32173
+ if (declaration.kind === "global" || declaration.kind === "enum")
32174
+ continue;
32175
+ for (const member of declaration.members) {
32176
+ if (member.kind !== "function") continue;
32177
+ const entry = { owner: declaration.name, member };
32178
+ const named = byName.get(member.name);
32179
+ if (named) named.push(entry);
32180
+ else byName.set(member.name, [entry]);
31739
32181
  }
31740
32182
  }
31741
32183
  }
32184
+ PROJECT_FUNCTIONS.set(analysis, byName);
31742
32185
  }
31743
- return result;
32186
+ return byName.get(name) ?? [];
31744
32187
  }
31745
- var PROJECT_BODY_SNAPSHOT_CACHE, CONSTRUCTION_INDEX_CACHE, VARIANT_SCOPE_INDEX_CACHE;
32188
+ var PROJECT_BODIES, PROJECT_BODY_INDEX_CACHE, PROJECT_BODY_SNAPSHOT_CACHE, SOURCE_TYPES, CONSTRUCTION_INDEX_CACHE, VARIANT_SCOPE_INDEX_CACHE, PROJECT_SYMBOL_INDEX_CACHE, INITIALIZER_ROOTS, CONSTRUCTION_SITE_CACHE, DECLARATION_FIELDS, PROJECT_TYPE_DECLARATIONS, PROJECT_FUNCTIONS;
31746
32189
  var init_project_source_language_features = __esm({
31747
32190
  "../packages/neoscript-language/src/project-source-language-features.ts"() {
31748
32191
  "use strict";
@@ -31756,11 +32199,19 @@ var init_project_source_language_features = __esm({
31756
32199
  init_project_source_settlement();
31757
32200
  init_project_source_tokens();
31758
32201
  init_quick_fixes();
31759
- init_source_text();
31760
32202
  init_syntax();
32203
+ PROJECT_BODIES = /* @__PURE__ */ new WeakMap();
32204
+ PROJECT_BODY_INDEX_CACHE = /* @__PURE__ */ new WeakMap();
31761
32205
  PROJECT_BODY_SNAPSHOT_CACHE = /* @__PURE__ */ new WeakMap();
32206
+ SOURCE_TYPES = /* @__PURE__ */ new WeakMap();
31762
32207
  CONSTRUCTION_INDEX_CACHE = /* @__PURE__ */ new WeakMap();
31763
32208
  VARIANT_SCOPE_INDEX_CACHE = /* @__PURE__ */ new WeakMap();
32209
+ PROJECT_SYMBOL_INDEX_CACHE = /* @__PURE__ */ new WeakMap();
32210
+ INITIALIZER_ROOTS = /* @__PURE__ */ new WeakMap();
32211
+ CONSTRUCTION_SITE_CACHE = /* @__PURE__ */ new WeakMap();
32212
+ DECLARATION_FIELDS = /* @__PURE__ */ new WeakMap();
32213
+ PROJECT_TYPE_DECLARATIONS = /* @__PURE__ */ new WeakMap();
32214
+ PROJECT_FUNCTIONS = /* @__PURE__ */ new WeakMap();
31764
32215
  }
31765
32216
  });
31766
32217
 
@@ -34967,6 +35418,57 @@ var init_project_local_value_refactorings = __esm({
34967
35418
  }
34968
35419
  });
34969
35420
 
35421
+ // ../packages/neoscript-language/src/project-source-reference-family.ts
35422
+ function projectReferenceFamily(analysis, target, index = buildProjectSymbolIndex(analysis.symbols)) {
35423
+ const named = index.byName.get(target.name) ?? [];
35424
+ if (target.kind === "class" || target.kind === "constructor") {
35425
+ const owner = target.kind === "class" ? target.name : target.ownerName;
35426
+ return named.filter(
35427
+ (symbol) => symbol.kind === "class" ? symbol.name === owner : symbol.kind === "constructor" && symbol.ownerName === owner
35428
+ );
35429
+ }
35430
+ if (target.kind !== "member" || !target.ownerName) return [target];
35431
+ const symbols = new Map(
35432
+ named.filter((symbol) => symbol.kind === "member").map((symbol) => [symbol.ownerName, symbol])
35433
+ );
35434
+ const classes = /* @__PURE__ */ new Map();
35435
+ for (const document of analysis.documents.values()) {
35436
+ for (const declaration of document.declarations) {
35437
+ if (declaration.kind === "class")
35438
+ classes.set(declaration.name, declaration);
35439
+ }
35440
+ }
35441
+ const roots = /* @__PURE__ */ new Map();
35442
+ const root = (name) => {
35443
+ if (roots.has(name)) return roots.get(name);
35444
+ roots.set(name, null);
35445
+ const declaration = classes.get(name);
35446
+ const own = symbols.get(name);
35447
+ const member = declaration?.members.find(
35448
+ (candidate) => candidate.name === target.name
35449
+ );
35450
+ if (own && !member?.modifiers.includes("override")) {
35451
+ roots.set(name, own);
35452
+ return own;
35453
+ }
35454
+ const base = declaration?.baseTypes.find((type) => classes.has(type.name));
35455
+ const resolved = (base ? root(base.name) : null) ?? own ?? null;
35456
+ roots.set(name, resolved);
35457
+ return resolved;
35458
+ };
35459
+ const selected = root(target.ownerName);
35460
+ if (!selected) return [target];
35461
+ return [...symbols.values()].filter(
35462
+ (symbol) => symbol.ownerName && root(symbol.ownerName) === selected
35463
+ );
35464
+ }
35465
+ var init_project_source_reference_family = __esm({
35466
+ "../packages/neoscript-language/src/project-source-reference-family.ts"() {
35467
+ "use strict";
35468
+ init_project_source_language_features();
35469
+ }
35470
+ });
35471
+
34970
35472
  // ../packages/neoscript-language/src/spelling-diagnostics.ts
34971
35473
  function enhanceSpellingDiagnostics(document, diagnostics, completionAt) {
34972
35474
  return diagnostics.map((diagnostic) => {
@@ -35198,6 +35700,16 @@ function sameLocationInList(target, locations) {
35198
35700
  (location3) => location3.uri === target.uri && location3.range.start.line === target.range.start.line && location3.range.start.character === target.range.start.character
35199
35701
  );
35200
35702
  }
35703
+ function locationKey(location3) {
35704
+ const { start, end } = location3.range;
35705
+ return JSON.stringify([
35706
+ location3.uri,
35707
+ start.line,
35708
+ start.character,
35709
+ end.line,
35710
+ end.character
35711
+ ]);
35712
+ }
35201
35713
  var LanguageService;
35202
35714
  var init_service = __esm({
35203
35715
  "../packages/neoscript-language/src/service.ts"() {
@@ -35210,6 +35722,7 @@ var init_service = __esm({
35210
35722
  init_project_source_identifiers();
35211
35723
  init_project_source_analysis();
35212
35724
  init_project_source_construction_quick_fixes();
35725
+ init_project_source_reference_family();
35213
35726
  init_project_source_language_features();
35214
35727
  init_project_source_tokens();
35215
35728
  init_quick_fixes();
@@ -35392,6 +35905,16 @@ var init_service = __esm({
35392
35905
  if (!reference2) return [];
35393
35906
  const symbolId = resolveReferenceSymbolId(source, reference2);
35394
35907
  if (!symbolId) return [];
35908
+ for (const location3 of definition(source, position)) {
35909
+ const owner = this.documents.get(location3.uri);
35910
+ if (owner && isProjectDocument(owner.document, owner.context)) {
35911
+ return this.projectReferences(
35912
+ owner.document,
35913
+ location3.range.start,
35914
+ includeDeclaration
35915
+ );
35916
+ }
35917
+ }
35395
35918
  const locations = [];
35396
35919
  for (const other of this.documents.values()) {
35397
35920
  if (isProjectDocument(other.document, other.context)) continue;
@@ -35445,8 +35968,10 @@ var init_service = __esm({
35445
35968
  );
35446
35969
  if (!resolved) return null;
35447
35970
  const memberLabel2 = resolved.symbol.kind === "member" || resolved.symbol.kind === "enumOption" || resolved.symbol.kind === "graphChild";
35448
- const binding = resolved.symbol.kind === "parameter" || resolved.symbol.kind === "genericParameter" || resolved.symbol.kind === "flowBinding";
35449
- const validNeoName = memberLabel2 ? isValidNeoProjectMemberIdentifier(newName) : binding ? isValidNeoProjectBindingIdentifier(newName) : isValidNeoProjectIdentifier(newName);
35971
+ const binding = resolved.symbol.kind === "local" || resolved.symbol.kind === "parameter" || resolved.symbol.kind === "genericParameter" || resolved.symbol.kind === "flowBinding";
35972
+ let validNeoName = memberLabel2 ? isValidNeoProjectMemberIdentifier(newName) : binding ? isValidNeoProjectBindingIdentifier(newName) : isValidNeoProjectIdentifier(newName);
35973
+ if (resolved.symbol.kind === "local")
35974
+ validNeoName = isValidNeoScriptBindingIdentifier(newName);
35450
35975
  validName = validNeoName && isGeneratedCSharpIdentifier(newName) && !isGeneratedCSharpReservedKeyword(newName);
35451
35976
  } else {
35452
35977
  validName = isValidNeoScriptBindingIdentifier(newName);
@@ -35555,12 +36080,22 @@ var init_service = __esm({
35555
36080
  const token = projectTokenAt(document, position);
35556
36081
  if (!isProjectNameToken(token)) return [];
35557
36082
  const analysis = this.projectAnalysis();
35558
- const resolved = projectSymbolAt(analysis, document, position);
36083
+ const index = buildProjectSymbolIndex(analysis.symbols);
36084
+ const resolved = projectSymbolAt(analysis, document, position, index);
35559
36085
  if (!resolved) return [];
35560
36086
  const declaration = resolved.symbol;
36087
+ const family = projectReferenceFamily(analysis, declaration, index);
36088
+ const familyLocations = new Set(
36089
+ family.map((member) => locationKey(member.location))
36090
+ );
36091
+ const familyIds = new Set(
36092
+ family.flatMap((member) => member.id === null ? [] : [member.id])
36093
+ );
35561
36094
  const locations = [];
35562
36095
  for (const state of this.documents.values()) {
35563
36096
  if (!isProjectDocument(state.document, state.context)) continue;
36097
+ if (declaration.kind === "local" && state.document.uri !== document.uri)
36098
+ continue;
35564
36099
  for (const candidate of projectTokens(state.document)) {
35565
36100
  if (!isProjectNameToken(candidate) || candidate.text !== token.text) {
35566
36101
  continue;
@@ -35568,28 +36103,29 @@ var init_service = __esm({
35568
36103
  const candidateSymbol = projectSymbolAt(
35569
36104
  analysis,
35570
36105
  state.document,
35571
- candidate.range.start
36106
+ candidate.range.start,
36107
+ index
35572
36108
  )?.symbol;
35573
- if (!candidateSymbol || !sameProjectSymbol(candidateSymbol, declaration)) {
36109
+ if (!candidateSymbol || !familyLocations.has(locationKey(candidateSymbol.location))) {
35574
36110
  continue;
35575
36111
  }
35576
36112
  const location3 = { uri: state.document.uri, range: candidate.range };
35577
- if (!includeDeclaration && location3.uri === declaration.location.uri && rangesEqual(location3.range, declaration.location.range)) {
36113
+ if (!includeDeclaration && familyLocations.has(locationKey(location3))) {
35578
36114
  continue;
35579
36115
  }
35580
36116
  locations.push(location3);
35581
36117
  }
35582
36118
  }
35583
36119
  for (const state of this.documents.values()) {
35584
- if (state.context.projectSourceKind !== "spec") continue;
36120
+ if (declaration.kind === "local") break;
36121
+ if (isProjectDocument(state.document, state.context)) continue;
35585
36122
  const snapshot = this.scriptSnapshot(state);
35586
36123
  for (const candidate of snapshot.parsed.references) {
35587
36124
  if (candidate.name !== declaration.name) continue;
35588
36125
  const symbolId = resolveReferenceSymbolId(snapshot, candidate);
35589
36126
  if (symbolId === null) continue;
35590
- const matches = declaration.id !== null ? symbolId === declaration.id : sameLocationInList(
35591
- declaration.location,
35592
- definition(snapshot, candidate.range.start)
36127
+ const matches = familyIds.has(symbolId) || definition(snapshot, candidate.range.start).some(
36128
+ (location3) => familyLocations.has(locationKey(location3))
35593
36129
  );
35594
36130
  if (!matches) continue;
35595
36131
  locations.push({ uri: state.document.uri, range: candidate.range });
@@ -50349,6 +50885,41 @@ var init_interface_graph = __esm({
50349
50885
  });
50350
50886
 
50351
50887
  // ../src/models/neoscript/type-info-compatibility.ts
50888
+ function isTypeInfoAssignable(from, to, context) {
50889
+ if (to.type === NS_TYPE_UNKNOWN) return true;
50890
+ if (from.type === NS_TYPE_UNKNOWN) return false;
50891
+ if (from.required === false && to.required === true) return false;
50892
+ if (from.type === 0 /* Null */) return to.required === false;
50893
+ if (from.type === 2 /* Int */ && to.type === 4 /* Float */) {
50894
+ return true;
50895
+ }
50896
+ if (from.type === 7 /* Class */ && to.type === 7 /* Class */) {
50897
+ if (from.classId === to.classId) {
50898
+ return typeInfosInvariantlyEqual({ ...from, required: to.required }, to);
50899
+ }
50900
+ return resolveInheritanceChain(from.classId, context.classes).some(
50901
+ (schemaClass2) => schemaClass2.id === to.classId
50902
+ );
50903
+ }
50904
+ if (from.type === 7 /* Class */ && to.type === 22 /* Interface */) {
50905
+ return classImplementsInterface(
50906
+ from.classId,
50907
+ to.interfaceId,
50908
+ context.classes,
50909
+ context.interfaces
50910
+ );
50911
+ }
50912
+ if (from.type === 22 /* Interface */ && to.type === 22 /* Interface */) {
50913
+ return resolveInterfaceClosure(from.interfaceId, context.interfaces).some(
50914
+ (neoInterface) => neoInterface.id === to.interfaceId
50915
+ );
50916
+ }
50917
+ if (from.type === 22 /* Interface */ || to.type === 22 /* Interface */) {
50918
+ return false;
50919
+ }
50920
+ const normalizedFrom = { ...from, required: to.required };
50921
+ return typeInfosInvariantlyEqual(normalizedFrom, to);
50922
+ }
50352
50923
  var init_type_info_compatibility = __esm({
50353
50924
  "../src/models/neoscript/type-info-compatibility.ts"() {
50354
50925
  "use strict";
@@ -62802,7 +63373,9 @@ function lowerInterface(context, declaration) {
62802
63373
  name: member.name,
62803
63374
  ...member.docsText === void 0 ? {} : { docsText: member.docsText },
62804
63375
  type: lowerType(context, member.type),
62805
- settable: /\bset\s*;/.test(member.body ?? ""),
63376
+ settable: projectSourceAccessors(member.body ?? "").some(
63377
+ (accessor) => accessor.keyword === "set"
63378
+ ),
62806
63379
  accessModifier,
62807
63380
  source
62808
63381
  };
@@ -64204,7 +64777,8 @@ function propertyAccessors(body) {
64204
64777
  const accessor = findProjectSourceAccessorBody(body, keyword);
64205
64778
  if (accessor === null) return null;
64206
64779
  const source = normalizeBodySource(accessor.source);
64207
- if (accessor.form === "block") return source;
64780
+ if (accessor.form === "block")
64781
+ return keyword === "set" && source.length === 0 ? "return;" : source;
64208
64782
  return keyword === "get" ? `return ${source};` : `${source};`;
64209
64783
  };
64210
64784
  return {
@@ -111977,8 +112551,11 @@ function validateDocumentInheritanceFlags(ctx) {
111977
112551
  )) {
111978
112552
  continue;
111979
112553
  }
112554
+ const declaringClass = classesById.get(
112555
+ inheritedMetadata.declarationClassId
112556
+ );
111980
112557
  errors.push({
111981
- message: `Schema member "${schemaKey}" on class "${schemaClass2.name}" (${schemaClass2.id}) does not match the inherited generated signature from member "${generatedAncestorMember.name}" (${generatedAncestorMember.id}). Abstract implementations and re-abstractions must preserve the generated member contract or have a supported codegen bridge.`
112558
+ message: `Schema member "${schemaKey}" on class "${schemaClass2.name}" (${schemaClass2.id}) does not match the inherited generated signature from member "${generatedAncestorMember.name}" (${generatedAncestorMember.id}). ${schemaClass2.name}.${schemaKey} generates "${ctx.signatureProjector.format(localSignature)}" but ${declaringClass?.name ?? inheritedMetadata.memberId}.${schemaKey} declares "${ctx.signatureProjector.format(ancestorSignature)}". Abstract implementations and re-abstractions must preserve the generated member contract or have a supported codegen bridge.`
111982
112559
  });
111983
112560
  }
111984
112561
  }
@@ -112166,6 +112743,7 @@ function createGenerationContext(exportData, diagnostics, docs = EMPTY_UNITY_COD
112166
112743
  docs,
112167
112744
  members: Object.values(exportData.members),
112168
112745
  classes: Object.values(exportData.classes),
112746
+ interfaces: Object.values(exportData.interfaces),
112169
112747
  diagnostics,
112170
112748
  enumNames: buildEnumNameMap(Object.values(exportData.enums), diagnostics),
112171
112749
  enumOptionFieldNames: /* @__PURE__ */ new Map(),
@@ -112401,29 +112979,35 @@ function emittedGeneratedMemberContract(ctx, ownerClassId, schemaKey, memberId,
112401
112979
  valueType: narrowedMemberValueType(ctx, member, valueType, inherited),
112402
112980
  hasSetter,
112403
112981
  getOnlyContract: (inherited?.getOnlyContract ?? false) || isAbstractMember(member) === true && !hasSetter,
112404
- valueClassId: generatedValueClassId(ctx, member),
112982
+ valueTypeInfo: generatedValueTypeInfo(ctx, member),
112405
112983
  closesGenericType: (inherited?.closesGenericType ?? false) || lookupNarrowsThroughGenericParam(member) || isMemberGenericBase(member)
112406
112984
  };
112407
112985
  }
112408
112986
  function suppressesInheritedSetter(contract) {
112409
112987
  return contract?.getOnlyContract === true;
112410
112988
  }
112411
- function generatedValueClassId(ctx, member) {
112412
- if (isMemberClassBase(member)) return member.classId;
112413
- const nsReturnTypeInfo = nsPropertyReturnTypeInfo(member);
112414
- if (nsReturnTypeInfo !== null) {
112415
- return nsReturnTypeInfo.type === 7 /* Class */ ? nsReturnTypeInfo.classId : null;
112989
+ function generatedValueTypeInfo(ctx, member) {
112990
+ const required2 = isRequiredMember2(member);
112991
+ if (isMemberClassBase(member)) {
112992
+ return { type: 7 /* Class */, classId: member.classId, required: required2 };
112416
112993
  }
112994
+ const returnType = nsPropertyReturnTypeInfo(member);
112995
+ if (returnType !== null) return returnType;
112417
112996
  if (isMemberLookupBase(member)) {
112418
- const declaredTypeInfo = lookupDeclaredTypeInfo2(member);
112419
- if (declaredTypeInfo !== null) {
112420
- return declaredTypeInfo.type === 7 /* Class */ ? declaredTypeInfo.classId : null;
112997
+ const declaredType = lookupDeclaredTypeInfo2(member);
112998
+ if (declaredType !== null) return declaredType;
112999
+ const entry = resolveLookupEntryMember(ctx, member);
113000
+ if (entry === null) return null;
113001
+ if (isMemberClassBase(entry)) {
113002
+ return { type: 7 /* Class */, classId: entry.classId, required: required2 };
112421
113003
  }
112422
- const entryMember = resolveLookupEntryMember(ctx, member);
112423
- return entryMember !== null && isMemberClassBase(entryMember) ? entryMember.classId : null;
112424
113004
  }
112425
113005
  return null;
112426
113006
  }
113007
+ function generatedValueClassId(ctx, member) {
113008
+ const typeInfo = generatedValueTypeInfo(ctx, member);
113009
+ return typeInfo?.type === 7 /* Class */ ? typeInfo.classId : null;
113010
+ }
112427
113011
  function narrowedMemberValueType(ctx, member, rawValueType, contract) {
112428
113012
  if (contract === null) return rawValueType;
112429
113013
  if (contract.valueType === rawValueType) return rawValueType;
@@ -112439,13 +113023,18 @@ function narrowedMemberValueType(ctx, member, rawValueType, contract) {
112439
113023
  )) {
112440
113024
  return contract.valueType;
112441
113025
  }
112442
- if (contract.valueClassId === null) return rawValueType;
112443
- const valueClassId = generatedValueClassId(ctx, member);
112444
- if (valueClassId === null) return rawValueType;
112445
- const narrows = resolveInheritanceChainForEmission(ctx, valueClassId).some(
112446
- (ancestor) => ancestor.id === contract.valueClassId
112447
- );
112448
- return narrows ? contract.valueType : rawValueType;
113026
+ const inheritedType = contract.valueTypeInfo;
113027
+ if (inheritedType === null) return rawValueType;
113028
+ if (inheritedType.type !== 7 /* Class */ && inheritedType.type !== 22 /* Interface */)
113029
+ return rawValueType;
113030
+ const valueType = generatedValueTypeInfo(ctx, member);
113031
+ if (valueType === null) return rawValueType;
113032
+ const sourceType = valueType.type === 7 /* Class */ && inheritedType.type === 7 /* Class */ && valueType.required === false && inheritedType.required === true ? { ...valueType, required: true } : valueType;
113033
+ try {
113034
+ return isTypeInfoAssignable(sourceType, inheritedType, ctx) ? contract.valueType : rawValueType;
113035
+ } catch {
113036
+ return rawValueType;
113037
+ }
112449
113038
  }
112450
113039
  function storedCollectionImplementsContract(ctx, member, rawValueType, contractType2) {
112451
113040
  const contract = contractType2.replace(/\?$/, "");
@@ -112629,6 +113218,13 @@ function createGeneratedInstanceMemberSignatureProjector(exportData) {
112629
113218
  equals: generatedInstanceMemberSignaturesEqual
112630
113219
  };
112631
113220
  }
113221
+ function formatGeneratedInstanceMemberSignature(signature) {
113222
+ if (signature.kind === "property") {
113223
+ const accessors = signature.hasSetter ? "get; set;" : "get;";
113224
+ return `${signature.valueType} ${signature.name} { ${accessors} }`;
113225
+ }
113226
+ return `${signature.returnType} ${signature.name}(${signature.parameterTypes.join(", ")})`;
113227
+ }
112632
113228
  function generatedInstanceMemberSignaturesEqual(left, right) {
112633
113229
  if (left.kind !== right.kind) return false;
112634
113230
  if (left.name !== right.name) return false;
@@ -114236,6 +114832,7 @@ var init_generate_unity_classes = __esm({
114236
114832
  init_sdk_runtime_enums();
114237
114833
  init_members();
114238
114834
  init_classes();
114835
+ init_type_info_compatibility();
114239
114836
  DEFAULT_NAMESPACE = "Assets.Scripts.Neo";
114240
114837
  isNSPropertyContractDeclaration = isMemberNSPropertyContractBase;
114241
114838
  SMART_TILE_CONTRACT_WORLD_KINDS = /* @__PURE__ */ new Set([
@@ -114324,6 +114921,7 @@ function assertProjectDocumentInheritanceFlagsValid(document) {
114324
114921
  PROJECT_ROOTS.map((root) => document.project[root.projectField])
114325
114922
  ),
114326
114923
  signatureProjector: {
114924
+ format: formatGeneratedInstanceMemberSignature,
114327
114925
  resolveLocal: (classId, schemaKey) => getGeneratedSignatures().projectLocal(classId, schemaKey),
114328
114926
  resolveInherited: (classId, schemaKey) => getGeneratedSignatures().projectInherited(classId, schemaKey),
114329
114927
  resolveInheritedMetadata: (classId, schemaKey) => {
@@ -114333,6 +114931,7 @@ function assertProjectDocumentInheritanceFlagsValid(document) {
114333
114931
  );
114334
114932
  return signature === null ? null : {
114335
114933
  memberId: signature.memberId,
114934
+ declarationClassId: signature.declarationClassId,
114336
114935
  isAbstract: signature.isAbstract
114337
114936
  };
114338
114937
  },
@@ -134246,7 +134845,7 @@ var init_project_document_cache = __esm({
134246
134845
  init_src();
134247
134846
  init_project_documents();
134248
134847
  PROJECT_SOURCE_BUILD_CACHE_PATH = ".neo/build/project-source-analysis-v4.json";
134249
- PROJECT_SOURCE_BUILD_CACHE_REVISION = 2;
134848
+ PROJECT_SOURCE_BUILD_CACHE_REVISION = 4;
134250
134849
  }
134251
134850
  });
134252
134851
 
@@ -146399,7 +146998,7 @@ var init_registry2 = __esm({
146399
146998
  "../packages/manifest/schema-contract/registry.mjs"() {
146400
146999
  "use strict";
146401
147000
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
146402
- cliVersion: "0.61.4",
147001
+ cliVersion: "0.62.0",
146403
147002
  projectFileUploadBatchSize: 32,
146404
147003
  documentRecords: {
146405
147004
  member: {
@@ -155079,7 +155678,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
155079
155678
  async function main() {
155080
155679
  const args = parseArgs(process.argv.slice(2));
155081
155680
  if (args.command === "--version") {
155082
- console.log("0.61.4");
155681
+ console.log("0.62.0");
155083
155682
  return;
155084
155683
  }
155085
155684
  if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {