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
3 changes: 3 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/),
- **Kotlin `enum class` entries now show up as symbols (#320)** — `SymbolExtractor` now captures indented Kotlin enum entries such as `NORTH,`, `OK(200),`, and bodied entries like `RED(0xFF0000) { ... }` as `property` symbols, so enum values and per-entry override members are visible again in `symbols` and related navigation. Added regression coverage for plain, parameterized, and bodied entries, plus the trailing `fun` / `abstract fun` members that follow the enum body. Affected: `src/CodeIndex/Indexer/SymbolExtractor.cs`, `tests/CodeIndex.Tests/SymbolExtractorTests.cs`, `DEVELOPER_GUIDE.md`.
- **TypeScript ambient declarations and overloads now index correctly (#236)** — `SymbolExtractor` now captures `declare function` / `declare const` forms, classifies `type` aliases as `interface`, skips same-line decorators before method parsing, and collapses overload signatures so the implementation row is kept. Added focused regressions for ambient declarations, decorated methods, and overload collapsing. Affected: `src/CodeIndex/Indexer/SymbolExtractor.cs`, `tests/CodeIndex.Tests/SymbolExtractorTests.cs`, `CHANGELOG.md`.
- **Plain C# interpolated strings no longer hide later helpers from symbol extraction (#882)** — `SymbolExtractor` now keeps plain `$"..."` strings that contain nested string literals inside interpolation holes from desynchronizing the C# lexer, so later helpers in the same file keep showing up in `symbols` and `definition`. Added a focused regression for the DbReader-style SQL fixture. Affected: `src/CodeIndex/Indexer/SymbolExtractor.cs`, `tests/CodeIndex.Tests/SymbolExtractorTests.cs`. Closes #882.
- **Kotlin secondary constructors now index as class-named function symbols (#316)** — `SymbolExtractor` now captures `constructor(...)` declarations inside Kotlin class bodies, then normalizes them to the enclosing class name after container assignment so `definition`, `callers`, and `outline` include alternate construction paths. Added regressions for overloaded constructors, brace-less constructor chaining, and private secondary constructors. Affected: `src/CodeIndex/Indexer/SymbolExtractor.cs`, `tests/CodeIndex.Tests/SymbolExtractorTests.cs`, `DEVELOPER_GUIDE.md`. Closes #316.
- **Java generic bounds now surface real bound references, and real short type names no longer get suppressed by generic-parameter spelling heuristics (#642, #644)** — `ReferenceExtractor` now indexes Java type-parameter bounds such as `class Demo<T extends Root & Bound>` and `<U extends Wrapper<Root>>`, keeping the bound types visible as `type_reference` edges. At the same time, the old spelling-based generic-parameter filter no longer hides real short names like `X` or `TResult`, so C# declaration positions keep surfacing genuine type names instead of dropping them as presumed generic parameters. Added focused regressions for Java generic bounds, Java nested generic bounds, and short C# type names. Affected: `src/CodeIndex/Indexer/ReferenceExtractor.cs`, `tests/CodeIndex.Tests/ReferenceExtractorTests.cs`.
- **C# nested lambda declaration-pattern shadowing stays scoped to the lambda, preserving outer qualified enum-member edges (#562)** — Added a tighter regression for the `values.Any(value => value is Holder RealNs && values.Any(other => other is Holder Other))` shape so the declaration-pattern receiver stays inside the nested lambda and the outer `RealNs.Status.Ready` references remain visible in both extractor-level and CLI exact-name JSON paths. This pins the same shadowing family as the existing lambda and static-lambda regressions, but exercises a deeper lambda nesting shape that better matches the reported false negative. Affected: `tests/CodeIndex.Tests/ReferenceExtractorTests.cs`, `tests/CodeIndex.Tests/QueryCommandRunnerTests.cs`.
- **`references --count` now stays count-like for the C# `using static` suppression path, and `--max-line-width` no longer changes which rows are suppressed (#688, #699)** — The exact C# `using static` fallback now keeps the count path on a leaner raw-row query instead of paying for ordered search-result pagination, so `references --count` no longer drags in the full search shape just to compute totals. The same suppression path continues to inspect the raw database context before any line-width clamping, so display-width options cannot resurrect a false positive. Added regressions that cover the count total on a suppressed-heavy fixture and the tiny-width CLI repro. Affected: `src/CodeIndex/Database/DbReader.cs`, `tests/CodeIndex.Tests/DbReaderTests.cs`, `tests/CodeIndex.Tests/QueryCommandRunnerTests.cs`. Closes #688, #699.
Expand Down Expand Up @@ -985,8 +986,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
### [Unreleased]

#### 修正
- **`cdidx find` が `--max-line-width 0` を no-truncation mode として受け付けるようになりました (#980)** — `find` が `0` を usage error として拒否していましたが、CLI の互換契約では `0` は「truncation を無効化する」扱いです。`--max-line-width` の検証を non-negative integer parser に通すようにしたため、`0` が再び有効になり、他の positive-only 制限値は従来どおり維持されます。
- **Kotlin の `enum class` エントリが symbols に現れるようになりました (#320)** — `SymbolExtractor` が `NORTH,`、`OK(200),`、`RED(0xFF0000) { ... }` のような Kotlin のインデント付き enum エントリを `property` として拾うようになり、enum 値と entry ごとの override メンバーが `symbols` と関連ナビゲーションに再び出るようになりました。plain / parameterized / bodied の各形と、enum 本体の後ろに続く `fun` / `abstract fun` の回帰テストを追加しています。対象: `src/CodeIndex/Indexer/SymbolExtractor.cs`、`tests/CodeIndex.Tests/SymbolExtractorTests.cs`、`DEVELOPER_GUIDE.md`。
- **TypeScript の ambient declaration と overload 行のインデックスを修正 (#236)** — `SymbolExtractor` が `declare function` / `declare const` を再びインデックスし、`type` エイリアスを `interface` として扱い、同一行の decorator 付きメンバーを取りこぼさず、関数オーバーロードの signature 行を implementation 1 行に畳みます。対象: `src/CodeIndex/Indexer/SymbolExtractor.cs`、`tests/CodeIndex.Tests/SymbolExtractorTests.cs`、`CHANGELOG.md`。
- **Kotlin のセカンダリコンストラクタがクラス名の function シンボルとして索引化されるようになりました (#316)** — `SymbolExtractor` が Kotlin の class body 内の `constructor(...)` 宣言を取り込み、container 確定後に外側クラス名へ正規化することで、`definition`、`callers`、`outline` に代替コンストラクタ経路が出るようにしました。overload 付きコンストラクタ、brace-less な constructor chaining、private な secondary constructor を固定する regression を追加しました。対象: `src/CodeIndex/Indexer/SymbolExtractor.cs`、`tests/CodeIndex.Tests/SymbolExtractorTests.cs`、`DEVELOPER_GUIDE.md`。Closes #316。
- **Java の generic bounds が実際の bound 参照を出力し、generic-parameter の綴りだけで実名の短い型名を抑制しなくなりました (#642, #644)** — `ReferenceExtractor` が `class Demo<T extends Root & Bound>` や `<U extends Wrapper<Root>>` のような Java の type-parameter bounds を `type_reference` として索引し、bound 側の型を可視化するようになりました。同時に、綴りベースの generic-parameter フィルタを廃止し、`X` や `TResult` のような実在の短い型名が generic parameter と誤認されて落ちることを防ぎます。Java の generic bounds と短い C# 型名の回帰テストを追加しました。対象: `src/CodeIndex/Indexer/ReferenceExtractor.cs`、`tests/CodeIndex.Tests/ReferenceExtractorTests.cs`。
- **ネストした lambda 内の declaration pattern でも outer `if` body を汚染しないように再確認 (#562)** — `values.Any(value => value is Holder RealNs && values.Any(other => other is Holder Other))` の形を追加回帰で固定し、declaration-pattern の receiver が nested lambda 内に閉じたままで outer の `RealNs.Status.Ready` 参照が `references` / `inspect` / CLI の exact-name JSON で落ちないことを確認しました。既存の lambda / static-lambda 回帰と同じ shadowing 系ですが、より深いネストを含む形を追加で押さえています。対象: `tests/CodeIndex.Tests/ReferenceExtractorTests.cs`、`tests/CodeIndex.Tests/QueryCommandRunnerTests.cs`。
- **`references --count` が C# `using static` 抑止経路でも count-like のままになり、`--max-line-width` で抑止判定が変わらなくなりました (#688, #699)** — exact C# `using static` の fallback count でも、順序付きの search-result pagination を経由せず raw-row ベースの軽い count query を使うようになり、`references --count` が total を数えるだけの場面で余計な結果 payload を抱え込まなくなりました。抑止ロジックは引き続き raw database context を基準に判定するため、表示用の line width を変えても false positive が復活しません。抑止-heavy な fixture での count total と、狭い `--max-line-width` を使う CLI 再現を固定する regression を追加しました。対象: `src/CodeIndex/Database/DbReader.cs`、`tests/CodeIndex.Tests/DbReaderTests.cs`、`tests/CodeIndex.Tests/QueryCommandRunnerTests.cs`。Closes #688, #699。
Expand Down
8 changes: 8 additions & 0 deletions DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,11 @@ Supported symbol kinds by language (33 languages with symbol extraction):
| Go | func, methods | type alias | struct | interface | -- | -- | -- | import | yes |
| Rust | fn, macro_rules!, const, static | impl, type alias | struct, union | trait | enum | -- | -- | use | yes |
| Java | methods (including same-line leading annotations, lexer-aware annotation arguments such as `@Label(")")` and `@SuppressWarnings({"unchecked"})`, same-line `@interface` members with later same-line siblings such as `value(); int age();`, compact constructors in both same-line and Allman-style brace layouts even when the record header contains annotation-array arguments, same-line brace-bodied siblings, and same-line enum-constant-body methods), static final, enum members (body-scoped scanner that tracks strings/chars/comments/text blocks and stops at the first top-level `;`, so method calls like `\tRED();` outside the enum body are not captured; enum constants with anonymous bodies retain body ranges so nested overrides, helper methods, and same-line body-local methods attach to the enum-member container) | class, record, sealed, @interface, `module-info.java` module declarations | -- | interface | enum | record primary components | -- | import (including `requires`, `exports`, `opens`, `uses`, `provides` in `module-info.java`) | yes |
<<<<<<< HEAD
| Kotlin | fun, extension fun, secondary constructor (`constructor(...)`) | class, object, companion (anonymous companions normalize to `Companion`), data/sealed/value class | -- | interface | enum class | val/var | -- | import | yes |
=======
| Kotlin | fun, extension fun | class, object, companion (anonymous companions normalize to `Companion`), data/sealed/value class | -- | interface | enum class, enum entries | val/var | -- | import | yes |
>>>>>>> origin/main
| Ruby | def, Rails DSL | class, module | -- | -- | -- | attr_accessor/reader/writer | -- | require | yes |
| C | functions | -- | struct | -- | enum | -- | -- | #include | yes |
| C++ | functions | class | struct | -- | enum, enum class | -- | -- | #include | yes |
Expand Down Expand Up @@ -1515,7 +1519,11 @@ LIMIT 20;
| Go | func, メソッド | 型エイリアス | struct | interface | -- | -- | -- | import | yes |
| Rust | fn, macro_rules!, const, static | impl, type alias | struct, union | trait | enum | -- | -- | use | yes |
| Java | メソッド(同一行の先頭アノテーション付き、`@Label(")")` や `@SuppressWarnings({"unchecked"})` のような annotation 引数、`value(); int age();` のような同一行 `@interface` メンバーの後続 sibling、annotation-array 引数を含む record header や opening brace が次行へ送られた形も含む compact constructor、同一行 brace-body sibling、same-line enum 定数 body 内メソッドを含む), static final, enum メンバー(文字列・char・コメント・text block を追跡する body-scoped scanner で抽出し、最初の top-level `;` で停止するため、enum 本体外の `\tRED();` のようなメソッド呼び出しを誤検出しない。匿名 body を持つ enum 定数は body range も保持し、入れ子の override / helper メソッドと same-line body-local method が enum 定数コンテナにぶら下がる) | class, record, sealed, @interface, `module-info.java` の module 宣言 | -- | interface | enum | record primary component | -- | import(`module-info.java` 内の `requires` / `exports` / `opens` / `uses` / `provides` を含む) | yes |
<<<<<<< HEAD
| Kotlin | fun, 拡張関数, セカンダリコンストラクタ(`constructor(...)`) | class, object, companion(無名 companion は `Companion` に正規化), data/sealed/value class | -- | interface | enum class | val/var | -- | import | yes |
=======
| Kotlin | fun, 拡張関数 | class, object, companion(無名 companion は `Companion` に正規化), data/sealed/value class | -- | interface | enum class, enum エントリ | val/var | -- | import | yes |
>>>>>>> origin/main
| Ruby | def, Rails DSL | class, module | -- | -- | -- | attr_accessor/reader/writer | -- | require | yes |
| C | 関数 | -- | struct | -- | enum | -- | -- | #include | yes |
| C++ | 関数 | class | struct | -- | enum, enum class | -- | -- | #include | yes |
Expand Down
30 changes: 30 additions & 0 deletions src/CodeIndex/Indexer/SymbolExtractor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1016,6 +1016,8 @@ private enum JavaScriptTypeScriptFunctionHeaderConsumeResult
// Function / 関数 (including extension, override, and abstract forms)
// 関数 — 拡張・override・abstract 形を含む
new("function", new Regex(@"^\s*(?<visibility>public|private|protected|internal)?\s*(?:(?:suspend|inline|infix|operator|tailrec|external|expect|actual|abstract|override)\s+)*fun\s+(?:\w+(?:<[^>]+>)?\.)?(?<name>\w+)\s*[\(<](?:.*?\))?(?::\s*(?<returnType>[^ {=]+))?", RegexOptions.Compiled), BodyStyle.Brace, "visibility", "returnType"),
// Secondary constructor / セカンダリコンストラクタ
new("function", new Regex(@"^\s*(?<visibility>public|private|protected|internal)?\s*constructor\s*\(", RegexOptions.Compiled), BodyStyle.Brace, "visibility"),
// Enum entry / enum エントリ
new("property", new Regex(@"^\s{2,}(?<name>[A-Z][A-Z0-9_]*)\s*(?:\((?<returnType>[^)]*)\))?\s*(?:,|\{|;)?\s*$", RegexOptions.Compiled), BodyStyle.Brace, "returnType"),
// Top-level val/var property / トップレベルプロパティ
Expand Down Expand Up @@ -2770,6 +2772,7 @@ public static List<SymbolRecord> Extract(long fileId, string? lang, string conte

AssignContainers(symbols, lines, csharpLineStartStates);
MaterializeRecordPrimaryComponentSymbols(symbols, pendingRecordPrimaryComponents);
NormalizeKotlinSecondaryConstructorNames(symbols);
PopulateDeclaredContainerQualifiedNames(symbols);
return symbols;
}
Expand Down Expand Up @@ -19273,6 +19276,33 @@ private static string NormalizeKotlinSymbolName(string name, string matchLine)
: name;
}

private static void NormalizeKotlinSecondaryConstructorNames(List<SymbolRecord> symbols)
{
foreach (var symbol in symbols)
{
if (symbol.Kind != "function"
|| symbol.ContainerKind != "class"
|| string.IsNullOrWhiteSpace(symbol.ContainerName))
{
continue;
}

var signature = symbol.Signature?.TrimStart();
if (string.IsNullOrWhiteSpace(signature))
continue;

var isSecondaryConstructor = signature.StartsWith("constructor", StringComparison.Ordinal)
|| signature.StartsWith("public constructor", StringComparison.Ordinal)
|| signature.StartsWith("private constructor", StringComparison.Ordinal)
|| signature.StartsWith("protected constructor", StringComparison.Ordinal)
|| signature.StartsWith("internal constructor", StringComparison.Ordinal);
if (!isSecondaryConstructor)
continue;

symbol.Name = symbol.ContainerName;
}
}

private static string NormalizeSqlSymbolName(string name)
{
if (string.IsNullOrWhiteSpace(name))
Expand Down
2 changes: 1 addition & 1 deletion tests/CodeIndex.Tests/ReferenceExtractorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1013,7 +1013,7 @@ class Derived extends Base {
Assert.DoesNotContain(references, reference => reference.SymbolName == "import");
Assert.DoesNotContain(references, reference => reference.SymbolName == "super");
Assert.DoesNotContain(references, reference => reference.SymbolName == "yield");
Assert.Contains(references, reference => reference.SymbolName == "require" && reference.ContainerName == "constructor");
Assert.Contains(references, reference => reference.SymbolName == "require" && reference.ContainerName == "Derived");
}

[Fact]
Expand Down
29 changes: 29 additions & 0 deletions tests/CodeIndex.Tests/SymbolExtractorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10726,6 +10726,35 @@ public void Extract_Kotlin_DetectsFunctionsAndClasses()
Assert.Contains(symbols, s => s.Kind == "function" && s.Name == "process");
}

[Fact]
public void Extract_Kotlin_DetectsSecondaryConstructors()
{
var content = """
class Person(val name: String) {
var age: Int = 0

constructor(name: String, age: Int) : this(name) { this.age = age }

constructor() : this("anonymous", 0)

fun greet(): String = "Hi $name"
}

class Box<T>(val value: T) {
private constructor(list: List<T>) : this(list.first())
}
""";

var symbols = SymbolExtractor.Extract(1, "kotlin", content);

Assert.Contains(symbols, s => s.Kind == "class" && s.Name == "Person");
Assert.Contains(symbols, s => s.Kind == "class" && s.Name == "Box");
Assert.Contains(symbols, s => s.Kind == "function" && s.Name == "Person" && s.ContainerName == "Person" && s.Signature == "constructor(name: String, age: Int) : this(name) { this.age = age }");
Assert.Contains(symbols, s => s.Kind == "function" && s.Name == "Person" && s.ContainerName == "Person" && s.Signature == "constructor() : this(\"anonymous\", 0)");
Assert.Contains(symbols, s => s.Kind == "function" && s.Name == "Box" && s.ContainerName == "Box" && s.Visibility == "private" && s.Signature == "private constructor(list: List<T>) : this(list.first())");
Assert.DoesNotContain(symbols, s => s.Kind == "function" && s.Name == "constructor");
}

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