You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The per-window LSP status indicator dims to `name…` while a server is initializing and brightens to `name` once "ready." Defining "ready" cleanly is tricky for language servers like rust-analyzer that have a noticeable indexing phase after the LSP handshake completes:
Original gate (Session 243): `BufferState.semantic_tokens.is_empty()` — wait for the first non-empty semantic-tokens response. Worked well for the common case (cargo project + normal Rust file), but pinned the indicator to dimmed forever for files where the server returns zero tokens (empty file, comment-only file, file outside any workspace). That was #230.
Post-LSP status indicator: 'rust-analyzer...' initializing text never updates after init completes #230 fix: track an explicit `semantic_tokens_received` bool — set true on any semanticTokens response, even empty. Fixed the stuck-on-dimmed bug. But rust-analyzer returns an empty response almost immediately when it doesn't have tokens indexed yet ("nothing for this file yet, still working"), so the indicator now goes bright within ~0.5s even though hover / definition / completion don't actually work until ~30s later for big projects.
So we have:
signal
sticky on empty-token files
accurate for big-project indexing
semantic_tokens.is_empty()
❌ stuck forever
✅ correct
semantic_tokens_received (current)
✅ unsticks
❌ goes bright while still indexing
Neither gate is right because semantic-tokens responses aren't a reliable proxy for "server fully ready."
Proposal: $/progress notifications
rust-analyzer (and gopls, pyright, several other servers) emit LSP `window/workDoneProgress/create` + `$/progress` notifications during workspace indexing. The progress reports `kind: begin → report → end`, and `end` is the actual "indexing complete" signal we want.
Sketch of the change:
LSP event parsing in `src/core/lsp.rs`:
Decode `$/progress` notifications.
Emit a new `LspEvent::WorkProgress { server_id, token, kind, title }` (begin/report/end variants).
LspManager state in `src/core/lsp_manager.rs`:
Track set of in-flight progress tokens per server: `pending_work: HashMap<LspServerId, HashSet>`.
Helper: `is_indexing(server_id) -> bool`.
Status dispatch in `src/core/engine/lsp_ops.rs::lsp_status_for_buffer`:
When server is in handshake-done state, check `mgr.is_indexing(server_id)`. If true, return `Initializing(name)`; if false, return `Running(name)`. Drop the semantic-tokens-based heuristic entirely.
Tests:
Indexing begins on first WorkProgress(begin) → indicator dims.
Indexing ends on WorkProgress(end) → indicator brightens.
Servers that don't send progress notifications (marksman, simple servers) brighten on handshake — current behavior preserved.
Why this matters
Workspace indexing on a fresh cargo project / large monorepo takes 15-60 seconds. During that window, hover / completion / go-to-definition either return nothing or return partial results from the small subset rust-analyzer has indexed. The user has no signal that what they're seeing is incomplete vs. the server simply having nothing to say.
The current post-#230 behavior (bright immediately) trains the user to ignore the indicator entirely, which makes it useless as a signal.
Affected files
`src/core/lsp.rs` — `$/progress` parsing + new event variant.
Background
The per-window LSP status indicator dims to `name…` while a server is initializing and brightens to `name` once "ready." Defining "ready" cleanly is tricky for language servers like rust-analyzer that have a noticeable indexing phase after the LSP handshake completes:
Original gate (Session 243): `BufferState.semantic_tokens.is_empty()` — wait for the first non-empty semantic-tokens response. Worked well for the common case (cargo project + normal Rust file), but pinned the indicator to dimmed forever for files where the server returns zero tokens (empty file, comment-only file, file outside any workspace). That was #230.
Post-LSP status indicator: 'rust-analyzer...' initializing text never updates after init completes #230 fix: track an explicit `semantic_tokens_received` bool — set true on any semanticTokens response, even empty. Fixed the stuck-on-dimmed bug. But rust-analyzer returns an empty response almost immediately when it doesn't have tokens indexed yet ("nothing for this file yet, still working"), so the indicator now goes bright within ~0.5s even though hover / definition / completion don't actually work until ~30s later for big projects.
So we have:
semantic_tokens.is_empty()semantic_tokens_received(current)Neither gate is right because semantic-tokens responses aren't a reliable proxy for "server fully ready."
Proposal: $/progress notifications
rust-analyzer (and gopls, pyright, several other servers) emit LSP `window/workDoneProgress/create` + `$/progress` notifications during workspace indexing. The progress reports `kind: begin → report → end`, and `end` is the actual "indexing complete" signal we want.
Sketch of the change:
LSP event parsing in `src/core/lsp.rs`:
LspManager state in `src/core/lsp_manager.rs`:
Status dispatch in `src/core/engine/lsp_ops.rs::lsp_status_for_buffer`:
Tests:
Why this matters
Workspace indexing on a fresh cargo project / large monorepo takes 15-60 seconds. During that window, hover / completion / go-to-definition either return nothing or return partial results from the small subset rust-analyzer has indexed. The user has no signal that what they're seeing is incomplete vs. the server simply having nothing to say.
The current post-#230 behavior (bright immediately) trains the user to ignore the indicator entirely, which makes it useless as a signal.
Affected files
`BufferState.semantic_tokens_received` can stay around (semantic tokens themselves are still rendered) but no longer drives the indicator.
Surfaced by
PR for #230. The semantic-tokens-received signal is too eager for big-project indexing.