diff --git a/grammars/csharp.tmLanguage b/grammars/csharp.tmLanguage index 3906d58..ff466bb 100644 --- a/grammars/csharp.tmLanguage +++ b/grammars/csharp.tmLanguage @@ -173,6 +173,10 @@ include #property-declaration + + include + #fixed-size-buffer-declaration + include #field-declaration @@ -1692,6 +1696,60 @@ + fixed-size-buffer-declaration + + begin + (?x) +\b(fixed)\b\s+ +(?<type_name> + (?: + (?:(?<identifier>@?[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification + (?<name_and_type_args> # identifier + type arguments (if any) + \g<identifier>\s* + (?<type_args>\s*<(?:[^<>]|\g<type_args>)+>\s*)? + ) + (?:\s*\.\s*\g<name_and_type_args>)* # Are there any more names being dotted into? + ) +)\s+ +(\g<identifier>)\s* # buffer name +(?=\[) + beginCaptures + + 1 + + name + storage.modifier.fixed.cs + + 2 + + patterns + + + include + #type + + + + 6 + + name + entity.name.variable.field.cs + + + end + (?=;) + patterns + + + include + #bracketed-argument-list + + + include + #comment + + + field-declaration begin diff --git a/grammars/csharp.tmLanguage.cson b/grammars/csharp.tmLanguage.cson index 0fb74a7..77892d1 100644 --- a/grammars/csharp.tmLanguage.cson +++ b/grammars/csharp.tmLanguage.cson @@ -117,6 +117,9 @@ repository: { include: "#property-declaration" } + { + include: "#fixed-size-buffer-declaration" + } { include: "#field-declaration" } @@ -1060,6 +1063,43 @@ repository: include: "#generic-constraints" } ] + "fixed-size-buffer-declaration": + begin: ''' + (?x) + \\b(fixed)\\b\\s+ + (? + (?: + (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification + (? # identifier + type arguments (if any) + \\g\\s* + (?\\s*<(?:[^<>]|\\g)+>\\s*)? + ) + (?:\\s*\\.\\s*\\g)* # Are there any more names being dotted into? + ) + )\\s+ + (\\g)\\s* # buffer name + (?=\\[) + ''' + beginCaptures: + "1": + name: "storage.modifier.fixed.cs" + "2": + patterns: [ + { + include: "#type" + } + ] + "6": + name: "entity.name.variable.field.cs" + end: "(?=;)" + patterns: [ + { + include: "#bracketed-argument-list" + } + { + include: "#comment" + } + ] "field-declaration": begin: ''' (?x) diff --git a/src/csharp.tmLanguage.yml b/src/csharp.tmLanguage.yml index 4576f37..d12bc44 100644 --- a/src/csharp.tmLanguage.yml +++ b/src/csharp.tmLanguage.yml @@ -54,6 +54,7 @@ repository: - include: '#type-declarations' - include: '#constructor-declaration' - include: '#property-declaration' + - include: '#fixed-size-buffer-declaration' - include: '#field-declaration' - include: '#event-declaration' - include: '#indexer-declaration' @@ -543,6 +544,36 @@ repository: - include: '#punctuation-comma' - include: '#generic-constraints' + fixed-size-buffer-declaration: + begin: |- + (?x) + \b(fixed)\b\s+ + (? + (?: + (?:(?@?[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification + (? # identifier + type arguments (if any) + \g\s* + (?\s*<(?:[^<>]|\g)+>\s*)? + ) + (?:\s*\.\s*\g)* # Are there any more names being dotted into? + ) + )\s+ + (\g)\s* # buffer name + (?=\[) + beginCaptures: + '1': { name: storage.modifier.fixed.cs } + '2': + patterns: + - include: '#type' + # '3': ? is a sub-expression. It's final value is not considered. + # '4': ? is a sub-expression. It's final value is not considered. + # '5': ? is a sub-expression. It's final value is not considered. + '6': { name: entity.name.variable.field.cs } + end: (?=;) + patterns: + - include: '#bracketed-argument-list' + - include: '#comment' + field-declaration: begin: |- (?x) diff --git a/test/field.tests.ts b/test/field.tests.ts index 795133f..771c367 100644 --- a/test/field.tests.ts +++ b/test/field.tests.ts @@ -395,5 +395,71 @@ class C Token.Punctuation.CloseBrace ]); }); + + it("fixed-size buffer declaration", async () => { + const input = Input.InStruct(`public fixed byte Buffer[30];`); + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Keyword.Modifier.Public, + Token.Keyword.Modifier.Fixed, + Token.PrimitiveType.Byte, + Token.Identifier.FieldName("Buffer"), + Token.Punctuation.OpenBracket, + Token.Literal.Numeric.Decimal("30"), + Token.Punctuation.CloseBracket, + Token.Punctuation.Semicolon + ]); + }); + + it("fixed-size buffer with unsafe modifier", async () => { + const input = Input.InStruct(`public unsafe fixed byte Buffer[30];`); + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Keyword.Modifier.Public, + Token.Keyword.Modifier.Unsafe, + Token.Keyword.Modifier.Fixed, + Token.PrimitiveType.Byte, + Token.Identifier.FieldName("Buffer"), + Token.Punctuation.OpenBracket, + Token.Literal.Numeric.Decimal("30"), + Token.Punctuation.CloseBracket, + Token.Punctuation.Semicolon + ]); + }); + + it("fixed-size buffer with const reference", async () => { + const input = ` +struct C +{ + public const int length = 30; + public unsafe fixed byte Buffer[length]; +}`; + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Keyword.Definition.Struct, + Token.Identifier.StructName("C"), + Token.Punctuation.OpenBrace, + Token.Keyword.Modifier.Public, + Token.Keyword.Modifier.Const, + Token.PrimitiveType.Int, + Token.Identifier.FieldName("length"), + Token.Operator.Assignment, + Token.Literal.Numeric.Decimal("30"), + Token.Punctuation.Semicolon, + Token.Keyword.Modifier.Public, + Token.Keyword.Modifier.Unsafe, + Token.Keyword.Modifier.Fixed, + Token.PrimitiveType.Byte, + Token.Identifier.FieldName("Buffer"), + Token.Punctuation.OpenBracket, + Token.Variable.ReadWrite("length"), + Token.Punctuation.CloseBracket, + Token.Punctuation.Semicolon, + Token.Punctuation.CloseBrace + ]); + }); }); }); diff --git a/test/utils/tokenize.ts b/test/utils/tokenize.ts index e6cde77..8acf3a2 100644 --- a/test/utils/tokenize.ts +++ b/test/utils/tokenize.ts @@ -341,6 +341,7 @@ export namespace Token { export const Explicit = createToken('explicit', 'storage.modifier.explicit.cs'); export const Extern = createToken('extern', 'storage.modifier.extern.cs'); export const File = createToken('file', 'storage.modifier.file.cs'); + export const Fixed = createToken('fixed', 'storage.modifier.fixed.cs'); export const Implicit = createToken('implicit', 'storage.modifier.implicit.cs'); export const In = createToken('in', 'storage.modifier.in.cs'); export const Internal = createToken('internal', 'storage.modifier.internal.cs');