-
Notifications
You must be signed in to change notification settings - Fork 391
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Formatter: Recycle parsed AST and tokens in between rule invocations when no correction was applied to improve performance #1462
Changes from all commits
8cbd179
5932cfa
12a6f55
e6ae0ff
0d01812
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1478,8 +1478,7 @@ public IEnumerable<DiagnosticRecord> AnalyzeAndFixPath(string path, Func<string, | |
if (shouldProcess(scriptFilePath, $"Analyzing and fixing file {scriptFilePath}")) | ||
{ | ||
var fileEncoding = GetFileEncoding(scriptFilePath); | ||
bool fixesWereApplied; | ||
var scriptFileContentWithFixes = Fix(File.ReadAllText(scriptFilePath, fileEncoding), out fixesWereApplied); | ||
var scriptFileContentWithFixes = Fix(File.ReadAllText(scriptFilePath, fileEncoding), out bool fixesWereApplied); | ||
if (fixesWereApplied) | ||
{ | ||
File.WriteAllText(scriptFilePath, scriptFileContentWithFixes, fileEncoding); | ||
|
@@ -1497,12 +1496,15 @@ public IEnumerable<DiagnosticRecord> AnalyzeAndFixPath(string path, Func<string, | |
/// <summary> | ||
/// Analyzes a script definition in the form of a string input | ||
/// </summary> | ||
/// <param name="scriptDefinition">The script to be analyzed</param> | ||
/// <param name="scriptDefinition">The script to be analyzed.</param> | ||
/// <param name="scriptAst">Parsed AST of <paramref name="scriptDefinition"/>.</param> | ||
/// <param name="scriptTokens">Parsed tokens of <paramref name="scriptDefinition"/.></param> | ||
/// <param name="skipVariableAnalysis">Whether variable analysis can be skipped (applicable if rules do not use variable analysis APIs).</param> | ||
/// <returns></returns> | ||
public IEnumerable<DiagnosticRecord> AnalyzeScriptDefinition(string scriptDefinition, bool skipVariableAnalysis = false) | ||
public IEnumerable<DiagnosticRecord> AnalyzeScriptDefinition(string scriptDefinition, out ScriptBlockAst scriptAst, out Token[] scriptTokens, bool skipVariableAnalysis = false) | ||
{ | ||
ScriptBlockAst scriptAst = null; | ||
Token[] scriptTokens = null; | ||
scriptAst = null; | ||
scriptTokens = null; | ||
ParseError[] errors = null; | ||
|
||
this.outputWriter.WriteVerbose(string.Format(CultureInfo.CurrentCulture, Strings.VerboseScriptDefinitionMessage)); | ||
|
@@ -1546,7 +1548,7 @@ public IEnumerable<DiagnosticRecord> AnalyzeScriptDefinition(string scriptDefini | |
/// Fix the violations in the given script text. | ||
/// </summary> | ||
/// <param name="scriptDefinition">The script text to be fixed.</param> | ||
/// <param name="updatedRange">Whether any warnings were fixed.</param> | ||
/// <param name="fixesWereApplied">Whether any warnings were fixed.</param> | ||
/// <returns>The fixed script text.</returns> | ||
public string Fix(string scriptDefinition, out bool fixesWereApplied) | ||
{ | ||
|
@@ -1555,20 +1557,24 @@ public string Fix(string scriptDefinition, out bool fixesWereApplied) | |
throw new ArgumentNullException(nameof(scriptDefinition)); | ||
} | ||
|
||
Range updatedRange; | ||
return Fix(new EditableText(scriptDefinition), null, out updatedRange, out fixesWereApplied).ToString(); | ||
ScriptBlockAst scriptAst = null; | ||
Token[] scriptTokens = null; | ||
return Fix(new EditableText(scriptDefinition), range: null, skipParsing: false, out _, out fixesWereApplied, ref scriptAst, ref scriptTokens).ToString(); | ||
} | ||
|
||
/// <summary> | ||
/// Fix the violations in the given script text. | ||
/// </summary> | ||
/// <param name="text">An object of type `EditableText` that encapsulates the script text to be fixed.</param> | ||
/// <param name="text">An object of type <see cref="EditableText"/> that encapsulates the script text to be fixed.</param> | ||
/// <param name="range">The range in which the fixes are allowed.</param> | ||
/// <param name="skipParsing">Whether to use the <paramref name="scriptAst"/> and <paramref name="scriptTokens"/> parameters instead of parsing the <paramref name="text"/> parameter.</param> | ||
/// <param name="updatedRange">The updated range after the fixes have been applied.</param> | ||
/// <param name="updatedRange">Whether any warnings were fixed.</param> | ||
/// <param name="fixesWereApplied">Whether any warnings were fixed.</param> | ||
/// <param name="scriptAst">Optionally pre-parsed AST.</param> | ||
/// <param name="scriptTokens">Optionally pre-parsed tokens.</param> | ||
/// <param name="skipVariableAnalysis">Whether to skip variable analysis.</param> | ||
/// <returns>The same instance of `EditableText` that was passed to the method, but the instance encapsulates the fixed script text. This helps in chaining the Fix method.</returns> | ||
public EditableText Fix(EditableText text, Range range, out Range updatedRange, out bool fixesWereApplied, bool skipVariableAnalysis = false) | ||
public EditableText Fix(EditableText text, Range range, bool skipParsing, out Range updatedRange, out bool fixesWereApplied, ref ScriptBlockAst scriptAst, ref Token[] scriptTokens, bool skipVariableAnalysis = false) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Technically this is a breaking change as well but I think this class shouldn't have been public but rather internal in the first place. Again, we could still provide a wrapping layer for legacy purposed but it'd just leave the change as-is. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is acceptable; we've proceeded on the idea that the cmdlets are the real API so far and I think we can continue with that in the 1.x timeframe |
||
{ | ||
if (text == null) | ||
{ | ||
|
@@ -1590,7 +1596,15 @@ public EditableText Fix(EditableText text, Range range, out Range updatedRange, | |
var previousUnusedCorrections = 0; | ||
do | ||
{ | ||
var records = AnalyzeScriptDefinition(text.ToString(), skipVariableAnalysis); | ||
IEnumerable<DiagnosticRecord> records; | ||
if (skipParsing && previousUnusedCorrections == 0) | ||
{ | ||
records = AnalyzeSyntaxTree(scriptAst, scriptTokens, String.Empty, skipVariableAnalysis); | ||
} | ||
else | ||
{ | ||
records = AnalyzeScriptDefinition(text.ToString(), out scriptAst, out scriptTokens, skipVariableAnalysis); | ||
} | ||
var corrections = records | ||
.Select(r => r.SuggestedCorrections) | ||
.Where(sc => sc != null && sc.Any()) | ||
|
@@ -1684,22 +1698,21 @@ private static Range SnapToEdges(EditableText text, Range range) | |
} | ||
|
||
private static IEnumerable<CorrectionExtent> GetCorrectionExtentsForFix( | ||
IEnumerable<CorrectionExtent> correctionExtents) | ||
List<CorrectionExtent> correctionExtents) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If one goes up in the call tree of the |
||
{ | ||
var ceList = correctionExtents.ToList(); | ||
ceList.Sort((x, y) => | ||
correctionExtents.Sort((x, y) => | ||
{ | ||
return x.StartLineNumber < y.StartLineNumber ? | ||
1 : | ||
(x.StartLineNumber == y.StartLineNumber ? 0 : -1); | ||
}); | ||
|
||
return ceList.GroupBy(ce => ce.StartLineNumber).Select(g => g.First()); | ||
return correctionExtents.GroupBy(ce => ce.StartLineNumber).Select(g => g.First()); | ||
} | ||
|
||
private static EditableText Fix( | ||
EditableText text, | ||
IEnumerable<CorrectionExtent> correctionExtents, | ||
List<CorrectionExtent> correctionExtents, | ||
out int unusedCorrections) | ||
{ | ||
var correctionsToUse = GetCorrectionExtentsForFix(correctionExtents); | ||
|
@@ -1710,7 +1723,7 @@ private static EditableText Fix( | |
text.ApplyEdit(correction); | ||
} | ||
|
||
unusedCorrections = correctionExtents.Count() - count; | ||
unusedCorrections = correctionExtents.Count - count; | ||
return text; | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically speaking this is a breaking change but I don't think anyone depends on it (at least publicly on GitHub) and even if they did it should be easy for them to adopt. If it is of concerns, an alternative to not have a breaking change is to provide a wrapping layer like this