UNPKG

@typespec/compiler

Version:

TypeSpec Compiler Preview

844 lines 24.1 kB
// TextMate-based syntax highlighting is implemented in this file. // typespec.tmLanguage is generated by running this script. import { mkdir, writeFile } from "fs/promises"; import { resolve } from "path"; import * as tm from "tmlanguage-generator"; const meta = tm.meta; const identifierStart = "[_$[:alpha:]]"; // cspell:disable-next-line const identifierContinue = "[_$[:alnum:]]"; const beforeIdentifier = `(?=(${identifierStart}|\`))`; const escapedIdentifier = "`(?:[^`\\\\]|\\\\.)*`"; const simpleIdentifier = `\\b${identifierStart}${identifierContinue}*\\b`; const identifier = `${simpleIdentifier}|${escapedIdentifier}`; const qualifiedIdentifier = `\\b${identifierStart}(?:${identifierContinue}|\\.${identifierStart})*\\b`; const stringPattern = '\\"(?:[^\\"\\\\]|\\\\.)*\\"'; const modifierKeyword = `\\b(?:extern)\\b`; const statementKeyword = `\\b(?:namespace|model|op|using|import|enum|alias|union|interface|dec|fn)\\b`; const universalEnd = `(?=,|;|@|\\)|\\}|${modifierKeyword}|${statementKeyword})`; const universalEndExceptComma = `(?=;|@|\\)|\\}|${modifierKeyword}|${statementKeyword})`; /** * Universal end with extra end char: `=` */ const expressionEnd = `(?=,|;|@|\\)|\\}|=|${statementKeyword})`; const hexNumber = "\\b(?<!\\$)0(?:x|X)[0-9a-fA-F][0-9a-fA-F_]*(n)?\\b(?!\\$)"; const binaryNumber = "\\b(?<!\\$)0(?:b|B)[01][01_]*(n)?\\b(?!\\$)"; const decimalNumber = "(?<!\\$)(?:" + "(?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)|" + // 1.1E+3 "(?:\\b[0-9][0-9_]*(\\.)[eE][+-]?[0-9][0-9_]*(n)?\\b)|" + // 1.E+3 "(?:\\B(\\.)[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)|" + // .1E+3 "(?:\\b[0-9][0-9_]*[eE][+-]?[0-9][0-9_]*(n)?\\b)|" + // 1E+3 "(?:\\b[0-9][0-9_]*(\\.)[0-9][0-9_]*(n)?\\b)|" + // 1.1 "(?:\\b[0-9][0-9_]*(\\.)(n)?\\B)|" + // 1. "(?:\\B(\\.)[0-9][0-9_]*(n)?\\b)|" + // .1 "(?:\\b[0-9][0-9_]*(n)?\\b(?!\\.))" + // 1 ")(?!\\$)"; const anyNumber = `(?:${hexNumber}|${binaryNumber}|${decimalNumber})`; const expression = { key: "expression", patterns: [ /* placeholder filled later due to cycle*/ ], }; const statement = { key: "statement", patterns: [ /*placeholder filled later due to cycle*/ ], }; const booleanLiteral = { key: "boolean-literal", scope: "constant.language.tsp", match: `\\b(true|false)\\b`, }; const escapeChar = { key: "escape-character", scope: "constant.character.escape.tsp", match: "\\\\.", }; const templateExpression = { key: "template-expression", scope: meta, begin: "\\$\\{", beginCaptures: { "0": { scope: "punctuation.definition.template-expression.begin.tsp" }, }, end: "\\}", endCaptures: { "0": { scope: "punctuation.definition.template-expression.end.tsp" }, }, patterns: [expression], }; const stringLiteral = { key: "string-literal", scope: "string.quoted.double.tsp", begin: '"', end: '"|$', patterns: [templateExpression, escapeChar], }; const tripleQuotedStringLiteral = { key: "triple-quoted-string-literal", scope: "string.quoted.triple.tsp", begin: '"""', end: '"""', patterns: [templateExpression, escapeChar], }; const punctuationComma = { key: "punctuation-comma", scope: "punctuation.comma.tsp", match: ",", }; const punctuationAccessor = { key: "punctuation-accessor", scope: "punctuation.accessor.tsp", match: "\\.", }; const punctuationSemicolon = { key: "punctuation-semicolon", scope: "punctuation.terminator.statement.tsp", match: ";", }; const operatorAssignment = { key: "operator-assignment", scope: "keyword.operator.assignment.tsp", match: "=", }; const numericLiteral = { key: "numeric-literal", scope: "constant.numeric.tsp", match: anyNumber, }; const lineComment = { key: "line-comment", scope: "comment.line.double-slash.tsp", match: "//.*$", }; const blockComment = { key: "block-comment", scope: "comment.block.tsp", begin: "/\\*", end: "\\*/", }; const docCommentParam = { key: "doc-comment-param", scope: "comment.block.tsp", match: `(?x)((@)(?:param|template|prop))\\s+(${identifier})\\b`, captures: { "1": { scope: "keyword.tag.tspdoc" }, "2": { scope: "keyword.tag.tspdoc" }, "3": { scope: "variable.name.tsp" }, }, }; const docCommentReturn = { key: "doc-comment-return-tag", scope: "comment.block.tsp", match: `(?x)((@)(?:returns))\\b`, captures: { "1": { scope: "keyword.tag.tspdoc" }, "2": { scope: "keyword.tag.tspdoc" }, }, }; const docCommentUnknownTag = { key: "doc-comment-unknown-tag", scope: "comment.block.tsp", match: `(?x)((@)(?:${identifier}))\\b`, captures: { "1": { scope: "entity.name.tag.tsp" }, "2": { scope: "entity.name.tag.tsp" }, }, }; const docCommentBlock = { key: "doc-comment-block", patterns: [docCommentParam, docCommentReturn, docCommentUnknownTag], }; const docComment = { key: "doc-comment", scope: "comment.block.tsp", begin: "/\\*\\*", beginCaptures: { "0": { scope: "comment.block.tsp" }, }, end: "\\*/", endCaptures: { "0": { scope: "comment.block.tsp" }, }, patterns: [docCommentBlock], }; // Tokens that match standing alone in any context: literals and comments const token = { key: "token", patterns: [ docComment, lineComment, blockComment, // `"""` must come before `"` or first two quotes of `"""` will match as // empty string tripleQuotedStringLiteral, stringLiteral, booleanLiteral, numericLiteral, ], }; const parenthesizedExpression = { key: "parenthesized-expression", scope: meta, begin: "\\(", beginCaptures: { "0": { scope: "punctuation.parenthesis.open.tsp" }, }, end: "\\)", endCaptures: { "0": { scope: "punctuation.parenthesis.close.tsp" }, }, patterns: [expression, punctuationComma], }; const callExpression = { key: "callExpression", scope: meta, begin: `(${qualifiedIdentifier})\\s*(\\()`, beginCaptures: { "1": { scope: "entity.name.function.tsp" }, "2": { scope: "punctuation.parenthesis.open.tsp" }, }, end: "\\)", endCaptures: { "0": { scope: "punctuation.parenthesis.close.tsp" }, }, patterns: [token, expression, punctuationComma], }; const decorator = { key: "decorator", scope: meta, begin: `((@)${qualifiedIdentifier})`, beginCaptures: { "1": { scope: "entity.name.tag.tsp" }, "2": { scope: "entity.name.tag.tsp" }, }, end: `${beforeIdentifier}|${universalEnd}`, patterns: [token, parenthesizedExpression], }; const augmentDecoratorStatement = { key: "augment-decorator-statement", scope: meta, begin: `((@@)${qualifiedIdentifier})`, beginCaptures: { "1": { scope: "entity.name.tag.tsp" }, "2": { scope: "entity.name.tag.tsp" }, }, end: `${beforeIdentifier}|${universalEnd}`, patterns: [token, parenthesizedExpression], }; const identifierExpression = { key: "identifier-expression", scope: "entity.name.type.tsp", match: identifier, }; const valueOfExpression = { key: "valueof", scope: meta, begin: `\\b(valueof)`, beginCaptures: { "1": { scope: "keyword.other.tsp" }, }, end: `(?=>)|${universalEnd}`, patterns: [expression], }; const typeOfExpression = { key: "typeof", scope: meta, begin: `\\b(typeof)`, beginCaptures: { "1": { scope: "keyword.other.tsp" }, }, end: `(?=>)|${universalEnd}`, patterns: [expression], }; const typeArgument = { key: "type-argument", scope: meta, begin: `(?:(${identifier})\\s*(=))`, beginCaptures: { "1": { scope: "entity.name.type.tsp" }, "2": { scope: "keyword.operator.assignment.tsp" }, }, end: `(?=>)|${universalEnd}`, endCaptures: { "0": { scope: "keyword.operator.assignment.tsp" }, }, patterns: [token, expression, punctuationComma], }; const typeArguments = { key: "type-arguments", scope: meta, begin: "<", beginCaptures: { "0": { scope: "punctuation.definition.typeparameters.begin.tsp" }, }, end: ">", endCaptures: { "0": { scope: "punctuation.definition.typeparameters.end.tsp" }, }, patterns: [typeArgument, expression, punctuationComma], }; const typeParameterConstraint = { key: "type-parameter-constraint", scope: meta, begin: `extends`, beginCaptures: { "0": { scope: "keyword.other.tsp" }, }, end: `(?=>)|${universalEnd}`, patterns: [expression], }; const typeParameterDefault = { key: "type-parameter-default", scope: meta, begin: `=`, beginCaptures: { "0": { scope: "keyword.operator.assignment.tsp" }, }, end: `(?=>)|${universalEnd}`, patterns: [expression], }; const typeParameter = { key: "type-parameter", scope: meta, begin: `(${identifier})`, beginCaptures: { "1": { scope: "entity.name.type.tsp" }, }, end: `(?=>)|${universalEnd}`, patterns: [token, typeParameterConstraint, typeParameterDefault], }; const typeParameters = { key: "type-parameters", scope: meta, begin: "<", beginCaptures: { "0": { scope: "punctuation.definition.typeparameters.begin.tsp" }, }, end: ">", endCaptures: { "0": { scope: "punctuation.definition.typeparameters.end.tsp" }, }, patterns: [typeParameter, punctuationComma], }; const tupleLiteral = { key: "tuple-literal", scope: meta, begin: "#\\[", beginCaptures: { "0": { scope: "punctuation.hashsquarebracket.open.tsp" }, }, end: "\\]", endCaptures: { "0": { scope: "punctuation.squarebracket.close.tsp" }, }, patterns: [expression, punctuationComma], }; const tupleExpression = { key: "tuple-expression", scope: meta, begin: "\\[", beginCaptures: { "0": { scope: "punctuation.squarebracket.open.tsp" }, }, end: "\\]", endCaptures: { "0": { scope: "punctuation.squarebracket.close.tsp" }, }, patterns: [expression], }; const typeAnnotation = { key: "type-annotation", scope: meta, begin: "\\s*(\\??)\\s*(:)", beginCaptures: { "1": { scope: "keyword.operator.optional.tsp" }, "2": { scope: "keyword.operator.type.annotation.tsp" }, }, end: expressionEnd, patterns: [expression], }; const modelProperty = { key: "model-property", scope: meta, begin: `(?:(${identifier})|(${stringPattern}))`, beginCaptures: { "1": { scope: "variable.name.tsp" }, "2": { scope: "string.quoted.double.tsp" }, }, end: universalEnd, patterns: [token, typeAnnotation, operatorAssignment, expression], }; const spreadExpression = { key: "spread-operator", scope: meta, begin: "\\.\\.\\.", beginCaptures: { "0": { scope: "keyword.operator.spread.tsp" }, }, end: universalEnd, patterns: [expression], }; const directive = { key: "directive", scope: meta, begin: `\\s*(#${simpleIdentifier})`, beginCaptures: { "1": { scope: "keyword.directive.name.tsp" }, }, end: `$|${universalEnd}`, patterns: [stringLiteral, identifierExpression], }; const modelExpression = { key: "model-expression", scope: meta, begin: "\\{", beginCaptures: { "0": { scope: "punctuation.curlybrace.open.tsp" }, }, end: "\\}", endCaptures: { "0": { scope: "punctuation.curlybrace.close.tsp" }, }, patterns: [ // modelProperty must come before token or quoted property name will be // considered an arbitrarily positioned string literal and not match as part // of modelProperty begin. modelProperty, token, directive, decorator, spreadExpression, punctuationSemicolon, ], }; const objectLiteralProperty = { key: "object-literal-property", scope: meta, begin: `(?:(${identifier})\\s*(:))`, beginCaptures: { "1": { scope: "variable.name.tsp" }, "2": { scope: "keyword.operator.type.annotation.tsp" }, }, end: universalEnd, patterns: [token, expression], }; const objectLiteral = { key: "object-literal", scope: meta, begin: "#\\{", beginCaptures: { "0": { scope: "punctuation.hashcurlybrace.open.tsp" }, }, end: "\\}", endCaptures: { "0": { scope: "punctuation.curlybrace.close.tsp" }, }, patterns: [token, objectLiteralProperty, directive, spreadExpression, punctuationComma], }; const modelHeritage = { key: "model-heritage", scope: meta, begin: "\\b(extends|is)\\b", beginCaptures: { "1": { scope: "keyword.other.tsp" }, }, end: `((?=\\{)|${universalEndExceptComma})`, patterns: [expression, punctuationComma], }; const modelStatement = { key: "model-statement", scope: meta, begin: "\\b(model)\\b", beginCaptures: { "1": { scope: "keyword.other.tsp" }, }, end: `(?<=\\})|${universalEnd}`, patterns: [ token, typeParameters, modelHeritage, // before expression or `extends` or `is` will look like type name expression, // enough to match name, type parameters, and body. ], }; const operationParameters = { key: "operation-parameters", scope: meta, begin: "\\(", beginCaptures: { "0": { scope: "punctuation.parenthesis.open.tsp" }, }, end: "\\)", endCaptures: { "0": { scope: "punctuation.parenthesis.close.tsp" }, }, patterns: [token, decorator, modelProperty, spreadExpression, punctuationComma], }; const scalarExtends = { key: "scalar-extends", scope: meta, begin: "\\b(extends)\\b", beginCaptures: { "1": { scope: "keyword.other.tsp" }, }, end: universalEndExceptComma, patterns: [expression, punctuationComma], }; const scalarConstructor = { key: "scalar-constructor", scope: meta, begin: `\\b(init)\\b\\s+(${identifier})`, beginCaptures: { "1": { scope: "keyword.other.tsp" }, "2": { scope: "entity.name.function.tsp" }, }, end: universalEnd, patterns: [token, operationParameters], }; const scalarBody = { key: "scalar-body", scope: meta, begin: "\\{", beginCaptures: { "0": { scope: "punctuation.curlybrace.open.tsp" }, }, end: "\\}", endCaptures: { "0": { scope: "punctuation.curlybrace.close.tsp" }, }, patterns: [token, directive, scalarConstructor, punctuationSemicolon], }; const scalarStatement = { key: "scalar-statement", scope: meta, begin: `\\b(scalar)\\b\\s+(${identifier})`, beginCaptures: { "1": { scope: "keyword.other.tsp" }, "2": { scope: "entity.name.type.tsp" }, }, end: `(?<=\\})|${universalEnd}`, patterns: [ token, typeParameters, scalarExtends, // before expression or `extends` will look like type name scalarBody, ], }; const enumMember = { key: "enum-member", scope: meta, begin: `(?:(${identifier})\\s*(:?))`, beginCaptures: { "1": { scope: "variable.name.tsp" }, "2": { scope: "keyword.operator.type.annotation.tsp" }, }, end: universalEnd, patterns: [token, typeAnnotation], }; const enumBody = { key: "enum-body", scope: meta, begin: "\\{", beginCaptures: { "0": { scope: "punctuation.curlybrace.open.tsp" }, }, end: "\\}", endCaptures: { "0": { scope: "punctuation.curlybrace.close.tsp" }, }, patterns: [enumMember, token, directive, decorator, punctuationComma], }; const enumStatement = { key: "enum-statement", scope: meta, begin: `\\b(enum)\\b\\s+(${identifier})`, beginCaptures: { "1": { scope: "keyword.other.tsp" }, "2": { scope: "entity.name.type.tsp" }, }, end: `(?<=\\})|${universalEnd}`, patterns: [token, enumBody], }; const namedUnionVariant = { key: "union-variant", scope: meta, begin: `(?:(${identifier})\\s*(:))`, beginCaptures: { "1": { scope: "variable.name.tsp" }, "2": { scope: "keyword.operator.type.annotation.tsp" }, }, end: universalEnd, patterns: [token, expression], }; const unionBody = { key: "union-body", scope: meta, begin: "\\{", beginCaptures: { "0": { scope: "punctuation.curlybrace.open.tsp" }, }, end: "\\}", endCaptures: { "0": { scope: "punctuation.curlybrace.close.tsp" }, }, patterns: [namedUnionVariant, token, directive, decorator, expression, punctuationComma], }; const unionStatement = { key: "union-statement", scope: meta, begin: `\\b(union)\\b\\s+(${identifier})`, beginCaptures: { "1": { scope: "keyword.other.tsp" }, "2": { scope: "entity.name.type.tsp" }, }, end: `(?<=\\})|${universalEnd}`, patterns: [token, unionBody], }; const aliasAssignment = { key: "alias-id", scope: meta, begin: `(=)\\s*`, beginCaptures: { "1": { scope: "keyword.operator.assignment.tsp" }, }, end: universalEnd, patterns: [expression], }; const aliasStatement = { key: "alias-statement", scope: meta, begin: `\\b(alias)\\b\\s+(${identifier})\\s*`, beginCaptures: { "1": { scope: "keyword.other.tsp" }, "2": { scope: "entity.name.type.tsp" }, }, end: universalEnd, patterns: [aliasAssignment, typeParameters], }; const constStatement = { key: "const-statement", scope: meta, begin: `\\b(const)\\b\\s+(${identifier})`, beginCaptures: { "1": { scope: "keyword.other.tsp" }, "2": { scope: "variable.name.tsp" }, }, end: universalEnd, patterns: [typeAnnotation, operatorAssignment, expression], }; const namespaceName = { key: "namespace-name", scope: meta, begin: beforeIdentifier, end: `((?=\\{)|${universalEnd})`, patterns: [identifierExpression, punctuationAccessor], }; const namespaceBody = { key: "namespace-body", scope: meta, begin: "\\{", beginCaptures: { "0": { scope: "punctuation.curlybrace.open.tsp" }, }, end: "\\}", endCaptures: { "0": { scope: "punctuation.curlybrace.close.tsp" }, }, patterns: [statement], }; const namespaceStatement = { key: "namespace-statement", scope: meta, begin: "\\b(namespace)\\b", beginCaptures: { "1": { scope: "keyword.other.tsp" }, }, end: `((?<=\\})|${universalEnd})`, patterns: [token, namespaceName, namespaceBody], }; const operationHeritage = { key: "operation-heritage", scope: meta, begin: "\\b(is)\\b", beginCaptures: { "1": { scope: "keyword.other.tsp" }, }, end: universalEnd, patterns: [expression], }; const operationSignature = { key: "operation-signature", patterns: [ typeParameters, operationHeritage, operationParameters, typeAnnotation, // return type ], }; const operationStatement = { key: "operation-statement", scope: meta, begin: `\\b(op)\\b\\s+(${identifier})`, beginCaptures: { "1": { scope: "keyword.other.tsp" }, "2": { scope: "entity.name.function.tsp" }, }, end: universalEnd, patterns: [token, operationSignature], }; const interfaceMember = { key: "interface-member", scope: meta, begin: `(?:\\b(op)\\b\\s+)?(${identifier})`, beginCaptures: { "1": { scope: "keyword.other.tsp" }, "2": { scope: "entity.name.function.tsp" }, }, end: universalEnd, patterns: [token, operationSignature], }; const interfaceHeritage = { key: "interface-heritage", scope: meta, begin: "\\b(extends)\\b", beginCaptures: { "1": { scope: "keyword.other.tsp" }, }, end: `((?=\\{)|${universalEndExceptComma})`, patterns: [expression, punctuationComma], }; const interfaceBody = { key: "interface-body", scope: meta, begin: "\\{", beginCaptures: { "0": { scope: "punctuation.curlybrace.open.tsp" }, }, end: "\\}", endCaptures: { "0": { scope: "punctuation.curlybrace.close.tsp" }, }, patterns: [token, directive, decorator, interfaceMember, punctuationSemicolon], }; const interfaceStatement = { key: "interface-statement", scope: meta, begin: "\\b(interface)\\b", beginCaptures: { "1": { scope: "keyword.other.tsp" }, }, end: `(?<=\\})|${universalEnd}`, patterns: [ token, typeParameters, interfaceHeritage, // before expression or extends will look like type name interfaceBody, // before expression or { will match model expression expression, // enough to match name and type parameters ], }; const importStatement = { key: "import-statement", scope: meta, begin: "\\b(import)\\b", beginCaptures: { "1": { scope: "keyword.other.tsp" }, }, end: universalEnd, patterns: [token], }; const usingStatement = { key: "using-statement", scope: meta, begin: "\\b(using)\\b", beginCaptures: { "1": { scope: "keyword.other.tsp" }, }, end: universalEnd, patterns: [token, identifierExpression, punctuationAccessor], }; const decoratorDeclarationStatement = { key: "decorator-declaration-statement", scope: meta, begin: `(?:(extern)\\s+)?\\b(dec)\\b\\s+(${identifier})`, beginCaptures: { "1": { scope: "keyword.other.tsp" }, "2": { scope: "keyword.other.tsp" }, "3": { scope: "entity.name.function.tsp" }, }, end: universalEnd, patterns: [token, operationParameters], }; const functionDeclarationStatement = { key: "function-declaration-statement", scope: meta, begin: `(?:(extern)\\s+)?\\b(fn)\\b\\s+(${identifier})`, beginCaptures: { "1": { scope: "keyword.other.tsp" }, "2": { scope: "keyword.other.tsp" }, "3": { scope: "entity.name.function.tsp" }, }, end: universalEnd, patterns: [token, operationParameters, typeAnnotation], }; // NOTE: We don't actually classify all the different expression types and their // punctuation yet. For now, at least, we only deal with the ones that would // break coloring due to breaking out of context inappropriately with parens/ // braces/brackets that weren't handled with appropriate precedence. The other // expressions color acceptably as unclassified punctuation around those we do // handle here. expression.patterns = [ token, directive, parenthesizedExpression, valueOfExpression, typeOfExpression, typeArguments, objectLiteral, tupleLiteral, tupleExpression, modelExpression, callExpression, identifierExpression, ]; statement.patterns = [ token, directive, augmentDecoratorStatement, decorator, modelStatement, scalarStatement, unionStatement, interfaceStatement, enumStatement, aliasStatement, constStatement, namespaceStatement, operationStatement, importStatement, usingStatement, decoratorDeclarationStatement, functionDeclarationStatement, punctuationSemicolon, ]; const grammar = { $schema: tm.schema, name: "TypeSpec", scopeName: "source.tsp", fileTypes: ["tsp"], patterns: [statement], }; export async function main() { const plist = await tm.emitPList(grammar, { errorSourceFilePath: resolve("./src/tmlanguage.ts"), }); const json = await tm.emitJSON(grammar, { errorSourceFilePath: resolve("./src/tmlanguage.ts"), }); await mkdir("./dist", { recursive: true }); await writeFile("./dist/typespec.tmLanguage", plist); await mkdir("../../grammars", { recursive: true }); await writeFile("../../grammars/typespec.json", json); } //# sourceMappingURL=tmlanguage.js.map