Skip to content

Commit ab4a695

Browse files
committed
Improve pipeline indentation handling in UseConsistentIndentation rule
1 parent 9b55ac2 commit ab4a695

File tree

2 files changed

+431
-9
lines changed

2 files changed

+431
-9
lines changed

Rules/UseConsistentIndentation.cs

Lines changed: 67 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)