@@ -30,6 +30,7 @@ public class FileHeaderCodeFixProvider : CodeFixProvider
3030 public override ImmutableArray < string > FixableDiagnosticIds { get ; }
3131 = ImmutableArray . Create (
3232 FileHeaderAnalyzers . SA1633DescriptorMissing . Id ,
33+ FileHeaderAnalyzers . SA1634Descriptor . Id ,
3334 FileHeaderAnalyzers . SA1635Descriptor . Id ,
3435 FileHeaderAnalyzers . SA1636Descriptor . Id ) ;
3536
@@ -56,58 +57,123 @@ private static async Task<Document> GetTransformedDocumentAsync(Document documen
5657 var settings = document . Project . AnalyzerOptions . GetStyleCopSettings ( ) ;
5758
5859 var fileHeader = FileHeaderHelpers . ParseFileHeader ( root ) ;
59- var newSyntaxRoot = fileHeader . IsMissing ? AddHeader ( document , root , document . Name , settings ) : ReplaceHeader ( document , root , settings ) ;
60+ var xmlFileHeader = FileHeaderHelpers . ParseXmlFileHeader ( root ) ;
61+ var newSyntaxRoot = fileHeader . IsMissing ? AddHeader ( document , root , document . Name , settings ) : ReplaceHeader ( document , root , settings , xmlFileHeader . IsMalformed ) ;
6062
6163 return document . WithSyntaxRoot ( newSyntaxRoot ) ;
6264 }
6365
64- private static SyntaxNode ReplaceHeader ( Document document , SyntaxNode root , StyleCopSettings settings )
66+ private static SyntaxNode ReplaceHeader ( Document document , SyntaxNode root , StyleCopSettings settings , bool isMalformedHeader )
6567 {
68+ // If the header is well formed Xml then we parse out the copyright otherwise
6669 // Skip single line comments, whitespace, and end of line trivia until a blank line is encountered.
6770 SyntaxTriviaList trivia = root . GetLeadingTrivia ( ) ;
68-
6971 bool onBlankLine = false ;
70- while ( trivia . Any ( ) )
72+ bool inCopyright = isMalformedHeader ;
73+ int ? copyrightTriviaIndex = null ;
74+ var removalList = new System . Collections . Generic . List < int > ( ) ;
75+
76+ // Need to do this with index so we get the line endings correct.
77+ for ( int i = 0 ; i < trivia . Count ; i ++ )
7178 {
79+ var triviaLine = trivia [ i ] ;
7280 bool done = false ;
73- switch ( trivia [ 0 ] . Kind ( ) )
81+ switch ( triviaLine . Kind ( ) )
7482 {
75- case SyntaxKind . SingleLineCommentTrivia :
76- trivia = trivia . RemoveAt ( 0 ) ;
77- onBlankLine = false ;
78- break ;
83+ case SyntaxKind . SingleLineCommentTrivia :
84+ if ( ! isMalformedHeader )
85+ {
86+ var openingTag = triviaLine . ToFullString ( ) . Contains ( "<copyright " ) ;
87+ var closingTag = triviaLine . ToFullString ( ) . Contains ( "</copyright>" ) ||
88+ ( openingTag && triviaLine . ToFullString ( ) . Trim ( ) . EndsWith ( "/>" ) ) ;
89+ if ( openingTag )
90+ {
91+ inCopyright = ! closingTag ;
92+ copyrightTriviaIndex = i ;
93+ }
94+ else
95+ {
96+ if ( inCopyright )
97+ {
98+ removalList . Add ( i ) ;
99+ inCopyright = ! closingTag ;
100+ }
101+ }
102+ }
103+ else
104+ {
105+ removalList . Add ( i ) ;
106+ }
107+
108+ onBlankLine = false ;
109+ break ;
110+
111+ case SyntaxKind . WhitespaceTrivia :
112+ removalList . Add ( i ) ;
113+ break ;
114+
115+ case SyntaxKind . EndOfLineTrivia :
116+ if ( inCopyright )
117+ {
118+ removalList . Add ( i ) ;
119+ }
120+
121+ if ( onBlankLine )
122+ {
123+ done = true ;
124+ }
125+ else
126+ {
127+ onBlankLine = true ;
128+ }
129+
130+ break ;
131+
132+ default :
133+ done = true ;
134+ break ;
135+ }
79136
80- case SyntaxKind . WhitespaceTrivia :
81- trivia = trivia . RemoveAt ( 0 ) ;
137+ if ( done )
138+ {
82139 break ;
140+ }
141+ }
83142
84- case SyntaxKind . EndOfLineTrivia :
85- trivia = trivia . RemoveAt ( 0 ) ;
143+ if ( isMalformedHeader )
144+ {
145+ copyrightTriviaIndex = null ;
146+ }
86147
87- if ( onBlankLine )
88- {
89- done = true ;
90- }
91- else
92- {
93- onBlankLine = true ;
94- }
148+ // Remove copyright lines in reverse order
149+ removalList . Reverse ( ) ;
150+ foreach ( var triviaLine in removalList )
151+ {
152+ trivia = trivia . RemoveAt ( triviaLine ) ;
153+ }
95154
96- break ;
155+ string newLineText = document . Project . Solution . Workspace . Options . GetOption ( FormattingOptions . NewLine , LanguageNames . CSharp ) ;
97156
98- default :
99- done = true ;
100- break ;
157+ var newHeaderTrivia = CreateNewHeader ( document . Name , settings , newLineText ) ;
158+ if ( copyrightTriviaIndex . HasValue )
159+ {
160+ if ( inCopyright )
161+ {
162+ newHeaderTrivia = newHeaderTrivia . Add ( SyntaxFactory . CarriageReturnLineFeed ) ;
101163 }
102164
103- if ( done )
165+ trivia = trivia . ReplaceRange ( trivia [ copyrightTriviaIndex . Value ] , newHeaderTrivia ) ;
166+ return root . WithLeadingTrivia ( trivia ) ;
167+ }
168+ else
169+ {
170+ if ( ( trivia . Count == 0 ) || ( trivia [ 0 ] . Kind ( ) != SyntaxKind . SingleLineCommentTrivia ) )
104171 {
105- break ;
172+ newHeaderTrivia = newHeaderTrivia . Add ( SyntaxFactory . CarriageReturnLineFeed ) ;
106173 }
107- }
108174
109- string newLineText = document . Project . Solution . Workspace . Options . GetOption ( FormattingOptions . NewLine , LanguageNames . CSharp ) ;
110- return root . WithLeadingTrivia ( CreateNewHeader ( document . Name , settings , newLineText ) . Add ( SyntaxFactory . CarriageReturnLineFeed ) . Add ( SyntaxFactory . CarriageReturnLineFeed ) . AddRange ( trivia ) ) ;
175+ return root . WithLeadingTrivia ( newHeaderTrivia . Add ( SyntaxFactory . CarriageReturnLineFeed ) . AddRange ( trivia ) ) ;
176+ }
111177 }
112178
113179 private static SyntaxNode AddHeader ( Document document , SyntaxNode root , string name , StyleCopSettings settings )
@@ -139,7 +205,7 @@ private static string WrapInXmlComment(string copyrightText, string filename, St
139205 private static string GetCopyrightText ( string copyrightText , string newLineText )
140206 {
141207 copyrightText = copyrightText . Replace ( "\r \n " , "\n " ) ;
142- return string . Join ( newLineText + "// " , copyrightText . Split ( '\n ' ) ) ;
208+ return string . Join ( newLineText + "// " , copyrightText . Split ( '\n ' ) ) . Replace ( "// " + newLineText , "//" + newLineText ) ;
143209 }
144210 }
145211}
0 commit comments