Skip to content

feat(lsp): use bulk workspace/diagnostic pull instead of per-file file-walk in runWorkspaceDiagnostics #235

Description

@apmantza

Summary

runWorkspaceDiagnostics (clients/lsp/index.ts) computes project-wide diagnostics by walking every LSP-supported source file under the root (collectWorkspaceDiagnosticFiles) and issuing a per-file touchFile(... {diagnostics:"document", clientScope:"all"}) across WORKSPACE_DIAGNOSTICS_CONCURRENCY workers. Its own comment calls this "intentionally expensive." It is — every file must be opened (didOpen) and individually pulled/awaited.

The LSP spec (3.17+) defines a bulk workspace/diagnostic pull: a single request that returns diagnostics for the entire project from the server's already-built model, without opening every file. tsserver, pyright/basedpyright, and rust-analyzer all advertise diagnosticProvider.workspaceDiagnostics: true.

We already detect the capability — client.ts populates workspaceDiagnosticsSupport.mode = "pull" and applyDynamicCapabilities honors a dynamic workspace/diagnostic registration — but we never issue the bulk request. The detected capability is only used to choose per-file pull-vs-push; the project-wide path still brute-forces the file walk.

Proposal

Add a workspaceDiagnostic() client method that issues a real workspace/diagnostic request (with previousResultIds for the incremental report form), and have runWorkspaceDiagnostics prefer it when the active server advertises workspaceDiagnostics, falling back to the current file-walk when it does not (or for files no server claims).

Expected win: project-wide diagnostics without N didOpens — the server returns the whole report it has already computed. Also sidesteps the read-your-writes freshness gymnastics (relates to #179; #180 closed) since the server owns the project model.

Notes / caveats

  • Servers may return RelatedFullDocumentDiagnosticReport vs unchanged partials — need result-id caching to exploit the incremental form.
  • Some servers stream via $/progress (partial result) — the client must assemble partials.
  • Coverage gap: bulk pull only covers files the server's project model includes; mixed-language roots still need the per-file fallback for the other languages. Keep the walk as the fallback, not a removal.
  • Source: LSP 3.18 feature-gap audit. Cheap sibling wins (typeDefinition, declaration) already shipped; this is the high-value, higher-effort item left.

Acceptance

  • workspaceDiagnostic() client method (full + incremental report forms, partial-result assembly)
  • runWorkspaceDiagnostics prefers bulk pull when advertised, falls back to the file walk otherwise
  • result-id cache for the unchanged/incremental path
  • tests: bulk-pull preferred when advertised; fallback when not; mixed-language root still covered

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:diagnosticsDiagnostics model, surfacing, suppressionarea:lspLSP servers & integrationarea:perfPerformance / hot-path / event loopenhancementImprove/harden/refactor/perf an existing capability (not net-new)

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions