Skip to content
This repository was archived by the owner on Jul 12, 2022. It is now read-only.

Commit 25b1b18

Browse files
committed
Strip existing copyright header if present
1 parent b1db85f commit 25b1b18

File tree

5 files changed

+196
-79
lines changed

5 files changed

+196
-79
lines changed

src/Microsoft.DotNet.CodeFormatting.Tests/Rules/CopyrightHeaderRuleTests.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,25 @@ class C
7272
Verify(source, expected);
7373
}
7474

75+
[Fact]
76+
public void CSharpRemoveOlder()
77+
{
78+
_options.CopyrightHeader = ImmutableArray.Create("test");
79+
var source = @"// copyright
80+
81+
class C
82+
{
83+
}";
84+
85+
var expected = @"// test
86+
87+
class C
88+
{
89+
}";
90+
Verify(source, expected);
91+
92+
}
93+
7594
[Fact]
7695
public void VisualBasicSimple()
7796
{

src/Microsoft.DotNet.CodeFormatting/Rules/CopyrightHeaderRule.CSharp.cs

Lines changed: 17 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,58 +10,43 @@
1010

1111
using Microsoft.CodeAnalysis;
1212
using Microsoft.CodeAnalysis.CSharp;
13+
using System.Collections.Immutable;
1314

1415
namespace Microsoft.DotNet.CodeFormatting.Rules
1516
{
1617
internal partial class CopyrightHeaderRule
1718
{
18-
private sealed class CSharpRule
19+
private sealed class CSharpRule : CommonRule
1920
{
20-
private readonly Options _options;
21-
22-
internal CSharpRule(Options options)
21+
internal CSharpRule(ImmutableArray<string> header) : base(header)
2322
{
24-
_options = options;
2523
}
2624

27-
internal SyntaxNode Process(SyntaxNode syntaxNode)
25+
protected override SyntaxTriviaList CreateTriviaList(IEnumerable<SyntaxTrivia> e)
2826
{
29-
if (_options.CopyrightHeader.IsDefaultOrEmpty)
30-
{
31-
return syntaxNode;
32-
}
33-
34-
if (HasCopyrightHeader(syntaxNode))
35-
return syntaxNode;
36-
37-
return AddCopyrightHeader(syntaxNode);
27+
return SyntaxFactory.TriviaList(e);
3828
}
3929

40-
private bool HasCopyrightHeader(SyntaxNode syntaxNode)
30+
protected override bool IsLineComment(SyntaxTrivia trivia)
4131
{
42-
var leadingComments = syntaxNode.GetLeadingTrivia().Where(t => t.CSharpKind() == SyntaxKind.SingleLineCommentTrivia).ToArray();
43-
var header = _options.CopyrightHeader;
44-
if (leadingComments.Length < header.Length)
45-
return false;
32+
return trivia.CSharpKind() == SyntaxKind.SingleLineCommentTrivia;
33+
}
4634

47-
return leadingComments.Take(header.Length)
48-
.Select(t => GetCommentText(t.ToFullString()))
49-
.SequenceEqual(header.Select(GetCommentText));
35+
protected override bool IsWhiteSpaceOrNewLine(SyntaxTrivia trivia)
36+
{
37+
return
38+
trivia.CSharpKind() == SyntaxKind.WhitespaceTrivia ||
39+
trivia.CSharpKind() == SyntaxKind.EndOfLineTrivia;
5040
}
5141

52-
private SyntaxNode AddCopyrightHeader(SyntaxNode syntaxNode)
42+
protected override SyntaxTrivia CreateLineComment(string commentText)
5343
{
54-
var newTrivia = GetCopyrightHeader().Concat(syntaxNode.GetLeadingTrivia());
55-
return syntaxNode.WithLeadingTrivia(newTrivia);
44+
return SyntaxFactory.Comment("// " + commentText);
5645
}
5746

58-
private IEnumerable<SyntaxTrivia> GetCopyrightHeader()
47+
protected override SyntaxTrivia CreateNewLine()
5948
{
60-
foreach (var headerLine in _options.CopyrightHeader)
61-
{
62-
yield return SyntaxFactory.Comment("// " + GetCommentText(headerLine));
63-
yield return SyntaxFactory.CarriageReturnLineFeed;
64-
}
49+
return SyntaxFactory.CarriageReturnLineFeed;
6550
}
6651
}
6752
}

src/Microsoft.DotNet.CodeFormatting/Rules/CopyrightHeaderRule.VisualBasic.cs

Lines changed: 17 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,58 +9,43 @@
99
using System.Threading.Tasks;
1010
using Microsoft.CodeAnalysis;
1111
using Microsoft.CodeAnalysis.VisualBasic;
12+
using System.Collections.Immutable;
1213

1314
namespace Microsoft.DotNet.CodeFormatting.Rules
1415
{
1516
internal partial class CopyrightHeaderRule
1617
{
17-
private sealed class VisualBasicRule
18+
private sealed class VisualBasicRule : CommonRule
1819
{
19-
private readonly Options _options;
20-
21-
internal VisualBasicRule(Options options)
20+
internal VisualBasicRule(ImmutableArray<string> header) : base(header)
2221
{
23-
_options = options;
2422
}
2523

26-
internal SyntaxNode Process(SyntaxNode syntaxNode)
24+
protected override SyntaxTriviaList CreateTriviaList(IEnumerable<SyntaxTrivia> e)
2725
{
28-
if (_options.CopyrightHeader.IsDefaultOrEmpty)
29-
{
30-
return syntaxNode;
31-
}
32-
33-
if (HasCopyrightHeader(syntaxNode))
34-
return syntaxNode;
35-
36-
return AddCopyrightHeader(syntaxNode);
26+
return SyntaxFactory.TriviaList(e);
3727
}
3828

39-
private bool HasCopyrightHeader(SyntaxNode syntaxNode)
29+
protected override bool IsLineComment(SyntaxTrivia trivia)
4030
{
41-
var leadingComments = syntaxNode.GetLeadingTrivia().Where(t => t.VBKind() == SyntaxKind.CommentTrivia).ToArray();
42-
var header = _options.CopyrightHeader;
43-
if (leadingComments.Length < header.Length)
44-
return false;
31+
return trivia.VBKind() == SyntaxKind.CommentTrivia;
32+
}
4533

46-
return leadingComments.Take(header.Length)
47-
.Select(t => GetCommentText(t.ToFullString()))
48-
.SequenceEqual(header.Select(GetCommentText));
34+
protected override bool IsWhiteSpaceOrNewLine(SyntaxTrivia trivia)
35+
{
36+
return
37+
trivia.VBKind() == SyntaxKind.WhitespaceTrivia ||
38+
trivia.VBKind() == SyntaxKind.EndOfLineTrivia;
4939
}
5040

51-
private SyntaxNode AddCopyrightHeader(SyntaxNode syntaxNode)
41+
protected override SyntaxTrivia CreateLineComment(string commentText)
5242
{
53-
var newTrivia = GetCopyrightHeader().Concat(syntaxNode.GetLeadingTrivia());
54-
return syntaxNode.WithLeadingTrivia(newTrivia);
43+
return SyntaxFactory.CommentTrivia("' " + commentText);
5544
}
5645

57-
private IEnumerable<SyntaxTrivia> GetCopyrightHeader()
46+
protected override SyntaxTrivia CreateNewLine()
5847
{
59-
foreach (var headerLine in _options.CopyrightHeader)
60-
{
61-
yield return SyntaxFactory.CommentTrivia("' " + GetCommentText(headerLine));
62-
yield return SyntaxFactory.CarriageReturnLineFeed;
63-
}
48+
return SyntaxFactory.CarriageReturnLineFeed;
6449
}
6550
}
6651
}

src/Microsoft.DotNet.CodeFormatting/Rules/CopyrightHeaderRule.cs

Lines changed: 142 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,36 +7,148 @@
77
using System.Linq;
88
using System.Threading;
99
using System.Threading.Tasks;
10-
10+
using System.Collections.Immutable;
1111
using Microsoft.CodeAnalysis;
12-
using Microsoft.CodeAnalysis.CSharp;
1312

1413
namespace Microsoft.DotNet.CodeFormatting.Rules
1514
{
16-
[SyntaxRuleOrder(SyntaxRuleOrder.HasCopyrightHeaderFormattingRule)]
15+
[SyntaxRuleOrder(SyntaxRuleOrder.CopyrightHeaderRule)]
1716
internal sealed partial class CopyrightHeaderRule : SyntaxFormattingRule, ISyntaxFormattingRule
1817
{
18+
private abstract class CommonRule
19+
{
20+
/// <summary>
21+
/// This is the normalized copyright header that has no comment delimeters.
22+
/// </summary>
23+
private readonly ImmutableArray<string> _header;
24+
25+
protected CommonRule(ImmutableArray<string> header)
26+
{
27+
_header = header;
28+
}
29+
30+
internal SyntaxNode Process(SyntaxNode syntaxNode)
31+
{
32+
if (_header.IsDefaultOrEmpty)
33+
{
34+
return syntaxNode;
35+
}
36+
37+
if (HasCopyrightHeader(syntaxNode))
38+
return syntaxNode;
39+
40+
return AddCopyrightHeader(syntaxNode);
41+
}
42+
43+
private bool HasCopyrightHeader(SyntaxNode syntaxNode)
44+
{
45+
var existingHeader = GetExistingHeader(syntaxNode.GetLeadingTrivia());
46+
return _header.SequenceEqual(existingHeader);
47+
}
48+
49+
private SyntaxNode AddCopyrightHeader(SyntaxNode syntaxNode)
50+
{
51+
var list = new List<SyntaxTrivia>();
52+
foreach (var headerLine in _header)
53+
{
54+
list.Add(CreateLineComment(headerLine));
55+
list.Add(CreateNewLine());
56+
}
57+
list.Add(CreateNewLine());
58+
59+
var triviaList = RemoveExistingHeader(syntaxNode.GetLeadingTrivia());
60+
var i = 0;
61+
MovePastBlankLines(triviaList, ref i);
62+
63+
while (i < triviaList.Count)
64+
{
65+
list.Add(triviaList[i]);
66+
i++;
67+
}
68+
69+
return syntaxNode.WithLeadingTrivia(CreateTriviaList(list));
70+
}
71+
72+
private List<string> GetExistingHeader(SyntaxTriviaList triviaList)
73+
{
74+
var i = 0;
75+
MovePastBlankLines(triviaList, ref i);
76+
77+
var headerList = new List<string>();
78+
while (i < triviaList.Count && IsLineComment(triviaList[i]))
79+
{
80+
headerList.Add(GetCommentText(triviaList[i].ToFullString()));
81+
i++;
82+
}
83+
84+
return headerList;
85+
}
86+
87+
/// <summary>
88+
/// Remove any copyright header that already exists.
89+
/// </summary>
90+
private SyntaxTriviaList RemoveExistingHeader(SyntaxTriviaList oldList)
91+
{
92+
var foundHeader = false;
93+
var i = 0;
94+
MovePastBlankLines(oldList, ref i);
95+
96+
while (i < oldList.Count && IsLineComment(oldList[i]))
97+
{
98+
if (oldList[i].ToFullString().IndexOf("copyright", StringComparison.OrdinalIgnoreCase) >= 0)
99+
{
100+
foundHeader = true;
101+
}
102+
i++;
103+
}
104+
105+
if (!foundHeader)
106+
{
107+
return oldList;
108+
}
109+
110+
MovePastBlankLines(oldList, ref i);
111+
return CreateTriviaList(oldList.Skip(i));
112+
}
113+
114+
private void MovePastBlankLines(SyntaxTriviaList list, ref int index)
115+
{
116+
while (index < list.Count && IsWhiteSpaceOrNewLine(list[index]))
117+
{
118+
index++;
119+
}
120+
}
121+
122+
protected abstract SyntaxTriviaList CreateTriviaList(IEnumerable<SyntaxTrivia> e);
123+
124+
protected abstract bool IsLineComment(SyntaxTrivia trivia);
125+
126+
protected abstract bool IsWhiteSpaceOrNewLine(SyntaxTrivia trivia);
127+
128+
protected abstract SyntaxTrivia CreateLineComment(string commentText);
129+
130+
protected abstract SyntaxTrivia CreateNewLine();
131+
}
132+
19133
private readonly Options _options;
134+
private ImmutableArray<string> _cachedHeader;
135+
private ImmutableArray<string> _cachedHeaderSource;
20136

21137
[ImportingConstructor]
22138
internal CopyrightHeaderRule(Options options)
23139
{
24140
_options = options;
25141
}
26142

27-
public override bool SupportsLanguage(string languageName)
28-
{
29-
return languageName == LanguageNames.CSharp || languageName == LanguageNames.VisualBasic;
30-
}
31-
32-
public override SyntaxNode ProcessCSharp(SyntaxNode syntaxNode)
143+
private ImmutableArray<string> GetHeader()
33144
{
34-
return (new CSharpRule(_options)).Process(syntaxNode);
35-
}
145+
if (_cachedHeaderSource != _options.CopyrightHeader)
146+
{
147+
_cachedHeaderSource = _options.CopyrightHeader;
148+
_cachedHeader = _options.CopyrightHeader.Select(GetCommentText).ToImmutableArray();
149+
}
36150

37-
public override SyntaxNode ProcessVisualBasic(SyntaxNode syntaxNode)
38-
{
39-
return (new VisualBasicRule(_options)).Process(syntaxNode);
151+
return _cachedHeader;
40152
}
41153

42154
private static string GetCommentText(string line)
@@ -53,5 +165,21 @@ private static string GetCommentText(string line)
53165

54166
return line;
55167
}
168+
169+
public override bool SupportsLanguage(string languageName)
170+
{
171+
return languageName == LanguageNames.CSharp || languageName == LanguageNames.VisualBasic;
172+
}
173+
174+
public override SyntaxNode ProcessCSharp(SyntaxNode syntaxNode)
175+
{
176+
return (new CSharpRule(GetHeader())).Process(syntaxNode);
177+
}
178+
179+
public override SyntaxNode ProcessVisualBasic(SyntaxNode syntaxNode)
180+
{
181+
return (new VisualBasicRule(GetHeader())).Process(syntaxNode);
182+
}
183+
56184
}
57185
}

src/Microsoft.DotNet.CodeFormatting/Rules/RuleOrder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace Microsoft.DotNet.CodeFormatting.Rules
1313
internal static class SyntaxRuleOrder
1414
{
1515
public const int HasNoCustomCopyrightHeaderFormattingRule = 1;
16-
public const int HasCopyrightHeaderFormattingRule = 2;
16+
public const int CopyrightHeaderRule = 2;
1717
public const int HasUsingsOutsideOfNamespaceFormattingRule = 3;
1818
public const int HasNewLineBeforeFirstUsingFormattingRule = 4;
1919
public const int HasNewLineBeforeFirstNamespaceFormattingRule = 5;

0 commit comments

Comments
 (0)