From 70c9c348ebf5cb2d5cdc25834052aa4d1d716767 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 27 Oct 2026 22:27:29 +0000 Subject: [PATCH 1/5] Initial plan From 07da3aefe2db3cac558e858692f61bca795b9678 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 27 Oct 2026 22:34:03 +0000 Subject: [PATCH 2/5] Fix PlaceOpenBrace/PlaceCloseBrace corrections leaving stray whitespace and wrong indentation Co-authored-by: bergmeister <9250262+bergmeister@users.noreply.github.com> --- Rules/PlaceCloseBrace.cs | 27 ++++++++-- Rules/PlaceOpenBrace.cs | 14 ++++- Tests/Rules/PlaceCloseBrace.tests.ps1 | 73 +++++++++++++++++++++++++-- Tests/Rules/PlaceOpenBrace.tests.ps1 | 9 ++++ 4 files changed, 114 insertions(+), 9 deletions(-) diff --git a/Rules/PlaceCloseBrace.cs b/Rules/PlaceCloseBrace.cs index 652197afd..e9a7183f3 100644 --- a/Rules/PlaceCloseBrace.cs +++ b/Rules/PlaceCloseBrace.cs @@ -277,9 +277,15 @@ private List GetCorrectionsForBraceShouldHaveNewLineAfter( var corrections = new List(); var nextToken = tokens[closeBracePos + 1]; var closeBraceToken = tokens[closeBracePos]; + + // Also remove the whitespace between the close brace and the next token + // and indent the next token to the same level as the close brace corrections.Add(new CorrectionExtent( - closeBraceToken.Extent, - closeBraceToken.Text + Environment.NewLine, + closeBraceToken.Extent.StartLineNumber, + nextToken.Extent.StartLineNumber, + closeBraceToken.Extent.StartColumnNumber, + nextToken.Extent.StartColumnNumber, + closeBraceToken.Text + Environment.NewLine + GetIndentation(tokens, closeBracePos, openBracePos), fileName)); return corrections; } @@ -296,7 +302,7 @@ private string GetIndentation(Token[] tokens, int closeBracePos, int openBracePo // if open brace follows any keywords use the identation of the first keyword // on the line containing the open brace Token firstTokenOnOpenBraceLine = openBraceToken; - for (int k = openBracePos; k > 0; --k) + for (int k = openBracePos; k >= 0; --k) { if (tokens[k].Extent.StartLineNumber == firstTokenOnOpenBraceLine.Extent.StartLineNumber) { @@ -433,10 +439,21 @@ private List GetCorrectionsForBraceShouldBeOnNewLine( { var corrections = new List(); var closeBraceToken = tokens[closeBracePos]; + + // Also remove any whitespace between the preceding token and the close brace + // so that the preceding line does not end with a trailing whitespace + var startLineNumber = closeBraceToken.Extent.StartLineNumber; + var startColumnNumber = closeBraceToken.Extent.StartColumnNumber; + var precedingTokenExtent = tokens[closeBracePos - 1].Extent; + if (precedingTokenExtent.EndLineNumber == startLineNumber) + { + startColumnNumber = precedingTokenExtent.EndColumnNumber; + } + corrections.Add(new CorrectionExtent( - closeBraceToken.Extent.StartLineNumber, + startLineNumber, closeBraceToken.Extent.EndLineNumber, - closeBraceToken.Extent.StartColumnNumber, + startColumnNumber, closeBraceToken.Extent.EndColumnNumber, Environment.NewLine + GetIndentation(tokens, closeBracePos, openBracePos) + closeBraceToken.Extent.Text, fileName)); diff --git a/Rules/PlaceOpenBrace.cs b/Rules/PlaceOpenBrace.cs index 5a74626d9..e2ba4b03d 100644 --- a/Rules/PlaceOpenBrace.cs +++ b/Rules/PlaceOpenBrace.cs @@ -288,12 +288,24 @@ private List GetCorrectionsForNoNewLineAfterBrace( var corrections = new List(); var extent = tokens[openBracePos].Extent; + // Also remove any whitespace between the open brace and the next token on the same line + // so that the next token does not start with a leading whitespace on the new line + var endColumnNumber = extent.EndColumnNumber; + if (openBracePos + 1 < tokens.Length) + { + var nextTokenExtent = tokens[openBracePos + 1].Extent; + if (nextTokenExtent.StartLineNumber == extent.EndLineNumber) + { + endColumnNumber = nextTokenExtent.StartColumnNumber; + } + } + corrections.Add( new CorrectionExtent( extent.StartLineNumber, extent.EndLineNumber, extent.StartColumnNumber, - extent.EndColumnNumber, + endColumnNumber, new StringBuilder().Append(extent.Text).Append(Environment.NewLine).ToString(), fileName)); return corrections; diff --git a/Tests/Rules/PlaceCloseBrace.tests.ps1 b/Tests/Rules/PlaceCloseBrace.tests.ps1 index fb741ee2a..86da4c237 100644 --- a/Tests/Rules/PlaceCloseBrace.tests.ps1 +++ b/Tests/Rules/PlaceCloseBrace.tests.ps1 @@ -174,7 +174,7 @@ if (Test-Path "blah") { RawContent = $def DiagnosticRecord = $violations[0] CorrectionsCount = 1 - ViolationText = '}' + ViolationText = '} ' CorrectionText = '}' + [System.Environment]::NewLine } Test-CorrectionExtentFromContent @params @@ -194,7 +194,7 @@ if (Test-Path "blah") { RawContent = $def DiagnosticRecord = $violations[0] CorrectionsCount = 1 - ViolationText = '}' + ViolationText = '} ' CorrectionText = '}' + [System.Environment]::NewLine } Test-CorrectionExtentFromContent @params @@ -215,7 +215,7 @@ try { RawContent = $def DiagnosticRecord = $violations[0] CorrectionsCount = 1 - ViolationText = '}' + ViolationText = '} ' CorrectionText = '}' + [System.Environment]::NewLine } Test-CorrectionExtentFromContent @params @@ -428,4 +428,71 @@ else { Invoke-Formatter -ScriptDefinition $OTBSDefinition -Settings 'CodeFormattingStroustrup' | Should -Be $StroustrupDefinition } } + + Context "When formatting a one line if/else statement with IgnoreOneLineBlock set to false" { + BeforeAll { + $oneLineDefinition = "if (`$RegEx) { '-match' } else { '-like' }" + } + + It "Should not leave stray whitespace or misplace the close braces" { + $formatterSettings = @{ + Rules = @{ + PSPlaceOpenBrace = @{ Enable = $true; IgnoreOneLineBlock = $false } + PSPlaceCloseBrace = @{ Enable = $true; IgnoreOneLineBlock = $false } + } + } + $expected = @" +if (`$RegEx) { +'-match' +} +else { +'-like' +} +"@ + Invoke-Formatter -ScriptDefinition $oneLineDefinition -Settings $formatterSettings | Should -Be $expected + } + + It "Should indent the close brace to the level of its statement" { + $formatterSettings = @{ + Rules = @{ + PSPlaceOpenBrace = @{ Enable = $true; IgnoreOneLineBlock = $false } + PSPlaceCloseBrace = @{ Enable = $true; IgnoreOneLineBlock = $false } + } + } + $def = @" +function foo { + if (`$RegEx) { + '-match' + } else { + '-like' + } +} +"@ + $expected = @" +function foo { + if (`$RegEx) { + '-match' + } + else { + '-like' + } +} +"@ + Invoke-Formatter -ScriptDefinition $def -Settings $formatterSettings | Should -Be $expected + } + + It "Should format to OTBS style" { + $formatterSettings = Import-PowerShellDataFile (Join-Path (Get-Module PSScriptAnalyzer).ModuleBase 'Settings/CodeFormattingOTBS.psd1') + $formatterSettings.Rules.PSPlaceOpenBrace.IgnoreOneLineBlock = $false + $formatterSettings.Rules.PSPlaceCloseBrace.IgnoreOneLineBlock = $false + $expected = @" +if (`$RegEx) { + '-match' +} else { + '-like' +} +"@ + Invoke-Formatter -ScriptDefinition $oneLineDefinition -Settings $formatterSettings | Should -Be $expected + } + } } diff --git a/Tests/Rules/PlaceOpenBrace.tests.ps1 b/Tests/Rules/PlaceOpenBrace.tests.ps1 index 4c8206102..352c58ffa 100644 --- a/Tests/Rules/PlaceOpenBrace.tests.ps1 +++ b/Tests/Rules/PlaceOpenBrace.tests.ps1 @@ -168,5 +168,14 @@ function foo { } It "Should mark only the open brace" { $violations[0].Extent.Text | Should -Be '{' } + + It "Should remove the whitespace following the open brace when correcting" { + $def = "if (`$true) { 'blah' }" + $violations = Invoke-ScriptAnalyzer -ScriptDefinition $def -Settings $settings + $violations.Count | Should -Be 1 + $violations[0].SuggestedCorrections[0].Text | Should -Be ('{' + [System.Environment]::NewLine) + $violations[0].SuggestedCorrections[0].StartColumnNumber | Should -Be 12 + $violations[0].SuggestedCorrections[0].EndColumnNumber | Should -Be 14 + } } } From 17e46caef5fba5828cdb1c47d549c26fae730d0f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Oct 2026 07:24:38 +0000 Subject: [PATCH 3/5] Make brace formatter expectations platform-independent Co-authored-by: bergmeister <9250262+bergmeister@users.noreply.github.com> --- Tests/Rules/PlaceCloseBrace.tests.ps1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Tests/Rules/PlaceCloseBrace.tests.ps1 b/Tests/Rules/PlaceCloseBrace.tests.ps1 index 86da4c237..5f270f75b 100644 --- a/Tests/Rules/PlaceCloseBrace.tests.ps1 +++ b/Tests/Rules/PlaceCloseBrace.tests.ps1 @@ -449,6 +449,7 @@ else { '-like' } "@ + $expected = $expected.Replace("`n", [System.Environment]::NewLine) Invoke-Formatter -ScriptDefinition $oneLineDefinition -Settings $formatterSettings | Should -Be $expected } @@ -492,6 +493,7 @@ if (`$RegEx) { '-like' } "@ + $expected = $expected.Replace("`n", [System.Environment]::NewLine) Invoke-Formatter -ScriptDefinition $oneLineDefinition -Settings $formatterSettings | Should -Be $expected } } From 810f339f347f319015124626dcbc4863b3d2a2cf Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:09:52 +0000 Subject: [PATCH 4/5] Preserve branch indentation after multiline conditions (#2271) * Initial plan * Preserve close brace indentation for branches Co-authored-by: bergmeister <9250262+bergmeister@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: bergmeister <9250262+bergmeister@users.noreply.github.com> --- Rules/PlaceCloseBrace.cs | 5 ++- Tests/Rules/PlaceCloseBrace.tests.ps1 | 46 +++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/Rules/PlaceCloseBrace.cs b/Rules/PlaceCloseBrace.cs index e9a7183f3..1bb15f6e1 100644 --- a/Rules/PlaceCloseBrace.cs +++ b/Rules/PlaceCloseBrace.cs @@ -277,6 +277,9 @@ private List GetCorrectionsForBraceShouldHaveNewLineAfter( var corrections = new List(); var nextToken = tokens[closeBracePos + 1]; var closeBraceToken = tokens[closeBracePos]; + var indentation = tokens[closeBracePos - 1].Kind == TokenKind.NewLine + ? new String(' ', closeBraceToken.Extent.StartColumnNumber - 1) + : GetIndentation(tokens, closeBracePos, openBracePos); // Also remove the whitespace between the close brace and the next token // and indent the next token to the same level as the close brace @@ -285,7 +288,7 @@ private List GetCorrectionsForBraceShouldHaveNewLineAfter( nextToken.Extent.StartLineNumber, closeBraceToken.Extent.StartColumnNumber, nextToken.Extent.StartColumnNumber, - closeBraceToken.Text + Environment.NewLine + GetIndentation(tokens, closeBracePos, openBracePos), + closeBraceToken.Text + Environment.NewLine + indentation, fileName)); return corrections; } diff --git a/Tests/Rules/PlaceCloseBrace.tests.ps1 b/Tests/Rules/PlaceCloseBrace.tests.ps1 index 5f270f75b..151ed64e8 100644 --- a/Tests/Rules/PlaceCloseBrace.tests.ps1 +++ b/Tests/Rules/PlaceCloseBrace.tests.ps1 @@ -180,6 +180,52 @@ if (Test-Path "blah") { Test-CorrectionExtentFromContent @params } + It "Should preserve close brace indentation after a multiline if condition" { + $def = @' +if ( + $true) { + $true +} else { + $false +} +'@ + $expected = @' +if ( + $true) { + $true +} +else { + $false +} +'@ + Invoke-Formatter -ScriptDefinition $def -Settings $settings | Should -Be $expected + } + + It "Should preserve nested close brace indentation after a multiline if condition" { + $def = @' +function Test { + if ( + $true) { + $true + } else { + $false + } +} +'@ + $expected = @' +function Test { + if ( + $true) { + $true + } + else { + $false + } +} +'@ + Invoke-Formatter -ScriptDefinition $def -Settings $settings | Should -Be $expected + } + It "Should find a violation for a close brace followed by elseif" { $def = @' if (Test-Path "blah") { From 7b3de72e49aeb14647eab161d1e4b994f6557907 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Oct 2026 13:12:30 +0000 Subject: [PATCH 5/5] Preserve tab indentation when formatting branches (#2275) * Initial plan * Preserve tab indentation when formatting branches Co-authored-by: bergmeister <9250262+bergmeister@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: bergmeister <9250262+bergmeister@users.noreply.github.com> --- Rules/PlaceCloseBrace.cs | 2 +- Tests/Rules/PlaceCloseBrace.tests.ps1 | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/Rules/PlaceCloseBrace.cs b/Rules/PlaceCloseBrace.cs index 1bb15f6e1..6d2bd51b3 100644 --- a/Rules/PlaceCloseBrace.cs +++ b/Rules/PlaceCloseBrace.cs @@ -278,7 +278,7 @@ private List GetCorrectionsForBraceShouldHaveNewLineAfter( var nextToken = tokens[closeBracePos + 1]; var closeBraceToken = tokens[closeBracePos]; var indentation = tokens[closeBracePos - 1].Kind == TokenKind.NewLine - ? new String(' ', closeBraceToken.Extent.StartColumnNumber - 1) + ? closeBraceToken.Extent.StartScriptPosition.Line.Substring(0, closeBraceToken.Extent.StartColumnNumber - 1) : GetIndentation(tokens, closeBracePos, openBracePos); // Also remove the whitespace between the close brace and the next token diff --git a/Tests/Rules/PlaceCloseBrace.tests.ps1 b/Tests/Rules/PlaceCloseBrace.tests.ps1 index 151ed64e8..f44d50bb1 100644 --- a/Tests/Rules/PlaceCloseBrace.tests.ps1 +++ b/Tests/Rules/PlaceCloseBrace.tests.ps1 @@ -226,6 +226,25 @@ function Test { Invoke-Formatter -ScriptDefinition $def -Settings $settings | Should -Be $expected } + It "Should preserve tab indentation before a close brace" { + $def = @' +if ($true) { + $true + } else { + $false + } +'@ + $expected = @' +if ($true) { + $true + } + else { + $false + } +'@ + Invoke-Formatter -ScriptDefinition $def -Settings $settings | Should -Be $expected + } + It "Should find a violation for a close brace followed by elseif" { $def = @' if (Test-Path "blah") {