@@ -29,6 +29,7 @@ public class EpiPartialRouterCodeFixProvider : CodeFixProvider
2929 public sealed override ImmutableArray < string > FixableDiagnosticIds => ImmutableArray . Create ( EpiPartialRouterAnalyzer . DiagnosticId ) ;
3030 private const string RoutingNamespace = "EPiServer.Core.Routing" ;
3131 private const string RoutingPipelineNamespace = "EPiServer.Core.Routing.Pipeline" ;
32+ private const string DependencyInjectionNamespace = "Microsoft.Extensions.DependencyInjection" ;
3233
3334 public sealed override FixAllProvider GetFixAllProvider ( ) =>
3435 // See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/FixAllProvider.md for more information on Fix All Providers
@@ -45,18 +46,26 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
4546
4647 var diagnostic = context . Diagnostics . First ( ) ;
4748 var diagnosticSpan = diagnostic . Location . SourceSpan ;
48- var methodDeclaration = root . FindNode ( diagnosticSpan ) as ClassDeclarationSyntax ;
49- if ( methodDeclaration is null )
49+ var declaration = root . FindNode ( diagnosticSpan ) ;
50+ if ( declaration is ClassDeclarationSyntax classDeclaration )
5051 {
51- return ;
52+ context . RegisterCodeFix (
53+ CodeAction . Create (
54+ Resources . EpiPartialRouterTitle ,
55+ c => ReplaceParametersAsync ( context . Document , classDeclaration , c ) ,
56+ nameof ( Resources . EpiPartialRouterTitle ) ) ,
57+ diagnostic ) ;
5258 }
53-
54- context . RegisterCodeFix (
55- CodeAction . Create (
56- Resources . EpiPartialRouterTitle ,
57- c => ReplaceParametersAsync ( context . Document , methodDeclaration , c ) ,
58- nameof ( Resources . EpiPartialRouterTitle ) ) ,
59- diagnostic ) ;
59+ else if ( declaration is InvocationExpressionSyntax invocationExpression )
60+ {
61+ context . RegisterCodeFix (
62+ CodeAction . Create (
63+ Resources . EpiPartialRouterTitle ,
64+ c => HandlePartialRouteRegistration ( context . Document , invocationExpression , c ) ,
65+ nameof ( Resources . EpiPartialRouterTitle ) ) ,
66+ diagnostic ) ;
67+ }
68+
6069 }
6170
6271 private static async Task < Document > ReplaceParametersAsync ( Document document , ClassDeclarationSyntax localDeclaration , CancellationToken cancellationToken )
@@ -92,5 +101,83 @@ private static MethodDeclarationSyntax FindMethod(ClassDeclarationSyntax classDe
92101 {
93102 return classDeclaration . Members . OfType < MethodDeclarationSyntax > ( ) . FirstOrDefault ( m => m . Identifier . Text == methodName ) ;
94103 }
104+
105+ private async Task < Document > HandlePartialRouteRegistration ( Document document , InvocationExpressionSyntax invocationExpression , CancellationToken cancellationToken )
106+ {
107+ //find the registration statement
108+ var currentCodeBlock = GetSurroundingBlock ( invocationExpression ) ;
109+ var getPartialRouterArgument = invocationExpression . ArgumentList . Arguments [ 0 ] . Expression ;
110+
111+ if ( currentCodeBlock is not null )
112+ {
113+ var root = await document . GetSyntaxRootAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
114+ var index = - 1 ;
115+ var statements = currentCodeBlock . Statements ;
116+ //find where registration statement is
117+ for ( var i = 0 ; i < statements . Count ; i ++ )
118+ {
119+ var statement = statements [ i ] ;
120+ if ( statement is ExpressionStatementSyntax expressionStatement && expressionStatement . Expression == invocationExpression )
121+ {
122+ index = i ;
123+ break ;
124+ }
125+ }
126+
127+ if ( index > - 1 )
128+ {
129+ var statement = statements [ index ] ;
130+ statements = statements . RemoveAt ( index ) ;
131+ var methodDeclaration = GetSurroundingMethod ( invocationExpression ) ;
132+ if ( methodDeclaration is not null && methodDeclaration . Identifier . Text == "ConfigureContainer" )
133+ {
134+ var parameterName = methodDeclaration . ParameterList . Parameters [ 0 ] . Identifier . Text ;
135+ if ( getPartialRouterArgument is ObjectCreationExpressionSyntax objectCreationExpression )
136+ {
137+ statements = statements . Insert ( index , SyntaxFactory . ParseStatement ( $ "{ parameterName } .Services.AddSingleton<IPartialRouter, { objectCreationExpression . Type } >();")
138+ . WithLeadingTrivia ( statement . GetLeadingTrivia ( ) ) . WithTrailingTrivia ( SyntaxFactory . CarriageReturnLineFeed ) ) ;
139+ }
140+ else
141+ {
142+ statements = statements . Insert ( index , SyntaxFactory . ParseStatement ( $ "{ parameterName } .Services.AddSingleton<IPartialRouter>({ getPartialRouterArgument . ToString ( ) } );")
143+ . WithLeadingTrivia ( statement . GetLeadingTrivia ( ) ) . WithTrailingTrivia ( SyntaxFactory . CarriageReturnLineFeed ) ) ;
144+ }
145+ }
146+ else
147+ {
148+ //The registration is done somewhere where we do not have access to IServiceCollection, add a comment on how registration should be done
149+ var parameterType = getPartialRouterArgument is ObjectCreationExpressionSyntax objectCreationExpression ? objectCreationExpression . Type . ToString ( ) : "CustomPartialRouter" ;
150+ var comment = SyntaxFactory . Comment ( $ "//Partial router should be registered in ioc container like 'services.AddSingleton<IPartialRouter, { parameterType } >()'") ;
151+ statements = statements . Insert ( index , statement . WithLeadingTrivia ( statement . GetLeadingTrivia ( ) . Add ( comment ) . Add ( SyntaxFactory . CarriageReturnLineFeed ) ) ) ;
152+ }
153+
154+ var newCodeBlock = currentCodeBlock . WithStatements ( statements ) ;
155+ var newroot = root . ReplaceNode ( currentCodeBlock , newCodeBlock ) ;
156+ return await document . WithSyntaxRoot ( newroot ) . AddUsingIfMissingAsync ( cancellationToken , DependencyInjectionNamespace ) ;
157+ }
158+ }
159+
160+ return document ;
161+ }
162+
163+ private BlockSyntax GetSurroundingBlock ( InvocationExpressionSyntax invocationExpression )
164+ {
165+ var parent = invocationExpression . Parent ;
166+ while ( parent is not null && parent is not BlockSyntax )
167+ {
168+ parent = parent . Parent ;
169+ }
170+ return parent as BlockSyntax ;
171+ }
172+
173+ private MethodDeclarationSyntax GetSurroundingMethod ( InvocationExpressionSyntax invocationExpression )
174+ {
175+ var parent = invocationExpression . Parent ;
176+ while ( parent is not null && parent is not MethodDeclarationSyntax )
177+ {
178+ parent = parent . Parent ;
179+ }
180+ return parent as MethodDeclarationSyntax ;
181+ }
95182 }
96183}
0 commit comments