From 6b8f1af1501b41a902c2979e5a5971ea998a80b3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Oct 2026 10:05:23 +0000 Subject: [PATCH 1/6] Initial plan From 312ded68275fc6582c4157373a4aeccba9c53971 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Oct 2026 10:50:04 +0000 Subject: [PATCH 2/6] Fix comment runs after line continuations Co-authored-by: bergmeister <9250262+bergmeister@users.noreply.github.com> --- Rules/UseConsistentIndentation.cs | 32 +++++++++++++++---- .../Rules/UseConsistentIndentation.tests.ps1 | 14 ++++++++ 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/Rules/UseConsistentIndentation.cs b/Rules/UseConsistentIndentation.cs index 97c913db5..e13ab6d2b 100644 --- a/Rules/UseConsistentIndentation.cs +++ b/Rules/UseConsistentIndentation.cs @@ -298,9 +298,19 @@ public override IEnumerable AnalyzeScript(Ast ast, string file } bool lineHasPipelineBeforeToken = LineHasPipelineBeforeToken(tokens, tokenIndex, token); - int commentIndentationOffset = token.Kind == TokenKind.Comment - ? GetCommentRunIndentationOffset(tokens, tokenIndex) - : 0; + int commentIndentationOffset = 0; + if (token.Kind == TokenKind.Comment) + { + commentIndentationOffset = GetCommentRunIndentationOffset( + tokens, + tokenIndex, + out bool commentRunStartsAfterLineContinuation); + if (commentRunStartsAfterLineContinuation) + { + tempIndentationLevel = indentationLevel + 1; + } + } + AddViolation(token, tempIndentationLevel, diagnosticRecords, ref onNewLine, lineHasPipelineBeforeToken, commentIndentationOffset); } break; @@ -339,18 +349,25 @@ public override IEnumerable AnalyzeScript(Ast ast, string file return diagnosticRecords; } - private static int GetCommentRunIndentationOffset(Token[] tokens, int tokenIndex) + private static int GetCommentRunIndentationOffset( + Token[] tokens, + int tokenIndex, + out bool commentRunStartsAfterLineContinuation) { int firstCommentIndex = tokenIndex; int currentLine = tokens[tokenIndex].Extent.StartLineNumber; int precedingIndex = tokenIndex - 1; - while (precedingIndex >= 0 && tokens[precedingIndex].Kind == TokenKind.NewLine) + while (precedingIndex >= 0 && + (tokens[precedingIndex].Kind == TokenKind.NewLine || + tokens[precedingIndex].Kind == TokenKind.LineContinuation)) { precedingIndex--; if (precedingIndex < 0 || tokens[precedingIndex].Kind != TokenKind.Comment || tokens[precedingIndex].Extent.EndLineNumber != currentLine - 1 || - (precedingIndex > 0 && tokens[precedingIndex - 1].Kind != TokenKind.NewLine)) + (precedingIndex > 0 && + tokens[precedingIndex - 1].Kind != TokenKind.NewLine && + tokens[precedingIndex - 1].Kind != TokenKind.LineContinuation)) { break; } @@ -360,6 +377,9 @@ private static int GetCommentRunIndentationOffset(Token[] tokens, int tokenIndex precedingIndex--; } + commentRunStartsAfterLineContinuation = + firstCommentIndex > 0 && + tokens[firstCommentIndex - 1].Kind == TokenKind.LineContinuation; return tokens[tokenIndex].Extent.StartColumnNumber - tokens[firstCommentIndex].Extent.StartColumnNumber; } diff --git a/Tests/Rules/UseConsistentIndentation.tests.ps1 b/Tests/Rules/UseConsistentIndentation.tests.ps1 index 3a0f325e2..42ce9f59b 100644 --- a/Tests/Rules/UseConsistentIndentation.tests.ps1 +++ b/Tests/Rules/UseConsistentIndentation.tests.ps1 @@ -253,6 +253,20 @@ function foo { Invoke-Formatter -ScriptDefinition $scriptDefinition -Settings $settings | Should -Be $expectedScriptDefinition } + + It 'Should preserve relative indentation in comments after a line continuation' { + $scriptDefinition = @' +Write-Output ` + # first + # nested + # last + -InputObject 'value' +'@ + $settings.Rules.PSUseConsistentIndentation.PipelineIndentation = 'NoIndentation' + + Invoke-Formatter -ScriptDefinition $scriptDefinition -Settings $settings | + Should -Be $scriptDefinition + } } Context "When a multi-line command is given" { From 7778cbe72d7e0f69a2ce0eacd41d4da9ed88f34d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Oct 2026 11:22:20 +0000 Subject: [PATCH 3/6] Fix continuation indent after comment runs Co-authored-by: bergmeister <9250262+bergmeister@users.noreply.github.com> --- Rules/UseConsistentIndentation.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Rules/UseConsistentIndentation.cs b/Rules/UseConsistentIndentation.cs index e13ab6d2b..af644e527 100644 --- a/Rules/UseConsistentIndentation.cs +++ b/Rules/UseConsistentIndentation.cs @@ -275,17 +275,15 @@ public override IEnumerable AnalyzeScript(Ast ast, string file ++tempIndentationLevel; } - // check for comments in between multi-line commands with line continuation + // Check for comments in between multi-line commands with line continuation. if (tokenIndex > 2 && tokens[tokenIndex - 1].Kind == TokenKind.NewLine && tokens[tokenIndex - 2].Kind == TokenKind.Comment) { - int searchForPrecedingLineContinuationIndex = tokenIndex - 2; - while (searchForPrecedingLineContinuationIndex > 0 && tokens[searchForPrecedingLineContinuationIndex].Kind == TokenKind.Comment) - { - searchForPrecedingLineContinuationIndex--; - } - - if (searchForPrecedingLineContinuationIndex >= 0 && tokens[searchForPrecedingLineContinuationIndex].Kind == TokenKind.LineContinuation) + GetCommentRunIndentationOffset( + tokens, + tokenIndex - 2, + out bool commentRunStartsAfterLineContinuation); + if (commentRunStartsAfterLineContinuation) { tempIndentationLevel++; } From 842d46c1684c0fb30aabaa8a317d2e8f1032ec3f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Oct 2026 12:45:14 +0000 Subject: [PATCH 4/6] Include line continuations in comment scan Co-authored-by: bergmeister <9250262+bergmeister@users.noreply.github.com> --- Rules/UseConsistentIndentation.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Rules/UseConsistentIndentation.cs b/Rules/UseConsistentIndentation.cs index b403d9165..968b37015 100644 --- a/Rules/UseConsistentIndentation.cs +++ b/Rules/UseConsistentIndentation.cs @@ -381,7 +381,8 @@ private static int GetCommentRunIndentationOffset( int leastIndentedColumn = tokens[firstCommentIndex].Extent.StartColumnNumber; for (int nextCommentIndex = firstCommentIndex + 2; nextCommentIndex < tokens.Length && - tokens[nextCommentIndex - 1].Kind == TokenKind.NewLine && + (tokens[nextCommentIndex - 1].Kind == TokenKind.NewLine || + tokens[nextCommentIndex - 1].Kind == TokenKind.LineContinuation) && tokens[nextCommentIndex].Kind == TokenKind.Comment && tokens[nextCommentIndex].Extent.StartLineNumber == tokens[nextCommentIndex - 2].Extent.EndLineNumber + 1; nextCommentIndex += 2) From 9f65c732c85feafba87ee4ab3f1dfebfbfd33270 Mon Sep 17 00:00:00 2001 From: Christoph Bergmeister Date: Mon, 26 Oct 2026 20:26:25 +0000 Subject: [PATCH 5/6] Apply last suggestion 'Prevent double-counting indentation in comment runs' https://github.com/PowerShell/PSScriptAnalyzer/pull/2238#discussion_r4381470637 --- Rules/UseConsistentIndentation.cs | 6 +-- .../Rules/UseConsistentIndentation.tests.ps1 | 53 ++++++++++++++++++- docs/Rules/UseConsistentIndentation.md | 3 ++ 3 files changed, 57 insertions(+), 5 deletions(-) diff --git a/Rules/UseConsistentIndentation.cs b/Rules/UseConsistentIndentation.cs index 968b37015..5fd573cc5 100644 --- a/Rules/UseConsistentIndentation.cs +++ b/Rules/UseConsistentIndentation.cs @@ -303,10 +303,8 @@ public override IEnumerable AnalyzeScript(Ast ast, string file tokens, tokenIndex, out bool commentRunStartsAfterLineContinuation); - if (commentRunStartsAfterLineContinuation) - { - tempIndentationLevel = indentationLevel + 1; - } + tempIndentationLevel = indentationLevel + + (commentRunStartsAfterLineContinuation ? 1 : 0); } AddViolation(token, tempIndentationLevel, diagnosticRecords, ref onNewLine, lineHasPipelineBeforeToken, commentIndentationOffset); diff --git a/Tests/Rules/UseConsistentIndentation.tests.ps1 b/Tests/Rules/UseConsistentIndentation.tests.ps1 index e443d6037..4e2e36b1c 100644 --- a/Tests/Rules/UseConsistentIndentation.tests.ps1 +++ b/Tests/Rules/UseConsistentIndentation.tests.ps1 @@ -1,4 +1,4 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. +# Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. BeforeAll { @@ -283,6 +283,57 @@ Write-Output ` Invoke-Formatter -ScriptDefinition $scriptDefinition -Settings $settings | Should -Be $scriptDefinition } + + It 'Should preserve a comment run containing an internal line continuation with indentation' -TestCases @( + @{ Kind = 'space'; Indentation = ' ' } + @{ Kind = 'tab'; Indentation = "`t" } + ) { + param([string] $Kind, [string] $Indentation) + $scriptDefinition = @( + '<# first #> `' + "$Indentation<# second #>" + "Write-Output 'after'" + ) -join "`n" + $settings.Rules.PSUseConsistentIndentation.Kind = $Kind + + $violations = Invoke-ScriptAnalyzer -ScriptDefinition $scriptDefinition -Settings $settings + $violations.Count | Should -Be 0 + $formatted = Invoke-Formatter -ScriptDefinition $scriptDefinition -Settings $settings + $formatted | Should -Be $scriptDefinition + Invoke-Formatter -ScriptDefinition $formatted -Settings $settings | + Should -Be $formatted + } + + It 'Should shift a comment run containing an internal line continuation uniformly with indentation' -TestCases @( + @{ Kind = 'space'; Indentation = ' ' } + @{ Kind = 'tab'; Indentation = "`t" } + ) { + param([string] $Kind, [string] $Indentation) + $scriptDefinition = @( + 'if ($a) {' + '<# first #> `' + "$Indentation<# second #>" + "$($Indentation)Write-Output 'after'" + '}' + ) -join "`n" + $expectedScriptDefinition = @( + 'if ($a) {' + "$Indentation<# first #> ``" + "$Indentation$Indentation<# second #>" + "$($Indentation)Write-Output 'after'" + '}' + ) -join "`n" + $settings.Rules.PSUseConsistentIndentation.Kind = $Kind + + $violations = Invoke-ScriptAnalyzer -ScriptDefinition $scriptDefinition -Settings $settings + $violations.Count | Should -Be 2 + $formatted = Invoke-Formatter -ScriptDefinition $scriptDefinition -Settings $settings + $formatted | Should -Be $expectedScriptDefinition + Invoke-Formatter -ScriptDefinition $formatted -Settings $settings | + Should -Be $formatted + $violations = Invoke-ScriptAnalyzer -ScriptDefinition $formatted -Settings $settings + $violations.Count | Should -Be 0 + } } Context "When a multi-line command is given" { diff --git a/docs/Rules/UseConsistentIndentation.md b/docs/Rules/UseConsistentIndentation.md index 5e6392772..4d020bbd4 100644 --- a/docs/Rules/UseConsistentIndentation.md +++ b/docs/Rules/UseConsistentIndentation.md @@ -15,6 +15,9 @@ title: UseConsistentIndentation This rule detects inconsistent indentation patterns within a source file. Indentation should be consistent throughout your script. +Consecutive comment lines are shifted together to preserve their relative indentation, including +when a line continuation occurs within the comment run. + ## Example ### Noncompliant From 229da6b141eb2a1a0111ae63522b78446fa47904 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Oct 2026 20:33:24 +0000 Subject: [PATCH 6/6] Fix Pester discovery by removing extra BOM Co-authored-by: bergmeister <9250262+bergmeister@users.noreply.github.com> --- Tests/Rules/UseConsistentIndentation.tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Rules/UseConsistentIndentation.tests.ps1 b/Tests/Rules/UseConsistentIndentation.tests.ps1 index 4e2e36b1c..6febc02c7 100644 --- a/Tests/Rules/UseConsistentIndentation.tests.ps1 +++ b/Tests/Rules/UseConsistentIndentation.tests.ps1 @@ -1,4 +1,4 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. +# Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. BeforeAll {