Skip to content

Commit 8a88bc9

Browse files
committed
Add test for formatting code fix relying on .editorconfig
1 parent 45877d3 commit 8a88bc9

File tree

7 files changed

+140
-17
lines changed

7 files changed

+140
-17
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
2+
3+
using System.Composition;
4+
using Microsoft.CodeAnalysis.CodeFixes;
5+
using Microsoft.CodeAnalysis.Options;
6+
using Microsoft.VisualStudio.CodingConventions;
7+
8+
namespace Microsoft.CodeAnalysis.CodeStyle
9+
{
10+
[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.FixFormatting)]
11+
[Shared]
12+
internal class CSharpFormattingCodeFixProvider : AbstractFormattingCodeFixProvider
13+
{
14+
private readonly EditorConfigOptionsApplier _editorConfigOptionsApplier = new EditorConfigOptionsApplier();
15+
16+
protected override OptionSet ApplyFormattingOptions(OptionSet optionSet, ICodingConventionContext codingConventionContext)
17+
{
18+
return _editorConfigOptionsApplier.ApplyConventions(optionSet, codingConventionContext.CurrentConventions, LanguageNames.CSharp);
19+
}
20+
}
21+
}

src/CodeStyle/CSharp/Tests/FormattingAnalyzerTests.cs

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
22

3+
using System.IO;
34
using System.Threading.Tasks;
45
using Microsoft.CodeAnalysis.CSharp.Testing;
56
using Microsoft.CodeAnalysis.Testing.Verifiers;
67
using Xunit;
78

89
namespace Microsoft.CodeAnalysis.CodeStyle
910
{
10-
using Verify = CSharpCodeFixVerifier<CSharpFormattingAnalyzer, FormattingCodeFixProvider, XUnitVerifier>;
11+
using Verify = CSharpCodeFixVerifier<CSharpFormattingAnalyzer, CSharpFormattingCodeFixProvider, XUnitVerifier>;
1112

1213
public class FormattingAnalyzerTests
1314
{
@@ -184,7 +185,7 @@ class MyClass
184185
}
185186
";
186187

187-
await new CSharpCodeFixTest<CSharpFormattingAnalyzer, FormattingCodeFixProvider, XUnitVerifier>
188+
await new CSharpCodeFixTest<CSharpFormattingAnalyzer, CSharpFormattingCodeFixProvider, XUnitVerifier>
188189
{
189190
TestCode = testCode,
190191
FixedCode = fixedCode,
@@ -194,5 +195,50 @@ class MyClass
194195
NumberOfIncrementalIterations = 2,
195196
}.RunAsync();
196197
}
198+
199+
[Fact]
200+
public async Task TestEditorConfigUsed()
201+
{
202+
var testCode = @"
203+
class MyClass {
204+
void MyMethod()[| |]{
205+
}
206+
}
207+
";
208+
var fixedCode = @"
209+
class MyClass {
210+
void MyMethod()
211+
{
212+
}
213+
}
214+
";
215+
var editorConfig = @"
216+
root = true
217+
218+
[*.cs]
219+
csharp_new_line_before_open_brace = methods
220+
";
221+
222+
var testDirectoryName = Path.GetRandomFileName();
223+
Directory.CreateDirectory(testDirectoryName);
224+
try
225+
{
226+
File.WriteAllText(Path.Combine(testDirectoryName, ".editorconfig"), editorConfig);
227+
228+
// The contents of this file are ignored, but the coding conventions library checks for existence before
229+
// .editorconfig is used.
230+
File.WriteAllText(Path.Combine(testDirectoryName, "Test0.cs"), string.Empty);
231+
232+
await new CSharpCodeFixTest<CSharpFormattingAnalyzer, CSharpFormattingCodeFixProvider, XUnitVerifier>
233+
{
234+
TestState = { Sources = { (Path.GetFullPath(Path.Combine(testDirectoryName, "Test0.cs")), testCode) } },
235+
FixedState = { Sources = { (Path.GetFullPath(Path.Combine(testDirectoryName, "Test0.cs")), fixedCode) } },
236+
}.RunAsync();
237+
}
238+
finally
239+
{
240+
Directory.Delete(testDirectoryName, true);
241+
}
242+
}
197243
}
198244
}

src/CodeStyle/Core/CodeFixes/FormattingCodeFixHelper.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33
using System.Threading;
44
using System.Threading.Tasks;
55
using Microsoft.CodeAnalysis.Formatting;
6+
using Microsoft.CodeAnalysis.Options;
67
using Microsoft.CodeAnalysis.Text;
78

89
namespace Microsoft.CodeAnalysis
910
{
1011
internal static class FormattingCodeFixHelper
1112
{
12-
internal static async Task<Document> FixOneAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
13+
internal static async Task<Document> FixOneAsync(Document document, OptionSet options, Diagnostic diagnostic, CancellationToken cancellationToken)
1314
{
1415
var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
1516

@@ -21,7 +22,6 @@ internal static async Task<Document> FixOneAsync(Document document, Diagnostic d
2122
text.Lines[diagnosticLinePositionSpan.Start.Line].Start,
2223
text.Lines[diagnosticLinePositionSpan.End.Line].End);
2324

24-
var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);
2525
return await Formatter.FormatAsync(document, spanToFormat, options, cancellationToken).ConfigureAwait(false);
2626
}
2727
}

src/CodeStyle/Core/CodeFixes/FormattingCodeFixProvider.cs

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,86 @@
11
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
22

33
using System.Collections.Immutable;
4-
using System.Composition;
4+
using System.IO;
55
using System.Threading;
66
using System.Threading.Tasks;
77
using Microsoft.CodeAnalysis.CodeActions;
88
using Microsoft.CodeAnalysis.CodeFixes;
99
using Microsoft.CodeAnalysis.Diagnostics;
1010
using Microsoft.CodeAnalysis.Formatting;
11+
using Microsoft.CodeAnalysis.Options;
12+
using Microsoft.VisualStudio.CodingConventions;
1113

1214
namespace Microsoft.CodeAnalysis.CodeStyle
1315
{
14-
[ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic, Name = PredefinedCodeFixProviderNames.FixFormatting)]
15-
[Shared]
16-
internal class FormattingCodeFixProvider : CodeFixProvider
16+
internal abstract class AbstractFormattingCodeFixProvider : CodeFixProvider
1717
{
18-
public override ImmutableArray<string> FixableDiagnosticIds
18+
public sealed override ImmutableArray<string> FixableDiagnosticIds
1919
=> ImmutableArray.Create(IDEDiagnosticIds.FormattingDiagnosticId);
2020

21-
public override FixAllProvider GetFixAllProvider()
21+
public sealed override FixAllProvider GetFixAllProvider()
2222
{
23-
return new FixAll();
23+
return new FixAll(this);
2424
}
2525

26-
public override Task RegisterCodeFixesAsync(CodeFixContext context)
26+
public sealed override Task RegisterCodeFixesAsync(CodeFixContext context)
2727
{
2828
foreach (var diagnostic in context.Diagnostics)
2929
{
3030
context.RegisterCodeFix(
3131
CodeAction.Create(
3232
CodeStyleResources.Fix_formatting,
33-
c => FormattingCodeFixHelper.FixOneAsync(context.Document, diagnostic, c),
34-
nameof(FormattingCodeFixProvider)),
33+
c => FixOneAsync(context, diagnostic, c),
34+
nameof(AbstractFormattingCodeFixProvider)),
3535
diagnostic);
3636
}
3737

3838
return Task.CompletedTask;
3939
}
4040

41+
protected abstract OptionSet ApplyFormattingOptions(OptionSet optionSet, ICodingConventionContext codingConventionContext);
42+
43+
private async Task<Document> FixOneAsync(CodeFixContext context, Diagnostic diagnostic, CancellationToken cancellationToken)
44+
{
45+
var options = await GetOptionsAsync(context.Document, cancellationToken).ConfigureAwait(false);
46+
return await FormattingCodeFixHelper.FixOneAsync(context.Document, options, diagnostic, cancellationToken).ConfigureAwait(false);
47+
}
48+
49+
private async Task<OptionSet> GetOptionsAsync(Document document, CancellationToken cancellationToken)
50+
{
51+
OptionSet options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);
52+
53+
// The in-IDE workspace supports .editorconfig without special handling. However, the AdhocWorkspace used
54+
// in testing requires manual handling of .editorconfig.
55+
if (document.Project.Solution.Workspace is AdhocWorkspace && File.Exists(document.FilePath ?? document.Name))
56+
{
57+
var codingConventionsManager = CodingConventionsManagerFactory.CreateCodingConventionsManager();
58+
var codingConventionContext = await codingConventionsManager.GetConventionContextAsync(document.FilePath ?? document.Name, cancellationToken).ConfigureAwait(false);
59+
options = ApplyFormattingOptions(options, codingConventionContext);
60+
}
61+
62+
return options;
63+
}
64+
4165
/// <summary>
4266
/// Provide an optimized Fix All implementation that runs
4367
/// <see cref="Formatter.FormatAsync(Document, Options.OptionSet, CancellationToken)"/> on the document(s)
4468
/// included in the Fix All scope.
4569
/// </summary>
4670
private class FixAll : DocumentBasedFixAllProvider
4771
{
72+
private readonly AbstractFormattingCodeFixProvider _formattingCodeFixProvider;
73+
74+
public FixAll(AbstractFormattingCodeFixProvider formattingCodeFixProvider)
75+
{
76+
_formattingCodeFixProvider = formattingCodeFixProvider;
77+
}
78+
4879
protected override string CodeActionTitle => CodeStyleResources.Fix_formatting;
4980

5081
protected override async Task<SyntaxNode> FixAllInDocumentAsync(FixAllContext fixAllContext, Document document, ImmutableArray<Diagnostic> diagnostics)
5182
{
52-
var options = await document.GetOptionsAsync(fixAllContext.CancellationToken).ConfigureAwait(false);
83+
var options = await _formattingCodeFixProvider.GetOptionsAsync(document, fixAllContext.CancellationToken).ConfigureAwait(false);
5384
var updatedDocument = await Formatter.FormatAsync(document, options, fixAllContext.CancellationToken).ConfigureAwait(false);
5485
return await updatedDocument.GetSyntaxRootAsync(fixAllContext.CancellationToken).ConfigureAwait(false);
5586
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
2+
3+
Imports System.Composition
4+
Imports Microsoft.CodeAnalysis.CodeFixes
5+
Imports Microsoft.CodeAnalysis.Options
6+
Imports Microsoft.VisualStudio.CodingConventions
7+
8+
Namespace Microsoft.CodeAnalysis.CodeStyle
9+
<ExportCodeFixProvider(LanguageNames.VisualBasic, Name:=PredefinedCodeFixProviderNames.FixFormatting)>
10+
<[Shared]>
11+
Friend Class VisualBasicFormattingCodeFixProvider
12+
Inherits AbstractFormattingCodeFixProvider
13+
14+
Protected Overrides Function ApplyFormattingOptions(optionSet As OptionSet, codingConventionContext As ICodingConventionContext) As OptionSet
15+
Return optionSet
16+
End Function
17+
End Class
18+
End Namespace
19+

src/CodeStyle/VisualBasic/Tests/FormattingAnalyzerTests.vb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Imports Xunit
44
Imports VerifyVB = Microsoft.CodeAnalysis.VisualBasic.Testing.VisualBasicCodeFixVerifier(
55
Of Microsoft.CodeAnalysis.CodeStyle.VisualBasicFormattingAnalyzer,
6-
Microsoft.CodeAnalysis.CodeStyle.FormattingCodeFixProvider,
6+
Microsoft.CodeAnalysis.CodeStyle.VisualBasicFormattingCodeFixProvider,
77
Microsoft.CodeAnalysis.Testing.Verifiers.XUnitVerifier)
88

99
Namespace Microsoft.CodeAnalysis.CodeStyle

src/Features/Core/Portable/Formatting/FormattingCodeFixProvider.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,19 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
2424
foreach (var diagnostic in context.Diagnostics)
2525
{
2626
context.RegisterCodeFix(
27-
new MyCodeAction(c => FormattingCodeFixHelper.FixOneAsync(context.Document, diagnostic, c)),
27+
new MyCodeAction(c => FixOneAsync(context, diagnostic, c)),
2828
diagnostic);
2929
}
3030

3131
return Task.CompletedTask;
3232
}
3333

34+
private static async Task<Document> FixOneAsync(CodeFixContext context, Diagnostic diagnostic, CancellationToken cancellationToken)
35+
{
36+
var options = await context.Document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);
37+
return await FormattingCodeFixHelper.FixOneAsync(context.Document, options, diagnostic, cancellationToken).ConfigureAwait(false);
38+
}
39+
3440
protected override async Task FixAllAsync(Document document, ImmutableArray<Diagnostic> diagnostics, SyntaxEditor editor, CancellationToken cancellationToken)
3541
{
3642
var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);

0 commit comments

Comments
 (0)