Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Engine/Formatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public static string Format<TCmdlet>(
ScriptBlockAst scriptAst = null;
Token[] scriptTokens = null;
bool skipParsing = false;
bool isRangeSpecified = range != null;
foreach (var rule in ruleOrder)
{
if (!settings.RuleArguments.ContainsKey(rule))
Expand All @@ -68,6 +69,10 @@ public static string Format<TCmdlet>(
text = ScriptAnalyzer.Instance.Fix(text, range, skipParsing, out Range updatedRange, out bool fixesWereApplied, ref scriptAst, ref scriptTokens, skipVariableAnalysis: true);
skipParsing = !fixesWereApplied;
range = updatedRange;
if (isRangeSpecified && range == null)
{
break;
}
}

return text.ToString();
Expand Down
50 changes: 36 additions & 14 deletions Engine/ScriptAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1556,7 +1556,6 @@ public EditableText Fix(EditableText text, Range range, bool skipParsing, out Ra
}

range = isRangeNull ? null : SnapToEdges(text, range);
var previousLineCount = text.LineCount;
var previousUnusedCorrections = 0;
do
{
Expand All @@ -1578,7 +1577,7 @@ public EditableText Fix(EditableText text, Range range, bool skipParsing, out Ra

this.outputWriter.WriteVerbose($"Found {corrections.Count} violations.");
int unusedCorrections;
Fix(text, corrections, out unusedCorrections);
Fix(text, corrections, out unusedCorrections, ref range);
var numberOfFixedViolatons = corrections.Count - unusedCorrections;
fixesWereApplied = numberOfFixedViolatons > 0;
this.outputWriter.WriteVerbose($"Fixed {numberOfFixedViolatons} violations.");
Expand All @@ -1595,17 +1594,7 @@ public EditableText Fix(EditableText text, Range range, bool skipParsing, out Ra
}

previousUnusedCorrections = unusedCorrections;
var lineCount = text.LineCount;
if (!isRangeNull && lineCount != previousLineCount)
{
range = new Range(
range.Start,
range.End.Shift(lineCount - previousLineCount, 0));
range = SnapToEdges(text, range);
}

previousLineCount = lineCount;
} while (previousUnusedCorrections > 0);
} while (previousUnusedCorrections > 0 && (isRangeNull || range != null));

updatedRange = range;
return text;
Expand Down Expand Up @@ -1728,14 +1717,47 @@ private static IEnumerable<CorrectionExtent> GetCorrectionExtentsForFix(
private static EditableText Fix(
EditableText text,
List<CorrectionExtent> correctionExtents,
out int unusedCorrections)
out int unusedCorrections,
ref Range range)
{
var correctionsToUse = GetCorrectionExtentsForFix(correctionExtents);
var count = 0;
foreach (var correction in correctionsToUse)
{
count++;
text.ApplyEdit(correction);
if (range != null)
{
var lineDelta = correction.Lines.Length - 1
- (correction.EndLineNumber - correction.StartLineNumber);
var columnDelta = 0;
if (correction.EndLineNumber == range.End.Line)
{
var replacementEndColumn = correction.Lines.Length == 0
? correction.StartColumnNumber
: correction.Lines.Last().TrimEnd('\r').Length
+ (correction.Lines.Length == 1 ? correction.StartColumnNumber : 1);
columnDelta = replacementEndColumn - correction.EndColumnNumber;
}

var endLine = range.End.Line + lineDelta;
if (correction.Lines.Length == 0 && endLine < range.Start.Line)
{
range = null;
}
else if (correction.Lines.Length == 0
&& lineDelta < 0
&& correction.EndLineNumber == range.End.Line)
{
range = new Range(
range.Start,
new Position(endLine, text.Lines[endLine - 1].Length + 1));
}
else
{
range = new Range(range.Start, range.End.Shift(lineDelta, columnDelta));
Comment thread
bergmeister marked this conversation as resolved.
Resolved
}
}
}

unusedCorrections = correctionExtents.Count - count;
Expand Down
95 changes: 95 additions & 0 deletions Tests/Engine/InvokeFormatter.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,101 @@ function foo {

Invoke-Formatter -ScriptDefinition $def -Range @(3, 1, 4, 1) | Should -Be $expected
}

It "Should track the range when an edit deletes its last line" {
$text = New-Object -TypeName "Microsoft.Windows.PowerShell.ScriptAnalyzer.EditableText" -ArgumentList "first`n`nlast"
$correctionType = "Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.CorrectionExtent" -as [type]
$correction = [Activator]::CreateInstance(
$correctionType,
[object[]]@(2, 2, 1, 1, [string[]]@(), $null, $null))
$correctionsType = [System.Collections.Generic.List``1].MakeGenericType($correctionType)
$corrections = [Activator]::CreateInstance($correctionsType)
$corrections.Add($correction)
$range = New-Object -TypeName "Microsoft.Windows.PowerShell.ScriptAnalyzer.Range" -ArgumentList 1, 1, 2, 1
$arguments = [object[]]@(
$text.PSObject.BaseObject,
$corrections.PSObject.BaseObject,
0,
$range.PSObject.BaseObject)
$fixMethod = ("Microsoft.Windows.PowerShell.ScriptAnalyzer.ScriptAnalyzer" -as [type]).GetMethod(
'Fix',
[Reflection.BindingFlags]'NonPublic, Static')

$fixMethod.Invoke($null, $arguments) | Out-Null

$text.ToString() | Should -Be "first`nlast"
$arguments[3].End.Line | Should -Be 1
$arguments[3].End.Column | Should -Be 6
}

It "Should exhaust the range when an edit deletes all selected lines" {
$text = New-Object -TypeName "Microsoft.Windows.PowerShell.ScriptAnalyzer.EditableText" -ArgumentList "first`nlast"
$correctionType = "Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic.CorrectionExtent" -as [type]
$correction = [Activator]::CreateInstance(
$correctionType,
[object[]]@(2, 2, 1, 5, [string[]]@(), $null, $null))
$correctionsType = [System.Collections.Generic.List``1].MakeGenericType($correctionType)
$corrections = [Activator]::CreateInstance($correctionsType)
$corrections.Add($correction)
$range = New-Object -TypeName "Microsoft.Windows.PowerShell.ScriptAnalyzer.Range" -ArgumentList 2, 1, 2, 5
$arguments = [object[]]@(
$text.PSObject.BaseObject,
$corrections.PSObject.BaseObject,
0,
$range.PSObject.BaseObject)
$fixMethod = ("Microsoft.Windows.PowerShell.ScriptAnalyzer.ScriptAnalyzer" -as [type]).GetMethod(
'Fix',
[Reflection.BindingFlags]'NonPublic, Static')

$fixMethod.Invoke($null, $arguments) | Out-Null
$text.ToString() | Should -Be "first"
$arguments[3] | Should -BeNullOrEmpty
}

It "Should apply all corrections when earlier fixes expand the selected line" {
$def = "if(`$a){'x'}`nif(`$b){'y'}"
$expected = "if(`$a){'x'}`nif (`$b) { 'y' }"

Invoke-Formatter -ScriptDefinition $def -Range @(2, 1, 2, 12) | Should -Be $expected
}

It "Should track the selected lines when fixes <Name>" -TestCases @(
@{
Name = 'expand the last line'
Selected = "if(`$b){'y'}"
Expected = "if (`$b) { 'y' }"
}
@{
Name = 'shorten the last line'
Selected = "if (`$b) { 'y' }"
Expected = "if (`$b) { 'y' }"
}
@{
Name = 'expand multiple lines'
Selected = "if(`$b){'y'}`nif(`$c){'z'}"
Expected = "if (`$b) { 'y' }`nif (`$c) { 'z' }"
}
@{
Name = 'remove a line'
Selected = "if(`$b)`n{`n'y'`n}"
Expected = "if (`$b) {`n 'y'`n}"
}
@{
Name = 'add a line'
Selected = "if(`$b) {`n'y'`n}"
Expected = "if (`$b)`n{`n 'y'`n}"
Settings = 'CodeFormattingAllman'
}
) {
param($Selected, $Expected, $Settings = 'CodeFormatting')

$def = "if(`$a){'x'}`n$Selected`nif(`$d){'w'}"
$lines = $Selected -split "`n"
$range = @(2, 1, (1 + $lines.Count), (1 + $lines[-1].Length))

Invoke-Formatter -ScriptDefinition $def -Range $range -Settings $Settings |
Should -Be "if(`$a){'x'}`n$Expected`nif(`$d){'w'}"
}
}

Context "When no settings are given" {
Expand Down
Loading