microsoft/typespec

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
e07d86c87a444d4145b98d85fa416eca819e251c

Branches

Tags

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

Clone

HTTPS

Download ZIP

packages/compiler/server/tmlanguage.ts

751lines · modecode

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