@@ -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,76 +38,70 @@ 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 ) )
41+ if ( ! HasFileSystemProperty ( _class ) )
4042 {
41- editor . AddParameter ( _constructor , parameter ) ;
42-
43- return editor . GetChangedDocument ( ) ;
43+ editor . InsertMembers ( _class ,
44+ 0 ,
45+ new SyntaxNode [ ]
46+ {
47+ CreateFileSystemPropertyDeclaration ( )
48+ } ) ;
4449 }
4550
46- var fileSystem = classDeclarationSyntax . Members
47- . OfType < FieldDeclarationSyntax > ( )
48- . FirstOrDefault ( x => x . NormalizeWhitespace ( ) . ToFullString ( ) . Equals ( Constants . FileSystemName ) ) ;
51+ ConstructorAddParameter ( _class , editor ) ;
4952
50- if ( fileSystem != null )
53+ var compilationUnitSyntax = GetCompilationUnit ( _class ) ;
54+ var fileSystemUsing = GetFileSystemUsing ( ) ;
55+
56+ if ( compilationUnitSyntax . Usings . Any ( ) )
5157 {
52- return editor . GetChangedDocument ( ) ;
58+ editor . ReplaceNode ( GetSystemIoUsing ( compilationUnitSyntax ) ,
59+ fileSystemUsing ) ;
5360 }
5461
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 ( ) ;
68-
69- editor . ReplaceNode ( _constructor , newConstructor ) ;
70- var compilationUnitSyntax = GetCompilationUnit ( _constructor ) ;
62+ return editor . GetChangedDocument ( ) ;
63+ }
7164
72- editor . ReplaceNode ( compilationUnitSyntax . Usings . FirstOrDefault ( ) ,
73- SF . UsingDirective ( SF . ParseName ( Constants . FileSystemNameSpace ) ) ) ;
65+ private static UsingDirectiveSyntax GetFileSystemUsing ( )
66+ {
67+ return SF . UsingDirective ( SF . ParseName ( Constants . FileSystemNameSpace ) ) ;
68+ }
7469
75- return editor . GetChangedDocument ( ) ;
70+ private static UsingDirectiveSyntax GetSystemIoUsing ( CompilationUnitSyntax unit )
71+ {
72+ return unit . Usings . FirstOrDefault ( x =>
73+ x . Name . NormalizeWhitespace ( ) . ToFullString ( ) . Equals ( typeof ( Path ) . Namespace ) ) ;
7674 }
7775
7876 private static FieldDeclarationSyntax CreateFileSystemPropertyDeclaration ( )
7977 {
80- return SF . FieldDeclaration ( SF . VariableDeclaration ( CreateFileSystemType ( ) )
81- . WithVariables ( SF . SingletonSeparatedList ( SF . VariableDeclarator ( SF . Identifier ( "_fileSystem" ) ) ) ) )
78+ return SF . FieldDeclaration ( SF . VariableDeclaration ( GetFileSystemType ( ) )
79+ . WithVariables ( SF . SingletonSeparatedList ( SF . VariableDeclarator ( SF . Identifier ( FieldFileSystemName ) ) ) ) )
8280 . WithModifiers ( SF . TokenList ( SF . Token ( SyntaxKind . PrivateKeyword ) ,
8381 SF . Token ( SyntaxKind . ReadOnlyKeyword ) ) ) ;
8482 }
8583
8684 private static ParameterSyntax CreateFileSystemParameterDeclaration ( )
8785 {
88- return SF . Parameter ( SF . Identifier ( "fileSystem" ) )
89- . WithType ( CreateFileSystemType ( ) )
86+ return SF . Parameter ( SF . Identifier ( ParameterFileSystemName ) )
87+ . WithType ( GetFileSystemType ( ) )
9088 . WithAdditionalAnnotations ( Formatter . Annotation , Simplifier . SpecialTypeAnnotation )
9189 . NormalizeWhitespace ( ) ;
9290 }
9391
94- private static TypeSyntax CreateFileSystemType ( )
92+ private static TypeSyntax GetFileSystemType ( )
9593 {
9694 return SF . ParseTypeName ( Constants . FileSystemName ) ;
9795 }
9896
9997 private static ExpressionStatementSyntax CreateAssignmentExpression ( )
10098 {
10199 return SF . ExpressionStatement ( SF . AssignmentExpression ( SyntaxKind . SimpleAssignmentExpression ,
102- SF . IdentifierName ( "_fileSystem" ) ,
103- SF . IdentifierName ( "fileSystem" ) ) ) ;
100+ SF . IdentifierName ( FieldFileSystemName ) ,
101+ SF . IdentifierName ( ParameterFileSystemName ) ) ) ;
104102 }
105103
106- private CompilationUnitSyntax GetCompilationUnit ( SyntaxNode node )
104+ private static CompilationUnitSyntax GetCompilationUnit ( SyntaxNode node )
107105 {
108106 switch ( node )
109107 {
@@ -118,5 +116,75 @@ private CompilationUnitSyntax GetCompilationUnit(SyntaxNode node)
118116 return GetCompilationUnit ( node . Parent ) ;
119117 }
120118 }
119+
120+ private static bool HasFileSystemProperty ( TypeDeclarationSyntax classDeclaration )
121+ {
122+ return classDeclaration . Members . OfType < PropertyDeclarationSyntax > ( )
123+ . Any ( x => x . Identifier . Text == FieldFileSystemName && x . Type == GetFileSystemType ( ) ) ;
124+ }
125+
126+ private static ConstructorDeclarationSyntax GetConstructor ( SyntaxNode classDeclaration )
127+ {
128+ return classDeclaration . ChildNodes ( ) . OfType < ConstructorDeclarationSyntax > ( ) . FirstOrDefault ( ) ;
129+ }
130+
131+ private static bool ConstructorHasFileSystemParameter ( BaseMethodDeclarationSyntax constructor )
132+ {
133+ return constructor . ParameterList . Parameters
134+ . Any ( x => x . Identifier . Text == ParameterFileSystemName && x . Type == GetFileSystemType ( ) ) ;
135+ }
136+
137+ private static bool ConstructorHasAssignmentExpression ( BaseMethodDeclarationSyntax constructor )
138+ {
139+ if ( constructor . Body == null )
140+ {
141+ return false ;
142+ }
143+
144+ return constructor . Body . Statements . OfType < ExpressionStatementSyntax > ( )
145+ . Any ( x => x . IsKind ( SyntaxKind . SimpleAssignmentExpression )
146+ && x . Expression . Contains ( SF . IdentifierName ( FieldFileSystemName ) )
147+ && x . Expression . Contains ( SF . IdentifierName ( ParameterFileSystemName ) ) ) ;
148+ }
149+
150+ private static bool HasConstructor ( SyntaxNode classDeclaration )
151+ {
152+ return classDeclaration . ChildNodes ( ) . OfType < ConstructorDeclarationSyntax > ( ) . Any ( ) ;
153+ }
154+
155+ private static void ConstructorAddParameter ( ClassDeclarationSyntax classDeclaration , SyntaxEditor editor )
156+ {
157+ var constructor = HasConstructor ( classDeclaration )
158+ ? GetConstructor ( classDeclaration )
159+ : SF . ConstructorDeclaration ( classDeclaration . Identifier )
160+ . WithModifiers ( SyntaxTokenList . Create ( SyntaxFactory . Token ( SyntaxKind . PublicKeyword ) ) ) ;
161+
162+ var newConstructor = constructor . WithAdditionalAnnotations ( Formatter . Annotation , Simplifier . Annotation )
163+ . NormalizeWhitespace ( ) ;
164+
165+ if ( ! ConstructorHasAssignmentExpression ( newConstructor ) )
166+ {
167+ newConstructor = newConstructor . AddBodyStatements ( CreateAssignmentExpression ( ) ) ;
168+ }
169+
170+ if ( ! ConstructorHasFileSystemParameter ( newConstructor ) )
171+ {
172+ var parameter = CreateFileSystemParameterDeclaration ( ) ;
173+ newConstructor = newConstructor . AddParameterListParameters ( parameter ) ;
174+ }
175+
176+ if ( HasConstructor ( classDeclaration ) )
177+ {
178+ editor . ReplaceNode ( constructor , newConstructor ) ;
179+ } else
180+ {
181+ editor . InsertBefore ( GetMethod ( classDeclaration ) , newConstructor ) ;
182+ }
183+ }
184+
185+ private static MethodDeclarationSyntax GetMethod ( ClassDeclarationSyntax classDeclaration )
186+ {
187+ return classDeclaration . ChildNodes ( ) . OfType < MethodDeclarationSyntax > ( ) . FirstOrDefault ( ) ;
188+ }
121189 }
122190}
0 commit comments