Skip to content

Commit ed5c63d

Browse files
authored
Merge pull request #2024 from sharwell/fix-2023
Implement SA1137 (ElementsShouldHaveTheSameIndentation)
2 parents 6d80369 + db7a4d4 commit ed5c63d

11 files changed

Lines changed: 2736 additions & 5 deletions

File tree

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
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+
// The warning was reported on a token because the line is not indented
74+
originalSpan = new TextSpan(diagnostic.Location.SourceSpan.Start, 0);
75+
}
76+
else
77+
{
78+
originalSpan = trivia.Span;
79+
}
80+
81+
textChange = new TextChange(originalSpan, replacement);
82+
return true;
83+
}
84+
85+
private class FixAll : DocumentBasedFixAllProvider
86+
{
87+
public static FixAllProvider Instance { get; } =
88+
new FixAll();
89+
90+
protected override string CodeActionTitle =>
91+
ReadabilityResources.IndentationCodeFix;
92+
93+
protected override async Task<SyntaxNode> FixAllInDocumentAsync(FixAllContext fixAllContext, Document document, ImmutableArray<Diagnostic> diagnostics)
94+
{
95+
if (diagnostics.IsEmpty)
96+
{
97+
return null;
98+
}
99+
100+
var syntaxRoot = await document.GetSyntaxRootAsync().ConfigureAwait(false);
101+
102+
List<TextChange> changes = new List<TextChange>();
103+
104+
foreach (var diagnostic in diagnostics)
105+
{
106+
TextChange textChange;
107+
if (TryGetTextChange(diagnostic, syntaxRoot, out textChange))
108+
{
109+
changes.Add(textChange);
110+
}
111+
}
112+
113+
changes.Sort((left, right) => left.Span.Start.CompareTo(right.Span.Start));
114+
115+
var text = await document.GetTextAsync().ConfigureAwait(false);
116+
return await document.WithText(text.WithChanges(changes)).GetSyntaxRootAsync(fixAllContext.CancellationToken).ConfigureAwait(false);
117+
}
118+
}
119+
}
120+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@
114114
<Compile Include="OrderingRules\UsingCodeFixProvider.TreeTextSpan.cs" />
115115
<Compile Include="OrderingRules\UsingCodeFixProvider.UsingsSorter.cs" />
116116
<Compile Include="Properties\AssemblyInfo.cs" />
117+
<Compile Include="ReadabilityRules\IndentationCodeFixProvider.cs" />
117118
<Compile Include="ReadabilityRules\RemoveRegionCodeFixProvider.cs" />
118119
<Compile Include="ReadabilityRules\RemoveRegionFixAllProvider.cs" />
119120
<Compile Include="ReadabilityRules\SA1100CodeFixProvider.cs" />

0 commit comments

Comments
 (0)