Skip to content

fix: Populate diagnostic fields on send codeAction#5341

Open
apatenotre wants to merge 1 commit into
microsoft:mainfrom
apatenotre:fix/emit-diag-fields-on-codeaction
Open

fix: Populate diagnostic fields on send codeAction#5341
apatenotre wants to merge 1 commit into
microsoft:mainfrom
apatenotre:fix/emit-diag-fields-on-codeaction

Conversation

@apatenotre
Copy link
Copy Markdown

@apatenotre apatenotre commented May 25, 2026

[LSP] Round-trip Diagnostic.data and other fields through textDocument/codeAction

Fixes #5342.

LspCodeActionProvider.provideCodeActions was sending context.diagnostics entries containing only range, message, and severity — dropping code, source, tags, relatedInformation, codeDescription, and data. The LSP spec requires data to be round-tripped (this client even advertises dataSupport: true), and real-world servers (e.g. ruff) silently drop quickfix actions when it's missing.

Approach

  1. New DiagnosticsCache (monaco-lsp-client/src/adapters/DiagnosticsCache.ts) keyed by Monaco URI string.
  2. Populated by LspDiagnosticsFeature from both push (publishDiagnostics) and pull (textDocument/diagnostic) paths, including the related-documents branch. Entries removed on model.onWillDisposeModel.
  3. Exposed on LspConnection.diagnosticsCache so any feature can read it.
  4. LspCodeActionProvider.provideCodeActions looks up each marker's matching cached Diagnostic (by range + message in LSP coordinates) and forwards the full object. Markers without a cache hit (set by some other source — e.g. ESLint extension setting markers under a different owner) fall back to a synthesized Diagnostic that still includes code / source / tags / relatedInformation from IMarker.

Cache keying is by Monaco URI (model.uri.toString()) throughout, to keep cleanup trivial via monaco.editor.onWillDisposeModel regardless of whether diagnostics came in via push or pull.

Verification done locally

In a real Next.js + Monaco app using monaco-editor 0.55.1:

  1. Before the fix — WS frames captured in DevTools show outgoing textDocument/codeAction.context.diagnostics entries containing only range, message, severity. Ruff returns 2 source-only actions. Monaco lightbulb shows no quickfixes on the F401 unused-import diagnostic.
  2. After the fix (applied via a local transport-proxy workaround that mirrors what this PR does inside the client) — outgoing entries include code: "F401", source: "Ruff", data: {…}. Ruff returns 4 actions including 2 quickfix.*. "Remove unused import: 'os'" and "Disable for this line" appear in the lightbulb.
  3. Regression check — basedpyright (which doesn't use data) is unchanged; its hover, definition, code actions, and diagnostics all still work.

Notes

  • No test added — monaco-lsp-client/ currently has no test infrastructure (no test script, no framework dep, no test/ folder). Happy to add one if you pick a framework; the assertion shape is: stub the LSP transport to capture outgoing textDocument/codeAction payloads, push a Diagnostic with data/code/source via publishDiagnostics, set a marker matching that range, trigger a code-action request, assert the captured outgoing payload's context.diagnostics[0] matches the original verbatim.
  • The cache is keyed by model.uri.toString() (Monaco URI), not LSP URI, so that cleanup via monaco.editor.onWillDisposeModel is a one-liner. Both producers translate to Monaco URI before caching.
  • For markers under a different owner (e.g. a separate extension's marker collection), the lookup misses and we fall back to the synthesized Diagnostic — preserving the fields IMarker actually carries. This is strictly more information than the current code sends.
  • Independent of # (the dispose leak). Either can land first.

@apatenotre
Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] [LSP] textDocument/codeAction request omits diagnostic fields the server needs to produce quickfixes (data, code, source, …)

1 participant