microsoft/typespec

Public

mirrored from https://github.com/microsoft/typespecAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
3e669c74c1e16a47afe2b98706bdcf8dad4434af

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

packages/compiler/server/tmlanguage.ts

821lines · modecode

1// TextMate-based syntax highlighting is implemented in this file.
2// typespec.tmLanguage is generated by running this script.
3
4import { writeFile } from "fs/promises";
5import mkdirp from "mkdirp";
6import { resolve } from "path";
7import * as tm from "tmlanguage-generator";
8
9type IncludeRule = tm.IncludeRule<TypeSpecScope>;
10type BeginEndRule = tm.BeginEndRule<TypeSpecScope>;
11type MatchRule = tm.MatchRule<TypeSpecScope>;
12type Grammar = tm.Grammar<TypeSpecScope>;
13
14export type TypeSpecScope =
15 | "comment.block.tsp"
16 | "comment.line.double-slash.tsp"
17 | "constant.character.escape.tsp"
18 | "constant.numeric.tsp"
19 | "constant.language.tsp"
20 | "keyword.directive.name.tsp"
21 | "entity.name.type.tsp"
22 | "entity.name.function.tsp"
23 | "entity.name.tag.tsp"
24 | "keyword.other.tsp"
25 | "string.quoted.double.tsp"
26 | "string.quoted.triple.tsp"
27 | "variable.name.tsp"
28 // Operators
29 | "keyword.operator.type.annotation.tsp"
30 | "keyword.operator.assignment.tsp"
31 | "keyword.operator.optional.tsp"
32 | "keyword.operator.selector.tsp"
33 | "keyword.operator.spread.tsp"
34 // Punctuation
35 | "punctuation.comma.tsp"
36 | "punctuation.accessor.tsp"
37 | "punctuation.terminator.statement.tsp"
38 | "punctuation.definition.typeparameters.begin.tsp"
39 | "punctuation.definition.typeparameters.end.tsp"
40 | "punctuation.squarebracket.open.tsp"
41 | "punctuation.squarebracket.close.tsp"
42 | "punctuation.curlybrace.open.tsp"
43 | "punctuation.curlybrace.close.tsp"
44 | "punctuation.parenthesis.open.tsp"
45 | "punctuation.parenthesis.close.tsp";
46
47const meta: typeof tm.meta = tm.meta;
48const identifierStart = "[_$[:alpha:]]";
49// cspell:disable-next-line
50const identifierContinue = "[_$[:alnum:]]";
51const beforeIdentifier = `(?=${identifierStart})`;
52const identifier = `\\b${identifierStart}${identifierContinue}*\\b`;
53const qualifiedIdentifier = `\\b${identifierStart}(${identifierContinue}|\\.${identifierStart})*\\b`;
54const stringPattern = '\\"(?:[^\\"\\\\]|\\\\.)*\\"';
55const modifierKeyword = `\\b(?:extern)\\b`;
56const statementKeyword = `\\b(?:namespace|model|op|using|import|enum|alias|union|interface|dec|fn)\\b`;
57const universalEnd = `(?=,|;|@|\\)|\\}|${modifierKeyword}|${statementKeyword})`;
58const universalEndExceptComma = `(?=;|@|\\)|\\}|${modifierKeyword}|${statementKeyword})`;
59
60/**
61 * Universal end with extra end char: `=`
62 */
63const expressionEnd = `(?=,|;|@|\\)|\\}|=|${statementKeyword})`;
64const hexNumber = "\\b(?<!\\$)0(?:x|X)[0-9a-fA-F][0-9a-fA-F_]*(n)?\\b(?!\\$)";
65const binaryNumber = "\\b(?<!\\$)0(?:b|B)[01][01_]*(n)?\\b(?!\\$)";
66const decimalNumber =
67 "(?<!\\$)(?:" +
68 "(?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)|" + // 1.1E+3
69 "(?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*(n)?\\b)|" + // 1.E+3
70 "(?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)|" + // .1E+3
71 "(?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)|" + // 1E+3
72 "(?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*(n)?\\b)|" + // 1.1
73 "(?:\\b[0-9][0-9_]*(\\.)(n)?\\B)|" + // 1.
74 "(?:\\B(\\.)[0-9][0-9_]*(n)?\\b)|" + // .1
75 "(?:\\b[0-9][0-9_]*(n)?\\b(?!\\.))" + // 1
76 ")(?!\\$)";
77const anyNumber = `(?:${hexNumber}|${binaryNumber}|${decimalNumber})`;
78
79const expression: IncludeRule = {
80 key: "expression",
81 patterns: [
82 /* placeholder filled later due to cycle*/
83 ],
84};
85
86const statement: IncludeRule = {
87 key: "statement",
88 patterns: [
89 /*placeholder filled later due to cycle*/
90 ],
91};
92
93const booleanLiteral: MatchRule = {
94 key: "boolean-literal",
95 scope: "constant.language.tsp",
96 match: `\\b(true|false)\\b`,
97};
98
99const escapeChar: MatchRule = {
100 key: "escape-character",
101 scope: "constant.character.escape.tsp",
102 match: "\\\\.",
103};
104
105const stringLiteral: BeginEndRule = {
106 key: "string-literal",
107 scope: "string.quoted.double.tsp",
108 begin: '"',
109 end: '"|$',
110 patterns: [escapeChar],
111};
112
113const tripleQuotedStringLiteral: BeginEndRule = {
114 key: "triple-quoted-string-literal",
115 scope: "string.quoted.triple.tsp",
116 begin: '"""',
117 end: '"""',
118 patterns: [escapeChar],
119};
120
121const punctuationComma: MatchRule = {
122 key: "punctuation-comma",
123 scope: "punctuation.comma.tsp",
124 match: ",",
125};
126
127const punctuationAccessor: MatchRule = {
128 key: "punctuation-accessor",
129 scope: "punctuation.accessor.tsp",
130 match: "\\.",
131};
132
133const punctuationSemicolon: MatchRule = {
134 key: "punctuation-semicolon",
135 scope: "punctuation.terminator.statement.tsp",
136 match: ";",
137};
138
139const operatorAssignment: MatchRule = {
140 key: "operator-assignment",
141 scope: "keyword.operator.assignment.tsp",
142 match: "=",
143};
144
145const numericLiteral: MatchRule = {
146 key: "numeric-literal",
147 scope: "constant.numeric.tsp",
148 match: anyNumber,
149};
150
151const lineComment: MatchRule = {
152 key: "line-comment",
153 scope: "comment.line.double-slash.tsp",
154 match: "//.*$",
155};
156
157const blockComment: BeginEndRule = {
158 key: "block-comment",
159 scope: "comment.block.tsp",
160 begin: "/\\*",
161 end: "\\*/",
162};
163
164// Tokens that match standing alone in any context: literals and comments
165const token: IncludeRule = {
166 key: "token",
167 patterns: [
168 lineComment,
169 blockComment,
170 // `"""` must come before `"` or first two quotes of `"""` will match as
171 // empty string
172 tripleQuotedStringLiteral,
173 stringLiteral,
174 booleanLiteral,
175 numericLiteral,
176 ],
177};
178
179const parenthesizedExpression: BeginEndRule = {
180 key: "parenthesized-expression",
181 scope: meta,
182 begin: "\\(",
183 beginCaptures: {
184 "0": { scope: "punctuation.parenthesis.open.tsp" },
185 },
186 end: "\\)",
187 endCaptures: {
188 "0": { scope: "punctuation.parenthesis.close.tsp" },
189 },
190 patterns: [expression, punctuationComma],
191};
192
193const decorator: BeginEndRule = {
194 key: "decorator",
195 scope: meta,
196 begin: `(@${qualifiedIdentifier})`,
197 beginCaptures: {
198 "1": { scope: "entity.name.tag.tsp" },
199 },
200 end: `${beforeIdentifier}|${universalEnd}`,
201 patterns: [token, parenthesizedExpression],
202};
203
204const augmentDecoratorStatement: BeginEndRule = {
205 key: "augment-decorator-statement",
206 scope: meta,
207 begin: `(@@${qualifiedIdentifier})`,
208 beginCaptures: {
209 "1": { scope: "entity.name.tag.tsp" },
210 },
211 end: `${beforeIdentifier}|${universalEnd}`,
212 patterns: [token, parenthesizedExpression],
213};
214
215const identifierExpression: MatchRule = {
216 key: "identifier-expression",
217 scope: "entity.name.type.tsp",
218 match: identifier,
219};
220
221const typeArguments: BeginEndRule = {
222 key: "type-arguments",
223 scope: meta,
224 begin: "<",
225 beginCaptures: {
226 "0": { scope: "punctuation.definition.typeparameters.begin.tsp" },
227 },
228 end: ">",
229 endCaptures: {
230 "0": { scope: "punctuation.definition.typeparameters.end.tsp" },
231 },
232 patterns: [expression, punctuationComma],
233};
234
235const typeParameterConstraint: BeginEndRule = {
236 key: "type-parameter-constraint",
237 scope: meta,
238 begin: `extends`,
239 beginCaptures: {
240 "0": { scope: "keyword.other.tsp" },
241 },
242 end: `(?=>)|${universalEnd}`,
243 patterns: [expression],
244};
245
246const typeParameterDefault: BeginEndRule = {
247 key: "type-parameter-default",
248 scope: meta,
249 begin: `=`,
250 beginCaptures: {
251 "0": { scope: "keyword.operator.assignment.tsp" },
252 },
253 end: `(?=>)|${universalEnd}`,
254 patterns: [expression],
255};
256
257const typeParameter: BeginEndRule = {
258 key: "type-parameter",
259 scope: meta,
260 begin: `(${identifier})`,
261 beginCaptures: {
262 "1": { scope: "entity.name.type.tsp" },
263 },
264 end: `(?=>)|${universalEnd}`,
265 patterns: [typeParameterConstraint, typeParameterDefault],
266};
267
268const typeParameters: BeginEndRule = {
269 key: "type-parameters",
270 scope: meta,
271 begin: "<",
272 beginCaptures: {
273 "0": { scope: "punctuation.definition.typeparameters.begin.tsp" },
274 },
275 end: ">",
276 endCaptures: {
277 "0": { scope: "punctuation.definition.typeparameters.end.tsp" },
278 },
279 patterns: [typeParameter, punctuationComma],
280};
281
282const tupleExpression: BeginEndRule = {
283 key: "tuple-expression",
284 scope: meta,
285 begin: "\\[",
286 beginCaptures: {
287 "0": { scope: "punctuation.squarebracket.open.tsp" },
288 },
289 end: "\\]",
290 endCaptures: {
291 "0": { scope: "punctuation.squarebracket.close.tsp" },
292 },
293 patterns: [expression],
294};
295
296const typeAnnotation: BeginEndRule = {
297 key: "type-annotation",
298 scope: meta,
299 begin: "\\s*(\\??)\\s*(:)",
300 beginCaptures: {
301 "1": { scope: "keyword.operator.optional.tsp" },
302 "2": { scope: "keyword.operator.type.annotation.tsp" },
303 },
304 end: expressionEnd,
305 patterns: [expression],
306};
307
308const modelProperty: BeginEndRule = {
309 key: "model-property",
310 scope: meta,
311 begin: `(?:(${identifier})|(${stringPattern}))`,
312 beginCaptures: {
313 "1": { scope: "variable.name.tsp" },
314 "2": { scope: "string.quoted.double.tsp" },
315 },
316 end: universalEnd,
317 patterns: [token, typeAnnotation, operatorAssignment, expression],
318};
319
320const modelSpreadProperty: BeginEndRule = {
321 key: "model-spread-property",
322 scope: meta,
323 begin: "\\.\\.\\.",
324 beginCaptures: {
325 "0": { scope: "keyword.operator.spread.tsp" },
326 },
327 end: universalEnd,
328 patterns: [expression],
329};
330
331const directive: BeginEndRule = {
332 key: "directive",
333 scope: meta,
334 begin: `\\s*(#${identifier})`,
335 beginCaptures: {
336 "1": { scope: "keyword.directive.name.tsp" },
337 },
338 end: `$|${universalEnd}`,
339 patterns: [stringLiteral, identifierExpression],
340};
341
342const modelExpression: BeginEndRule = {
343 key: "model-expression",
344 scope: meta,
345 begin: "\\{",
346 beginCaptures: {
347 "0": { scope: "punctuation.curlybrace.open.tsp" },
348 },
349 end: "\\}",
350 endCaptures: {
351 "0": { scope: "punctuation.curlybrace.close.tsp" },
352 },
353 patterns: [
354 // modelProperty must come before token or quoted property name will be
355 // considered an arbitrarily positioned string literal and not match as part
356 // of modelProperty begin.
357 modelProperty,
358 token,
359 directive,
360 decorator,
361 modelSpreadProperty,
362 punctuationSemicolon,
363 ],
364};
365
366const modelHeritage: BeginEndRule = {
367 key: "model-heritage",
368 scope: meta,
369 begin: "\\b(extends|is)\\b",
370 beginCaptures: {
371 "1": { scope: "keyword.other.tsp" },
372 },
373 end: `((?=\\{)|${universalEndExceptComma})`,
374 patterns: [expression, punctuationComma],
375};
376
377const modelStatement: BeginEndRule = {
378 key: "model-statement",
379 scope: meta,
380 begin: "\\b(model)\\b",
381 beginCaptures: {
382 "1": { scope: "keyword.other.tsp" },
383 },
384 end: `(?<=\\})|${universalEnd}`,
385 patterns: [
386 token,
387 typeParameters,
388 modelHeritage, // before expression or `extends` or `is` will look like type name
389 expression, // enough to match name, type parameters, and body.
390 ],
391};
392
393const scalarExtends: BeginEndRule = {
394 key: "scalar-extends",
395 scope: meta,
396 begin: "\\b(extends)\\b",
397 beginCaptures: {
398 "1": { scope: "keyword.other.tsp" },
399 },
400 end: universalEndExceptComma,
401 patterns: [expression, punctuationComma],
402};
403
404const scalarStatement: BeginEndRule = {
405 key: "scalar-statement",
406 scope: meta,
407 begin: "\\b(scalar)\\b",
408 beginCaptures: {
409 "1": { scope: "keyword.other.tsp" },
410 },
411 end: universalEnd,
412 patterns: [
413 token,
414 typeParameters,
415 scalarExtends, // before expression or `extends` will look like type name
416 expression, // enough to match name, type parameters, and body.
417 ],
418};
419
420const enumStatement: BeginEndRule = {
421 key: "enum-statement",
422 scope: meta,
423 begin: "\\b(enum)\\b",
424 beginCaptures: {
425 "1": { scope: "keyword.other.tsp" },
426 },
427 end: `(?<=\\})|${universalEnd}`,
428 patterns: [token, expression],
429};
430
431const unionStatement: BeginEndRule = {
432 key: "union-statement",
433 scope: meta,
434 begin: "\\b(union)\\b",
435 beginCaptures: {
436 "1": { scope: "keyword.other.tsp" },
437 },
438 end: `(?<=\\})|${universalEnd}`,
439 patterns: [token, expression],
440};
441
442const aliasStatement: BeginEndRule = {
443 key: "alias-statement",
444 scope: meta,
445 begin: "\\b(alias)\\b",
446 beginCaptures: {
447 "1": { scope: "keyword.other.tsp" },
448 },
449 end: universalEnd,
450 patterns: [typeParameters, operatorAssignment, expression],
451};
452
453const namespaceName: BeginEndRule = {
454 key: "namespace-name",
455 scope: meta,
456 begin: beforeIdentifier,
457 end: `((?=\\{)|${universalEnd})`,
458 patterns: [identifierExpression, punctuationAccessor],
459};
460
461const namespaceBody: BeginEndRule = {
462 key: "namespace-body",
463 scope: meta,
464 begin: "\\{",
465 beginCaptures: {
466 "0": { scope: "punctuation.curlybrace.open.tsp" },
467 },
468 end: "\\}",
469 endCaptures: {
470 "0": { scope: "punctuation.curlybrace.close.tsp" },
471 },
472 patterns: [statement],
473};
474
475const namespaceStatement: BeginEndRule = {
476 key: "namespace-statement",
477 scope: meta,
478 begin: "\\b(namespace)\\b",
479 beginCaptures: {
480 "1": { scope: "keyword.other.tsp" },
481 },
482 end: `((?<=\\})|${universalEnd})`,
483 patterns: [token, namespaceName, namespaceBody],
484};
485
486const operationParameters: BeginEndRule = {
487 key: "operation-parameters",
488 scope: meta,
489 begin: "\\(",
490 beginCaptures: {
491 "0": { scope: "punctuation.parenthesis.open.tsp" },
492 },
493 end: "\\)",
494 endCaptures: {
495 "0": { scope: "punctuation.parenthesis.close.tsp" },
496 },
497 patterns: [token, decorator, modelProperty, modelSpreadProperty, punctuationComma],
498};
499
500const operationHeritage: BeginEndRule = {
501 key: "operation-heritage",
502 scope: meta,
503 begin: "\\b(is)\\b",
504 beginCaptures: {
505 "1": { scope: "keyword.other.tsp" },
506 },
507 end: universalEnd,
508 patterns: [expression],
509};
510
511const operationSignature: IncludeRule = {
512 key: "operation-signature",
513 patterns: [
514 typeParameters,
515 operationHeritage,
516 operationParameters,
517 typeAnnotation, // return type
518 ],
519};
520
521const operationStatement: BeginEndRule = {
522 key: "operation-statement",
523 scope: meta,
524 begin: `\\b(op)\\b\\s+(${identifier})`,
525 beginCaptures: {
526 "1": { scope: "keyword.other.tsp" },
527 "2": { scope: "entity.name.function.tsp" },
528 },
529 end: universalEnd,
530 patterns: [token, operationSignature],
531};
532
533const interfaceMember: BeginEndRule = {
534 key: "interface-member",
535 scope: meta,
536 begin: `(?:\\b(op)\\b\\s+)?(${identifier})`,
537 beginCaptures: {
538 "1": { scope: "keyword.other.tsp" },
539 "2": { scope: "entity.name.function.tsp" },
540 },
541 end: universalEnd,
542 patterns: [token, operationSignature],
543};
544
545const interfaceHeritage: BeginEndRule = {
546 key: "interface-heritage",
547 scope: meta,
548 begin: "\\b(extends)\\b",
549 beginCaptures: {
550 "1": { scope: "keyword.other.tsp" },
551 },
552 end: `((?=\\{)|${universalEndExceptComma})`,
553 patterns: [expression, punctuationComma],
554};
555
556const interfaceBody: BeginEndRule = {
557 key: "interface-body",
558 scope: meta,
559 begin: "\\{",
560 beginCaptures: {
561 "0": { scope: "punctuation.curlybrace.open.tsp" },
562 },
563 end: "\\}",
564 endCaptures: {
565 "0": { scope: "punctuation.curlybrace.close.tsp" },
566 },
567 patterns: [token, directive, decorator, interfaceMember, punctuationSemicolon],
568};
569
570const interfaceStatement: BeginEndRule = {
571 key: "interface-statement",
572 scope: meta,
573 begin: "\\b(interface)\\b",
574 beginCaptures: {
575 "1": { scope: "keyword.other.tsp" },
576 },
577 end: `(?<=\\})|${universalEnd}`,
578 patterns: [
579 token,
580 interfaceHeritage, // before expression or extends will look like type name
581 interfaceBody, // before expression or { will match model expression
582 expression, // enough to match name and type parameters
583 ],
584};
585
586const importStatement: BeginEndRule = {
587 key: "import-statement",
588 scope: meta,
589 begin: "\\b(import)\\b",
590 beginCaptures: {
591 "1": { scope: "keyword.other.tsp" },
592 },
593 end: universalEnd,
594 patterns: [token],
595};
596
597const usingStatement: BeginEndRule = {
598 key: "using-statement",
599 scope: meta,
600 begin: "\\b(using)\\b",
601 beginCaptures: {
602 "1": { scope: "keyword.other.tsp" },
603 },
604 end: universalEnd,
605 patterns: [token, identifierExpression],
606};
607
608const decoratorDeclarationStatement: BeginEndRule = {
609 key: "decorator-declaration-statement",
610 scope: meta,
611 begin: `(?:(extern)\\s+)?\\b(dec)\\b\\s+(${identifier})`,
612 beginCaptures: {
613 "1": { scope: "keyword.other.tsp" },
614 "2": { scope: "keyword.other.tsp" },
615 "3": { scope: "entity.name.function.tsp" },
616 },
617 end: universalEnd,
618 patterns: [token, operationParameters],
619};
620
621const functionDeclarationStatement: BeginEndRule = {
622 key: "function-declaration-statement",
623 scope: meta,
624 begin: `(?:(extern)\\s+)?\\b(fn)\\b\\s+(${identifier})`,
625 beginCaptures: {
626 "1": { scope: "keyword.other.tsp" },
627 "2": { scope: "keyword.other.tsp" },
628 "3": { scope: "entity.name.function.tsp" },
629 },
630 end: universalEnd,
631 patterns: [token, operationParameters, typeAnnotation],
632};
633
634const projectionParameter: BeginEndRule = {
635 key: "projection-parameter",
636 scope: meta,
637 begin: `(${identifier})`,
638 beginCaptures: {
639 "1": { scope: "variable.name.tsp" },
640 },
641 end: `(?=\\))|${universalEnd}`,
642 patterns: [],
643};
644
645const projectionParameters: BeginEndRule = {
646 key: "projection-parameters",
647 scope: meta,
648 begin: "\\(",
649 beginCaptures: {
650 "0": { scope: "punctuation.parenthesis.open.tsp" },
651 },
652 end: "\\)",
653 endCaptures: {
654 "0": { scope: "punctuation.parenthesis.close.tsp" },
655 },
656 patterns: [token, projectionParameter],
657};
658
659const projectionExpression: IncludeRule = {
660 key: "projection-expression",
661 patterns: [
662 /* placeholder filled later due to cycle*/
663 ],
664};
665
666const projectionBody: BeginEndRule = {
667 key: "projection-body",
668 scope: meta,
669 begin: "\\{",
670 beginCaptures: {
671 "0": { scope: "punctuation.curlybrace.open.tsp" },
672 },
673 end: "\\}",
674 endCaptures: {
675 "0": { scope: "punctuation.curlybrace.close.tsp" },
676 },
677 patterns: [projectionExpression, punctuationSemicolon],
678};
679
680const ifExpression: BeginEndRule = {
681 key: "if-expression",
682 scope: meta,
683 begin: `\\b(if)\\b`,
684 beginCaptures: {
685 "1": { scope: "keyword.other.tsp" },
686 },
687 end: `((?<=\\})|${universalEnd})`,
688 patterns: [projectionExpression, projectionBody],
689};
690
691const elseIfExpression: BeginEndRule = {
692 key: "else-if-expression",
693 scope: meta,
694 begin: `\\b(else)\\s+(if)\\b`,
695 beginCaptures: {
696 "1": { scope: "keyword.other.tsp" },
697 "2": { scope: "keyword.other.tsp" },
698 },
699 end: `((?<=\\})|${universalEnd})`,
700 patterns: [projectionExpression, projectionBody],
701};
702
703const elseExpression: BeginEndRule = {
704 key: "else-expression",
705 scope: meta,
706 begin: `\\b(else)\\b`,
707 beginCaptures: {
708 "1": { scope: "keyword.other.tsp" },
709 },
710 end: `((?<=\\})|${universalEnd})`,
711 patterns: [projectionExpression, projectionBody],
712};
713
714const functionCall: BeginEndRule = {
715 key: "function-call",
716 scope: meta,
717 begin: `(${identifier})\\s*(\\()`,
718 beginCaptures: {
719 "1": { scope: "entity.name.function.tsp" },
720 "2": { scope: "punctuation.parenthesis.open.tsp" },
721 },
722 end: `\\)`,
723 endCaptures: {
724 "0": { scope: "punctuation.parenthesis.close.tsp" },
725 },
726 patterns: [expression],
727};
728
729projectionExpression.patterns = [elseIfExpression, ifExpression, elseExpression, functionCall];
730
731const projection: BeginEndRule = {
732 key: "projection",
733 scope: meta,
734 begin: "(from|to)",
735 beginCaptures: {
736 "1": { scope: "keyword.other.tsp" },
737 },
738 end: `((?<=\\})|${universalEnd})`,
739 patterns: [projectionParameters, projectionBody],
740};
741
742const projectionStatementBody: BeginEndRule = {
743 key: "projection-statement-body",
744 scope: meta,
745 begin: "\\{",
746 beginCaptures: {
747 "0": { scope: "punctuation.curlybrace.open.tsp" },
748 },
749 end: "\\}",
750 endCaptures: {
751 "0": { scope: "punctuation.curlybrace.close.tsp" },
752 },
753 patterns: [projection],
754};
755
756const projectionStatement: BeginEndRule = {
757 key: "projection-statement",
758 scope: meta,
759 begin: `\\b(projection)\\b\\s+(${identifier})(#)(${identifier})`,
760 beginCaptures: {
761 "1": { scope: "keyword.other.tsp" },
762 "2": { scope: "keyword.other.tsp" },
763 "3": { scope: "keyword.operator.selector.tsp" },
764 "4": { scope: "variable.name.tsp" },
765 },
766 end: `((?<=\\})|${universalEnd})`,
767 patterns: [projectionStatementBody],
768};
769
770// NOTE: We don't actually classify all the different expression types and their
771// punctuation yet. For now, at least, we only deal with the ones that would
772// break coloring due to breaking out of context inappropriately with parens/
773// braces/brackets that weren't handled with appropriate precedence. The other
774// expressions color acceptably as unclassified punctuation around those we do
775// handle here.
776expression.patterns = [
777 token,
778 directive,
779 parenthesizedExpression,
780 typeArguments,
781 tupleExpression,
782 modelExpression,
783 identifierExpression,
784];
785
786statement.patterns = [
787 token,
788 directive,
789 augmentDecoratorStatement,
790 decorator,
791 modelStatement,
792 scalarStatement,
793 unionStatement,
794 interfaceStatement,
795 enumStatement,
796 aliasStatement,
797 namespaceStatement,
798 operationStatement,
799 importStatement,
800 usingStatement,
801 decoratorDeclarationStatement,
802 functionDeclarationStatement,
803 projectionStatement,
804 punctuationSemicolon,
805];
806
807const grammar: Grammar = {
808 $schema: tm.schema,
809 name: "TypeSpec",
810 scopeName: "source.tsp",
811 fileTypes: [".tsp"],
812 patterns: [statement],
813};
814
815export async function main() {
816 const plist = await tm.emitPList(grammar, {
817 errorSourceFilePath: resolve("./src/tmlanguage.ts"),
818 });
819 await mkdirp("./dist");
820 await writeFile("./dist/typespec.tmLanguage", plist);
821}
822