Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions changelog.d/unreleased/2710.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
category: fixed
issues:
- 2710
- 2711
- 2717
affected:
- src/CodeIndex/Indexer/Symbols/SymbolExtractor.cs
- tests/CodeIndex.Tests/SymbolExtractorTests.cs
---

## English

- **Full self-indexing no longer stalls while extracting C# symbols (#2710, #2711, #2717)** — C# symbol extraction now reuses the per-line multi-line member candidate during pattern matching, avoiding repeated expensive lookahead on large source files.

## 日本語

- **C# シンボル抽出中に full self-indexing が停止しないようになりました (#2710, #2711, #2717)** — C# シンボル抽出はパターン照合中に行単位の複数行メンバー候補を再利用し、大きなソースファイルで高コストな先読みを繰り返さないようになりました。
3 changes: 2 additions & 1 deletion src/CodeIndex/Indexer/Symbols/SymbolExtractor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2447,6 +2447,7 @@ public static List<SymbolRecord> Extract(long fileId, string? lang, string conte
{
var stopAfterFirstPatternMatch = false;
var restartPatternScanOffset = -1;
CSharpPropertyMatchCandidate? csharpPropertyCandidateForLine = null;
foreach (var pattern in patterns)
{
if (lang == "csharp" && ReferenceEquals(pattern.Regex, CSharpEnumMemberRegex))
Expand All @@ -2463,7 +2464,7 @@ public static List<SymbolRecord> Extract(long fileId, string? lang, string conte
// function パターンは `CSharpPropertyHeaderPrefixRegex` が `(` や `{` を含む行を
// 受け付けないため影響を受けず、merger は元の行をそのまま返す。Closes #355.
var csharpPropertyCandidate = lang == "csharp" && pattern.Kind is "property" or "function"
? BuildCSharpPropertyMatchLine(lines, csharpMatchLines!, i)
? csharpPropertyCandidateForLine ??= BuildCSharpPropertyMatchLine(lines, csharpMatchLines!, i)
: new CSharpPropertyMatchCandidate(matchLine, i, i);
var patternMatchLine = csharpPropertyCandidate.MatchLine;
if (fortranContinuationCandidate != null)
Expand Down
22 changes: 22 additions & 0 deletions tests/CodeIndex.Tests/SymbolExtractorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24399,6 +24399,28 @@ public void Extract_CSharp_InstallScriptFixture_CompletesWithinPracticalBudget()
$"InstallScriptTests.cs extraction took {stopwatch.Elapsed.TotalSeconds:F2}s, expected < {runawayBudget.TotalSeconds:F0}s runaway guard budget.");
}

[Fact]
public void Extract_CSharp_ReferenceExtractorFixture_CompletesWithinPracticalBudget()
{
// issue #2710/#2711/#2717 regression: full self-indexing could spend minutes
// repeatedly rebuilding the same multi-line C# member candidate while scanning large
// extractor sources. Keep this as a broad runaway guard for the realistic file that
// reproduced the stall on origin/main.
var path = Path.Combine(GetRepositoryRoot(), "src", "CodeIndex", "Indexer", "References", "ReferenceExtractor.cs");
var content = File.ReadAllText(path);

var stopwatch = Stopwatch.StartNew();
var symbols = SymbolExtractor.Extract(1, "csharp", content);
stopwatch.Stop();

Assert.Contains(symbols, s => s.Kind == "class" && s.Name == "ReferenceExtractor");
Assert.Contains(symbols, s => s.Kind == "function" && s.Name == "Extract");
var runawayBudget = TimeSpan.FromSeconds(30);
Assert.True(
stopwatch.Elapsed < runawayBudget,
$"ReferenceExtractor.cs extraction took {stopwatch.Elapsed.TotalSeconds:F2}s, expected < {runawayBudget.TotalSeconds:F0}s runaway guard budget.");
}

[Fact]
public void Extract_Java_SameLineAnnotationsCompactConstructorsAndEnumOverrides_StayIndexed()
{
Expand Down
Loading