33
44namespace StyleCop . Analyzers . DocumentationRules
55{
6+ using System . Collections . Generic ;
67 using System . Collections . Immutable ;
78 using System . Composition ;
9+ using System . Linq ;
810 using System . Threading ;
911 using System . Threading . Tasks ;
1012 using Helpers ;
1113 using Microsoft . CodeAnalysis ;
1214 using Microsoft . CodeAnalysis . CodeActions ;
1315 using Microsoft . CodeAnalysis . CodeFixes ;
16+ using Microsoft . CodeAnalysis . CSharp ;
1417 using Microsoft . CodeAnalysis . CSharp . Syntax ;
1518
1619 /// <summary>
@@ -24,14 +27,16 @@ namespace StyleCop.Analyzers.DocumentationRules
2427 [ Shared ]
2528 internal class SA1651CodeFixProvider : CodeFixProvider
2629 {
30+ private static readonly SyntaxAnnotation NodeToReplaceAnnotation = new SyntaxAnnotation ( nameof ( NodeToReplaceAnnotation ) ) ;
31+
2732 /// <inheritdoc/>
2833 public override ImmutableArray < string > FixableDiagnosticIds { get ; } =
2934 ImmutableArray . Create ( SA1651DoNotUsePlaceholderElements . DiagnosticId ) ;
3035
3136 /// <inheritdoc/>
3237 public override FixAllProvider GetFixAllProvider ( )
3338 {
34- return CustomFixAllProviders . BatchFixer ;
39+ return FixAll . Instance ;
3540 }
3641
3742 /// <inheritdoc/>
@@ -68,13 +73,9 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
6873 }
6974 }
7075
71- private async Task < Document > GetTransformedDocumentAsync ( Document document , XmlElementSyntax elementSyntax , CancellationToken cancellationToken )
76+ private static IEnumerable < XmlNodeSyntax > RemovePlaceHolder ( XmlElementSyntax elementSyntax )
7277 {
7378 SyntaxList < XmlNodeSyntax > content = elementSyntax . Content ;
74- if ( content . Count == 0 )
75- {
76- return document ;
77- }
7879
7980 var leadingTrivia = elementSyntax . StartTag . GetLeadingTrivia ( ) ;
8081 leadingTrivia = leadingTrivia . AddRange ( elementSyntax . StartTag . GetTrailingTrivia ( ) ) ;
@@ -86,9 +87,77 @@ private async Task<Document> GetTransformedDocumentAsync(Document document, XmlE
8687 trailingTrivia = trailingTrivia . AddRange ( elementSyntax . EndTag . GetTrailingTrivia ( ) ) ;
8788 content = content . Replace ( content [ content . Count - 1 ] , content [ content . Count - 1 ] . WithTrailingTrivia ( trailingTrivia ) ) ;
8889
90+ return content ;
91+ }
92+
93+ private async Task < Document > GetTransformedDocumentAsync ( Document document , XmlElementSyntax elementSyntax , CancellationToken cancellationToken )
94+ {
95+ if ( elementSyntax . Content . Count == 0 )
96+ {
97+ return document ;
98+ }
99+
89100 SyntaxNode root = await document . GetSyntaxRootAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
90- SyntaxNode newRoot = root . ReplaceNode ( elementSyntax , content ) ;
101+ SyntaxNode newRoot = root . ReplaceNode ( elementSyntax , RemovePlaceHolder ( elementSyntax ) ) ;
91102 return document . WithSyntaxRoot ( newRoot ) ;
92103 }
104+
105+ private class FixAll : DocumentBasedFixAllProvider
106+ {
107+ public static FixAll Instance { get ; } = new FixAll ( ) ;
108+
109+ protected override string CodeActionTitle { get ; } = DocumentationResources . SA1651CodeFix ;
110+
111+ protected override async Task < SyntaxNode > FixAllInDocumentAsync ( FixAllContext fixAllContext , Document document , ImmutableArray < Diagnostic > diagnostics )
112+ {
113+ var syntaxRoot = await document . GetSyntaxRootAsync ( fixAllContext . CancellationToken ) . ConfigureAwait ( false ) ;
114+ var elements = new List < XmlElementSyntax > ( ) ;
115+
116+ foreach ( var diagnostic in diagnostics )
117+ {
118+ var xmlElement = syntaxRoot . FindNode ( diagnostic . Location . SourceSpan , findInsideTrivia : true , getInnermostNodeForTie : true ) as XmlElementSyntax ;
119+
120+ if ( ( xmlElement != null )
121+ && ( xmlElement . Content . Count > 0 )
122+ && ! string . IsNullOrWhiteSpace ( xmlElement . Content . ToString ( ) ) )
123+ {
124+ elements . Add ( xmlElement ) ;
125+ }
126+ }
127+
128+ var newSyntaxRoot = syntaxRoot . ReplaceNodes ( elements , ( original , rewritten ) => rewritten . WithAdditionalAnnotations ( NodeToReplaceAnnotation ) ) ;
129+ newSyntaxRoot = new FixAllVisitor ( ) . Visit ( newSyntaxRoot ) ;
130+ return newSyntaxRoot ;
131+ }
132+ }
133+
134+ private class FixAllVisitor : CSharpSyntaxRewriter
135+ {
136+ public FixAllVisitor ( )
137+ : base ( true )
138+ {
139+ }
140+
141+ public override SyntaxList < TNode > VisitList < TNode > ( SyntaxList < TNode > list )
142+ {
143+ list = base . VisitList ( list ) ;
144+
145+ var index = 0 ;
146+ while ( index < list . Count )
147+ {
148+ var element = list [ index ] ;
149+ if ( element . HasAnnotation ( NodeToReplaceAnnotation ) )
150+ {
151+ list = list . ReplaceRange ( element , RemovePlaceHolder ( element as XmlElementSyntax ) . Cast < TNode > ( ) ) ;
152+ }
153+ else
154+ {
155+ index ++ ;
156+ }
157+ }
158+
159+ return list ;
160+ }
161+ }
93162 }
94163}
0 commit comments