Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions grammars/csharp.tmLanguage
Original file line number Diff line number Diff line change
Expand Up @@ -1526,6 +1526,10 @@
<string>(?=\{|where|;)</string>
<key>patterns</key>
<array>
<dict>
<key>include</key>
<string>#base-class-constructor-call</string>
</dict>
<dict>
<key>include</key>
<string>#type</string>
Expand All @@ -1540,6 +1544,62 @@
</dict>
</array>
</dict>
<key>base-class-constructor-call</key>
<dict>
<key>begin</key>
<string>(?x)
(?:
(@?[_[:alpha:]][_[:alnum:]]*)\s*(\.) # qualified name part
)*
(@?[_[:alpha:]][_[:alnum:]]*)\s* # type name
(
&lt;
(?&lt;type_args&gt;
[^&lt;&gt;()]|
\((?:[^&lt;&gt;()]|&lt;[^&lt;&gt;()]*&gt;|\([^&lt;&gt;()]*\))*\)|
&lt;\g&lt;type_args&gt;*&gt;
)*
&gt;\s*
)? # optional type arguments
(?=\() # followed by argument list</string>
<key>beginCaptures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>entity.name.type.cs</string>
</dict>
<key>2</key>
<dict>
<key>name</key>
<string>punctuation.accessor.cs</string>
</dict>
<key>3</key>
<dict>
<key>name</key>
<string>entity.name.type.cs</string>
</dict>
<key>4</key>
<dict>
<key>patterns</key>
<array>
<dict>
<key>include</key>
<string>#type-arguments</string>
</dict>
</array>
</dict>
</dict>
<key>end</key>
<string>(?&lt;=\))</string>
<key>patterns</key>
<array>
<dict>
<key>include</key>
<string>#argument-list</string>
</dict>
</array>
</dict>
<key>generic-constraints</key>
<dict>
<key>begin</key>
Expand Down
40 changes: 40 additions & 0 deletions grammars/csharp.tmLanguage.cson
Original file line number Diff line number Diff line change
Expand Up @@ -959,6 +959,9 @@ repository:
name: "punctuation.separator.colon.cs"
end: "(?=\\{|where|;)"
patterns: [
{
include: "#base-class-constructor-call"
}
{
include: "#type"
}
Expand All @@ -969,6 +972,43 @@ repository:
include: "#preprocessor"
}
]
"base-class-constructor-call":
begin: '''
(?x)
(?:
(@?[_[:alpha:]][_[:alnum:]]*)\\s*(\\.) # qualified name part
)*
(@?[_[:alpha:]][_[:alnum:]]*)\\s* # type name
(
<
(?<type_args>
[^<>()]|
\\((?:[^<>()]|<[^<>()]*>|\\([^<>()]*\\))*\\)|
<\\g<type_args>*>
)*
>\\s*
)? # optional type arguments
(?=\\() # followed by argument list
'''
beginCaptures:
"1":
name: "entity.name.type.cs"
"2":
name: "punctuation.accessor.cs"
"3":
name: "entity.name.type.cs"
"4":
patterns: [
{
include: "#type-arguments"
}
]
end: "(?<=\\))"
patterns: [
{
include: "#argument-list"
}
]
"generic-constraints":
begin: "(where)\\s+(@?[_[:alpha:]][_[:alnum:]]*)\\s*(:)"
beginCaptures:
Expand Down
29 changes: 29 additions & 0 deletions src/csharp.tmLanguage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -483,10 +483,39 @@ repository:
'0': { name: punctuation.separator.colon.cs }
end: (?=\{|where|;)
patterns:
- include: '#base-class-constructor-call'
- include: '#type'
- include: '#punctuation-comma'
- include: '#preprocessor'

base-class-constructor-call:
begin: |-
(?x)
(?:
(@?[_[:alpha:]][_[:alnum:]]*)\s*(\.) # qualified name part
)*
(@?[_[:alpha:]][_[:alnum:]]*)\s* # type name
(
<
(?<type_args>
[^<>()]|
\((?:[^<>()]|<[^<>()]*>|\([^<>()]*\))*\)|
<\g<type_args>*>
)*
>\s*
)? # optional type arguments
(?=\() # followed by argument list
beginCaptures:
'1': { name: entity.name.type.cs }
'2': { name: punctuation.accessor.cs }
'3': { name: entity.name.type.cs }
'4':
patterns:
- include: '#type-arguments'
end: (?<=\))
patterns:
- include: '#argument-list'

generic-constraints:
begin: (where)\s+(@?[_[:alpha:]][_[:alnum:]]*)\s*(:)
beginCaptures:
Expand Down
155 changes: 154 additions & 1 deletion test/constructor.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { should } from 'chai';
import { tokenize, Input, Token } from './utils/tokenize';
import { tokenize, Input, Token, NamespaceStyle } from './utils/tokenize';

describe("Constructors", () => {
before(() => { should(); });
Expand Down Expand Up @@ -415,4 +415,157 @@ public AccountController(
]);
});
});

describe("Primary Constructors with Base Class Arguments", () => {
for (const namespaceStyle of [NamespaceStyle.BlockScoped, NamespaceStyle.FileScoped]) {
const styleName = namespaceStyle == NamespaceStyle.BlockScoped
? "Block-Scoped"
: "File-Scoped";

it(`class: primary constructor with base class simple argument (${styleName} Namespace)`, async () => {

const input = Input.InNamespace(`class Derived(string name) : Base(name) { }`, namespaceStyle);
const tokens = await tokenize(input);

tokens.should.deep.equal([
Token.Keyword.Definition.Class,
Token.Identifier.ClassName("Derived"),
Token.Punctuation.OpenParen,
Token.PrimitiveType.String,
Token.Identifier.ParameterName("name"),
Token.Punctuation.CloseParen,
Token.Punctuation.Colon,
Token.Type("Base"),
Token.Punctuation.OpenParen,
Token.Variable.ReadWrite("name"),
Token.Punctuation.CloseParen,
Token.Punctuation.OpenBrace,
Token.Punctuation.CloseBrace
]);
});

it(`class: primary constructor with base class lambda argument (${styleName} Namespace)`, async () => {

const input = Input.InNamespace(`class Bar(Action action) : Base(() => {}) { }`, namespaceStyle);
const tokens = await tokenize(input);

tokens.should.deep.equal([
Token.Keyword.Definition.Class,
Token.Identifier.ClassName("Bar"),
Token.Punctuation.OpenParen,
Token.Type("Action"),
Token.Identifier.ParameterName("action"),
Token.Punctuation.CloseParen,
Token.Punctuation.Colon,
Token.Type("Base"),
Token.Punctuation.OpenParen,
Token.Punctuation.OpenParen,
Token.Punctuation.CloseParen,
Token.Operator.Arrow,
Token.Punctuation.OpenBrace,
Token.Punctuation.CloseBrace,
Token.Punctuation.CloseParen,
Token.Punctuation.OpenBrace,
Token.Punctuation.CloseBrace
]);
});

it(`class: primary constructor with base class multiple arguments (${styleName} Namespace)`, async () => {

const input = Input.InNamespace(`class Child(int x, string y) : Parent(x, y) { }`, namespaceStyle);
const tokens = await tokenize(input);

tokens.should.deep.equal([
Token.Keyword.Definition.Class,
Token.Identifier.ClassName("Child"),
Token.Punctuation.OpenParen,
Token.PrimitiveType.Int,
Token.Identifier.ParameterName("x"),
Token.Punctuation.Comma,
Token.PrimitiveType.String,
Token.Identifier.ParameterName("y"),
Token.Punctuation.CloseParen,
Token.Punctuation.Colon,
Token.Type("Parent"),
Token.Punctuation.OpenParen,
Token.Variable.ReadWrite("x"),
Token.Punctuation.Comma,
Token.Variable.ReadWrite("y"),
Token.Punctuation.CloseParen,
Token.Punctuation.OpenBrace,
Token.Punctuation.CloseBrace
]);
});

it(`class: primary constructor with base class and interface (${styleName} Namespace)`, async () => {

const input = Input.InNamespace(`class Derived(string name) : Base(name), IInterface { }`, namespaceStyle);
const tokens = await tokenize(input);

tokens.should.deep.equal([
Token.Keyword.Definition.Class,
Token.Identifier.ClassName("Derived"),
Token.Punctuation.OpenParen,
Token.PrimitiveType.String,
Token.Identifier.ParameterName("name"),
Token.Punctuation.CloseParen,
Token.Punctuation.Colon,
Token.Type("Base"),
Token.Punctuation.OpenParen,
Token.Variable.ReadWrite("name"),
Token.Punctuation.CloseParen,
Token.Punctuation.Comma,
Token.Type("IInterface"),
Token.Punctuation.OpenBrace,
Token.Punctuation.CloseBrace
]);
});

it(`record: primary constructor with base class simple argument (${styleName} Namespace)`, async () => {

const input = Input.InNamespace(`record Derived(string name) : Base(name);`, namespaceStyle);
const tokens = await tokenize(input);

tokens.should.deep.equal([
Token.Keyword.Definition.Record,
Token.Identifier.ClassName("Derived"),
Token.Punctuation.OpenParen,
Token.PrimitiveType.String,
Token.Identifier.ParameterName("name"),
Token.Punctuation.CloseParen,
Token.Punctuation.Colon,
Token.Type("Base"),
Token.Punctuation.OpenParen,
Token.Variable.ReadWrite("name"),
Token.Punctuation.CloseParen,
Token.Punctuation.Semicolon
]);
});

it(`record: primary constructor with base class lambda argument (${styleName} Namespace)`, async () => {

const input = Input.InNamespace(`record Bar(Action action) : Base(() => {});`, namespaceStyle);
const tokens = await tokenize(input);

tokens.should.deep.equal([
Token.Keyword.Definition.Record,
Token.Identifier.ClassName("Bar"),
Token.Punctuation.OpenParen,
Token.Type("Action"),
Token.Identifier.ParameterName("action"),
Token.Punctuation.CloseParen,
Token.Punctuation.Colon,
Token.Type("Base"),
Token.Punctuation.OpenParen,
Token.Punctuation.OpenParen,
Token.Punctuation.CloseParen,
Token.Operator.Arrow,
Token.Punctuation.OpenBrace,
Token.Punctuation.CloseBrace,
Token.Punctuation.CloseParen,
Token.Punctuation.Semicolon
]);
});
}
});
});