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
2 changes: 2 additions & 0 deletions apps/vscode/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## 1.130.0 (Unreleased)

- Fixed a bug where a parse error in one chunk meant you could not perform statement execution in another chunk (<https://github.com/quarto-dev/quarto/pull/914>).

## 1.129.0 (Release on 2026-01-29)

- Fixed Copilot completions in `.qmd` documents (<https://github.com/quarto-dev/quarto/pull/887>).
Expand Down
6 changes: 3 additions & 3 deletions apps/vscode/src/host/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* Positron-specific functionality.
*
* Copyright (C) 2022 by Posit Software, PBC
* Copyright (C) 2022-2026 by Posit Software, PBC
*
* Unless you have received this program directly from Posit Software pursuant
* to the terms of a commercial license agreement with Posit Software, then
Expand All @@ -22,7 +22,7 @@ import { ExtensionHost, HostWebviewPanel, HostStatementRangeProvider, HostHelpTo
import { CellExecutor, cellExecutorForLanguage, executableLanguages, isKnitrDocument, pythonWithReticulate } from './executors';
import { ExecuteQueue } from './execute-queue';
import { MarkdownEngine } from '../markdown/engine';
import { virtualDoc, adjustedPosition, unadjustedRange, withVirtualDocUri } from "../vdoc/vdoc";
import { virtualDoc, adjustedPosition, unadjustedRange, withVirtualDocUri, VirtualDocStyle } from "../vdoc/vdoc";
import { Position, Range } from 'vscode';
import { Uri } from 'vscode';

Expand Down Expand Up @@ -199,7 +199,7 @@ class EmbeddedStatementRangeProvider implements HostStatementRangeProvider {
document: vscode.TextDocument,
position: vscode.Position,
token: vscode.CancellationToken): Promise<hooks.StatementRange | undefined> {
const vdoc = await virtualDoc(document, position, this._engine);
const vdoc = await virtualDoc(document, position, this._engine, VirtualDocStyle.Block);
if (vdoc) {
return await withVirtualDocUri(vdoc, document.uri, "statementRange", async (uri: vscode.Uri) => {
const result = await vscode.commands.executeCommand<hooks.StatementRange>(
Expand Down
6 changes: 3 additions & 3 deletions apps/vscode/src/test/codeBlocks.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as vscode from "vscode";
import * as assert from "assert";
import { WORKSPACE_PATH, examplesOutUri, openAndShowTextDocument } from "./test-utils";
import { WORKSPACE_PATH, examplesOutUri, openAndShowExamplesOutTextDocument } from "./test-utils";
import { isExecutableLanguageBlock, languageNameFromBlock } from "quarto-core";
import { MarkdownEngine } from "../markdown/engine";

Expand All @@ -17,7 +17,7 @@ suite("Code block detection", function () {
// doesn't have a preceding blank line. This real-world example has many
// .notes divs which caused later code blocks to not be detected.
test("Detects code blocks in document with many divs (issue #521)", async function () {
const { doc } = await openAndShowTextDocument("div-code-blocks.qmd");
const { doc } = await openAndShowExamplesOutTextDocument("div-code-blocks.qmd");

const tokens = engine.parse(doc);
const executableBlocks = tokens.filter(isExecutableLanguageBlock);
Expand Down Expand Up @@ -49,7 +49,7 @@ suite("Code block detection", function () {
});

test("Detects code block in simple document", async function () {
const { doc } = await openAndShowTextDocument("hello.qmd");
const { doc } = await openAndShowExamplesOutTextDocument("hello.qmd");

const tokens = engine.parse(doc);
const executableBlocks = tokens.filter(isExecutableLanguageBlock);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# type: ignore
# flake8: noqa
#
#
#
#
#
#
#
#
1 + 1
#
#
#
2 + 2
#
#
#
#
#
#
#
4 + 4
#
#
#
#
#
#
#
#
29 changes: 29 additions & 0 deletions apps/vscode/src/test/examples/generated_snapshots/vdoc/blocks-r.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
3 + 3
#
#
#
#
#
#
#
5 + 5
#
#
#
#
29 changes: 29 additions & 0 deletions apps/vscode/src/test/examples/generated_snapshots/vdoc/one-block.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
3 + 3
#
#
#
#
#
#
#
#
#
#
#
#
26 changes: 26 additions & 0 deletions apps/vscode/src/test/examples/vdoc/blocks.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
title: "Test all blocks for language at position"
format: html
---

## Header

```{python}
1 + 1
```

```{python}
2 + 2
```

```{r}
3 + 3
```

```{python}
4 + 4
```

```{r}
5 + 5
```
12 changes: 12 additions & 0 deletions apps/vscode/src/test/examples/vdoc/oob.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
title: "OOB"
format: html
---

## Header

```{python}
1 + 1
```

Yo
12 changes: 6 additions & 6 deletions apps/vscode/src/test/quartoDoc.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as vscode from "vscode";
import * as assert from "assert";
import { WORKSPACE_PATH, readOrCreateSnapshot, examplesOutUri, wait, roundtrip, openAndShowTextDocument } from "./test-utils";
import { WORKSPACE_PATH, readOrCreateSnapshot, examplesOutUri, wait, roundtrip, openAndShowExamplesOutTextDocument } from "./test-utils";
import { isQuartoDoc } from "../core/doc";


Expand All @@ -13,7 +13,7 @@ suite("Quarto basics", function () {
});

test("Can open a Quarto document", async function () {
const { editor } = await openAndShowTextDocument("hello.qmd");
const { editor } = await openAndShowExamplesOutTextDocument("hello.qmd");

assert.strictEqual(editor?.document.languageId, "quarto");
assert.strictEqual(isQuartoDoc(editor?.document), true);
Expand All @@ -23,7 +23,7 @@ suite("Quarto basics", function () {
// test. That's okay for this test, but could cause issues if you expect a qmd to look how it
// does in `/examples`.
test("Roundtrip doesn't change hello.qmd", async function () {
const { doc } = await openAndShowTextDocument("hello.qmd");
const { doc } = await openAndShowExamplesOutTextDocument("hello.qmd");

const { before, after } = await roundtrip(doc);

Expand All @@ -44,7 +44,7 @@ suite("Quarto basics", function () {

// a test to prevent situations like https://github.com/quarto-dev/quarto/issues/845
test("Can open a non-qmd file normally", async function () {
const { editor, doc } = await openAndShowTextDocument("hello.lua");
const { editor, doc } = await openAndShowExamplesOutTextDocument("hello.lua");

editor.edit((editBuilder) => {
editBuilder.insert(new vscode.Position(0, 0), 'print("hiyo")\n');
Expand All @@ -57,7 +57,7 @@ suite("Quarto basics", function () {
});

test("Roundtrip doesn't change nested-checked-list.qmd", async function () {
const { doc } = await openAndShowTextDocument("nested-checked-list.qmd");
const { doc } = await openAndShowExamplesOutTextDocument("nested-checked-list.qmd");

const { before, after } = await roundtrip(doc);

Expand All @@ -78,7 +78,7 @@ function roundtripSnapshotTest(filename: string) {
const snapshotFileName = `roundtripped-${filename}`;

test(`Roundtripped ${filename} matches snapshot`, async function () {
const { doc } = await openAndShowTextDocument(filename);
const { doc } = await openAndShowExamplesOutTextDocument(filename);

const { after } = await roundtrip(doc);

Expand Down
12 changes: 10 additions & 2 deletions apps/vscode/src/test/test-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,16 @@ export function wait(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}

export async function openAndShowTextDocument(fileName: string) {
const doc = await vscode.workspace.openTextDocument(examplesOutUri(fileName));
export async function openAndShowExamplesTextDocument(fileName: string) {
return openAndShowUri(examplesUri(fileName));
}

export async function openAndShowExamplesOutTextDocument(fileName: string) {
return openAndShowUri(examplesOutUri(fileName));
}

async function openAndShowUri(uri: vscode.Uri) {
const doc = await vscode.workspace.openTextDocument(uri);
const editor = await vscode.window.showTextDocument(doc);
return { doc, editor };
}
Expand Down
63 changes: 63 additions & 0 deletions apps/vscode/src/test/vdoc.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import * as vscode from "vscode";
import * as assert from "assert";
import { readOrCreateSnapshot, openAndShowExamplesTextDocument } from "./test-utils";
import { MarkdownEngine } from "../markdown/engine";
import { virtualDoc, VirtualDocStyle } from "../vdoc/vdoc";
import path from "path";

suite("Virtual documents", function () {
const engine = new MarkdownEngine();

snapshotVirtualDocument(
"vdoc/blocks-python.py",
"vdoc/blocks.qmd",
new vscode.Position(8, 0),
engine,
VirtualDocStyle.Language
);
snapshotVirtualDocument(
"vdoc/blocks-r.R",
"vdoc/blocks.qmd",
new vscode.Position(16, 0),
engine,
VirtualDocStyle.Language
);

snapshotVirtualDocument(
"vdoc/one-block.R",
"vdoc/blocks.qmd",
new vscode.Position(16, 0),
engine,
VirtualDocStyle.Block
);

test("OOB position returns `undefined` virtual doc", async function () {
const { doc } = await openAndShowExamplesTextDocument("vdoc/oob.qmd");

const position = new vscode.Position(0, 0);
const style = VirtualDocStyle.Language;

const vdoc = await virtualDoc(doc, position, engine, style);
assert.strictEqual(vdoc, undefined);
});
});

function snapshotVirtualDocument(
snapshotFilename: string,
filename: string,
position: vscode.Position,
engine: MarkdownEngine,
style: VirtualDocStyle
) {
test(`Virtual document ${filename} matches snapshot ${snapshotFilename}`, async function () {
const { doc } = await openAndShowExamplesTextDocument(filename);

const vdoc = await virtualDoc(doc, position, engine, style);
assert.ok(vdoc);

assert.equal(
vdoc.content,
await readOrCreateSnapshot(snapshotFilename, vdoc.content)
);
});
}
Loading