@@ -143,15 +143,14 @@ public override IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string file
143143 // Track pipeline indentation increases per PipelineAst instead of as a single
144144 // flat counter. A flat counter caused all accumulated pipeline indentation to be
145145 // subtracted when *any* pipeline ended, instead of only the contribution from
146- // that specific pipeline — leading to runaway indentation with nested pipelines.
146+ // that specific pipeline - leading to runaway indentation with nested pipelines.
147147 var pipelineIndentationIncreases = new Dictionary < PipelineAst , int > ( ) ;
148- /*
149- When an LParen and LBrace are on the same line, it can lead to too much de-indentation.
150- In order to prevent the RParen code from de-indenting too much, we keep a stack of when we skipped the indentation
151- caused by tokens that require a closing RParen (which are LParen, AtParen and DollarParen).
152- */
153- var lParenSkippedIndentation = new Stack < bool > ( ) ;
154-
148+ // When multiple openers appear on the same line (e.g. ({ or @(@{),
149+ // only the last unclosed opener should affect indentation. We
150+ // track, for every opener, whether its indentation increment was
151+ // skipped so that the matching closer knows not to decrement.
152+ var openerSkippedIndentation = new Stack < bool > ( ) ;
153+
155154 for ( int tokenIndex = 0 ; tokenIndex < tokens . Length ; tokenIndex ++ )
156155 {
157156 var token = tokens [ tokenIndex ] ;
@@ -165,27 +164,39 @@ caused by tokens that require a closing RParen (which are LParen, AtParen and Do
165164 {
166165 case TokenKind . AtCurly :
167166 case TokenKind . LCurly :
168- AddViolation ( token , indentationLevel ++ , diagnosticRecords , ref onNewLine ) ;
169- break ;
170-
171167 case TokenKind . DollarParen :
172168 case TokenKind . AtParen :
173- lParenSkippedIndentation . Push ( false ) ;
174- AddViolation ( token , indentationLevel ++ , diagnosticRecords , ref onNewLine ) ;
169+ AddViolation ( token , indentationLevel , diagnosticRecords , ref onNewLine ) ;
170+ if ( HasUnclosedOpenerBeforeLineEnd ( tokens , tokenIndex ) )
171+ {
172+ openerSkippedIndentation . Push ( true ) ;
173+ }
174+ else
175+ {
176+ indentationLevel ++ ;
177+ openerSkippedIndentation . Push ( false ) ;
178+ }
175179 break ;
176180
177181 case TokenKind . LParen :
178182 AddViolation ( token , indentationLevel , diagnosticRecords , ref onNewLine ) ;
179- // When a line starts with a parenthesis and it is not the last non-comment token of that line,
180- // then indentation does not need to be increased.
183+ // When a line starts with a parenthesis and it is not the
184+ // last non-comment token of that line, indentation does
185+ // not need to be increased.
181186 if ( ( tokenIndex == 0 || tokens [ tokenIndex - 1 ] . Kind == TokenKind . NewLine ) &&
182187 NextTokenIgnoringComments ( tokens , tokenIndex ) ? . Kind != TokenKind . NewLine )
183188 {
184- onNewLine = false ;
185- lParenSkippedIndentation . Push ( true ) ;
189+ openerSkippedIndentation . Push ( true ) ;
190+ break ;
191+ }
192+ // General case: skip when another opener follows so that
193+ // only the last unclosed opener on a line is indent-affecting.
194+ if ( HasUnclosedOpenerBeforeLineEnd ( tokens , tokenIndex ) )
195+ {
196+ openerSkippedIndentation . Push ( true ) ;
186197 break ;
187198 }
188- lParenSkippedIndentation . Push ( false ) ;
199+ openerSkippedIndentation . Push ( false ) ;
189200 indentationLevel ++ ;
190201 break ;
191202
@@ -232,23 +243,18 @@ caused by tokens that require a closing RParen (which are LParen, AtParen and Do
232243 break ;
233244
234245 case TokenKind . RParen :
235- bool matchingLParenIncreasedIndentation = false ;
236- if ( lParenSkippedIndentation . Count > 0 )
246+ case TokenKind . RCurly :
247+ if ( openerSkippedIndentation . Count > 0 && openerSkippedIndentation . Pop ( ) )
237248 {
238- matchingLParenIncreasedIndentation = lParenSkippedIndentation . Pop ( ) ;
249+ // The matching opener skipped its increment, so we
250+ // skip the decrement but still enforce indentation.
251+ AddViolation ( token , indentationLevel , diagnosticRecords , ref onNewLine ) ;
239252 }
240- if ( matchingLParenIncreasedIndentation )
253+ else
241254 {
242- onNewLine = false ;
243- break ;
255+ indentationLevel = ClipNegative ( indentationLevel - 1 ) ;
256+ AddViolation ( token , indentationLevel , diagnosticRecords , ref onNewLine ) ;
244257 }
245- indentationLevel = ClipNegative ( indentationLevel - 1 ) ;
246- AddViolation ( token , indentationLevel , diagnosticRecords , ref onNewLine ) ;
247- break ;
248-
249- case TokenKind . RCurly :
250- indentationLevel = ClipNegative ( indentationLevel - 1 ) ;
251- AddViolation ( token , indentationLevel , diagnosticRecords , ref onNewLine ) ;
252258 break ;
253259
254260 case TokenKind . NewLine :
@@ -330,6 +336,49 @@ caused by tokens that require a closing RParen (which are LParen, AtParen and Do
330336 return diagnosticRecords ;
331337 }
332338
339+ /// <summary>
340+ /// Scans forward from the current opener to the end of the line.
341+ /// Returns true if there is at least one unclosed opener when
342+ /// the line ends, meaning the current opener should skip its
343+ /// indentation increment. If the current opener's own closer
344+ /// is found on the same line (depth drops below zero), returns
345+ /// false so that it indents normally.
346+ /// </summary>
347+ private static bool HasUnclosedOpenerBeforeLineEnd ( Token [ ] tokens , int currentIndex )
348+ {
349+ int depth = 0 ;
350+ for ( int i = currentIndex + 1 ; i < tokens . Length ; i ++ )
351+ {
352+ switch ( tokens [ i ] . Kind )
353+ {
354+ case TokenKind . NewLine :
355+ case TokenKind . LineContinuation :
356+ case TokenKind . EndOfInput :
357+ return depth > 0 ;
358+
359+ case TokenKind . LCurly :
360+ case TokenKind . AtCurly :
361+ case TokenKind . LParen :
362+ case TokenKind . AtParen :
363+ case TokenKind . DollarParen :
364+ depth ++ ;
365+ break ;
366+
367+ case TokenKind . RCurly :
368+ case TokenKind . RParen :
369+ depth -- ;
370+ if ( depth < 0 )
371+ {
372+ // Our own closer was found on this line.
373+ return false ;
374+ }
375+ break ;
376+ }
377+ }
378+
379+ return depth > 0 ;
380+ }
381+
333382 private static Token NextTokenIgnoringComments ( Token [ ] tokens , int startIndex )
334383 {
335384 if ( startIndex >= tokens . Length - 1 )
0 commit comments