@mirascript/textmate 0.1.81 → 0.1.83

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 ADDED
@@ -0,0 +1,1033 @@
1
+ import type { LanguageRegistration } from '@shikijs/types';
2
+ import {
3
+ CONSTANT_KEYWORDS,
4
+ CONTROL_KEYWORDS,
5
+ KEYWORDS,
6
+ NUMERIC_KEYWORDS,
7
+ REG_IDENTIFIER,
8
+ RESERVED_KEYWORDS,
9
+ } from '@mirascript/constants';
10
+ import { mirascriptDocLanguage, mirascriptLanguage, mirascriptTemplateLanguage } from './language.ts';
11
+
12
+ const SCOPE_SUFFIX = 'mira';
13
+ const IDENTIFIER = REG_IDENTIFIER.source;
14
+ const IDENTIFIER_END = String.raw`(?!\p{XID_Continue})`;
15
+ const MAX_VERBATIM_LENGTH = 16;
16
+ const BUILT_IN_TYPES = [
17
+ 'any',
18
+ 'unknown',
19
+ 'never',
20
+ 'boolean',
21
+ 'number',
22
+ 'string',
23
+ 'record',
24
+ 'array',
25
+ 'extern',
26
+ 'module',
27
+ 'nil',
28
+ ];
29
+ const DOC_CONSTANT_IDENTIFIER = String.raw`(?:@+\p{XID_Continue}+|\p{Lu}[\p{XID_Continue}]*)`;
30
+
31
+ /**
32
+ * Escape a literal value for insertion into an Oniguruma regular expression.
33
+ */
34
+ function escapeRegex(value: string): string {
35
+ return value.replaceAll(/[\\^$.*+?()[\]{}|]/g, String.raw`\$&`).replaceAll(/\s+/g, String.raw`\s+`);
36
+ }
37
+
38
+ /**
39
+ * Build a longest-first regular-expression alternation.
40
+ */
41
+ function alternatives(values: Iterable<string>): string {
42
+ return [...values]
43
+ .sort((a, b) => b.length - a.length)
44
+ .map(escapeRegex)
45
+ .join('|');
46
+ }
47
+
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
+ const numericKeywordSet = new Set<string>(NUMERIC_KEYWORDS);
60
+ const constantKeywords = CONSTANT_KEYWORDS.filter((keyword) => !numericKeywordSet.has(keyword));
61
+ const languageVariables = ['_', 'global'];
62
+ const wordOperators = ['in', 'is', 'and', 'or', 'not'];
63
+ const declarationKeywords = ['fn', 'op', 'let', 'const', 'mut', 'where'];
64
+ const moduleKeywords = ['mod', 'pub', 'use'];
65
+ const classifiedKeywords = new Set([
66
+ ...CONSTANT_KEYWORDS,
67
+ ...CONTROL_KEYWORDS,
68
+ ...languageVariables,
69
+ ...wordOperators,
70
+ ...declarationKeywords,
71
+ ...moduleKeywords,
72
+ ...RESERVED_KEYWORDS,
73
+ 'type',
74
+ ]);
75
+ const otherKeywords = KEYWORDS.filter((keyword) => !classifiedKeywords.has(keyword));
76
+ const mirascriptFenceTags = alternatives([mirascriptLanguage.name, ...(mirascriptLanguage.aliases ?? [])]);
77
+ const documentationMiraFenceBegin = String.raw`^(\s*\*\s?)(\x60{3,})\s*((?i:${mirascriptFenceTags}))?\s*$`;
78
+ const documentationOtherFenceBegin = String.raw`^(\s*\*\s?)(\x60{3,})\s*(\S+)(?:\s+.*)?\s*$`;
79
+ const documentationFenceEnd = String.raw`^(?:(\s*\*\s?)(\2\x60*)\s*$)|(?=\*/)`;
80
+ const documentationMiraLine = String.raw`^(?!\s*\*\s*\x60{3,}\s*$)(?!.*\*/)(\s*\*\s?)`;
81
+ const documentationInjectionSelector =
82
+ 'L:comment.block.documentation.mira -markup.fenced_code.block.mira -meta.documentation.inline.mira';
83
+
84
+ /**
85
+ * Create interpolation rules for one exact dollar-sign width.
86
+ */
87
+ function interpolationPatterns(dollarCount: number) {
88
+ const dollars = String.raw`\$`.repeat(dollarCount);
89
+ return [
90
+ {
91
+ name: `meta.interpolation.simple.${SCOPE_SUFFIX}`,
92
+ match: `(${dollars})(${IDENTIFIER})`,
93
+ captures: {
94
+ 1: { name: `punctuation.definition.template-expression.begin.${SCOPE_SUFFIX}` },
95
+ 2: { name: `variable.other.${SCOPE_SUFFIX}` },
96
+ },
97
+ },
98
+ {
99
+ name: `meta.interpolation.block.${SCOPE_SUFFIX}`,
100
+ begin: String.raw`(${dollars}\{)`,
101
+ beginCaptures: { 1: { name: `punctuation.definition.template-expression.begin.${SCOPE_SUFFIX}` } },
102
+ end: String.raw`\}`,
103
+ endCaptures: { 0: { name: `punctuation.definition.template-expression.end.${SCOPE_SUFFIX}` } },
104
+ patterns: [{ include: '#braced-content' }],
105
+ },
106
+ {
107
+ name: `meta.interpolation.expression.${SCOPE_SUFFIX}`,
108
+ begin: String.raw`(${dollars}\()`,
109
+ beginCaptures: { 1: { name: `punctuation.definition.template-expression.begin.${SCOPE_SUFFIX}` } },
110
+ end: String.raw`\)`,
111
+ endCaptures: { 0: { name: `punctuation.definition.template-expression.end.${SCOPE_SUFFIX}` } },
112
+ patterns: [
113
+ {
114
+ begin: ':(?!:)',
115
+ beginCaptures: { 0: { name: `punctuation.separator.format.${SCOPE_SUFFIX}` } },
116
+ end: String.raw`(?=\))`,
117
+ contentName: `string.unquoted.format.${SCOPE_SUFFIX}`,
118
+ patterns: [{ include: '#format-content' }],
119
+ },
120
+ { include: '#interpolation-expression-content' },
121
+ ],
122
+ },
123
+ ];
124
+ }
125
+
126
+ /**
127
+ * Create a quoted-string rule with escapes and single-dollar interpolation.
128
+ */
129
+ function normalString(quote: string, label: string) {
130
+ const escapedQuote = escapeRegex(quote);
131
+ return {
132
+ name: `string.quoted.${label}.${SCOPE_SUFFIX}`,
133
+ begin: escapedQuote,
134
+ beginCaptures: { 0: { name: `punctuation.definition.string.begin.${SCOPE_SUFFIX}` } },
135
+ end: escapedQuote,
136
+ endCaptures: { 0: { name: `punctuation.definition.string.end.${SCOPE_SUFFIX}` } },
137
+ patterns: [
138
+ { name: `constant.character.escape.unicode.${SCOPE_SUFFIX}`, match: String.raw`\\u\{[0-9A-Fa-f]+\}` },
139
+ { name: `constant.character.escape.hex.${SCOPE_SUFFIX}`, match: String.raw`\\x[0-9A-Fa-f]{2}` },
140
+ { name: `constant.character.escape.${SCOPE_SUFFIX}`, match: '\\\\[\\\\\'"\\`$rntbfv0]' },
141
+ { name: `invalid.illegal.escape.${SCOPE_SUFFIX}`, match: String.raw`\\.` },
142
+ ...interpolationPatterns(1),
143
+ ],
144
+ };
145
+ }
146
+
147
+ /**
148
+ * Create an exact-width MiraScript verbatim-string rule.
149
+ */
150
+ function verbatimString(atCount: number, quote: string, label: string) {
151
+ const ats = '@'.repeat(atCount);
152
+ const escapedQuote = escapeRegex(quote);
153
+ return {
154
+ name: `string.quoted.${label}.verbatim.${SCOPE_SUFFIX}`,
155
+ begin: `(?<!@)${ats}${escapedQuote}`,
156
+ beginCaptures: { 0: { name: `punctuation.definition.string.begin.${SCOPE_SUFFIX}` } },
157
+ end: `${escapedQuote}${ats}(?!@)`,
158
+ endCaptures: { 0: { name: `punctuation.definition.string.end.${SCOPE_SUFFIX}` } },
159
+ patterns: interpolationPatterns(atCount),
160
+ };
161
+ }
162
+
163
+ /**
164
+ * Build normal and 1-16 marker verbatim-string rules for every quote style.
165
+ */
166
+ function stringPatterns() {
167
+ const quotes = [
168
+ ['"', 'double'],
169
+ ["'", 'single'],
170
+ ['`', 'backtick'],
171
+ ] as const;
172
+ return [
173
+ ...Array.from({ length: MAX_VERBATIM_LENGTH }, (_, index) => MAX_VERBATIM_LENGTH - index).flatMap((atCount) =>
174
+ quotes.map(([quote, label]) => verbatimString(atCount, quote, label)),
175
+ ),
176
+ ...quotes.map(([quote, label]) => normalString(quote, label)),
177
+ ];
178
+ }
179
+
180
+ /**
181
+ * Build the documentation-comment rules shared by source and doc grammars.
182
+ */
183
+ function documentationRepository(sourceInclude = '#source') {
184
+ return {
185
+ documentation: {
186
+ patterns: [
187
+ { include: '#documentation-mirascript-fence' },
188
+ { include: '#documentation-other-fence' },
189
+ { include: '#documentation-line' },
190
+ { include: '#documentation-fragment' },
191
+ ],
192
+ },
193
+ 'documentation-inline': {
194
+ patterns: [
195
+ {
196
+ name: `storage.type.class.documentation.${SCOPE_SUFFIX}`,
197
+ match: String.raw`@(param|returns)\b`,
198
+ },
199
+ {
200
+ name: `markup.bold.documentation.${SCOPE_SUFFIX}`,
201
+ match: String.raw`\*\*[^*]+\*\*`,
202
+ },
203
+ { name: `markup.italic.documentation.${SCOPE_SUFFIX}`, match: String.raw`\*[^*]+\*` },
204
+ { name: `constant.character.escape.documentation.${SCOPE_SUFFIX}`, match: String.raw`\\\*` },
205
+ ],
206
+ },
207
+ 'documentation-line': {
208
+ patterns: [
209
+ {
210
+ match: String.raw`^(?!\s*\*/)(\s*\*\s?)(.*?)(?=\*/|$)`,
211
+ captures: {
212
+ 1: { name: `comment.block.documentation.${SCOPE_SUFFIX}` },
213
+ 2: {
214
+ name: `meta.documentation.inline.${SCOPE_SUFFIX}`,
215
+ patterns: [{ include: '#documentation-inline' }],
216
+ },
217
+ },
218
+ },
219
+ ],
220
+ },
221
+ 'documentation-fragment': {
222
+ patterns: [
223
+ {
224
+ match: String.raw`(?!\*/)(.+?)(?=\*/|$)`,
225
+ captures: {
226
+ 1: {
227
+ name: `meta.documentation.inline.${SCOPE_SUFFIX}`,
228
+ patterns: [{ include: '#documentation-inline' }],
229
+ },
230
+ },
231
+ },
232
+ ],
233
+ },
234
+ 'documentation-mirascript-fence': {
235
+ patterns: [
236
+ {
237
+ name: `markup.fenced_code.block.${SCOPE_SUFFIX}`,
238
+ begin: documentationMiraFenceBegin,
239
+ beginCaptures: {
240
+ 1: { name: `comment.block.documentation.${SCOPE_SUFFIX}` },
241
+ 2: { name: `punctuation.definition.markdown.${SCOPE_SUFFIX}` },
242
+ 3: { name: `fenced_code.block.language.${SCOPE_SUFFIX}` },
243
+ },
244
+ end: documentationFenceEnd,
245
+ endCaptures: {
246
+ 1: { name: `comment.block.documentation.${SCOPE_SUFFIX}` },
247
+ 2: { name: `punctuation.definition.markdown.${SCOPE_SUFFIX}` },
248
+ },
249
+ patterns: [
250
+ {
251
+ name: `meta.embedded.block.${SCOPE_SUFFIX}`,
252
+ contentName: `source.${SCOPE_SUFFIX}`,
253
+ begin: documentationMiraLine,
254
+ beginCaptures: {
255
+ 1: { name: `comment.block.documentation.${SCOPE_SUFFIX}` },
256
+ },
257
+ while: documentationMiraLine,
258
+ whileCaptures: {
259
+ 1: { name: `comment.block.documentation.${SCOPE_SUFFIX}` },
260
+ },
261
+ patterns: [{ include: sourceInclude }],
262
+ },
263
+ {
264
+ name: `comment.block.documentation.${SCOPE_SUFFIX}`,
265
+ match: String.raw`^(?!\s*\*/)(\s*\*\s?)`,
266
+ },
267
+ ],
268
+ },
269
+ ],
270
+ },
271
+ 'documentation-other-fence': {
272
+ patterns: [
273
+ {
274
+ name: `markup.fenced_code.block.${SCOPE_SUFFIX}`,
275
+ contentName: `markup.raw.block.${SCOPE_SUFFIX}`,
276
+ begin: documentationOtherFenceBegin,
277
+ beginCaptures: {
278
+ 1: { name: `comment.block.documentation.${SCOPE_SUFFIX}` },
279
+ 2: { name: `punctuation.definition.markdown.${SCOPE_SUFFIX}` },
280
+ 3: { name: `fenced_code.block.language.${SCOPE_SUFFIX}` },
281
+ },
282
+ end: documentationFenceEnd,
283
+ endCaptures: {
284
+ 1: { name: `comment.block.documentation.${SCOPE_SUFFIX}` },
285
+ 2: { name: `punctuation.definition.markdown.${SCOPE_SUFFIX}` },
286
+ },
287
+ patterns: [
288
+ {
289
+ name: `comment.block.documentation.${SCOPE_SUFFIX}`,
290
+ match: String.raw`^(?!\s*\*/)(\s*\*\s?)`,
291
+ },
292
+ ],
293
+ },
294
+ ],
295
+ },
296
+ };
297
+ }
298
+
299
+ /**
300
+ * Build the shared repository used by source and template grammars.
301
+ */
302
+ function sourceRepository(): LanguageRegistration['repository'] {
303
+ const keywordPatterns = [
304
+ keywordRule(NUMERIC_KEYWORDS, 'constant.numeric.language'),
305
+ keywordRule(constantKeywords, 'constant.language'),
306
+ keywordRule(CONTROL_KEYWORDS, 'keyword.control'),
307
+ keywordRule(wordOperators, 'keyword.operator.wordlike'),
308
+ keywordRule(declarationKeywords, 'keyword.declaration'),
309
+ keywordRule(moduleKeywords, 'keyword.control.module'),
310
+ keywordRule(RESERVED_KEYWORDS, 'keyword.reserved'),
311
+ keywordRule(languageVariables, 'variable.language'),
312
+ keywordRule(otherKeywords, 'keyword.other'),
313
+ ].filter((p) => p != null);
314
+
315
+ return {
316
+ source: {
317
+ patterns: [
318
+ { include: '#comments' },
319
+ { include: '#strings' },
320
+ { include: '#function-declarations' },
321
+ { include: '#module-declarations' },
322
+ { include: '#for-bindings' },
323
+ { include: '#record-properties' },
324
+ { include: '#member-access' },
325
+ { include: '#type-keyword' },
326
+ { include: '#keywords' },
327
+ { include: '#function-calls' },
328
+ { include: '#numbers' },
329
+ { include: '#identifiers' },
330
+ { include: '#operators' },
331
+ { include: '#punctuation' },
332
+ ],
333
+ },
334
+ comments: {
335
+ patterns: [
336
+ { name: `comment.line.double-slash.${SCOPE_SUFFIX}`, match: '//.*$' },
337
+ {
338
+ name: `comment.block.documentation.${SCOPE_SUFFIX}`,
339
+ begin: String.raw`/\*\*`,
340
+ end: String.raw`\*/`,
341
+ },
342
+ { name: `comment.block.${SCOPE_SUFFIX}`, begin: String.raw`/\*`, end: String.raw`\*/` },
343
+ ],
344
+ },
345
+ ...documentationRepository(),
346
+ 'format-content': {
347
+ patterns: [
348
+ { name: `constant.character.escape.format.${SCOPE_SUFFIX}`, match: String.raw`\\.` },
349
+ {
350
+ begin: String.raw`\[`,
351
+ end: String.raw`\]`,
352
+ name: `string.unquoted.format.character-class.${SCOPE_SUFFIX}`,
353
+ patterns: [{ name: `constant.character.escape.format.${SCOPE_SUFFIX}`, match: String.raw`\\.` }],
354
+ },
355
+ {
356
+ begin: String.raw`\(`,
357
+ end: String.raw`\)`,
358
+ name: `string.unquoted.format.group.${SCOPE_SUFFIX}`,
359
+ patterns: [{ include: '#format-content' }],
360
+ },
361
+ ],
362
+ },
363
+ strings: { patterns: stringPatterns() },
364
+ 'function-declarations': {
365
+ patterns: [
366
+ {
367
+ name: `meta.function.declaration.${SCOPE_SUFFIX}`,
368
+ begin: String.raw`(?<!\p{XID_Continue})(fn)(\s+)(${IDENTIFIER})(\s*)(\()`,
369
+ beginCaptures: {
370
+ 1: { name: `keyword.declaration.function.${SCOPE_SUFFIX}` },
371
+ 3: { name: `entity.name.function.${SCOPE_SUFFIX}` },
372
+ 5: { name: `punctuation.section.parameters.begin.${SCOPE_SUFFIX}` },
373
+ },
374
+ end: String.raw`\)`,
375
+ endCaptures: { 0: { name: `punctuation.section.parameters.end.${SCOPE_SUFFIX}` } },
376
+ patterns: [{ include: '#parameters' }],
377
+ },
378
+ {
379
+ name: `meta.function.declaration.${SCOPE_SUFFIX}`,
380
+ match: String.raw`(?<!\p{XID_Continue})(fn)(\s+)(${IDENTIFIER})`,
381
+ captures: {
382
+ 1: { name: `keyword.declaration.function.${SCOPE_SUFFIX}` },
383
+ 3: { name: `entity.name.function.${SCOPE_SUFFIX}` },
384
+ },
385
+ },
386
+ {
387
+ name: `meta.function.expression.parameters.${SCOPE_SUFFIX}`,
388
+ begin: String.raw`(?<!\p{XID_Continue})(fn)(\s*)(\()`,
389
+ beginCaptures: {
390
+ 1: { name: `keyword.declaration.function.${SCOPE_SUFFIX}` },
391
+ 3: { name: `punctuation.section.parameters.begin.${SCOPE_SUFFIX}` },
392
+ },
393
+ end: String.raw`\)`,
394
+ endCaptures: { 0: { name: `punctuation.section.parameters.end.${SCOPE_SUFFIX}` } },
395
+ patterns: [{ include: '#parameters' }],
396
+ },
397
+ ],
398
+ },
399
+ parameters: {
400
+ patterns: [
401
+ {
402
+ match: String.raw`(?<!\p{XID_Continue})(mut)(\s+)(${IDENTIFIER})`,
403
+ captures: {
404
+ 1: { name: `keyword.declaration.mutable.${SCOPE_SUFFIX}` },
405
+ 3: { name: `variable.emphasis.${SCOPE_SUFFIX}` },
406
+ },
407
+ },
408
+ {
409
+ name: `keyword.declaration.mutable.${SCOPE_SUFFIX}`,
410
+ match: String.raw`(?<!\p{XID_Continue})mut${IDENTIFIER_END}`,
411
+ },
412
+ { name: `keyword.operator.spread.${SCOPE_SUFFIX}`, match: String.raw`\.\.` },
413
+ { name: `variable.other.constant.emphasis.${SCOPE_SUFFIX}`, match: IDENTIFIER },
414
+ { name: `punctuation.separator.parameter.${SCOPE_SUFFIX}`, match: ',' },
415
+ {
416
+ begin: String.raw`\(`,
417
+ end: String.raw`\)`,
418
+ patterns: [{ include: '#parameters' }],
419
+ },
420
+ {
421
+ begin: String.raw`\[`,
422
+ end: String.raw`\]`,
423
+ patterns: [{ include: '#parameters' }],
424
+ },
425
+ { include: '#record-properties' },
426
+ ],
427
+ },
428
+ 'module-declarations': {
429
+ patterns: [
430
+ {
431
+ match: String.raw`(?<!\p{XID_Continue})(mod)(\s+)(${IDENTIFIER})`,
432
+ captures: {
433
+ 1: { name: `keyword.control.module.${SCOPE_SUFFIX}` },
434
+ 3: { name: `entity.name.namespace.${SCOPE_SUFFIX}` },
435
+ },
436
+ },
437
+ ],
438
+ },
439
+ 'for-bindings': {
440
+ patterns: [
441
+ {
442
+ match: String.raw`(?<!\p{XID_Continue})(for)(\s+)(mut)(\s+)(${IDENTIFIER})(\s+)(in)${IDENTIFIER_END}`,
443
+ captures: {
444
+ 1: { name: `keyword.control.loop.${SCOPE_SUFFIX}` },
445
+ 3: { name: `keyword.declaration.mutable.${SCOPE_SUFFIX}` },
446
+ 5: { name: `variable.other.readwrite.${SCOPE_SUFFIX}` },
447
+ 7: { name: `keyword.control.loop.${SCOPE_SUFFIX}` },
448
+ },
449
+ },
450
+ {
451
+ match: String.raw`(?<!\p{XID_Continue})(for)(\s+)(${IDENTIFIER})(\s+)(in)${IDENTIFIER_END}`,
452
+ captures: {
453
+ 1: { name: `keyword.control.loop.${SCOPE_SUFFIX}` },
454
+ 3: { name: `variable.other.${SCOPE_SUFFIX}` },
455
+ 5: { name: `keyword.control.loop.${SCOPE_SUFFIX}` },
456
+ },
457
+ },
458
+ ],
459
+ },
460
+ 'record-properties': {
461
+ patterns: [
462
+ {
463
+ match: String.raw`(${IDENTIFIER})(\s*)(\??:)(?!:)`,
464
+ captures: {
465
+ 1: { name: `variable.other.property.${SCOPE_SUFFIX}` },
466
+ 3: { name: `punctuation.separator.key-value.${SCOPE_SUFFIX}` },
467
+ },
468
+ },
469
+ ],
470
+ },
471
+ 'member-access': {
472
+ patterns: [
473
+ {
474
+ match: String.raw`(\.)(\s*)(\d+)`,
475
+ captures: {
476
+ 1: { name: `punctuation.accessor.${SCOPE_SUFFIX}` },
477
+ 3: { name: `variable.other.property.${SCOPE_SUFFIX}` },
478
+ },
479
+ },
480
+ {
481
+ match: `(\\.)(\\s*)(${IDENTIFIER})(\\s*)(!?)(?=\\s*(?:\\(|@*["'\`]))`,
482
+ captures: {
483
+ 1: { name: `punctuation.accessor.${SCOPE_SUFFIX}` },
484
+ 3: { name: `entity.name.function.member.${SCOPE_SUFFIX}` },
485
+ 5: { name: `keyword.operator.non-null.${SCOPE_SUFFIX}` },
486
+ },
487
+ },
488
+ {
489
+ match: String.raw`(\.)(\s*)(${IDENTIFIER})`,
490
+ captures: {
491
+ 1: { name: `punctuation.accessor.${SCOPE_SUFFIX}` },
492
+ 3: { name: `variable.other.property.${SCOPE_SUFFIX}` },
493
+ },
494
+ },
495
+ ],
496
+ },
497
+ 'function-calls': {
498
+ patterns: [
499
+ {
500
+ match: `(${IDENTIFIER})(\\s*)(!?)(?=\\s*(?:\\(|@*["'\`]))`,
501
+ captures: {
502
+ 1: { name: `entity.name.function.${SCOPE_SUFFIX}` },
503
+ 3: { name: `keyword.operator.non-null.${SCOPE_SUFFIX}` },
504
+ },
505
+ },
506
+ ],
507
+ },
508
+ 'type-keyword': {
509
+ patterns: [
510
+ {
511
+ name: `keyword.operator.expression.${SCOPE_SUFFIX}`,
512
+ match: String.raw`(?<!\p{XID_Continue})type${IDENTIFIER_END}(?=\s*\(|\s+${IDENTIFIER})`,
513
+ },
514
+ ],
515
+ },
516
+ numbers: {
517
+ patterns: [
518
+ {
519
+ name: `constant.numeric.hex.${SCOPE_SUFFIX}`,
520
+ match: `0[xX][0-9A-Fa-f](?:[0-9A-Fa-f_]*[0-9A-Fa-f])?${IDENTIFIER_END}`,
521
+ },
522
+ {
523
+ name: `constant.numeric.octal.${SCOPE_SUFFIX}`,
524
+ match: `0[oO][0-7](?:[0-7_]*[0-7])?${IDENTIFIER_END}`,
525
+ },
526
+ {
527
+ name: `constant.numeric.binary.${SCOPE_SUFFIX}`,
528
+ match: `0[bB][01](?:[01_]*[01])?${IDENTIFIER_END}`,
529
+ },
530
+ {
531
+ name: `invalid.illegal.numeric.${SCOPE_SUFFIX}`,
532
+ match: String.raw`0[xXoObB]\p{XID_Continue}*`,
533
+ },
534
+ {
535
+ name: `constant.numeric.float.${SCOPE_SUFFIX}`,
536
+ match: String.raw`\d[\d_]*(?:\.[\d_]+)?(?:[eE][+-]?[\d_]*\d)?${IDENTIFIER_END}`,
537
+ },
538
+ ],
539
+ },
540
+ keywords: { patterns: keywordPatterns },
541
+ identifiers: {
542
+ patterns: [
543
+ { name: `variable.other.constant.${SCOPE_SUFFIX}`, match: String.raw`@+\p{XID_Continue}+` },
544
+ { name: `variable.other.${SCOPE_SUFFIX}`, match: IDENTIFIER },
545
+ ],
546
+ },
547
+ operators: {
548
+ patterns: [
549
+ { name: `keyword.operator.spread.${SCOPE_SUFFIX}`, match: String.raw`\.\.<|\.\.` },
550
+ { name: `keyword.operator.bind.${SCOPE_SUFFIX}`, match: '::' },
551
+ { name: `keyword.operator.conditional.${SCOPE_SUFFIX}`, match: String.raw`\?:` },
552
+ {
553
+ name: `keyword.operator.assignment.${SCOPE_SUFFIX}`,
554
+ match: String.raw`&&=|\|\|=|\?\?=|\+=|-=|\*=|/=|%=|\^=|=`,
555
+ },
556
+ {
557
+ name: `keyword.operator.comparison.${SCOPE_SUFFIX}`,
558
+ match: '===|!==|>=|<=|==|!=|=~|!~|>|<',
559
+ },
560
+ { name: `keyword.operator.logical.${SCOPE_SUFFIX}`, match: String.raw`&&|\|\||\?\?|!` },
561
+ { name: `keyword.operator.arithmetic.${SCOPE_SUFFIX}`, match: String.raw`\+|-|\*|/|%|\^` },
562
+ ],
563
+ },
564
+ punctuation: {
565
+ patterns: [
566
+ { name: `punctuation.section.braces.${SCOPE_SUFFIX}`, match: '[{}]' },
567
+ { name: `punctuation.section.brackets.${SCOPE_SUFFIX}`, match: String.raw`[\[\]]` },
568
+ { name: `punctuation.section.parens.${SCOPE_SUFFIX}`, match: '[()]' },
569
+ { name: `punctuation.separator.${SCOPE_SUFFIX}`, match: '[,;:]' },
570
+ { name: `punctuation.accessor.${SCOPE_SUFFIX}`, match: String.raw`\.` },
571
+ ],
572
+ },
573
+ 'braced-content': {
574
+ patterns: [
575
+ { begin: String.raw`\{`, end: String.raw`\}`, patterns: [{ include: '#braced-content' }] },
576
+ { begin: String.raw`\(`, end: String.raw`\)`, patterns: [{ include: '#parenthesized-content' }] },
577
+ { begin: String.raw`\[`, end: String.raw`\]`, patterns: [{ include: '#bracketed-content' }] },
578
+ { include: '#source' },
579
+ ],
580
+ },
581
+ 'interpolation-expression-content': {
582
+ patterns: [
583
+ { begin: String.raw`\(`, end: String.raw`\)`, patterns: [{ include: '#parenthesized-content' }] },
584
+ { begin: String.raw`\{`, end: String.raw`\}`, patterns: [{ include: '#braced-content' }] },
585
+ { begin: String.raw`\[`, end: String.raw`\]`, patterns: [{ include: '#bracketed-content' }] },
586
+ { include: '#comments' },
587
+ { include: '#strings' },
588
+ { include: '#function-declarations' },
589
+ { include: '#module-declarations' },
590
+ { include: '#for-bindings' },
591
+ { include: '#member-access' },
592
+ { include: '#type-keyword' },
593
+ { include: '#keywords' },
594
+ { include: '#function-calls' },
595
+ { include: '#numbers' },
596
+ { include: '#identifiers' },
597
+ { include: '#operators' },
598
+ { include: '#punctuation' },
599
+ ],
600
+ },
601
+ 'parenthesized-content': {
602
+ patterns: [
603
+ { begin: String.raw`\(`, end: String.raw`\)`, patterns: [{ include: '#parenthesized-content' }] },
604
+ { begin: String.raw`\{`, end: String.raw`\}`, patterns: [{ include: '#braced-content' }] },
605
+ { begin: String.raw`\[`, end: String.raw`\]`, patterns: [{ include: '#bracketed-content' }] },
606
+ { include: '#source' },
607
+ ],
608
+ },
609
+ 'bracketed-content': {
610
+ patterns: [
611
+ { begin: String.raw`\[`, end: String.raw`\]`, patterns: [{ include: '#bracketed-content' }] },
612
+ { begin: String.raw`\{`, end: String.raw`\}`, patterns: [{ include: '#braced-content' }] },
613
+ { begin: String.raw`\(`, end: String.raw`\)`, patterns: [{ include: '#parenthesized-content' }] },
614
+ { include: '#source' },
615
+ ],
616
+ },
617
+ };
618
+ }
619
+
620
+ /**
621
+ * Create the ordinary MiraScript TextMate grammar.
622
+ */
623
+ export function createMiraScriptGrammar(): LanguageRegistration {
624
+ return {
625
+ ...mirascriptLanguage,
626
+ patterns: [{ include: '#source' }],
627
+ repository: sourceRepository(),
628
+ injections: {
629
+ [documentationInjectionSelector]: {
630
+ patterns: [{ include: '#documentation' }],
631
+ },
632
+ },
633
+ };
634
+ }
635
+
636
+ /**
637
+ * Create the MiraScript template TextMate grammar.
638
+ */
639
+ export function createMiraScriptTemplateGrammar(): LanguageRegistration {
640
+ return {
641
+ ...mirascriptTemplateLanguage,
642
+ patterns: [
643
+ ...interpolationPatterns(1),
644
+ { name: 'string.unquoted.template.mira', match: '[^$]+' },
645
+ { name: 'string.unquoted.template.mira', match: String.raw`\$` },
646
+ ],
647
+ repository: sourceRepository(),
648
+ };
649
+ }
650
+
651
+ /**
652
+ * Create the generated-document and type-signature TextMate grammar.
653
+ */
654
+ export function createMiraScriptDocGrammar(): LanguageRegistration {
655
+ return {
656
+ ...mirascriptDocLanguage,
657
+ patterns: [{ include: '#doc' }],
658
+ injections: {
659
+ [documentationInjectionSelector]: {
660
+ patterns: [{ include: '#documentation' }],
661
+ },
662
+ },
663
+ repository: {
664
+ ...documentationRepository('source.mira#source'),
665
+ doc: {
666
+ patterns: [
667
+ { include: '#global-value' },
668
+ { include: '#global-constants' },
669
+ { include: '#inline-parameter' },
670
+ { include: '#inline-label' },
671
+ { include: '#bindings' },
672
+ { include: '#function-signatures' },
673
+ { include: '#properties' },
674
+ { include: '#generic-types' },
675
+ { include: '#parameters' },
676
+ { include: '#return-type' },
677
+ { include: '#metadata' },
678
+ { include: 'source.mira' },
679
+ ],
680
+ },
681
+ 'global-value': {
682
+ patterns: [
683
+ {
684
+ begin: String.raw`^(\x00)?(\(global\))(\s+)(${IDENTIFIER})(\s*)(=)`,
685
+ beginCaptures: {
686
+ 2: { name: 'entity.name.label.mira' },
687
+ 4: { name: 'variable.other.constant.mira' },
688
+ 6: { name: 'keyword.operator.assignment.mira' },
689
+ },
690
+ end: '$',
691
+ patterns: [{ include: '#doc-value' }],
692
+ },
693
+ ],
694
+ },
695
+ 'global-constants': {
696
+ patterns: [
697
+ {
698
+ match: String.raw`^(\x00)?(\(global\))(\s+)(${DOC_CONSTANT_IDENTIFIER})(?=\s*(?:=|;|$))`,
699
+ captures: {
700
+ 2: { name: 'entity.name.label.mira' },
701
+ 4: { name: 'variable.other.constant.mira' },
702
+ },
703
+ },
704
+ {
705
+ match: String.raw`^(\x00)?(${DOC_CONSTANT_IDENTIFIER})(?=\s*(?:=|;|$))`,
706
+ captures: { 2: { name: 'variable.other.constant.mira' } },
707
+ },
708
+ ],
709
+ },
710
+ 'inline-parameter': {
711
+ patterns: [
712
+ {
713
+ match: String.raw`^(\x00)?(\(parameter(?:\s+pattern)?\))(\s+)(\.\.)?(mut)(\s+)(${IDENTIFIER})`,
714
+ captures: {
715
+ 2: { name: 'entity.name.label.mira' },
716
+ 4: { name: 'keyword.operator.spread.mira' },
717
+ 5: { name: 'keyword.declaration.mutable.mira' },
718
+ 7: { name: 'variable.emphasis.mira' },
719
+ },
720
+ },
721
+ {
722
+ match: String.raw`^(\x00)?(\(parameter(?:\s+pattern)?\))(\s+)(\.\.)?(\s*)(${IDENTIFIER})`,
723
+ captures: {
724
+ 2: { name: 'entity.name.label.mira' },
725
+ 4: { name: 'keyword.operator.spread.mira' },
726
+ 6: { name: 'variable.other.constant.emphasis.mira' },
727
+ },
728
+ },
729
+ ],
730
+ },
731
+ 'inline-label': {
732
+ patterns: [
733
+ {
734
+ match: String.raw`^(\x00)?(\([^():,\[\]<>|&\r\n]+\))(?=\s+\S)`,
735
+ captures: { 2: { name: 'entity.name.label.mira' } },
736
+ },
737
+ ],
738
+ },
739
+ bindings: {
740
+ patterns: [
741
+ {
742
+ match: String.raw`(?<!\p{XID_Continue})(let)(\s+)(mut)(\s+)(${IDENTIFIER})`,
743
+ captures: {
744
+ 1: { name: 'keyword.declaration.variable.mira' },
745
+ 3: { name: 'keyword.declaration.mutable.mira' },
746
+ 5: { name: 'variable.other.readwrite.mira' },
747
+ },
748
+ },
749
+ {
750
+ match: String.raw`(?<!\p{XID_Continue})(let|const)(\s+)(${IDENTIFIER})`,
751
+ captures: {
752
+ 1: { name: 'keyword.declaration.variable.mira' },
753
+ 3: { name: 'variable.other.constant.mira' },
754
+ },
755
+ },
756
+ ],
757
+ },
758
+ 'function-signatures': {
759
+ patterns: [
760
+ {
761
+ match: String.raw`(?<!\p{XID_Continue})(?:(pub)(\s+))?(fn)(\s+)(${IDENTIFIER})(?=\s*(?:<|\())`,
762
+ captures: {
763
+ 1: { name: 'keyword.control.module.mira' },
764
+ 3: { name: 'keyword.declaration.function.mira' },
765
+ 5: { name: 'entity.name.function.mira' },
766
+ },
767
+ },
768
+ ],
769
+ },
770
+ properties: {
771
+ patterns: [
772
+ {
773
+ begin: String.raw`(${IDENTIFIER})(\s*)(\??:)`,
774
+ beginCaptures: {
775
+ 1: { name: 'variable.other.property.mira' },
776
+ 3: { name: 'punctuation.separator.type.mira' },
777
+ },
778
+ end: '$',
779
+ patterns: [{ include: '#metadata' }, { include: '#type-context' }],
780
+ },
781
+ ],
782
+ },
783
+ 'doc-value': {
784
+ patterns: [
785
+ { include: '#extern-function-properties' },
786
+ { include: '#extern-class-properties' },
787
+ { include: '#metadata' },
788
+ { include: '#doc-record-value' },
789
+ { include: 'source.mira' },
790
+ ],
791
+ },
792
+ 'doc-record-value': {
793
+ patterns: [
794
+ {
795
+ begin: String.raw`\(`,
796
+ beginCaptures: { 0: { name: 'punctuation.section.parens.begin.mira' } },
797
+ end: String.raw`\)`,
798
+ endCaptures: { 0: { name: 'punctuation.section.parens.end.mira' } },
799
+ patterns: [{ include: '#doc-value' }],
800
+ },
801
+ ],
802
+ },
803
+ 'extern-function-properties': {
804
+ patterns: [
805
+ {
806
+ match: String.raw`(${IDENTIFIER})(\s*)(:)(?=\s*/\*\s*<\s*extern\s+(?:(?:async\s+)?function\*?)(?:\s+${IDENTIFIER})?\s*>\s*\*/)`,
807
+ captures: {
808
+ 1: { name: 'entity.name.function.mira' },
809
+ 3: { name: 'punctuation.separator.key-value.mira' },
810
+ },
811
+ },
812
+ ],
813
+ },
814
+ 'extern-class-properties': {
815
+ patterns: [
816
+ {
817
+ match: String.raw`(${IDENTIFIER})(\s*)(:)(?=\s*/\*\s*<\s*extern\s+class(?:\s+${IDENTIFIER})?\s*>\s*\*/)`,
818
+ captures: {
819
+ 1: { name: 'entity.name.type.mira' },
820
+ 3: { name: 'punctuation.separator.key-value.mira' },
821
+ },
822
+ },
823
+ ],
824
+ },
825
+ metadata: {
826
+ patterns: [
827
+ {
828
+ 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
+ ],
864
+ },
865
+ ],
866
+ },
867
+ 'reflection-type': {
868
+ patterns: [
869
+ {
870
+ match: String.raw`(?<!\p{XID_Continue})(type)(\s*)(\()(\s*)(${IDENTIFIER})(\s*)(\))`,
871
+ captures: {
872
+ 1: { name: 'keyword.operator.expression.mira' },
873
+ 3: { name: 'punctuation.section.parens.begin.mira' },
874
+ 5: { name: 'variable.other.mira' },
875
+ 7: { name: 'punctuation.section.parens.end.mira' },
876
+ },
877
+ },
878
+ ],
879
+ },
880
+ 'type-context': {
881
+ patterns: [
882
+ { include: '#reflection-type' },
883
+ { include: '#function-type' },
884
+ { include: '#generic-types' },
885
+ { include: '#type-grouping' },
886
+ { include: '#type-brackets' },
887
+ { include: '#type-atoms' },
888
+ ],
889
+ },
890
+ 'function-type': {
891
+ patterns: [
892
+ {
893
+ begin: String.raw`(?<!\p{XID_Continue})(fn)${IDENTIFIER_END}(?=\s*(?:<|\())`,
894
+ beginCaptures: { 1: { name: 'support.type.function.mira' } },
895
+ end: String.raw`(?=\s*(?:,|\)|$))`,
896
+ patterns: [
897
+ { include: '#generic-types' },
898
+ { include: '#parameters' },
899
+ { include: '#return-type' },
900
+ { include: '#type-context' },
901
+ ],
902
+ },
903
+ ],
904
+ },
905
+ 'generic-types': {
906
+ patterns: [
907
+ {
908
+ begin: '<',
909
+ beginCaptures: { 0: { name: 'punctuation.definition.type-parameters.begin.mira' } },
910
+ end: '>',
911
+ endCaptures: { 0: { name: 'punctuation.definition.type-parameters.end.mira' } },
912
+ patterns: [
913
+ { include: '#reflection-type' },
914
+ { include: '#function-type' },
915
+ { include: '#generic-types' },
916
+ { include: '#type-grouping' },
917
+ { include: '#type-brackets' },
918
+ { include: '#type-atoms' },
919
+ ],
920
+ },
921
+ ],
922
+ },
923
+ parameters: {
924
+ patterns: [
925
+ {
926
+ begin: String.raw`\(`,
927
+ beginCaptures: { 0: { name: 'punctuation.section.parameters.begin.mira' } },
928
+ end: String.raw`\)`,
929
+ endCaptures: { 0: { name: 'punctuation.section.parameters.end.mira' } },
930
+ patterns: [
931
+ {
932
+ begin: String.raw`(\.\.)?(\s*)(mut)?(\s*)(${IDENTIFIER})(\s*)(\??:)(?=\s*fn${IDENTIFIER_END})`,
933
+ beginCaptures: {
934
+ 1: { name: 'keyword.operator.spread.mira' },
935
+ 3: { name: 'keyword.declaration.mutable.mira' },
936
+ 5: { name: 'entity.name.function.emphasis.mira' },
937
+ 7: { name: 'punctuation.separator.type.mira' },
938
+ },
939
+ end: String.raw`(?=,|\))`,
940
+ patterns: [{ include: '#type-context' }],
941
+ },
942
+ {
943
+ begin: String.raw`(\.\.)?(\s*)(mut)?(\s*)(${IDENTIFIER})(\s*)(\??:)`,
944
+ beginCaptures: {
945
+ 1: { name: 'keyword.operator.spread.mira' },
946
+ 3: { name: 'keyword.declaration.mutable.mira' },
947
+ 5: { name: 'variable.emphasis.mira' },
948
+ 7: { name: 'punctuation.separator.type.mira' },
949
+ },
950
+ end: String.raw`(?=,|\))`,
951
+ patterns: [{ include: '#type-context' }],
952
+ },
953
+ {
954
+ match: String.raw`(\.\.)?(\s*)(mut)(\s+)(${IDENTIFIER})`,
955
+ captures: {
956
+ 1: { name: 'keyword.operator.spread.mira' },
957
+ 3: { name: 'keyword.declaration.mutable.mira' },
958
+ 5: { name: 'variable.emphasis.mira' },
959
+ },
960
+ },
961
+ { name: 'keyword.operator.spread.mira', match: String.raw`\.\.` },
962
+ { name: 'variable.emphasis.mira', match: IDENTIFIER },
963
+ { name: 'punctuation.separator.parameter.mira', match: ',' },
964
+ ],
965
+ },
966
+ ],
967
+ },
968
+ 'type-grouping': {
969
+ patterns: [
970
+ {
971
+ begin: String.raw`\(`,
972
+ beginCaptures: { 0: { name: 'punctuation.section.parens.begin.mira' } },
973
+ end: String.raw`\)`,
974
+ endCaptures: { 0: { name: 'punctuation.section.parens.end.mira' } },
975
+ patterns: [
976
+ {
977
+ begin: String.raw`(${IDENTIFIER})(\s*)(\??:)`,
978
+ beginCaptures: {
979
+ 1: { name: 'variable.other.property.mira' },
980
+ 3: { name: 'punctuation.separator.type.mira' },
981
+ },
982
+ end: String.raw`(?=,|\))`,
983
+ patterns: [{ include: '#type-context' }],
984
+ },
985
+ { include: '#type-context' },
986
+ ],
987
+ },
988
+ ],
989
+ },
990
+ 'type-brackets': {
991
+ patterns: [
992
+ {
993
+ begin: String.raw`\[`,
994
+ beginCaptures: { 0: { name: 'punctuation.section.brackets.begin.mira' } },
995
+ end: String.raw`\]`,
996
+ endCaptures: { 0: { name: 'punctuation.section.brackets.end.mira' } },
997
+ patterns: [{ include: '#type-context' }],
998
+ },
999
+ ],
1000
+ },
1001
+ 'return-type': {
1002
+ patterns: [
1003
+ {
1004
+ begin: '->',
1005
+ beginCaptures: { 0: { name: 'keyword.operator.type.mira' } },
1006
+ end: String.raw`(?=,|\)|$)`,
1007
+ patterns: [{ include: '#type-context' }],
1008
+ },
1009
+ ],
1010
+ },
1011
+ 'type-atoms': {
1012
+ patterns: [
1013
+ { include: '#reflection-type' },
1014
+ {
1015
+ 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}`,
1021
+ },
1022
+ { name: 'keyword.operator.type.mira', match: '[&|]' },
1023
+ { name: 'keyword.operator.spread.mira', match: String.raw`\.\.` },
1024
+ { name: 'entity.name.type.mira', match: IDENTIFIER },
1025
+ { name: 'punctuation.section.brackets.mira', match: String.raw`[\[\]]` },
1026
+ { name: 'punctuation.separator.type.mira', match: '[,.?:]' },
1027
+ { include: 'source.mira#strings' },
1028
+ { include: 'source.mira#numbers' },
1029
+ ],
1030
+ },
1031
+ },
1032
+ };
1033
+ }