@spyglassmc/mcdoc 0.1.1 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/binder/index.d.ts +10 -2
- package/lib/binder/index.js +705 -18
- package/lib/colorizer/index.d.ts +1 -1
- package/lib/colorizer/index.js +8 -14
- package/lib/{binder/util.d.ts → common.d.ts} +4 -1
- package/lib/common.js +11 -0
- package/lib/index.d.ts +9 -6
- package/lib/index.js +14 -42
- package/lib/node/index.d.ts +194 -27
- package/lib/node/index.js +404 -88
- package/lib/parser/index.d.ts +4 -2
- package/lib/parser/index.js +223 -242
- package/lib/type/index.d.ts +70 -41
- package/lib/type/index.js +47 -44
- package/lib/uri_processors.d.ts +4 -0
- package/lib/{binder/uriBinder.js → uri_processors.js} +19 -16
- package/package.json +4 -3
- package/lib/binder/uriBinder.d.ts +0 -3
- package/lib/binder/util.js +0 -16
- package/lib/checker/CheckerContext.d.ts +0 -18
- package/lib/checker/CheckerContext.js +0 -3
- package/lib/checker/entry.d.ts +0 -4
- package/lib/checker/entry.js +0 -358
- package/lib/checker/index.d.ts +0 -2
- package/lib/checker/index.js +0 -18
package/lib/binder/index.js
CHANGED
|
@@ -1,19 +1,706 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
1
|
+
import { AsyncBinder, atArray, Dev, Range, ResourceLocationNode, SymbolUtil, traversePreOrder } from '@spyglassmc/core';
|
|
2
|
+
import { localeQuote, localize } from '@spyglassmc/locales';
|
|
3
|
+
import { AttributeNode, AttributeTreeNamedValuesNode, AttributeTreeNode, AttributeTreePosValuesNode, DispatcherTypeNode, DispatchStatementNode, DocCommentsNode, DynamicIndexNode, EnumBlockNode, EnumFieldNode, EnumInjectionNode, EnumNode, FloatRangeNode, IndexBodyNode, InjectionNode, IntRangeNode, ListTypeNode, LiteralNode, LiteralTypeNode, NumericTypeNode, PathNode, PrimitiveArrayTypeNode, ReferenceTypeNode, StaticIndexNode, StringTypeNode, StructBlockNode, StructMapKeyNode, StructNode, StructPairFieldNode, StructSpreadFieldNode, TopLevelNode, TupleTypeNode, TypeAliasNode, TypeBaseNode, TypedNumberNode, TypeParamBlockNode, TypeParamNode, UnionTypeNode, UseStatementNode } from '../node/index.js';
|
|
4
|
+
const ModuleSymbolData = Object.freeze({
|
|
5
|
+
is(data) {
|
|
6
|
+
return !!data && typeof data === 'object' && typeof data.nextAnonymousIndex === 'number';
|
|
7
|
+
},
|
|
8
|
+
});
|
|
9
|
+
const TypeDefSymbolData = Object.freeze({
|
|
10
|
+
is(data) {
|
|
11
|
+
return !!data && typeof data === 'object' && typeof data.typeDef === 'object';
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
export const fileModule = AsyncBinder.create(async (node, ctx) => {
|
|
15
|
+
const moduleIdentifier = uriToIdentifier(ctx.doc.uri, ctx);
|
|
16
|
+
if (!moduleIdentifier) {
|
|
17
|
+
ctx.err.report(localize('mcdoc.binder.out-of-root', localeQuote(ctx.doc.uri)), Range.Beginning, 0 /* ErrorSeverity.Hint */);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const mcdocCtx = {
|
|
21
|
+
...ctx,
|
|
22
|
+
moduleIdentifier,
|
|
23
|
+
};
|
|
24
|
+
return module_(node, mcdocCtx);
|
|
25
|
+
});
|
|
26
|
+
export async function module_(node, ctx) {
|
|
27
|
+
const data = { nextAnonymousIndex: 0 };
|
|
28
|
+
ctx.symbols
|
|
29
|
+
.query({ doc: ctx.doc, node }, 'mcdoc', ctx.moduleIdentifier)
|
|
30
|
+
.amend({ data: { data } });
|
|
31
|
+
hoist(node, ctx);
|
|
32
|
+
for (const child of node.children) {
|
|
33
|
+
switch (child.type) {
|
|
34
|
+
case 'mcdoc:dispatch_statement':
|
|
35
|
+
await bindDispatchStatement(child, ctx);
|
|
36
|
+
break;
|
|
37
|
+
case 'mcdoc:enum':
|
|
38
|
+
bindEnum(child, ctx);
|
|
39
|
+
break;
|
|
40
|
+
case 'mcdoc:injection':
|
|
41
|
+
await bindInjection(child, ctx);
|
|
42
|
+
break;
|
|
43
|
+
case 'mcdoc:struct':
|
|
44
|
+
await bindStruct(child, ctx);
|
|
45
|
+
break;
|
|
46
|
+
case 'mcdoc:type_alias':
|
|
47
|
+
await bindTypeAlias(child, ctx);
|
|
48
|
+
break;
|
|
49
|
+
case 'mcdoc:use_statement':
|
|
50
|
+
await bindUseStatement(child, ctx);
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Hoist enums, structs, type aliases, and use statements under the module scope.
|
|
57
|
+
*/
|
|
58
|
+
function hoist(node, ctx) {
|
|
59
|
+
traversePreOrder(node, () => true, TopLevelNode.is, child => {
|
|
60
|
+
switch (child.type) {
|
|
61
|
+
case 'mcdoc:enum':
|
|
62
|
+
hoistEnum(child);
|
|
63
|
+
break;
|
|
64
|
+
case 'mcdoc:struct':
|
|
65
|
+
hoistStruct(child);
|
|
66
|
+
break;
|
|
67
|
+
case 'mcdoc:type_alias':
|
|
68
|
+
hoistTypeAlias(child);
|
|
69
|
+
break;
|
|
70
|
+
case 'mcdoc:use_statement':
|
|
71
|
+
hoistUseStatement(child);
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
function hoistEnum(node) {
|
|
76
|
+
hoistFor('enum', node, EnumNode.destruct, n => ({ typeDef: convertEnum(n, ctx) }));
|
|
77
|
+
}
|
|
78
|
+
function hoistStruct(node) {
|
|
79
|
+
hoistFor('struct', node, StructNode.destruct, n => ({ typeDef: convertStruct(n, ctx) }));
|
|
80
|
+
}
|
|
81
|
+
function hoistTypeAlias(node) {
|
|
82
|
+
hoistFor('type_alias', node, TypeAliasNode.destruct, n => {
|
|
83
|
+
const { rhs } = TypeAliasNode.destruct(n);
|
|
84
|
+
if (!rhs) {
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
return { typeDef: convertType(rhs, ctx) };
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
function hoistUseStatement(node) {
|
|
91
|
+
const { binding, path } = UseStatementNode.destruct(node);
|
|
92
|
+
if (!path) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const { lastIdentifier } = PathNode.destruct(path);
|
|
96
|
+
const identifier = binding ?? lastIdentifier;
|
|
97
|
+
if (!identifier) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
// hoistUseStatement associates the AST node with the binding definition in the file symbol table,
|
|
101
|
+
// which may get overridden by bindUseStatement in the later stage as an reference to the imported symbol in the global symbol table.
|
|
102
|
+
// This way when the user tries to go to definition on the path in the use statement,
|
|
103
|
+
// they will go to the definition in the imported file.
|
|
104
|
+
ctx.symbols
|
|
105
|
+
.query({ doc: ctx.doc, node }, 'mcdoc', `${ctx.moduleIdentifier}::${identifier.value}`)
|
|
106
|
+
.ifDeclared(symbol => reportDuplicatedDeclaration(ctx, symbol, identifier))
|
|
107
|
+
.elseEnter({
|
|
108
|
+
data: { subcategory: 'use_statement_binding', visibility: 1 /* SymbolVisibility.File */ },
|
|
109
|
+
usage: { type: 'definition', node: identifier, fullRange: node },
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
function hoistFor(subcategory, node, destructor, getData) {
|
|
113
|
+
const { docComments, identifier, keyword } = destructor(node);
|
|
114
|
+
const name = identifier?.value ?? nextAnonymousIdentifier(node, ctx);
|
|
115
|
+
ctx.symbols
|
|
116
|
+
.query({ doc: ctx.doc, node }, 'mcdoc', `${ctx.moduleIdentifier}::${name}`)
|
|
117
|
+
.ifDeclared(symbol => reportDuplicatedDeclaration(ctx, symbol, identifier ?? node))
|
|
118
|
+
.elseEnter({
|
|
119
|
+
data: { data: getData(node), desc: DocCommentsNode.asText(docComments), subcategory },
|
|
120
|
+
// If the current syntax structure is named, then the identifier node is entered as a definition;
|
|
121
|
+
// otherwise, an anonymous identifier is generated for the symbol and the keyword node is entered as a definition.
|
|
122
|
+
usage: { type: 'definition', node: identifier ?? keyword, fullRange: identifier && node },
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
function nextAnonymousIndex(node, ctx) {
|
|
126
|
+
const data = ctx.symbols
|
|
127
|
+
.query({ doc: ctx.doc, node }, 'mcdoc', ctx.moduleIdentifier)
|
|
128
|
+
.getData(ModuleSymbolData.is);
|
|
129
|
+
if (!data) {
|
|
130
|
+
throw new Error(`No symbol data for module '${ctx.moduleIdentifier}'`);
|
|
131
|
+
}
|
|
132
|
+
return data.nextAnonymousIndex++;
|
|
133
|
+
}
|
|
134
|
+
function nextAnonymousIdentifier(node, ctx) {
|
|
135
|
+
return `<anonymous ${nextAnonymousIndex(node, ctx)}>`;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async function bindDispatchStatement(node, ctx) {
|
|
139
|
+
const { attributes, location, index, target } = DispatchStatementNode.destruct(node);
|
|
140
|
+
if (!(location && index && target)) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const locationStr = ResourceLocationNode.toString(location, 'full');
|
|
144
|
+
ctx.symbols
|
|
145
|
+
.query(ctx.doc, 'mcdoc/dispatcher', locationStr)
|
|
146
|
+
.enter({
|
|
147
|
+
usage: { type: 'reference', node: location, fullRange: node },
|
|
148
|
+
});
|
|
149
|
+
const { parallelIndices } = IndexBodyNode.destruct(index);
|
|
150
|
+
for (const key of parallelIndices) {
|
|
151
|
+
if (DynamicIndexNode.is(key)) {
|
|
152
|
+
// Ignore dynamic indices in dispatch statements.
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
ctx.symbols
|
|
156
|
+
.query(ctx.doc, 'mcdoc/dispatcher', locationStr, asString(key))
|
|
157
|
+
.ifDeclared(symbol => reportDuplicatedDeclaration(ctx, symbol, key, { localeString: 'mcdoc.binder.dispatcher-statement.duplicated-key' }))
|
|
158
|
+
.elseEnter({
|
|
159
|
+
data: {
|
|
160
|
+
data: {
|
|
161
|
+
attributes: convertAttributes(attributes, ctx),
|
|
162
|
+
typeDef: convertType(target, ctx),
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
usage: { type: 'definition', node: key, fullRange: node },
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
await bindType(target, ctx);
|
|
169
|
+
}
|
|
170
|
+
async function bindType(node, ctx) {
|
|
171
|
+
if (DispatcherTypeNode.is(node)) {
|
|
172
|
+
bindDispatcherType(node, ctx);
|
|
173
|
+
}
|
|
174
|
+
else if (EnumNode.is(node)) {
|
|
175
|
+
bindEnum(node, ctx);
|
|
176
|
+
}
|
|
177
|
+
else if (ListTypeNode.is(node)) {
|
|
178
|
+
const { item } = ListTypeNode.destruct(node);
|
|
179
|
+
await bindType(item, ctx);
|
|
180
|
+
}
|
|
181
|
+
else if (ReferenceTypeNode.is(node)) {
|
|
182
|
+
const { path, typeParameters } = ReferenceTypeNode.destruct(node);
|
|
183
|
+
await bindPath(path, ctx);
|
|
184
|
+
for (const param of typeParameters) {
|
|
185
|
+
await bindType(param, ctx);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
else if (StructNode.is(node)) {
|
|
189
|
+
await bindStruct(node, ctx);
|
|
190
|
+
}
|
|
191
|
+
else if (TupleTypeNode.is(node)) {
|
|
192
|
+
const { items } = TupleTypeNode.destruct(node);
|
|
193
|
+
for (const item of items) {
|
|
194
|
+
await bindType(item, ctx);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
else if (UnionTypeNode.is(node)) {
|
|
198
|
+
const { members } = UnionTypeNode.destruct(node);
|
|
199
|
+
for (const member of members) {
|
|
200
|
+
await bindType(member, ctx);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function bindDispatcherType(node, ctx) {
|
|
205
|
+
const { index, location } = DispatcherTypeNode.destruct(node);
|
|
206
|
+
const locationStr = ResourceLocationNode.toString(location, 'full');
|
|
207
|
+
const { parallelIndices } = IndexBodyNode.destruct(index);
|
|
208
|
+
for (const key of parallelIndices) {
|
|
209
|
+
if (DynamicIndexNode.is(key)) {
|
|
210
|
+
// Although it is technically possible to bind some of the dynamic indices as references
|
|
211
|
+
// of struct keys, it is rather complicated to do so. We will ignore them for now.
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
ctx.symbols
|
|
215
|
+
.query(ctx.doc, 'mcdoc/dispatcher', locationStr, asString(key))
|
|
216
|
+
.enter({ usage: { type: 'reference', node: key, fullRange: node } });
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
async function bindPath(node, ctx) {
|
|
220
|
+
for (const { identifiers, node: identNode, indexRight } of resolvePathByStep(node, ctx, { reportErrors: true })) {
|
|
221
|
+
if (!identifiers?.length) {
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
if (indexRight === 1) {
|
|
225
|
+
// The second last identifier in a path points to a file module.
|
|
226
|
+
const referencedModuleFile = pathArrayToString(identifiers);
|
|
227
|
+
const referencedModuleUri = identifierToUri(referencedModuleFile, ctx);
|
|
228
|
+
if (!referencedModuleUri) {
|
|
229
|
+
ctx.err.report(localize('mcdoc.binder.path.unknown-module', localeQuote(referencedModuleFile)), node, 2 /* ErrorSeverity.Warning */);
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
await ctx.ensureBindingStarted(referencedModuleUri);
|
|
233
|
+
}
|
|
234
|
+
ctx.symbols
|
|
235
|
+
.query({ doc: ctx.doc, node: identNode }, 'mcdoc', pathArrayToString(identifiers))
|
|
236
|
+
.ifDeclared((_, query) => query.enter({
|
|
237
|
+
usage: { type: 'reference', node: identNode, fullRange: node, skipRenaming: LiteralNode.is(identNode) },
|
|
238
|
+
}))
|
|
239
|
+
.else(() => {
|
|
240
|
+
if (indexRight === 0) {
|
|
241
|
+
ctx.err.report(localize('mcdoc.binder.path.unknown-identifier', localeQuote(atArray(identifiers, -1)), localeQuote(pathArrayToString(identifiers.slice(0, -1)))), node, 2 /* ErrorSeverity.Warning */);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
function bindEnum(node, ctx) {
|
|
247
|
+
const { block, identifier, keyword } = EnumNode.destruct(node);
|
|
248
|
+
const symbol = identifier?.symbol ?? keyword.symbol;
|
|
249
|
+
if (symbol?.subcategory !== 'enum') {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const query = ctx.symbols.query({ doc: ctx.doc, node }, 'mcdoc', ...symbol.path);
|
|
253
|
+
Dev.assertDefined(query.symbol);
|
|
254
|
+
bindEnumBlock(block, ctx, query);
|
|
255
|
+
}
|
|
256
|
+
function bindEnumBlock(node, ctx, query, options = {}) {
|
|
257
|
+
const { fields } = EnumBlockNode.destruct(node);
|
|
258
|
+
for (const field of fields) {
|
|
259
|
+
const { identifier } = EnumFieldNode.destruct(field);
|
|
260
|
+
query.member(identifier.value, fieldQuery => fieldQuery
|
|
261
|
+
.ifDeclared(symbol => reportDuplicatedDeclaration(ctx, symbol, identifier))
|
|
262
|
+
.elseEnter({ usage: { type: 'definition', node: identifier, fullRange: field } }));
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
async function bindInjection(node, ctx) {
|
|
266
|
+
const { injection } = InjectionNode.destruct(node);
|
|
267
|
+
if (EnumInjectionNode.is(injection)) {
|
|
268
|
+
// TODO
|
|
269
|
+
// const { } = EnumInjectionNode.destruct(injection)
|
|
270
|
+
// bindEnumBlock(block, ctx, query, { extendsTypeDefData: true })
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
async function bindStruct(node, ctx) {
|
|
274
|
+
const { block, identifier, keyword } = StructNode.destruct(node);
|
|
275
|
+
const symbol = identifier?.symbol ?? keyword.symbol;
|
|
276
|
+
if (symbol?.subcategory !== 'struct') {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
const query = ctx.symbols.query({ doc: ctx.doc, node }, 'mcdoc', ...symbol.path);
|
|
280
|
+
Dev.assertDefined(query.symbol);
|
|
281
|
+
await bindStructBlock(block, ctx, query);
|
|
282
|
+
}
|
|
283
|
+
async function bindStructBlock(node, ctx, query, options = {}) {
|
|
284
|
+
const { fields } = StructBlockNode.destruct(node);
|
|
285
|
+
for (const field of fields) {
|
|
286
|
+
if (StructPairFieldNode.is(field)) {
|
|
287
|
+
const { key, type } = StructPairFieldNode.destruct(field);
|
|
288
|
+
if (!StructMapKeyNode.is(key)) {
|
|
289
|
+
query.member(key.value, fieldQuery => fieldQuery
|
|
290
|
+
.ifDeclared(symbol => reportDuplicatedDeclaration(ctx, symbol, key))
|
|
291
|
+
.elseEnter({ usage: { type: 'definition', node: key, fullRange: field } }));
|
|
292
|
+
}
|
|
293
|
+
await bindType(type, ctx);
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
const { type } = StructSpreadFieldNode.destruct(field);
|
|
297
|
+
await bindType(type, ctx);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
async function bindTypeAlias(node, ctx) {
|
|
302
|
+
const { identifier, rhs, typeParams } = TypeAliasNode.destruct(node);
|
|
303
|
+
if (!identifier?.value) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
if (typeParams) {
|
|
307
|
+
// Type parameters are added as local symbols on the type alias AST node.
|
|
308
|
+
node.locals = Object.create(null);
|
|
309
|
+
const { params } = TypeParamBlockNode.destruct(typeParams);
|
|
310
|
+
const query = ctx.symbols.query({ doc: ctx.doc, node }, 'mcdoc', `${ctx.moduleIdentifier}::${identifier.value}`);
|
|
311
|
+
if (query.symbol?.subcategory === 'type_alias') {
|
|
312
|
+
// Type parameters are also added to the symbol data.
|
|
313
|
+
const oldData = query.symbol.data;
|
|
314
|
+
if (!TypeDefSymbolData.is(oldData)) {
|
|
315
|
+
throw new Error('Failed to locate the typeDef data associated with a supposedly hoisted type alias symbol');
|
|
316
|
+
}
|
|
317
|
+
const data = {
|
|
318
|
+
...oldData,
|
|
319
|
+
typeParams: [],
|
|
320
|
+
};
|
|
321
|
+
query.symbol.data = data;
|
|
322
|
+
for (const param of params) {
|
|
323
|
+
const { identifier: paramIdentifier } = TypeParamNode.destruct(param);
|
|
324
|
+
if (paramIdentifier.value) {
|
|
325
|
+
// Add the type parameter as a local symbol.
|
|
326
|
+
ctx.symbols
|
|
327
|
+
.query({ doc: ctx.doc, node }, 'mcdoc', `${ctx.moduleIdentifier}::${paramIdentifier.value}`)
|
|
328
|
+
.ifDeclared(symbol => reportDuplicatedDeclaration(ctx, symbol, paramIdentifier))
|
|
329
|
+
.elseEnter({ data: { visibility: 0 /* SymbolVisibility.Block */ }, usage: { type: 'declaration', node: paramIdentifier, fullRange: param } });
|
|
330
|
+
// Also add it to the symbol data.
|
|
331
|
+
data.typeParams.push({ identifier: paramIdentifier.value });
|
|
332
|
+
}
|
|
333
|
+
// if (constraint) {
|
|
334
|
+
// await bindPath(constraint, ctx)
|
|
335
|
+
// }
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
if (rhs) {
|
|
340
|
+
await bindType(rhs, ctx);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
async function bindUseStatement(node, ctx) {
|
|
344
|
+
const { path } = UseStatementNode.destruct(node);
|
|
345
|
+
if (!path) {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
return bindPath(path, ctx);
|
|
349
|
+
}
|
|
350
|
+
export function registerMcdocBinders(meta) {
|
|
351
|
+
meta.registerBinder('mcdoc:module', fileModule);
|
|
352
|
+
}
|
|
353
|
+
function reportDuplicatedDeclaration(ctx, symbol, range, options = { localeString: 'mcdoc.binder.duplicated-declaration' }) {
|
|
354
|
+
ctx.err.report(localize(options.localeString, localeQuote(symbol.identifier)), range, 2 /* ErrorSeverity.Warning */, {
|
|
355
|
+
related: [{
|
|
356
|
+
location: SymbolUtil.getDeclaredLocation(symbol),
|
|
357
|
+
message: localize(`${options.localeString}.related`, localeQuote(symbol.identifier)),
|
|
358
|
+
}],
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
function* resolvePathByStep(path, ctx, options = {}) {
|
|
362
|
+
const { children, isAbsolute } = PathNode.destruct(path);
|
|
363
|
+
const identifiers = isAbsolute
|
|
364
|
+
? []
|
|
365
|
+
: ctx.moduleIdentifier.slice(2).split('::');
|
|
366
|
+
for (const [i, child] of children.entries()) {
|
|
367
|
+
switch (child.type) {
|
|
368
|
+
case 'mcdoc:identifier':
|
|
369
|
+
identifiers.push(child.value);
|
|
370
|
+
break;
|
|
371
|
+
case 'mcdoc:literal':
|
|
372
|
+
// super
|
|
373
|
+
if (identifiers.length === 0) {
|
|
374
|
+
if (options.reportErrors) {
|
|
375
|
+
ctx.err.report(localize('mcdoc.binder.path.super-from-root'), child);
|
|
376
|
+
}
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
identifiers.pop();
|
|
380
|
+
break;
|
|
381
|
+
default:
|
|
382
|
+
Dev.assertNever(child);
|
|
383
|
+
}
|
|
384
|
+
yield { identifiers, node: child, index: i, indexRight: children.length - 1 - i };
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
function resolvePath(path, ctx, options = {}) {
|
|
388
|
+
return atArray([...resolvePathByStep(path, ctx, options)], -1)?.identifiers;
|
|
389
|
+
}
|
|
390
|
+
function identifierToUri(module, ctx) {
|
|
391
|
+
return ctx.symbols.global.mcdoc?.[module]?.definition?.[0]?.uri;
|
|
392
|
+
}
|
|
393
|
+
function uriToIdentifier(uri, ctx) {
|
|
394
|
+
return Object
|
|
395
|
+
.values(ctx.symbols.global.mcdoc ?? {})
|
|
396
|
+
.find(symbol => {
|
|
397
|
+
return symbol.subcategory === 'module' && symbol.definition?.some(loc => loc.uri === uri);
|
|
398
|
+
})
|
|
399
|
+
?.identifier;
|
|
400
|
+
}
|
|
401
|
+
function pathArrayToString(path) {
|
|
402
|
+
return path ? `::${path.join('::')}` : undefined;
|
|
403
|
+
}
|
|
404
|
+
function convertType(node, ctx) {
|
|
405
|
+
switch (node.type) {
|
|
406
|
+
case 'mcdoc:enum': return convertEnum(node, ctx);
|
|
407
|
+
case 'mcdoc:struct': return convertStruct(node, ctx);
|
|
408
|
+
case 'mcdoc:type/any': return convertAny(node, ctx);
|
|
409
|
+
case 'mcdoc:type/boolean': return convertBoolean(node, ctx);
|
|
410
|
+
case 'mcdoc:type/dispatcher': return convertDispatcher(node, ctx);
|
|
411
|
+
case 'mcdoc:type/list': return convertList(node, ctx);
|
|
412
|
+
case 'mcdoc:type/literal': return convertLiteral(node, ctx);
|
|
413
|
+
case 'mcdoc:type/numeric_type': return convertNumericType(node, ctx);
|
|
414
|
+
case 'mcdoc:type/primitive_array': return convertPrimitiveArray(node, ctx);
|
|
415
|
+
case 'mcdoc:type/string': return convertString(node, ctx);
|
|
416
|
+
case 'mcdoc:type/reference': return convertReference(node, ctx);
|
|
417
|
+
case 'mcdoc:type/tuple': return convertTuple(node, ctx);
|
|
418
|
+
case 'mcdoc:type/union': return convertUnion(node, ctx);
|
|
419
|
+
default: return Dev.assertNever(node);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
function convertBase(node, ctx, options = {}) {
|
|
423
|
+
const { attributes, indices } = TypeBaseNode.destruct(node);
|
|
424
|
+
return {
|
|
425
|
+
attributes: convertAttributes(attributes, ctx),
|
|
426
|
+
indices: convertIndexBodies(options.skipFirstIndexBody ? indices.slice(1) : indices, ctx),
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
function convertAttributes(nodes, ctx) {
|
|
430
|
+
return undefineEmptyArray(nodes.map(n => convertAttribute(n, ctx)));
|
|
431
|
+
}
|
|
432
|
+
function undefineEmptyArray(array) {
|
|
433
|
+
return array.length ? array : undefined;
|
|
434
|
+
}
|
|
435
|
+
function convertAttribute(node, ctx) {
|
|
436
|
+
const { name, value } = AttributeNode.destruct(node);
|
|
437
|
+
return {
|
|
438
|
+
name: name.value,
|
|
439
|
+
value: value && convertAttributeValue(value, ctx),
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
function convertAttributeValue(node, ctx) {
|
|
443
|
+
if (node.type === 'mcdoc:attribute/tree') {
|
|
444
|
+
return {
|
|
445
|
+
kind: 'tree',
|
|
446
|
+
values: convertAttributeTree(node, ctx),
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
450
|
+
return convertType(node, ctx);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
function convertAttributeTree(node, ctx) {
|
|
454
|
+
const ans = {};
|
|
455
|
+
const { named, positional } = AttributeTreeNode.destruct(node);
|
|
456
|
+
if (positional) {
|
|
457
|
+
const { values } = AttributeTreePosValuesNode.destruct(positional);
|
|
458
|
+
for (const [i, child] of values.entries()) {
|
|
459
|
+
ans[i] = convertAttributeValue(child, ctx);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
if (named) {
|
|
463
|
+
const { values } = AttributeTreeNamedValuesNode.destruct(named);
|
|
464
|
+
for (const { key, value } of values) {
|
|
465
|
+
ans[key.value] = convertAttributeValue(value, ctx);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
return ans;
|
|
469
|
+
}
|
|
470
|
+
function convertIndexBodies(nodes, ctx) {
|
|
471
|
+
return undefineEmptyArray(nodes.map(n => convertIndexBody(n, ctx)));
|
|
472
|
+
}
|
|
473
|
+
function convertIndexBody(node, ctx) {
|
|
474
|
+
const { parallelIndices } = IndexBodyNode.destruct(node);
|
|
475
|
+
return parallelIndices.map(n => convertIndex(n, ctx));
|
|
476
|
+
}
|
|
477
|
+
function convertIndex(node, ctx) {
|
|
478
|
+
return StaticIndexNode.is(node)
|
|
479
|
+
? convertStaticIndex(node, ctx)
|
|
480
|
+
: convertDynamicIndex(node, ctx);
|
|
481
|
+
}
|
|
482
|
+
function convertStaticIndex(node, ctx) {
|
|
483
|
+
return {
|
|
484
|
+
kind: 'static',
|
|
485
|
+
value: asString(node),
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
function convertDynamicIndex(node, ctx) {
|
|
489
|
+
const { keys } = DynamicIndexNode.destruct(node);
|
|
490
|
+
return {
|
|
491
|
+
kind: 'dynamic',
|
|
492
|
+
accessor: keys.map(asString),
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
function convertEnum(node, ctx) {
|
|
496
|
+
const { block, enumKind, identifier } = EnumNode.destruct(node);
|
|
497
|
+
// Shortcut if the typeDef has been added to the enum symbol.
|
|
498
|
+
const symbol = identifier?.symbol ?? node.symbol;
|
|
499
|
+
if (symbol && TypeDefSymbolData.is(symbol.data) && symbol.data.typeDef.kind === 'enum') {
|
|
500
|
+
return symbol.data.typeDef;
|
|
501
|
+
}
|
|
502
|
+
return {
|
|
503
|
+
...convertBase(node, ctx),
|
|
504
|
+
kind: 'enum',
|
|
505
|
+
enumKind,
|
|
506
|
+
values: convertEnumBlock(block, ctx),
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
function convertEnumBlock(node, ctx) {
|
|
510
|
+
const { fields } = EnumBlockNode.destruct(node);
|
|
511
|
+
return fields.map(n => convertEnumField(n, ctx));
|
|
512
|
+
}
|
|
513
|
+
function convertEnumField(node, ctx) {
|
|
514
|
+
const { attributes, identifier, value } = EnumFieldNode.destruct(node);
|
|
515
|
+
return {
|
|
516
|
+
attributes: convertAttributes(attributes, ctx),
|
|
517
|
+
identifier: identifier.value,
|
|
518
|
+
value: convertEnumValue(value, ctx),
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
function convertEnumValue(node, ctx) {
|
|
522
|
+
if (TypedNumberNode.is(node)) {
|
|
523
|
+
const { value } = TypedNumberNode.destruct(node);
|
|
524
|
+
return value.value;
|
|
525
|
+
}
|
|
526
|
+
return node.value;
|
|
527
|
+
}
|
|
528
|
+
function convertStruct(node, ctx) {
|
|
529
|
+
const { block, identifier } = StructNode.destruct(node);
|
|
530
|
+
// Shortcut if the typeDef has been added to the struct symbol.
|
|
531
|
+
const symbol = identifier?.symbol ?? node.symbol;
|
|
532
|
+
if (symbol && TypeDefSymbolData.is(symbol.data) && symbol.data.typeDef.kind === 'struct') {
|
|
533
|
+
return symbol.data.typeDef;
|
|
534
|
+
}
|
|
535
|
+
return {
|
|
536
|
+
...convertBase(node, ctx),
|
|
537
|
+
kind: 'struct',
|
|
538
|
+
fields: convertStructBlock(block, ctx),
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
function convertStructBlock(node, ctx) {
|
|
542
|
+
const { fields } = StructBlockNode.destruct(node);
|
|
543
|
+
return fields.map(n => convertStructField(n, ctx));
|
|
544
|
+
}
|
|
545
|
+
function convertStructField(node, ctx) {
|
|
546
|
+
return StructPairFieldNode.is(node)
|
|
547
|
+
? convertStructPairField(node, ctx)
|
|
548
|
+
: convertStructSpreadField(node, ctx);
|
|
549
|
+
}
|
|
550
|
+
function convertStructPairField(node, ctx) {
|
|
551
|
+
const { attributes, key, type, isOptional } = StructPairFieldNode.destruct(node);
|
|
552
|
+
return {
|
|
553
|
+
kind: 'pair',
|
|
554
|
+
attributes: convertAttributes(attributes, ctx),
|
|
555
|
+
key: convertStructKey(key, ctx),
|
|
556
|
+
type: convertType(type, ctx),
|
|
557
|
+
optional: isOptional,
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
function convertStructKey(node, ctx) {
|
|
561
|
+
if (StructMapKeyNode.is(node)) {
|
|
562
|
+
const { type } = StructMapKeyNode.destruct(node);
|
|
563
|
+
return convertType(type, ctx);
|
|
564
|
+
}
|
|
565
|
+
else {
|
|
566
|
+
return asString(node);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
function convertStructSpreadField(node, ctx) {
|
|
570
|
+
const { attributes, type } = StructSpreadFieldNode.destruct(node);
|
|
571
|
+
return {
|
|
572
|
+
kind: 'spread',
|
|
573
|
+
attributes: convertAttributes(attributes, ctx),
|
|
574
|
+
type: convertType(type, ctx),
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
function convertAny(node, ctx) {
|
|
578
|
+
return {
|
|
579
|
+
...convertBase(node, ctx),
|
|
580
|
+
kind: 'any',
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
function convertBoolean(node, ctx) {
|
|
584
|
+
return {
|
|
585
|
+
...convertBase(node, ctx),
|
|
586
|
+
kind: 'boolean',
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
function convertDispatcher(node, ctx) {
|
|
590
|
+
const { index, location } = DispatcherTypeNode.destruct(node);
|
|
591
|
+
return {
|
|
592
|
+
...convertBase(node, ctx, {
|
|
593
|
+
skipFirstIndexBody: true,
|
|
594
|
+
}),
|
|
595
|
+
kind: 'dispatcher',
|
|
596
|
+
index: convertIndexBody(index, ctx),
|
|
597
|
+
registry: ResourceLocationNode.toString(location, 'full'),
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
function convertList(node, ctx) {
|
|
601
|
+
const { item, lengthRange } = ListTypeNode.destruct(node);
|
|
602
|
+
return {
|
|
603
|
+
...convertBase(node, ctx),
|
|
604
|
+
kind: 'list',
|
|
605
|
+
item: convertType(item, ctx),
|
|
606
|
+
lengthRange: convertRange(lengthRange, ctx),
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
function convertRange(node, ctx) {
|
|
610
|
+
if (!node) {
|
|
611
|
+
return undefined;
|
|
612
|
+
}
|
|
613
|
+
const { kind, min, max } = FloatRangeNode.is(node) ? FloatRangeNode.destruct(node) : IntRangeNode.destruct(node);
|
|
614
|
+
return { kind, min: min?.value, max: max?.value };
|
|
615
|
+
}
|
|
616
|
+
function convertLiteral(node, ctx) {
|
|
617
|
+
const { value } = LiteralTypeNode.destruct(node);
|
|
618
|
+
return {
|
|
619
|
+
...convertBase(node, ctx),
|
|
620
|
+
kind: 'literal',
|
|
621
|
+
value: convertLiteralValue(value, ctx),
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
function convertLiteralValue(node, ctx) {
|
|
625
|
+
if (LiteralNode.is(node)) {
|
|
626
|
+
return {
|
|
627
|
+
kind: 'boolean',
|
|
628
|
+
value: node.value === 'true',
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
else if (TypedNumberNode.is(node)) {
|
|
632
|
+
const { suffix, value } = TypedNumberNode.destruct(node);
|
|
633
|
+
return {
|
|
634
|
+
kind: 'number',
|
|
635
|
+
value: value.value,
|
|
636
|
+
suffix: convertLiteralNumberSuffix(suffix, ctx),
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
else {
|
|
640
|
+
return {
|
|
641
|
+
kind: 'string',
|
|
642
|
+
value: node.value,
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
function convertLiteralNumberSuffix(node, ctx) {
|
|
647
|
+
const suffix = node?.value;
|
|
648
|
+
return suffix?.toLowerCase();
|
|
649
|
+
}
|
|
650
|
+
function convertNumericType(node, ctx) {
|
|
651
|
+
const { numericKind, valueRange } = NumericTypeNode.destruct(node);
|
|
652
|
+
return {
|
|
653
|
+
...convertBase(node, ctx),
|
|
654
|
+
kind: numericKind.value,
|
|
655
|
+
valueRange: convertRange(valueRange, ctx),
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
function convertPrimitiveArray(node, ctx) {
|
|
659
|
+
const { arrayKind, lengthRange, valueRange } = PrimitiveArrayTypeNode.destruct(node);
|
|
660
|
+
return {
|
|
661
|
+
...convertBase(node, ctx),
|
|
662
|
+
kind: `${arrayKind.value}_array`,
|
|
663
|
+
lengthRange: convertRange(lengthRange, ctx),
|
|
664
|
+
valueRange: convertRange(valueRange, ctx),
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
function convertString(node, ctx) {
|
|
668
|
+
const { lengthRange } = StringTypeNode.destruct(node);
|
|
669
|
+
return {
|
|
670
|
+
...convertBase(node, ctx),
|
|
671
|
+
kind: 'string',
|
|
672
|
+
lengthRange: convertRange(lengthRange, ctx),
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
function convertReference(node, ctx) {
|
|
676
|
+
const { path, typeParameters } = ReferenceTypeNode.destruct(node);
|
|
677
|
+
return {
|
|
678
|
+
...convertBase(node, ctx),
|
|
679
|
+
kind: 'reference',
|
|
680
|
+
path: pathArrayToString(resolvePath(path, ctx)),
|
|
681
|
+
typeParameters: undefineEmptyArray(typeParameters.map(n => convertType(n, ctx))),
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
function convertTuple(node, ctx) {
|
|
685
|
+
const { items } = TupleTypeNode.destruct(node);
|
|
686
|
+
return {
|
|
687
|
+
...convertBase(node, ctx),
|
|
688
|
+
kind: 'tuple',
|
|
689
|
+
items: items.map(n => convertType(n, ctx)),
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
function convertUnion(node, ctx) {
|
|
693
|
+
const { members } = UnionTypeNode.destruct(node);
|
|
694
|
+
return {
|
|
695
|
+
...convertBase(node, ctx),
|
|
696
|
+
kind: 'union',
|
|
697
|
+
members: members.map(n => convertType(n, ctx)),
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
function asString(node) {
|
|
701
|
+
if (ResourceLocationNode.is(node)) {
|
|
702
|
+
return ResourceLocationNode.toString(node, 'short');
|
|
703
|
+
}
|
|
704
|
+
return node.value;
|
|
705
|
+
}
|
|
19
706
|
//# sourceMappingURL=index.js.map
|