@@ -15,13 +15,17 @@ namespace System.IO.Abstractions.Analyzers.CodeActions
1515 /// <inheritdoc />
1616 public class FileServiceInterfaceInjectionCodeAction : CodeAction
1717 {
18- private readonly ConstructorDeclarationSyntax _constructor ;
18+ private const string FieldFileSystemName = "_fileSystem" ;
19+
20+ private const string ParameterFileSystemName = "fileSystem" ;
21+
22+ private readonly ClassDeclarationSyntax _class ;
1923
2024 private readonly Document _document ;
2125
22- public FileServiceInterfaceInjectionCodeAction ( string title , Document document , ConstructorDeclarationSyntax constructor )
26+ public FileServiceInterfaceInjectionCodeAction ( string title , Document document , ClassDeclarationSyntax @class )
2327 {
24- _constructor = constructor ;
28+ _class = @class ;
2529 _document = document ;
2630 Title = title ;
2731 }
@@ -34,40 +38,21 @@ protected override async Task<Document> GetChangedDocumentAsync(CancellationToke
3438 {
3539 var editor = await DocumentEditor . CreateAsync ( _document , cancellationToken ) . ConfigureAwait ( false ) ;
3640
37- var parameter = CreateFileSystemParameterDeclaration ( ) ;
38-
39- if ( ! ( _constructor . Parent is ClassDeclarationSyntax classDeclarationSyntax ) )
40- {
41- editor . AddParameter ( _constructor , parameter ) ;
42-
43- return editor . GetChangedDocument ( ) ;
44- }
45-
46- var fileSystem = classDeclarationSyntax . Members
47- . OfType < FieldDeclarationSyntax > ( )
48- . FirstOrDefault ( x => x . NormalizeWhitespace ( ) . ToFullString ( ) . Equals ( Constants . FileSystemName ) ) ;
49-
50- if ( fileSystem != null )
41+ if ( ! HasFileSystemProperty ( _class ) )
5142 {
52- return editor . GetChangedDocument ( ) ;
43+ var fileSystemPropertyDeclaration = CreateFileSystemPropertyDeclaration ( ) ;
44+
45+ editor . InsertMembers ( _class ,
46+ 0 ,
47+ new SyntaxNode [ ]
48+ {
49+ fileSystemPropertyDeclaration
50+ } ) ;
5351 }
5452
55- var fileSystemPropertyDeclaration = CreateFileSystemPropertyDeclaration ( ) ;
56-
57- editor . InsertMembers ( classDeclarationSyntax ,
58- 0 ,
59- new SyntaxNode [ ]
60- {
61- fileSystemPropertyDeclaration
62- } ) ;
63-
64- var newConstructor = _constructor . WithBody ( _constructor . Body . AddStatements ( CreateAssignmentExpression ( ) ) )
65- . AddParameterListParameters ( parameter )
66- . WithAdditionalAnnotations ( Formatter . Annotation , Simplifier . Annotation )
67- . NormalizeWhitespace ( ) ;
53+ ConstructorAddParameter ( _class , editor ) ;
6854
69- editor . ReplaceNode ( _constructor , newConstructor ) ;
70- var compilationUnitSyntax = GetCompilationUnit ( _constructor ) ;
55+ var compilationUnitSyntax = GetCompilationUnit ( _class ) ;
7156
7257 editor . ReplaceNode ( compilationUnitSyntax . Usings . FirstOrDefault ( ) ,
7358 SF . UsingDirective ( SF . ParseName ( Constants . FileSystemNameSpace ) ) ) ;
@@ -77,33 +62,33 @@ protected override async Task<Document> GetChangedDocumentAsync(CancellationToke
7762
7863 private static FieldDeclarationSyntax CreateFileSystemPropertyDeclaration ( )
7964 {
80- return SF . FieldDeclaration ( SF . VariableDeclaration ( CreateFileSystemType ( ) )
81- . WithVariables ( SF . SingletonSeparatedList ( SF . VariableDeclarator ( SF . Identifier ( "_fileSystem" ) ) ) ) )
65+ return SF . FieldDeclaration ( SF . VariableDeclaration ( GetFileSystemType ( ) )
66+ . WithVariables ( SF . SingletonSeparatedList ( SF . VariableDeclarator ( SF . Identifier ( FieldFileSystemName ) ) ) ) )
8267 . WithModifiers ( SF . TokenList ( SF . Token ( SyntaxKind . PrivateKeyword ) ,
8368 SF . Token ( SyntaxKind . ReadOnlyKeyword ) ) ) ;
8469 }
8570
8671 private static ParameterSyntax CreateFileSystemParameterDeclaration ( )
8772 {
88- return SF . Parameter ( SF . Identifier ( "fileSystem" ) )
89- . WithType ( CreateFileSystemType ( ) )
73+ return SF . Parameter ( SF . Identifier ( ParameterFileSystemName ) )
74+ . WithType ( GetFileSystemType ( ) )
9075 . WithAdditionalAnnotations ( Formatter . Annotation , Simplifier . SpecialTypeAnnotation )
9176 . NormalizeWhitespace ( ) ;
9277 }
9378
94- private static TypeSyntax CreateFileSystemType ( )
79+ private static TypeSyntax GetFileSystemType ( )
9580 {
9681 return SF . ParseTypeName ( Constants . FileSystemName ) ;
9782 }
9883
9984 private static ExpressionStatementSyntax CreateAssignmentExpression ( )
10085 {
10186 return SF . ExpressionStatement ( SF . AssignmentExpression ( SyntaxKind . SimpleAssignmentExpression ,
102- SF . IdentifierName ( "_fileSystem" ) ,
103- SF . IdentifierName ( "fileSystem" ) ) ) ;
87+ SF . IdentifierName ( FieldFileSystemName ) ,
88+ SF . IdentifierName ( ParameterFileSystemName ) ) ) ;
10489 }
10590
106- private CompilationUnitSyntax GetCompilationUnit ( SyntaxNode node )
91+ private static CompilationUnitSyntax GetCompilationUnit ( SyntaxNode node )
10792 {
10893 switch ( node )
10994 {
@@ -118,5 +103,65 @@ private CompilationUnitSyntax GetCompilationUnit(SyntaxNode node)
118103 return GetCompilationUnit ( node . Parent ) ;
119104 }
120105 }
106+
107+ private static bool HasFileSystemProperty ( TypeDeclarationSyntax classDeclaration )
108+ {
109+ return classDeclaration . Members . OfType < PropertyDeclarationSyntax > ( )
110+ . Any ( x => x . Identifier . Text == FieldFileSystemName && x . Type == GetFileSystemType ( ) ) ;
111+ }
112+
113+ private static ConstructorDeclarationSyntax GetConstructor ( SyntaxNode classDeclaration )
114+ {
115+ return classDeclaration . ChildNodes ( ) . OfType < ConstructorDeclarationSyntax > ( ) . FirstOrDefault ( ) ;
116+ }
117+
118+ private static bool ConstructorHasFileSystemParameter ( BaseMethodDeclarationSyntax constructor )
119+ {
120+ return constructor . ParameterList . Parameters
121+ . Any ( x => x . Identifier . Text == ParameterFileSystemName && x . Type == GetFileSystemType ( ) ) ;
122+ }
123+
124+ private static bool ConstructorHasAssignmentExpression ( BaseMethodDeclarationSyntax constructor )
125+ {
126+ return constructor . Body . Statements . OfType < ExpressionStatementSyntax > ( )
127+ . Any ( x => x . IsKind ( SyntaxKind . SimpleAssignmentExpression )
128+ && x . Expression . Contains ( SF . IdentifierName ( FieldFileSystemName ) )
129+ && x . Expression . Contains ( SF . IdentifierName ( ParameterFileSystemName ) ) ) ;
130+ }
131+
132+ private static bool HasConstructor ( SyntaxNode classDeclaration )
133+ {
134+ return classDeclaration . ChildNodes ( ) . OfType < ConstructorDeclarationSyntax > ( ) . Any ( ) ;
135+ }
136+
137+ private static void ConstructorAddParameter ( ClassDeclarationSyntax classDeclaration , DocumentEditor editor )
138+ {
139+ var constructor = HasConstructor ( classDeclaration )
140+ ? GetConstructor ( classDeclaration )
141+ : SF . ConstructorDeclaration ( classDeclaration . Identifier ) ;
142+
143+ var newConstructor = constructor . WithBody ( constructor . Body ? . AddStatements ( CreateAssignmentExpression ( ) ) )
144+ . WithAdditionalAnnotations ( Formatter . Annotation , Simplifier . Annotation )
145+ . NormalizeWhitespace ( ) ;
146+
147+ if ( ! ConstructorHasFileSystemParameter ( newConstructor ) )
148+ {
149+ var parameter = CreateFileSystemParameterDeclaration ( ) ;
150+ newConstructor = newConstructor . AddParameterListParameters ( parameter ) ;
151+ }
152+
153+ if ( HasConstructor ( classDeclaration ) )
154+ {
155+ editor . ReplaceNode ( constructor , newConstructor ) ;
156+ } else
157+ {
158+ editor . InsertMembers ( classDeclaration ,
159+ 0 ,
160+ new SyntaxNode [ ]
161+ {
162+ newConstructor
163+ } ) ;
164+ }
165+ }
121166 }
122167}
0 commit comments