@@ -39,16 +39,44 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
3939
4040 foreach ( Diagnostic diagnostic in context . Diagnostics )
4141 {
42+ var insertBlankLine = DetermineCodeFixAction ( diagnostic ) ;
43+ if ( insertBlankLine == null )
44+ {
45+ continue ;
46+ }
47+
4248 context . RegisterCodeFix (
4349 CodeAction . Create (
44- LayoutResources . SA1516CodeFix ,
45- cancellationToken => GetTransformedDocumentAsync ( context . Document , syntaxRoot , diagnostic , context . CancellationToken ) ,
50+ insertBlankLine . Value ? LayoutResources . SA1516CodeFixInsert : LayoutResources . SA1516CodeFixRemove ,
51+ cancellationToken => GetTransformedDocumentAsync ( context . Document , syntaxRoot , diagnostic , insertBlankLine . Value , context . CancellationToken ) ,
4652 nameof ( SA1516CodeFixProvider ) ) ,
4753 diagnostic ) ;
4854 }
4955 }
5056
51- private static Task < Document > GetTransformedDocumentAsync ( Document document , SyntaxNode syntaxRoot , Diagnostic diagnostic , CancellationToken cancellationToken )
57+ private static bool ? DetermineCodeFixAction ( Diagnostic diagnostic )
58+ {
59+ string codeFixAction ;
60+
61+ if ( ! diagnostic . Properties . TryGetValue ( SA1516ElementsMustBeSeparatedByBlankLine . CodeFixActionKey , out codeFixAction ) )
62+ {
63+ return null ;
64+ }
65+
66+ switch ( codeFixAction )
67+ {
68+ case SA1516ElementsMustBeSeparatedByBlankLine . InsertBlankLineValue :
69+ return true ;
70+
71+ case SA1516ElementsMustBeSeparatedByBlankLine . RemoveBlankLinesValue :
72+ return false ;
73+
74+ default :
75+ return null ;
76+ }
77+ }
78+
79+ private static Task < Document > GetTransformedDocumentAsync ( Document document , SyntaxNode syntaxRoot , Diagnostic diagnostic , bool insertBlankLine , CancellationToken cancellationToken )
5280 {
5381 var node = syntaxRoot . FindNode ( diagnostic . Location . SourceSpan , getInnermostNodeForTie : true ) ;
5482 node = GetRelevantNode ( node ) ;
@@ -58,18 +86,32 @@ private static Task<Document> GetTransformedDocumentAsync(Document document, Syn
5886 return Task . FromResult ( document ) ;
5987 }
6088
61- var leadingTrivia = node . GetLeadingTrivia ( ) ;
62-
63- var newTriviaList = leadingTrivia ;
64- newTriviaList = newTriviaList . Insert ( 0 , SyntaxFactory . CarriageReturnLineFeed ) ;
65-
66- var newNode = node . WithLeadingTrivia ( newTriviaList ) ;
67- var newSyntaxRoot = syntaxRoot . ReplaceNode ( node , newNode ) ;
89+ // Using the token replacement here to use the same strategy as the FixAll.
90+ var firstToken = node . GetFirstToken ( ) ;
91+ var newToken = ProcessToken ( firstToken , insertBlankLine ) ;
92+ var newSyntaxRoot = syntaxRoot . ReplaceToken ( firstToken , newToken ) ;
6893 var newDocument = document . WithSyntaxRoot ( newSyntaxRoot ) ;
6994
7095 return Task . FromResult ( newDocument ) ;
7196 }
7297
98+ private static SyntaxToken ProcessToken ( SyntaxToken token , bool insertBlankLine )
99+ {
100+ var leadingTrivia = token . LeadingTrivia ;
101+ SyntaxTriviaList newLeadingTrivia ;
102+
103+ if ( insertBlankLine )
104+ {
105+ newLeadingTrivia = leadingTrivia . Insert ( 0 , SyntaxFactory . CarriageReturnLineFeed ) ;
106+ }
107+ else
108+ {
109+ newLeadingTrivia = leadingTrivia . WithoutBlankLines ( ) ;
110+ }
111+
112+ return token . WithLeadingTrivia ( newLeadingTrivia ) ;
113+ }
114+
73115 private static SyntaxNode GetRelevantNode ( SyntaxNode innerNode )
74116 {
75117 SyntaxNode currentNode = innerNode ;
@@ -112,7 +154,7 @@ private class FixAll : DocumentBasedFixAllProvider
112154 new FixAll ( ) ;
113155
114156 protected override string CodeActionTitle =>
115- LayoutResources . SA1516CodeFix ;
157+ LayoutResources . SA1516CodeFixAll ;
116158
117159 protected override async Task < SyntaxNode > FixAllInDocumentAsync ( FixAllContext fixAllContext , Document document , ImmutableArray < Diagnostic > diagnostics )
118160 {
@@ -123,26 +165,29 @@ protected override async Task<SyntaxNode> FixAllInDocumentAsync(FixAllContext fi
123165
124166 var syntaxRoot = await document . GetSyntaxRootAsync ( ) . ConfigureAwait ( false ) ;
125167
126- List < SyntaxNode > nodes = new List < SyntaxNode > ( ) ;
168+ // Using token replacement, because node replacement will do nothing when replacing child nodes from a replaced parent node.
169+ Dictionary < SyntaxToken , SyntaxToken > replaceMap = new Dictionary < SyntaxToken , SyntaxToken > ( ) ;
127170
128171 foreach ( var diagnostic in diagnostics )
129172 {
173+ var insertBlankLine = DetermineCodeFixAction ( diagnostic ) ;
174+ if ( insertBlankLine == null )
175+ {
176+ continue ;
177+ }
178+
130179 var node = syntaxRoot . FindNode ( diagnostic . Location . SourceSpan , getInnermostNodeForTie : true ) ;
131180 node = GetRelevantNode ( node ) ;
132181
133182 if ( node != null )
134183 {
135- nodes . Add ( node ) ;
184+ var firstToken = node . GetFirstToken ( ) ;
185+
186+ replaceMap [ firstToken ] = ProcessToken ( firstToken , insertBlankLine . Value ) ;
136187 }
137188 }
138189
139- return syntaxRoot . ReplaceNodes ( nodes , ( oldNode , newNode ) =>
140- {
141- var newTriviaList = newNode . GetLeadingTrivia ( ) ;
142- newTriviaList = newTriviaList . Insert ( 0 , SyntaxFactory . CarriageReturnLineFeed ) ;
143-
144- return newNode . WithLeadingTrivia ( newTriviaList ) ;
145- } ) ;
190+ return syntaxRoot . ReplaceTokens ( replaceMap . Keys , ( original , rewritten ) => replaceMap [ original ] ) ;
146191 }
147192 }
148193 }
0 commit comments