Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[ty] Implement rust-analyzer's "Click for full compiler diagnostic" feature#26269

Merged
AlexWaygood merged 3 commits into
mainfrom
alex/full-compiler-diagnostic
Jun 25, 2026
Merged

[ty] Implement rust-analyzer's "Click for full compiler diagnostic" feature#26269
AlexWaygood merged 3 commits into
mainfrom
alex/full-compiler-diagnostic

Conversation

@AlexWaygood

@AlexWaygood AlexWaygood commented Jun 23, 2026

Copy link
Copy Markdown
Member

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:

Screen.Recording.2025-12-02.at.19.55.13.mov

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 and wsgl-analyzer.

Pyrefly has experimental support for Markdown rendering in tooltips, but this is an orthogonal feature. Here's codex's summary of the differences between the two features.

Codex comparison

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, while the client-side transformation can be seen in ty-vscode’s diagnostic handling.

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, and the protocol is described under 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

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 (source code here), 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:

Screen.Recording.2026-06-23.at.14.39.07.mov

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.

Allow clients to advertise an experimental fullDiagnosticOutput capability. For clients that advertise this capability, include both a plain-text rendering and the original diagnostic identifier in Diagnostic.data; otherwise retain the existing fix-only payload and avoid the rendering cost. Model full-output and fix-only payloads as separate untagged variants so Rust represents the required field combinations while preserving the existing wire format.

Keep the identifier in Diagnostic.data because clients may replace Diagnostic.code with a link to the rendered report. 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. 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, continue to omit result IDs from empty workspace reports.

Document the experimental protocol and add tests covering push and pull output, source-sensitive cache invalidation, and code actions after the visible diagnostic code has been replaced with a link.
@AlexWaygood AlexWaygood added server Related to the LSP server ty Multi-file analysis & type inference labels Jun 23, 2026
@AlexWaygood AlexWaygood changed the title [ty] Add an LSP extension for full diagnostic output [ty] Implement rust-analyzer's "Click for full compiler diagnostic" feature Jun 23, 2026
@AlexWaygood AlexWaygood marked this pull request as ready for review June 23, 2026 14:38
@AlexWaygood AlexWaygood requested a review from a team as a code owner June 23, 2026 14:38
@astral-sh-bot astral-sh-bot Bot requested a review from dhruvmanila June 23, 2026 14:38
@dhruvmanila

Copy link
Copy Markdown
Member

(Nice, excited to review this tomorrow unless someone else gets to it :))

@dhruvmanila dhruvmanila left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

Comment thread crates/ty_server/README.md Outdated

`ty server` implements the Language Server Protocol for ty's editor integrations.

## LSP extensions

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should add this in https://docs.astral.sh/ty/features/language-server/ to make it more visible.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. That'll have to be a followup PR. I'll remove nearly everything from the README here and just add a link to that page, saying that extensions to the LSP are documented there.

Comment thread crates/ty_server/tests/e2e/publish_diagnostics.rs Outdated
Comment thread crates/ty_server/tests/e2e/pull_diagnostics.rs
Comment on lines -499 to -502
let new_result_id = Diagnostics::result_id_from_hash(&[], &[]);

let report = match new_result_id {
Some(new_id) if new_id == previous_result_id => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, good catch! I was confused to see why is this code being deleted, then I realized that the result_id_from_hash call above is a no-op as it always returns None

Comment thread crates/ty_server/src/server/api/diagnostics.rs
Comment thread crates/ty_server/tests/e2e/code_actions.rs Outdated

`ty server` implements the Language Server Protocol for ty's editor integrations.

Extensions to the protocol are documented in the [ty language server documentation](https://docs.astral.sh/ty/features/language-server/).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I'll add docs there as a followup after this lands, there obviously aren't any there right now)

Apply path-separator filtering in the shared end-to-end test context so every server snapshot uses stable Unix-style separators. This avoids platform-only failures when rendered diagnostics include relative Windows paths.
@AlexWaygood AlexWaygood merged commit ef81835 into main Jun 25, 2026
90 of 91 checks passed
@AlexWaygood AlexWaygood deleted the alex/full-compiler-diagnostic branch June 25, 2026 16:08
AlexWaygood added a commit to astral-sh/ty-vscode that referenced this pull request Jun 25, 2026
## 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
AlexWaygood added a commit to astral-sh/ty that referenced this pull request Jun 25, 2026
…Protocol (#3855)

## Summary

This documents the experimental LSP extension added in
astral-sh/ruff#26269 /
astral-sh/ty-vscode#462
AlexWaygood added a commit to astral-sh/ty-vscode that referenced this pull request Jun 26, 2026
## 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
AlexWaygood added a commit to astral-sh/ty that referenced this pull request Jun 26, 2026
## 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)
AlexWaygood added a commit that referenced this pull request Jun 26, 2026
## 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

server Related to the LSP server ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement rust-analyzer's "Click for full compiler diagnostic" feature

2 participants