1+ using System . Linq ;
2+ using System . Threading ;
3+ using System . Threading . Tasks ;
4+ using Microsoft . CodeAnalysis ;
5+ using Microsoft . CodeAnalysis . CodeActions ;
6+ using Microsoft . CodeAnalysis . CSharp ;
7+ using Microsoft . CodeAnalysis . CSharp . Syntax ;
8+ using Microsoft . CodeAnalysis . Editing ;
9+ using Microsoft . CodeAnalysis . Formatting ;
10+ using Microsoft . CodeAnalysis . Simplification ;
11+ using SyntaxNode = Microsoft . CodeAnalysis . SyntaxNode ;
12+ using SF = Microsoft . CodeAnalysis . CSharp . SyntaxFactory ;
13+
14+ namespace System . IO . Abstractions . Analyzers . CodeActions
15+ {
16+ public class FileServiceConstructorInitialCodeAction : CodeAction
17+ {
18+ private readonly ClassDeclarationSyntax _class ;
19+
20+ private readonly Document _document ;
21+
22+ public FileServiceConstructorInitialCodeAction ( string title , Document document , ClassDeclarationSyntax @class )
23+ {
24+ _class = @class ;
25+ _document = document ;
26+ Title = title ;
27+ }
28+
29+ public override string Title { get ; }
30+
31+ public override string EquivalenceKey => Title ;
32+
33+ protected override async Task < Document > GetChangedDocumentAsync ( CancellationToken cancellationToken )
34+ {
35+ var editor = await DocumentEditor . CreateAsync ( _document , cancellationToken ) . ConfigureAwait ( false ) ;
36+
37+ if ( ! HasFileSystemProperty ( _class ) )
38+ {
39+ editor . InsertMembers ( _class ,
40+ 0 ,
41+ new SyntaxNode [ ]
42+ {
43+ CreateFileSystemPropertyDeclaration ( )
44+ } ) ;
45+ }
46+
47+ ConstructorAddParameter ( _class , editor ) ;
48+
49+ var compilationUnitSyntax = GetCompilationUnit ( _class ) ;
50+
51+ if ( compilationUnitSyntax . Usings . Any ( ) )
52+ {
53+ editor . ReplaceNode ( GetSystemIoUsing ( compilationUnitSyntax ) , GetFileSystemUsing ( ) ) ;
54+ }
55+
56+ return editor . GetChangedDocument ( ) ;
57+ }
58+
59+ private static UsingDirectiveSyntax GetFileSystemUsing ( )
60+ {
61+ return SF . UsingDirective ( SF . ParseName ( Constants . FileSystemNameSpace ) ) ;
62+ }
63+
64+ private static UsingDirectiveSyntax GetSystemIoUsing ( CompilationUnitSyntax unit )
65+ {
66+ return unit . Usings . FirstOrDefault ( x =>
67+ x . Name . NormalizeWhitespace ( ) . ToFullString ( ) . Equals ( typeof ( Path ) . Namespace ) ) ;
68+ }
69+
70+ private static FieldDeclarationSyntax CreateFileSystemPropertyDeclaration ( )
71+ {
72+ return SF . FieldDeclaration ( SF . VariableDeclaration ( GetFileSystemType ( ) )
73+ . WithVariables ( SF . SingletonSeparatedList ( SF . VariableDeclarator ( SF . Identifier ( Constants . FieldFileSystemName ) ) ) ) )
74+ . WithModifiers ( SF . TokenList ( SF . Token ( SyntaxKind . PrivateKeyword ) ,
75+ SF . Token ( SyntaxKind . ReadOnlyKeyword ) ) ) ;
76+ }
77+
78+ private static ParameterSyntax CreateFileSystemParameterDeclaration ( )
79+ {
80+ return SF . Parameter ( SF . Identifier ( Constants . ParameterFileSystemName ) )
81+ . WithType ( GetFileSystemType ( ) )
82+ . WithAdditionalAnnotations ( Formatter . Annotation , Simplifier . SpecialTypeAnnotation )
83+ . NormalizeWhitespace ( ) ;
84+ }
85+
86+ private static TypeSyntax GetFileSystemType ( )
87+ {
88+ return SF . ParseTypeName ( Constants . FileSystemInterfaceName ) ;
89+ }
90+
91+ private static ExpressionStatementSyntax CreateAssignmentExpression ( )
92+ {
93+ return SyntaxFactory . ExpressionStatement ( SyntaxFactory . AssignmentExpression ( SyntaxKind . SimpleAssignmentExpression ,
94+ SyntaxFactory . IdentifierName ( "_fileSystem" ) ,
95+ SyntaxFactory . ObjectCreationExpression ( SyntaxFactory . IdentifierName ( Constants . FileSystemClassName ) )
96+ . WithArgumentList ( SyntaxFactory . ArgumentList ( ) ) ) ) ;
97+ }
98+
99+ private static CompilationUnitSyntax GetCompilationUnit ( SyntaxNode node )
100+ {
101+ switch ( node )
102+ {
103+ case null :
104+
105+ return null ;
106+ case CompilationUnitSyntax compilationUnitSyntax :
107+
108+ return compilationUnitSyntax ;
109+ default :
110+
111+ return GetCompilationUnit ( node . Parent ) ;
112+ }
113+ }
114+
115+ private static bool HasFileSystemProperty ( TypeDeclarationSyntax classDeclaration )
116+ {
117+ return classDeclaration . Members . OfType < PropertyDeclarationSyntax > ( )
118+ . Any ( x => x . Identifier . Text == Constants . FieldFileSystemName && x . Type == GetFileSystemType ( ) ) ;
119+ }
120+
121+ private static ConstructorDeclarationSyntax GetConstructor ( SyntaxNode classDeclaration )
122+ {
123+ return classDeclaration . ChildNodes ( ) . OfType < ConstructorDeclarationSyntax > ( ) . FirstOrDefault ( ) ;
124+ }
125+
126+ private static bool ConstructorHasFileSystemParameter ( BaseMethodDeclarationSyntax constructor )
127+ {
128+ return constructor . ParameterList . Parameters
129+ . Any ( x => x . Identifier . Text == Constants . ParameterFileSystemName && x . Type == GetFileSystemType ( ) ) ;
130+ }
131+
132+ private static bool ConstructorHasAssignmentExpression ( BaseMethodDeclarationSyntax constructor )
133+ {
134+ if ( constructor . Body == null )
135+ {
136+ return false ;
137+ }
138+
139+ return constructor . Body . Statements . OfType < ExpressionStatementSyntax > ( )
140+ . Any ( x => x . IsKind ( SyntaxKind . SimpleAssignmentExpression )
141+ && x . Expression . Contains ( SF . IdentifierName ( Constants . FieldFileSystemName ) )
142+ && x . Expression . Contains ( SF . IdentifierName ( Constants . ParameterFileSystemName ) ) ) ;
143+ }
144+
145+ private static bool HasConstructor ( SyntaxNode classDeclaration )
146+ {
147+ return classDeclaration . ChildNodes ( ) . OfType < ConstructorDeclarationSyntax > ( ) . Any ( ) ;
148+ }
149+
150+ private static void ConstructorAddParameter ( ClassDeclarationSyntax classDeclaration , SyntaxEditor editor )
151+ {
152+ var constructor = HasConstructor ( classDeclaration )
153+ ? GetConstructor ( classDeclaration )
154+ : SF . ConstructorDeclaration ( classDeclaration . Identifier )
155+ . WithModifiers ( SyntaxTokenList . Create ( SyntaxFactory . Token ( SyntaxKind . PublicKeyword ) ) ) ;
156+
157+ var newConstructor = constructor . WithAdditionalAnnotations ( Formatter . Annotation , Simplifier . Annotation )
158+ . NormalizeWhitespace ( ) ;
159+
160+ if ( ! ConstructorHasAssignmentExpression ( newConstructor ) )
161+ {
162+ newConstructor = newConstructor . AddBodyStatements ( CreateAssignmentExpression ( ) ) ;
163+ }
164+
165+ if ( HasConstructor ( classDeclaration ) )
166+ {
167+ editor . ReplaceNode ( constructor , newConstructor ) ;
168+ } else
169+ {
170+ editor . InsertBefore ( GetMethod ( classDeclaration ) , newConstructor ) ;
171+ }
172+ }
173+
174+ private static MethodDeclarationSyntax GetMethod ( ClassDeclarationSyntax classDeclaration )
175+ {
176+ return classDeclaration . ChildNodes ( ) . OfType < MethodDeclarationSyntax > ( ) . FirstOrDefault ( ) ;
177+ }
178+ }
179+ }
0 commit comments