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
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ internal sealed class SnippetCompletionProvider : LSPCompletionProvider
CSharpSnippetIdentifiers.StaticIntMain,
CSharpSnippetIdentifiers.Struct,
CSharpSnippetIdentifiers.StaticVoidMain,
CSharpSnippetIdentifiers.Using,
CSharpSnippetIdentifiers.While
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ internal static class CSharpSnippetIdentifiers
public const string StaticIntMain = "sim";
public const string Struct = "struct";
public const string StaticVoidMain = "svm";
public const string Using = "using";
public const string While = "while";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Immutable;
using System.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.Snippets;
using Microsoft.CodeAnalysis.Snippets.SnippetProviders;
using Microsoft.CodeAnalysis.Text;

namespace Microsoft.CodeAnalysis.CSharp.Snippets;

[ExportSnippetProvider(nameof(ISnippetProvider), LanguageNames.CSharp), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class CSharpUsingSnippetProvider() : AbstractUsingSnippetProvider<UsingStatementSyntax>
{
public override string Identifier => CSharpSnippetIdentifiers.Using;

public override string Description => CSharpFeaturesResources.using_statement;

protected override ImmutableArray<SnippetPlaceholder> GetPlaceHolderLocationsList(UsingStatementSyntax node, ISyntaxFacts syntaxFacts, CancellationToken cancellationToken)
{
var expression = node.Expression!;
return [new SnippetPlaceholder(expression.ToString(), expression.SpanStart)];
}

protected override int GetTargetCaretPosition(UsingStatementSyntax usingStatement, SourceText sourceText)
=> CSharpSnippetHelpers.GetTargetCaretPositionInBlock(
usingStatement,
static s => (BlockSyntax)s.Statement,
sourceText);

protected override Task<Document> AddIndentationToDocumentAsync(Document document, UsingStatementSyntax usingStatement, CancellationToken cancellationToken)
=> CSharpSnippetHelpers.AddBlockIndentationToDocumentAsync(
document,
usingStatement,
static s => (BlockSyntax)s.Statement,
cancellationToken);
}
202 changes: 202 additions & 0 deletions src/Features/CSharpTest/Snippets/CSharpUsingSnippetProviderTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Snippets;

[Trait(Traits.Feature, Traits.Features.Snippets)]
public sealed class CSharpUsingSnippetProviderTests : AbstractCSharpSnippetProviderTests
{
protected override string SnippetIdentifier => "using";

[Fact]
public async Task InsertUsingSnippetInMethodTest()
{
await VerifySnippetAsync("""
class Program
{
public void Method()
{
$$
}
}
""", """
class Program
{
public void Method()
{
using ({|0:resource|})
{
$$
}
}
}
""");
}

[Fact]
public async Task InsertUsingSnippetInGlobalContextTest()
{
await VerifySnippetAsync("""
$$
""", """
using ({|0:resource|})
{
$$
}
""");
}

[Fact]
public async Task NoUsingSnippetInBusingNamespaceTest()
{
await VerifySnippetIsAbsentAsync("""
namespace Namespace
{
$$
}
""");
}

[Fact]
public async Task NoUsingSnippetInFileScopedNamespaceTest()
{
await VerifySnippetIsAbsentAsync("""
namespace Namespace;
$$
""");
}

[Fact]
public async Task InsertUsingSnippetInConstructorTest()
{
await VerifySnippetAsync("""
class Program
{
public Program()
{
$$
}
}
""", """
class Program
{
public Program()
{
using ({|0:resource|})
{
$$
}
}
}
""");
}

[Fact]
public async Task NoUsingSnippetInTypeBodyTest()
{
await VerifySnippetIsAbsentAsync("""
class Program
{
$$
}
""");
}

[Fact]
public async Task InsertUsingSnippetInLocalFunctionTest()
{
await VerifySnippetAsync("""
class Program
{
public void Method()
{
void LocalFunction()
{
$$
}
}
}
""", """
class Program
{
public void Method()
{
void LocalFunction()
{
using ({|0:resource|})
{
$$
}
}
}
}
""");
}

[Fact]
public async Task InsertUsingSnippetInAnonymousFunctionTest()
{
await VerifySnippetAsync("""
class Program
{
public void Method()
{
var action = delegate()
{
$$
};
}
}
""", """
class Program
{
public void Method()
{
var action = delegate()
{
using ({|0:resource|})
{
$$
}
};
}
}
""");
}

[Fact]
public async Task InsertUsingSnippetInParenthesizedLambdaExpressionTest()
{
await VerifySnippetAsync("""
class Program
{
public void Method()
{
var action = () =>
{
$$
};
}
}
""", """
class Program
{
public void Method()
{
var action = () =>
{
using ({|0:resource|})
{
$$
}
};
}
}
""");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.Text;

namespace Microsoft.CodeAnalysis.Snippets.SnippetProviders;

internal abstract class AbstractUsingSnippetProvider<TUsingStatementSyntax> : AbstractStatementSnippetProvider<TUsingStatementSyntax>
where TUsingStatementSyntax : SyntaxNode
{
protected sealed override async Task<TextChange> GenerateSnippetTextChangeAsync(Document document, int position, CancellationToken cancellationToken)
{
var generator = SyntaxGenerator.GetGenerator(document);
var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var identifierName = NameGenerator.GenerateUniqueName("resource",
n => semanticModel.LookupSymbols(position, name: n).IsEmpty);
var statement = generator.UsingStatement(generator.IdentifierName(identifierName), statements: []);
return new TextChange(TextSpan.FromBounds(position, position), statement.NormalizeWhitespace().ToFullString());
}
}
Loading