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
34 changes: 28 additions & 6 deletions DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -427,9 +427,17 @@ Editor integrations can request standard location shapes directly. `definition`,
The `cdidx lsp` server advertises full text document synchronization and keeps
open document text in a bounded in-memory cache only. Position-based providers
must read that live cache before disk so unsaved editor buffers can identify the
requested token, but provider results remain conservative and index-backed:
return empty arrays or null when the database cannot answer safely instead of
inventing language-server analysis.
requested token. Provider results remain conservative and index-backed except
that document symbols for an indexed document may be structurally re-extracted
from the bounded live buffer through the normal language extractor and container
pipeline, using the indexed file's authoritative language rather than
re-detecting it from the path. Live extraction stops at the document-symbol
materialization bound and falls back to indexed symbols when that bounded
extractor is unavailable. Numeric document-version tombstones remain bounded
across live-text eviction and are cleared by `didClose`, so an evicted newer
version cannot be replaced by a stale change. Other providers return empty
arrays or null when the database cannot answer safely instead of inventing
language-server analysis.
`LspServer` uses one lock-protected lifecycle state machine across ordinary
dispatch, the cancellation fast path, and queue-overload responses. Its phases
are before-initialize, initializing, running, shutdown, and exited. Only the
Expand Down Expand Up @@ -468,6 +476,10 @@ container names, container kinds, enclosing ranges, and same-line selection
columns. Same-range members such as positional record properties therefore stay
beneath their declaring type regardless of deterministic presentation order,
while a later same-named container on the line cannot capture an earlier member.
Live document symbols use the same extractor, normalization, and hierarchy
builder as indexed symbols, so a full-text change updates both ranges and
containers together. Numeric document versions must increase; an older or equal
change cannot replace the newest accepted live text.
The stdio reader and the single response worker are separated by a bounded
queue, so `$/cancelRequest` can cancel an active or queued symbol request without
making database-backed request processing concurrent. Cancellation IDs retain
Expand Down Expand Up @@ -3645,9 +3657,15 @@ editor integration は標準的な location 形状を直接要求できる。`de

`cdidx lsp` server は full text document synchronization を advertise し、open document text は
上限付きの in-memory cache にだけ保持する。position-based provider は未保存 editor buffer から
request token を特定できるよう disk より先に live cache を読む必要があるが、provider result は
保守的かつ index-backed のままにする。database が安全に答えられない場合は、language-server
analysis を作り上げず、空配列または null を返す。
request token を特定できるよう disk より先に live cache を読む必要がある。provider result は
保守的かつ index-backed のままとするが、indexed document の document symbol だけは上限付きの
live buffer を通常の language extractor と container pipeline で構造的に再抽出できる。このとき
path から再判定せず、indexed file の authoritative language を使う。live extraction は
document-symbol materialization 上限で停止し、その bounded extractor を利用できない場合は
indexed symbol に fallback する。numeric document-version tombstone は live text の eviction
後も上限付きで保持し、`didClose` で消去するため、evict 済みの新しい version を stale change が
置き換えることはない。それ以外の provider は database が安全に答えられない場合、
language-server analysis を作り上げず、空配列または null を返す。
`LspServer` は通常 dispatch、cancellation fast path、queue-overload response の全経路で、
1 つの lock 保護された lifecycle state machine を使う。phase は before-initialize、
initializing、running、shutdown、exited である。最初の `initialize` request だけが初期化へ
Expand Down Expand Up @@ -3679,6 +3697,10 @@ integer `partialResultToken` と `workDoneToken` を処理する。partial resul
container name・container kind・包含 range・同一行の selection column で親を解決するため、
positional record property のように同じ range を持つ member も決定的な表示順序に左右されず
宣言元 type の配下に留まり、行内で後にある同名 container が前の member を取り込まない。
live document symbol は indexed symbol と同じ extractor、normalization、hierarchy builder を
使うため、full-text change では range と container が一緒に更新される。numeric document version
は増加する必要があり、古い、または同じ version の change は最後に受理した live text を
置き換えられない。
stdio reader と単一 response worker は上限付き queue
で分離するため、database-backed request processing を並行化せずに `$/cancelRequest` で active
または queued symbol request を cancel できる。cancellation ID は JSON 型を保持し、cancel
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ incomplete generation; JSON still reports `status: "partial"`.
|---|---|
| Search and navigation | `search`, `find`, `excerpt`, `symbols`, `definition`, `references`, `callers`, `callees`, `inspect`, `map`, `deps`, `impact`, `unused`, and `hotspots`. See the [command reference](USER_GUIDE.md#command-reference). |
| AI integration | `cdidx mcp` exposes indexed search tools for Claude Code, Cursor, Windsurf, Copilot, Codex, and other MCP clients. See [AI Integration](USER_GUIDE.md#ai-integration). |
| Editor lookup | `cdidx lsp --db .cdidx/codeindex.db` starts a read-only LSP shim for editors that can launch an LSP command. Large document/workspace symbol requests support bounded partial-result chunks, work-done progress, and request cancellation. References use indexed symbol identity, symbol locations select identifiers, and type hints omit explicitly declared types. C# semantic tokens distinguish keywords, modifiers, namespace components, types, fields, methods, and declarations. |
| Editor lookup | `cdidx lsp --db .cdidx/codeindex.db` starts a read-only LSP shim for editors that can launch an LSP command. Large document/workspace symbol requests support bounded partial-result chunks, work-done progress, and request cancellation. Open-document symbols are re-extracted from the latest accepted full-text version, so hierarchy and ranges follow unsaved edits while stale document versions are ignored. References use indexed symbol identity, symbol locations select identifiers, and type hints omit explicitly declared types. C# semantic tokens distinguish keywords, modifiers, namespace components, types, fields, methods, and declarations. |
| Freshness | `status --check`, `--files`, `--commits`, `--changed-between`, and `--watch` keep the DB aligned with the workspace. |
| Validation | `cdidx validate` reports encoding and line-ending issues in indexed files. See [Validate indexed files](USER_GUIDE.md#validate-indexed-files). |
| Language coverage | `cdidx languages --json` is the live capability probe; add `--format count`, `--summary-only`, `--capability <filter>`, `--language`, `--extension`, or `--alias` to narrow output. See [Supported languages](USER_GUIDE.md#supported-languages). |
Expand Down Expand Up @@ -583,7 +583,7 @@ commit し、構造化 `file_errors` を返して partial-result 終了コード
|---|---|
| 検索とナビゲーション | `search`、`find`、`excerpt`、`symbols`、`definition`、`references`、`callers`、`callees`、`inspect`、`map`、`deps`、`impact`、`unused`、`hotspots`。詳細は [コマンドリファレンス](USER_GUIDE.md#コマンドリファレンス)。 |
| AI 連携 | `cdidx mcp` は Claude Code、Cursor、Windsurf、Copilot、Codex などの MCP client に indexed search tool を提供します。詳細は [AIとの連携](USER_GUIDE.md#aiとの連携)。 |
| editor lookup | `cdidx lsp --db .cdidx/codeindex.db` は、LSP command を起動できる editor 向けの read-only LSP shim です。大きな document/workspace symbol request は上限付き partial-result chunk、work-done progress、request cancellation に対応します。reference は indexed symbol identity を使い、symbol location は identifier を選択し、type hint は明示的に宣言された型を省略します。C# semantic token は keyword、modifier、namespace component、type、field、method、declaration を区別します。 |
| editor lookup | `cdidx lsp --db .cdidx/codeindex.db` は、LSP command を起動できる editor 向けの read-only LSP shim です。大きな document/workspace symbol request は上限付き partial-result chunk、work-done progress、request cancellation に対応します。open document の symbol は最後に受理した full-text version から再抽出するため、古い document version を無視しながら未保存の編集に hierarchy と range を追従させます。reference は indexed symbol identity を使い、symbol location は identifier を選択し、type hint は明示的に宣言された型を省略します。C# semantic token は keyword、modifier、namespace component、type、field、method、declaration を区別します。 |
| 鮮度管理 | `status --check`、`--files`、`--commits`、`--changed-between`、`--watch` で DB と workspace を揃えます。 |
| validation | `cdidx validate` は indexed file の encoding / line-ending 問題を報告します。詳細は [Indexed files を validate する](USER_GUIDE.md#indexed-files-を-validate-する)。 |
| 対応言語 | `cdidx languages --json` が live capability probe です。`--language`、`--extension`、`--alias` で 1 行を lookup できます。詳細は [対応言語](USER_GUIDE.md#対応言語)。 |
Expand Down
28 changes: 18 additions & 10 deletions USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2736,9 +2736,13 @@ capped at 4194304 bytes, the session holds at most 64 live documents and
16777216 aggregate live-document bytes, and older entries are evicted when a
budget is exceeded. `textDocument/didChange` processes only the last 64 change
entries in an oversized `contentChanges` array, preserving the latest full-text
update without retaining unbounded intermediate edits. Position-based requests
read the live buffer first, so unsaved edits can drive token lookup without
writing back to the CodeIndex database.
update without retaining unbounded intermediate edits. A full-text change whose
numeric document version is older than or equal to the latest accepted version
does not replace that live buffer. Position-based requests read the live buffer
first, so unsaved edits can drive token lookup without writing back to the
CodeIndex database. `textDocument/documentSymbol` also re-extracts the latest
accepted live text through the normal language extractor and container pipeline;
when no live buffer is available, it falls back to indexed symbols.
Incoming `textDocument.uri` values must be strings, must be absolute `file:`
URIs, and are rejected before URI parsing when they exceed 4096 characters,
matching the MCP resource URI limit and keeping error responses bounded. LSP
Expand All @@ -2761,9 +2765,9 @@ strings are capped at 1000 characters before symbol search runs.
`workspace/symbol` accepts optional numeric `limit` / `maxResults` parameters
and clamps them to 1000 results. `textDocument/documentSymbol` returns
hierarchical `DocumentSymbol` children when container metadata is available,
returns at most 1000 indexed symbols, truncates each `detail` string to 512
characters with `...`, and trims the tree before the result array exceeds
524288 JSON bytes.
returns at most 1000 symbols from the latest accepted live buffer or the index,
truncates each `detail` string to 512 characters with `...`, and trims the tree
before the result array exceeds 524288 JSON bytes.
Both symbol providers advertise work-done progress. Requests may pass bounded
string or integer `partialResultToken` / `workDoneToken` values. With a partial
result token, the server sends deterministic `$/progress` notifications capped
Expand Down Expand Up @@ -5857,8 +5861,11 @@ open buffer は上限付きの in-memory cache に保持されます。各 docum
session 全体では最大 64 live documents / 16777216 aggregate live-document bytes に制限され、
budget を超えた場合は古い entry から evict されます。`textDocument/didChange` は過大な
`contentChanges` array では最後の 64 change entries だけを処理し、unbounded な intermediate edit を
保持せずに最新の full-text update を維持します。position-based request は live buffer を先に読むため、
未保存の編集内容でも CodeIndex database に書き戻さず token lookup に利用できます。
保持せずに最新の full-text update を維持します。numeric document version が最後に受理した version
以下の full-text change は、その live buffer を置き換えません。position-based request は live buffer
を先に読むため、未保存の編集内容でも CodeIndex database に書き戻さず token lookup に利用できます。
`textDocument/documentSymbol` も最後に受理した live text を通常の language extractor と
container pipeline で再抽出し、live buffer がない場合は indexed symbol に fallback します。
受信した `textDocument.uri` は string かつ absolute `file:` URI である必要があり、
4096 文字を超える場合は URI parse の前に拒否されます。これは MCP resource URI の上限と
揃えており、エラー応答が過大にならないようにします。
Expand All @@ -5878,8 +5885,9 @@ invalid request として拒否します。
`workspace/symbol` の query string は symbol search を実行する前に 1000 文字で上限をかけます。
`workspace/symbol` は任意の numeric `limit` / `maxResults` parameter を受け取り、1000 件までに
clamp します。`textDocument/documentSymbol` は container metadata がある場合に階層化された
`DocumentSymbol` children を返し、最大 1000 件の indexed symbol を返し、各 `detail` string を
`...` 付きの 512 文字に切り詰め、result tree が 524288 JSON bytes を超える前に trim します。
`DocumentSymbol` children を返し、最後に受理した live buffer または index から最大 1000 件の
symbol を返し、各 `detail` string を `...` 付きの 512 文字に切り詰め、result tree が
524288 JSON bytes を超える前に trim します。
両方の symbol provider は work-done progress を advertise します。request は上限付きの string
または integer の `partialResultToken` / `workDoneToken` を渡せます。partial-result token が
ある場合、server は決定的な順序の `$/progress` notification を1件あたり最大100 symbol・
Expand Down
21 changes: 21 additions & 0 deletions changelog.d/unreleased/4851.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
category: fixed
issues:
- 4851
affected:
- src/CodeIndex/Lsp/LspLiveDocumentStore.cs
- src/CodeIndex/Lsp/LspServer.cs
- src/CodeIndex/Lsp/LspServer.SymbolRequests.cs
- tests/CodeIndex.Tests/LspServerTests.cs
- README.md
- USER_GUIDE.md
- DEVELOPER_GUIDE.md
---

## English

- **Preserved live LSP document-symbol hierarchy after full document changes (#4851)** — `textDocument/documentSymbol` now re-extracts the latest accepted live buffer with the indexed file's authoritative language and the same container pipeline used for indexed symbols, preserving the hierarchy and ranges fixed in #4736 across successive full-text changes. Live extraction stops at the document-symbol materialization bound and safely falls back to indexed symbols when the extractor is unavailable. Bounded version tombstones prevent stale or duplicate numeric document versions from overwriting newer live text after cache eviction.

## 日本語

- **document 全体の変更後も live LSP document-symbol hierarchy を維持しました(#4851)** — `textDocument/documentSymbol` は indexed file の authoritative language と indexed symbol と同じ container pipeline を使って、最後に受理した live buffer を再抽出するようになり、連続する full-text change の後も #4736 で修正した hierarchy と range を維持します。live extraction は document-symbol materialization 上限で停止し、extractor を利用できない場合は indexed symbol に安全に fallback します。上限付き version tombstone により、cache eviction 後も古い、または重複した numeric document version が新しい live text を上書きしません。
41 changes: 24 additions & 17 deletions src/CodeIndex/Indexer/Symbols/SymbolExtractor.ExtractCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ private static List<SymbolRecord> ExtractCore(
string? filePath = null,
string? projectRoot = null,
bool patternConfigsAlreadyLoaded = false,
CancellationToken cancellationToken = default)
CancellationToken cancellationToken = default,
int? maxSymbols = null)
{
var originalLang = lang;
if (TryPrepareSymbolExtraction(
Expand Down Expand Up @@ -124,7 +125,7 @@ private static List<SymbolRecord> ExtractCore(
var getCSharpSwitchExpressionLines = scanInputs.GetCSharpSwitchExpressionLines;
var getCssQualifiedRuleAncestors = scanInputs.GetCssQualifiedRuleAncestors;
var initialSymbolCapacity = EstimateSymbolListInitialCapacity(lines.Length);
var symbols = new SymbolExtractionList(initialSymbolCapacity);
var symbols = new SymbolExtractionList(initialSymbolCapacity, maxSymbols);
var extractionState = symbols.ExtractionState;
var scanState = new PatternScanState();
List<PendingRecordPrimaryComponents>? pendingRecordPrimaryComponents = null;
Expand All @@ -137,6 +138,9 @@ private static List<SymbolRecord> ExtractCore(
: null;
for (int i = 0; i < lines.Length; i++)
{
if (symbols.IsAtCapacity)
break;

if ((i & 0x3f) == 0)
cancellationToken.ThrowIfCancellationRequested();

Expand Down Expand Up @@ -1238,21 +1242,24 @@ private static List<SymbolRecord> ExtractCore(
}
}

AddSupplementalSymbols(
fileId,
originalLang,
lang,
content,
filePath,
lines,
structuralLines,
symbols,
extractionState,
getPrivateScopeColumns,
GetJavaScriptTypeScriptSanitizedLines,
csharpMatchLines,
pythonModulePrefix,
prologMultilineHeads);
if (!symbols.IsAtCapacity)
{
AddSupplementalSymbols(
fileId,
originalLang,
lang,
content,
filePath,
lines,
structuralLines,
symbols,
extractionState,
getPrivateScopeColumns,
GetJavaScriptTypeScriptSanitizedLines,
csharpMatchLines,
pythonModulePrefix,
prologMultilineHeads);
}
FinalizePatternSymbols(
fileId,
lang,
Expand Down
Loading
Loading