Skip to content

Commit 6cdf53b

Browse files
committed
Use SyntaxGenerator and leave trivia handling up to it
1 parent 3b2226b commit 6cdf53b

File tree

1 file changed

+8
-33
lines changed

1 file changed

+8
-33
lines changed

src/CommunityToolkit.Mvvm.CodeFixers/ClassUsingAttributeInsteadOfInheritanceCodeFixer.cs

Lines changed: 8 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using Microsoft.CodeAnalysis.CodeActions;
1414
using Microsoft.CodeAnalysis.CodeFixes;
1515
using Microsoft.CodeAnalysis.CSharp.Syntax;
16+
using Microsoft.CodeAnalysis.Editing;
1617
using Microsoft.CodeAnalysis.Text;
1718
using static CommunityToolkit.Mvvm.SourceGenerators.Diagnostics.DiagnosticDescriptors;
1819
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
@@ -63,7 +64,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
6364
context.RegisterCodeFix(
6465
CodeAction.Create(
6566
title: "Inherit from ObservableObject",
66-
createChangedDocument: token => UpdateReference(context.Document, classDeclaration, attributeTypeName, token),
67+
createChangedDocument: token => UpdateReference(context.Document, root, classDeclaration, attributeTypeName, token),
6768
equivalenceKey: "Inherit from ObservableObject"),
6869
diagnostic);
6970

@@ -76,21 +77,17 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
7677
/// Applies the code fix to a target class declaration and returns an updated document.
7778
/// </summary>
7879
/// <param name="document">The original document being fixed.</param>
80+
/// <param name="root">The original tree root belonging to the current document.</param>
7981
/// <param name="classDeclaration">The <see cref="ClassDeclarationSyntax"/> to update.</param>
8082
/// <param name="attributeTypeName">The name of the attribute that should be removed.</param>
8183
/// <param name="cancellationToken">The cancellation token for the operation.</param>
8284
/// <returns>An updated document with the applied code fix, and <paramref name="classDeclaration"/> inheriting from <c>ObservableObject</c>.</returns>
83-
private static async Task<Document> UpdateReference(Document document, ClassDeclarationSyntax classDeclaration, string attributeTypeName, CancellationToken cancellationToken)
85+
private static Task<Document> UpdateReference(Document document, SyntaxNode root, ClassDeclarationSyntax classDeclaration, string attributeTypeName, CancellationToken cancellationToken)
8486
{
8587
// Insert ObservableObject always in first position in the base list. The type might have
8688
// some interfaces in the base list, so we just copy them back after ObservableObject.
87-
ClassDeclarationSyntax updatedClassDeclaration =
88-
classDeclaration.WithBaseList(BaseList(SingletonSeparatedList(
89-
(BaseTypeSyntax)SimpleBaseType(IdentifierName("ObservableObject"))))
90-
.AddTypes(classDeclaration.BaseList?.Types.ToArray() ?? Array.Empty<BaseTypeSyntax>()));
91-
92-
AttributeListSyntax? targetAttributeList = null;
93-
AttributeSyntax? targetAttribute = null;
89+
SyntaxGenerator generator = SyntaxGenerator.GetGenerator(document);
90+
ClassDeclarationSyntax updatedClassDeclaration = (ClassDeclarationSyntax)generator.AddBaseType(classDeclaration, IdentifierName("ObservableObject"));
9491

9592
// Find the attribute list and attribute to remove
9693
foreach (AttributeListSyntax attributeList in updatedClassDeclaration.AttributeLists)
@@ -101,35 +98,13 @@ private static async Task<Document> UpdateReference(Document document, ClassDecl
10198
(identifierName == attributeTypeName || (identifierName + "Attribute") == attributeTypeName))
10299
{
103100
// We found the attribute to remove and the list to update
104-
targetAttributeList = attributeList;
105-
targetAttribute = attribute;
101+
updatedClassDeclaration = (ClassDeclarationSyntax)generator.RemoveNode(updatedClassDeclaration, attribute);
106102

107103
break;
108104
}
109105
}
110106
}
111107

112-
// If we found an attribute to remove, do that
113-
if (targetAttribute is not null)
114-
{
115-
// If the target list has more than one attribute, keep it and just remove the target one
116-
if (targetAttributeList!.Attributes.Count > 1)
117-
{
118-
updatedClassDeclaration =
119-
updatedClassDeclaration.ReplaceNode(
120-
targetAttributeList,
121-
targetAttributeList.RemoveNode(targetAttribute, SyntaxRemoveOptions.KeepNoTrivia)!);
122-
}
123-
else
124-
{
125-
// Otherwise, remove the entire attribute list
126-
updatedClassDeclaration = updatedClassDeclaration.RemoveNode(targetAttributeList, SyntaxRemoveOptions.KeepExteriorTrivia)!;
127-
}
128-
}
129-
130-
SyntaxNode originalRoot = await classDeclaration.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false);
131-
SyntaxTree updatedTree = originalRoot.ReplaceNode(classDeclaration, updatedClassDeclaration).SyntaxTree;
132-
133-
return document.WithSyntaxRoot(await updatedTree.GetRootAsync(cancellationToken).ConfigureAwait(false));
108+
return Task.FromResult(document.WithSyntaxRoot(root.ReplaceNode(classDeclaration, updatedClassDeclaration)));
134109
}
135110
}

0 commit comments

Comments
 (0)