From f3cd3855a1f8a74ee0f1f115031105624d5884f4 Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Mon, 29 Sep 2025 18:25:02 -0600 Subject: [PATCH 1/5] Hide diagnostics from virtual documents --- apps/vscode/package.json | 7 +++++++ apps/vscode/src/lsp/client.ts | 27 ++++++++++++++++++++++++++- apps/vscode/src/vdoc/vdoc.ts | 12 ++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/apps/vscode/package.json b/apps/vscode/package.json index e5a881c3..2ea78a42 100644 --- a/apps/vscode/package.json +++ b/apps/vscode/package.json @@ -1045,6 +1045,13 @@ "default": true, "markdownDescription": "Use reticulate to execute Python cells within Knitr engine documents." }, + "quarto.vdoc.hideDiagnostics": { + "order": 26, + "scope": "window", + "type": "boolean", + "default": true, + "markdownDescription": "Hide diagnostics (problems) reported for internal virtual document files. These are temporary files used to provide language features in code blocks." + }, "quarto.mathjax.scale": { "order": 15, "scope": "window", diff --git a/apps/vscode/src/lsp/client.ts b/apps/vscode/src/lsp/client.ts index 5cf9fca3..0fc72e2d 100644 --- a/apps/vscode/src/lsp/client.ts +++ b/apps/vscode/src/lsp/client.ts @@ -23,7 +23,8 @@ import { LocationLink, Definition, LogOutputChannel, - Uri + Uri, + Diagnostic } from "vscode"; import { LanguageClient, @@ -46,6 +47,7 @@ import { ProvideHoverSignature, ProvideSignatureHelpSignature, State, + HandleDiagnosticsSignature } from "vscode-languageclient"; import { MarkdownEngine } from "../markdown/engine"; import { @@ -53,6 +55,7 @@ import { unadjustedRange, virtualDoc, withVirtualDocUri, + isVirtualDoc, } from "../vdoc/vdoc"; import { activateVirtualDocEmbeddedContent } from "../vdoc/vdoc-content"; import { vdocCompletions } from "../vdoc/vdoc-completion"; @@ -112,6 +115,9 @@ export async function activateLsp( if (config.get("cells.signatureHelp.enabled", true)) { middleware.provideSignatureHelp = embeddedSignatureHelpProvider(engine); } + if (config.get("vdoc.hideDiagnostics", true)) { + middleware.handleDiagnostics = createDiagnosticFilter(); + } extensionHost().registerStatementRangeProvider(engine); extensionHost().registerHelpTopicProvider(engine); @@ -338,3 +344,22 @@ function isWithinYamlComment(doc: TextDocument, pos: Position) { const line = doc.lineAt(pos.line).text; return !!line.match(/^\s*#\s*\| /); } + +/** + * Creates a diagnostic handler middleware that filters out diagnostics from virtual documents + * + * @param enabled Whether filtering is enabled + * @returns A handler function for the middleware + */ +function createDiagnosticFilter() { + return (uri: Uri, diagnostics: Diagnostic[], next: HandleDiagnosticsSignature) => { + // If filtering is disabled or this is not a virtual document, pass through all diagnostics + if (!isVirtualDoc(uri)) { + next(uri, diagnostics); + return; + } + + // For virtual documents, filter out all diagnostics + next(uri, []); + }; +} diff --git a/apps/vscode/src/vdoc/vdoc.ts b/apps/vscode/src/vdoc/vdoc.ts index f1441fe8..0472b1f9 100644 --- a/apps/vscode/src/vdoc/vdoc.ts +++ b/apps/vscode/src/vdoc/vdoc.ts @@ -15,6 +15,7 @@ import { Position, TextDocument, Uri, Range } from "vscode"; import { Token, isExecutableLanguageBlock, languageBlockAtPosition, languageNameFromBlock } from "quarto-core"; +import * as path from "path"; import { isQuartoDoc } from "../core/doc"; import { MarkdownEngine } from "../markdown/engine"; @@ -53,6 +54,17 @@ export async function virtualDoc( } } +export function isVirtualDoc(uri: Uri): boolean { + // Check for tempfile virtual docs + if (uri.scheme === "file") { + const filename = path.basename(uri.fsPath); + // Virtual docs have a specific filename pattern .vdoc.[uuid].[extension] + return filename.startsWith(".vdoc.") && filename.split(".").length > 3; + } + + return false; +} + export function virtualDocForBlock(document: TextDocument, block: Token, language: EmbeddedLanguage) { const lines = linesForLanguage(document, language); fillLinesFromBlock(lines, document, block); From 67faaf3f75f22857d8e6ab52f2d5f24070c65807 Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Mon, 29 Sep 2025 18:50:10 -0600 Subject: [PATCH 2/5] Write a little (mocked) test --- apps/vscode/src/lsp/client.ts | 5 +- .../src/test/diagnosticFiltering.test.ts | 53 +++++++++++++++++++ 2 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 apps/vscode/src/test/diagnosticFiltering.test.ts diff --git a/apps/vscode/src/lsp/client.ts b/apps/vscode/src/lsp/client.ts index 0fc72e2d..fbba37ad 100644 --- a/apps/vscode/src/lsp/client.ts +++ b/apps/vscode/src/lsp/client.ts @@ -348,12 +348,11 @@ function isWithinYamlComment(doc: TextDocument, pos: Position) { /** * Creates a diagnostic handler middleware that filters out diagnostics from virtual documents * - * @param enabled Whether filtering is enabled * @returns A handler function for the middleware */ -function createDiagnosticFilter() { +export function createDiagnosticFilter() { return (uri: Uri, diagnostics: Diagnostic[], next: HandleDiagnosticsSignature) => { - // If filtering is disabled or this is not a virtual document, pass through all diagnostics + // If this is not a virtual document, pass through all diagnostics if (!isVirtualDoc(uri)) { next(uri, diagnostics); return; diff --git a/apps/vscode/src/test/diagnosticFiltering.test.ts b/apps/vscode/src/test/diagnosticFiltering.test.ts new file mode 100644 index 00000000..673bcf7b --- /dev/null +++ b/apps/vscode/src/test/diagnosticFiltering.test.ts @@ -0,0 +1,53 @@ +import * as vscode from "vscode"; +import * as assert from "assert"; +import { createDiagnosticFilter } from "../lsp/client"; + +suite("Diagnostic Filtering", function () { + + test("Diagnostic filter removes diagnostics for virtual documents", async function () { + // Create mocks + const virtualDocUri = vscode.Uri.file("/tmp/.vdoc.12345678-1234-1234-1234-123456789abc.py"); + const regularDocUri = vscode.Uri.file("/tmp/regular-file.py"); + + // Create some test diagnostics + const testDiagnostics = [ + new vscode.Diagnostic( + new vscode.Range(0, 0, 0, 10), + "Test diagnostic message", + vscode.DiagnosticSeverity.Error + ) + ]; + + // Create a mock diagnostics handler function to verify behavior + let capturedUri: vscode.Uri | undefined; + let capturedDiagnostics: vscode.Diagnostic[] | undefined; + + const mockHandler = (uri: vscode.Uri, diagnostics: vscode.Diagnostic[]) => { + capturedUri = uri; + capturedDiagnostics = diagnostics; + }; + + // Create the filter function + const diagnosticFilter = createDiagnosticFilter(); + + // Test with a virtual document + diagnosticFilter(virtualDocUri, testDiagnostics, mockHandler); + + // Verify diagnostics were filtered (empty array) + assert.strictEqual(capturedUri, virtualDocUri, "URI should be passed through"); + assert.strictEqual(capturedDiagnostics!.length, 0, "Diagnostics should be empty for virtual documents"); + + // Reset captured values + capturedUri = undefined; + capturedDiagnostics = undefined; + + // Test with a regular document + diagnosticFilter(regularDocUri, testDiagnostics, mockHandler); + + // Verify diagnostics were not filtered + assert.strictEqual(capturedUri, regularDocUri, "URI should be passed through"); + assert.strictEqual(capturedDiagnostics!.length, testDiagnostics.length, "Diagnostics should not be filtered for regular documents"); + assert.deepStrictEqual(capturedDiagnostics!, testDiagnostics, "Original diagnostics should be passed through unchanged"); + }); + +}); From ea2ed68879563889353d40cbb57221585a07a228 Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Mon, 29 Sep 2025 18:54:12 -0600 Subject: [PATCH 3/5] Update CHANGELOG --- apps/vscode/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/vscode/CHANGELOG.md b/apps/vscode/CHANGELOG.md index 086c8580..a46b6b77 100644 --- a/apps/vscode/CHANGELOG.md +++ b/apps/vscode/CHANGELOG.md @@ -4,6 +4,7 @@ - Fixed an issue where attribute values containing '='s could be truncated in some scenarios (). - Fixed an issue where a loading spinner for qmd previews wasn't dismissed on preview errors () +- Diagnostics are no longer reported for internal temporary virtual document files (). ## 1.124.0 (Release on 2025-08-20) From 7b272556ab58f24c260c2251fc5196a3a31debbd Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Wed, 1 Oct 2025 08:36:12 -0600 Subject: [PATCH 4/5] Move new `isVirtualDoc()` to better file --- apps/vscode/src/vdoc/vdoc-tempfile.ts | 11 +++++++++++ apps/vscode/src/vdoc/vdoc.ts | 12 ------------ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/apps/vscode/src/vdoc/vdoc-tempfile.ts b/apps/vscode/src/vdoc/vdoc-tempfile.ts index b016c258..0a0bb337 100644 --- a/apps/vscode/src/vdoc/vdoc-tempfile.ts +++ b/apps/vscode/src/vdoc/vdoc-tempfile.ts @@ -150,3 +150,14 @@ function createVirtualDoc(filepath: string, content: string): void { function generateVirtualDocFilepath(directory: string, extension: string): string { return path.join(directory, ".vdoc." + uuid.v4() + "." + extension); } + +export function isVirtualDoc(uri: Uri): boolean { + // Check for tempfile virtual docs + if (uri.scheme === "file") { + const filename = path.basename(uri.fsPath); + // Virtual docs have a specific filename pattern .vdoc.[uuid].[extension] + return filename.startsWith(".vdoc.") && filename.split(".").length > 3; + } + + return false; +} diff --git a/apps/vscode/src/vdoc/vdoc.ts b/apps/vscode/src/vdoc/vdoc.ts index 0472b1f9..f1441fe8 100644 --- a/apps/vscode/src/vdoc/vdoc.ts +++ b/apps/vscode/src/vdoc/vdoc.ts @@ -15,7 +15,6 @@ import { Position, TextDocument, Uri, Range } from "vscode"; import { Token, isExecutableLanguageBlock, languageBlockAtPosition, languageNameFromBlock } from "quarto-core"; -import * as path from "path"; import { isQuartoDoc } from "../core/doc"; import { MarkdownEngine } from "../markdown/engine"; @@ -54,17 +53,6 @@ export async function virtualDoc( } } -export function isVirtualDoc(uri: Uri): boolean { - // Check for tempfile virtual docs - if (uri.scheme === "file") { - const filename = path.basename(uri.fsPath); - // Virtual docs have a specific filename pattern .vdoc.[uuid].[extension] - return filename.startsWith(".vdoc.") && filename.split(".").length > 3; - } - - return false; -} - export function virtualDocForBlock(document: TextDocument, block: Token, language: EmbeddedLanguage) { const lines = linesForLanguage(document, language); fillLinesFromBlock(lines, document, block); From ff654d4a18234c74e3098f1923abffcf0b085008 Mon Sep 17 00:00:00 2001 From: Julia Silge Date: Wed, 1 Oct 2025 08:36:24 -0600 Subject: [PATCH 5/5] No more config --- apps/vscode/package.json | 7 ------- apps/vscode/src/lsp/client.ts | 6 ++---- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/apps/vscode/package.json b/apps/vscode/package.json index 2ea78a42..e5a881c3 100644 --- a/apps/vscode/package.json +++ b/apps/vscode/package.json @@ -1045,13 +1045,6 @@ "default": true, "markdownDescription": "Use reticulate to execute Python cells within Knitr engine documents." }, - "quarto.vdoc.hideDiagnostics": { - "order": 26, - "scope": "window", - "type": "boolean", - "default": true, - "markdownDescription": "Hide diagnostics (problems) reported for internal virtual document files. These are temporary files used to provide language features in code blocks." - }, "quarto.mathjax.scale": { "order": 15, "scope": "window", diff --git a/apps/vscode/src/lsp/client.ts b/apps/vscode/src/lsp/client.ts index fbba37ad..07f92e9f 100644 --- a/apps/vscode/src/lsp/client.ts +++ b/apps/vscode/src/lsp/client.ts @@ -55,8 +55,8 @@ import { unadjustedRange, virtualDoc, withVirtualDocUri, - isVirtualDoc, } from "../vdoc/vdoc"; +import { isVirtualDoc } from "../vdoc/vdoc-tempfile"; import { activateVirtualDocEmbeddedContent } from "../vdoc/vdoc-content"; import { vdocCompletions } from "../vdoc/vdoc-completion"; @@ -102,6 +102,7 @@ export async function activateLsp( const config = workspace.getConfiguration("quarto"); activateVirtualDocEmbeddedContent(); const middleware: Middleware = { + handleDiagnostics: createDiagnosticFilter(), provideCompletionItem: embeddedCodeCompletionProvider(engine), provideDefinition: embeddedGoToDefinitionProvider(engine), provideDocumentFormattingEdits: embeddedDocumentFormattingProvider(engine), @@ -115,9 +116,6 @@ export async function activateLsp( if (config.get("cells.signatureHelp.enabled", true)) { middleware.provideSignatureHelp = embeddedSignatureHelpProvider(engine); } - if (config.get("vdoc.hideDiagnostics", true)) { - middleware.handleDiagnostics = createDiagnosticFilter(); - } extensionHost().registerStatementRangeProvider(engine); extensionHost().registerHelpTopicProvider(engine);