Skip to content

Commit e29d028

Browse files
committed
Add support for lambda attributes
1 parent be24caf commit e29d028

File tree

4 files changed

+174
-3
lines changed

4 files changed

+174
-3
lines changed

grammars/csharp.tmLanguage

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,7 @@
660660
<key>attribute-section</key>
661661
<dict>
662662
<key>begin</key>
663-
<string>(\[)(assembly|module|field|event|method|param|property|return|type)?(\:)?</string>
663+
<string>(\[)(assembly|module|event|field|method|param|property|return|type|typevar)?(\:)?</string>
664664
<key>beginCaptures</key>
665665
<dict>
666666
<key>1</key>
@@ -2725,12 +2725,38 @@
27252725
<key>include</key>
27262726
<string>#ref-modifier</string>
27272727
</dict>
2728+
<dict>
2729+
<key>include</key>
2730+
<string>#lambda-expression</string>
2731+
</dict>
27282732
<dict>
27292733
<key>include</key>
27302734
<string>#expression</string>
27312735
</dict>
27322736
</array>
27332737
</dict>
2738+
<key>lambda-expression</key>
2739+
<dict>
2740+
<key>begin</key>
2741+
<string>(?=(?:\[.*\].*)?\(.*\).*=&gt;)</string>
2742+
<key>end</key>
2743+
<string>(?=;)</string>
2744+
<key>patterns</key>
2745+
<array>
2746+
<dict>
2747+
<key>include</key>
2748+
<string>#attribute-section</string>
2749+
</dict>
2750+
<dict>
2751+
<key>include</key>
2752+
<string>#parenthesized-parameter-list</string>
2753+
</dict>
2754+
<dict>
2755+
<key>include</key>
2756+
<string>#expression-body</string>
2757+
</dict>
2758+
</array>
2759+
</dict>
27342760
<key>expression-body</key>
27352761
<dict>
27362762
<key>begin</key>
@@ -7270,6 +7296,10 @@
72707296
<key>include</key>
72717297
<string>#parameter</string>
72727298
</dict>
7299+
<dict>
7300+
<key>include</key>
7301+
<string>#parameter-identifier</string>
7302+
</dict>
72737303
<dict>
72747304
<key>include</key>
72757305
<string>#punctuation-comma</string>
@@ -7333,6 +7363,13 @@
73337363
</dict>
73347364
</dict>
73357365
</dict>
7366+
<key>parameter-identifier</key>
7367+
<dict>
7368+
<key>name</key>
7369+
<string>entity.name.variable.parameter.cs</string>
7370+
<key>match</key>
7371+
<string>(?&lt;!\.)@?\b[_[:alpha:]][_[:alnum:]]*\b(?!\.)</string>
7372+
</dict>
73367373
<key>argument-list</key>
73377374
<dict>
73387375
<key>begin</key>

grammars/csharp.tmLanguage.cson

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ repository:
441441
}
442442
]
443443
"attribute-section":
444-
begin: "(\\[)(assembly|module|field|event|method|param|property|return|type)?(\\:)?"
444+
begin: "(\\[)(assembly|module|event|field|method|param|property|return|type|typevar)?(\\:)?"
445445
beginCaptures:
446446
"1":
447447
name: "punctuation.squarebracket.open.cs"
@@ -1738,10 +1738,27 @@ repository:
17381738
{
17391739
include: "#ref-modifier"
17401740
}
1741+
{
1742+
include: "#lambda-expression"
1743+
}
17411744
{
17421745
include: "#expression"
17431746
}
17441747
]
1748+
"lambda-expression":
1749+
begin: "(?=(?:\\[.*\\].*)?\\(.*\\).*=>)"
1750+
end: "(?=;)"
1751+
patterns: [
1752+
{
1753+
include: "#attribute-section"
1754+
}
1755+
{
1756+
include: "#parenthesized-parameter-list"
1757+
}
1758+
{
1759+
include: "#expression-body"
1760+
}
1761+
]
17451762
"expression-body":
17461763
begin: "=>"
17471764
beginCaptures:
@@ -4383,6 +4400,9 @@ repository:
43834400
{
43844401
include: "#parameter"
43854402
}
4403+
{
4404+
include: "#parameter-identifier"
4405+
}
43864406
{
43874407
include: "#punctuation-comma"
43884408
}
@@ -4430,6 +4450,9 @@ repository:
44304450
]
44314451
"7":
44324452
name: "entity.name.variable.parameter.cs"
4453+
"parameter-identifier":
4454+
name: "entity.name.variable.parameter.cs"
4455+
match: "(?<!\\.)@?\\b[_[:alpha:]][_[:alnum:]]*\\b(?!\\.)"
44334456
"argument-list":
44344457
begin: "\\("
44354458
beginCaptures:

src/csharp.tmLanguage.yml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ repository:
187187
- include: '#operator-assignment'
188188

189189
attribute-section:
190-
begin: (\[)(assembly|module|field|event|method|param|property|return|type)?(\:)?
190+
begin: (\[)(assembly|module|event|field|method|param|property|return|type|typevar)?(\:)?
191191
beginCaptures:
192192
'1': { name: punctuation.squarebracket.open.cs }
193193
'2': { name: keyword.other.attribute-specifier.cs }
@@ -1024,8 +1024,17 @@ repository:
10241024
end: (?=[,\)\];}])
10251025
patterns:
10261026
- include: '#ref-modifier'
1027+
- include: '#lambda-expression'
10271028
- include: '#expression'
10281029

1030+
lambda-expression:
1031+
begin: (?=(?:\[.*\].*)?\(.*\).*=>)
1032+
end: (?=;)
1033+
patterns:
1034+
- include: '#attribute-section'
1035+
- include: '#parenthesized-parameter-list'
1036+
- include: '#expression-body'
1037+
10291038
expression-body:
10301039
begin: =>
10311040
beginCaptures:
@@ -2823,6 +2832,7 @@ repository:
28232832
- include: '#comment'
28242833
- include: '#attribute-section'
28252834
- include: '#parameter'
2835+
- include: '#lambda-parameter'
28262836
- include: '#punctuation-comma'
28272837
- include: '#variable-initializer'
28282838

@@ -2865,6 +2875,10 @@ repository:
28652875
# '6': ?<tuple> is a sub-expression. It's final value is not considered.
28662876
7: { name: entity.name.variable.parameter.cs }
28672877

2878+
lambda-parameter:
2879+
name: entity.name.variable.parameter.cs
2880+
match: '(?<!\.)@?\b[_[:alpha:]][_[:alnum:]]*\b(?!\.)'
2881+
28682882
argument-list:
28692883
begin: \(
28702884
beginCaptures:

test/expressions.tests.ts

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,103 @@ var a = new A // comment
435435
]);
436436
});
437437

438+
it("lambda with attributes (issue #303)", async () => {
439+
const input = Input.InMethod(`
440+
var concat = ([DisallowNull] string a, [DisallowNull] string b) => a + b;
441+
var inc = [return: NotNullIfNotNull(nameof(s))] (int? s) => s.HasValue ? s++ : null;
442+
Func<string?, int?> parse = [ProvidesNullCheck] (s) => (s is not null) ? int.Parse(s) : null;
443+
`);
444+
const tokens = await tokenize(input);
445+
446+
tokens.should.deep.equal([
447+
Token.Keyword.Definition.Var,
448+
Token.Identifier.LocalName("concat"),
449+
Token.Operator.Assignment,
450+
Token.Punctuation.OpenParen,
451+
Token.Punctuation.OpenBracket,
452+
Token.Type("DisallowNull"),
453+
Token.Punctuation.CloseBracket,
454+
Token.PrimitiveType.String,
455+
Token.Identifier.ParameterName("a"),
456+
Token.Punctuation.Comma,
457+
Token.Punctuation.OpenBracket,
458+
Token.Type("DisallowNull"),
459+
Token.Punctuation.CloseBracket,
460+
Token.PrimitiveType.String,
461+
Token.Identifier.ParameterName("b"),
462+
Token.Punctuation.CloseParen,
463+
Token.Operator.Arrow,
464+
Token.Variable.ReadWrite("a"),
465+
Token.Operator.Arithmetic.Addition,
466+
Token.Variable.ReadWrite("b"),
467+
Token.Punctuation.Semicolon,
468+
469+
Token.Keyword.Definition.Var,
470+
Token.Identifier.LocalName("inc"),
471+
Token.Operator.Assignment,
472+
Token.Punctuation.OpenBracket,
473+
Token.Keyword.AttributeSpecifier("return"),
474+
Token.Punctuation.Colon,
475+
Token.Type("NotNullIfNotNull"),
476+
Token.Punctuation.OpenParen,
477+
Token.Operator.Expression.NameOf,
478+
Token.Punctuation.OpenParen,
479+
Token.Variable.ReadWrite("s"),
480+
Token.Punctuation.CloseParen,
481+
Token.Punctuation.CloseParen,
482+
Token.Punctuation.CloseBracket,
483+
Token.Punctuation.OpenParen,
484+
Token.PrimitiveType.Int,
485+
Token.Punctuation.QuestionMark,
486+
Token.Identifier.ParameterName("s"),
487+
Token.Punctuation.CloseParen,
488+
Token.Operator.Arrow,
489+
Token.Variable.Object("s"),
490+
Token.Punctuation.Accessor,
491+
Token.Variable.Property("HasValue"),
492+
Token.Operator.Conditional.QuestionMark,
493+
Token.Variable.ReadWrite("s"),
494+
Token.Operator.Increment,
495+
Token.Operator.Conditional.Colon,
496+
Token.Literal.Null,
497+
Token.Punctuation.Semicolon,
498+
499+
Token.Type("Func"),
500+
Token.Punctuation.TypeParameter.Begin,
501+
Token.PrimitiveType.String,
502+
Token.Punctuation.QuestionMark,
503+
Token.Punctuation.Comma,
504+
Token.PrimitiveType.Int,
505+
Token.Punctuation.QuestionMark,
506+
Token.Punctuation.TypeParameter.End,
507+
Token.Identifier.LocalName("parse"),
508+
Token.Operator.Assignment,
509+
Token.Punctuation.OpenBracket,
510+
Token.Type("ProvidesNullCheck"),
511+
Token.Punctuation.CloseBracket,
512+
Token.Punctuation.OpenParen,
513+
Token.Identifier.ParameterName("s"),
514+
Token.Punctuation.CloseParen,
515+
Token.Operator.Arrow,
516+
Token.Punctuation.OpenParen,
517+
Token.Variable.ReadWrite("s"),
518+
Token.Operator.Pattern.Is,
519+
Token.Operator.Pattern.Not,
520+
Token.Literal.Null,
521+
Token.Punctuation.CloseParen,
522+
Token.Operator.Conditional.QuestionMark,
523+
Token.PrimitiveType.Int,
524+
Token.Punctuation.Accessor,
525+
Token.Identifier.MethodName("Parse"),
526+
Token.Punctuation.OpenParen,
527+
Token.Variable.ReadWrite("s"),
528+
Token.Punctuation.CloseParen,
529+
Token.Operator.Conditional.Colon,
530+
Token.Literal.Null,
531+
Token.Punctuation.Semicolon
532+
]);
533+
});
534+
438535
it("async lambda assigned to dotted name (issue #142)", async () => {
439536
const input = Input.InMethod(
440537
`Something.listener = async args => { return true; };`

0 commit comments

Comments
 (0)