Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/vscode/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- Fixed an issue where attribute values containing '='s could be truncated in some scenarios (<https://github.com/quarto-dev/quarto/pull/814>).
- Fixed an issue where a loading spinner for qmd previews wasn't dismissed on preview errors (<https://github.com/quarto-dev/quarto/pull/823>)
- Diagnostics are no longer reported for internal temporary virtual document files (<https://github.com/quarto-dev/quarto/pull/832>).

## 1.124.0 (Release on 2025-08-20)

Expand Down
24 changes: 23 additions & 1 deletion apps/vscode/src/lsp/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ import {
LocationLink,
Definition,
LogOutputChannel,
Uri
Uri,
Diagnostic
} from "vscode";
import {
LanguageClient,
Expand All @@ -46,6 +47,7 @@ import {
ProvideHoverSignature,
ProvideSignatureHelpSignature,
State,
HandleDiagnosticsSignature
} from "vscode-languageclient";
import { MarkdownEngine } from "../markdown/engine";
import {
Expand All @@ -54,6 +56,7 @@ import {
virtualDoc,
withVirtualDocUri,
} from "../vdoc/vdoc";
import { isVirtualDoc } from "../vdoc/vdoc-tempfile";
import { activateVirtualDocEmbeddedContent } from "../vdoc/vdoc-content";
import { vdocCompletions } from "../vdoc/vdoc-completion";

Expand Down Expand Up @@ -99,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),
Expand Down Expand Up @@ -338,3 +342,21 @@ 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
*
* @returns A handler function for the middleware
*/
export function createDiagnosticFilter() {
return (uri: Uri, diagnostics: Diagnostic[], next: HandleDiagnosticsSignature) => {
// If 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, []);
};
}
53 changes: 53 additions & 0 deletions apps/vscode/src/test/diagnosticFiltering.test.ts
Original file line number Diff line number Diff line change
@@ -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");
});

});
11 changes: 11 additions & 0 deletions apps/vscode/src/vdoc/vdoc-tempfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}