Skip to content

Commit 0cdf3ec

Browse files
committed
Add IndentationCodeFixProvider
Currently this code fix only corrects SA1137, but the implementation is general enough to support other types of indentation corrections in the future.
1 parent 0281a31 commit 0cdf3ec

4 files changed

Lines changed: 132 additions & 0 deletions

File tree

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
3+
4+
namespace StyleCop.Analyzers.ReadabilityRules
5+
{
6+
using System.Collections.Generic;
7+
using System.Collections.Immutable;
8+
using System.Composition;
9+
using System.Threading;
10+
using System.Threading.Tasks;
11+
using Helpers;
12+
using Microsoft.CodeAnalysis;
13+
using Microsoft.CodeAnalysis.CodeActions;
14+
using Microsoft.CodeAnalysis.CodeFixes;
15+
using Microsoft.CodeAnalysis.Text;
16+
17+
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(IndentationCodeFixProvider))]
18+
[Shared]
19+
internal class IndentationCodeFixProvider : CodeFixProvider
20+
{
21+
/// <inheritdoc/>
22+
public override ImmutableArray<string> FixableDiagnosticIds { get; } =
23+
ImmutableArray.Create(SA1137ElementsShouldHaveTheSameIndentation.DiagnosticId);
24+
25+
/// <inheritdoc/>
26+
public sealed override FixAllProvider GetFixAllProvider() =>
27+
FixAll.Instance;
28+
29+
/// <inheritdoc/>
30+
public override Task RegisterCodeFixesAsync(CodeFixContext context)
31+
{
32+
foreach (var diagnostic in context.Diagnostics)
33+
{
34+
context.RegisterCodeFix(
35+
CodeAction.Create(
36+
ReadabilityResources.IndentationCodeFix,
37+
cancellationToken => GetTransformedDocumentAsync(context.Document, diagnostic, cancellationToken),
38+
nameof(IndentationCodeFixProvider)),
39+
diagnostic);
40+
}
41+
42+
return SpecializedTasks.CompletedTask;
43+
}
44+
45+
private static async Task<Document> GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
46+
{
47+
var syntaxRoot = await document.GetSyntaxRootAsync().ConfigureAwait(false);
48+
49+
TextChange textChange;
50+
if (!TryGetTextChange(diagnostic, syntaxRoot, out textChange))
51+
{
52+
return document;
53+
}
54+
55+
var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
56+
return document.WithText(text.WithChanges(textChange));
57+
}
58+
59+
private static bool TryGetTextChange(Diagnostic diagnostic, SyntaxNode syntaxRoot, out TextChange textChange)
60+
{
61+
string replacement;
62+
if (!diagnostic.Properties.TryGetValue(SA1137ElementsShouldHaveTheSameIndentation.ExpectedIndentationKey, out replacement))
63+
{
64+
textChange = default(TextChange);
65+
return false;
66+
}
67+
68+
var trivia = syntaxRoot.FindTrivia(diagnostic.Location.SourceSpan.Start);
69+
70+
TextSpan originalSpan;
71+
if (trivia == default(SyntaxTrivia))
72+
{
73+
originalSpan = trivia.Span;
74+
}
75+
else
76+
{
77+
originalSpan = new TextSpan(diagnostic.Location.SourceSpan.Start, 0);
78+
}
79+
80+
textChange = new TextChange(originalSpan, replacement);
81+
return true;
82+
}
83+
84+
private class FixAll : DocumentBasedFixAllProvider
85+
{
86+
public static FixAllProvider Instance { get; } =
87+
new FixAll();
88+
89+
protected override string CodeActionTitle =>
90+
ReadabilityResources.IndentationCodeFix;
91+
92+
protected override async Task<SyntaxNode> FixAllInDocumentAsync(FixAllContext fixAllContext, Document document, ImmutableArray<Diagnostic> diagnostics)
93+
{
94+
if (diagnostics.IsEmpty)
95+
{
96+
return null;
97+
}
98+
99+
var syntaxRoot = await document.GetSyntaxRootAsync().ConfigureAwait(false);
100+
101+
List<TextChange> changes = new List<TextChange>();
102+
103+
foreach (var diagnostic in diagnostics)
104+
{
105+
TextChange textChange;
106+
if (TryGetTextChange(diagnostic, syntaxRoot, out textChange))
107+
{
108+
changes.Add(textChange);
109+
}
110+
}
111+
112+
changes.Sort((left, right) => left.Span.Start.CompareTo(right.Span.Start));
113+
114+
var text = await document.GetTextAsync().ConfigureAwait(false);
115+
return await document.WithText(text.WithChanges(changes)).GetSyntaxRootAsync(fixAllContext.CancellationToken).ConfigureAwait(false);
116+
}
117+
}
118+
}
119+
}

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/StyleCop.Analyzers.CodeFixes.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@
108108
<Compile Include="OrderingRules\SA1212SA1213CodeFixProvider.cs" />
109109
<Compile Include="OrderingRules\UsingCodeFixProvider.cs" />
110110
<Compile Include="Properties\AssemblyInfo.cs" />
111+
<Compile Include="ReadabilityRules\IndentationCodeFixProvider.cs" />
111112
<Compile Include="ReadabilityRules\RemoveRegionCodeFixProvider.cs" />
112113
<Compile Include="ReadabilityRules\RemoveRegionFixAllProvider.cs" />
113114
<Compile Include="ReadabilityRules\SA1100CodeFixProvider.cs" />

StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/ReadabilityResources.Designer.cs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/ReadabilityResources.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@
117117
<resheader name="writer">
118118
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
119119
</resheader>
120+
<data name="IndentationCodeFix" xml:space="preserve">
121+
<value>Fix indentation</value>
122+
</data>
120123
<data name="RemoveRegionCodeFix" xml:space="preserve">
121124
<value>Remove region</value>
122125
</data>

0 commit comments

Comments
 (0)