Skip to content

Commit

Permalink
Merge pull request #72892 from CyrusNajmabadi/strongTypeSnippets
Browse files Browse the repository at this point in the history
  • Loading branch information
CyrusNajmabadi authored Apr 5, 2024
2 parents 648471f + 4ddd025 commit b07ea36
Show file tree
Hide file tree
Showing 35 changed files with 251 additions and 409 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// 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.Linq;
using System.Threading;
Expand All @@ -22,7 +21,7 @@

namespace Microsoft.CodeAnalysis.CSharp.Snippets;

internal abstract class AbstractCSharpAutoPropertySnippetProvider : AbstractPropertySnippetProvider
internal abstract class AbstractCSharpAutoPropertySnippetProvider : AbstractPropertySnippetProvider<PropertyDeclarationSyntax>
{
protected virtual AccessorDeclarationSyntax? GenerateGetAccessorDeclaration(CSharpSyntaxContext syntaxContext, SyntaxGenerator generator)
=> (AccessorDeclarationSyntax)generator.GetAccessorDeclaration();
Expand All @@ -36,7 +35,7 @@ protected override bool IsValidSnippetLocation(in SnippetContext context, Cancel
SyntaxKindSet.AllMemberModifiers, SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, canBePartial: true, cancellationToken);
}

protected override async Task<SyntaxNode> GenerateSnippetSyntaxAsync(Document document, int position, CancellationToken cancellationToken)
protected override async Task<PropertyDeclarationSyntax> GenerateSnippetSyntaxAsync(Document document, int position, CancellationToken cancellationToken)
{
var compilation = await document.Project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false);
var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);
Expand Down Expand Up @@ -67,25 +66,22 @@ protected override async Task<SyntaxNode> GenerateSnippetSyntaxAsync(Document do
accessorList: SyntaxFactory.AccessorList([.. accessors.Where(a => a is not null)!]));
}

protected override int GetTargetCaretPosition(ISyntaxFactsService syntaxFacts, SyntaxNode caretTarget, SourceText sourceText)
{
var propertyDeclaration = (PropertyDeclarationSyntax)caretTarget;
return propertyDeclaration.AccessorList!.CloseBraceToken.Span.End;
}
protected override int GetTargetCaretPosition(ISyntaxFactsService syntaxFacts, PropertyDeclarationSyntax propertyDeclaration, SourceText sourceText)
=> propertyDeclaration.AccessorList!.CloseBraceToken.Span.End;

protected override ImmutableArray<SnippetPlaceholder> GetPlaceHolderLocationsList(SyntaxNode node, ISyntaxFacts syntaxFacts, CancellationToken cancellationToken)
protected override ImmutableArray<SnippetPlaceholder> GetPlaceHolderLocationsList(PropertyDeclarationSyntax propertyDeclaration, ISyntaxFacts syntaxFacts, CancellationToken cancellationToken)
{
using var _ = ArrayBuilder<SnippetPlaceholder>.GetInstance(out var arrayBuilder);
var propertyDeclaration = (PropertyDeclarationSyntax)node;
var identifier = propertyDeclaration.Identifier;
var type = propertyDeclaration.Type;

arrayBuilder.Add(new SnippetPlaceholder(type.ToString(), type.SpanStart));
arrayBuilder.Add(new SnippetPlaceholder(identifier.ValueText, identifier.SpanStart));
return arrayBuilder.ToImmutableArray();
return
[
new SnippetPlaceholder(type.ToString(), type.SpanStart),
new SnippetPlaceholder(identifier.ValueText, identifier.SpanStart),
];
}

protected override SyntaxNode? FindAddedSnippetSyntaxNode(SyntaxNode root, int position, Func<SyntaxNode?, bool> isCorrectContainer)
protected override PropertyDeclarationSyntax? FindAddedSnippetSyntaxNode(SyntaxNode root, int position)
{
var node = root.FindNode(TextSpan.FromBounds(position, position));
return node.GetAncestorOrThis<PropertyDeclarationSyntax>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Snippets;

using static SyntaxFactory;

internal abstract class AbstractCSharpForLoopSnippetProvider : AbstractForLoopSnippetProvider
internal abstract class AbstractCSharpForLoopSnippetProvider : AbstractForLoopSnippetProvider<ForStatementSyntax>
{
private static readonly string[] s_iteratorBaseNames = ["i", "j", "k"];

Expand All @@ -38,7 +38,7 @@ internal abstract class AbstractCSharpForLoopSnippetProvider : AbstractForLoopSn

protected abstract void AddSpecificPlaceholders(MultiDictionary<string, int> placeholderBuilder, ExpressionSyntax initializer, ExpressionSyntax rightOfCondition);

protected override SyntaxNode GenerateStatement(SyntaxGenerator generator, SyntaxContext syntaxContext, InlineExpressionInfo? inlineExpressionInfo)
protected override ForStatementSyntax GenerateStatement(SyntaxGenerator generator, SyntaxContext syntaxContext, InlineExpressionInfo? inlineExpressionInfo)
{
var semanticModel = syntaxContext.SemanticModel;
var compilation = semanticModel.Compilation;
Expand Down Expand Up @@ -81,13 +81,15 @@ protected override SyntaxNode GenerateStatement(SyntaxGenerator generator, Synta
}
}

protected override ImmutableArray<SnippetPlaceholder> GetPlaceHolderLocationsList(SyntaxNode node, ISyntaxFacts syntaxFacts, CancellationToken cancellationToken)
protected override ImmutableArray<SnippetPlaceholder> GetPlaceHolderLocationsList(ForStatementSyntax forStatement, ISyntaxFacts syntaxFacts, CancellationToken cancellationToken)
{
using var _ = ArrayBuilder<SnippetPlaceholder>.GetInstance(out var result);
var placeholderBuilder = new MultiDictionary<string, int>();
GetPartsOfForStatement(node, out var declaration, out var condition, out var incrementor, out var _);
var declaration = forStatement.Declaration;
var condition = forStatement.Condition;
var incrementor = forStatement.Incrementors.Single();

var variableDeclarator = ((VariableDeclarationSyntax)declaration!).Variables.Single();
var variableDeclarator = declaration!.Variables.Single();
var declaratorIdentifier = variableDeclarator.Identifier;
placeholderBuilder.Add(declaratorIdentifier.ValueText, declaratorIdentifier.SpanStart);

Expand All @@ -106,25 +108,16 @@ protected override ImmutableArray<SnippetPlaceholder> GetPlaceHolderLocationsLis
return result.ToImmutableAndClear();
}

protected override int GetTargetCaretPosition(ISyntaxFactsService syntaxFacts, SyntaxNode caretTarget, SourceText sourceText)
=> CSharpSnippetHelpers.GetTargetCaretPositionInBlock<ForStatementSyntax>(
caretTarget,
protected override int GetTargetCaretPosition(ISyntaxFactsService syntaxFacts, ForStatementSyntax forStatement, SourceText sourceText)
=> CSharpSnippetHelpers.GetTargetCaretPositionInBlock(
forStatement,
static s => (BlockSyntax)s.Statement,
sourceText);

protected override Task<Document> AddIndentationToDocumentAsync(Document document, CancellationToken cancellationToken)
=> CSharpSnippetHelpers.AddBlockIndentationToDocumentAsync<ForStatementSyntax>(
protected override Task<Document> AddIndentationToDocumentAsync(Document document, ForStatementSyntax forStatement, CancellationToken cancellationToken)
=> CSharpSnippetHelpers.AddBlockIndentationToDocumentAsync(
document,
FindSnippetAnnotation,
forStatement,
static s => (BlockSyntax)s.Statement,
cancellationToken);

private static void GetPartsOfForStatement(SyntaxNode node, out SyntaxNode? declaration, out SyntaxNode? condition, out SyntaxNode? incrementor, out SyntaxNode? statement)
{
var forStatement = (ForStatementSyntax)node;
declaration = forStatement.Declaration;
condition = forStatement.Condition;
incrementor = forStatement.Incrementors.Single();
statement = forStatement.Statement;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@

using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Utilities;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Snippets;
using Microsoft.CodeAnalysis.Snippets.SnippetProviders;

namespace Microsoft.CodeAnalysis.CSharp.Snippets;

internal abstract class AbstractCSharpMainMethodSnippetProvider : AbstractMainMethodSnippetProvider
internal abstract class AbstractCSharpMainMethodSnippetProvider
: AbstractMainMethodSnippetProvider<MethodDeclarationSyntax, StatementSyntax, TypeSyntax>
{
protected override bool IsValidSnippetLocation(in SnippetContext context, CancellationToken cancellationToken)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// 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.Generic;
using System.Linq;
using System.Threading;
Expand All @@ -24,7 +23,8 @@

namespace Microsoft.CodeAnalysis.CSharp.Snippets;

internal abstract class AbstractCSharpTypeSnippetProvider : AbstractTypeSnippetProvider
internal abstract class AbstractCSharpTypeSnippetProvider<TTypeDeclarationSyntax> : AbstractTypeSnippetProvider<TTypeDeclarationSyntax>
where TTypeDeclarationSyntax : BaseTypeDeclarationSyntax
{
protected abstract ISet<SyntaxKind> ValidModifiers { get; }

Expand Down Expand Up @@ -78,42 +78,34 @@ protected override bool IsValidSnippetLocation(in SnippetContext context, Cancel
return new TextChange(TextSpan.FromBounds(targetPosition, targetPosition), SyntaxFacts.GetText(SyntaxKind.PublicKeyword) + " ");
}

protected override int GetTargetCaretPosition(ISyntaxFactsService syntaxFacts, SyntaxNode caretTarget, SourceText sourceText)
protected override int GetTargetCaretPosition(ISyntaxFactsService syntaxFacts, TTypeDeclarationSyntax typeDeclaration, SourceText sourceText)
{
var typeDeclaration = (BaseTypeDeclarationSyntax)caretTarget;
var triviaSpan = typeDeclaration.CloseBraceToken.LeadingTrivia.Span;
var line = sourceText.Lines.GetLineFromPosition(triviaSpan.Start);
// Getting the location at the end of the line before the newline.
return line.Span.End;
}

protected override SyntaxNode? FindAddedSnippetSyntaxNode(SyntaxNode root, int position, Func<SyntaxNode?, bool> isCorrectContainer)
protected override TTypeDeclarationSyntax? FindAddedSnippetSyntaxNode(SyntaxNode root, int position)
{
var node = root.FindNode(TextSpan.FromBounds(position, position));
return node.GetAncestorOrThis<BaseTypeDeclarationSyntax>();
return node.GetAncestorOrThis<TTypeDeclarationSyntax>();
}

protected override async Task<Document> AddIndentationToDocumentAsync(Document document, CancellationToken cancellationToken)
protected override async Task<Document> AddIndentationToDocumentAsync(Document document, TTypeDeclarationSyntax typeDeclaration, CancellationToken cancellationToken)
{
var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var snippet = root.GetAnnotatedNodes(FindSnippetAnnotation).FirstOrDefault();

if (snippet is not BaseTypeDeclarationSyntax originalTypeDeclaration)
return document;

var syntaxFormattingOptions = await document.GetSyntaxFormattingOptionsAsync(fallbackOptions: null, cancellationToken).ConfigureAwait(false);
var indentationString = CSharpSnippetHelpers.GetBlockLikeIndentationString(document, originalTypeDeclaration.OpenBraceToken.SpanStart, syntaxFormattingOptions, cancellationToken);
var indentationString = CSharpSnippetHelpers.GetBlockLikeIndentationString(document, typeDeclaration.OpenBraceToken.SpanStart, syntaxFormattingOptions, cancellationToken);

var newTypeDeclaration = originalTypeDeclaration.WithCloseBraceToken(
originalTypeDeclaration.CloseBraceToken.WithPrependedLeadingTrivia(SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, indentationString)));
var newTypeDeclaration = typeDeclaration.WithCloseBraceToken(
typeDeclaration.CloseBraceToken.WithPrependedLeadingTrivia(SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, indentationString)));

var newRoot = root.ReplaceNode(originalTypeDeclaration, newTypeDeclaration.WithAdditionalAnnotations(CursorAnnotation, FindSnippetAnnotation));
var newRoot = root.ReplaceNode(typeDeclaration, newTypeDeclaration.WithAdditionalAnnotations(FindSnippetAnnotation));
return document.WithSyntaxRoot(newRoot);
}

protected override void GetTypeDeclarationIdentifier(SyntaxNode node, out SyntaxToken identifier)
{
var typeDeclaration = (BaseTypeDeclarationSyntax)node;
identifier = typeDeclaration.Identifier;
}
protected sealed override SyntaxToken GetTypeDeclarationIdentifier(TTypeDeclarationSyntax baseTypeDeclaration)
=> baseTypeDeclaration.Identifier;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
using System.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.Snippets;
Expand All @@ -20,7 +20,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Snippets;
[ExportSnippetProvider(nameof(ISnippetProvider), LanguageNames.CSharp), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class CSharpClassSnippetProvider() : AbstractCSharpTypeSnippetProvider
internal sealed class CSharpClassSnippetProvider() : AbstractCSharpTypeSnippetProvider<ClassDeclarationSyntax>
{
private static readonly ISet<SyntaxKind> s_validModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
{
Expand All @@ -42,17 +42,12 @@ internal sealed class CSharpClassSnippetProvider() : AbstractCSharpTypeSnippetPr

protected override ISet<SyntaxKind> ValidModifiers => s_validModifiers;

protected override async Task<SyntaxNode> GenerateTypeDeclarationAsync(Document document, int position, CancellationToken cancellationToken)
protected override async Task<ClassDeclarationSyntax> GenerateTypeDeclarationAsync(Document document, int position, CancellationToken cancellationToken)
{
var generator = SyntaxGenerator.GetGenerator(document);
var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);

var name = NameGenerator.GenerateUniqueName("MyClass", name => semanticModel.LookupSymbols(position, name: name).IsEmpty);
return generator.ClassDeclaration(name);
}

protected override Func<SyntaxNode?, bool> GetSnippetContainerFunction(ISyntaxFacts syntaxFacts)
{
return syntaxFacts.IsClassDeclaration;
return (ClassDeclarationSyntax)generator.ClassDeclaration(name);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System;
using System.Composition;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Snippets;
using Microsoft.CodeAnalysis.Snippets.SnippetProviders;
Expand All @@ -13,6 +14,6 @@ namespace Microsoft.CodeAnalysis.CSharp.Snippets;
[ExportSnippetProvider(nameof(ISnippetProvider), LanguageNames.CSharp), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class CSharpConsoleSnippetProvider() : AbstractConsoleSnippetProvider
internal sealed class CSharpConsoleSnippetProvider() : AbstractConsoleSnippetProvider<ExpressionStatementSyntax>
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Snippets;
[ExportSnippetProvider(nameof(ISnippetProvider), LanguageNames.CSharp), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class CSharpConstructorSnippetProvider() : AbstractConstructorSnippetProvider
internal sealed class CSharpConstructorSnippetProvider() : AbstractConstructorSnippetProvider<ConstructorDeclarationSyntax>
{
private static readonly ISet<SyntaxKind> s_validModifiers = new HashSet<SyntaxKind>(SyntaxFacts.EqualityComparer)
{
Expand Down Expand Up @@ -76,20 +76,16 @@ protected override async Task<TextChange> GenerateSnippetTextChangeAsync(Documen
return new TextChange(TextSpan.FromBounds(position, position), constructorDeclaration.NormalizeWhitespace().ToFullString());
}

protected override int GetTargetCaretPosition(ISyntaxFactsService syntaxFacts, SyntaxNode caretTarget, SourceText sourceText)
{
return CSharpSnippetHelpers.GetTargetCaretPositionInBlock<ConstructorDeclarationSyntax>(
caretTarget,
protected override int GetTargetCaretPosition(ISyntaxFactsService syntaxFacts, ConstructorDeclarationSyntax constructorDeclaration, SourceText sourceText)
=> CSharpSnippetHelpers.GetTargetCaretPositionInBlock(
constructorDeclaration,
static d => d.Body!,
sourceText);
}

protected override Task<Document> AddIndentationToDocumentAsync(Document document, CancellationToken cancellationToken)
{
return CSharpSnippetHelpers.AddBlockIndentationToDocumentAsync<ConstructorDeclarationSyntax>(
protected override Task<Document> AddIndentationToDocumentAsync(Document document, ConstructorDeclarationSyntax constructorDeclaration, CancellationToken cancellationToken)
=> CSharpSnippetHelpers.AddBlockIndentationToDocumentAsync(
document,
FindSnippetAnnotation,
constructorDeclaration,
static d => d.Body!,
cancellationToken);
}
}
Loading

0 comments on commit b07ea36

Please sign in to comment.