Skip to content

Commit 00f5363

Browse files
authored
Do not increase indentation after LParen if the previous token is a Newline and the next token is not a Newline (#1469)
1 parent 965eca5 commit 00f5363

File tree

2 files changed

+129
-2
lines changed

2 files changed

+129
-2
lines changed

Rules/UseConsistentIndentation.cs

+63-2
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,13 @@ public override IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string file
134134
var onNewLine = true;
135135
var pipelineAsts = ast.FindAll(testAst => testAst is PipelineAst && (testAst as PipelineAst).PipelineElements.Count > 1, true).ToList();
136136
int minimumPipelineAstIndex = 0;
137+
/*
138+
When an LParen and LBrace are on the same line, it can lead to too much de-indentation.
139+
In order to prevent the RParen code from de-indenting too much, we keep a stack of when we skipped the indentation
140+
caused by tokens that require a closing RParen (which are LParen, AtParen and DollarParen).
141+
*/
142+
var lParenSkippedIndentation = new Stack<bool>();
143+
137144
for (int tokenIndex = 0; tokenIndex < tokens.Length; tokenIndex++)
138145
{
139146
var token = tokens[tokenIndex];
@@ -146,10 +153,27 @@ public override IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string file
146153
switch (token.Kind)
147154
{
148155
case TokenKind.AtCurly:
149-
case TokenKind.AtParen:
150-
case TokenKind.LParen:
151156
case TokenKind.LCurly:
157+
AddViolation(token, indentationLevel++, diagnosticRecords, ref onNewLine);
158+
break;
159+
152160
case TokenKind.DollarParen:
161+
case TokenKind.AtParen:
162+
lParenSkippedIndentation.Push(false);
163+
AddViolation(token, indentationLevel++, diagnosticRecords, ref onNewLine);
164+
break;
165+
166+
case TokenKind.LParen:
167+
// When a line starts with a parenthesis and it is not the last non-comment token of that line,
168+
// then indentation does not need to be increased.
169+
if ((tokenIndex == 0 || tokens[tokenIndex - 1].Kind == TokenKind.NewLine) &&
170+
NextTokenIgnoringComments(tokens, tokenIndex)?.Kind != TokenKind.NewLine)
171+
{
172+
onNewLine = false;
173+
lParenSkippedIndentation.Push(true);
174+
break;
175+
}
176+
lParenSkippedIndentation.Push(false);
153177
AddViolation(token, indentationLevel++, diagnosticRecords, ref onNewLine);
154178
break;
155179

@@ -181,6 +205,20 @@ public override IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string file
181205
break;
182206

183207
case TokenKind.RParen:
208+
bool matchingLParenIncreasedIndentation = false;
209+
if (lParenSkippedIndentation.Count > 0)
210+
{
211+
matchingLParenIncreasedIndentation = lParenSkippedIndentation.Pop();
212+
}
213+
if (matchingLParenIncreasedIndentation)
214+
{
215+
onNewLine = false;
216+
break;
217+
}
218+
indentationLevel = ClipNegative(indentationLevel - 1);
219+
AddViolation(token, indentationLevel, diagnosticRecords, ref onNewLine);
220+
break;
221+
184222
case TokenKind.RCurly:
185223
indentationLevel = ClipNegative(indentationLevel - 1);
186224
AddViolation(token, indentationLevel, diagnosticRecords, ref onNewLine);
@@ -259,6 +297,29 @@ public override IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string file
259297
return diagnosticRecords;
260298
}
261299

300+
private static Token NextTokenIgnoringComments(Token[] tokens, int startIndex)
301+
{
302+
if (startIndex >= tokens.Length - 1)
303+
{
304+
return null;
305+
}
306+
307+
for (int i = startIndex + 1; i < tokens.Length; i++)
308+
{
309+
switch (tokens[i].Kind)
310+
{
311+
case TokenKind.Comment:
312+
continue;
313+
314+
default:
315+
return tokens[i];
316+
}
317+
}
318+
319+
// We've run out of tokens
320+
return null;
321+
}
322+
262323
private static bool PipelineIsFollowedByNewlineOrLineContinuation(Token[] tokens, int startIndex)
263324
{
264325
if (startIndex >= tokens.Length - 1)

Tests/Rules/UseConsistentIndentation.tests.ps1

+66
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,72 @@ $param3
113113
}
114114
}
115115

116+
Context 'LParen indentation' {
117+
It 'Should preserve script when line starts with LParen' {
118+
$IdempotentScriptDefinition = @'
119+
function test {
120+
(foo | bar {
121+
baz
122+
})
123+
Do-Something
124+
}
125+
'@
126+
Invoke-Formatter -ScriptDefinition $IdempotentScriptDefinition | Should -Be $idempotentScriptDefinition
127+
}
128+
129+
It 'Should preserve script when line starts and ends with LParen' {
130+
$IdempotentScriptDefinition = @'
131+
function test {
132+
(
133+
foo | bar {
134+
baz
135+
}
136+
)
137+
Do-Something
138+
}
139+
'@
140+
Invoke-Formatter -ScriptDefinition $IdempotentScriptDefinition | Should -Be $idempotentScriptDefinition
141+
}
142+
143+
It 'Should preserve script when line starts and ends with LParen but trailing comment' {
144+
$IdempotentScriptDefinition = @'
145+
function test {
146+
( # comment
147+
foo | bar {
148+
baz
149+
}
150+
)
151+
Do-Something
152+
}
153+
'@
154+
Invoke-Formatter -ScriptDefinition $IdempotentScriptDefinition | Should -Be $idempotentScriptDefinition
155+
}
156+
157+
It 'Should preserve script when there is Newline after LParen' {
158+
$IdempotentScriptDefinition = @'
159+
function test {
160+
$result = (
161+
Get-Something
162+
).Property
163+
Do-Something
164+
}
165+
'@
166+
Invoke-Formatter -ScriptDefinition $IdempotentScriptDefinition | Should -Be $idempotentScriptDefinition
167+
}
168+
169+
It 'Should preserve script when there is a comment and Newline after LParen' {
170+
$IdempotentScriptDefinition = @'
171+
function test {
172+
$result = ( # comment
173+
Get-Something
174+
).Property
175+
Do-Something
176+
}
177+
'@
178+
Invoke-Formatter -ScriptDefinition $IdempotentScriptDefinition | Should -Be $idempotentScriptDefinition
179+
}
180+
}
181+
116182
Context "When a sub-expression is provided" {
117183
It "Should not find a violations" {
118184
$def = @'

0 commit comments

Comments
 (0)