Skip to content

Commit da5c0c5

Browse files
authored
Semantic Snippets - using snippet (#77214)
Adds snippet provider for the using snippet.
2 parents 39048c8 + 191ee36 commit da5c0c5

File tree

5 files changed

+276
-0
lines changed

5 files changed

+276
-0
lines changed

src/Features/CSharp/Portable/Completion/CompletionProviders/SnippetCompletionProvider.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ internal sealed class SnippetCompletionProvider : LSPCompletionProvider
5151
CSharpSnippetIdentifiers.StaticIntMain,
5252
CSharpSnippetIdentifiers.Struct,
5353
CSharpSnippetIdentifiers.StaticVoidMain,
54+
CSharpSnippetIdentifiers.Using,
5455
CSharpSnippetIdentifiers.While
5556
];
5657

src/Features/CSharp/Portable/Snippets/CSharpSnippetIdentifiers.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,6 @@ internal static class CSharpSnippetIdentifiers
2525
public const string StaticIntMain = "sim";
2626
public const string Struct = "struct";
2727
public const string StaticVoidMain = "svm";
28+
public const string Using = "using";
2829
public const string While = "while";
2930
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Collections.Immutable;
7+
using System.Composition;
8+
using System.Threading;
9+
using System.Threading.Tasks;
10+
using Microsoft.CodeAnalysis.CSharp.Syntax;
11+
using Microsoft.CodeAnalysis.Host.Mef;
12+
using Microsoft.CodeAnalysis.LanguageService;
13+
using Microsoft.CodeAnalysis.Snippets;
14+
using Microsoft.CodeAnalysis.Snippets.SnippetProviders;
15+
using Microsoft.CodeAnalysis.Text;
16+
17+
namespace Microsoft.CodeAnalysis.CSharp.Snippets;
18+
19+
[ExportSnippetProvider(nameof(ISnippetProvider), LanguageNames.CSharp), Shared]
20+
[method: ImportingConstructor]
21+
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
22+
internal sealed class CSharpUsingSnippetProvider() : AbstractUsingSnippetProvider<UsingStatementSyntax>
23+
{
24+
public override string Identifier => CSharpSnippetIdentifiers.Using;
25+
26+
public override string Description => CSharpFeaturesResources.using_statement;
27+
28+
protected override ImmutableArray<SnippetPlaceholder> GetPlaceHolderLocationsList(UsingStatementSyntax node, ISyntaxFacts syntaxFacts, CancellationToken cancellationToken)
29+
{
30+
var expression = node.Expression!;
31+
return [new SnippetPlaceholder(expression.ToString(), expression.SpanStart)];
32+
}
33+
34+
protected override int GetTargetCaretPosition(UsingStatementSyntax usingStatement, SourceText sourceText)
35+
=> CSharpSnippetHelpers.GetTargetCaretPositionInBlock(
36+
usingStatement,
37+
static s => (BlockSyntax)s.Statement,
38+
sourceText);
39+
40+
protected override Task<Document> AddIndentationToDocumentAsync(Document document, UsingStatementSyntax usingStatement, CancellationToken cancellationToken)
41+
=> CSharpSnippetHelpers.AddBlockIndentationToDocumentAsync(
42+
document,
43+
usingStatement,
44+
static s => (BlockSyntax)s.Statement,
45+
cancellationToken);
46+
}
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Threading.Tasks;
6+
using Microsoft.CodeAnalysis.Test.Utilities;
7+
using Xunit;
8+
9+
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Snippets;
10+
11+
[Trait(Traits.Feature, Traits.Features.Snippets)]
12+
public sealed class CSharpUsingSnippetProviderTests : AbstractCSharpSnippetProviderTests
13+
{
14+
protected override string SnippetIdentifier => "using";
15+
16+
[Fact]
17+
public async Task InsertUsingSnippetInMethodTest()
18+
{
19+
await VerifySnippetAsync("""
20+
class Program
21+
{
22+
public void Method()
23+
{
24+
$$
25+
}
26+
}
27+
""", """
28+
class Program
29+
{
30+
public void Method()
31+
{
32+
using ({|0:resource|})
33+
{
34+
$$
35+
}
36+
}
37+
}
38+
""");
39+
}
40+
41+
[Fact]
42+
public async Task InsertUsingSnippetInGlobalContextTest()
43+
{
44+
await VerifySnippetAsync("""
45+
$$
46+
""", """
47+
using ({|0:resource|})
48+
{
49+
$$
50+
}
51+
""");
52+
}
53+
54+
[Fact]
55+
public async Task NoUsingSnippetInBusingNamespaceTest()
56+
{
57+
await VerifySnippetIsAbsentAsync("""
58+
namespace Namespace
59+
{
60+
$$
61+
}
62+
""");
63+
}
64+
65+
[Fact]
66+
public async Task NoUsingSnippetInFileScopedNamespaceTest()
67+
{
68+
await VerifySnippetIsAbsentAsync("""
69+
namespace Namespace;
70+
$$
71+
""");
72+
}
73+
74+
[Fact]
75+
public async Task InsertUsingSnippetInConstructorTest()
76+
{
77+
await VerifySnippetAsync("""
78+
class Program
79+
{
80+
public Program()
81+
{
82+
$$
83+
}
84+
}
85+
""", """
86+
class Program
87+
{
88+
public Program()
89+
{
90+
using ({|0:resource|})
91+
{
92+
$$
93+
}
94+
}
95+
}
96+
""");
97+
}
98+
99+
[Fact]
100+
public async Task NoUsingSnippetInTypeBodyTest()
101+
{
102+
await VerifySnippetIsAbsentAsync("""
103+
class Program
104+
{
105+
$$
106+
}
107+
""");
108+
}
109+
110+
[Fact]
111+
public async Task InsertUsingSnippetInLocalFunctionTest()
112+
{
113+
await VerifySnippetAsync("""
114+
class Program
115+
{
116+
public void Method()
117+
{
118+
void LocalFunction()
119+
{
120+
$$
121+
}
122+
}
123+
}
124+
""", """
125+
class Program
126+
{
127+
public void Method()
128+
{
129+
void LocalFunction()
130+
{
131+
using ({|0:resource|})
132+
{
133+
$$
134+
}
135+
}
136+
}
137+
}
138+
""");
139+
}
140+
141+
[Fact]
142+
public async Task InsertUsingSnippetInAnonymousFunctionTest()
143+
{
144+
await VerifySnippetAsync("""
145+
class Program
146+
{
147+
public void Method()
148+
{
149+
var action = delegate()
150+
{
151+
$$
152+
};
153+
}
154+
}
155+
""", """
156+
class Program
157+
{
158+
public void Method()
159+
{
160+
var action = delegate()
161+
{
162+
using ({|0:resource|})
163+
{
164+
$$
165+
}
166+
};
167+
}
168+
}
169+
""");
170+
}
171+
172+
[Fact]
173+
public async Task InsertUsingSnippetInParenthesizedLambdaExpressionTest()
174+
{
175+
await VerifySnippetAsync("""
176+
class Program
177+
{
178+
public void Method()
179+
{
180+
var action = () =>
181+
{
182+
$$
183+
};
184+
}
185+
}
186+
""", """
187+
class Program
188+
{
189+
public void Method()
190+
{
191+
var action = () =>
192+
{
193+
using ({|0:resource|})
194+
{
195+
$$
196+
}
197+
};
198+
}
199+
}
200+
""");
201+
}
202+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Threading;
6+
using System.Threading.Tasks;
7+
using Microsoft.CodeAnalysis.Editing;
8+
using Microsoft.CodeAnalysis.Shared.Extensions;
9+
using Microsoft.CodeAnalysis.Shared.Utilities;
10+
using Microsoft.CodeAnalysis.Text;
11+
12+
namespace Microsoft.CodeAnalysis.Snippets.SnippetProviders;
13+
14+
internal abstract class AbstractUsingSnippetProvider<TUsingStatementSyntax> : AbstractStatementSnippetProvider<TUsingStatementSyntax>
15+
where TUsingStatementSyntax : SyntaxNode
16+
{
17+
protected sealed override async Task<TextChange> GenerateSnippetTextChangeAsync(Document document, int position, CancellationToken cancellationToken)
18+
{
19+
var generator = SyntaxGenerator.GetGenerator(document);
20+
var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);
21+
var identifierName = NameGenerator.GenerateUniqueName("resource",
22+
n => semanticModel.LookupSymbols(position, name: n).IsEmpty);
23+
var statement = generator.UsingStatement(generator.IdentifierName(identifierName), statements: []);
24+
return new TextChange(TextSpan.FromBounds(position, position), statement.NormalizeWhitespace().ToFullString());
25+
}
26+
}

0 commit comments

Comments
 (0)