13
13
using Microsoft . CodeAnalysis . CodeActions ;
14
14
using Microsoft . CodeAnalysis . CodeFixes ;
15
15
using Microsoft . CodeAnalysis . CSharp . Syntax ;
16
+ using Microsoft . CodeAnalysis . Editing ;
16
17
using Microsoft . CodeAnalysis . Text ;
17
18
using static CommunityToolkit . Mvvm . SourceGenerators . Diagnostics . DiagnosticDescriptors ;
18
19
using static Microsoft . CodeAnalysis . CSharp . SyntaxFactory ;
@@ -63,7 +64,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
63
64
context . RegisterCodeFix (
64
65
CodeAction . Create (
65
66
title : "Inherit from ObservableObject" ,
66
- createChangedDocument : token => UpdateReference ( context . Document , classDeclaration , attributeTypeName , token ) ,
67
+ createChangedDocument : token => UpdateReference ( context . Document , root , classDeclaration , attributeTypeName , token ) ,
67
68
equivalenceKey : "Inherit from ObservableObject" ) ,
68
69
diagnostic ) ;
69
70
@@ -76,21 +77,17 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
76
77
/// Applies the code fix to a target class declaration and returns an updated document.
77
78
/// </summary>
78
79
/// <param name="document">The original document being fixed.</param>
80
+ /// <param name="root">The original tree root belonging to the current document.</param>
79
81
/// <param name="classDeclaration">The <see cref="ClassDeclarationSyntax"/> to update.</param>
80
82
/// <param name="attributeTypeName">The name of the attribute that should be removed.</param>
81
83
/// <param name="cancellationToken">The cancellation token for the operation.</param>
82
84
/// <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 )
84
86
{
85
87
// Insert ObservableObject always in first position in the base list. The type might have
86
88
// 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" ) ) ;
94
91
95
92
// Find the attribute list and attribute to remove
96
93
foreach ( AttributeListSyntax attributeList in updatedClassDeclaration . AttributeLists )
@@ -101,35 +98,13 @@ private static async Task<Document> UpdateReference(Document document, ClassDecl
101
98
( identifierName == attributeTypeName || ( identifierName + "Attribute" ) == attributeTypeName ) )
102
99
{
103
100
// We found the attribute to remove and the list to update
104
- targetAttributeList = attributeList ;
105
- targetAttribute = attribute ;
101
+ updatedClassDeclaration = ( ClassDeclarationSyntax ) generator . RemoveNode ( updatedClassDeclaration , attribute ) ;
106
102
107
103
break ;
108
104
}
109
105
}
110
106
}
111
107
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 ) ) ) ;
134
109
}
135
110
}
0 commit comments