@spyglassmc/nbt 0.3.8 → 0.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,18 @@
1
1
  import * as core from '@spyglassmc/core';
2
2
  import { localeQuote, localize } from '@spyglassmc/locales';
3
3
  import * as mcdoc from '@spyglassmc/mcdoc';
4
- import { NbtListNode } from '../node/index.js';
5
- import { localizeTag } from '../util.js';
6
- import { getBlocksFromItem, getEntityFromItem, getSpecialStringParser, } from './mcdocUtil.js';
4
+ import { NbtCompoundNode, NbtNode, NbtPathFilterNode, NbtPathIndexNode, NbtPathKeyNode, NbtPrimitiveNode, NbtStringNode, } from '../node/index.js';
5
+ import { getBlocksFromItem, getEntityFromItem } from './mcdocUtil.js';
6
+ const typed = (node, ctx) => {
7
+ typeDefinition(node.targetType)(node.children[0], ctx);
8
+ };
9
+ export function register(meta) {
10
+ meta.registerChecker('nbt:typed', typed);
11
+ }
12
+ /**
13
+ * @param id If the registry is under the `custom` namespace, `id` can only be a string. Otherwise it can be a string, string array, or `undefined`.
14
+ * If set to `undefined` or an empty array, all mcdoc compound definitions for this registry will be merged for checking, and unknown keys are allowed.
15
+ */
7
16
  export function index(registry, id, options = {}) {
8
17
  switch (registry) {
9
18
  case 'custom:blockitemstates':
@@ -13,65 +22,170 @@ export function index(registry, id, options = {}) {
13
22
  return blockStates([id], options);
14
23
  case 'custom:spawnitemtag':
15
24
  const entityId = getEntityFromItem(id);
16
- return entityId
17
- ? index('entity_type', entityId, options)
18
- : core.checker.noop;
25
+ return entityId ? index('minecraft:entity', entityId, options) : core.checker.noop;
19
26
  default:
20
- const identifier = getRegistryIdentifier(registry);
21
- if (!identifier) {
22
- return core.checker.noop;
23
- }
27
+ const typeDef = {
28
+ kind: 'dispatcher',
29
+ registry,
30
+ parallelIndices: getIndices(id),
31
+ };
24
32
  return (node, ctx) => {
25
- definition(identifier, options)(node, ctx);
33
+ typeDefinition(typeDef, options)(node, ctx);
26
34
  };
27
35
  }
28
36
  }
29
- function getRegistryIdentifier(registry) {
30
- switch (registry) {
31
- case 'block':
32
- return '::java::server::world::block::BlockEntity';
33
- case 'entity_type':
34
- return '::java::server::world::entity::AnyEntity';
35
- case 'item':
36
- return '::java::server::world::item::AnyItem';
37
- default:
38
- return undefined;
37
+ function getIndices(id) {
38
+ if (typeof id === 'string') {
39
+ return [{ kind: 'static', value: id }];
40
+ }
41
+ else if (id === undefined || id.length === 0) {
42
+ return [{ kind: 'static', value: '%fallback' }];
43
+ }
44
+ else {
45
+ return id.map(i => ({ kind: 'static', value: i }));
39
46
  }
40
47
  }
41
48
  /**
42
49
  * @param identifier An identifier of mcdoc compound definition. e.g. `::minecraft::util::invitem::InventoryItem`
43
50
  */
44
- export function definition(identifier, options = {}) {
51
+ export function typeDefinition(typeDef, options = {}) {
45
52
  return (node, ctx) => {
46
- const symbol = ctx.symbols.query(ctx.doc, 'mcdoc', identifier);
47
- const typeDef = symbol.getData(mcdoc.binder.TypeDefSymbolData.is)?.typeDef;
48
- if (!typeDef) {
49
- return;
50
- }
51
- switch (typeDef.kind) {
52
- case 'struct':
53
- compound(typeDef, options)(node, ctx);
54
- break;
55
- default:
56
- ctx.logger.error(`[nbt.checker.definition] Expected a struct type, but got ${typeDef.kind}`);
57
- }
53
+ mcdoc.runtime.checker.typeDefinition([{ originalNode: node, inferredType: inferType(node) }], typeDef, mcdoc.runtime.checker.McdocCheckerContext.create(ctx, {
54
+ allowMissingKeys: options.isPredicate || options.isMerge,
55
+ requireCanonical: options.isPredicate,
56
+ isEquivalent: (inferred, def) => {
57
+ if (def.kind === 'boolean') {
58
+ // TODO: this should check whether the value is 0 or 1
59
+ return inferred.kind === 'byte';
60
+ }
61
+ if (inferred.kind === 'list') {
62
+ return def.kind === 'list' || def.kind === 'tuple';
63
+ }
64
+ if (options.isPredicate) {
65
+ return inferred.kind === def.kind;
66
+ }
67
+ switch (inferred.kind) {
68
+ case 'struct':
69
+ return def.kind === 'struct';
70
+ case 'byte':
71
+ case 'short':
72
+ case 'int':
73
+ case 'long':
74
+ return ['byte', 'short', 'int', 'long', 'float', 'double'].includes(def.kind);
75
+ case 'float':
76
+ case 'double':
77
+ return ['float', 'double'].includes(def.kind);
78
+ default:
79
+ return false;
80
+ }
81
+ },
82
+ getChildren: node => {
83
+ const { type } = node;
84
+ if (type === 'nbt:list' || type === 'nbt:byte_array'
85
+ || type === 'nbt:int_array' || type === 'nbt:long_array') {
86
+ return node.children.filter(n => n.value).map(n => [{ originalNode: n.value, inferredType: inferType(n.value) }]);
87
+ }
88
+ if (type === 'nbt:compound') {
89
+ return node.children.filter(kvp => kvp.key).map(kvp => ({
90
+ key: { originalNode: kvp.key, inferredType: inferType(kvp.key) },
91
+ possibleValues: kvp.value
92
+ ? [{ originalNode: kvp.value, inferredType: inferType(kvp.value) }]
93
+ : [],
94
+ }));
95
+ }
96
+ return [];
97
+ },
98
+ reportError: mcdoc.runtime.checker.getDefaultErrorReporter(ctx, (mcdoc.runtime.checker.getDefaultErrorRange)),
99
+ attachTypeInfo: (node, definition, desc = '') => {
100
+ node.typeDef = definition;
101
+ node.requireCanonical = options.isPredicate;
102
+ // TODO: improve hover info
103
+ if (node.parent && core.PairNode?.is(node.parent)
104
+ && NbtNode.is(node.parent.key)
105
+ && NbtNode.is(node.parent.value)) {
106
+ if (node.parent.key?.typeDef && node.parent.value?.typeDef) {
107
+ const valueString = mcdoc.McdocType.toString(node.parent.value.typeDef);
108
+ let keyString = mcdoc.McdocType.toString(node.parent.key.typeDef);
109
+ if (node.parent.key.typeDef.kind !== 'literal') {
110
+ keyString = `[${keyString}]`;
111
+ }
112
+ node.parent.key.hover =
113
+ `\`\`\`typescript\n${keyString}: ${valueString}\n\`\`\`\n${desc}`;
114
+ if (NbtPrimitiveNode.is(node.parent.value)) {
115
+ node.parent.value.hover =
116
+ `\`\`\`typescript\n${valueString}\n\`\`\`\n${desc}`;
117
+ }
118
+ }
119
+ }
120
+ else if (NbtPrimitiveNode.is(node)) {
121
+ node.hover = `\`\`\`typescript\n${mcdoc.McdocType.toString(definition)}\n\`\`\`\n${desc}`;
122
+ }
123
+ },
124
+ stringAttacher: (node, attacher) => {
125
+ if (!NbtStringNode.is(node)) {
126
+ return;
127
+ }
128
+ attacher(node);
129
+ if (node.children) {
130
+ core.AstNode.setParents(node);
131
+ // Because the runtime checker happens after binding, we need to manually call this
132
+ core.binder.fallbackSync(node, ctx);
133
+ core.checker.fallbackSync(node, ctx);
134
+ }
135
+ },
136
+ }));
58
137
  };
59
138
  }
139
+ function inferType(node) {
140
+ switch (node.type) {
141
+ case 'nbt:byte':
142
+ return { kind: 'literal', value: { kind: 'byte', value: node.value } };
143
+ case 'nbt:double':
144
+ return { kind: 'literal', value: { kind: 'double', value: node.value } };
145
+ case 'nbt:float':
146
+ return { kind: 'literal', value: { kind: 'float', value: node.value } };
147
+ case 'nbt:long':
148
+ return {
149
+ kind: 'literal',
150
+ // TODO: this should NOT change type from `bigint` to `number`
151
+ value: { kind: 'long', value: Number(node.value) },
152
+ };
153
+ case 'nbt:int':
154
+ return { kind: 'literal', value: { kind: 'int', value: node.value } };
155
+ case 'nbt:short':
156
+ return { kind: 'literal', value: { kind: 'short', value: node.value } };
157
+ case 'nbt:string':
158
+ return { kind: 'literal', value: { kind: 'string', value: node.value } };
159
+ case 'nbt:list':
160
+ return { kind: 'list', item: { kind: 'any' } };
161
+ case 'nbt:compound':
162
+ return { kind: 'struct', fields: [] };
163
+ case 'nbt:byte_array':
164
+ return { kind: 'byte_array' };
165
+ case 'nbt:long_array':
166
+ return { kind: 'long_array' };
167
+ case 'nbt:int_array':
168
+ return { kind: 'int_array' };
169
+ }
170
+ }
60
171
  export function blockStates(blocks, _options = {}) {
61
172
  return (node, ctx) => {
173
+ if (!NbtCompoundNode.is(node)) {
174
+ return;
175
+ }
62
176
  const states = core.getStates('block', blocks, ctx);
63
177
  for (const { key: keyNode, value: valueNode } of node.children) {
64
178
  if (!keyNode || !valueNode) {
65
179
  continue;
66
180
  }
67
181
  // Type check.
68
- if (valueNode.type === 'nbt:byte' &&
69
- (ctx.src.slice(valueNode.range).toLowerCase() === 'false' ||
70
- ctx.src.slice(valueNode.range).toLowerCase() === 'true')) {
182
+ if (valueNode.type === 'nbt:byte'
183
+ && (ctx.src.slice(valueNode.range).toLowerCase() === 'false'
184
+ || ctx.src.slice(valueNode.range).toLowerCase() === 'true')) {
71
185
  ctx.err.report(localize('nbt.checker.block-states.fake-boolean'), valueNode, 2 /* core.ErrorSeverity.Warning */);
72
186
  continue;
73
187
  }
74
- else if (valueNode.type !== 'string' && valueNode.type !== 'nbt:int') {
188
+ else if (valueNode.type !== 'nbt:string' && valueNode.type !== 'nbt:int') {
75
189
  ctx.err.report(localize('nbt.checker.block-states.unexpected-value-type'), valueNode, 2 /* core.ErrorSeverity.Warning */);
76
190
  continue;
77
191
  }
@@ -89,330 +203,111 @@ export function blockStates(blocks, _options = {}) {
89
203
  }
90
204
  };
91
205
  }
92
- export function compound(typeDef, options = {}) {
93
- return (node, ctx) => {
94
- for (const { key: keyNode, value: valueNode } of node.children) {
95
- if (!keyNode || !valueNode) {
96
- continue;
97
- }
98
- const key = keyNode.value;
99
- // TODO: handle spread types
100
- const fieldDef = typeDef.fields.find((p) => p.kind === 'pair' && p.key === key);
101
- if (fieldDef) {
102
- // TODO: enter a reference to the mcdoc key
103
- fieldValue(fieldDef.type, options)(valueNode, ctx);
104
- }
105
- else if (!options.allowUnknownKey) {
106
- ctx.err.report(localize('unknown-key', localeQuote(key)), keyNode, 2 /* core.ErrorSeverity.Warning */);
107
- }
108
- }
109
- // TODO: check for required fields
110
- // requires an update to vanilla-mcdoc to make most fields optional
111
- };
112
- }
113
- export function enum_(path, _options = {}) {
114
- if (!path) {
115
- return core.checker.noop;
116
- }
117
- return (node, ctx) => {
118
- // const query = ctx.symbols.query(ctx.doc, path.category, ...path.path)
119
- // const data = query.symbol?.data as mcdoc.EnumNode.SymbolData | undefined
120
- // // Check type.
121
- // if (data?.enumKind && node.type !== data.enumKind && node.type !== `nbt:${data.enumKind}`) {
122
- // ctx.err.report(localize('expected', localize(`nbt.node.${data.enumKind}`)), node, core.ErrorSeverity.Warning)
123
- // }
124
- // // Get all enum members.
125
- // const enumMembers: Record<string, string> = {}
126
- // query.forEachMember((name, memberQuery) => {
127
- // const value = (memberQuery.symbol?.data as mcdoc.EnumFieldNode.SymbolData | undefined)?.value
128
- // if (value !== undefined) {
129
- // enumMembers[name] = value.toString()
130
- // }
131
- // })
132
- // // Check value.
133
- // if (!Object.values(enumMembers).includes(node.value.toString())) {
134
- // ctx.err.report(localize('expected',
135
- // Object.entries(enumMembers).map(([k, v]) => `${k} = ${v}`)
136
- // ), node, core.ErrorSeverity.Warning)
137
- // }
138
- };
139
- }
140
- /**
141
- * @param id If set to `undefined` or an empty array, all mcdoc compound definitions for this registry will be merged for checking, and unknown keys are allowed.
142
- */
206
+ // TODO: check nbt index nodes and nbt compound nodes
143
207
  export function path(registry, id) {
144
208
  return (node, ctx) => {
145
- // const resolveResult = resolveRootRegistry(registry, id, ctx, undefined)
146
- // let targetType: mcdoc.McdocType | undefined = {
147
- // kind: 'dispatcher',
148
- // registry,
149
- // index: ((): mcdoc.DispatcherData['index'] => {
150
- // if (id === undefined) {
151
- // return { kind: 'static', value: { keyword: '()' } }
152
- // } else if (typeof id === 'string') {
153
- // return { kind: 'static', value: id }
154
- // } else {
155
- // return id.map(v => ({ kind: 'static', value: v }))
156
- // }
157
- // })(),
158
- // }
159
- // const options: Options = { allowUnknownKey: resolveResult.allowUnknownKey, isPredicate: true }
160
- // let currentCompound: NbtCompoundNode | undefined
161
- // for (const child of node.children) {
162
- // if (NbtCompoundNode.is(child)) {
163
- // // Compound filter.
164
- // currentCompound = child
165
- // if (data?.type === 'union') {
166
- // }
167
- // if (data?.type === 'resolved_compound') {
168
- // compound(data.data, options)(child, ctx)
169
- // } else {
170
- // ctx.err.report(localize('nbt.checker.path.unexpected-filter'), child, core.ErrorSeverity.Warning)
171
- // }
172
- // } else if (core.StringNode.is(child)) {
173
- // // Key.
174
- // if (data?.type === 'union') {
175
- // }
176
- // if (data?.type === 'resolved_compound') {
177
- // const fieldData: ResolvedCompoundData[string] = data.data[child.value]
178
- // if (fieldData) {
179
- // fieldData.query.enter({ usage: { type: 'reference', node: child } })
180
- // if (fieldData.data.type === 'byte_array' || fieldData.data.type === 'int_array' || fieldData.data.type === 'long_array' || fieldData.data.type === 'list' || fieldData.data.type === 'union') {
181
- // data = fieldData.data
182
- // } else {
183
- // const resolveResult = resolveSymbolData(fieldData.data, ctx, currentCompound)
184
- // if (resolveResult.value) {
185
- // options.allowUnknownKey ||= resolveResult.allowUnknownKey
186
- // data.data = resolveResult.value
187
- // } else {
188
- // data = undefined
189
- // }
190
- // }
191
- // targetType = fieldData.data
192
- // } else {
193
- // if (!options.allowUnknownKey) {
194
- // ctx.err.report(localize('unknown-key', localeQuote(child.value)), child, core.ErrorSeverity.Warning)
195
- // }
196
- // targetType = undefined
197
- // break
198
- // }
199
- // } else {
200
- // ctx.err.report(localize('nbt.checker.path.unexpected-key'), child, core.ErrorSeverity.Warning)
201
- // targetType = undefined
202
- // break
203
- // }
204
- // currentCompound = undefined
205
- // } else {
206
- // // Index.
207
- // if (data?.type === 'byte_array' || data?.type === 'int_array' || data?.type === 'long_array' || data?.type === 'list') {
208
- // // Check content.
209
- // if (child.children !== undefined) {
210
- // const [content] = child.children
211
- // if (content.type === 'integer') {
212
- // const absIndex = content.value < 0 ? -1 - content.value : content.value
213
- // const [, maxLength] = data.lengthRange ?? [undefined, undefined]
214
- // if (maxLength !== undefined && absIndex >= maxLength) {
215
- // ctx.err.report(localize('nbt.checker.path.index-out-of-bound', content.value, maxLength), content, core.ErrorSeverity.Warning)
216
- // }
217
- // } else {
218
- // let isUnexpectedFilter = true
219
- // if (data.type === 'list') {
220
- // const { allowUnknownKey, value } = resolveSymbolData(data.item, ctx, currentCompound)
221
- // options.allowUnknownKey ||= allowUnknownKey
222
- // if (value) {
223
- // isUnexpectedFilter = false
224
- // compound(value, options)(content, ctx)
225
- // }
226
- // }
227
- // if (isUnexpectedFilter) {
228
- // ctx.err.report(localize('nbt.checker.path.unexpected-filter'), content, core.ErrorSeverity.Warning)
229
- // targetType = undefined
230
- // break
231
- // }
232
- // currentCompound = content
233
- // }
234
- // }
235
- // // Set data for the next iteration.
236
- // if (data.type === 'list') {
237
- // const { allowUnknownKey, value } = resolveSymbolData(data.item, ctx, currentCompound)
238
- // options.allowUnknownKey ||= allowUnknownKey
239
- // targetType = data.item
240
- // if (value) {
241
- // data = { type: 'resolved_compound', data: value }
242
- // } else {
243
- // data = undefined
244
- // }
245
- // } else {
246
- // targetType = {
247
- // type: data.type.split('_')[0] as 'byte' | 'int' | 'long',
248
- // valueRange: data.valueRange,
249
- // }
250
- // data = undefined
251
- // }
252
- // } else {
253
- // ctx.err.report(localize('nbt.checker.path.unexpected-index'), child, core.ErrorSeverity.Warning)
254
- // targetType = undefined
255
- // break
256
- // }
257
- // }
258
- // }
259
- // ctx.ops.set(node, 'targetType', targetType)
260
- };
261
- }
262
- export function fieldValue(type, options) {
263
- const isInRange = (value, { kind, min = -Infinity, max = Infinity }) => {
264
- const comparator = (a, b, exclusive) => exclusive ? a < b : a <= b;
265
- return (comparator(min, value, kind & 0b10) &&
266
- comparator(value, max, kind & 0b01));
267
- };
268
- const ExpectedTypes = {
269
- boolean: 'nbt:byte',
270
- byte: 'nbt:byte',
271
- byte_array: 'nbt:byte_array',
272
- double: 'nbt:double',
273
- float: 'nbt:float',
274
- int: 'nbt:int',
275
- int_array: 'nbt:int_array',
276
- list: 'nbt:list',
277
- long: 'nbt:long',
278
- long_array: 'nbt:long_array',
279
- short: 'nbt:short',
280
- string: 'string',
281
- struct: 'nbt:compound',
282
- tuple: 'nbt:list',
283
- };
284
- return (node, ctx) => {
285
- // Rough type check.
286
- if (type.kind !== 'any' &&
287
- type.kind !== 'dispatcher' &&
288
- type.kind !== 'enum' &&
289
- type.kind !== 'literal' &&
290
- type.kind !== 'reference' &&
291
- type.kind !== 'union' &&
292
- type.kind !== 'attributed' &&
293
- type.kind !== 'unsafe' &&
294
- type.kind !== 'concrete' &&
295
- type.kind !== 'indexed' &&
296
- type.kind !== 'template' &&
297
- node.type !== ExpectedTypes[type.kind]) {
298
- ctx.err.report(localize('expected', localizeTag(ExpectedTypes[type.kind])), node, 2 /* core.ErrorSeverity.Warning */);
299
- return;
209
+ // TODO: support dispatcher
210
+ const typeDef = {
211
+ kind: 'dispatcher',
212
+ registry,
213
+ parallelIndices: getIndices(id),
214
+ };
215
+ // Create a linked list representation
216
+ const leaf = { type: 'leaf', range: core.Range.create(node.range.end) };
217
+ let link = { path: node, node: leaf };
218
+ for (let i = node.children.length - 1; i >= 0; i -= 1) {
219
+ link = { path: node, node: node.children[i], next: link };
300
220
  }
301
- switch (type.kind) {
302
- case 'boolean':
303
- node = node;
304
- if (node.value !== 0 && node.value !== 1) {
305
- ctx.err.report(localize('nbt.checker.boolean.out-of-range', localeQuote('0b'), localeQuote('1b')), node, 2 /* core.ErrorSeverity.Warning */);
306
- }
307
- break;
308
- case 'byte_array':
309
- case 'int_array':
310
- case 'long_array':
311
- node = node;
312
- if (type.lengthRange &&
313
- !isInRange(node.children.length, type.lengthRange)) {
314
- ctx.err.report(localize('expected', localize('nbt.checker.collection.length-between', localizeTag(node.type), type.lengthRange.min ?? '-∞', type.lengthRange.max ?? '+∞')), node, 2 /* core.ErrorSeverity.Warning */);
221
+ let prev = link;
222
+ while (prev.next) {
223
+ prev.next.prev = prev;
224
+ prev = prev.next;
225
+ }
226
+ mcdoc.runtime.checker.typeDefinition([{ originalNode: link, inferredType: inferPath(link) }], typeDef, mcdoc.runtime.checker.McdocCheckerContext.create(ctx, {
227
+ allowMissingKeys: true,
228
+ requireCanonical: true,
229
+ isEquivalent: (inferred, def) => {
230
+ switch (inferred.kind) {
231
+ case 'list':
232
+ case 'byte_array':
233
+ case 'int_array':
234
+ case 'long_array':
235
+ return ['list', 'tuple', 'byte_array', 'int_array', 'long_array'].includes(def.kind);
236
+ default:
237
+ return false;
315
238
  }
316
- if (type.valueRange) {
317
- for (const { value: childNode } of node.children) {
318
- if (childNode &&
319
- !isInRange(Number(childNode.value), type.valueRange)) {
320
- ctx.err.report(localize('number.between', type.valueRange.min ?? '-∞', type.valueRange.max ?? '+∞'), node, 2 /* core.ErrorSeverity.Warning */);
321
- }
322
- }
239
+ },
240
+ getChildren: (link) => {
241
+ while (link.next && link.node.type !== 'leaf' && NbtPathFilterNode.is(link.node)) {
242
+ link = link.next;
323
243
  }
324
- break;
325
- case 'byte':
326
- case 'short':
327
- case 'int':
328
- case 'long':
329
- case 'float':
330
- case 'double':
331
- node = node;
332
- if (type.valueRange &&
333
- !isInRange(Number(node.value), type.valueRange)) {
334
- ctx.err.report(localize('number.between', type.valueRange.min ?? '-∞', type.valueRange.max ?? '+∞'), node, 2 /* core.ErrorSeverity.Warning */);
244
+ if (!link.next || link.node.type === 'leaf') {
245
+ return [];
335
246
  }
336
- break;
337
- case 'dispatcher':
338
- node = node;
339
- // const id = resolveFieldPath(node.parent?.parent, type.index.path)
340
- // if (type.index.registry) {
341
- // if (ExtendableRootRegistry.is(type.index.registry)) {
342
- // index(type.index.registry, id ? core.ResourceLocation.lengthen(id) : undefined, options)(node, ctx)
343
- // } else if (id) {
344
- // index(type.index.registry, core.ResourceLocation.lengthen(id), options)(node, ctx)
345
- // }
346
- // }
347
- break;
348
- case 'list':
349
- node = node;
350
- type = mcdoc.simplifyListType(type);
351
- if (type.lengthRange &&
352
- !isInRange(node.children.length, type.lengthRange)) {
353
- ctx.err.report(localize('expected', localize('nbt.checker.collection.length-between', localizeTag(node.type), type.lengthRange.min ?? '-∞', type.lengthRange.max ?? '+∞')), node, 2 /* core.ErrorSeverity.Warning */);
247
+ if (NbtPathIndexNode.is(link.node)) {
248
+ return [[{ originalNode: link.next, inferredType: inferPath(link.next) }]];
354
249
  }
355
- for (const { value: childNode } of node.children) {
356
- if (childNode) {
357
- fieldValue(type.item, options)(childNode, ctx);
358
- }
250
+ if (NbtPathKeyNode.is(link.node)) {
251
+ return [{
252
+ key: {
253
+ originalNode: link,
254
+ inferredType: {
255
+ kind: 'literal',
256
+ value: { kind: 'string', value: link.node.children[0].value },
257
+ },
258
+ },
259
+ possibleValues: [{
260
+ originalNode: link.next,
261
+ inferredType: inferPath(link.next),
262
+ }],
263
+ }];
359
264
  }
360
- break;
361
- case 'string':
362
- node = node;
363
- let suffix = '';
364
- let valueNode = node;
365
- if (core.ItemNode.is(node.parent) &&
366
- NbtListNode.is(node.parent.parent)) {
367
- suffix = '[]';
368
- valueNode = node.parent.parent;
265
+ // Never reachable
266
+ return [];
267
+ },
268
+ reportError: (error) => {
269
+ if (error.kind === 'invalid_collection_length') {
270
+ return;
369
271
  }
370
- if (core.PairNode.is(valueNode.parent)) {
371
- const structMcdocPath = valueNode.parent.key?.symbol
372
- ?.parentSymbol
373
- ?.path.join('::');
374
- const key = valueNode.parent.key?.value;
375
- const path = `${structMcdocPath}.${key}${suffix}`;
376
- const parserName = getSpecialStringParser(path);
377
- if (parserName) {
378
- try {
379
- const parser = ctx.meta.getParser(parserName);
380
- const result = core.parseStringValue(parser, node.value, node.valueMap, ctx);
381
- if (result !== core.Failure) {
382
- node.children = [result];
383
- result.parent = node;
384
- }
385
- }
386
- catch (e) {
387
- ctx.logger.error('[nbt.checker.fieldValue#string]', e);
388
- }
389
- }
272
+ mcdoc.runtime.checker.getDefaultErrorReporter(ctx, ({ originalNode: link }) => link.node.range)(error);
273
+ },
274
+ attachTypeInfo: (link, definition, desc = '') => {
275
+ if (definition.kind === 'literal' && !definition.attributes?.length) {
276
+ return;
390
277
  }
391
- break;
392
- case 'reference':
393
- node = node;
394
- // if (type.symbol) {
395
- // const { allowUnknownKey, value } = resolveSymbolPaths([type.symbol], ctx, node)
396
- // compound(value, { ...options, allowUnknownKey: options.allowUnknownKey || allowUnknownKey })(node, ctx)
397
- // }
398
- break;
399
- case 'union':
400
- type = mcdoc.flattenUnionType(type);
401
- if (type.members.length === 0) {
402
- ctx.err.report(localize('nbt.checker.compound.field.union-empty-members'), core.PairNode.is(node.parent)
403
- ? node.parent.key ?? node.parent
404
- : node, 2 /* core.ErrorSeverity.Warning */);
278
+ if (link.node.type === 'leaf') {
279
+ link.path.endTypeDef = definition;
405
280
  }
406
281
  else {
407
- ;
408
- core.checker.any(type.members.map((t) => fieldValue(t, options)))(node, ctx);
282
+ link.node.typeDef = definition;
409
283
  }
410
- break;
411
- case 'attributed':
412
- // TODO: don't just ignore the attribute
413
- fieldValue(type.child, options)(node, ctx);
414
- break;
415
- }
284
+ // TODO: improve hover info
285
+ if (NbtPathKeyNode.is(link.prev?.node)) {
286
+ link.prev.node.hover = `\`\`\`typescript\n${link.prev.node.children[0].value}: ${mcdoc.McdocType.toString(definition)}\n\`\`\`\n${desc}`;
287
+ }
288
+ },
289
+ stringAttacher: (link, attacher) => {
290
+ if (!NbtPathKeyNode.is(link.node)) {
291
+ return;
292
+ }
293
+ attacher(link.node.children[0]);
294
+ if (link.node.children[0].children) {
295
+ core.AstNode.setParents(link.node.children[0]);
296
+ // Because the runtime checker happens after binding, we need to manually call this
297
+ core.binder.fallbackSync(link.node.children[0], ctx);
298
+ core.checker.fallbackSync(link.node.children[0], ctx);
299
+ }
300
+ },
301
+ }));
416
302
  };
417
303
  }
304
+ function inferPath(link) {
305
+ if (link.node.type === 'leaf') {
306
+ return { kind: 'unsafe' };
307
+ }
308
+ if (NbtPathIndexNode.is(link.node)) {
309
+ return { kind: 'list', item: { kind: 'any' } };
310
+ }
311
+ return { kind: 'struct', fields: [] };
312
+ }
418
313
  //# sourceMappingURL=index.js.map
@@ -1,9 +1,4 @@
1
1
  import type * as core from '@spyglassmc/core';
2
2
  export declare function getBlocksFromItem(item: core.FullResourceLocation): core.FullResourceLocation[] | undefined;
3
3
  export declare function getEntityFromItem(item: core.FullResourceLocation): core.FullResourceLocation | undefined;
4
- export declare function getSpecialStringParser(mcdocPath: string): string | undefined;
5
- /**
6
- * @param mcdocPath Path of the mcdoc compound definition.
7
- */
8
- export declare function isExpandableCompound(mcdocPath: string): boolean;
9
4
  //# sourceMappingURL=mcdocUtil.d.ts.map