@@ -130,9 +130,21 @@ public override IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string file
130130 var tokens = Helper . Instance . Tokens ;
131131 var diagnosticRecords = new List < DiagnosticRecord > ( ) ;
132132 var indentationLevel = 0 ;
133- var currentIndenationLevelIncreaseDueToPipelines = 0 ;
134133 var onNewLine = true ;
135134 var pipelineAsts = ast . FindAll ( testAst => testAst is PipelineAst && ( testAst as PipelineAst ) . PipelineElements . Count > 1 , true ) . ToList ( ) ;
135+ // Sort by end position so that inner (nested) pipelines appear before outer ones.
136+ // This is required by MatchingPipelineAstEnd, whose early-break optimization
137+ // would otherwise skip nested pipelines that end before their outer pipeline.
138+ pipelineAsts . Sort ( ( a , b ) =>
139+ {
140+ int lineCmp = a . Extent . EndScriptPosition . LineNumber . CompareTo ( b . Extent . EndScriptPosition . LineNumber ) ;
141+ return lineCmp != 0 ? lineCmp : a . Extent . EndScriptPosition . ColumnNumber . CompareTo ( b . Extent . EndScriptPosition . ColumnNumber ) ;
142+ } ) ;
143+ // Track pipeline indentation increases per PipelineAst instead of as a single
144+ // flat counter. A flat counter caused all accumulated pipeline indentation to be
145+ // subtracted when *any* pipeline ended, instead of only the contribution from
146+ // that specific pipeline — leading to runaway indentation with nested pipelines.
147+ var pipelineIndentationIncreases = new Dictionary < PipelineAst , int > ( ) ;
136148 /*
137149 When an LParen and LBrace are on the same line, it can lead to too much de-indentation.
138150 In order to prevent the RParen code from de-indenting too much, we keep a stack of when we skipped the indentation
@@ -188,18 +200,33 @@ caused by tokens that require a closing RParen (which are LParen, AtParen and Do
188200 if ( pipelineIndentationStyle == PipelineIndentationStyle . IncreaseIndentationAfterEveryPipeline )
189201 {
190202 AddViolation ( token , indentationLevel ++ , diagnosticRecords , ref onNewLine ) ;
191- currentIndenationLevelIncreaseDueToPipelines ++ ;
203+ // Attribute this increase to the innermost pipeline containing
204+ // this pipe token so it is only reversed when that specific
205+ // pipeline ends, not when an unrelated outer pipeline ends.
206+ PipelineAst containingPipeline = FindInnermostContainingPipeline ( pipelineAsts , token ) ;
207+ if ( containingPipeline != null )
208+ {
209+ if ( ! pipelineIndentationIncreases . ContainsKey ( containingPipeline ) )
210+ pipelineIndentationIncreases [ containingPipeline ] = 0 ;
211+ pipelineIndentationIncreases [ containingPipeline ] ++ ;
212+ }
192213 break ;
193214 }
194215 if ( pipelineIndentationStyle == PipelineIndentationStyle . IncreaseIndentationForFirstPipeline )
195216 {
196- bool isFirstPipeInPipeline = pipelineAsts . Any ( pipelineAst =>
197- PositionIsEqual ( LastPipeOnFirstLineWithPipeUsage ( ( PipelineAst ) pipelineAst ) . Extent . EndScriptPosition ,
198- tokens [ tokenIndex - 1 ] . Extent . EndScriptPosition ) ) ;
199- if ( isFirstPipeInPipeline )
217+ // Capture which specific PipelineAst this is the first pipe for,
218+ // so the indentation increase is attributed to that pipeline only.
219+ PipelineAst firstPipePipeline = pipelineAsts
220+ . Cast < PipelineAst > ( )
221+ . FirstOrDefault ( pipelineAst =>
222+ PositionIsEqual ( LastPipeOnFirstLineWithPipeUsage ( pipelineAst ) . Extent . EndScriptPosition ,
223+ tokens [ tokenIndex - 1 ] . Extent . EndScriptPosition ) ) ;
224+ if ( firstPipePipeline != null )
200225 {
201226 AddViolation ( token , indentationLevel ++ , diagnosticRecords , ref onNewLine ) ;
202- currentIndenationLevelIncreaseDueToPipelines ++ ;
227+ if ( ! pipelineIndentationIncreases . ContainsKey ( firstPipePipeline ) )
228+ pipelineIndentationIncreases [ firstPipePipeline ] = 0 ;
229+ pipelineIndentationIncreases [ firstPipePipeline ] ++ ;
203230 }
204231 }
205232 break ;
@@ -290,8 +317,13 @@ caused by tokens that require a closing RParen (which are LParen, AtParen and Do
290317 if ( pipelineIndentationStyle == PipelineIndentationStyle . IncreaseIndentationForFirstPipeline ||
291318 pipelineIndentationStyle == PipelineIndentationStyle . IncreaseIndentationAfterEveryPipeline )
292319 {
293- indentationLevel = ClipNegative ( indentationLevel - currentIndenationLevelIncreaseDueToPipelines ) ;
294- currentIndenationLevelIncreaseDueToPipelines = 0 ;
320+ // Only subtract the indentation contributed by this specific pipeline,
321+ // leaving contributions from outer/unrelated pipelines intact.
322+ if ( pipelineIndentationIncreases . TryGetValue ( matchingPipeLineAstEnd , out int contribution ) )
323+ {
324+ indentationLevel = ClipNegative ( indentationLevel - contribution ) ;
325+ pipelineIndentationIncreases . Remove ( matchingPipeLineAstEnd ) ;
326+ }
295327 }
296328 }
297329
@@ -432,6 +464,32 @@ private static PipelineAst MatchingPipelineAstEnd(List<Ast> pipelineAsts, Token
432464 return matchingPipeLineAstEnd ;
433465 }
434466
467+ /// <summary>
468+ /// Finds the innermost (smallest) PipelineAst whose extent fully contains the given token.
469+ /// Used to attribute pipeline indentation increases to the correct pipeline when
470+ /// using IncreaseIndentationAfterEveryPipeline.
471+ /// </summary>
472+ private static PipelineAst FindInnermostContainingPipeline ( List < Ast > pipelineAsts , Token token )
473+ {
474+ PipelineAst best = null ;
475+ int bestSize = int . MaxValue ;
476+ foreach ( var ast in pipelineAsts )
477+ {
478+ var pipeline = ( PipelineAst ) ast ;
479+ int pipelineStart = pipeline . Extent . StartOffset ;
480+ int pipelineEnd = pipeline . Extent . EndOffset ;
481+ int pipelineSize = pipelineEnd - pipelineStart ;
482+ if ( pipelineStart <= token . Extent . StartOffset &&
483+ token . Extent . EndOffset <= pipelineEnd &&
484+ pipelineSize < bestSize )
485+ {
486+ best = pipeline ;
487+ bestSize = pipelineSize ;
488+ }
489+ }
490+ return best ;
491+ }
492+
435493 private static bool PositionIsEqual ( IScriptPosition position1 , IScriptPosition position2 )
436494 {
437495 return position1 . ColumnNumber == position2 . ColumnNumber &&
0 commit comments