Improved FIDL2 syntax highlighting.
Generate the fidl.tmLanguage.json from a script to make it easier to
construct comprehensive regexes.
Change-Id: Ibc53c733c60401f8104c6a3d286cffb0ad32b202
diff --git a/vscode-language-fidl/CHANGELOG.md b/vscode-language-fidl/CHANGELOG.md
index e689528..ee8c181 100644
--- a/vscode-language-fidl/CHANGELOG.md
+++ b/vscode-language-fidl/CHANGELOG.md
@@ -4,4 +4,8 @@
# v0.0.3
- - Offer a "FIDL: Go To Source" command to jump from generated bindings to FIDL source when working in the Fuchsia tree.
\ No newline at end of file
+- Offer a "FIDL: Go To Source" command to jump from generated bindings to FIDL source when working in the Fuchsia tree.
+
+# v0.1.0
+
+- FIDL2 syntax highlighting.
\ No newline at end of file
diff --git a/vscode-language-fidl/README.md b/vscode-language-fidl/README.md
index 65adeef..d406fa3 100644
--- a/vscode-language-fidl/README.md
+++ b/vscode-language-fidl/README.md
@@ -5,4 +5,10 @@
- a command to jump from generated bindings to FIDL source.
-[fidl]: https://fuchsia.googlesource.com/fidl
+The syntax highlighting is generated by a script called `generate-syntax.ts`. That can be run by:
+```
+npm install
+npm run-script build-syntax
+```
+
+[fidl]: https://fuchsia.googlesource.com/docs/+/master/development/languages/fidl/
diff --git a/vscode-language-fidl/generate-syntax.ts b/vscode-language-fidl/generate-syntax.ts
new file mode 100644
index 0000000..c39df55
--- /dev/null
+++ b/vscode-language-fidl/generate-syntax.ts
@@ -0,0 +1,398 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This generates a language definition file because writing it by hand is too hard.
+
+// Format of language definition JSON
+const tmSchema = 'https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json';
+type TmCaptures = { [k: string]: { name: string } };
+type TmIncludePattern = { include: string };
+type TmMatchPattern = {
+ name?: string,
+ match: string,
+ captures?: TmCaptures
+};
+type TmBlockPattern = {
+ name?: string,
+ begin: string,
+ beginCaptures?: TmCaptures,
+ end: string,
+ endCaptures?: TmCaptures,
+ patterns: TmPattern[]
+};
+type TmPattern = TmIncludePattern | TmMatchPattern | TmBlockPattern;
+type TmLanguage = {
+ '$schema': string,
+ name: string,
+ scopeName: string,
+ patterns: TmPattern[],
+ repository: {
+ [key: string]: { patterns: TmPattern[] }
+ },
+};
+
+
+class Pattern {
+ readonly re: string;
+ readonly names: string[];
+ constructor(re: string, names: string[]) {
+ this.re = re;
+ this.names = names;
+ const num_groups = new RegExp(re + '|').exec('')!.length - 1;
+ if (num_groups !== this.names.length) {
+ throw new Error(`Found ${num_groups} but expected ${this.names.length} groups in ${re}`);
+ }
+ }
+
+ toString() {
+ return this.re;
+ }
+
+ assert(s: string) {
+ const re = new RegExp(this.re);
+ const m = s.match(re);
+ if (!m) {
+ throw Error(`${JSON.stringify(s)} did not match pattern ${JSON.stringify(this.re)}`);
+ }
+ if (m[0] !== s) {
+ throw Error(`${JSON.stringify(s)} did not fully match pattern ${JSON.stringify(this.re)}, only matched ${JSON.stringify(m[0])}`);
+ }
+ }
+
+ captures(): TmCaptures {
+ const captures: { [k: string]: { name: string } } = {};
+ for (let i = 0; i < this.names.length; i++) {
+ captures[`${i + 1}`] = { name: this.names[i] };
+ }
+ return captures;
+ }
+}
+
+function include(name: string): TmIncludePattern {
+ return { include: `#${name}` };
+}
+
+function match(name: string, pat: Pattern): TmMatchPattern {
+ return {
+ name: `${name}.fidl`,
+ match: pat.re,
+ captures: pat.captures(),
+ };
+}
+
+function anonMatch(pattern: Pattern | TmPattern): TmPattern {
+ if (pattern instanceof Pattern) {
+ return {
+ match: pattern.re,
+ captures: pattern.captures(),
+ };
+ }
+ return pattern;
+}
+
+function block(args: { name: string, begin: Pattern, end: Pattern, patterns: Array<Pattern | TmPattern> }): TmBlockPattern {
+ return {
+ name: `${args.name}.fidl`,
+ begin: args.begin.re,
+ beginCaptures: args.begin.captures(),
+ end: args.end.re,
+ endCaptures: args.end.captures(),
+ patterns: args.patterns.map(anonMatch),
+ };
+}
+
+
+enum Whitespace {
+ None, Optional
+}
+
+function p(re: string, names?: string[]): Pattern {
+ return new Pattern(re, names || []);
+}
+
+
+function _join(pats: Pattern[], prefix: string, separator: string, suffix: string): Pattern {
+ const re = pats.map(p => p.toString()).join(separator);
+ const names = pats.reduce((ns, p) => [...ns, ...p.names], [] as string[]);
+ return p(prefix + re + suffix, names);
+}
+
+/**
+ * Create a new pattern consisting of adjacent input patterns.
+ * @param pats Patterns
+ */
+function seq(...pats: Pattern[]): Pattern {
+ return _join(pats, '', '', '');
+}
+
+/**
+ * Create a new pattern consisting of adjacent input patterns optionally separated by whitespace.
+ * @param pats Patterns
+ */
+function ws_seq(...pats: Pattern[]): Pattern {
+ return _join(pats, '', '\\s*', '');
+}
+
+function one_of(...pats: Pattern[]): Pattern {
+ return _join(pats, '(?:', '|', ')');
+}
+
+function one_of_words(...words: string[]) {
+ return word(one_of(...words.map(word => p(word))));
+}
+
+function optional(pat: Pattern): Pattern {
+ return p(`(?:${pat})?`, pat.names);
+}
+
+function word(word: Pattern): Pattern {
+ return p(`\\b${word}\\b`, word.names);
+}
+
+function named(pat: Pattern, name: string) {
+ return p(`(${pat.re})`, [name, ...pat.names]);
+}
+
+const NUMERIC_CONSTANT = named(p("-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:(?:[0-9]+\\.?[0-9]*)|(?:\\.[0-9]+))(?:(?:e|E)(?:\\+|-)?[0-9]+)?)\\b"), 'constant.numeric');
+const BOOLEAN_CONSTANT = named(one_of_words('true', 'false'), 'constant.language');
+const STRING_CONSTANT = named(p('"(?:[^\\"]|\\.)*"'), 'string.quoted.double');
+const LITERAL = one_of(NUMERIC_CONSTANT, BOOLEAN_CONSTANT, STRING_CONSTANT);
+
+const IDENTIFIER = p("@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b");
+const QUALIFIED_IDENTIFIER = p(`${IDENTIFIER}(?:\\.${IDENTIFIER})*`);
+
+const NULLABLE = p('([?])?', ['punctuation.nullable']);
+
+function nullable(pat: Pattern): Pattern {
+ return seq(pat, NULLABLE);
+}
+
+const SIZE = seq(p('[:]'), one_of(NUMERIC_CONSTANT, QUALIFIED_IDENTIFIER));
+
+function sized(pat: Pattern, size_required: boolean) {
+ let size = SIZE;
+ if (!size_required) {
+ size = optional(size);
+ }
+ return seq(pat, size);
+}
+
+function keyword(keyword: string, name: string = 'keyword.control') {
+ return word(named(p(keyword), name));
+}
+
+function separator(sep: string) {
+ return named(p(sep), 'punctuation.separator');
+}
+
+const PRIMITIVE_TYPE = p("\\b(bool|float32|float64|int8|int16|int32|int64|uint8|uint16|uint32|uint64)\\b", ['storage.type.basic']);
+const HANDLE_TYPE = named(nullable(p("\\bhandle\\b(?:<(?:process|thread|vmo|event|port|log|socket|eventpair|job|vmar|fifo|timer|channel|interrupt)>)?")), 'storage.type.basic');
+const STRING_TYPE = named(nullable(sized(p(`\\bstring\\b`), false)), 'storage.type.basic');
+const REQUEST_TYPE = named(nullable(seq(keyword('request'), p('<'), named(QUALIFIED_IDENTIFIER, 'entity.type.name'), p('>'))), 'storage.type.basic');
+const BASIC_TYPE = one_of(PRIMITIVE_TYPE, HANDLE_TYPE, STRING_TYPE, REQUEST_TYPE);
+
+const EOL = p('(;)', ['punctuation.terminator']);
+const LIBRARY_NAME = named(QUALIFIED_IDENTIFIER, 'entity.name.type');
+const LOCAL_TYPE = named(IDENTIFIER, 'entity.name.type');
+const NULLABLE_CUSTOM_TYPE = named(nullable(QUALIFIED_IDENTIFIER), 'entity.name.type');
+const VARIABLE = named(IDENTIFIER, 'variable');
+
+const LOOKAHEAD_IDENTIFIER = p('(?=[a-zA-Z_@])');
+
+
+// Checks
+
+QUALIFIED_IDENTIFIER.assert('foo');
+QUALIFIED_IDENTIFIER.assert('foo_bar');
+QUALIFIED_IDENTIFIER.assert('foo.bar.baz');
+
+STRING_TYPE.assert('string');
+STRING_TYPE.assert('string?');
+STRING_TYPE.assert('string:2');
+STRING_TYPE.assert('string:strings_size');
+STRING_TYPE.assert('string:2?');
+STRING_TYPE.assert('string:strings_size?');
+
+NUMERIC_CONSTANT.assert('-42');
+
+
+
+// TODO: support attributes
+const tmLanguage: TmLanguage = {
+ '$schema': tmSchema,
+ name: "FIDL",
+ scopeName: "source.fidl",
+ patterns: [
+ include('comments'),
+
+ // Library declaration
+ match('meta.library', ws_seq(keyword('library'), LIBRARY_NAME, EOL)),
+
+ // Variations of using
+ match('meta.library', ws_seq(keyword('using'), LIBRARY_NAME, EOL)),
+ match('meta.library', ws_seq(keyword('using'), LIBRARY_NAME, keyword('as'), LOCAL_TYPE, EOL)),
+ match('meta.library', ws_seq(keyword('using'), LOCAL_TYPE, separator('='), PRIMITIVE_TYPE, EOL)),
+
+ // Const declaration
+ // TODO: allow string constants
+ match('meta.const', ws_seq(keyword('const'), PRIMITIVE_TYPE, named(IDENTIFIER, 'variable.constant'), separator('='),
+ one_of(QUALIFIED_IDENTIFIER, NUMERIC_CONSTANT, BOOLEAN_CONSTANT), EOL)),
+
+ // Interfaces
+ // TODO: inheritance
+ block({
+ name: 'meta.interface-block',
+ begin: ws_seq(keyword('interface'), LOCAL_TYPE, p('{')),
+ end: p("}"),
+ patterns: [include('method'), include('comments')],
+ }),
+
+ // Enums
+ block({
+ name: 'meta.enum-block',
+ // TODO: need to have a NUMERIC_TYPE
+ begin: ws_seq(keyword('enum'), LOCAL_TYPE, optional(ws_seq(separator(':'), PRIMITIVE_TYPE)), p('{')),
+ end: p("}"),
+ patterns: [include('enum-member'), include('comments')]
+ }),
+
+ // Struct
+ block({
+ name: 'meta.struct-block',
+ begin: ws_seq(keyword('struct'), LOCAL_TYPE, p('{')),
+ end: p("}"),
+ patterns: [include('struct-member'), include('comments')],
+ }),
+
+ // Union
+ block({
+ name: 'meta.union-block',
+ begin: ws_seq(keyword('union'), LOCAL_TYPE, p('{')),
+ end: p("}"),
+ patterns: [include('union-member'), include('comments')],
+ })
+ ],
+ repository: {
+ "comments": {
+ patterns: [
+ block({
+ name: 'comment.block',
+ begin: p("/\\*"),
+ end: p("\\*/"),
+ patterns: [],
+ }),
+ match('invalid.illegal.stray-comment-end', p("\\*/.*\\n")),
+ match('comment.line.double-slash', p('//.*\\n')),
+ ]
+ },
+ "method": {
+ patterns: [
+ block({
+ name: 'meta.method',
+ begin: ws_seq(NUMERIC_CONSTANT, separator(':'), named(IDENTIFIER, 'entity.name.function')),
+ end: EOL,
+ patterns: [
+ include('method-arguments'),
+ separator('->'),
+ ],
+ }),
+ block({
+ name: 'meta.method.event',
+ begin: ws_seq(NUMERIC_CONSTANT, separator(':'), separator('->'), named(IDENTIFIER, 'entity.name.function'), p('[(]')),
+ end: ws_seq(p('[)]'), EOL),
+ patterns: [
+ include('method-argument'),
+ ],
+ }),]
+ },
+ "method-arguments": {
+ patterns: [
+ block({
+ name: 'meta.method.arguments',
+ begin: p("\\("),
+ end: p("\\)"),
+ patterns: [
+ include('method-argument')
+ ]
+ })
+ ]
+ },
+ "method-argument": {
+ patterns: [
+ block({
+ name: "meta.method.argument",
+ begin: LOOKAHEAD_IDENTIFIER,
+ end: ws_seq(named(IDENTIFIER, 'variable.name'), p('(?:(?:,)|(?=\\)))')),
+ patterns: [
+ include('type'),
+ named(IDENTIFIER, 'variable.parameter'),
+ ]
+ })
+ ]
+ },
+ "enum-member": {
+ patterns: [
+ match('meta.enum.member', ws_seq(VARIABLE, separator('='), NUMERIC_CONSTANT, EOL))
+ ]
+ },
+ "struct-member": {
+ patterns: [
+ match('meta.struct.member', ws_seq(BASIC_TYPE, VARIABLE, EOL)),
+ match('meta.struct.member', ws_seq(BASIC_TYPE, VARIABLE, separator('='), LITERAL, EOL)),
+ match('meta.struct.member', ws_seq(NULLABLE_CUSTOM_TYPE, VARIABLE, EOL)),
+ block({
+ name: 'meta.struct.member',
+ begin: LOOKAHEAD_IDENTIFIER,
+ end: ws_seq(VARIABLE, EOL),
+ patterns: [include('type')]
+ }),
+ ]
+ },
+ "union-member": {
+ patterns: [
+ match('meta.union.member', ws_seq(BASIC_TYPE, VARIABLE, EOL)),
+ match('meta.union.member', ws_seq(NULLABLE_CUSTOM_TYPE, VARIABLE, EOL)),
+ block({
+ name: 'meta.union.member',
+ begin: LOOKAHEAD_IDENTIFIER,
+ end: ws_seq(VARIABLE, EOL),
+ patterns: [include('type')]
+ }),
+ ]
+ },
+ "type": {
+ patterns: [
+ include('array-type'),
+ include('vector-type'),
+ anonMatch(BASIC_TYPE),
+ ]
+ },
+ "array-type": {
+ patterns: [
+ block({
+ name: "storage.type.array",
+ begin: seq(keyword('array', 'storage.type.array'), p('<')),
+ end: seq(p(">"), SIZE),
+ patterns: [
+ include('type')
+ ]
+ }),
+ ]
+ },
+ "vector-type": {
+ patterns: [
+ block({
+ name: "storage.type.vector",
+ begin: seq(keyword('vector', 'storage.type.array'), p('<')),
+ end: seq(p(">"), optional(SIZE), NULLABLE),
+ patterns: [
+ include('type'),
+ ]
+ }),
+ ]
+ },
+ },
+};
+
+console.log(JSON.stringify(tmLanguage, null, ' '));
diff --git a/vscode-language-fidl/package.json b/vscode-language-fidl/package.json
index c5cb57b..e1c15c6 100644
--- a/vscode-language-fidl/package.json
+++ b/vscode-language-fidl/package.json
@@ -1,68 +1,69 @@
{
- "name": "language-fidl",
- "displayName": "FIDL Language Support",
- "description": "Support for FIDL files",
- "license": "SEE LICENSE IN LICENSE",
- "version": "0.0.3",
- "publisher": "fuchsia-authors",
- "engines": {
- "vscode": "^1.10.0"
- },
- "categories": [
- "Languages"
- ],
- "activationEvents": [
- "onCommand:extension.goToFidlSource"
- ],
- "main": "./out/extension",
- "contributes": {
- "languages": [
- {
- "id": "fidl",
- "aliases": [
- "FIDL",
- "fidl"
- ],
- "extensions": [
- ".fidl"
- ],
- "configuration": "./language-configuration.json"
- }
+ "name": "language-fidl",
+ "displayName": "FIDL Language Support",
+ "description": "Support for FIDL files",
+ "license": "SEE LICENSE IN LICENSE",
+ "version": "0.0.3",
+ "publisher": "fuchsia-authors",
+ "engines": {
+ "vscode": "^1.10.0"
+ },
+ "categories": [
+ "Programming Languages"
+ ],
+ "activationEvents": [
+ "onCommand:extension.goToFidlSource"
+ ],
+ "main": "./out/extension",
+ "contributes": {
+ "languages": [
+ {
+ "id": "fidl",
+ "aliases": [
+ "FIDL",
+ "fidl"
],
- "grammars": [
- {
- "language": "fidl",
- "scopeName": "source.fidl",
- "path": "./syntaxes/fidl.tmLanguage.json"
- }
+ "extensions": [
+ ".fidl"
],
- "commands": [
- {
- "command": "extension.goToFidlSource",
- "title": "FIDL: Go To Source"
- }
- ]
- },
- "homepage": "https://fuchsia.googlesource.com/vscode-language-fidl",
- "repository": {
- "type": "git",
- "url": "https://fuchsia.googlesource.com/vscode-language-fidl"
- },
- "icon": "images/fuchsia_logo_128x128.png",
- "scripts": {
- "vscode:prepublish": "npm run compile",
- "compile": "tsc -p ./",
- "watch": "tsc -watch -p ./",
- "postinstall": "node ./node_modules/vscode/bin/install",
- "test": "mocha --require ts-node/register ./test/*.test.ts"
- },
- "devDependencies": {
- "@types/mocha": "^2.2.48",
- "@types/node": "^7.0.43",
- "mocha": "^5.0.1",
- "ts-node": "^5.0.1",
- "tslint": "^5.8.0",
- "typescript": "^2.6.1",
- "vscode": "^1.1.6"
- }
-}
\ No newline at end of file
+ "configuration": "./language-configuration.json"
+ }
+ ],
+ "grammars": [
+ {
+ "language": "fidl",
+ "scopeName": "source.fidl",
+ "path": "./syntaxes/fidl.tmLanguage.json"
+ }
+ ],
+ "commands": [
+ {
+ "command": "extension.goToFidlSource",
+ "title": "FIDL: Go To Source"
+ }
+ ]
+ },
+ "homepage": "https://fuchsia.googlesource.com/vscode-language-fidl",
+ "repository": {
+ "type": "git",
+ "url": "https://fuchsia.googlesource.com/vscode-language-fidl"
+ },
+ "icon": "images/fuchsia_logo_128x128.png",
+ "scripts": {
+ "vscode:prepublish": "npm run compile",
+ "compile": "tsc -p ./",
+ "watch": "tsc -watch -p ./",
+ "postinstall": "node ./node_modules/vscode/bin/install",
+ "test": "mocha --require ts-node/register ./test/*.test.ts",
+ "build-syntax": "ts-node ./generate-syntax.ts > ./syntaxes/fidl.tmLanguage.json"
+ },
+ "devDependencies": {
+ "@types/mocha": "^2.2.48",
+ "@types/node": "^7.0.43",
+ "mocha": "^5.0.1",
+ "ts-node": "^5.0.1",
+ "tslint": "^5.8.0",
+ "typescript": "^2.6.1",
+ "vscode": "^1.1.6"
+ }
+}
diff --git a/vscode-language-fidl/syntaxes/fidl.tmLanguage.json b/vscode-language-fidl/syntaxes/fidl.tmLanguage.json
index 2ad9785..40657da 100644
--- a/vscode-language-fidl/syntaxes/fidl.tmLanguage.json
+++ b/vscode-language-fidl/syntaxes/fidl.tmLanguage.json
@@ -1,221 +1,676 @@
{
- "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
- "name": "FIDL",
- "patterns": [
- {
- "include": "#comments"
- },
- {
- "include": "#literal"
- },
- {
- "match": "\\b(import|module)\\b",
- "name": "keyword.control.fidl"
- },
- {
- "match": "\\b(default|const)\\b",
- "name": "keyword.fidl"
- },
- {
- "match": "\\b(bool|float|double|int8|int16|int32|int64|uint8|uint16|uint32|uint64|string)\\b",
- "name": "storage.type.basic.fidl"
- },
- {
- "match": "\\b(handle|array|map)\\b",
- "name": "storage.type.compound.fidl"
- },
- {
- "begin": "\\b(struct|union)\\b\\s*([_A-Za-z][_A-Za-z0-9]*)\\b",
- "beginCaptures": {
- "1": {
- "name": "storage.type.fidl"
- },
- "2": {
- "name": "entity.name.type.fidl"
- }
- },
- "end": "(?<=\\})",
- "name": "meta.struct-union-block.fidl",
- "patterns": [
- {
- "begin": "\\{",
- "beginCaptures": {
- "0": {
- "name": "punctuation.section.block.begin.fidl"
- }
- },
- "end": "\\}",
- "endCaptures": {
- "0": {
- "name": "punctuation.section.block.end.fidl"
- }
- },
- "name": "meta.block.cpp",
- "patterns": [
- {
- "include": "$base"
- }
- ]
- }
- ]
- },
- {
- "begin": "\\b(interface)\\b\\s*([_A-Za-z][_A-Za-z0-9]*)\\b",
- "beginCaptures": {
- "1": {
- "name": "storage.type.fidl"
- },
- "2": {
- "name": "entity.name.type.fidl"
- }
- },
- "end": "(?<=\\})",
- "name": "meta.interface-block.fidl",
- "patterns": [
- {
- "begin": "\\b([_A-Za-z][_A-Za-z0-9]*)\\b",
- "beginCaptures": {
- "1": {
- "name": "entity.name.function.fidl"
- }
- },
- "end": "(?<=;)",
- "patterns": [
- {
- "include": "$base"
- }
- ]
- },
- {
- "include": "$base"
- }
- ]
- },
- {
- "begin": "\\b(enum)\\b\\s*([_A-Za-z][_A-Za-z0-9]*)\\b",
- "beginCaptures": {
- "1": {
- "name": "storage.type.fidl"
- },
- "2": {
- "name": "entity.name.type.fidl"
- }
- },
- "end": "(?<=\\})",
- "name": "meta.enum-block.fidl",
- "patterns": [
- {
- "include": "$base"
- }
- ]
- }
- ],
- "repository": {
- "comments": {
- "patterns": [
- {
- "begin": "/\\*",
- "beginCaptures": {
- "0": {
- "name": "punctuation.definition.comment.begin.fidl"
- }
- },
- "end": "\\*/",
- "endCaptures": {
- "0": {
- "name": "punctuation.definition.comment.end.fidl"
- }
- },
- "name": "comment.block.fidl"
- },
- {
- "match": "\\*/.*\\n",
- "name": "invalid.illegal.stray-comment-end.fidl"
- },
- {
- "begin": "(^[ \\t]+)?(?=//)",
- "beginCaptures": {
- "1": {
- "name": "punctuation.whitespace.comment.leading.fidl"
- }
- },
- "end": "(?!\\G)",
- "patterns": [
- {
- "begin": "//",
- "beginCaptures": {
- "0": {
- "name": "punctuation.definition.comment.fidl"
- }
- },
- "end": "\\n",
- "name": "comment.line.double-slash",
- "patterns": [
- {
- "match": "(?>\\\\\\s*\\n)",
- "name": "punctuation.separator.continuation.fidl"
- }
- ]
- }
- ]
- }
- ]
- },
- "literal": {
- "patterns": [
- {
- "include": "#string"
- },
- {
- "match": "\\b(true|false)\\b",
- "name": "constant.bool.language.fidl"
- },
- {
- "match": "\\b(float|double)(.INFINITY|.NEGATIVE_INFINITY|.NAN)\\b",
- "name": "constant.float.language.fidl"
- },
- {
- "match": "\\b((0(x|X)[0-9a-fA-F]*)|(([0-9]+\\.?[0-9]*)|(\\.[0-9]+))((e|E)(\\+|-)?[0-9]+)?)\\b",
- "name": "constant.numeric.c"
- }
- ]
- },
- "string": {
- "patterns": [
- {
- "begin": "\"",
- "beginCaptures": {
- "0": {
- "name": "punctuation.definition.string.begin.fidl"
- }
- },
- "end": "\"",
- "endCaptures": {
- "0": {
- "name": "punctuation.definition.string.end.fidl"
- }
- },
- "name": "string.quoted.double.fidl",
- "patterns": [
- {
- "include": "#string_escaped_char"
- }
- ]
- }
- ]
- },
- "string_escaped_char": {
- "patterns": [
- {
- "match": "\\\\(\\\\|[abefnprtv'\"?]|[0-3]\\d{,2}|[4-7]\\d?|x[a-fA-F0-9]{,2}|u[a-fA-F0-9]{,4}|U[a-fA-F0-9]{,8})",
- "name": "constant.character.escape.fidl"
- },
- {
- "match": "\\\\.",
- "name": "invalid.illegal.unknown-escape.fidl"
- }
- ]
- }
- },
- "scopeName": "source.fidl"
-}
\ No newline at end of file
+ "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
+ "name": "FIDL",
+ "scopeName": "source.fidl",
+ "patterns": [
+ {
+ "include": "#comments"
+ },
+ {
+ "name": "meta.library.fidl",
+ "match": "\\b(library)\\b\\s*(@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*)\\s*(;)",
+ "captures": {
+ "1": {
+ "name": "keyword.control"
+ },
+ "2": {
+ "name": "entity.name.type"
+ },
+ "3": {
+ "name": "punctuation.terminator"
+ }
+ }
+ },
+ {
+ "name": "meta.library.fidl",
+ "match": "\\b(using)\\b\\s*(@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*)\\s*(;)",
+ "captures": {
+ "1": {
+ "name": "keyword.control"
+ },
+ "2": {
+ "name": "entity.name.type"
+ },
+ "3": {
+ "name": "punctuation.terminator"
+ }
+ }
+ },
+ {
+ "name": "meta.library.fidl",
+ "match": "\\b(using)\\b\\s*(@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*)\\s*\\b(as)\\b\\s*(@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)\\s*(;)",
+ "captures": {
+ "1": {
+ "name": "keyword.control"
+ },
+ "2": {
+ "name": "entity.name.type"
+ },
+ "3": {
+ "name": "keyword.control"
+ },
+ "4": {
+ "name": "entity.name.type"
+ },
+ "5": {
+ "name": "punctuation.terminator"
+ }
+ }
+ },
+ {
+ "name": "meta.library.fidl",
+ "match": "\\b(using)\\b\\s*(@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)\\s*(=)\\s*\\b(bool|float32|float64|int8|int16|int32|int64|uint8|uint16|uint32|uint64)\\b\\s*(;)",
+ "captures": {
+ "1": {
+ "name": "keyword.control"
+ },
+ "2": {
+ "name": "entity.name.type"
+ },
+ "3": {
+ "name": "punctuation.separator"
+ },
+ "4": {
+ "name": "storage.type.basic"
+ },
+ "5": {
+ "name": "punctuation.terminator"
+ }
+ }
+ },
+ {
+ "name": "meta.const.fidl",
+ "match": "\\b(const)\\b\\s*\\b(bool|float32|float64|int8|int16|int32|int64|uint8|uint16|uint32|uint64)\\b\\s*(@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)\\s*(=)\\s*(?:@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*|(-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:(?:[0-9]+\\.?[0-9]*)|(?:\\.[0-9]+))(?:(?:e|E)(?:\\+|-)?[0-9]+)?)\\b)|(\\b(?:true|false)\\b))\\s*(;)",
+ "captures": {
+ "1": {
+ "name": "keyword.control"
+ },
+ "2": {
+ "name": "storage.type.basic"
+ },
+ "3": {
+ "name": "variable.constant"
+ },
+ "4": {
+ "name": "punctuation.separator"
+ },
+ "5": {
+ "name": "constant.numeric"
+ },
+ "6": {
+ "name": "constant.language"
+ },
+ "7": {
+ "name": "punctuation.terminator"
+ }
+ }
+ },
+ {
+ "name": "meta.interface-block.fidl",
+ "begin": "\\b(interface)\\b\\s*(@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)\\s*{",
+ "beginCaptures": {
+ "1": {
+ "name": "keyword.control"
+ },
+ "2": {
+ "name": "entity.name.type"
+ }
+ },
+ "end": "}",
+ "endCaptures": {},
+ "patterns": [
+ {
+ "include": "#method"
+ },
+ {
+ "include": "#comments"
+ }
+ ]
+ },
+ {
+ "name": "meta.enum-block.fidl",
+ "begin": "\\b(enum)\\b\\s*(@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)\\s*(?:(:)\\s*\\b(bool|float32|float64|int8|int16|int32|int64|uint8|uint16|uint32|uint64)\\b)?\\s*{",
+ "beginCaptures": {
+ "1": {
+ "name": "keyword.control"
+ },
+ "2": {
+ "name": "entity.name.type"
+ },
+ "3": {
+ "name": "punctuation.separator"
+ },
+ "4": {
+ "name": "storage.type.basic"
+ }
+ },
+ "end": "}",
+ "endCaptures": {},
+ "patterns": [
+ {
+ "include": "#enum-member"
+ },
+ {
+ "include": "#comments"
+ }
+ ]
+ },
+ {
+ "name": "meta.struct-block.fidl",
+ "begin": "\\b(struct)\\b\\s*(@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)\\s*{",
+ "beginCaptures": {
+ "1": {
+ "name": "keyword.control"
+ },
+ "2": {
+ "name": "entity.name.type"
+ }
+ },
+ "end": "}",
+ "endCaptures": {},
+ "patterns": [
+ {
+ "include": "#struct-member"
+ },
+ {
+ "include": "#comments"
+ }
+ ]
+ },
+ {
+ "name": "meta.union-block.fidl",
+ "begin": "\\b(union)\\b\\s*(@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)\\s*{",
+ "beginCaptures": {
+ "1": {
+ "name": "keyword.control"
+ },
+ "2": {
+ "name": "entity.name.type"
+ }
+ },
+ "end": "}",
+ "endCaptures": {},
+ "patterns": [
+ {
+ "include": "#union-member"
+ },
+ {
+ "include": "#comments"
+ }
+ ]
+ }
+ ],
+ "repository": {
+ "comments": {
+ "patterns": [
+ {
+ "name": "comment.block.fidl",
+ "begin": "/\\*",
+ "beginCaptures": {},
+ "end": "\\*/",
+ "endCaptures": {},
+ "patterns": []
+ },
+ {
+ "name": "invalid.illegal.stray-comment-end.fidl",
+ "match": "\\*/.*\\n",
+ "captures": {}
+ },
+ {
+ "name": "comment.line.double-slash.fidl",
+ "match": "//.*\\n",
+ "captures": {}
+ }
+ ]
+ },
+ "method": {
+ "patterns": [
+ {
+ "name": "meta.method.fidl",
+ "begin": "(-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:(?:[0-9]+\\.?[0-9]*)|(?:\\.[0-9]+))(?:(?:e|E)(?:\\+|-)?[0-9]+)?)\\b)\\s*(:)\\s*(@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)",
+ "beginCaptures": {
+ "1": {
+ "name": "constant.numeric"
+ },
+ "2": {
+ "name": "punctuation.separator"
+ },
+ "3": {
+ "name": "entity.name.function"
+ }
+ },
+ "end": "(;)",
+ "endCaptures": {
+ "1": {
+ "name": "punctuation.terminator"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#method-arguments"
+ },
+ {
+ "match": "(->)",
+ "captures": {
+ "1": {
+ "name": "punctuation.separator"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "name": "meta.method.event.fidl",
+ "begin": "(-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:(?:[0-9]+\\.?[0-9]*)|(?:\\.[0-9]+))(?:(?:e|E)(?:\\+|-)?[0-9]+)?)\\b)\\s*(:)\\s*(->)\\s*(@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)\\s*[(]",
+ "beginCaptures": {
+ "1": {
+ "name": "constant.numeric"
+ },
+ "2": {
+ "name": "punctuation.separator"
+ },
+ "3": {
+ "name": "punctuation.separator"
+ },
+ "4": {
+ "name": "entity.name.function"
+ }
+ },
+ "end": "[)]\\s*(;)",
+ "endCaptures": {
+ "1": {
+ "name": "punctuation.terminator"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#method-argument"
+ }
+ ]
+ }
+ ]
+ },
+ "method-arguments": {
+ "patterns": [
+ {
+ "name": "meta.method.arguments.fidl",
+ "begin": "\\(",
+ "beginCaptures": {},
+ "end": "\\)",
+ "endCaptures": {},
+ "patterns": [
+ {
+ "include": "#method-argument"
+ }
+ ]
+ }
+ ]
+ },
+ "method-argument": {
+ "patterns": [
+ {
+ "name": "meta.method.argument.fidl",
+ "begin": "(?=[a-zA-Z_@])",
+ "beginCaptures": {},
+ "end": "(@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)\\s*(?:(?:,)|(?=\\)))",
+ "endCaptures": {
+ "1": {
+ "name": "variable.name"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#type"
+ },
+ {
+ "match": "(@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)",
+ "captures": {
+ "1": {
+ "name": "variable.parameter"
+ }
+ }
+ }
+ ]
+ }
+ ]
+ },
+ "enum-member": {
+ "patterns": [
+ {
+ "name": "meta.enum.member.fidl",
+ "match": "(@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)\\s*(=)\\s*(-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:(?:[0-9]+\\.?[0-9]*)|(?:\\.[0-9]+))(?:(?:e|E)(?:\\+|-)?[0-9]+)?)\\b)\\s*(;)",
+ "captures": {
+ "1": {
+ "name": "variable"
+ },
+ "2": {
+ "name": "punctuation.separator"
+ },
+ "3": {
+ "name": "constant.numeric"
+ },
+ "4": {
+ "name": "punctuation.terminator"
+ }
+ }
+ }
+ ]
+ },
+ "struct-member": {
+ "patterns": [
+ {
+ "name": "meta.struct.member.fidl",
+ "match": "(?:\\b(bool|float32|float64|int8|int16|int32|int64|uint8|uint16|uint32|uint64)\\b|(\\bhandle\\b(?:<(?:process|thread|vmo|event|port|log|socket|eventpair|job|vmar|fifo|timer|channel|interrupt)>)?([?])?)|(\\bstring\\b(?:[:](?:(-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:(?:[0-9]+\\.?[0-9]*)|(?:\\.[0-9]+))(?:(?:e|E)(?:\\+|-)?[0-9]+)?)\\b)|@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*))?([?])?)|(\\b(request)\\b<(@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*)>([?])?))\\s*(@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)\\s*(;)",
+ "captures": {
+ "1": {
+ "name": "storage.type.basic"
+ },
+ "2": {
+ "name": "storage.type.basic"
+ },
+ "3": {
+ "name": "punctuation.nullable"
+ },
+ "4": {
+ "name": "storage.type.basic"
+ },
+ "5": {
+ "name": "constant.numeric"
+ },
+ "6": {
+ "name": "punctuation.nullable"
+ },
+ "7": {
+ "name": "storage.type.basic"
+ },
+ "8": {
+ "name": "keyword.control"
+ },
+ "9": {
+ "name": "entity.type.name"
+ },
+ "10": {
+ "name": "punctuation.nullable"
+ },
+ "11": {
+ "name": "variable"
+ },
+ "12": {
+ "name": "punctuation.terminator"
+ }
+ }
+ },
+ {
+ "name": "meta.struct.member.fidl",
+ "match": "(?:\\b(bool|float32|float64|int8|int16|int32|int64|uint8|uint16|uint32|uint64)\\b|(\\bhandle\\b(?:<(?:process|thread|vmo|event|port|log|socket|eventpair|job|vmar|fifo|timer|channel|interrupt)>)?([?])?)|(\\bstring\\b(?:[:](?:(-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:(?:[0-9]+\\.?[0-9]*)|(?:\\.[0-9]+))(?:(?:e|E)(?:\\+|-)?[0-9]+)?)\\b)|@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*))?([?])?)|(\\b(request)\\b<(@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*)>([?])?))\\s*(@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)\\s*(=)\\s*(?:(-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:(?:[0-9]+\\.?[0-9]*)|(?:\\.[0-9]+))(?:(?:e|E)(?:\\+|-)?[0-9]+)?)\\b)|(\\b(?:true|false)\\b)|(\"(?:[^\\\"]|\\.)*\"))\\s*(;)",
+ "captures": {
+ "1": {
+ "name": "storage.type.basic"
+ },
+ "2": {
+ "name": "storage.type.basic"
+ },
+ "3": {
+ "name": "punctuation.nullable"
+ },
+ "4": {
+ "name": "storage.type.basic"
+ },
+ "5": {
+ "name": "constant.numeric"
+ },
+ "6": {
+ "name": "punctuation.nullable"
+ },
+ "7": {
+ "name": "storage.type.basic"
+ },
+ "8": {
+ "name": "keyword.control"
+ },
+ "9": {
+ "name": "entity.type.name"
+ },
+ "10": {
+ "name": "punctuation.nullable"
+ },
+ "11": {
+ "name": "variable"
+ },
+ "12": {
+ "name": "punctuation.separator"
+ },
+ "13": {
+ "name": "constant.numeric"
+ },
+ "14": {
+ "name": "constant.language"
+ },
+ "15": {
+ "name": "string.quoted.double"
+ },
+ "16": {
+ "name": "punctuation.terminator"
+ }
+ }
+ },
+ {
+ "name": "meta.struct.member.fidl",
+ "match": "(@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*([?])?)\\s*(@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)\\s*(;)",
+ "captures": {
+ "1": {
+ "name": "entity.name.type"
+ },
+ "2": {
+ "name": "punctuation.nullable"
+ },
+ "3": {
+ "name": "variable"
+ },
+ "4": {
+ "name": "punctuation.terminator"
+ }
+ }
+ },
+ {
+ "name": "meta.struct.member.fidl",
+ "begin": "(?=[a-zA-Z_@])",
+ "beginCaptures": {},
+ "end": "(@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)\\s*(;)",
+ "endCaptures": {
+ "1": {
+ "name": "variable"
+ },
+ "2": {
+ "name": "punctuation.terminator"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#type"
+ }
+ ]
+ }
+ ]
+ },
+ "union-member": {
+ "patterns": [
+ {
+ "name": "meta.union.member.fidl",
+ "match": "(?:\\b(bool|float32|float64|int8|int16|int32|int64|uint8|uint16|uint32|uint64)\\b|(\\bhandle\\b(?:<(?:process|thread|vmo|event|port|log|socket|eventpair|job|vmar|fifo|timer|channel|interrupt)>)?([?])?)|(\\bstring\\b(?:[:](?:(-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:(?:[0-9]+\\.?[0-9]*)|(?:\\.[0-9]+))(?:(?:e|E)(?:\\+|-)?[0-9]+)?)\\b)|@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*))?([?])?)|(\\b(request)\\b<(@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*)>([?])?))\\s*(@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)\\s*(;)",
+ "captures": {
+ "1": {
+ "name": "storage.type.basic"
+ },
+ "2": {
+ "name": "storage.type.basic"
+ },
+ "3": {
+ "name": "punctuation.nullable"
+ },
+ "4": {
+ "name": "storage.type.basic"
+ },
+ "5": {
+ "name": "constant.numeric"
+ },
+ "6": {
+ "name": "punctuation.nullable"
+ },
+ "7": {
+ "name": "storage.type.basic"
+ },
+ "8": {
+ "name": "keyword.control"
+ },
+ "9": {
+ "name": "entity.type.name"
+ },
+ "10": {
+ "name": "punctuation.nullable"
+ },
+ "11": {
+ "name": "variable"
+ },
+ "12": {
+ "name": "punctuation.terminator"
+ }
+ }
+ },
+ {
+ "name": "meta.union.member.fidl",
+ "match": "(@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*([?])?)\\s*(@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)\\s*(;)",
+ "captures": {
+ "1": {
+ "name": "entity.name.type"
+ },
+ "2": {
+ "name": "punctuation.nullable"
+ },
+ "3": {
+ "name": "variable"
+ },
+ "4": {
+ "name": "punctuation.terminator"
+ }
+ }
+ },
+ {
+ "name": "meta.union.member.fidl",
+ "begin": "(?=[a-zA-Z_@])",
+ "beginCaptures": {},
+ "end": "(@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)\\s*(;)",
+ "endCaptures": {
+ "1": {
+ "name": "variable"
+ },
+ "2": {
+ "name": "punctuation.terminator"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#type"
+ }
+ ]
+ }
+ ]
+ },
+ "type": {
+ "patterns": [
+ {
+ "include": "#array-type"
+ },
+ {
+ "include": "#vector-type"
+ },
+ {
+ "match": "(?:\\b(bool|float32|float64|int8|int16|int32|int64|uint8|uint16|uint32|uint64)\\b|(\\bhandle\\b(?:<(?:process|thread|vmo|event|port|log|socket|eventpair|job|vmar|fifo|timer|channel|interrupt)>)?([?])?)|(\\bstring\\b(?:[:](?:(-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:(?:[0-9]+\\.?[0-9]*)|(?:\\.[0-9]+))(?:(?:e|E)(?:\\+|-)?[0-9]+)?)\\b)|@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*))?([?])?)|(\\b(request)\\b<(@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*)>([?])?))",
+ "captures": {
+ "1": {
+ "name": "storage.type.basic"
+ },
+ "2": {
+ "name": "storage.type.basic"
+ },
+ "3": {
+ "name": "punctuation.nullable"
+ },
+ "4": {
+ "name": "storage.type.basic"
+ },
+ "5": {
+ "name": "constant.numeric"
+ },
+ "6": {
+ "name": "punctuation.nullable"
+ },
+ "7": {
+ "name": "storage.type.basic"
+ },
+ "8": {
+ "name": "keyword.control"
+ },
+ "9": {
+ "name": "entity.type.name"
+ },
+ "10": {
+ "name": "punctuation.nullable"
+ }
+ }
+ }
+ ]
+ },
+ "array-type": {
+ "patterns": [
+ {
+ "name": "storage.type.array.fidl",
+ "begin": "\\b(array)\\b<",
+ "beginCaptures": {
+ "1": {
+ "name": "storage.type.array"
+ }
+ },
+ "end": ">[:](?:(-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:(?:[0-9]+\\.?[0-9]*)|(?:\\.[0-9]+))(?:(?:e|E)(?:\\+|-)?[0-9]+)?)\\b)|@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*)",
+ "endCaptures": {
+ "1": {
+ "name": "constant.numeric"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#type"
+ }
+ ]
+ }
+ ]
+ },
+ "vector-type": {
+ "patterns": [
+ {
+ "name": "storage.type.vector.fidl",
+ "begin": "\\b(vector)\\b<",
+ "beginCaptures": {
+ "1": {
+ "name": "storage.type.array"
+ }
+ },
+ "end": ">(?:[:](?:(-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:(?:[0-9]+\\.?[0-9]*)|(?:\\.[0-9]+))(?:(?:e|E)(?:\\+|-)?[0-9]+)?)\\b)|@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.@?\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*))?([?])?",
+ "endCaptures": {
+ "1": {
+ "name": "constant.numeric"
+ },
+ "2": {
+ "name": "punctuation.nullable"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#type"
+ }
+ ]
+ }
+ ]
+ }
+ }
+}