Skip to content

Commit 227ab25

Browse files
Copilotbergmeister
andauthored
Preserve relative indentation in commented-out code (#2238)
* Initial plan * Fix comment runs after line continuations Co-authored-by: bergmeister <9250262+bergmeister@users.noreply.github.com> * Fix continuation indent after comment runs Co-authored-by: bergmeister <9250262+bergmeister@users.noreply.github.com> * Include line continuations in comment scan Co-authored-by: bergmeister <9250262+bergmeister@users.noreply.github.com> * Apply last suggestion 'Prevent double-counting indentation in comment runs' #2238 (comment) * Fix Pester discovery by removing extra BOM 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> Co-authored-by: Christoph Bergmeister <c.bergmeister@gmail.com>
1 parent 0c2d630 commit 227ab25

3 files changed

Lines changed: 100 additions & 15 deletions

File tree

‎Rules/UseConsistentIndentation.cs‎

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -275,17 +275,15 @@ public override IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string file
275275
++tempIndentationLevel;
276276
}
277277

278-
// check for comments in between multi-line commands with line continuation
278+
// Check for comments in between multi-line commands with line continuation.
279279
if (tokenIndex > 2 && tokens[tokenIndex - 1].Kind == TokenKind.NewLine
280280
&& tokens[tokenIndex - 2].Kind == TokenKind.Comment)
281281
{
282-
int searchForPrecedingLineContinuationIndex = tokenIndex - 2;
283-
while (searchForPrecedingLineContinuationIndex > 0 && tokens[searchForPrecedingLineContinuationIndex].Kind == TokenKind.Comment)
284-
{
285-
searchForPrecedingLineContinuationIndex--;
286-
}
287-
288-
if (searchForPrecedingLineContinuationIndex >= 0 && tokens[searchForPrecedingLineContinuationIndex].Kind == TokenKind.LineContinuation)
282+
GetCommentRunIndentationOffset(
283+
tokens,
284+
tokenIndex - 2,
285+
out bool commentRunStartsAfterLineContinuation);
286+
if (commentRunStartsAfterLineContinuation)
289287
{
290288
tempIndentationLevel++;
291289
}
@@ -298,9 +296,17 @@ public override IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string file
298296
}
299297

300298
bool lineHasPipelineBeforeToken = LineHasPipelineBeforeToken(tokens, tokenIndex, token);
301-
int commentIndentationOffset = token.Kind == TokenKind.Comment
302-
? GetCommentRunIndentationOffset(tokens, tokenIndex)
303-
: 0;
299+
int commentIndentationOffset = 0;
300+
if (token.Kind == TokenKind.Comment)
301+
{
302+
commentIndentationOffset = GetCommentRunIndentationOffset(
303+
tokens,
304+
tokenIndex,
305+
out bool commentRunStartsAfterLineContinuation);
306+
tempIndentationLevel = indentationLevel +
307+
(commentRunStartsAfterLineContinuation ? 1 : 0);
308+
}
309+
304310
AddViolation(token, tempIndentationLevel, diagnosticRecords, ref onNewLine, lineHasPipelineBeforeToken, commentIndentationOffset);
305311
}
306312
break;
@@ -339,18 +345,25 @@ public override IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string file
339345
return diagnosticRecords;
340346
}
341347

342-
private static int GetCommentRunIndentationOffset(Token[] tokens, int tokenIndex)
348+
private static int GetCommentRunIndentationOffset(
349+
Token[] tokens,
350+
int tokenIndex,
351+
out bool commentRunStartsAfterLineContinuation)
343352
{
344353
int firstCommentIndex = tokenIndex;
345354
int currentLine = tokens[tokenIndex].Extent.StartLineNumber;
346355
int precedingIndex = tokenIndex - 1;
347-
while (precedingIndex >= 0 && tokens[precedingIndex].Kind == TokenKind.NewLine)
356+
while (precedingIndex >= 0 &&
357+
(tokens[precedingIndex].Kind == TokenKind.NewLine ||
358+
tokens[precedingIndex].Kind == TokenKind.LineContinuation))
348359
{
349360
precedingIndex--;
350361
if (precedingIndex < 0 ||
351362
tokens[precedingIndex].Kind != TokenKind.Comment ||
352363
tokens[precedingIndex].Extent.EndLineNumber != currentLine - 1 ||
353-
(precedingIndex > 0 && tokens[precedingIndex - 1].Kind != TokenKind.NewLine))
364+
(precedingIndex > 0 &&
365+
tokens[precedingIndex - 1].Kind != TokenKind.NewLine &&
366+
tokens[precedingIndex - 1].Kind != TokenKind.LineContinuation))
354367
{
355368
break;
356369
}
@@ -360,10 +373,14 @@ private static int GetCommentRunIndentationOffset(Token[] tokens, int tokenIndex
360373
precedingIndex--;
361374
}
362375

376+
commentRunStartsAfterLineContinuation =
377+
firstCommentIndex > 0 &&
378+
tokens[firstCommentIndex - 1].Kind == TokenKind.LineContinuation;
363379
int leastIndentedColumn = tokens[firstCommentIndex].Extent.StartColumnNumber;
364380
for (int nextCommentIndex = firstCommentIndex + 2;
365381
nextCommentIndex < tokens.Length &&
366-
tokens[nextCommentIndex - 1].Kind == TokenKind.NewLine &&
382+
(tokens[nextCommentIndex - 1].Kind == TokenKind.NewLine ||
383+
tokens[nextCommentIndex - 1].Kind == TokenKind.LineContinuation) &&
367384
tokens[nextCommentIndex].Kind == TokenKind.Comment &&
368385
tokens[nextCommentIndex].Extent.StartLineNumber == tokens[nextCommentIndex - 2].Extent.EndLineNumber + 1;
369386
nextCommentIndex += 2)

‎Tests/Rules/UseConsistentIndentation.tests.ps1‎

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,20 @@ function foo {
254254
Should -Be $expectedScriptDefinition
255255
}
256256

257+
It 'Should preserve relative indentation in comments after a line continuation' {
258+
$scriptDefinition = @'
259+
Write-Output `
260+
# first
261+
# nested
262+
# last
263+
-InputObject 'value'
264+
'@
265+
$settings.Rules.PSUseConsistentIndentation.PipelineIndentation = 'NoIndentation'
266+
267+
Invoke-Formatter -ScriptDefinition $scriptDefinition -Settings $settings |
268+
Should -Be $scriptDefinition
269+
}
270+
257271
It 'Should preserve indentation when the first comment is not the least-indented with <Kind> indentation' -TestCases @(
258272
@{ Kind = 'space'; Indentation = ' ' }
259273
@{ Kind = 'tab'; Indentation = "`t" }
@@ -269,6 +283,57 @@ function foo {
269283
Invoke-Formatter -ScriptDefinition $scriptDefinition -Settings $settings |
270284
Should -Be $scriptDefinition
271285
}
286+
287+
It 'Should preserve a comment run containing an internal line continuation with <Kind> indentation' -TestCases @(
288+
@{ Kind = 'space'; Indentation = ' ' }
289+
@{ Kind = 'tab'; Indentation = "`t" }
290+
) {
291+
param([string] $Kind, [string] $Indentation)
292+
$scriptDefinition = @(
293+
'<# first #> `'
294+
"$Indentation<# second #>"
295+
"Write-Output 'after'"
296+
) -join "`n"
297+
$settings.Rules.PSUseConsistentIndentation.Kind = $Kind
298+
299+
$violations = Invoke-ScriptAnalyzer -ScriptDefinition $scriptDefinition -Settings $settings
300+
$violations.Count | Should -Be 0
301+
$formatted = Invoke-Formatter -ScriptDefinition $scriptDefinition -Settings $settings
302+
$formatted | Should -Be $scriptDefinition
303+
Invoke-Formatter -ScriptDefinition $formatted -Settings $settings |
304+
Should -Be $formatted
305+
}
306+
307+
It 'Should shift a comment run containing an internal line continuation uniformly with <Kind> indentation' -TestCases @(
308+
@{ Kind = 'space'; Indentation = ' ' }
309+
@{ Kind = 'tab'; Indentation = "`t" }
310+
) {
311+
param([string] $Kind, [string] $Indentation)
312+
$scriptDefinition = @(
313+
'if ($a) {'
314+
'<# first #> `'
315+
"$Indentation<# second #>"
316+
"$($Indentation)Write-Output 'after'"
317+
'}'
318+
) -join "`n"
319+
$expectedScriptDefinition = @(
320+
'if ($a) {'
321+
"$Indentation<# first #> ``"
322+
"$Indentation$Indentation<# second #>"
323+
"$($Indentation)Write-Output 'after'"
324+
'}'
325+
) -join "`n"
326+
$settings.Rules.PSUseConsistentIndentation.Kind = $Kind
327+
328+
$violations = Invoke-ScriptAnalyzer -ScriptDefinition $scriptDefinition -Settings $settings
329+
$violations.Count | Should -Be 2
330+
$formatted = Invoke-Formatter -ScriptDefinition $scriptDefinition -Settings $settings
331+
$formatted | Should -Be $expectedScriptDefinition
332+
Invoke-Formatter -ScriptDefinition $formatted -Settings $settings |
333+
Should -Be $formatted
334+
$violations = Invoke-ScriptAnalyzer -ScriptDefinition $formatted -Settings $settings
335+
$violations.Count | Should -Be 0
336+
}
272337
}
273338

274339
Context "When a multi-line command is given" {

‎docs/Rules/UseConsistentIndentation.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ title: UseConsistentIndentation
1515
This rule detects inconsistent indentation patterns within a source file. Indentation should be
1616
consistent throughout your script.
1717

18+
Consecutive comment lines are shifted together to preserve their relative indentation, including
19+
when a line continuation occurs within the comment run.
20+
1821
## Example
1922

2023
### Noncompliant

0 commit comments

Comments
 (0)