@spyglassmc/mcdoc 0.3.7 → 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.
@@ -4,18 +4,18 @@ import { AttributeNode, AttributeTreeNamedValuesNode, AttributeTreeNode, Attribu
4
4
  var ModuleSymbolData;
5
5
  (function (ModuleSymbolData) {
6
6
  function is(data) {
7
- return (!!data &&
8
- typeof data === 'object' &&
9
- typeof data.nextAnonymousIndex === 'number');
7
+ return (!!data
8
+ && typeof data === 'object'
9
+ && typeof data.nextAnonymousIndex === 'number');
10
10
  }
11
11
  ModuleSymbolData.is = is;
12
12
  })(ModuleSymbolData || (ModuleSymbolData = {}));
13
13
  export var TypeDefSymbolData;
14
14
  (function (TypeDefSymbolData) {
15
15
  function is(data) {
16
- return (!!data &&
17
- typeof data === 'object' &&
18
- typeof data.typeDef === 'object');
16
+ return (!!data
17
+ && typeof data === 'object'
18
+ && typeof data.typeDef === 'object');
19
19
  }
20
20
  TypeDefSymbolData.is = is;
21
21
  })(TypeDefSymbolData || (TypeDefSymbolData = {}));
@@ -25,17 +25,14 @@ export const fileModule = AsyncBinder.create(async (node, ctx) => {
25
25
  ctx.err.report(localize('mcdoc.binder.out-of-root', localeQuote(ctx.doc.uri)), Range.Beginning, 0 /* ErrorSeverity.Hint */);
26
26
  return;
27
27
  }
28
- const mcdocCtx = {
29
- ...ctx,
30
- moduleIdentifier,
31
- };
28
+ const mcdocCtx = { ...ctx, moduleIdentifier };
32
29
  return module_(node, mcdocCtx);
33
30
  });
34
31
  export async function module_(node, ctx) {
35
32
  const data = { nextAnonymousIndex: 0 };
36
- ctx.symbols
37
- .query({ doc: ctx.doc, node }, 'mcdoc', ctx.moduleIdentifier)
38
- .amend({ data: { data } });
33
+ ctx.symbols.query({ doc: ctx.doc, node }, 'mcdoc', ctx.moduleIdentifier).amend({
34
+ data: { data },
35
+ });
39
36
  hoist(node, ctx);
40
37
  for (const child of node.children) {
41
38
  switch (child.type) {
@@ -81,14 +78,10 @@ function hoist(node, ctx) {
81
78
  }
82
79
  });
83
80
  function hoistEnum(node) {
84
- hoistFor('enum', node, EnumNode.destruct, (n) => ({
85
- typeDef: convertEnum(n, ctx),
86
- }));
81
+ hoistFor('enum', node, EnumNode.destruct, (n) => ({ typeDef: convertEnum(n, ctx) }));
87
82
  }
88
83
  function hoistStruct(node) {
89
- hoistFor('struct', node, StructNode.destruct, (n) => ({
90
- typeDef: convertStruct(n, ctx),
91
- }));
84
+ hoistFor('struct', node, StructNode.destruct, (n) => ({ typeDef: convertStruct(n, ctx) }));
92
85
  }
93
86
  function hoistTypeAlias(node) {
94
87
  hoistFor('type_alias', node, TypeAliasNode.destruct, (n) => {
@@ -100,7 +93,7 @@ function hoist(node, ctx) {
100
93
  if (typeParams) {
101
94
  bindTypeParamBlock(node, typeParams, ans, ctx);
102
95
  }
103
- ans.typeDef = attributeType(ans.typeDef, attributes, ctx);
96
+ appendAttributes(ans.typeDef, attributes, ctx);
104
97
  return ans;
105
98
  });
106
99
  }
@@ -115,16 +108,15 @@ function hoist(node, ctx) {
115
108
  return;
116
109
  }
117
110
  // hoistUseStatement associates the AST node with the binding definition in the file symbol table,
118
- // which may get overridden by bindUseStatement in the later stage as an reference to the imported symbol in the global symbol table.
111
+ // which will get overridden by bindUseStatement in the later stage as an reference to the imported symbol in the global symbol table.
119
112
  // This way when the user tries to go to definition on the path in the use statement,
120
113
  // they will go to the definition in the imported file.
121
- ctx.symbols
122
- .query({ doc: ctx.doc, node }, 'mcdoc', `${ctx.moduleIdentifier}::${identifier.value}`)
123
- .ifDeclared((symbol) => reportDuplicatedDeclaration(ctx, symbol, identifier))
124
- .elseEnter({
114
+ const target = resolvePath(path, ctx);
115
+ ctx.symbols.query({ doc: ctx.doc, node }, 'mcdoc', `${ctx.moduleIdentifier}::${identifier.value}`).ifDeclared((symbol) => reportDuplicatedDeclaration(ctx, symbol, identifier)).elseEnter({
125
116
  data: {
126
117
  subcategory: 'use_statement_binding',
127
118
  visibility: 1 /* SymbolVisibility.File */,
119
+ data: target ? { target } : undefined,
128
120
  },
129
121
  usage: { type: 'definition', node: identifier, fullRange: node },
130
122
  });
@@ -132,15 +124,10 @@ function hoist(node, ctx) {
132
124
  function hoistFor(subcategory, node, destructor, getData) {
133
125
  const { docComments, identifier, keyword } = destructor(node);
134
126
  const name = identifier?.value ?? nextAnonymousIdentifier(node, ctx);
135
- ctx.symbols
136
- .query({ doc: ctx.doc, node }, 'mcdoc', `${ctx.moduleIdentifier}::${name}`)
127
+ ctx.symbols.query({ doc: ctx.doc, node }, 'mcdoc', `${ctx.moduleIdentifier}::${name}`)
137
128
  .ifDeclared((symbol) => reportDuplicatedDeclaration(ctx, symbol, identifier ?? node))
138
129
  .elseEnter({
139
- data: {
140
- data: getData(node),
141
- desc: DocCommentsNode.asText(docComments),
142
- subcategory,
143
- },
130
+ data: { data: getData(node), desc: DocCommentsNode.asText(docComments), subcategory },
144
131
  // If the current syntax structure is named, then the identifier node is entered as a definition;
145
132
  // otherwise, an anonymous identifier is generated for the symbol and the keyword node is entered as a definition.
146
133
  usage: {
@@ -151,9 +138,7 @@ function hoist(node, ctx) {
151
138
  });
152
139
  }
153
140
  function nextAnonymousIndex(node, ctx) {
154
- const data = ctx.symbols
155
- .query({ doc: ctx.doc, node }, 'mcdoc', ctx.moduleIdentifier)
156
- .getData(ModuleSymbolData.is);
141
+ const data = ctx.symbols.query({ doc: ctx.doc, node }, 'mcdoc', ctx.moduleIdentifier).getData(ModuleSymbolData.is);
157
142
  if (!data) {
158
143
  throw new Error(`No symbol data for module '${ctx.moduleIdentifier}'`);
159
144
  }
@@ -171,27 +156,16 @@ function bindTypeParamBlock(node, typeParams, data, ctx) {
171
156
  // Thus we create a new local scope on the type alias statement node first.
172
157
  node.locals = Object.create(null);
173
158
  // They are also added to the type definition.
174
- data.typeDef = {
175
- kind: 'template',
176
- child: data.typeDef,
177
- typeParams: [],
178
- };
159
+ data.typeDef = { kind: 'template', child: data.typeDef, typeParams: [] };
179
160
  const { params } = TypeParamBlockNode.destruct(typeParams);
180
161
  for (const param of params) {
181
162
  const { identifier: paramIdentifier } = TypeParamNode.destruct(param);
182
163
  if (paramIdentifier.value) {
183
164
  // Add the type parameter as a local symbol.
184
165
  const paramPath = `${ctx.moduleIdentifier}::${paramIdentifier.value}`;
185
- ctx.symbols
186
- .query({ doc: ctx.doc, node }, 'mcdoc', paramPath)
187
- .ifDeclared((symbol) => reportDuplicatedDeclaration(ctx, symbol, paramIdentifier))
188
- .elseEnter({
166
+ ctx.symbols.query({ doc: ctx.doc, node }, 'mcdoc', paramPath).ifDeclared((symbol) => reportDuplicatedDeclaration(ctx, symbol, paramIdentifier)).elseEnter({
189
167
  data: { visibility: 0 /* SymbolVisibility.Block */ },
190
- usage: {
191
- type: 'declaration',
192
- node: paramIdentifier,
193
- fullRange: param,
194
- },
168
+ usage: { type: 'declaration', node: paramIdentifier, fullRange: param },
195
169
  });
196
170
  // Also add it to the type definition.
197
171
  data.typeDef.typeParams.push({ path: paramPath });
@@ -212,27 +186,19 @@ async function bindDispatchStatement(node, ctx) {
212
186
  });
213
187
  const { parallelIndices } = IndexBodyNode.destruct(index);
214
188
  if (parallelIndices.length) {
215
- const data = {
216
- typeDef: convertType(target, ctx),
217
- };
189
+ const data = { typeDef: convertType(target, ctx) };
218
190
  if (typeParams) {
219
191
  bindTypeParamBlock(node, typeParams, data, ctx);
220
192
  }
221
- data.typeDef = attributeType(data.typeDef, attributes, ctx);
193
+ appendAttributes(data.typeDef, attributes, ctx);
222
194
  for (const key of parallelIndices) {
223
195
  if (DynamicIndexNode.is(key)) {
224
196
  // Ignore dynamic indices in dispatch statements.
225
197
  continue;
226
198
  }
227
- ctx.symbols
228
- .query(ctx.doc, 'mcdoc/dispatcher', locationStr, asString(key))
229
- .ifDeclared((symbol) => reportDuplicatedDeclaration(ctx, symbol, key, {
199
+ ctx.symbols.query(ctx.doc, 'mcdoc/dispatcher', locationStr, asString(key)).ifDeclared((symbol) => reportDuplicatedDeclaration(ctx, symbol, key, {
230
200
  localeString: 'mcdoc.binder.dispatcher-statement.duplicated-key',
231
- }))
232
- .elseEnter({
233
- data: { data },
234
- usage: { type: 'definition', node: key, fullRange: node },
235
- });
201
+ })).elseEnter({ data: { data }, usage: { type: 'definition', node: key, fullRange: node } });
236
202
  }
237
203
  }
238
204
  await bindType(target, ctx);
@@ -280,9 +246,9 @@ async function bindType(node, ctx) {
280
246
  async function bindDispatcherType(node, ctx) {
281
247
  const { index, location } = DispatcherTypeNode.destruct(node);
282
248
  const locationStr = ResourceLocationNode.toString(location, 'full');
283
- ctx.symbols
284
- .query(ctx.doc, 'mcdoc/dispatcher', locationStr)
285
- .enter({ usage: { type: 'reference', node: location, fullRange: node } });
249
+ ctx.symbols.query(ctx.doc, 'mcdoc/dispatcher', locationStr).enter({
250
+ usage: { type: 'reference', node: location, fullRange: node },
251
+ });
286
252
  const { parallelIndices } = IndexBodyNode.destruct(index);
287
253
  for (const key of parallelIndices) {
288
254
  if (DynamicIndexNode.is(key)) {
@@ -290,13 +256,15 @@ async function bindDispatcherType(node, ctx) {
290
256
  // of struct keys, it is rather complicated to do so. We will ignore them for now.
291
257
  continue;
292
258
  }
293
- ctx.symbols
294
- .query(ctx.doc, 'mcdoc/dispatcher', locationStr, asString(key))
295
- .enter({ usage: { type: 'reference', node: key, fullRange: node } });
259
+ ctx.symbols.query(ctx.doc, 'mcdoc/dispatcher', locationStr, asString(key)).enter({
260
+ usage: { type: 'reference', node: key, fullRange: node },
261
+ });
296
262
  }
297
263
  }
298
264
  async function bindPath(node, ctx) {
299
- for (const { identifiers, node: identNode, indexRight } of resolvePathByStep(node, ctx, { reportErrors: true })) {
265
+ for (const { identifiers, node: identNode, indexRight } of resolvePathByStep(node, ctx, {
266
+ reportErrors: true,
267
+ })) {
300
268
  if (!identifiers?.length) {
301
269
  continue;
302
270
  }
@@ -310,8 +278,7 @@ async function bindPath(node, ctx) {
310
278
  }
311
279
  await ctx.ensureBindingStarted(referencedModuleUri);
312
280
  }
313
- ctx.symbols
314
- .query({ doc: ctx.doc, node: identNode }, 'mcdoc', pathArrayToString(identifiers))
281
+ ctx.symbols.query({ doc: ctx.doc, node: identNode }, 'mcdoc', pathArrayToString(identifiers))
315
282
  .ifDeclared((_, query) => query.enter({
316
283
  usage: {
317
284
  type: 'reference',
@@ -319,8 +286,7 @@ async function bindPath(node, ctx) {
319
286
  fullRange: node,
320
287
  skipRenaming: LiteralNode.is(identNode),
321
288
  },
322
- }))
323
- .else(() => {
289
+ })).else(() => {
324
290
  if (indexRight === 0) {
325
291
  ctx.err.report(localize('mcdoc.binder.path.unknown-identifier', localeQuote(atArray(identifiers, -1)), localeQuote(pathArrayToString(identifiers.slice(0, -1)))), node, 2 /* ErrorSeverity.Warning */);
326
292
  }
@@ -341,15 +307,8 @@ function bindEnumBlock(node, ctx, query, options = {}) {
341
307
  const { fields } = EnumBlockNode.destruct(node);
342
308
  for (const field of fields) {
343
309
  const { identifier } = EnumFieldNode.destruct(field);
344
- query.member(identifier.value, (fieldQuery) => fieldQuery
345
- .ifDeclared((symbol) => reportDuplicatedDeclaration(ctx, symbol, identifier))
346
- .elseEnter({
347
- usage: {
348
- type: 'definition',
349
- node: identifier,
350
- fullRange: field,
351
- },
352
- }));
310
+ query.member(identifier.value, (fieldQuery) => fieldQuery.ifDeclared((symbol) => reportDuplicatedDeclaration(ctx, symbol, identifier))
311
+ .elseEnter({ usage: { type: 'definition', node: identifier, fullRange: field } }));
353
312
  }
354
313
  }
355
314
  async function bindInjection(node, ctx) {
@@ -376,11 +335,8 @@ async function bindStructBlock(node, ctx, query, options = {}) {
376
335
  if (StructPairFieldNode.is(field)) {
377
336
  const { key, type } = StructPairFieldNode.destruct(field);
378
337
  if (!StructMapKeyNode.is(key)) {
379
- query.member(key.value, (fieldQuery) => fieldQuery
380
- .ifDeclared((symbol) => reportDuplicatedDeclaration(ctx, symbol, key))
381
- .elseEnter({
382
- usage: { type: 'definition', node: key, fullRange: field },
383
- }));
338
+ query.member(key.value, (fieldQuery) => fieldQuery.ifDeclared((symbol) => reportDuplicatedDeclaration(ctx, symbol, key))
339
+ .elseEnter({ usage: { type: 'definition', node: key, fullRange: field } }));
384
340
  }
385
341
  await bindType(type, ctx);
386
342
  }
@@ -411,23 +367,34 @@ export function registerMcdocBinders(meta) {
411
367
  }
412
368
  function reportDuplicatedDeclaration(ctx, symbol, range, options = { localeString: 'mcdoc.binder.duplicated-declaration' }) {
413
369
  ctx.err.report(localize(options.localeString, localeQuote(symbol.identifier)), range, 2 /* ErrorSeverity.Warning */, {
414
- related: [
415
- {
370
+ related: [{
416
371
  location: SymbolUtil.getDeclaredLocation(symbol),
417
372
  message: localize(`${options.localeString}.related`, localeQuote(symbol.identifier)),
418
- },
419
- ],
373
+ }],
420
374
  });
421
375
  }
422
376
  function* resolvePathByStep(path, ctx, options = {}) {
423
377
  const { children, isAbsolute } = PathNode.destruct(path);
424
- const identifiers = isAbsolute
425
- ? []
426
- : ctx.moduleIdentifier.slice(2).split('::');
378
+ let identifiers = isAbsolute ? [] : pathStringToArray(ctx.moduleIdentifier);
427
379
  for (const [i, child] of children.entries()) {
380
+ const indexRight = children.length - 1 - i;
428
381
  switch (child.type) {
429
382
  case 'mcdoc:identifier':
383
+ // For a path node with `n` children, the first `n-1` child nodes specify
384
+ // the path of the module that contains the symbol. They will be pushed
385
+ // to the `identifiers` array and yielded as-is. The last node, however,
386
+ // may be created by a use statement and points to a global symbol
387
+ // in a different file. We will query the symbol table and rewrite
388
+ // the `identifiers` array to be the target path if needed.
430
389
  identifiers.push(child.value);
390
+ if (indexRight === 0) {
391
+ ctx.symbols.query({ doc: ctx.doc, node: child }, 'mcdoc', pathArrayToString(identifiers)).ifDeclared((symbol) => {
392
+ const data = symbol.data;
393
+ if (data?.target) {
394
+ identifiers = [...data.target];
395
+ }
396
+ });
397
+ }
431
398
  break;
432
399
  case 'mcdoc:literal':
433
400
  // super
@@ -442,12 +409,7 @@ function* resolvePathByStep(path, ctx, options = {}) {
442
409
  default:
443
410
  Dev.assertNever(child);
444
411
  }
445
- yield {
446
- identifiers,
447
- node: child,
448
- index: i,
449
- indexRight: children.length - 1 - i,
450
- };
412
+ yield { identifiers, node: child, index: i, indexRight };
451
413
  }
452
414
  }
453
415
  function resolvePath(path, ctx, options = {}) {
@@ -458,13 +420,18 @@ function identifierToUri(module, ctx) {
458
420
  }
459
421
  function uriToIdentifier(uri, ctx) {
460
422
  return Object.values(ctx.symbols.global.mcdoc ?? {}).find((symbol) => {
461
- return (symbol.subcategory === 'module' &&
462
- symbol.definition?.some((loc) => loc.uri === uri));
423
+ return (symbol.subcategory === 'module' && symbol.definition?.some((loc) => loc.uri === uri));
463
424
  })?.identifier;
464
425
  }
465
426
  function pathArrayToString(path) {
466
427
  return path ? `::${path.join('::')}` : undefined;
467
428
  }
429
+ function pathStringToArray(path) {
430
+ if (!path.startsWith('::')) {
431
+ throw new Error('Only absolute paths are supported');
432
+ }
433
+ return path.slice(2).split('::');
434
+ }
468
435
  function convertType(node, ctx) {
469
436
  switch (node.type) {
470
437
  case 'mcdoc:enum':
@@ -506,32 +473,25 @@ function wrapType(node, type, ctx, options = {}) {
506
473
  options.skipFirstIndexBody = false;
507
474
  continue;
508
475
  }
509
- ans = {
510
- kind: 'indexed',
511
- child: ans,
512
- parallelIndices: convertIndexBody(appendix, ctx),
513
- };
476
+ ans = { kind: 'indexed', child: ans, parallelIndices: convertIndexBody(appendix, ctx) };
514
477
  }
515
478
  else {
516
- ans = {
517
- kind: 'concrete',
518
- child: ans,
519
- typeArgs: convertTypeArgBlock(appendix, ctx),
520
- };
479
+ ans = { kind: 'concrete', child: ans, typeArgs: convertTypeArgBlock(appendix, ctx) };
521
480
  }
522
481
  }
523
- ans = attributeType(ans, attributes, ctx);
482
+ ans.attributes = convertAttributes(attributes, ctx);
524
483
  return ans;
525
484
  }
526
- function attributeType(type, attributes, ctx) {
527
- for (const attribute of attributes) {
528
- type = {
529
- kind: 'attributed',
530
- attribute: convertAttribute(attribute, ctx),
531
- child: type,
532
- };
485
+ function appendAttributes(typeDef, attributes, ctx) {
486
+ const convertedAttributes = convertAttributes(attributes, ctx);
487
+ if (convertedAttributes) {
488
+ if (typeDef.attributes) {
489
+ typeDef.attributes = [...typeDef.attributes, ...convertedAttributes];
490
+ }
491
+ else {
492
+ typeDef.attributes = convertedAttributes;
493
+ }
533
494
  }
534
- return type;
535
495
  }
536
496
  function convertAttributes(nodes, ctx) {
537
497
  return undefineEmptyArray(nodes.map((n) => convertAttribute(n, ctx)));
@@ -541,17 +501,11 @@ function undefineEmptyArray(array) {
541
501
  }
542
502
  function convertAttribute(node, ctx) {
543
503
  const { name, value } = AttributeNode.destruct(node);
544
- return {
545
- name: name.value,
546
- value: value && convertAttributeValue(value, ctx),
547
- };
504
+ return { name: name.value, value: value && convertAttributeValue(value, ctx) };
548
505
  }
549
506
  function convertAttributeValue(node, ctx) {
550
507
  if (node.type === 'mcdoc:attribute/tree') {
551
- return {
552
- kind: 'tree',
553
- values: convertAttributeTree(node, ctx),
554
- };
508
+ return { kind: 'tree', values: convertAttributeTree(node, ctx) };
555
509
  }
556
510
  else {
557
511
  return convertType(node, ctx);
@@ -582,21 +536,21 @@ function convertIndexBody(node, ctx) {
582
536
  return parallelIndices.map((n) => convertIndex(n, ctx));
583
537
  }
584
538
  function convertIndex(node, ctx) {
585
- return StaticIndexNode.is(node)
586
- ? convertStaticIndex(node, ctx)
587
- : convertDynamicIndex(node, ctx);
539
+ return StaticIndexNode.is(node) ? convertStaticIndex(node, ctx) : convertDynamicIndex(node, ctx);
588
540
  }
589
541
  function convertStaticIndex(node, ctx) {
590
- return {
591
- kind: 'static',
592
- value: asString(node),
593
- };
542
+ return { kind: 'static', value: asString(node) };
594
543
  }
595
544
  function convertDynamicIndex(node, ctx) {
596
545
  const { keys } = DynamicIndexNode.destruct(node);
597
546
  return {
598
547
  kind: 'dynamic',
599
- accessor: keys.map(asString),
548
+ accessor: keys.map(key => {
549
+ if (LiteralNode.is(key) && key.value.startsWith('%')) {
550
+ return { keyword: key.value.slice(1) };
551
+ }
552
+ return asString(key);
553
+ }),
600
554
  };
601
555
  }
602
556
  function convertTypeArgBlock(node, ctx) {
@@ -607,16 +561,10 @@ function convertEnum(node, ctx) {
607
561
  const { block, enumKind, identifier } = EnumNode.destruct(node);
608
562
  // Shortcut if the typeDef has been added to the enum symbol.
609
563
  const symbol = identifier?.symbol ?? node.symbol;
610
- if (symbol &&
611
- TypeDefSymbolData.is(symbol.data) &&
612
- symbol.data.typeDef.kind === 'enum') {
564
+ if (symbol && TypeDefSymbolData.is(symbol.data) && symbol.data.typeDef.kind === 'enum') {
613
565
  return symbol.data.typeDef;
614
566
  }
615
- return wrapType(node, {
616
- kind: 'enum',
617
- enumKind,
618
- values: convertEnumBlock(block, ctx),
619
- }, ctx);
567
+ return wrapType(node, { kind: 'enum', enumKind, values: convertEnumBlock(block, ctx) }, ctx);
620
568
  }
621
569
  function convertEnumBlock(node, ctx) {
622
570
  const { fields } = EnumBlockNode.destruct(node);
@@ -641,15 +589,10 @@ function convertStruct(node, ctx) {
641
589
  const { block, identifier } = StructNode.destruct(node);
642
590
  // Shortcut if the typeDef has been added to the struct symbol.
643
591
  const symbol = identifier?.symbol ?? node.symbol;
644
- if (symbol &&
645
- TypeDefSymbolData.is(symbol.data) &&
646
- symbol.data.typeDef.kind === 'struct') {
592
+ if (symbol && TypeDefSymbolData.is(symbol.data) && symbol.data.typeDef.kind === 'struct') {
647
593
  return symbol.data.typeDef;
648
594
  }
649
- return wrapType(node, {
650
- kind: 'struct',
651
- fields: convertStructBlock(block, ctx),
652
- }, ctx);
595
+ return wrapType(node, { kind: 'struct', fields: convertStructBlock(block, ctx) }, ctx);
653
596
  }
654
597
  function convertStructBlock(node, ctx) {
655
598
  const { fields } = StructBlockNode.destruct(node);
@@ -661,10 +604,11 @@ function convertStructField(node, ctx) {
661
604
  : convertStructSpreadField(node, ctx);
662
605
  }
663
606
  function convertStructPairField(node, ctx) {
664
- const { attributes, key, type, isOptional } = StructPairFieldNode.destruct(node);
607
+ const { attributes, docComments, key, type, isOptional } = StructPairFieldNode.destruct(node);
665
608
  return {
666
609
  kind: 'pair',
667
610
  attributes: convertAttributes(attributes, ctx),
611
+ desc: DocCommentsNode.asText(docComments),
668
612
  key: convertStructKey(key, ctx),
669
613
  type: convertType(type, ctx),
670
614
  optional: isOptional,
@@ -688,14 +632,10 @@ function convertStructSpreadField(node, ctx) {
688
632
  };
689
633
  }
690
634
  function convertAny(node, ctx) {
691
- return wrapType(node, {
692
- kind: 'any',
693
- }, ctx);
635
+ return wrapType(node, { kind: 'any' }, ctx);
694
636
  }
695
637
  function convertBoolean(node, ctx) {
696
- return wrapType(node, {
697
- kind: 'boolean',
698
- }, ctx);
638
+ return wrapType(node, { kind: 'boolean' }, ctx);
699
639
  }
700
640
  function convertDispatcher(node, ctx) {
701
641
  const { index, location } = DispatcherTypeNode.destruct(node);
@@ -724,36 +664,40 @@ function convertRange(node, ctx) {
724
664
  }
725
665
  function convertLiteral(node, ctx) {
726
666
  const { value } = LiteralTypeNode.destruct(node);
727
- return wrapType(node, {
728
- kind: 'literal',
729
- value: convertLiteralValue(value, ctx),
730
- }, ctx);
667
+ return wrapType(node, { kind: 'literal', value: convertLiteralValue(value, ctx) }, ctx);
731
668
  }
732
669
  function convertLiteralValue(node, ctx) {
733
670
  if (LiteralNode.is(node)) {
734
- return {
735
- kind: 'boolean',
736
- value: node.value === 'true',
737
- };
671
+ return { kind: 'boolean', value: node.value === 'true' };
738
672
  }
739
673
  else if (TypedNumberNode.is(node)) {
740
674
  const { suffix, value } = TypedNumberNode.destruct(node);
741
675
  return {
742
- kind: 'number',
676
+ kind: convertLiteralNumberSuffix(suffix, ctx)
677
+ ?? (value.type === 'integer' ? 'int' : 'double'),
743
678
  value: value.value,
744
- suffix: convertLiteralNumberSuffix(suffix, ctx),
745
679
  };
746
680
  }
747
681
  else {
748
- return {
749
- kind: 'string',
750
- value: node.value,
751
- };
682
+ return { kind: 'string', value: node.value };
752
683
  }
753
684
  }
754
685
  function convertLiteralNumberSuffix(node, ctx) {
755
686
  const suffix = node?.value;
756
- return suffix?.toLowerCase();
687
+ switch (suffix?.toLowerCase()) {
688
+ case 'b':
689
+ return 'byte';
690
+ case 's':
691
+ return 'short';
692
+ case 'l':
693
+ return 'long';
694
+ case 'f':
695
+ return 'float';
696
+ case 'd':
697
+ return 'double';
698
+ default:
699
+ return undefined;
700
+ }
757
701
  }
758
702
  function convertNumericType(node, ctx) {
759
703
  const { numericKind, valueRange } = NumericTypeNode.destruct(node);
@@ -763,8 +707,7 @@ function convertNumericType(node, ctx) {
763
707
  }, ctx);
764
708
  }
765
709
  function convertPrimitiveArray(node, ctx) {
766
- const { arrayKind, lengthRange, valueRange } = PrimitiveArrayTypeNode
767
- .destruct(node);
710
+ const { arrayKind, lengthRange, valueRange } = PrimitiveArrayTypeNode.destruct(node);
768
711
  return wrapType(node, {
769
712
  kind: `${arrayKind.value}_array`,
770
713
  lengthRange: convertRange(lengthRange, ctx),
@@ -773,31 +716,19 @@ function convertPrimitiveArray(node, ctx) {
773
716
  }
774
717
  function convertString(node, ctx) {
775
718
  const { lengthRange } = StringTypeNode.destruct(node);
776
- return wrapType(node, {
777
- kind: 'string',
778
- lengthRange: convertRange(lengthRange, ctx),
779
- }, ctx);
719
+ return wrapType(node, { kind: 'string', lengthRange: convertRange(lengthRange, ctx) }, ctx);
780
720
  }
781
721
  function convertReference(node, ctx) {
782
722
  const { path } = ReferenceTypeNode.destruct(node);
783
- return wrapType(node, {
784
- kind: 'reference',
785
- path: pathArrayToString(resolvePath(path, ctx)),
786
- }, ctx);
723
+ return wrapType(node, { kind: 'reference', path: pathArrayToString(resolvePath(path, ctx)) }, ctx);
787
724
  }
788
725
  function convertTuple(node, ctx) {
789
726
  const { items } = TupleTypeNode.destruct(node);
790
- return wrapType(node, {
791
- kind: 'tuple',
792
- items: items.map((n) => convertType(n, ctx)),
793
- }, ctx);
727
+ return wrapType(node, { kind: 'tuple', items: items.map((n) => convertType(n, ctx)) }, ctx);
794
728
  }
795
729
  function convertUnion(node, ctx) {
796
730
  const { members } = UnionTypeNode.destruct(node);
797
- return wrapType(node, {
798
- kind: 'union',
799
- members: members.map((n) => convertType(n, ctx)),
800
- }, ctx);
731
+ return wrapType(node, { kind: 'union', members: members.map((n) => convertType(n, ctx)) }, ctx);
801
732
  }
802
733
  function asString(node) {
803
734
  if (ResourceLocationNode.is(node)) {
package/lib/index.d.ts CHANGED
@@ -3,6 +3,7 @@ export * as binder from './binder/index.js';
3
3
  export * as colorizer from './colorizer/index.js';
4
4
  export * from './node/index.js';
5
5
  export * from './parser/index.js';
6
+ export * as runtime from './runtime/index.js';
6
7
  export * from './type/index.js';
7
8
  export * from './uri_processors.js';
8
9
  export declare const initialize: ({ meta }: {
package/lib/index.js CHANGED
@@ -1,19 +1,19 @@
1
1
  import * as binder from './binder/index.js';
2
2
  import * as colorizer from './colorizer/index.js';
3
3
  import * as parser from './parser/index.js';
4
+ import { registerBuiltinAttributes } from './runtime/attribute/builtin.js';
4
5
  import * as uri_professors from './uri_processors.js';
5
6
  export * as binder from './binder/index.js';
6
7
  export * as colorizer from './colorizer/index.js';
7
8
  export * from './node/index.js';
8
9
  export * from './parser/index.js';
10
+ export * as runtime from './runtime/index.js';
9
11
  export * from './type/index.js';
10
12
  export * from './uri_processors.js';
11
13
  /* istanbul ignore next */
12
14
  export const initialize = ({ meta }) => {
13
- meta.registerLanguage('mcdoc', {
14
- extensions: ['.mcdoc'],
15
- parser: parser.module_,
16
- });
15
+ meta.registerLanguage('mcdoc', { extensions: ['.mcdoc'], parser: parser.module_ });
16
+ registerBuiltinAttributes(meta);
17
17
  meta.registerUriBinder(uri_professors.uriBinder);
18
18
  meta.setUriSorter(uri_professors.uriSorter);
19
19
  binder.registerMcdocBinders(meta);
@@ -1,5 +1,5 @@
1
- import type { AstNode, ColorTokenType, IntegerNode, SymbolBaseNode } from '@spyglassmc/core';
2
- import { CommentNode, FloatNode, ResourceLocationNode, StringNode } from '@spyglassmc/core';
1
+ import type { AstNode, ColorTokenType, SymbolBaseNode } from '@spyglassmc/core';
2
+ import { CommentNode, FloatNode, IntegerNode, ResourceLocationNode, StringNode } from '@spyglassmc/core';
3
3
  export interface ModuleNode extends AstNode {
4
4
  type: 'mcdoc:module';
5
5
  children: TopLevelNode[];
@@ -185,11 +185,11 @@ export declare namespace LiteralTypeValueNode {
185
185
  }
186
186
  export interface TypedNumberNode extends AstNode {
187
187
  type: 'mcdoc:typed_number';
188
- children: (FloatNode | LiteralNode)[];
188
+ children: (FloatNode | IntegerNode | LiteralNode)[];
189
189
  }
190
190
  export declare namespace TypedNumberNode {
191
191
  function destruct(node: TypedNumberNode): {
192
- value: FloatNode;
192
+ value: FloatNode | IntegerNode;
193
193
  suffix?: LiteralNode;
194
194
  };
195
195
  function is(node: AstNode | undefined): node is TypedNumberNode;
@@ -211,6 +211,10 @@ export declare const RangeExclusiveChar = "<";
211
211
  * The bit is turned on if the range is exclusive on that end.
212
212
  */
213
213
  export type RangeKind = 0b00 | 0b01 | 0b10 | 0b11;
214
+ export declare namespace RangeKind {
215
+ function isLeftExclusive(rangeKind: RangeKind): boolean;
216
+ function isRightExclusive(rangeKind: RangeKind): boolean;
217
+ }
214
218
  export declare function getRangeDelimiter(kind: RangeKind): string;
215
219
  export interface FloatRangeNode extends AstNode {
216
220
  type: 'mcdoc:float_range';
@@ -395,6 +399,7 @@ export interface StructPairFieldNode extends AstNode {
395
399
  export declare namespace StructPairFieldNode {
396
400
  function destruct(node: StructPairFieldNode): {
397
401
  attributes: AttributeNode[];
402
+ docComments?: DocCommentsNode;
398
403
  key: StructKeyNode;
399
404
  type: TypeNode;
400
405
  isOptional?: boolean;