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
94 changes: 94 additions & 0 deletions DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,54 @@ inlay hints are emitted only when the indexed return type is not already written
before the declaration identifier, so explicit method return, local, and field
types remain suppressed.

LSP symbol and completion kinds come from one ordinal internal-kind mapping so
document symbols, workspace symbols, and completion items cannot drift. The
deliberate mappings and fallbacks are:

| Internal kinds | LSP `SymbolKind` | LSP `CompletionItemKind` |
|---|---|---|
| `base_image`, `class`, `service`, `specialization`, `type` | `Class` | `Class` |
| `record`, `struct`, `union` | `Struct` | `Struct` |
| `annotation`, `implements`, `interface`, `protocol`, `trait` | `Interface` | `Interface` |
| `enum` | `Enum` | `Enum` |
| Enum-entry shapes stored as `enum`, `function`, or `property` under an enum container | `EnumMember` | `EnumMember` |
| `function` or `method` identified by constructor subkind, declaration keyword, dedicated initializer name, or a language-valid type-name declaration | `Constructor` | `Constructor` |
| `method`, `test.method`, `accessor`, `class_hook` | `Method` | `Method` |
| `add`, `async_function`, `async_generator`, `copy`, `delegate`, `function`, `generator`, `hook`, `lambda`, `procedure`, `route`, `run`, `shell`, `subroutine` | `Function` | `Function` |
| `attribute`, `expose`, `property`, `stopsignal` | `Property` | `Property` |
| `field`, `volume` | `Field` | `Field` |
| `event` | `Event` | `Event` |
| `constant` | `Constant` | `Constant` |
| `operator` | `Operator` | `Operator` |
| `associatedtype`, `type_parameter`, `typealias` | `TypeParameter` | `TypeParameter` |
| `array` | `Array` | `Value` |
| `assembly`, `file_module`, `import`, `module`, `program`, `project`, `stage`, `submodule` | `Module` | `Module` |
| `namespace` | `Namespace` | `Module` |
| `package` | `Package` | `Module` |
| `block data`, `layout`, `object`, `protocol_impl`, `rule` | `Object` | `Class` |
| `reference` | `Variable` | `Reference` |
| `build_arg`, `environment`, `user`, `variable` | `Variable` | `Variable` |
| `value` | `Variable` | `Value` |
| `code` | `String` | `Text` |
| `anchor` | `Key` | `Reference` |
| `heading`, `label` | `Key` | `Text` |
| `workdir` | `Module` | `Folder` |
| Non-catalog `parameter` and unknown/plugin-defined kinds | `Variable` | `Variable` |

Constructors and enum members are semantic refinements because extractors reuse
broader persisted kinds. Constructor detection honors explicit subkind/keyword
metadata, dedicated initializer names, and type-name constructors only in
languages where that declaration shape is valid; same-name methods in other
languages, return-typed same-name methods, JavaScript object/static `constructor`
members, shell functions, and finalizers stay functions. This includes named
Dart constructors, Java compact record constructors, C# verbatim constructor
identifiers, case-insensitive Pascal constructor keywords, and Visual Basic
`New`. Enum-entry detection recognizes decorated, escaped (including C# verbatim
identifiers), indirect, and comma-grouped persisted shapes across C#, Java,
Kotlin, PHP, and Swift while keeping a nested enum as an enum declaration. The current persisted
catalog has no standalone `parameter` kind; the conservative fallback keeps
legacy or plugin-provided parameter-like symbols compatible.

Document/workspace symbol providers advertise work-done support and honor
bounded string/integer `partialResultToken` and `workDoneToken` values. Partial
results preserve the provider's deterministic order and use `$/progress`
Expand Down Expand Up @@ -3807,6 +3855,52 @@ identifier を確認して、古い不正確な column にも対応する。sour
indexed return type が declaration identifier の前に明示されていない場合だけ返すため、method の
明示 return type、local、field の明示型は表示しない。

LSP の symbol kind と completion kind は、ordinal 比較する1つの internal-kind mapping から導出する。
これにより document symbol、workspace symbol、completion item の分類がずれない。意図的な mapping と
fallback は次のとおりである。

| internal kind | LSP `SymbolKind` | LSP `CompletionItemKind` |
|---|---|---|
| `base_image`, `class`, `service`, `specialization`, `type` | `Class` | `Class` |
| `record`, `struct`, `union` | `Struct` | `Struct` |
| `annotation`, `implements`, `interface`, `protocol`, `trait` | `Interface` | `Interface` |
| `enum` | `Enum` | `Enum` |
| enum container 配下で `enum`、`function`、`property` として保存される enum-entry 形状 | `EnumMember` | `EnumMember` |
| constructor subkind、declaration keyword、専用 initializer 名、または言語上有効な型名 declaration で識別される `function` / `method` | `Constructor` | `Constructor` |
| `method`, `test.method`, `accessor`, `class_hook` | `Method` | `Method` |
| `add`, `async_function`, `async_generator`, `copy`, `delegate`, `function`, `generator`, `hook`, `lambda`, `procedure`, `route`, `run`, `shell`, `subroutine` | `Function` | `Function` |
| `attribute`, `expose`, `property`, `stopsignal` | `Property` | `Property` |
| `field`, `volume` | `Field` | `Field` |
| `event` | `Event` | `Event` |
| `constant` | `Constant` | `Constant` |
| `operator` | `Operator` | `Operator` |
| `associatedtype`, `type_parameter`, `typealias` | `TypeParameter` | `TypeParameter` |
| `array` | `Array` | `Value` |
| `assembly`, `file_module`, `import`, `module`, `program`, `project`, `stage`, `submodule` | `Module` | `Module` |
| `namespace` | `Namespace` | `Module` |
| `package` | `Package` | `Module` |
| `block data`, `layout`, `object`, `protocol_impl`, `rule` | `Object` | `Class` |
| `reference` | `Variable` | `Reference` |
| `build_arg`, `environment`, `user`, `variable` | `Variable` | `Variable` |
| `value` | `Variable` | `Value` |
| `code` | `String` | `Text` |
| `anchor` | `Key` | `Reference` |
| `heading`, `label` | `Key` | `Text` |
| `workdir` | `Module` | `Folder` |
| catalog 外の `parameter` および未知または plugin 定義の kind | `Variable` | `Variable` |

constructor と enum member は extractor が広い永続化 kind を再利用するため、metadata に基づいて
意味を詳細化する。constructor は明示 subkind / keyword、専用 initializer 名、およびその declaration
形状が有効な言語だけで型名 constructor を認識する。他言語の同名 method と finalizer は function の
ままにする。戻り値型付きの同名 method、JavaScript object / static の `constructor` member、shell
function も同様である。これには Dart の named constructor、Java の compact record constructor、
C# の逐語識別子を使う constructor、大文字小文字を区別しない Pascal の constructor keyword、
Visual Basic の `New` も含む。enum entry は C#、Java、Kotlin、PHP、Swift における annotation 付き、
C# の逐語識別子を含む escape 済み、indirect、comma-grouped の保存形状を認識し、enum 内の nested
enum は enum declaration のままにする。現在の永続化 catalog には独立した
`parameter` kind がないため、legacy または plugin が提供する parameter 相当の symbol は保守的な
fallback で互換性を維持する。

document/workspace symbol provider は work-done 対応を advertise し、上限付きの string /
integer `partialResultToken` と `workDoneToken` を処理する。partial result は provider の
決定的な順序を維持し、1 notification あたり最大100 item・64 KiB JSON body の
Expand Down
2 changes: 2 additions & 0 deletions TESTING_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,7 @@ Use `docs/test-doc-maintenance-plan.md` before moving oversized suites or adding
keeps explicit constructors split across partial declarations in one fixture. Preserve the separate `definition` / `goto --kind function` assertions and the grouped `--kind class --group-partials` assertion so callable constructor results cannot merge back into logical type-family results.
- `LspServerTests.cs`
LSP JSON-RPC behavior and indexed editor semantics. Reference parity coverage must compare LSP locations with the matching CLI candidate bundle, including overload identity and both `includeDeclaration` states. Keep document/workspace identifier ranges and explicit-versus-inferred inlay hints in one source-semantics fixture so persisted-column anchoring, source confirmation, and hint suppression cannot drift independently.
The issue-4870 kind-mapping matrix seeds every registered built-in internal kind plus semantic constructor and enum-member rows, then asserts decoded document-symbol, workspace-symbol, and completion responses. It also feeds real TypeScript, Kotlin, Solidity, Java, Swift, Dart, Visual Basic, Pascal, C#, and Shell extractor output through the shared mapper so keyword, compact, named, dedicated-name, and verbatim-identifier constructors; same-name, return-typed, object/static, and shell non-constructors; decorated, escaped, indirect, and comma-grouped enum entries; and nested enums cannot invert classification. It separately pins the conservative parameter and unknown/plugin fallback. Keep all three protocol surfaces in that one fixture so constructor/import handling and deliberate fallback mappings cannot drift.
Coordinate-validation coverage keeps every supported position provider in one negative/malformed/overflow matrix, with missing-document precedence and missing/reversed inlay ranges in the same read-only fixture. Keep UTF-16 and EOF success controls together, but retain invalid ranged-`didChange` as a separate mutable-state fixture so a rejected notification cannot replace accepted live text (#4869).
C# semantic-token coverage decodes LSP delta coordinates and pairs the same keyword/modifier, namespace/type, method/property/event/enum-member, parameter/variable/field, generic/record-struct, explicit-interface-member, attribute, using-declaration, accessor-local, numeric member-access, escaped/contextual-identifier, interpolation, comma-separated declaration, and declaration-kind fixture with `QueryCommandRunnerTests.RunExcerpt_JsonCSharpSemanticKindsMatchLspClassifier_Issue4852`. Keep both surfaces on the shared source classifier, retain ordering/non-overlap assertions, preserve the narrow excerpt check whose parameter/type-parameter declarations are outside the returned range, and retain `RunExcerpt_JsonCSharpSemanticTokensUseVisibleRangeBudget_Issue4852` so earlier tokens cannot empty a late excerpt while indexed-source reconstruction remains line/character bounded. `HandleMessage_SemanticTokens_FallsBackToIndexedCSharpSymbolsWhenSourceReadFails_Issue4852` keeps indexed declaration tokens available when bounded source loading fails.
Constructor-navigation coverage keeps explicit zero- and one-argument constructors in separate partial files, including a caller beside the zero-argument declaration, plus an implicit partial constructor, a positional record, and an ordinary partial-type reference in one fixture. The adversarial fixtures add static constructors, finalizers, primary-plus-secondary constructors, coexisting generic arities, same-arity overloads, value-type default construction beside explicit constructors, enums, and delegates. Assert both definition and declaration requests so source-site identity, invocation-arity filtering, stable implicit representatives, constructor-only ambiguity, preserved non-class construction, and intentional type-family expansion remain separate.
Expand Down Expand Up @@ -1446,6 +1447,7 @@ dotnet test --filter "FullyQualifiedName~GitHelperTests"
は partial declaration の別ファイルに分かれた明示 constructor を 1 つの fixture にまとめます。callable constructor result が logical type-family result に再び混在しないよう、`definition` / `goto --kind function` の検証と、`--kind class --group-partials` による grouped type の検証を分離したまま維持してください。
- `LspServerTests.cs`
LSP の JSON-RPC 挙動と indexed editor semantics のテスト。reference parity coverage では、overload identity と `includeDeclaration` の両状態を含め、LSP location と対応する CLI candidate bundle を比較してください。document/workspace の identifier range と explicit/inferred inlay hint は 1 つの source-semantics fixture にまとめ、保存済み column の anchoring、source 上の確認、hint 抑制が別々に drift しないようにします。
issue-4870 の kind-mapping matrix は、登録済みの全 built-in internal kind に加えて semantic な constructor と enum-member の row を seed し、decode 済みの document-symbol、workspace-symbol、completion response を検証します。また、実際の TypeScript、Kotlin、Solidity、Java、Swift、Dart、Visual Basic、Pascal、C#、Shell extractor 出力を shared mapper に通し、keyword / compact / named / 専用名 / 逐語識別子 constructor、同名 / 戻り値型付き / object・static / shell の非 constructor、annotation 付き / escape 済み / indirect / comma-grouped enum entry、nested enum の分類が逆転しないようにします。保守的な parameter および未知/plugin fallback は別途固定します。constructor/import の処理と意図的な fallback mapping がずれないよう、3つの protocol surface を同じ fixture に維持してください。
coordinate validation coverage は、対応済みの全 position provider を1つの負数 / malformed / overflow matrix にまとめ、missing document より検証を優先することと、inlay range の欠落 / 逆順も同じ read-only fixture で検証します。UTF-16 と EOF の成功 control は一緒に維持し、不正な range 付き `didChange` は mutable state の独立 fixture として残して、拒否された notification が受理済み live text を置き換えられないことを検証してください(#4869)。
C# semantic-token coverage は LSP の delta 座標を decode し、同じ keyword/modifier、namespace/type、method/property/event/enum-member、parameter/variable/field、generic/record struct、explicit interface member、attribute、using declaration、accessor local、numeric member access、escaped/contextual identifier、interpolation、comma-separated declaration、declaration kind の fixture を `QueryCommandRunnerTests.RunExcerpt_JsonCSharpSemanticKindsMatchLspClassifier_Issue4852` と対にします。両 surface が shared source classifier を使い続け、順序と非 overlap の assertion、parameter / type-parameter の declaration が返却範囲外にある narrow excerpt の検証、および indexed-source reconstruction を line / character 上限内に保ちながら手前の token によって file 後半の excerpt が空にならないことを保証する `RunExcerpt_JsonCSharpSemanticTokensUseVisibleRangeBudget_Issue4852` を維持してください。`HandleMessage_SemanticTokens_FallsBackToIndexedCSharpSymbolsWhenSourceReadFails_Issue4852` は bounded source loading が失敗しても indexed declaration token を利用できる状態を維持します。
constructor-navigation coverage は、0 引数と 1 引数の明示 constructor を別々の partial file に置き、0 引数 declaration と同じ file の caller、暗黙 partial constructor、positional record、通常の partial-type reference と同じ fixture にまとめます。adversarial fixture には static constructor、finalizer、primary / secondary constructor の併存、同名 type の generic arity 違い、同一 arity overload、明示 constructor と併存する value type の default construction、enum、delegate も含めます。source-site identity、invocation-arity filtering、暗黙 constructor の安定した代表 declaration、constructor だけの ambiguity、class 以外の construction の維持、意図的な type-family expansion が混在しないよう、definition と declaration の両 request を assert してください。
Expand Down
20 changes: 20 additions & 0 deletions changelog.d/unreleased/4870.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
category: fixed
issues:
- 4870
affected:
- src/CodeIndex/Lsp/LspServer.SymbolKinds.cs
- src/CodeIndex/Lsp/LspServer.Protocol.cs
- src/CodeIndex/Lsp/LspServer.NavigationFeatures.cs
- tests/CodeIndex.Tests/LspServerTests.cs
- DEVELOPER_GUIDE.md
- TESTING_GUIDE.md
---

## English

- **LSP symbols and completion items now share exhaustive protocol-kind mapping (#4870)** — imports are reported as modules, constructors as constructors, and every built-in internal symbol kind plus unknown plugin kinds has a deliberate, tested fallback across document symbols, workspace symbols, and completion.

## 日本語

- **LSP の symbol と completion item が網羅的な protocol-kind mapping を共有するようになりました (#4870)** — import は module、constructor は constructor として返し、全 built-in internal symbol kind と未知の plugin kind に対する意図的な fallback を document symbol、workspace symbol、completion の各経路で検証します。
15 changes: 1 addition & 14 deletions src/CodeIndex/Lsp/LspServer.NavigationFeatures.cs
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ private static string BuildCompletionIdentity(SymbolResult symbol)
private static JsonObject ToCompletionItem(SymbolResult symbol, int index) => new()
{
["label"] = symbol.Name,
["kind"] = CompletionItemKind(symbol.Kind),
["kind"] = CompletionItemKind(symbol),
["detail"] = FormatSymbolDetail(symbol),
["sortText"] = index.ToString("D4", CultureInfo.InvariantCulture) + "_" + symbol.Name,
};
Expand Down Expand Up @@ -318,19 +318,6 @@ private static string FormatSymbolDetail(SymbolResult symbol)
: detail[..(MaxDocumentSymbolDetailChars - "...".Length)] + "...";
}

private static int CompletionItemKind(string kind) => kind switch
{
"class" => 7,
"function" or "test.method" => 3,
"property" => 10,
"field" => 5,
"enum" => 13,
"interface" => 8,
"namespace" => 9,
"struct" => 22,
_ => 6,
};

private static void AddDocumentHighlight(JsonArray array, HashSet<string> seenRanges, int startLine, int startColumn, int endLine, int endColumn)
{
var key = string.Join('\0', startLine, startColumn, endLine, endColumn);
Expand Down
19 changes: 3 additions & 16 deletions src/CodeIndex/Lsp/LspServer.Protocol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ private JsonObject ToWorkspaceSymbol(
return new JsonObject
{
["name"] = symbol.Name,
["kind"] = SymbolKind(symbol.Kind),
["kind"] = SymbolKind(symbol),
["location"] = ToLocation(symbol.Path, identifier.Line, identifier.StartColumn, identifier.Line, identifier.EndColumn),
["containerName"] = symbol.ContainerName,
};
Expand All @@ -41,7 +41,7 @@ private JsonObject ToDocumentSymbol(
return new JsonObject
{
["name"] = symbol.Name,
["kind"] = SymbolKind(symbol.Kind),
["kind"] = SymbolKind(symbol),
["range"] = ToRange(rangeStartLine, 1, rangeEndLine, rangeEndColumn),
["selectionRange"] = ToRange(identifier.Line, identifier.StartColumn, identifier.Line, identifier.EndColumn),
["detail"] = TruncateDocumentSymbolDetail(symbol.Signature),
Expand All @@ -57,7 +57,7 @@ private JsonObject ToDocumentSymbolInformation(
return new JsonObject
{
["name"] = symbol.Name,
["kind"] = SymbolKind(symbol.Kind),
["kind"] = SymbolKind(symbol),
["location"] = ToLocation(
symbol.Path,
identifier.Line,
Expand Down Expand Up @@ -129,19 +129,6 @@ private JsonObject ToDocumentSymbolInformation(
["character"] = Math.Max(column - 1, 0),
};

private static int SymbolKind(string kind) => kind switch
{
"class" => 5,
"function" or "test.method" => 12,
"property" => 7,
"field" => 8,
"enum" => 10,
"interface" => 11,
"namespace" => 3,
"struct" => 23,
_ => 13,
};

private static string GetDocumentPath(JsonElement root)
{
var uri = GetTextDocumentUri(root);
Expand Down
Loading
Loading