diff --git a/grammars/csharp.tmLanguage b/grammars/csharp.tmLanguage index 6b6a385..9d66b73 100644 --- a/grammars/csharp.tmLanguage +++ b/grammars/csharp.tmLanguage @@ -4799,9 +4799,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 @@ -4832,19 +4835,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 @@ -4854,7 +4867,7 @@ - 9 + 11 name entity.name.variable.local.cs @@ -7284,7 +7297,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 @@ -8204,6 +8217,10 @@ include #comment + + include + #scoped-modifier + include #ref-modifier @@ -8242,6 +8259,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 3f09e3e..86263bf 100644 --- a/grammars/csharp.tmLanguage.cson +++ b/grammars/csharp.tmLanguage.cson @@ -2931,9 +2931,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 @@ -2962,18 +2965,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: [ @@ -4393,7 +4400,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 @@ -4961,6 +4968,9 @@ repository: { include: "#comment" } + { + include: "#scoped-modifier" + } { include: "#ref-modifier" } @@ -4989,6 +4999,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 360b7d2..d99b878 100644 --- a/src/csharp.tmLanguage.yml +++ b/src/csharp.tmLanguage.yml @@ -1739,9 +1739,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 @@ -1768,17 +1771,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 @@ -2829,7 +2834,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 @@ -3226,6 +3231,7 @@ repository: type: patterns: - include: '#comment' + - include: '#scoped-modifier' - include: '#ref-modifier' - include: '#readonly-modifier' - include: '#tuple-type' @@ -3236,6 +3242,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 71f950d..e032e35 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');