1 | using System.Collections.Generic;
|
---|
2 | using System.Collections.Immutable;
|
---|
3 | using System.Linq;
|
---|
4 | using System.Threading;
|
---|
5 | using System.Threading.Tasks;
|
---|
6 | using Microsoft.CodeAnalysis;
|
---|
7 | using Microsoft.CodeAnalysis.CodeActions;
|
---|
8 | using Microsoft.CodeAnalysis.CodeFixes;
|
---|
9 |
|
---|
10 | namespace PersistenceCodeFix {
|
---|
11 | public sealed class SequentialFixAllProvider : FixAllProvider {
|
---|
12 | private static readonly SyntaxAnnotation marker = new SyntaxAnnotation(nameof(SequentialFixAllProvider));
|
---|
13 | private const string title = "Apply this fix sequentially";
|
---|
14 |
|
---|
15 | private SequentialFixAllProvider() { }
|
---|
16 | public static readonly SequentialFixAllProvider Instance = new SequentialFixAllProvider();
|
---|
17 |
|
---|
18 | public override async Task<CodeAction> GetFixAsync(FixAllContext fixAllContext) {
|
---|
19 | var diagnosticsToFix = new List<Diagnostic>();
|
---|
20 |
|
---|
21 | switch (fixAllContext.Scope) {
|
---|
22 | case FixAllScope.Document: {
|
---|
23 | var diagnostics = await fixAllContext.GetDocumentDiagnosticsAsync(fixAllContext.Document);
|
---|
24 | diagnosticsToFix.AddRange(diagnostics);
|
---|
25 | }
|
---|
26 | break;
|
---|
27 | case FixAllScope.Project: {
|
---|
28 | var diagnostics = await fixAllContext.GetAllDiagnosticsAsync(fixAllContext.Project);
|
---|
29 | diagnosticsToFix.AddRange(diagnostics);
|
---|
30 | }
|
---|
31 | break;
|
---|
32 | case FixAllScope.Solution: {
|
---|
33 | foreach (var project in fixAllContext.Solution.Projects) {
|
---|
34 | var diagnostics = await fixAllContext.GetAllDiagnosticsAsync(project);
|
---|
35 | diagnosticsToFix.AddRange(diagnostics);
|
---|
36 | }
|
---|
37 | }
|
---|
38 | break;
|
---|
39 | }
|
---|
40 |
|
---|
41 | var solution = fixAllContext.Solution;
|
---|
42 | var codeFixProvider = (IDocumentCodeFixProvider)fixAllContext.CodeFixProvider;
|
---|
43 | return await Task.FromResult(CodeAction.Create(title, ct => GetFixedSolutionAsync(solution, diagnosticsToFix.ToImmutableArray(), codeFixProvider, ct)));
|
---|
44 | }
|
---|
45 |
|
---|
46 | private static async Task<Solution> GetFixedSolutionAsync(Solution solution,
|
---|
47 | ImmutableArray<Diagnostic> diagnosticsToFix,
|
---|
48 | IDocumentCodeFixProvider codeFixProvider,
|
---|
49 | CancellationToken cancellationToken) {
|
---|
50 | var documentsToFix = diagnosticsToFix.GroupBy(x => solution.GetDocument(x.Location.SourceTree));
|
---|
51 |
|
---|
52 | var tasks = new List<Task<Document>>();
|
---|
53 | foreach (var grouping in documentsToFix)
|
---|
54 | tasks.Add(GetFixedDocumentAsync(grouping.Key, grouping.ToImmutableArray(), codeFixProvider, cancellationToken));
|
---|
55 |
|
---|
56 | await Task.WhenAll(tasks);
|
---|
57 |
|
---|
58 | foreach (var newDocument in tasks.Select(x => x.Result)) {
|
---|
59 | var oldDocument = solution.GetDocument(newDocument.Id);
|
---|
60 | if (oldDocument == newDocument) continue;
|
---|
61 | solution = solution.WithDocumentSyntaxRoot(newDocument.Id, await newDocument.GetSyntaxRootAsync());
|
---|
62 | cancellationToken.ThrowIfCancellationRequested();
|
---|
63 | }
|
---|
64 |
|
---|
65 | return solution;
|
---|
66 | }
|
---|
67 |
|
---|
68 | private static async Task<Document> GetFixedDocumentAsync(Document document,
|
---|
69 | ImmutableArray<Diagnostic> diagnosticsToFix,
|
---|
70 | IDocumentCodeFixProvider codeFixProvider,
|
---|
71 | CancellationToken cancellationToken) {
|
---|
72 |
|
---|
73 | var root = await document.GetSyntaxRootAsync(cancellationToken);
|
---|
74 |
|
---|
75 | foreach (var diagnostic in diagnosticsToFix) {
|
---|
76 | var node = root.FindNode(diagnostic.Location.SourceSpan);
|
---|
77 | document = document.WithSyntaxRoot(root.ReplaceNode(node, node.WithAdditionalAnnotations(marker)));
|
---|
78 | root = await document.GetSyntaxRootAsync(cancellationToken);
|
---|
79 | cancellationToken.ThrowIfCancellationRequested();
|
---|
80 | }
|
---|
81 |
|
---|
82 | var annotatedNodes = root.GetAnnotatedNodes(marker);
|
---|
83 |
|
---|
84 | while (annotatedNodes.Any()) {
|
---|
85 | var node = annotatedNodes.First();
|
---|
86 | document = await codeFixProvider.FixDocumentAsync(document, node, cancellationToken);
|
---|
87 | cancellationToken.ThrowIfCancellationRequested();
|
---|
88 |
|
---|
89 | root = await document.GetSyntaxRootAsync(cancellationToken);
|
---|
90 | annotatedNodes = root.GetAnnotatedNodes(marker);
|
---|
91 | node = annotatedNodes.First();
|
---|
92 | document = document.WithSyntaxRoot(root.ReplaceNode(node, node.WithoutAnnotations(marker)));
|
---|
93 | root = await document.GetSyntaxRootAsync(cancellationToken);
|
---|
94 | annotatedNodes = root.GetAnnotatedNodes(marker);
|
---|
95 | cancellationToken.ThrowIfCancellationRequested();
|
---|
96 | }
|
---|
97 |
|
---|
98 | return document;
|
---|
99 | }
|
---|
100 |
|
---|
101 | }
|
---|
102 | }
|
---|