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

Commit 8b09def

Browse files
committed
Changed the format engine
The format engine now uses the new rules and rewriters.
1 parent 7544f54 commit 8b09def

File tree

3 files changed

+115
-103
lines changed

3 files changed

+115
-103
lines changed

src/Microsoft.DotNet.CodeFormatting/FormattingEngineImplementation.cs

Lines changed: 111 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -43,54 +43,40 @@ public FormattingEngineImplementation(
4343
_globalSemanticRules = globalSemanticRules.OrderBy(r => r.Metadata.Order).Select(r => r.Value);
4444
}
4545

46-
public Task<bool> FormatSolutionAsync(Solution solution, CancellationToken cancellationToken)
46+
public Task FormatSolutionAsync(Solution solution, CancellationToken cancellationToken)
4747
{
4848
var documentIds = solution.Projects.SelectMany(x => x.DocumentIds).ToList();
4949
return FormatAsync(solution.Workspace, documentIds, cancellationToken);
5050
}
5151

52-
public Task<bool> FormatProjectAsync(Project project, CancellationToken cancellationToken)
52+
public Task FormatProjectAsync(Project project, CancellationToken cancellationToken)
5353
{
5454
return FormatAsync(project.Solution.Workspace, project.DocumentIds, cancellationToken);
5555
}
5656

57-
private async Task<bool> FormatAsync(Workspace workspace, IReadOnlyList<DocumentId> documentIds, CancellationToken cancellationToken)
57+
private async Task FormatAsync(Workspace workspace, IReadOnlyList<DocumentId> documentIds, CancellationToken cancellationToken)
5858
{
59-
var solution = workspace.CurrentSolution;
60-
var hasChanges = false;
61-
var longRuleList = new List<Tuple<string, TimeSpan>>();
62-
63-
foreach (var id in documentIds)
59+
var originalSolution = workspace.CurrentSolution;
60+
var solution = originalSolution;
61+
solution = await RunSyntaxPass(solution, documentIds, cancellationToken);
62+
solution = await RunLocalSemanticPass(solution, documentIds, cancellationToken);
63+
solution = await RunGlobalSemanticPass(solution, documentIds, cancellationToken);
64+
65+
foreach (var projectChange in solution.GetChanges(originalSolution).GetProjectChanges())
6466
{
65-
var document = solution.GetDocument(id);
66-
var shouldBeProcessed = await ShouldBeProcessedAsync(document);
67-
if (!shouldBeProcessed)
68-
{
69-
continue;
70-
}
71-
72-
longRuleList.Clear();
73-
var watch = new Stopwatch();
74-
watch.Start();
75-
Console.Write("Processing document: " + document.Name);
76-
var newDocument = await RewriteDocumentAsync(document, longRuleList, cancellationToken);
77-
hasChanges |= newDocument != document;
78-
watch.Stop();
79-
Console.WriteLine(" {0} seconds", watch.Elapsed.TotalSeconds);
80-
foreach (var tuple in longRuleList)
67+
foreach (var documentId in projectChange.GetChangedDocuments())
8168
{
82-
Console.WriteLine("\t{0} {1} seconds", tuple.Item1, tuple.Item2.TotalSeconds);
69+
var document = solution.GetDocument(documentId);
70+
var sourceText = await document.GetTextAsync(cancellationToken);
71+
using (var file = File.Open(document.FilePath, FileMode.Truncate, FileAccess.Write))
72+
{
73+
using (var writer = new StreamWriter(file, sourceText.Encoding))
74+
{
75+
sourceText.Write(writer, cancellationToken);
76+
}
77+
}
8378
}
84-
85-
solution = newDocument.Project.Solution;
86-
}
87-
88-
if (workspace.TryApplyChanges(solution))
89-
{
90-
Console.WriteLine("Solution changes committed");
9179
}
92-
93-
return hasChanges;
9480
}
9581

9682
private async Task<bool> ShouldBeProcessedAsync(Document document)
@@ -105,31 +91,24 @@ private async Task<bool> ShouldBeProcessedAsync(Document document)
10591
return true;
10692
}
10793

108-
private async Task<Document> RewriteDocumentAsync(Document document, List<Tuple<string, TimeSpan>> longRuleList, CancellationToken cancellationToken)
94+
private async Task<SyntaxNode> GetSyntaxRootAndFilter(Document document, CancellationToken cancellationToken)
10995
{
110-
var docText = await document.GetTextAsync();
111-
var originalEncoding = docText.Encoding;
112-
var watch = new Stopwatch();
113-
foreach (var rule in _rules)
96+
if (!await ShouldBeProcessedAsync(document))
11497
{
115-
watch.Start();
116-
document = await rule.ProcessAsync(document, cancellationToken);
117-
watch.Stop();
118-
var timeSpan = watch.Elapsed;
119-
if (timeSpan.TotalSeconds > 1.0)
120-
{
121-
longRuleList.Add(Tuple.Create(rule.GetType().Name, timeSpan));
122-
}
123-
124-
watch.Reset();
98+
return null;
12599
}
126100

127-
return await ChangeEncoding(document, originalEncoding);
101+
return await document.GetSyntaxRootAsync(cancellationToken);
128102
}
129103

130-
private void StartDocument(Document document)
104+
private void StartDocument(Document document, int depth = 1)
131105
{
132-
Console.Write("\tProcessing {0}", document.Name);
106+
for (int i = 0; i < depth; i++)
107+
{
108+
Console.Write("\t");
109+
}
110+
111+
Console.Write("Processing {0}", document.Name);
133112
_watch.Restart();
134113
}
135114

@@ -138,91 +117,126 @@ private void EndDocument()
138117
_watch.Stop();
139118
if (_verbose && _watch.Elapsed.TotalSeconds > 1)
140119
{
141-
120+
Console.WriteLine(" {0} seconds", _watch.Elapsed.TotalSeconds);
142121
}
122+
123+
Console.WriteLine();
143124
}
144125

145-
private Task<Solution> FormatDocumentsSyntaxPass(Solution originalSolution, IReadOnlyList<DocumentId> documentIds, CancellationToken cancellationToken)
126+
/// <summary>
127+
/// Semantics is not involved in this pass at all. It is just a straight modification of the
128+
/// parse tree so there are no issues about ensuring the version of <see cref="SemanticModel"/> and
129+
/// the <see cref="SyntaxNode"/> line up. Hence we do this by iteraning every <see cref="Document"/>
130+
/// and processing all rules against them at once
131+
/// </summary>
132+
private async Task<Solution> RunSyntaxPass(Solution originalSolution, IReadOnlyList<DocumentId> documentIds, CancellationToken cancellationToken)
146133
{
147134
Console.WriteLine("Syntax Pass");
148135

149136
var currentSolution = originalSolution;
150137
foreach (var documentId in documentIds)
151138
{
152139
var document = originalSolution.GetDocument(documentId);
153-
154-
Console.Write("\tProcessing {0}", document.Name);
155-
156-
watch.Restart();
157-
var newRoot = await documentFunc(document);
158-
watch.Stop();
159-
160-
if (_verbose && watch.Elapsed.TotalSeconds > 1)
140+
var syntaxRoot = await GetSyntaxRootAndFilter(document, cancellationToken);
141+
if (syntaxRoot == null)
161142
{
162-
Console.Write(" {0} seconds", watch.Elapsed.TotalSeconds);
143+
continue;
163144
}
164-
Console.WriteLine();
165145

166-
if (newRoot != null)
146+
StartDocument(document);
147+
var newRoot = RunSyntaxPass(syntaxRoot);
148+
EndDocument();
149+
150+
if (newRoot != syntaxRoot)
167151
{
168-
currentSolution = currentSolution.WithDocumentSyntaxRoot(documentId, newRoot);
152+
currentSolution = currentSolution.WithDocumentSyntaxRoot(document.Id, newRoot);
169153
}
170154
}
171155

172156
return currentSolution;
173157
}
174158

175-
private async Task<Solution> FormatDocumentsLocalSemanticPass(Solution originalSolution, IReadOnlyList<DocumentId> documentIds, CancellationToken cancellationToken)
159+
private SyntaxNode RunSyntaxPass(SyntaxNode root)
176160
{
177-
Console.WriteLine("Local Semantic Pass");
178-
Func<Document, Task<SyntaxTree> documentFunc = async(documentFunc) =>
179-
{
180-
var syntaxRoot = await document.GetSyntaxRootAsync(cancellationToken);
181-
if (syntaxRoot == null)
182-
{
183-
return null;
184-
}
185-
186-
return FormatLocalSemantic(document, syntaxRoot);
187-
};
161+
foreach (var rule in _syntaxRules)
162+
{
163+
root = rule.Process(root);
164+
}
188165

189-
return FormatDocumentsCore(originalSolution, documentIds, documentFunc, cancellationToken);
166+
return root;
190167
}
191168

192-
private async Task<Solution> FormatDocumentsCore(
193-
Solution originalSolution,
194-
IReadOnlyList<DocumentId> documentIds,
195-
Func<Document, Task<SyntaxTree> documentFunc,
196-
CancellationToken cancellationToken)
169+
private async Task<Solution> RunLocalSemanticPass(Solution solution, IReadOnlyList<DocumentId> documentIds, CancellationToken cancellationToken)
197170
{
171+
Console.WriteLine("Local Semantic Pass");
172+
foreach (var localSemanticRule in _localSemanticRules)
173+
{
174+
solution = await RunLocalSemanticPass(solution, documentIds, localSemanticRule, cancellationToken);
175+
}
176+
177+
return solution;
198178
}
199179

200-
private Task<SyntaxTree> FormatSyntaxTree(Document document, SyntaxNode syntaxRoot)
180+
private async Task<Solution> RunLocalSemanticPass(Solution originalSolution, IReadOnlyList<DocumentId> documentIds, ILocalSemanticFormattingRule localSemanticRule, CancellationToken cancellationToken)
201181
{
202-
foreach (var syntaxRule in _syntaxRules)
182+
Console.WriteLine("\t{0}", localSemanticRule.GetType().Name);
183+
var currentSolution = originalSolution;
184+
foreach (var documentId in documentIds)
203185
{
204-
syntaxRoot = syntaxRule.Process(syntaxRoot);
186+
var document = originalSolution.GetDocument(documentId);
187+
var syntaxRoot = await GetSyntaxRootAndFilter(document, cancellationToken);
188+
if (syntaxRoot == null)
189+
{
190+
continue;
191+
}
192+
193+
StartDocument(document, depth: 2);
194+
var newRoot = await localSemanticRule.ProcessAsync(document, syntaxRoot, cancellationToken);
195+
EndDocument();
196+
197+
if (syntaxRoot != newRoot)
198+
{
199+
currentSolution = currentSolution.WithDocumentSyntaxRoot(documentId, newRoot);
200+
}
205201
}
206202

207-
return Task.FromResult(root);
203+
return currentSolution;
208204
}
209205

210-
private async Task<SyntaxTree> FormatLocalSemantic(Document originalDocument, SyntaxNode originalSyntaxRoot)
206+
private async Task<Solution> RunGlobalSemanticPass(Solution solution, IReadOnlyList<DocumentId> documentIds, CancellationToken cancellationToken)
211207
{
212-
var currentSyntaxRoot = originalSyntaxRoot;
213-
foreach (var localSemanticRule in _localSemanticRules)
208+
Console.WriteLine("Global Semantic Pass");
209+
foreach (var globalSemanticRule in _globalSemanticRules)
214210
{
215-
currentSyntaxRoot = await localSemanticRule.ProcessAsync(originalDocument, originalSyntaxRoot, currentSyntaxRoot)
211+
solution = await RunGlobalSemanticPass(solution, documentIds, globalSemanticRule, cancellationToken);
216212
}
217213

218-
return currentSyntaxRoot;
214+
return solution;
219215
}
220216

221-
private async Task<Document> ChangeEncoding(Document document, Encoding encoding)
217+
private async Task<Solution> RunGlobalSemanticPass(Solution solution, IReadOnlyList<DocumentId> documentIds, IGlobalSemanticFormattingRule globalSemanticRule, CancellationToken cancellationToken)
222218
{
223-
var text = await document.GetTextAsync();
224-
var newText = SourceText.From(text.ToString(), encoding);
225-
return document.WithText(newText);
219+
Console.WriteLine("\t{0}", globalSemanticRule.GetType().Name);
220+
foreach (var documentId in documentIds)
221+
{
222+
var document = solution.GetDocument(documentId);
223+
var syntaxRoot = await GetSyntaxRootAndFilter(document, cancellationToken);
224+
if (syntaxRoot == null)
225+
{
226+
continue;
227+
}
228+
229+
StartDocument(document, depth: 2);
230+
var newRoot = await globalSemanticRule.ProcessAsync(document, syntaxRoot, cancellationToken);
231+
EndDocument();
232+
233+
if (syntaxRoot != newRoot)
234+
{
235+
solution = solution.WithDocumentSyntaxRoot(documentId, newRoot);
236+
}
237+
}
238+
239+
return solution;
226240
}
227241
}
228242
}

src/Microsoft.DotNet.CodeFormatting/IFormattingEngine.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace Microsoft.DotNet.CodeFormatting
1111
{
1212
public interface IFormattingEngine
1313
{
14-
Task<bool> FormatSolutionAsync(Solution solution, CancellationToken cancellationToken);
15-
Task<bool> FormatProjectAsync(Project porject, CancellationToken cancellationToken);
14+
Task FormatSolutionAsync(Solution solution, CancellationToken cancellationToken);
15+
Task FormatProjectAsync(Project porject, CancellationToken cancellationToken);
1616
}
1717
}

src/Microsoft.DotNet.CodeFormatting/IFormattingRule.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,14 @@ internal interface ISyntaxFormattingRule
2929
/// </summary>
3030
internal interface ILocalSemanticFormattingRule
3131
{
32-
Task Record(Document document, SyntaxRoot )
33-
34-
Task<SyntaxNode> ProcessAsync(Document originalDocument, SyntaxNode originalSyntaxRoot, SyntaxNode currentSyntaxRoot, CancellationToken cancellationToken);
32+
Task<SyntaxNode> ProcessAsync(Document document, SyntaxNode syntaxRoot, CancellationToken cancellationToken);
3533
}
3634

3735
/// <summary>
3836
/// Rules which can affect more than the local document
3937
/// </summary>
4038
internal interface IGlobalSemanticFormattingRule
4139
{
42-
Task<Solution> ProcessAsync(Solution solution, CancellationToken cancellationToken);
40+
Task<SyntaxNode> ProcessAsync(Document document, SyntaxNode syntaxRoot, CancellationToken cancellationToken);
4341
}
4442
}

0 commit comments

Comments
 (0)