@mirascript/textmate 0.1.83 → 0.1.84

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/src/grammar.ts CHANGED
@@ -11,22 +11,33 @@ import { mirascriptDocLanguage, mirascriptLanguage, mirascriptTemplateLanguage }
11
11
 
12
12
  const SCOPE_SUFFIX = 'mira';
13
13
  const IDENTIFIER = REG_IDENTIFIER.source;
14
+ const IDENTIFIER_START = String.raw`(?<!\p{XID_Continue})`;
14
15
  const IDENTIFIER_END = String.raw`(?!\p{XID_Continue})`;
15
16
  const MAX_VERBATIM_LENGTH = 16;
16
17
  const BUILT_IN_TYPES = [
17
18
  'any',
18
19
  'unknown',
19
20
  'never',
21
+
20
22
  'boolean',
23
+ 'true',
24
+ 'false',
25
+
21
26
  'number',
22
27
  'string',
28
+
23
29
  'record',
24
30
  'array',
25
31
  'extern',
26
32
  'module',
33
+
27
34
  'nil',
28
35
  ];
29
36
  const DOC_CONSTANT_IDENTIFIER = String.raw`(?:@+\p{XID_Continue}+|\p{Lu}[\p{XID_Continue}]*)`;
37
+ const DOC_TAG_NAME = String.raw`[^>\r\n]*[^>\s]`;
38
+ const DOC_TAG_CONTENT = String.raw`(?:module|function|extern)(?:[ \t]+${DOC_TAG_NAME})?`;
39
+ const DOC_FIELD_NAME = String.raw`(?:${IDENTIFIER}|\d+|"(?:\\.|[^"\\\r\n])*"|'(?:\\.|[^'\\\r\n])*')`;
40
+ const DOC_TAG_NUMBER = String.raw`\d[\d_]*(?:\.[\d_]+)?(?:[eE][+-]?[\d_]*\d)?`;
30
41
 
31
42
  /**
32
43
  * Escape a literal value for insertion into an Oniguruma regular expression.
@@ -45,17 +56,6 @@ function alternatives(values: Iterable<string>): string {
45
56
  .join('|');
46
57
  }
47
58
 
48
- /**
49
- * Create a boundary-aware TextMate keyword rule.
50
- */
51
- function keywordRule(values: readonly string[], name: string): { name: string; match: string } | null {
52
- if (values.length === 0) return null;
53
- return {
54
- name: `${name}.${SCOPE_SUFFIX}`,
55
- match: String.raw`(?<!\p{XID_Continue})(?:${alternatives(values)})${IDENTIFIER_END}`,
56
- };
57
- }
58
-
59
59
  const numericKeywordSet = new Set<string>(NUMERIC_KEYWORDS);
60
60
  const constantKeywords = CONSTANT_KEYWORDS.filter((keyword) => !numericKeywordSet.has(keyword));
61
61
  const languageVariables = ['_', 'global'];
@@ -73,7 +73,7 @@ const classifiedKeywords = new Set([
73
73
  'type',
74
74
  ]);
75
75
  const otherKeywords = KEYWORDS.filter((keyword) => !classifiedKeywords.has(keyword));
76
- const mirascriptFenceTags = alternatives([mirascriptLanguage.name, ...(mirascriptLanguage.aliases ?? [])]);
76
+ const mirascriptFenceTags = alternatives([mirascriptLanguage.name, ...mirascriptLanguage.aliases]);
77
77
  const documentationMiraFenceBegin = String.raw`^(\s*\*\s?)(\x60{3,})\s*((?i:${mirascriptFenceTags}))?\s*$`;
78
78
  const documentationOtherFenceBegin = String.raw`^(\s*\*\s?)(\x60{3,})\s*(\S+)(?:\s+.*)?\s*$`;
79
79
  const documentationFenceEnd = String.raw`^(?:(\s*\*\s?)(\2\x60*)\s*$)|(?=\*/)`;
@@ -300,6 +300,13 @@ function documentationRepository(sourceInclude = '#source') {
300
300
  * Build the shared repository used by source and template grammars.
301
301
  */
302
302
  function sourceRepository(): LanguageRegistration['repository'] {
303
+ /**
304
+ * Create a boundary-aware TextMate keyword rule.
305
+ */
306
+ const keywordRule = (values: readonly string[], name: string) => ({
307
+ name: `${name}.${SCOPE_SUFFIX}`,
308
+ match: `${IDENTIFIER_START}(?:${alternatives(values)})${IDENTIFIER_END}`,
309
+ });
303
310
  const keywordPatterns = [
304
311
  keywordRule(NUMERIC_KEYWORDS, 'constant.numeric.language'),
305
312
  keywordRule(constantKeywords, 'constant.language'),
@@ -310,7 +317,7 @@ function sourceRepository(): LanguageRegistration['repository'] {
310
317
  keywordRule(RESERVED_KEYWORDS, 'keyword.reserved'),
311
318
  keywordRule(languageVariables, 'variable.language'),
312
319
  keywordRule(otherKeywords, 'keyword.other'),
313
- ].filter((p) => p != null);
320
+ ];
314
321
 
315
322
  return {
316
323
  source: {
@@ -365,7 +372,7 @@ function sourceRepository(): LanguageRegistration['repository'] {
365
372
  patterns: [
366
373
  {
367
374
  name: `meta.function.declaration.${SCOPE_SUFFIX}`,
368
- begin: String.raw`(?<!\p{XID_Continue})(fn)(\s+)(${IDENTIFIER})(\s*)(\()`,
375
+ begin: String.raw`${IDENTIFIER_START}(fn)(\s+)(${IDENTIFIER})(\s*)(\()`,
369
376
  beginCaptures: {
370
377
  1: { name: `keyword.declaration.function.${SCOPE_SUFFIX}` },
371
378
  3: { name: `entity.name.function.${SCOPE_SUFFIX}` },
@@ -377,7 +384,7 @@ function sourceRepository(): LanguageRegistration['repository'] {
377
384
  },
378
385
  {
379
386
  name: `meta.function.declaration.${SCOPE_SUFFIX}`,
380
- match: String.raw`(?<!\p{XID_Continue})(fn)(\s+)(${IDENTIFIER})`,
387
+ match: String.raw`${IDENTIFIER_START}(fn)(\s+)(${IDENTIFIER})`,
381
388
  captures: {
382
389
  1: { name: `keyword.declaration.function.${SCOPE_SUFFIX}` },
383
390
  3: { name: `entity.name.function.${SCOPE_SUFFIX}` },
@@ -385,7 +392,7 @@ function sourceRepository(): LanguageRegistration['repository'] {
385
392
  },
386
393
  {
387
394
  name: `meta.function.expression.parameters.${SCOPE_SUFFIX}`,
388
- begin: String.raw`(?<!\p{XID_Continue})(fn)(\s*)(\()`,
395
+ begin: String.raw`${IDENTIFIER_START}(fn)(\s*)(\()`,
389
396
  beginCaptures: {
390
397
  1: { name: `keyword.declaration.function.${SCOPE_SUFFIX}` },
391
398
  3: { name: `punctuation.section.parameters.begin.${SCOPE_SUFFIX}` },
@@ -399,7 +406,7 @@ function sourceRepository(): LanguageRegistration['repository'] {
399
406
  parameters: {
400
407
  patterns: [
401
408
  {
402
- match: String.raw`(?<!\p{XID_Continue})(mut)(\s+)(${IDENTIFIER})`,
409
+ match: String.raw`${IDENTIFIER_START}(mut)(\s+)(${IDENTIFIER})`,
403
410
  captures: {
404
411
  1: { name: `keyword.declaration.mutable.${SCOPE_SUFFIX}` },
405
412
  3: { name: `variable.emphasis.${SCOPE_SUFFIX}` },
@@ -407,7 +414,7 @@ function sourceRepository(): LanguageRegistration['repository'] {
407
414
  },
408
415
  {
409
416
  name: `keyword.declaration.mutable.${SCOPE_SUFFIX}`,
410
- match: String.raw`(?<!\p{XID_Continue})mut${IDENTIFIER_END}`,
417
+ match: `${IDENTIFIER_START}mut${IDENTIFIER_END}`,
411
418
  },
412
419
  { name: `keyword.operator.spread.${SCOPE_SUFFIX}`, match: String.raw`\.\.` },
413
420
  { name: `variable.other.constant.emphasis.${SCOPE_SUFFIX}`, match: IDENTIFIER },
@@ -428,7 +435,7 @@ function sourceRepository(): LanguageRegistration['repository'] {
428
435
  'module-declarations': {
429
436
  patterns: [
430
437
  {
431
- match: String.raw`(?<!\p{XID_Continue})(mod)(\s+)(${IDENTIFIER})`,
438
+ match: String.raw`${IDENTIFIER_START}(mod)(\s+)(${IDENTIFIER})`,
432
439
  captures: {
433
440
  1: { name: `keyword.control.module.${SCOPE_SUFFIX}` },
434
441
  3: { name: `entity.name.namespace.${SCOPE_SUFFIX}` },
@@ -439,7 +446,7 @@ function sourceRepository(): LanguageRegistration['repository'] {
439
446
  'for-bindings': {
440
447
  patterns: [
441
448
  {
442
- match: String.raw`(?<!\p{XID_Continue})(for)(\s+)(mut)(\s+)(${IDENTIFIER})(\s+)(in)${IDENTIFIER_END}`,
449
+ match: String.raw`${IDENTIFIER_START}(for)(\s+)(mut)(\s+)(${IDENTIFIER})(\s+)(in)${IDENTIFIER_END}`,
443
450
  captures: {
444
451
  1: { name: `keyword.control.loop.${SCOPE_SUFFIX}` },
445
452
  3: { name: `keyword.declaration.mutable.${SCOPE_SUFFIX}` },
@@ -448,7 +455,7 @@ function sourceRepository(): LanguageRegistration['repository'] {
448
455
  },
449
456
  },
450
457
  {
451
- match: String.raw`(?<!\p{XID_Continue})(for)(\s+)(${IDENTIFIER})(\s+)(in)${IDENTIFIER_END}`,
458
+ match: String.raw`${IDENTIFIER_START}(for)(\s+)(${IDENTIFIER})(\s+)(in)${IDENTIFIER_END}`,
452
459
  captures: {
453
460
  1: { name: `keyword.control.loop.${SCOPE_SUFFIX}` },
454
461
  3: { name: `variable.other.${SCOPE_SUFFIX}` },
@@ -509,7 +516,7 @@ function sourceRepository(): LanguageRegistration['repository'] {
509
516
  patterns: [
510
517
  {
511
518
  name: `keyword.operator.expression.${SCOPE_SUFFIX}`,
512
- match: String.raw`(?<!\p{XID_Continue})type${IDENTIFIER_END}(?=\s*\(|\s+${IDENTIFIER})`,
519
+ match: String.raw`${IDENTIFIER_START}type${IDENTIFIER_END}(?=\s*\(|\s+${IDENTIFIER})`,
513
520
  },
514
521
  ],
515
522
  },
@@ -664,27 +671,43 @@ export function createMiraScriptDocGrammar(): LanguageRegistration {
664
671
  ...documentationRepository('source.mira#source'),
665
672
  doc: {
666
673
  patterns: [
674
+ { include: '#tag' },
667
675
  { include: '#global-value' },
668
676
  { include: '#global-constants' },
669
677
  { include: '#inline-parameter' },
678
+ { include: '#properties' },
670
679
  { include: '#inline-label' },
680
+ { include: '#binding-value' },
671
681
  { include: '#bindings' },
672
682
  { include: '#function-signatures' },
673
- { include: '#properties' },
674
683
  { include: '#generic-types' },
675
684
  { include: '#parameters' },
676
685
  { include: '#return-type' },
677
- { include: '#metadata' },
686
+ { include: '#tag-comment' },
678
687
  { include: 'source.mira' },
679
688
  ],
680
689
  },
690
+ tag: {
691
+ patterns: [
692
+ {
693
+ name: 'meta.documentation.tag.mira',
694
+ contentName: 'meta.documentation.tag.content.mira',
695
+ begin: `(<)(?=${DOC_TAG_CONTENT}>)`,
696
+ beginCaptures: { 1: { name: 'punctuation.definition.tag.begin.mira' } },
697
+ end: '(>)',
698
+ endCaptures: { 1: { name: 'punctuation.definition.tag.end.mira' } },
699
+ patterns: [{ include: '#tag-content' }],
700
+ },
701
+ ],
702
+ },
681
703
  'global-value': {
682
704
  patterns: [
683
705
  {
706
+ name: 'meta.documentation.global-value.mira',
684
707
  begin: String.raw`^(\x00)?(\(global\))(\s+)(${IDENTIFIER})(\s*)(=)`,
685
708
  beginCaptures: {
686
709
  2: { name: 'entity.name.label.mira' },
687
- 4: { name: 'variable.other.constant.mira' },
710
+ 4: { name: 'variable.other.mira' },
688
711
  6: { name: 'keyword.operator.assignment.mira' },
689
712
  },
690
713
  end: '$',
@@ -739,7 +762,7 @@ export function createMiraScriptDocGrammar(): LanguageRegistration {
739
762
  bindings: {
740
763
  patterns: [
741
764
  {
742
- match: String.raw`(?<!\p{XID_Continue})(let)(\s+)(mut)(\s+)(${IDENTIFIER})`,
765
+ match: String.raw`${IDENTIFIER_START}(let)(\s+)(mut)(\s+)(${IDENTIFIER})`,
743
766
  captures: {
744
767
  1: { name: 'keyword.declaration.variable.mira' },
745
768
  3: { name: 'keyword.declaration.mutable.mira' },
@@ -747,7 +770,7 @@ export function createMiraScriptDocGrammar(): LanguageRegistration {
747
770
  },
748
771
  },
749
772
  {
750
- match: String.raw`(?<!\p{XID_Continue})(let|const)(\s+)(${IDENTIFIER})`,
773
+ match: String.raw`${IDENTIFIER_START}(let|const)(\s+)(${IDENTIFIER})`,
751
774
  captures: {
752
775
  1: { name: 'keyword.declaration.variable.mira' },
753
776
  3: { name: 'variable.other.constant.mira' },
@@ -755,10 +778,35 @@ export function createMiraScriptDocGrammar(): LanguageRegistration {
755
778
  },
756
779
  ],
757
780
  },
781
+ 'binding-value': {
782
+ patterns: [
783
+ {
784
+ begin: String.raw`${IDENTIFIER_START}(let)(\s+)(mut)(\s+)(${IDENTIFIER})(\s*)(=)`,
785
+ beginCaptures: {
786
+ 1: { name: 'keyword.declaration.variable.mira' },
787
+ 3: { name: 'keyword.declaration.mutable.mira' },
788
+ 5: { name: 'variable.other.readwrite.mira' },
789
+ 7: { name: 'keyword.operator.assignment.mira' },
790
+ },
791
+ end: '$',
792
+ patterns: [{ include: '#doc-value' }],
793
+ },
794
+ {
795
+ begin: String.raw`${IDENTIFIER_START}(let|const)(\s+)(${IDENTIFIER})(\s*)(=)`,
796
+ beginCaptures: {
797
+ 1: { name: 'keyword.declaration.variable.mira' },
798
+ 3: { name: 'variable.other.constant.mira' },
799
+ 5: { name: 'keyword.operator.assignment.mira' },
800
+ },
801
+ end: '$',
802
+ patterns: [{ include: '#doc-value' }],
803
+ },
804
+ ],
805
+ },
758
806
  'function-signatures': {
759
807
  patterns: [
760
808
  {
761
- match: String.raw`(?<!\p{XID_Continue})(?:(pub)(\s+))?(fn)(\s+)(${IDENTIFIER})(?=\s*(?:<|\())`,
809
+ match: String.raw`${IDENTIFIER_START}(?:(pub)(\s+))?(fn)(\s+)(${IDENTIFIER})(?=\s*(?:<|\())`,
762
810
  captures: {
763
811
  1: { name: 'keyword.control.module.mira' },
764
812
  3: { name: 'keyword.declaration.function.mira' },
@@ -770,22 +818,65 @@ export function createMiraScriptDocGrammar(): LanguageRegistration {
770
818
  properties: {
771
819
  patterns: [
772
820
  {
773
- begin: String.raw`(${IDENTIFIER})(\s*)(\??:)`,
821
+ name: 'meta.documentation.field.mira',
822
+ begin: String.raw`^(\x00)?(?:(\(field\))(\s+))?(${DOC_FIELD_NAME})(\s*)(\??:)(?=[ \t]*/\*[ \t]*<extern[ \t]+(?:(?:async[ \t]+)?function\*?)(?:[ \t]+${DOC_TAG_NAME})?>[ \t]*\*/)`,
823
+ beginCaptures: {
824
+ 2: { name: 'entity.name.label.mira' },
825
+ 4: { name: 'entity.name.function.mira' },
826
+ 6: { name: 'punctuation.separator.type.mira' },
827
+ },
828
+ end: '$',
829
+ patterns: [
830
+ { include: '#tag-comment' },
831
+ { include: '#tag' },
832
+ { include: '#doc-record-value' },
833
+ { include: '#doc-array-value' },
834
+ { include: '#type-context' },
835
+ ],
836
+ },
837
+ {
838
+ name: 'meta.documentation.field.mira',
839
+ begin: String.raw`^(\x00)?(?:(\(field\))(\s+))?(${DOC_FIELD_NAME})(\s*)(\??:)(?=[ \t]*/\*[ \t]*<extern[ \t]+class(?:[ \t]+${DOC_TAG_NAME})?>[ \t]*\*/)`,
774
840
  beginCaptures: {
775
- 1: { name: 'variable.other.property.mira' },
776
- 3: { name: 'punctuation.separator.type.mira' },
841
+ 2: { name: 'entity.name.label.mira' },
842
+ 4: { name: 'entity.name.type.mira' },
843
+ 6: { name: 'punctuation.separator.type.mira' },
777
844
  },
778
845
  end: '$',
779
- patterns: [{ include: '#metadata' }, { include: '#type-context' }],
846
+ patterns: [
847
+ { include: '#tag-comment' },
848
+ { include: '#tag' },
849
+ { include: '#doc-record-value' },
850
+ { include: '#doc-array-value' },
851
+ { include: '#type-context' },
852
+ ],
853
+ },
854
+ {
855
+ name: 'meta.documentation.field.mira',
856
+ begin: String.raw`^(\x00)?(?:(\(field\))(\s+))?(${DOC_FIELD_NAME})(\s*)(\??:)`,
857
+ beginCaptures: {
858
+ 2: { name: 'entity.name.label.mira' },
859
+ 4: { name: 'variable.other.property.mira' },
860
+ 6: { name: 'punctuation.separator.type.mira' },
861
+ },
862
+ end: '$',
863
+ patterns: [
864
+ { include: '#tag-comment' },
865
+ { include: '#tag' },
866
+ { include: '#doc-record-value' },
867
+ { include: '#doc-array-value' },
868
+ { include: '#type-context' },
869
+ ],
780
870
  },
781
871
  ],
782
872
  },
783
873
  'doc-value': {
784
874
  patterns: [
785
- { include: '#extern-function-properties' },
786
- { include: '#extern-class-properties' },
787
- { include: '#metadata' },
875
+ { include: '#tag-properties' },
876
+ { include: '#tag-comment' },
877
+ { include: '#tag' },
788
878
  { include: '#doc-record-value' },
879
+ { include: '#doc-array-value' },
789
880
  { include: 'source.mira' },
790
881
  ],
791
882
  },
@@ -800,21 +891,28 @@ export function createMiraScriptDocGrammar(): LanguageRegistration {
800
891
  },
801
892
  ],
802
893
  },
803
- 'extern-function-properties': {
894
+ 'doc-array-value': {
895
+ patterns: [
896
+ {
897
+ begin: String.raw`\[`,
898
+ beginCaptures: { 0: { name: 'punctuation.section.brackets.begin.mira' } },
899
+ end: String.raw`\]`,
900
+ endCaptures: { 0: { name: 'punctuation.section.brackets.end.mira' } },
901
+ patterns: [{ include: '#doc-value' }],
902
+ },
903
+ ],
904
+ },
905
+ 'tag-properties': {
804
906
  patterns: [
805
907
  {
806
- match: String.raw`(${IDENTIFIER})(\s*)(:)(?=\s*/\*\s*<\s*extern\s+(?:(?:async\s+)?function\*?)(?:\s+${IDENTIFIER})?\s*>\s*\*/)`,
908
+ match: String.raw`(${DOC_FIELD_NAME})(\s*)(:)(?=[ \t]*/\*[ \t]*<extern[ \t]+(?:(?:async[ \t]+)?function\*?)(?:[ \t]+${DOC_TAG_NAME})?>[ \t]*\*/)`,
807
909
  captures: {
808
910
  1: { name: 'entity.name.function.mira' },
809
911
  3: { name: 'punctuation.separator.key-value.mira' },
810
912
  },
811
913
  },
812
- ],
813
- },
814
- 'extern-class-properties': {
815
- patterns: [
816
914
  {
817
- match: String.raw`(${IDENTIFIER})(\s*)(:)(?=\s*/\*\s*<\s*extern\s+class(?:\s+${IDENTIFIER})?\s*>\s*\*/)`,
915
+ match: String.raw`(${DOC_FIELD_NAME})(\s*)(:)(?=[ \t]*/\*[ \t]*<extern[ \t]+class(?:[ \t]+${DOC_TAG_NAME})?>[ \t]*\*/)`,
818
916
  captures: {
819
917
  1: { name: 'entity.name.type.mira' },
820
918
  3: { name: 'punctuation.separator.key-value.mira' },
@@ -822,54 +920,84 @@ export function createMiraScriptDocGrammar(): LanguageRegistration {
822
920
  },
823
921
  ],
824
922
  },
825
- metadata: {
923
+ 'tag-comment': {
826
924
  patterns: [
827
925
  {
828
926
  name: 'comment.block.mira',
829
- contentName: 'meta.documentation.type-hint.mira',
830
- begin: String.raw`/\*\s*<`,
831
- beginCaptures: { 0: { name: 'punctuation.definition.comment.begin.mira' } },
832
- end: String.raw`>\s*\*/`,
833
- endCaptures: { 0: { name: 'punctuation.definition.comment.end.mira' } },
834
- patterns: [
835
- {
836
- match: String.raw`(?<!\p{XID_Continue})(extern)(\s+)(?:(async)(\s+))?(function)(\*)?(?:\s+(${IDENTIFIER}))?`,
837
- captures: {
838
- 1: { name: 'storage.modifier.extern.mira' },
839
- 3: { name: 'storage.modifier.async.mira' },
840
- 5: { name: 'keyword.declaration.function.mira' },
841
- 6: { name: 'keyword.operator.generator.mira' },
842
- 7: { name: 'entity.name.function.mira' },
843
- },
844
- },
845
- {
846
- match: String.raw`(?<!\p{XID_Continue})(extern)(\s+)(class)(?:\s+(${IDENTIFIER}))?`,
847
- captures: {
848
- 1: { name: 'storage.modifier.extern.mira' },
849
- 3: { name: 'keyword.declaration.class.mira' },
850
- 4: { name: 'entity.name.type.mira' },
851
- },
852
- },
853
- {
854
- match: String.raw`(?<!\p{XID_Continue})(extern)(?:\s+(${IDENTIFIER}))?`,
855
- captures: {
856
- 1: { name: 'storage.modifier.extern.mira' },
857
- 2: { name: 'entity.name.type.mira' },
858
- },
859
- },
860
- { name: 'entity.name.type.mira', match: IDENTIFIER },
861
- { name: 'constant.numeric.mira', match: String.raw`\d+` },
862
- { name: 'punctuation.separator.type.mira', match: '[().,<>]' },
863
- ],
927
+ contentName: 'meta.documentation.tag.mira',
928
+ begin: String.raw`(/\*)([ \t]*)(<)(?=${DOC_TAG_CONTENT}>[ \t]*\*/)`,
929
+ beginCaptures: {
930
+ 1: { name: 'punctuation.definition.comment.begin.mira' },
931
+ 3: { name: 'punctuation.definition.tag.begin.mira' },
932
+ },
933
+ end: String.raw`(>)([ \t]*)(\*/)`,
934
+ endCaptures: {
935
+ 1: { name: 'punctuation.definition.tag.end.mira' },
936
+ 3: { name: 'punctuation.definition.comment.end.mira' },
937
+ },
938
+ patterns: [{ include: '#tag-content' }],
939
+ },
940
+ ],
941
+ },
942
+ 'tag-content': {
943
+ patterns: [
944
+ {
945
+ match: String.raw`(extern)([ \t]+)(?:(async)([ \t]+))?(function)(\*)?(?:([ \t]+)(${DOC_TAG_NAME}))?(?=>)`,
946
+ captures: {
947
+ 1: { name: 'keyword.declaration.extern.mira' },
948
+ 3: { name: 'keyword.js' },
949
+ 5: { name: 'keyword.js' },
950
+ 6: { name: 'keyword.operator.generator.js' },
951
+ 8: { name: 'entity.name.function.js' },
952
+ },
953
+ },
954
+ {
955
+ match: String.raw`(extern)([ \t]+)(class)(?:([ \t]+)(${DOC_TAG_NAME}))?(?=>)`,
956
+ captures: {
957
+ 1: { name: 'keyword.declaration.extern.mira' },
958
+ 3: { name: 'keyword.js' },
959
+ 5: { name: 'entity.name.type.js' },
960
+ },
961
+ },
962
+ {
963
+ match: String.raw`(extern)([ \t]+)(${DOC_TAG_NAME})(\()(${DOC_TAG_NUMBER})(\))(?=>)`,
964
+ captures: {
965
+ 1: { name: 'keyword.declaration.extern.mira' },
966
+ 3: { name: 'entity.name.type.js' },
967
+ 4: { name: 'punctuation.section.parens.begin.mira' },
968
+ 5: { name: 'constant.numeric.mira' },
969
+ 6: { name: 'punctuation.section.parens.end.mira' },
970
+ },
971
+ },
972
+ {
973
+ match: String.raw`(extern)(?:([ \t]+)(${DOC_TAG_NAME}))?(?=>)`,
974
+ captures: {
975
+ 1: { name: 'keyword.declaration.extern.mira' },
976
+ 3: { name: 'entity.name.type.js' },
977
+ },
978
+ },
979
+ {
980
+ match: String.raw`(module)(?:([ \t]+)(${DOC_TAG_NAME}))?(?=>)`,
981
+ captures: {
982
+ 1: { name: 'keyword.declaration.module.mira' },
983
+ 3: { name: 'entity.name.namespace.mira' },
984
+ },
985
+ },
986
+ {
987
+ match: String.raw`(function)(?:([ \t]+)(${DOC_TAG_NAME}))?(?=>)`,
988
+ captures: {
989
+ 1: { name: 'keyword.declaration.function.mira' },
990
+ 3: { name: 'entity.name.function.mira' },
991
+ },
864
992
  },
865
993
  ],
866
994
  },
867
995
  'reflection-type': {
868
996
  patterns: [
869
997
  {
870
- match: String.raw`(?<!\p{XID_Continue})(type)(\s*)(\()(\s*)(${IDENTIFIER})(\s*)(\))`,
998
+ match: String.raw`${IDENTIFIER_START}(type)(\s*)(\()(\s*)(${IDENTIFIER})(\s*)(\))`,
871
999
  captures: {
872
- 1: { name: 'keyword.operator.expression.mira' },
1000
+ 1: { name: 'support.type.type.mira' },
873
1001
  3: { name: 'punctuation.section.parens.begin.mira' },
874
1002
  5: { name: 'variable.other.mira' },
875
1003
  7: { name: 'punctuation.section.parens.end.mira' },
@@ -890,7 +1018,7 @@ export function createMiraScriptDocGrammar(): LanguageRegistration {
890
1018
  'function-type': {
891
1019
  patterns: [
892
1020
  {
893
- begin: String.raw`(?<!\p{XID_Continue})(fn)${IDENTIFIER_END}(?=\s*(?:<|\())`,
1021
+ begin: String.raw`${IDENTIFIER_START}(fn)${IDENTIFIER_END}(?=\s*(?:<|\())`,
894
1022
  beginCaptures: { 1: { name: 'support.type.function.mira' } },
895
1023
  end: String.raw`(?=\s*(?:,|\)|$))`,
896
1024
  patterns: [
@@ -1013,11 +1141,7 @@ export function createMiraScriptDocGrammar(): LanguageRegistration {
1013
1141
  { include: '#reflection-type' },
1014
1142
  {
1015
1143
  name: 'support.type.builtin.mira',
1016
- match: String.raw`(?<!\p{XID_Continue})(?:${alternatives(BUILT_IN_TYPES)})${IDENTIFIER_END}`,
1017
- },
1018
- {
1019
- name: 'constant.language.boolean.mira',
1020
- match: String.raw`(?<!\p{XID_Continue})(?:true|false)${IDENTIFIER_END}`,
1144
+ match: `${IDENTIFIER_START}(?:${alternatives(BUILT_IN_TYPES)})${IDENTIFIER_END}`,
1021
1145
  },
1022
1146
  { name: 'keyword.operator.type.mira', match: '[&|]' },
1023
1147
  { name: 'keyword.operator.spread.mira', match: String.raw`\.\.` },
package/src/index.ts CHANGED
@@ -7,4 +7,4 @@ export const mirascriptDoc = createMiraScriptDocGrammar();
7
7
 
8
8
  export { mirascriptTemplate as 'mirascript-template', mirascriptDoc as 'mirascript-doc' };
9
9
 
10
- export const grammars: readonly LanguageRegistration[] = Object.freeze([mirascript, mirascriptTemplate, mirascriptDoc]);
10
+ export const grammars: LanguageRegistration[] = [mirascript, mirascriptTemplate, mirascriptDoc];
@@ -0,0 +1,53 @@
1
+ import { createHighlighter, type Highlighter } from 'shiki';
2
+ import { INITIAL, type StateStack } from 'shiki/textmate';
3
+ import test, { type ExecutionContext } from 'ava';
4
+ import { grammars } from '../src/index.ts';
5
+
6
+ let highlighter: Highlighter;
7
+
8
+ test.before(async () => {
9
+ highlighter = await createHighlighter({
10
+ langs: grammars,
11
+ themes: [],
12
+ });
13
+ });
14
+
15
+ test.after.always(() => highlighter.dispose());
16
+
17
+ /**
18
+ * Tokenize complete source while preserving TextMate state across lines.
19
+ */
20
+ export function tokenize(
21
+ code: string,
22
+ language = 'mirascript',
23
+ ): Array<{ line: number; text: string; scopes: string[] }> {
24
+ const grammar = highlighter.getLanguage(language);
25
+ let state: StateStack = INITIAL;
26
+ return code.split('\n').flatMap((line, lineIndex) => {
27
+ const result = grammar.tokenizeLine(line, state);
28
+ state = result.ruleStack;
29
+ return result.tokens.map((token) => ({
30
+ line: lineIndex,
31
+ text: line.slice(token.startIndex, token.endIndex),
32
+ scopes: token.scopes,
33
+ }));
34
+ });
35
+ }
36
+
37
+ /**
38
+ * Assert that a selected textual token contains the expected scope.
39
+ */
40
+ export function expectScope(
41
+ t: ExecutionContext,
42
+ tokens: Array<{ text: string; scopes: string[] }>,
43
+ text: string,
44
+ scope: string,
45
+ occurrence = 0,
46
+ ): void {
47
+ const matching = tokens.filter((token) => token.text === text);
48
+ t.true(matching.length > occurrence, `Missing token ${JSON.stringify(text)} #${occurrence}`);
49
+ t.true(
50
+ matching[occurrence].scopes.includes(scope),
51
+ `${JSON.stringify(text)} should include ${scope}; got ${matching[occurrence].scopes.join(', ')}`,
52
+ );
53
+ }