@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.
- package/lib/attributes.d.ts +3 -0
- package/lib/attributes.js +36 -0
- package/lib/checker/index.d.ts +8 -18
- package/lib/checker/index.js +244 -349
- package/lib/checker/mcdocUtil.d.ts +0 -5
- package/lib/checker/mcdocUtil.js +11 -80
- package/lib/colorizer/index.js +1 -0
- package/lib/completer/index.d.ts +3 -0
- package/lib/completer/index.js +160 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +11 -1
- package/lib/node/index.d.ts +53 -16
- package/lib/node/index.js +39 -12
- package/lib/parser/collection.js +4 -4
- package/lib/parser/compound.js +2 -5
- package/lib/parser/entry.js +4 -1
- package/lib/parser/path.js +16 -28
- package/lib/parser/primitive.d.ts +3 -1
- package/lib/parser/primitive.js +5 -15
- package/package.json +4 -4
package/lib/checker/index.js
CHANGED
|
@@ -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 {
|
|
5
|
-
import {
|
|
6
|
-
|
|
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
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
27
|
+
const typeDef = {
|
|
28
|
+
kind: 'dispatcher',
|
|
29
|
+
registry,
|
|
30
|
+
parallelIndices: getIndices(id),
|
|
31
|
+
};
|
|
24
32
|
return (node, ctx) => {
|
|
25
|
-
|
|
33
|
+
typeDefinition(typeDef, options)(node, ctx);
|
|
26
34
|
};
|
|
27
35
|
}
|
|
28
36
|
}
|
|
29
|
-
function
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
|
51
|
+
export function typeDefinition(typeDef, options = {}) {
|
|
45
52
|
return (node, ctx) => {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
//
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
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
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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
|
-
|
|
325
|
-
|
|
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
|
-
|
|
337
|
-
|
|
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
|
-
|
|
356
|
-
|
|
357
|
-
|
|
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
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
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
|
-
|
|
392
|
-
|
|
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
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
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
|