feat(repomap): index C#/Godot-Mono (.cs) type symbols

Conservative type-declaration pattern (class/interface/struct/enum/record);
no method capture to avoid false positives. (BACKLOG I)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-20 09:32:02 +00:00
parent 266bbf0269
commit f715ffa540
2 changed files with 42 additions and 0 deletions
@@ -84,6 +84,12 @@ class RepoMapIndexer(
"ts" to Regex("""(?m)^\s*(?:export\s+)?(?:default\s+)?(?:async\s+)?(?:function|class|interface|type|enum)\s+(?<name>[A-Za-z_$][A-Za-z0-9_$]*)"""),
// GDScript: column-0 declarations only, so indented inner-class members never match.
"gd" to Regex("""(?m)^(?:static\s+)?(?:func|class_name|class|signal|enum)\s+(?<name>[A-Za-z_][A-Za-z0-9_]*)"""),
// C# / Godot Mono: top-level type declarations with leading modifiers, kept conservative
// (no method capture — return-type heuristics there are noisy and risk false positives).
"cs" to Regex(
"""(?m)^\s*(?:public |private |protected |internal |static |sealed |abstract |""" +
"""partial )*(?:class|interface|struct|enum|record)\s+(?<name>[A-Za-z_][A-Za-z0-9_]*)""",
),
// Markdown: h1h3 headings as "symbols" — deeper levels are noise relative to retrieval value.
"md" to Regex("""(?m)^#{1,3}\s+(?<name>.+?)\s*$"""),
)
@@ -75,6 +75,42 @@ class RepoMapIndexerTest {
assertFalse(entry.symbols.contains("help"), "indented inner method must not be matched")
}
@Test
fun `extracts C# (Godot Mono) top-level type symbols`(@TempDir root: Path) {
root.resolve("Player.cs").writeText(
"""
using Godot;
namespace Game;
public sealed partial class Player : CharacterBody2D
{
private int _health = 100;
public void TakeDamage(int amount)
{
_health -= amount;
}
}
internal interface IDamageable
{
void TakeDamage(int amount);
}
public enum State { Idle, Running }
""".trimIndent(),
)
val entry = RepoMapIndexer().index(root).single()
assertEquals("Player.cs", entry.path)
assertTrue(
entry.symbols.containsAll(listOf("Player", "IDamageable", "State")),
"missing symbols in: ${entry.symbols}",
)
assertFalse(entry.symbols.contains("TakeDamage"), "method/field bodies must not be matched")
}
@Test
fun `extracts markdown headings as symbols`(@TempDir root: Path) {
root.resolve("design.md").writeText(