[vscode] Support for >2 type constraints
The previous implementation failed for this case because we attempted to
use repeated capture groups. When a capture group is repeated, like it
was in the now-removed `comma_separated` helper, only the last match is
retained. Thus, the regex template `(?:${capt_1})(?:,\\s*${capt_2})*`
will fail for a case like `:A, B, C`. This is because `A` will always
match `capt_1`, but both `B` and `C` will be caught by the repeated
`capt_2`, with only the last match being retained. The upshot is that
for cases with >2 matches, only the first and last will have the proper
names applied via tmLanguage's "captures" feature, leaving the middle
instance unhighlighted.
This change uses a different strategy: `type-constraints` are now their
own repository definition, always checked as the last possible
constituent pattern on a `type-constructor`. This allows for repeated
highlighting, allowing constraints lists to be of arbitrary length.
Change-Id: I41b16b8cd1414fcce4e02c66f1e2bf08f6451dd5
Reviewed-on: https://fuchsia-review.googlesource.com/c/fidl-misc/+/662244
Reviewed-by: Mitchell Kember <mkember@google.com>
diff --git a/vscode-language-fidl/syntaxes/fidl.tmLanguage.json b/vscode-language-fidl/syntaxes/fidl.tmLanguage.json
index f639f2a..b709d65 100644
--- a/vscode-language-fidl/syntaxes/fidl.tmLanguage.json
+++ b/vscode-language-fidl/syntaxes/fidl.tmLanguage.json
@@ -62,8 +62,8 @@
},
{
"name": "meta.type-alias.fidl",
- "match": "\\b(alias)\\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)*)\\s*(?::\\s*(?:((?:(?:(-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:0(?:b|B)[01]*)|(?:(?:[0-9]+\\.?[0-9]*)|(?:\\.[0-9]+))(?:(?:e|E)(?:\\+|-)?[0-9]+)?)\\b)|(\\b(?:true|false)\\b)|(\"(?:[^\\\"]|\\.)*\"))|\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*))|(<)\\s*(?:((?:(?:(-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:0(?:b|B)[01]*)|(?:(?:[0-9]+\\.?[0-9]*)|(?:\\.[0-9]+))(?:(?:e|E)(?:\\+|-)?[0-9]+)?)\\b)|(\\b(?:true|false)\\b)|(\"(?:[^\\\"]|\\.)*\"))|\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*)))(?:,\\s*((?:(?:(-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:0(?:b|B)[01]*)|(?:(?:[0-9]+\\.?[0-9]*)|(?:\\.[0-9]+))(?:(?:e|E)(?:\\+|-)?[0-9]+)?)\\b)|(\\b(?:true|false)\\b)|(\"(?:[^\\\"]|\\.)*\"))|\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*)))*\\s*(>)))?",
- "captures": {
+ "begin": "\\b(alias)\\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)*)",
+ "beginCaptures": {
"1": {
"name": "keyword.control"
},
@@ -75,50 +75,19 @@
},
"4": {
"name": "entity.name.type"
- },
- "5": {
- "name": "storage.type.constraint"
- },
- "6": {
- "name": "constant.numeric"
- },
- "7": {
- "name": "constant.language"
- },
- "8": {
- "name": "string.quoted.double"
- },
- "9": {
- "name": "punctuation.bracket.angle"
- },
- "10": {
- "name": "storage.type.constraint"
- },
- "11": {
- "name": "constant.numeric"
- },
- "12": {
- "name": "constant.language"
- },
- "13": {
- "name": "string.quoted.double"
- },
- "14": {
- "name": "storage.type.constraint"
- },
- "15": {
- "name": "constant.numeric"
- },
- "16": {
- "name": "constant.language"
- },
- "17": {
- "name": "string.quoted.double"
- },
- "18": {
- "name": "punctuation.bracket.angle"
}
- }
+ },
+ "end": "(;)",
+ "endCaptures": {
+ "1": {
+ "name": "punctuation.terminator"
+ }
+ },
+ "patterns": [
+ {
+ "include": "#type-constructor"
+ }
+ ]
},
{
"name": "meta.const.fidl",
@@ -419,6 +388,9 @@
},
{
"include": "#type-constructor-reference"
+ },
+ {
+ "include": "#type-constraints"
}
]
},
@@ -453,51 +425,8 @@
"name": "entity.name.type"
}
},
- "end": "}\\s*(?::\\s*(?:((?:(?:(-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:0(?:b|B)[01]*)|(?:(?:[0-9]+\\.?[0-9]*)|(?:\\.[0-9]+))(?:(?:e|E)(?:\\+|-)?[0-9]+)?)\\b)|(\\b(?:true|false)\\b)|(\"(?:[^\\\"]|\\.)*\"))|\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*))|(<)\\s*(?:((?:(?:(-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:0(?:b|B)[01]*)|(?:(?:[0-9]+\\.?[0-9]*)|(?:\\.[0-9]+))(?:(?:e|E)(?:\\+|-)?[0-9]+)?)\\b)|(\\b(?:true|false)\\b)|(\"(?:[^\\\"]|\\.)*\"))|\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*)))(?:,\\s*((?:(?:(-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:0(?:b|B)[01]*)|(?:(?:[0-9]+\\.?[0-9]*)|(?:\\.[0-9]+))(?:(?:e|E)(?:\\+|-)?[0-9]+)?)\\b)|(\\b(?:true|false)\\b)|(\"(?:[^\\\"]|\\.)*\"))|\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*)))*\\s*(>)))?",
- "endCaptures": {
- "1": {
- "name": "storage.type.constraint"
- },
- "2": {
- "name": "constant.numeric"
- },
- "3": {
- "name": "constant.language"
- },
- "4": {
- "name": "string.quoted.double"
- },
- "5": {
- "name": "punctuation.bracket.angle"
- },
- "6": {
- "name": "storage.type.constraint"
- },
- "7": {
- "name": "constant.numeric"
- },
- "8": {
- "name": "constant.language"
- },
- "9": {
- "name": "string.quoted.double"
- },
- "10": {
- "name": "storage.type.constraint"
- },
- "11": {
- "name": "constant.numeric"
- },
- "12": {
- "name": "constant.language"
- },
- "13": {
- "name": "string.quoted.double"
- },
- "14": {
- "name": "punctuation.bracket.angle"
- }
- },
+ "end": "}",
+ "endCaptures": {},
"patterns": [
{
"include": "#comments"
@@ -546,7 +475,7 @@
"name": "entity.name.type"
}
},
- "end": "(?:,\\s*(?:(?:(-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:0(?:b|B)[01]*)|(?:(?:[0-9]+\\.?[0-9]*)|(?:\\.[0-9]+))(?:(?:e|E)(?:\\+|-)?[0-9]+)?)\\b)|(\\b(?:true|false)\\b)|(\"(?:[^\\\"]|\\.)*\"))|\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*))?\\s*>\\s*(?::\\s*(?:((?:(?:(-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:0(?:b|B)[01]*)|(?:(?:[0-9]+\\.?[0-9]*)|(?:\\.[0-9]+))(?:(?:e|E)(?:\\+|-)?[0-9]+)?)\\b)|(\\b(?:true|false)\\b)|(\"(?:[^\\\"]|\\.)*\"))|\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*))|(<)\\s*(?:((?:(?:(-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:0(?:b|B)[01]*)|(?:(?:[0-9]+\\.?[0-9]*)|(?:\\.[0-9]+))(?:(?:e|E)(?:\\+|-)?[0-9]+)?)\\b)|(\\b(?:true|false)\\b)|(\"(?:[^\\\"]|\\.)*\"))|\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*)))(?:,\\s*((?:(?:(-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:0(?:b|B)[01]*)|(?:(?:[0-9]+\\.?[0-9]*)|(?:\\.[0-9]+))(?:(?:e|E)(?:\\+|-)?[0-9]+)?)\\b)|(\\b(?:true|false)\\b)|(\"(?:[^\\\"]|\\.)*\"))|\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*)))*\\s*(>)))?",
+ "end": "(?:,\\s*(?:(?:(-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:0(?:b|B)[01]*)|(?:(?:[0-9]+\\.?[0-9]*)|(?:\\.[0-9]+))(?:(?:e|E)(?:\\+|-)?[0-9]+)?)\\b)|(\\b(?:true|false)\\b)|(\"(?:[^\\\"]|\\.)*\"))|\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*))?\\s*>",
"endCaptures": {
"1": {
"name": "constant.numeric"
@@ -556,48 +485,6 @@
},
"3": {
"name": "string.quoted.double"
- },
- "4": {
- "name": "storage.type.constraint"
- },
- "5": {
- "name": "constant.numeric"
- },
- "6": {
- "name": "constant.language"
- },
- "7": {
- "name": "string.quoted.double"
- },
- "8": {
- "name": "punctuation.bracket.angle"
- },
- "9": {
- "name": "storage.type.constraint"
- },
- "10": {
- "name": "constant.numeric"
- },
- "11": {
- "name": "constant.language"
- },
- "12": {
- "name": "string.quoted.double"
- },
- "13": {
- "name": "storage.type.constraint"
- },
- "14": {
- "name": "constant.numeric"
- },
- "15": {
- "name": "constant.language"
- },
- "16": {
- "name": "string.quoted.double"
- },
- "17": {
- "name": "punctuation.bracket.angle"
}
},
"patterns": [
@@ -618,7 +505,7 @@
"patterns": [
{
"name": "meta.type-constructor-reference.fidl",
- "match": "(\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.\\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)*)\\s*(?:(,)\\s*(?:(-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:0(?:b|B)[01]*)|(?:(?:[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)*))?\\s*(>))?\\s*(?::\\s*(?:((?:(?:(-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:0(?:b|B)[01]*)|(?:(?:[0-9]+\\.?[0-9]*)|(?:\\.[0-9]+))(?:(?:e|E)(?:\\+|-)?[0-9]+)?)\\b)|(\\b(?:true|false)\\b)|(\"(?:[^\\\"]|\\.)*\"))|\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*))|(<)\\s*(?:((?:(?:(-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:0(?:b|B)[01]*)|(?:(?:[0-9]+\\.?[0-9]*)|(?:\\.[0-9]+))(?:(?:e|E)(?:\\+|-)?[0-9]+)?)\\b)|(\\b(?:true|false)\\b)|(\"(?:[^\\\"]|\\.)*\"))|\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*)))(?:,\\s*((?:(?:(-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:0(?:b|B)[01]*)|(?:(?:[0-9]+\\.?[0-9]*)|(?:\\.[0-9]+))(?:(?:e|E)(?:\\+|-)?[0-9]+)?)\\b)|(\\b(?:true|false)\\b)|(\"(?:[^\\\"]|\\.)*\"))|\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*)))*\\s*(>)))?",
+ "match": "(\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.\\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)*)\\s*(?:(,)\\s*(?:(-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:0(?:b|B)[01]*)|(?:(?:[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)*))?\\s*(>))?",
"captures": {
"1": {
"name": "entity.name.type"
@@ -637,48 +524,54 @@
},
"6": {
"name": "punctuation.bracket.angle"
- },
- "7": {
+ }
+ }
+ }
+ ]
+ },
+ "type-constraints": {
+ "patterns": [
+ {
+ "include": "#type-constraints-list"
+ },
+ {
+ "include": "#type-constraints-singleton"
+ }
+ ]
+ },
+ "type-constraints-list": {
+ "patterns": [
+ {
+ "name": "meta.type-constraints-list.fidl",
+ "begin": ":<",
+ "beginCaptures": {},
+ "end": ">",
+ "endCaptures": {},
+ "patterns": [
+ {
+ "include": "#const-value"
+ }
+ ]
+ }
+ ]
+ },
+ "type-constraints-singleton": {
+ "patterns": [
+ {
+ "name": "meta.type-constraints-singleton.fidl",
+ "match": ":\\s*((?:(?:(-?\\b(?:(?:0(?:x|X)[0-9a-fA-F]*)|(?:0(?:b|B)[01]*)|(?:(?:[0-9]+\\.?[0-9]*)|(?:\\.[0-9]+))(?:(?:e|E)(?:\\+|-)?[0-9]+)?)\\b)|(\\b(?:true|false)\\b)|(\"(?:[^\\\"]|\\.)*\"))|\\b[a-zA-Z_][0-9a-zA-Z_]*\\b(?:\\.\\b[a-zA-Z_][0-9a-zA-Z_]*\\b)*))",
+ "captures": {
+ "1": {
"name": "storage.type.constraint"
},
- "8": {
+ "2": {
"name": "constant.numeric"
},
- "9": {
+ "3": {
"name": "constant.language"
},
- "10": {
+ "4": {
"name": "string.quoted.double"
- },
- "11": {
- "name": "punctuation.bracket.angle"
- },
- "12": {
- "name": "storage.type.constraint"
- },
- "13": {
- "name": "constant.numeric"
- },
- "14": {
- "name": "constant.language"
- },
- "15": {
- "name": "string.quoted.double"
- },
- "16": {
- "name": "storage.type.constraint"
- },
- "17": {
- "name": "constant.numeric"
- },
- "18": {
- "name": "constant.language"
- },
- "19": {
- "name": "string.quoted.double"
- },
- "20": {
- "name": "punctuation.bracket.angle"
}
}
}
diff --git a/vscode-language-fidl/test.test.fidl b/vscode-language-fidl/test.test.fidl
index e7ef5ce..77f26e6 100644
--- a/vscode-language-fidl/test.test.fidl
+++ b/vscode-language-fidl/test.test.fidl
@@ -25,8 +25,19 @@
@available(deprecated=1000)
@available(removed=1001, note="bar")
alias Foo = foo.bar.baz;
+alias Bar = vector<string>;
+alias Baz = Bar:10;
-alias OptionalChannel = zx.handle:<CHANNEL, zx.rights.DUPLICATE, optional>;
+alias OptionalChannel = zx.handle:optional;
+alias OptionalChannelWithRights = zx.handle:<CHANNEL, zx.rights.DUPLICATE, optional>;
+
+type HandleConstraints = resource struct {
+ a zx.handle:VMO;
+ b zx.handle:<VMO>;
+ c zx.handle:<VMO, zx.rights.DUPLICATE | zx.rights.READ>;
+ d zx.handle:<VMO | CHANNEL, zx.rights.DUPLICATE | zx.rights.READ>;
+ e zx.handle:<VMO | CHANNEL, zx.rights.DUPLICATE | zx.rights.READ, 25, optional>;
+};
// hi
type Foo = struct {};
@@ -100,6 +111,7 @@
f1 uint8;
}) -> (struct {
f2 uint8;
+ f3 string:optional = "abc";
});
Foo(struct {
diff --git a/vscode-language-fidl/tools/generate-syntax.ts b/vscode-language-fidl/tools/generate-syntax.ts
index da6c2fb..2258b38 100644
--- a/vscode-language-fidl/tools/generate-syntax.ts
+++ b/vscode-language-fidl/tools/generate-syntax.ts
@@ -178,13 +178,6 @@
return word(one_of(...words));
}
-function comma_separated(pat: Pattern): NamedPattern {
- return new NamedPattern(`(?:${pat})(?:,\\s*${pat})*`, [
- ..._names(pat),
- ..._names(pat),
- ]);
-}
-
function zero_or_more(pat: Pattern): NamedPattern {
return new NamedPattern(`(?:${pat}\\s*)*`, _names(pat));
}
@@ -291,13 +284,6 @@
)
)));
-const TYPE_CONSTRAINT = named(CONSTANT, "storage.type.constraint");
-
-const TYPE_CONSTRAINTS = seq(
- ":",
- one_of(TYPE_CONSTRAINT, angle_brackets(comma_separated(TYPE_CONSTRAINT)))
-);
-
// Checks
const INLINE_LAYOUT_PREFIX = seq(
@@ -307,8 +293,6 @@
"{"
);
-comma_separated("[a-zA-Z]+").assert("foo, bar");
-
COMPOUND_IDENTIFIER.assert("foo");
COMPOUND_IDENTIFIER.assert("foo_bar");
COMPOUND_IDENTIFIER.assert("foo.bar.baz");
@@ -331,12 +315,6 @@
TYPE_PARAMETERS.assert("<Foo, 3>");
TYPE_PARAMETERS.assert("<Foo>");
-TYPE_CONSTRAINTS.assert(":optional");
-TYPE_CONSTRAINTS.assert(":zx.rights.DUPLICATE");
-TYPE_CONSTRAINTS.assert(":MAX_SIZE");
-TYPE_CONSTRAINTS.assert(":<MAX_SIZE, optional>");
-TYPE_CONSTRAINTS.assert(":<CHANNEL, zx.rights.DUPLICATE, optional>");
-
INLINE_LAYOUT_PREFIX.assert("resource flexible struct {");
INLINE_LAYOUT_PREFIX.assert("strict enum : int32 {");
@@ -347,7 +325,6 @@
include("attributes"),
];
-// TODO: support handle rights
const tmLanguage: TmLanguage = {
$schema: tmSchema,
name: "FIDL",
@@ -366,16 +343,17 @@
),
// Aliases: an aliased type can only be a layout reference, and cannot be re-parameterized
- match(
- "meta.type-alias",
- seq(
+ block({
+ name: "meta.type-alias",
+ begin: seq(
keyword("alias"),
named(IDENTIFIER, "variable.alias"),
separator("="),
LAYOUT_REFERENCE,
- optional(TYPE_CONSTRAINTS)
- )
- ),
+ ),
+ end: EOL,
+ patterns: [include("type-constructor")],
+ }),
// Const declaration
block({
@@ -490,6 +468,8 @@
include("type-constructor-inline"),
include("type-constructor-inline-arg"),
include("type-constructor-reference"),
+ // this must go last, so that we attempt to match on all unconstrained constructors first
+ include("type-constraints"),
],
},
// a type constructor that contains an inline layout, e.g. `foo struct { ... };
@@ -500,10 +480,6 @@
begin: seq(INLINE_LAYOUT_PREFIX),
end: seq(
"}",
- // no user-defined layouts currently accept parameters, so don't
- // worry about syntax highlighting for them yet; just check for
- // constraints
- optional(TYPE_CONSTRAINTS),
),
patterns: [...WITH_COMMENTS_AND_ATTRIBUTES, include("layout-member")],
}),
@@ -526,7 +502,6 @@
// the rest of the layout parameters (i.e. array size)
optional(seq(",", CONSTANT)),
">",
- optional(TYPE_CONSTRAINTS),
),
patterns: [...WITH_COMMENTS_AND_ATTRIBUTES, include("layout-member")],
}),
@@ -540,7 +515,33 @@
seq(
LAYOUT_REFERENCE,
optional(TYPE_PARAMETERS),
- optional(TYPE_CONSTRAINTS),
+ )
+ ),
+ ],
+ },
+ "type-constraints": {
+ patterns: [
+ include("type-constraints-list"),
+ include("type-constraints-singleton"),
+ ],
+ },
+ "type-constraints-list": {
+ patterns: [
+ block({
+ name: "meta.type-constraints-list",
+ begin: ":<",
+ end: ">",
+ patterns: [include("const-value")],
+ }),
+ ],
+ },
+ "type-constraints-singleton": {
+ patterns: [
+ match(
+ "meta.type-constraints-singleton",
+ seq(
+ ":",
+ named(CONSTANT, "storage.type.constraint"),
)
),
],