1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under MIT. See LICENSE in the project root for license information.
3
+ using System ;
4
+ using System . Collections . Generic ;
5
+ using System . ComponentModel . Composition ;
6
+ using System . IO ;
7
+ using System . Linq ;
8
+ using System . Reflection ;
9
+ using System . Threading ;
10
+ using System . Threading . Tasks ;
11
+
12
+ using Microsoft . CodeAnalysis ;
13
+ using Microsoft . CodeAnalysis . CSharp ;
14
+
15
+ namespace Microsoft . DotNet . CodeFormatting . Rules
16
+ {
17
+ [ RuleOrder ( 1 ) ]
18
+ internal sealed class HasNoCustomCopyrightHeaderFormattingRule : IFormattingRule
19
+ {
20
+ private static string RulerMarker { get ; set ; }
21
+ private static string StartMarker { get ; set ; }
22
+ private static string EndMarker { get ; set ; }
23
+
24
+ public async Task < Document > ProcessAsync ( Document document , CancellationToken cancellationToken )
25
+ {
26
+ var syntaxNode = await document . GetSyntaxRootAsync ( cancellationToken ) as CSharpSyntaxNode ;
27
+ if ( syntaxNode == null )
28
+ return document ;
29
+
30
+ var triviaList = syntaxNode . GetLeadingTrivia ( ) ;
31
+
32
+ // SetHeaders
33
+ SetHeaders ( ) ;
34
+
35
+ SyntaxTrivia start ;
36
+ SyntaxTrivia end ;
37
+ if ( ! TryGetStartAndEndOfXmlHeader ( triviaList , out start , out end ) )
38
+ return document ;
39
+
40
+ var filteredList = Filter ( triviaList , start , end ) ;
41
+ var newSyntaxNode = syntaxNode . WithLeadingTrivia ( filteredList ) ;
42
+ return document . WithSyntaxRoot ( newSyntaxNode ) ;
43
+ }
44
+
45
+ private static IEnumerable < SyntaxTrivia > Filter ( SyntaxTriviaList triviaList , SyntaxTrivia start , SyntaxTrivia end )
46
+ {
47
+ var inHeader = false ;
48
+
49
+ foreach ( var trivia in triviaList )
50
+ {
51
+ if ( trivia == start )
52
+ inHeader = true ;
53
+ else if ( trivia == end )
54
+ inHeader = false ;
55
+ else if ( ! inHeader )
56
+ yield return trivia ;
57
+ }
58
+ }
59
+
60
+ private static bool TryGetStartAndEndOfXmlHeader ( SyntaxTriviaList triviaList , out SyntaxTrivia start , out SyntaxTrivia end )
61
+ {
62
+ start = default ( SyntaxTrivia ) ;
63
+ end = default ( SyntaxTrivia ) ;
64
+
65
+ var hasStart = false ;
66
+ var hasEnd = false ;
67
+
68
+ foreach ( var trivia in triviaList )
69
+ {
70
+ if ( ! hasStart && IsBeginningOfXmlHeader ( trivia , out start ) )
71
+ hasStart = true ;
72
+
73
+ if ( ! hasEnd && IsEndOfXmlHeader ( trivia , out end ) )
74
+ hasEnd = true ;
75
+ }
76
+
77
+ return hasStart && hasEnd ;
78
+ }
79
+
80
+ private static bool IsBeginningOfXmlHeader ( SyntaxTrivia trivia , out SyntaxTrivia start )
81
+ {
82
+ var next = GetNextComment ( trivia ) ;
83
+
84
+ var currentFullText = trivia . ToFullString ( ) ;
85
+ var nextFullText = next == null ? string . Empty : next . Value . ToFullString ( ) ;
86
+
87
+ start = trivia ;
88
+ return currentFullText . StartsWith ( StartMarker , StringComparison . OrdinalIgnoreCase ) ||
89
+ currentFullText . StartsWith ( RulerMarker , StringComparison . OrdinalIgnoreCase ) &&
90
+ nextFullText . StartsWith ( StartMarker , StringComparison . OrdinalIgnoreCase ) ;
91
+ }
92
+
93
+ private static bool IsEndOfXmlHeader ( SyntaxTrivia trivia , out SyntaxTrivia end )
94
+ {
95
+ var next = GetNextComment ( trivia ) ;
96
+
97
+ var currentFullText = trivia . ToFullString ( ) ;
98
+ var nextFullText = next == null ? string . Empty : next . Value . ToFullString ( ) ;
99
+
100
+ end = nextFullText . StartsWith ( RulerMarker , StringComparison . OrdinalIgnoreCase )
101
+ ? next . Value
102
+ : trivia ;
103
+ return currentFullText . StartsWith ( EndMarker , StringComparison . OrdinalIgnoreCase ) ;
104
+ }
105
+
106
+ private static SyntaxTrivia ? GetNextComment ( SyntaxTrivia currentTrivia )
107
+ {
108
+ var trivia = currentTrivia . Token . LeadingTrivia ;
109
+ return trivia . SkipWhile ( t => t != currentTrivia )
110
+ . Skip ( 1 )
111
+ . SkipWhile ( t => t . CSharpKind ( ) != SyntaxKind . SingleLineCommentTrivia )
112
+ . Select ( t => ( SyntaxTrivia ? ) t )
113
+ . FirstOrDefault ( ) ;
114
+ }
115
+
116
+ private static void SetHeaders ( )
117
+ {
118
+ var filePath = Path . Combine (
119
+ Path . GetDirectoryName ( Uri . UnescapeDataString ( new UriBuilder ( Assembly . GetExecutingAssembly ( ) . CodeBase ) . Path ) ) ,
120
+ "CopyrightHeader.md" ) ;
121
+
122
+ if ( ! File . Exists ( filePath ) )
123
+ {
124
+ RulerMarker = "" ;
125
+ StartMarker = "" ;
126
+ EndMarker = "" ;
127
+ }
128
+ else
129
+ {
130
+ var lines = File . ReadAllLines ( filePath ) . Where ( l => ! l . StartsWith ( "##" ) && ! l . Equals ( "" ) ) . ToArray ( ) ;
131
+ RulerMarker = lines [ 0 ] ;
132
+ StartMarker = lines [ 1 ] ;
133
+ EndMarker = lines [ 2 ] ;
134
+ }
135
+ }
136
+ }
137
+ }
0 commit comments