Show links to full ty diagnostics - #462
#462Show links to full ty diagnostics#462AlexWaygood merged 2 commits intomainastral-sh/ty-vscode:mainfrom alex/full-compiler-diagnosticastral-sh/ty-vscode:alex/full-compiler-diagnosticCopy head branch name to clipboard
Conversation
Advertise the experimental fullDiagnosticOutput capability and replace the code of each diagnostic for which the server provides full output with a “Click for full diagnostic” link. Back each link with a virtual text document containing the server-rendered plain text. Append any existing rule-documentation URL to that virtual document so replacing the diagnostic code with a link does not hide access to the rule documentation. Support push, document-pull, and workspace-pull diagnostics. Store rendered contents in the virtual-document provider instead of reading the current diagnostic array by index because pull reports can be rejected by vscode-languageclient result-ID precedence logic. Tag each converted pull report with an extension-local ID, and commit its rendered contents to the cache only after transformed diagnostics for that same ID appear in the VS Code diagnostic collection. This prevents discarded pull results from overwriting current content while retaining the simple index-based virtual URI scheme. Handle workspace pulls directly in middleware because vscode-languageclient 9 captures its original reporter in a closure; replacing the reporter therefore cannot decorate reports before the library applies its result-ID precedence check. Serialize conversion of workspace-pull partial results, discard results from superseded or cancelled requests, and feed converted reports through the original reporter to preserve the built-in precedence behavior. Clear cached virtual documents on server restart and extension disposal. Add a link from the extension contributor documentation to the server protocol documentation.
|
I saw the test plan in the ty PR but I think it only showcases what the diagnostic look like and the flow with the default extension settings. Can you try using workspace diagnostics as well just to make sure it's working well? Also, can you open multiple full diagnostic windows either of the same diagnostic or multiple diagnostics? Does this re-use the same window for displaying diagnostics or opens another window? If it's the latter, can you make sure those windows are not rendering the same diagnostics and the contents are actually different (just to make sure there's no sync between these diagnostic windows). I can try to think of other scenarios but this is what comes to my mind right now. |
|
For workspace diagnostics. What's very important is that we only send new diagnostics or clients will start to lag. That's maybe something worth testing |
|
Here's what it looks like with multiple diagnostics open at once: Screen.Recording.2026-06-25.at.14.56.44.mov |
|
Here's what happens if you try to open the same diagnostic multiple times or if the diagnostic goes stale: Screen.Recording.2026-06-25.at.15.01.51.mov |
|
Here's a video using the feature in homeassistant with workspace diagnostics enabled. It doesn't feel laggy at all to me and it seems to be working great: Screen.Recording.2026-06-25.at.17.01.16.movI also did some testing to see if it started to lag when I added some new diagnostics and it also seemed fine to me. The "problems" count at the bottom of the screen stayed the same at 63,000 thorughout |
…eature (#26269) ## Summary Fixes astral-sh/ty#1731. See astral-sh/ty-vscode#462 for the companion ty-vscode PR. This PR implements a parallel to rust-analyzer's "Click for full compiler diagnostic feature" in its VSCode extension. Here's a video of rust-analyzer's feature in action in VSCode: https://github.com/user-attachments/assets/7c14c7d6-46ea-4716-9f05-4df032cdc913 The feature is implemented by allowing LSP clients to advertise an experimental `fullDiagnosticOutput` capability. For clients that advertise this capability, we include both a plain-text rendering and the original diagnostic identifier in `Diagnostic.data`; otherwise, we retain the existing fix-only payload and avoid the rendering cost. The one (significant) part of rust-analyzer's feature that is not implemented in this PR is full color rendering of diagnostics. This is left out of scope for this PR, as it would have led to a significant increase in code, and can be done as a followup. ## Prior art in other LSPs rust-analyzer is the best known LSP that provides this kind of feature. Similar features are also found in [Flow](https://github.com/facebook/flow/blob/main/packages/flow-for-vscode/src/FlowLanguageClient/DetailedDiagnostics.ts) and [wsgl-analyzer](https://github.com/wgsl-analyzer/wgsl-analyzer/blob/main/editors/code/src/client.ts). Pyrefly has [experimental support for Markdown rendering](https://github.com/facebook/pyrefly/blob/3ca46218e4d5b63f0d079449d93376d535682795/pyrefly/lib/lsp/non_wasm/server.rs#L681-L765) in tooltips, but this is an orthogonal feature. Here's codex's summary of the differences between the two features. <details> <summary>Codex comparison</summary> ## Full compiler diagnostics and Markdown diagnostic messages ty’s full-diagnostic feature and Pyrefly’s Markdown diagnostic-message support improve diagnostic presentation at different layers and are largely complementary. The ty feature follows the model established by rust-analyzer. The language server preserves the ordinary concise `Diagnostic.message` but attaches a second, substantially richer representation in `Diagnostic.data.rendered`. This rendering can contain source snippets, annotated spans, notes, and other multiline context that would be unwieldy inside a normal diagnostic hover. ty-vscode detects the additional data and replaces the displayed diagnostic code with a **Click for full diagnostic** link. Following the link opens the rendered report in a read-only virtual document. This requires bespoke client middleware and a virtual-document provider, but clients that do not understand the extension can safely ignore the additional `data` and continue displaying the standard concise diagnostic. Repurposing `Diagnostic.code` as a link means that ty must preserve the original diagnostic identifier elsewhere. It therefore includes `data.diagnostic_id`, which allows the server to recover identifiers such as `invalid-argument-type` when processing subsequent code-action requests. The protocol is documented in [ty’s language-server documentation](https://github.com/astral-sh/ruff/blob/9f12f376467bd84e0aff651c09860e5bdbc8fc52/crates/ty_server/README.md#full-diagnostic-output), while the client-side transformation can be seen in [ty-vscode’s diagnostic handling](https://github.com/astral-sh/ty-vscode/blob/48fa1b6567b2deaf923f5724a9d58a6bea2266eb/src/common/diagnostics.ts#L139-L172). rust-analyzer uses essentially the same client-side design. Its VS Code extension reads a compiler rendering from `Diagnostic.data.rendered`, changes the diagnostic code into a link, and opens the report through a custom URI. The implementation is in [rust-analyzer’s VS Code client](https://github.com/rust-lang/rust-analyzer/blob/master/editors/code/src/client.ts), and the protocol is described under [Colored Diagnostic Output](https://rust-analyzer.github.io/book/contributing/lsp-extensions.html#colored-diagnostic-output). One distinction is that rust-analyzer receives these reports from rustc through check-on-save and can request ANSI styling, whereas ty renders its own diagnostics and the current MVP emits plain text. Pyrefly’s Markdown diagnostic-message support has a different purpose. Rather than providing a second, expanded representation, it changes the content type of the existing `Diagnostic.message`. When the client advertises `textDocument.diagnostic.markupMessageSupport`, Pyrefly sends the message as: { "kind": "markdown", "value": "..." } This uses the proposed LSP 3.18 extension that permits `Diagnostic.message` to contain `MarkupContent`. A compatible client can render the Markdown directly in its ordinary diagnostic UI without any Pyrefly-specific virtual-document provider. Clients that do not advertise support continue receiving a standard string. Pyrefly currently uses this mechanism primarily to render backtick-delimited identifiers and types as inline </details> ## Implementation details We keep the identifier in `Diagnostic.data` because clients may replace `Diagnostic.code` with a link to the rendered report. We teach code actions to recover the identifier and optional fix from `Diagnostic.data` so replacing the visible diagnostic code with a link does not disable lazy or eager quick fixes. Rendered reports embed source snippets whereas raw diagnostics do not. We therefore include the source text of every file referenced by diagnostic annotations in pull-diagnostic result IDs for opted-in clients, deduplicating files before hashing. This conservatively invalidates cached reports after source-only edits without rendering every diagnostic merely to compute cache keys. To preserve existing behavior, we continue to omit result IDs from empty workspace reports. In [the same way that rust-analyzer does](https://rust-analyzer.github.io/book/contributing/lsp-extensions.html#colored-diagnostic-output) (source code [here](https://github.com/rust-lang/rust-analyzer/blob/master/docs/book/src/contributing/lsp-extensions.md#colored-diagnostic-output)), we document the experimental protocol explicitly, describing how clients can declare support for it. ## Test Plan Tests have been added covering push and pull output, source-sensitive cache invalidation, and code actions after the visible diagnostic code has been replaced with a link. And here is a manual test: https://github.com/user-attachments/assets/ed65ade1-a111-49be-b8a1-ce357286f33e To reproduce the above manual test, clone https://github.com/AlexWaygood/ty-full-diagnostic-demo and follow the instructions in that repo's README.md.
…Protocol (#3855) ## Summary This documents the experimental LSP extension added in astral-sh/ruff#26269 / astral-sh/ty-vscode#462
## Context This builds on the original **Click for full diagnostic** feature implemented in [astral-sh/ruff#26269](astral-sh/ruff#26269) and [#462](#462). The colored rendering follows the approach established by [rust-lang/rust-analyzer#13848](rust-lang/rust-analyzer#13848). ## Summary - advertise support for colored full diagnostic output - strip ANSI sequences from the virtual document and recreate their styles with VS Code decorations - support named terminal colors, 256-color palette entries, truecolor, and text decorations ## Companion PRs - [astral-sh/ruff#26384](astral-sh/ruff#26384) - [astral-sh/ty#3858](astral-sh/ty#3858) ## Test plan https://github.com/user-attachments/assets/36638263-fe48-4ba7-8abe-95db353f1257
## Context This builds on the original **Click for full diagnostic** feature implemented in [astral-sh/ruff#26269](astral-sh/ruff#26269) and [astral-sh/ty-vscode#462](astral-sh/ty-vscode#462). The colored rendering follows the approach established by [rust-lang/rust-analyzer#13848](rust-lang/rust-analyzer#13848). ## Summary Explain that `fullDiagnosticOutput` can include ANSI colour codes and clients are expected to be able to handle this ## Companion PRs - [astral-sh/ruff#26384](astral-sh/ruff#26384) - [astral-sh/ty-vscode#463](astral-sh/ty-vscode#463)
## Context This builds on the original **Click for full diagnostic** feature implemented in [#26269](#26269) and [astral-sh/ty-vscode#462](astral-sh/ty-vscode#462). The colored rendering follows the approach established by [rust-lang/rust-analyzer#13848](rust-lang/rust-analyzer#13848). ## Summary - render full ty diagnostics with ANSI color and styling when the client opts in - negotiate color support separately from `fullDiagnosticOutput` for rollout compatibility - disable terminal hyperlinks in LSP output and cover the behavior under forced hyperlink support ## Companion PRs - [astral-sh/ty-vscode#463](astral-sh/ty-vscode#463) - [astral-sh/ty#3858](astral-sh/ty#3858) ## Test plan https://github.com/user-attachments/assets/dc7e7538-ae2b-4870-9e9b-f59943ee804a
Summary
This is the ty-vscode companion PR to astral-sh/ruff#26269. See the description on that PR for a full breakdown of the feature and a demo video of it in action.
This was all written and checked by codex. I'm unfamiliar with both TypeScript and writing VSCode extensions, so I didn't even try to review it :-)
Implementation details (Codex-authored)
Advertise the experimental fullDiagnosticOutput capability and replace the code of each diagnostic for which the server provides full output with a “Click for full diagnostic” link. Back each link with a virtual text document containing the server-rendered plain text. Append any existing rule-documentation URL to that virtual document so replacing the diagnostic code with a link does not hide access to the rule documentation.
Support push, document-pull, and workspace-pull diagnostics. Store rendered contents in the virtual-document provider instead of reading the current diagnostic array by index because pull reports can be rejected by vscode-languageclient result-ID precedence logic. Tag each converted pull report with an extension-local ID, and commit its rendered contents to the cache only after transformed diagnostics for that same ID appear in the VS Code diagnostic collection. This prevents discarded pull results from overwriting current content while retaining the simple index-based virtual URI scheme.
Handle workspace pulls directly in middleware because vscode-languageclient 9 captures its original reporter in a closure; replacing the reporter therefore cannot decorate reports before the library applies its result-ID precedence check. Serialize conversion of workspace-pull partial results, discard results from superseded or cancelled requests, and feed converted reports through the original reporter to preserve the built-in precedence behavior.
Clear cached virtual documents on server restart and extension disposal. Add a link from the extension contributor documentation to the server protocol documentation.
Test Plan
See astral-sh/ruff#26269