diff --git a/grammars/csharp.tmLanguage b/grammars/csharp.tmLanguage index 3906d58..756f8a9 100644 --- a/grammars/csharp.tmLanguage +++ b/grammars/csharp.tmLanguage @@ -4885,9 +4885,12 @@ begin (?x) (?: - (?:(\bref)\s+(?:(\breadonly)\s+)?)?(\bvar\b)| # ref local + (?:(\bscoped)\s+(?:(\bref)\s+)?)? # scoped local + (?:(\bref)\s+(?:(\breadonly)\s+)?)? # ref local + (\bvar\b)| (?<type_name> (?: + (?:scoped\s+(?:ref\s+)?)? # scoped local (?:ref\s+(?:readonly\s+)?)? # ref local (?: (?:(?<identifier>@?[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification @@ -4918,19 +4921,29 @@ 1 name - storage.modifier.ref.cs + storage.modifier.scoped.cs 2 name - storage.modifier.readonly.cs + storage.modifier.ref.cs 3 name - storage.type.var.cs + storage.modifier.ref.cs 4 + + name + storage.modifier.readonly.cs + + 5 + + name + storage.type.var.cs + + 6 patterns @@ -4940,7 +4953,7 @@ - 9 + 11 name entity.name.variable.local.cs @@ -7370,7 +7383,7 @@ match (?x) -(?:(?:\b(ref|params|out|in|this)\b)\s+)? +(?:(?:\b(scoped|ref|params|out|in|this)\b)\s+)? (?<type_name> (?: (?:ref\s+)? # ref return @@ -8290,6 +8303,10 @@ include #comment + + include + #scoped-modifier + include #ref-modifier @@ -8328,6 +8345,13 @@ + scoped-modifier + + name + storage.modifier.scoped.cs + match + \bscoped\b + ref-modifier name diff --git a/grammars/csharp.tmLanguage.cson b/grammars/csharp.tmLanguage.cson index 0fb74a7..49b12ca 100644 --- a/grammars/csharp.tmLanguage.cson +++ b/grammars/csharp.tmLanguage.cson @@ -2989,9 +2989,12 @@ repository: begin: ''' (?x) (?: - (?:(\\bref)\\s+(?:(\\breadonly)\\s+)?)?(\\bvar\\b)| # ref local + (?:(\\bscoped)\\s+(?:(\\bref)\\s+)?)? # scoped local + (?:(\\bref)\\s+(?:(\\breadonly)\\s+)?)? # ref local + (\\bvar\\b)| (? (?: + (?:scoped\\s+(?:ref\\s+)?)? # scoped local (?:ref\\s+(?:readonly\\s+)?)? # ref local (?: (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification @@ -3020,18 +3023,22 @@ repository: ''' beginCaptures: "1": - name: "storage.modifier.ref.cs" + name: "storage.modifier.scoped.cs" "2": - name: "storage.modifier.readonly.cs" + name: "storage.modifier.ref.cs" "3": - name: "storage.type.var.cs" + name: "storage.modifier.ref.cs" "4": + name: "storage.modifier.readonly.cs" + "5": + name: "storage.type.var.cs" + "6": patterns: [ { include: "#type" } ] - "9": + "11": name: "entity.name.variable.local.cs" end: "(?=[;)}])" patterns: [ @@ -4451,7 +4458,7 @@ repository: parameter: match: ''' (?x) - (?:(?:\\b(ref|params|out|in|this)\\b)\\s+)? + (?:(?:\\b(scoped|ref|params|out|in|this)\\b)\\s+)? (? (?: (?:ref\\s+)? # ref return @@ -5019,6 +5026,9 @@ repository: { include: "#comment" } + { + include: "#scoped-modifier" + } { include: "#ref-modifier" } @@ -5047,6 +5057,9 @@ repository: include: "#type-pointer-suffix" } ] + "scoped-modifier": + name: "storage.modifier.scoped.cs" + match: "\\bscoped\\b" "ref-modifier": name: "storage.modifier.ref.cs" match: "\\bref\\b" diff --git a/src/csharp.tmLanguage.yml b/src/csharp.tmLanguage.yml index 4576f37..f121d8f 100644 --- a/src/csharp.tmLanguage.yml +++ b/src/csharp.tmLanguage.yml @@ -1775,9 +1775,12 @@ repository: begin: |- (?x) (?: - (?:(\bref)\s+(?:(\breadonly)\s+)?)?(\bvar\b)| # ref local + (?:(\bscoped)\s+(?:(\bref)\s+)?)? # scoped local + (?:(\bref)\s+(?:(\breadonly)\s+)?)? # ref local + (\bvar\b)| (? (?: + (?:scoped\s+(?:ref\s+)?)? # scoped local (?:ref\s+(?:readonly\s+)?)? # ref local (?: (?:(?@?[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification @@ -1804,17 +1807,19 @@ repository: (?!=>) (?=,|;|=|\)) beginCaptures: - 1: { name: storage.modifier.ref.cs } - 2: { name: storage.modifier.readonly.cs } - 3: { name: storage.type.var.cs } - 4: + 1: { name: storage.modifier.scoped.cs } + 2: { name: storage.modifier.ref.cs } + 3: { name: storage.modifier.ref.cs } + 4: { name: storage.modifier.readonly.cs } + 5: { name: storage.type.var.cs } + 6: patterns: - include: '#type' - # 5: ? is a sub-expression. It's final value is not considered. - # 6: ? is a sub-expression. It's final value is not considered. - # 7: ? is a sub-expression. It's final value is not considered. - # 8: ? is a sub-expression. It's final value is not considered. - 9: { name: entity.name.variable.local.cs } + # 7: ? is a sub-expression. It's final value is not considered. + # 8: ? is a sub-expression. It's final value is not considered. + # 9: ? is a sub-expression. It's final value is not considered. + # 10: ? is a sub-expression. It's final value is not considered. + 11: { name: entity.name.variable.local.cs } end: (?=[;)}]) patterns: - name: entity.name.variable.local.cs @@ -2865,7 +2870,7 @@ repository: parameter: match: |- (?x) - (?:(?:\b(ref|params|out|in|this)\b)\s+)? + (?:(?:\b(scoped|ref|params|out|in|this)\b)\s+)? (? (?: (?:ref\s+)? # ref return @@ -3262,6 +3267,7 @@ repository: type: patterns: - include: '#comment' + - include: '#scoped-modifier' - include: '#ref-modifier' - include: '#readonly-modifier' - include: '#tuple-type' @@ -3272,6 +3278,10 @@ repository: - include: '#type-nullable-suffix' - include: '#type-pointer-suffix' + scoped-modifier: + name: storage.modifier.scoped.cs + match: \bscoped\b + ref-modifier: name: storage.modifier.ref.cs match: \bref\b diff --git a/test/local.tests.ts b/test/local.tests.ts index 9b342c4..4077bf3 100644 --- a/test/local.tests.ts +++ b/test/local.tests.ts @@ -96,6 +96,79 @@ describe("Locals", () => { ]); }); + it("scoped local", async () => { + const input = Input.InMethod(`scoped Span x = stackalloc int[42];`); + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Keyword.Modifier.Scoped, + Token.Type("Span"), + Token.Punctuation.TypeParameter.Begin, + Token.PrimitiveType.Int, + Token.Punctuation.TypeParameter.End, + Token.Identifier.LocalName("x"), + Token.Operator.Assignment, + Token.Operator.Expression.StackAlloc, + Token.PrimitiveType.Int, + Token.Punctuation.OpenBracket, + Token.Literal.Numeric.Decimal("42"), + Token.Punctuation.CloseBracket, + Token.Punctuation.Semicolon + ]); + }); + + it("scoped local var", async () => { + const input = Input.InMethod(`scoped var x = y;`); + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Keyword.Modifier.Scoped, + Token.Keyword.Definition.Var, + Token.Identifier.LocalName("x"), + Token.Operator.Assignment, + Token.Variable.ReadWrite("y"), + Token.Punctuation.Semicolon + ]); + }); + + it("scoped ref local", async () => { + const input = Input.InMethod(`scoped ref Span x = stackalloc int[42];`); + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Keyword.Modifier.Scoped, + Token.Keyword.Modifier.Ref, + Token.Type("Span"), + Token.Punctuation.TypeParameter.Begin, + Token.PrimitiveType.Int, + Token.Punctuation.TypeParameter.End, + Token.Identifier.LocalName("x"), + Token.Operator.Assignment, + Token.Operator.Expression.StackAlloc, + Token.PrimitiveType.Int, + Token.Punctuation.OpenBracket, + Token.Literal.Numeric.Decimal("42"), + Token.Punctuation.CloseBracket, + Token.Punctuation.Semicolon + ]); + }); + + it("scoped ref local var", async () => { + const input = Input.InMethod(`scoped ref var x = ref y;`); + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Keyword.Modifier.Scoped, + Token.Keyword.Modifier.Ref, + Token.Keyword.Definition.Var, + Token.Identifier.LocalName("x"), + Token.Operator.Assignment, + Token.Keyword.Modifier.Ref, + Token.Variable.ReadWrite("y"), + Token.Punctuation.Semicolon + ]); + }); + it("ref local", async () => { const input = Input.InMethod(`ref int x;`); const tokens = await tokenize(input); diff --git a/test/method.tests.ts b/test/method.tests.ts index ad90879..edaba0b 100644 --- a/test/method.tests.ts +++ b/test/method.tests.ts @@ -796,6 +796,67 @@ public void LinearRegression(double[,] samples, double[] standardDeviations, int ]); }); + it("ref parameter in parameter list", async () => { + const input = Input.InClass(` +public static Span CreateSpan(ref T reference, int length) {} +`); + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Keyword.Modifier.Public, + Token.Keyword.Modifier.Static, + Token.Type("Span"), + Token.Punctuation.TypeParameter.Begin, + Token.Type("T"), + Token.Punctuation.TypeParameter.End, + Token.Identifier.MethodName("CreateSpan"), + Token.Punctuation.TypeParameter.Begin, + Token.Identifier.TypeParameterName("T"), + Token.Punctuation.TypeParameter.End, + Token.Punctuation.OpenParen, + Token.Keyword.Modifier.Ref, + Token.Type("T"), + Token.Identifier.ParameterName("reference"), + Token.Punctuation.Comma, + Token.PrimitiveType.Int, + Token.Identifier.ParameterName("length"), + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Punctuation.CloseBrace + ]); + }); + + it("scoped ref parameter in parameter list (#306)", async () => { + const input = Input.InClass(` +public static Span CreateSpan(scoped ref T reference, int length) {} +`); + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Keyword.Modifier.Public, + Token.Keyword.Modifier.Static, + Token.Type("Span"), + Token.Punctuation.TypeParameter.Begin, + Token.Type("T"), + Token.Punctuation.TypeParameter.End, + Token.Identifier.MethodName("CreateSpan"), + Token.Punctuation.TypeParameter.Begin, + Token.Identifier.TypeParameterName("T"), + Token.Punctuation.TypeParameter.End, + Token.Punctuation.OpenParen, + Token.Keyword.Modifier.Scoped, + Token.Keyword.Modifier.Ref, + Token.Type("T"), + Token.Identifier.ParameterName("reference"), + Token.Punctuation.Comma, + Token.PrimitiveType.Int, + Token.Identifier.ParameterName("length"), + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Punctuation.CloseBrace + ]); + }); + it("expression body and type constraint (issue #74)", async () => { const input = Input.InClass(` T id1(T a) => a; diff --git a/test/utils/tokenize.ts b/test/utils/tokenize.ts index e6cde77..9d8163e 100644 --- a/test/utils/tokenize.ts +++ b/test/utils/tokenize.ts @@ -355,6 +355,7 @@ export namespace Token { export const ReadOnly = createToken('readonly', 'storage.modifier.readonly.cs'); export const Ref = createToken('ref', 'storage.modifier.ref.cs'); export const Required = createToken('required', 'storage.modifier.required.cs'); + export const Scoped = createToken('scoped', 'storage.modifier.scoped.cs'); export const Sealed = createToken('sealed', 'storage.modifier.sealed.cs'); export const Static = createToken('static', 'storage.modifier.static.cs'); export const This = createToken('this', 'storage.modifier.this.cs');