# Workspace Diagnostics Deep Dive > [Docs Index](README.md) • [Repo README](../README.md) • [Performance Guide](performance-and-debugging.md) • [Testing & CI](testing-and-ci.md) Workspace diagnostics allow clients to request a consolidated snapshot of diagnostics across every tracked document. Volar includes built-in support, but when you author custom servers or plugins you may need to understand the full protocol to extend or debug it. This guide covers request/response semantics, result IDs, incremental updates, and best practices. ## Why Workspace Diagnostics? - Provide a single “Problems” list for projects with hundreds of files without opening each file. - Enable CLI tooling (`volar check`) to return the same diagnostics as the editor. - Support `workspace/diagnostic` clients (VS Code Insiders, Neovim, Sublime LSP) that fetch diagnostics on demand. ## Protocol Primer ### Request: `workspace/diagnostic` ```ts connection.languages.diagnostics.onWorkspace(async (params, token) => { // params.previousResultIds – known results from last run }); ``` Parameters: - `identifier`: optional string set by the client during registration; echo it back in responses if present. - `previousResultIds`: array of `{ uri: string, value: string }` – tells the server what diagnostics the client already has. ### Response: `WorkspaceDiagnosticReport` ```ts type WorkspaceDiagnosticReport = { items: WorkspaceDocumentDiagnosticReport[]; }; type WorkspaceDocumentDiagnosticReport = | WorkspaceFullDocumentDiagnosticReport | WorkspaceUnchangedDocumentDiagnosticReport; type WorkspaceFullDocumentDiagnosticReport = { kind: 'full'; uri: string; version: number | null; items: Diagnostic[]; resultId?: string; }; type WorkspaceUnchangedDocumentDiagnosticReport = { kind: 'unchanged'; uri: string; version: number | null; resultId: string; }; ``` - `resultId`: opaque string used to identify a diagnostic result. If you send `kind: 'unchanged'`, you must supply the previous `resultId`. ## Implementation Patterns ### 1. Full Refresh Every Time Simplest approach (used in our JSON/YAML example): ```ts connection.languages.diagnostics.onWorkspace(async (_params, token) => { const items = []; for (const doc of server.documents.all()) { if (token.isCancellationRequested) break; items.push({ uri: doc.uri, kind: DocumentDiagnosticReportKind.Full, version: doc.version ?? null, items: await collectDiagnostics(doc), }); } return { items }; }); ``` - Pros: Easy to implement, no result IDs to track. - Cons: Potentially expensive for large workspaces; clients will reprocess every diagnostic each run. ### 2. Incremental with `resultId` Track diagnostics per document: ```ts const workspaceResults = new Map(); connection.languages.diagnostics.onWorkspace(async (params, token) => { const items: WorkspaceDocumentDiagnosticReport[] = []; for (const doc of server.documents.all()) { if (token.isCancellationRequested) break; const cached = workspaceResults.get(doc.uri); const previousResultId = params.previousResultIds?.find((p) => p.uri === doc.uri)?.value; if (cached && cached.resultId === previousResultId && !doc.versionChangedSince(cached.resultId)) { items.push({ uri: doc.uri, kind: 'unchanged', version: doc.version ?? null, resultId: cached.resultId, }); continue; } const diagnostics = await collectDiagnostics(doc); const resultId = crypto.randomUUID(); workspaceResults.set(doc.uri, { resultId, items: diagnostics }); items.push({ uri: doc.uri, kind: 'full', version: doc.version ?? null, items: diagnostics, resultId, }); } return { items }; }); ``` - Pros: Clients skip reprocessing unchanged documents; faster on subsequent runs. - Cons: Requires caching `resultId` + diagnostics and invalidating cache when files change. ### When to Invalidate 1. Document opened/changed – remove cached entry and recompute on next workspace request. 2. Configuration change (e.g., schema update) – bump all `resultId`s to ensure clients refresh. 3. Project reload – clear cache entirely. ## Progress & Partial Results The protocol supports work-done progress and partial results. To implement: ```ts connection.languages.diagnostics.onWorkspace(async (params, token, _workDone, resultProgress) => { for (const doc of server.documents.all()) { if (token.isCancellationRequested) break; const report = ...buildReport(doc)...; resultProgress?.report({ items: [report] }); } return { items: [] }; // or final aggregated array }); ``` Use partial results for very large workspaces so clients receive incremental updates instead of waiting for completion. ## Volar Built-in Behavior `@volar/language-server` already implements workspace diagnostics for TypeScript-based projects when `workspaceDiagnostics` capability is enabled. If you use `createTypeScriptProject`, you get: - Automatic `resultId` management. - Honor for `previousResultIds`. - Partial result streaming. However, if you build custom projects (e.g., JSON/YAML server) you need to implement the handler yourself, as shown above. ## Best Practices 1. **Version numbers** – include the document’s version (or `null` if unknown). Helps clients detect stale reports. 2. **Respect cancellation** – workspace diagnostics can be expensive; break when the token is canceled. 3. **Throttle frequency** – only run workspace diagnostics on explicit requests or when the user triggers a “Refresh” command. Avoid running them on every change by default. 4. **Surface progress** – pair workspace diagnostics with a work-done progress notification so users see progress for large workspaces. 5. **Log durations** – record how long workspace diagnostics take; emit telemetry (`workspaceDiagnostics.run`) to spot regressions. 6. **Tie into CLI** – reuse the same diagnostics pipeline for CLI tools (`volar check`) to ensure consistent results. ## Debugging Workspace Diagnostics - Enable protocol tracing (`VOLAR_TRACE=protocol`) and watch `workspace/diagnostic` traffic. - Verify `resultId` usage: when the client provides previous IDs, ensure you respond with `kind: 'unchanged'` if nothing changed. - Check that `collectDiagnostics` returns the same results as `textDocument/publishDiagnostics`; any divergence indicates a caching or mapping issue. By understanding the workspace diagnostics protocol and building on Volar’s primitives, you can provide lightning-fast, up-to-date diagnostic summaries for any workspace size, both in editors and in automated tooling.