This repository was archived by the owner on Jul 12, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 243
/
Copy pathHasNoCustomCopyrightHeaderFormattingRule.cs
158 lines (128 loc) · 5.71 KB
/
HasNoCustomCopyrightHeaderFormattingRule.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
namespace Microsoft.DotNet.CodeFormatting.Rules
{
[SyntaxRule(HasNoCustomCopyrightHeaderFormattingRule.Name, HasNoCustomCopyrightHeaderFormattingRule.Description, SyntaxRuleOrder.HasNoCustomCopyrightHeaderFormattingRule)]
internal sealed class HasNoCustomCopyrightHeaderFormattingRule : CSharpOnlyFormattingRule, ISyntaxFormattingRule
{
internal const string Name = "CustomCopyright";
internal const string Description = "Remove any custom copyright header from the file";
private static string RulerMarker { get; set; }
private static string StartMarker { get; set; }
private static string EndMarker { get; set; }
private readonly Options _options;
[ImportingConstructor]
internal HasNoCustomCopyrightHeaderFormattingRule(Options options)
{
_options = options;
}
public SyntaxNode Process(SyntaxNode syntaxNode, string languageName)
{
// SetHeaders
if (!SetHeaders())
return syntaxNode;
var triviaList = syntaxNode.GetLeadingTrivia();
SyntaxTrivia start;
SyntaxTrivia end;
if (!TryGetStartAndEndOfXmlHeader(triviaList, out start, out end))
return syntaxNode;
var filteredList = Filter(triviaList, start, end);
return syntaxNode.WithLeadingTrivia(filteredList);
}
private static IEnumerable<SyntaxTrivia> Filter(SyntaxTriviaList triviaList, SyntaxTrivia start, SyntaxTrivia end)
{
var inHeader = false;
foreach (var trivia in triviaList)
{
if (trivia == start)
inHeader = true;
else if (trivia == end)
inHeader = false;
else if (!inHeader)
yield return trivia;
}
}
private static bool TryGetStartAndEndOfXmlHeader(SyntaxTriviaList triviaList, out SyntaxTrivia start, out SyntaxTrivia end)
{
start = default(SyntaxTrivia);
end = default(SyntaxTrivia);
var hasStart = false;
var hasEnd = false;
foreach (var trivia in triviaList)
{
if (!hasStart && IsBeginningOfXmlHeader(trivia, out start))
{
hasStart = true;
continue; // we need to continue because start and end can be the same token
}
if (!hasEnd && IsEndOfXmlHeader(trivia, out end))
{
hasEnd = true;
continue; // we need to continue because start and end can be the same token
}
}
return hasStart && hasEnd;
}
private static bool IsBeginningOfXmlHeader(SyntaxTrivia trivia, out SyntaxTrivia start)
{
var next = GetNextComment(trivia);
var currentFullText = trivia.ToFullString();
var nextFullText = next == null ? string.Empty : next.Value.ToFullString();
start = trivia;
return currentFullText.StartsWith(StartMarker, StringComparison.OrdinalIgnoreCase) ||
currentFullText.StartsWith(RulerMarker, StringComparison.OrdinalIgnoreCase) &&
nextFullText.StartsWith(StartMarker, StringComparison.OrdinalIgnoreCase);
}
private static bool IsEndOfXmlHeader(SyntaxTrivia trivia, out SyntaxTrivia end)
{
var next = GetNextComment(trivia);
var currentFullText = trivia.ToFullString();
var nextFullText = next == null ? string.Empty : next.Value.ToFullString();
end = nextFullText.StartsWith(RulerMarker, StringComparison.OrdinalIgnoreCase)
? next.Value
: trivia;
return currentFullText.StartsWith(EndMarker, StringComparison.OrdinalIgnoreCase);
}
private static SyntaxTrivia? GetNextComment(SyntaxTrivia currentTrivia)
{
var trivia = currentTrivia.Token.LeadingTrivia;
return trivia.SkipWhile(t => t != currentTrivia)
.Skip(1)
.SkipWhile(t => t.Kind() != SyntaxKind.SingleLineCommentTrivia)
.Select(t => (SyntaxTrivia?)t)
.FirstOrDefault();
}
private bool SetHeaders()
{
var filePath = Path.Combine(
Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().CodeBase).Path)),
"CopyrightHeader.md");
if (!File.Exists(filePath))
{
_options.FormatLogger.WriteErrorLine("The specified CopyrightHeader.md file was not found.");
return false;
}
var lines = File.ReadAllLines(filePath).Where(l => !l.StartsWith("##") && !l.Equals("")).ToArray();
if (lines.Count() != 3)
{
_options.FormatLogger.WriteErrorLine("There should be exactly 3 lines in CopyrightHeader.md.");
return false;
}
RulerMarker = lines[0];
StartMarker = lines[1];
EndMarker = lines[2];
return true;
}
}
}