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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
- **Interactive indexing/output no longer goes visually silent after `Indexing...`** — `IndexCommandRunner` now keeps a live `Indexing...` spinner active on interactive terminals during full-scan indexing work instead of stopping it before the first file and leaving the user with a fixed line until the next 50-file progress redraw or warning. Scoped update runs now also keep a live `Updating...` spinner under the same interactive-only contract, pausing only long enough to print `[WARN]`, `[OK]`, `[SKIP]`, `[DEL]`, `[ERR]`, or progress-bar output cleanly and then resuming immediately. Redirected stdout behavior is intentionally unchanged in shape: stdout still gets a single `Indexing...` banner plus line-based progress, while warnings stay on stderr, so logs/pipes do not collect spinner-frame noise. README examples now document both the interactive and redirected-output behavior, and `SELF_IMPROVEMENT.md` now explicitly treats visible liveness for long-running human-facing CLI flows as a regression bar. Added regression coverage ensuring redirected full-scan output prints `Indexing...` exactly once and redirected verbose update output does not duplicate the `Updating ... file(s)...` banner. Affected: `src/CodeIndex/Cli/IndexCommandRunner.cs`, `tests/CodeIndex.Tests/IndexCommandRunnerTests.cs`, `README.md`, `SELF_IMPROVEMENT.md`.

#### Fixed
- **Added explicit regression coverage for the existing C# object-initializer phantom-enum fix (#374)** — Current `main` already suppresses object-initializer assignments such as `Age = 30,` and `Priority = 2` from reappearing as phantom C# enum members, but the open issue's exact dogfood fixture was not pinned end-to-end. Added an extractor-level regression that uses the full issue repro with both multiline and inline `new Person { ... }` initializers, plus a `QueryCommandRunner` integration test that round-trips the indexed file through `symbols --json --exact-name` and asserts `Age` / `Priority` resolve only to the real `Person` properties. This keeps issue #374 closable without relying on an older, narrower unit fixture alone. Affected: `tests/CodeIndex.Tests/SymbolExtractorTests.cs`, `tests/CodeIndex.Tests/QueryCommandRunnerTests.cs`. Closes #374.
- **C# interface and struct members now keep their immediate type container instead of falling back to the enclosing namespace** — `AssignContainers` now treats `interface` and `struct` symbols as container-stack candidates, so members such as interface events and struct properties/events inherit `container_kind` / `container_name` / `container_qualified_name` from the enclosing type rather than the surrounding namespace. Added regression coverage for the #466 interface-event repro and the same-root-cause struct-member case tracked in #468. Affected: `src/CodeIndex/Indexer/SymbolExtractor.cs`, `tests/CodeIndex.Tests/SymbolExtractorTests.cs`. Closes #466.
- **C# interface events now stay attached to their interface container in symbol output** — `AssignContainers` now treats `interface` and `struct` as real symbol containers instead of only `class` / `namespace` / `enum`, so members declared inside them inherit the innermost enclosing type instead of falling back to the surrounding namespace. This fixes the concrete `#334` dogfood fixture where `IBus.Regular`, `IBus.StaticAbs`, and `IBus.StaticVirt` were emitted as namespace-level events in `symbols`, `outline`, and `inspect`, and adds regression coverage that locks both the modifier acceptance and the interface container attribution in place. Affected: `src/CodeIndex/Indexer/SymbolExtractor.cs`, `tests/CodeIndex.Tests/SymbolExtractorTests.cs`. Closes #466.
- **`status --json` now exposes hotspot-family trust signals before `hotspots` runs (#460)** — `StatusResult` now carries `hotspot_family_ready` plus `hotspot_family_degraded_reason`, `DbReader.GetStatus()` populates those fields from the existing hotspot-family readiness signal, and the one-line status summary now marks the index as degraded when duplicate-name hotspot families are not yet authoritative. To keep the operational guidance consistent, `cdidx index` JSON/human readiness output and degraded-readiness warnings now surface the same hotspot-family trust bit as well. Added CLI and MCP regression coverage so automation can discover degraded hotspot-family metadata up front instead of probing `hotspots --json` first. Affected: `src/CodeIndex/Cli/IndexCommandRunner.cs`, `src/CodeIndex/Cli/QueryCommandRunner.cs`, `src/CodeIndex/Database/DbReader.cs`, `src/CodeIndex/Database/DbWriter.cs`, `src/CodeIndex/Models/QueryResults.cs`, `tests/CodeIndex.Tests/IndexCommandRunnerTests.cs`, `tests/CodeIndex.Tests/QueryCommandRunnerTests.cs`, `tests/CodeIndex.Tests/McpServerTests.cs`, `README.md`, `DEVELOPER_GUIDE.md`, `CLAUDE.md`. Closes #460.
Expand Down Expand Up @@ -763,6 +764,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
- **対話ターミナルで `Indexing...` の後に視覚的に無音化しないよう改善** — `IndexCommandRunner` は、フルスキャン index の最初のファイル処理前に `Indexing...` スピナーを止めて次の 50 ファイル進捗更新または警告まで固定行のまま放置するのではなく、対話ターミナルでは重い処理中も `Indexing...` スピナーを維持するようになった。`--files` / `--commits` の scoped update も同じ interactive-only 契約で `Updating...` スピナーを維持し、`[WARN]` / `[OK]` / `[SKIP]` / `[DEL]` / `[ERR]` / プログレスバーを表示する直前だけ一時停止し、表示後すぐ再開する。stdout をリダイレクトした場合の形は意図的に維持しており、stdout には従来どおり `Indexing...` を 1 回だけ出し、その後は行ベースの進捗だけを出し、警告は引き続き stderr に分離するため、ログや pipe にスピナーフレームが混ざらない。README には interactive / redirected の両挙動を追記し、`SELF_IMPROVEMENT.md` にも「長時間の人間向け CLI では visible liveness を保つ」ことを回帰基準として明文化した。加えて、redirected full-scan 出力で `Indexing...` が 1 回だけ出ること、redirected verbose update で `Updating ... file(s)...` バナーが重複しないことを固定する回帰テストを追加。対象: `src/CodeIndex/Cli/IndexCommandRunner.cs`、`tests/CodeIndex.Tests/IndexCommandRunnerTests.cs`、`README.md`、`SELF_IMPROVEMENT.md`。

#### 修正
- **既存の C# object initializer 由来 phantom enum 抑止に issue #374 の明示回帰を追加** — 現在の `main` は `Age = 30,` や `Priority = 2` のような object initializer 代入を phantom C# enum member として再出力しないが、open の issue 本文そのものの dogfood fixture は end-to-end で固定されていなかった。multiline / inline の `new Person { ... }` を両方含む issue 本文相当の full repro を使う extractor-level 回帰に加え、index 後に `symbols --json --exact-name` まで通して `Age` / `Priority` が本物の `Person` property にしか解決しないことを固定する `QueryCommandRunner` integration test を追加した。これにより、より狭い既存 unit fixture だけに依存せず issue #374 を閉じられる状態を保てる。対象: `tests/CodeIndex.Tests/SymbolExtractorTests.cs`、`tests/CodeIndex.Tests/QueryCommandRunnerTests.cs`。Closes #374。
- **C# の interface / struct メンバーが、外側 namespace ではなく直近の型コンテナを保持するよう修正** — `AssignContainers` で親候補として積める symbol kind に `interface` と `struct` を追加し、interface event や struct property/event の `container_kind` / `container_name` / `container_qualified_name` が、周囲の namespace ではなく定義元の型を指すようにした。#466 の interface event 再現と、同じ根本原因だった struct member ケース(#468)を固定する回帰テストを追加。対象: `src/CodeIndex/Indexer/SymbolExtractor.cs`, `tests/CodeIndex.Tests/SymbolExtractorTests.cs`。Closes #466。
- **C# の interface event がシンボル出力で interface 配下に正しくぶら下がるよう修正** — `AssignContainers` が実コンテナとして扱う kind に `interface` と `struct` を追加し、これまで `class` / `namespace` / `enum` しか親として積まれていなかった問題を修正した。これにより interface / struct 内で宣言された member は外側 namespace へフォールバックせず、最も内側の型に帰属する。具体的には `#334` の dogfood fixture で `IBus.Regular` / `IBus.StaticAbs` / `IBus.StaticVirt` が `symbols` / `outline` / `inspect` で namespace-level event として出ていた不具合が解消され、modifier 受理に加えて interface container 帰属まで固定する回帰テストを追加した。対象: `src/CodeIndex/Indexer/SymbolExtractor.cs`、`tests/CodeIndex.Tests/SymbolExtractorTests.cs`。Closes #466。
- **`status --json` が `hotspots` 実行前でも hotspot-family trust signal を返すよう修正 (#460)** — `StatusResult` に `hotspot_family_ready` と `hotspot_family_degraded_reason` を追加し、`DbReader.GetStatus()` が既存の hotspot-family readiness signal からそれらを埋めるようにした。さらに status の 1 行 summary も duplicate-name hotspot family が authoritative でない間は degraded を示すように更新した。運用導線を揃えるため、`cdidx index` の JSON/人間向け readiness 出力と degraded-readiness warning も同じ hotspot-family trust bit を出すようにした。これにより automation は `hotspots --json` を先に叩かなくても hotspot-family metadata の劣化を検知できる。CLI / MCP の回帰テストも追加。対象: `src/CodeIndex/Cli/IndexCommandRunner.cs`, `src/CodeIndex/Cli/QueryCommandRunner.cs`, `src/CodeIndex/Database/DbReader.cs`, `src/CodeIndex/Database/DbWriter.cs`, `src/CodeIndex/Models/QueryResults.cs`, `tests/CodeIndex.Tests/IndexCommandRunnerTests.cs`, `tests/CodeIndex.Tests/QueryCommandRunnerTests.cs`, `tests/CodeIndex.Tests/McpServerTests.cs`, `README.md`, `DEVELOPER_GUIDE.md`, `CLAUDE.md`. Closes #460。
Expand Down
78 changes: 78 additions & 0 deletions tests/CodeIndex.Tests/QueryCommandRunnerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4736,6 +4736,84 @@ public enum Second
}
}

[Fact]
public void RunSymbols_CSharpExactNameDoesNotReturnObjectInitializerNumericAssignments_Issue374()
{
var projectRoot = TestProjectHelper.CreateTempProject("cdidx_symbols_csharp_object_initializer_issue374");
try
{
Directory.CreateDirectory(Path.Combine(projectRoot, "src"));
File.WriteAllText(
Path.Combine(projectRoot, "src", "fixture.cs"),
"""
using System.Collections.Generic;

namespace CsObjInitPhantom;

public class Person
{
public string Name { get; set; } = "";
public int Age { get; set; }
public int Priority { get; set; }
}

public class Creator
{
public Person CreatePerson() => new Person
{
Name = "Alice",
Age = 30,
Priority = 1
};

public List<Person> CreateMany() => new()
{
new Person { Name = "Bob", Age = 25, Priority = 2 },
new Person
{
Name = "Carol",
Age = 45,
Priority = 3
}
};
}
""");

var dbPath = Path.Combine(projectRoot, ".cdidx", "codeindex.db");
var (indexExitCode, _, indexStderr) = CaptureConsole(() => IndexCommandRunner.Run(
[projectRoot, "--json"],
_jsonOptions));
var (ageExitCode, ageStdout, ageStderr) = CaptureConsole(() => QueryCommandRunner.RunSymbols(
["Age", "--db", dbPath, "--json", "--exact-name", "--lang", "csharp"],
_jsonOptions));
var (priorityExitCode, priorityStdout, priorityStderr) = CaptureConsole(() => QueryCommandRunner.RunSymbols(
["Priority", "--db", dbPath, "--json", "--exact-name", "--lang", "csharp"],
_jsonOptions));

var ageRows = ParseJsonLines(ageStdout);
var priorityRows = ParseJsonLines(priorityStdout);

Assert.Equal(CommandExitCodes.Success, indexExitCode);
Assert.Equal(string.Empty, indexStderr);
Assert.Equal(CommandExitCodes.Success, ageExitCode);
Assert.Equal(string.Empty, ageStderr);
Assert.Equal(CommandExitCodes.Success, priorityExitCode);
Assert.Equal(string.Empty, priorityStderr);

Assert.Single(ageRows);
Assert.Equal("property", ageRows[0].RootElement.GetProperty("kind").GetString());
Assert.Equal("Person", ageRows[0].RootElement.GetProperty("container_name").GetString());

Assert.Single(priorityRows);
Assert.Equal("property", priorityRows[0].RootElement.GetProperty("kind").GetString());
Assert.Equal("Person", priorityRows[0].RootElement.GetProperty("container_name").GetString());
}
finally
{
TestProjectHelper.DeleteDirectory(projectRoot);
}
}

[Fact]
public void RunSymbols_ExactNameStaleCSharpCanonicalNamesReportDegradedState()
{
Expand Down
64 changes: 64 additions & 0 deletions tests/CodeIndex.Tests/SymbolExtractorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6634,6 +6634,70 @@ public void Extract_CSharp_EnumMemberDoesNotMatchObjectInitializer()
Assert.DoesNotContain(symbols, s => s.Name == "Email");
}

[Fact]
public void Extract_CSharp_Issue374_ObjectInitializerNumericAssignmentsDoNotCreatePhantomSymbols()
{
// Closes #374: the full issue repro uses both multiline and inline object initializers
// with numeric assignments like `Age = 30,` and `Priority = 2`. Those lines must not
// reappear as phantom enum-member symbols just because they share the old `Name = 1,`
// surface shape.
// Closes #374: issue 本文の再現ケースでは `Age = 30,` や `Priority = 2` のような
// 数値代入を含む multiline / inline object initializer が混在する。旧来の
// `Name = 1,` 形に見えても phantom enum-member symbol を再発させてはいけない。
var content = """
using System.Collections.Generic;

namespace CsObjInitPhantom;

public class Person
{
public string Name { get; set; } = "";
public int Age { get; set; }
public int Priority { get; set; }
}

public class Creator
{
public Person CreatePerson() => new Person
{
Name = "Alice",
Age = 30,
Priority = 1
};

public List<Person> CreateMany() => new()
{
new Person { Name = "Bob", Age = 25, Priority = 2 },
new Person
{
Name = "Carol",
Age = 45,
Priority = 3
}
};
}
""";
var symbols = SymbolExtractor.Extract(1, "csharp", content);

var ageSymbols = symbols.Where(s => s.Name == "Age").ToList();
Assert.Single(ageSymbols);
Assert.Equal("property", ageSymbols[0].Kind);
Assert.Equal("Person", ageSymbols[0].ContainerName);

var prioritySymbols = symbols.Where(s => s.Name == "Priority").ToList();
Assert.Single(prioritySymbols);
Assert.Equal("property", prioritySymbols[0].Kind);
Assert.Equal("Person", prioritySymbols[0].ContainerName);

var nameSymbols = symbols.Where(s => s.Name == "Name").ToList();
Assert.Single(nameSymbols);
Assert.Equal("property", nameSymbols[0].Kind);
Assert.Equal("Person", nameSymbols[0].ContainerName);

Assert.Contains(symbols, s => s.Kind == "function" && s.Name == "CreatePerson" && s.ContainerName == "Creator");
Assert.Contains(symbols, s => s.Kind == "function" && s.Name == "CreateMany" && s.ContainerName == "Creator");
}

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