Skip to content

LSP indicator: use $/progress notifications instead of semantic-tokens-arrived as indexing-complete signal #450

Description

@JDonaghy

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:

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:

  1. 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).
  2. 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`.
  3. 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.
  4. 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.
  • `src/core/lsp_manager.rs` — pending_work map + `is_indexing` helper + event routing.
  • `src/core/engine/lsp_ops.rs` — replace the semantic-tokens-received gate.
  • `src/core/engine/panels.rs` — handle the new `LspEvent::WorkProgress` variant.

`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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingenhancementNew feature or requestuiUI/rendering

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions