Skip to content
Open
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 @@

- Added clickable document links for file paths in `_quarto.yml` files. File paths are now clickable and navigate directly to the referenced file (<https://github.com/quarto-dev/quarto/pull/906>).
- Added filepath autocompletion in `_quarto.yml` files. When editing YAML values, the extension now suggests project files as you type (<https://github.com/quarto-dev/quarto/pull/906>).
- Now use Positron's active runtime to choose the language for new code cells in an empty document (<https://github.com/quarto-dev/quarto/pull/951>).

## 1.131.0 (Release on 2026-04-14)

Expand Down
8 changes: 8 additions & 0 deletions apps/vscode/src/@types/hooks.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ declare module 'positron' {
StatementRangeSyntaxError: typeof StatementRangeSyntaxError;
}

export interface LanguageRuntimeSession {
readonly runtimeMetadata: {
readonly languageId: string;
};
}

export interface PositronRuntime {
executeCode(
languageId: string,
Expand All @@ -25,6 +31,8 @@ declare module 'positron' {
documentUri: vscode.Uri,
cellRanges: vscode.Range[]
): Thenable<void>;

getForegroundSession(): Thenable<LanguageRuntimeSession | undefined>;
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I'm adding this here to the PositronRuntime interface in along with a minimal LanguageRuntimeSession type.

}

export interface PositronLanguages {
Expand Down
14 changes: 12 additions & 2 deletions apps/vscode/src/providers/insert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
import { Command } from "../core/command";
import { isQuartoDoc } from "../core/doc";
import { MarkdownEngine } from "../markdown/engine";
import { hooksApi } from "../host/hooks";
import { isExecutableLanguageBlock, languageBlockAtPosition, languageNameFromBlock } from "quarto-core";


Expand Down Expand Up @@ -98,15 +99,24 @@ class InsertCodeCellCommand implements Command {
}
}

// if no language found in document, fall back to Positron's active runtime
const kSupportedLanguages = ['python', 'r', 'julia', 'ojs', 'sql', 'bash', 'mermaid', 'dot'];
if (!language) {
const session = await hooksApi()?.runtime.getForegroundSession();
const sessionLang = session?.runtimeMetadata.languageId ?? "";
if (kSupportedLanguages.includes(sessionLang)) {
language = sessionLang;
}
}

// if we have a known language, use it and put the cursor directly in the
// code cell, otherwise let the user select the language first
let header;

if (language) {
header = "```{" + language + "}";
} else {
const languages = ['python', 'r', 'julia', 'ojs', 'sql', 'bash', 'mermaid', 'dot'];
header = "```{${1|" + languages.join(",") + "|}}";
header = "```{${1|" + kSupportedLanguages.join(",") + "|}}";
}

// insert snippet
Expand Down
96 changes: 96 additions & 0 deletions apps/vscode/src/test/insert.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import * as vscode from "vscode";
import * as assert from "assert";
import { examplesOutUri, openAndShowExamplesOutTextDocument, WORKSPACE_PATH } from "./test-utils";

suite("Insert Code Cell", function () {
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

These tests only test the VS Code behavior, not what would happen in Positron in an empty document. We don't yet have a good way to write tests against Positron APIs.

suiteSetup(async function () {
await vscode.workspace.fs.delete(examplesOutUri(), { recursive: true });
await vscode.workspace.fs.copy(vscode.Uri.file(WORKSPACE_PATH), examplesOutUri());
});

teardown(async function () {
await vscode.commands.executeCommand("undo");
});

// format/basics.qmd (0-indexed lines):
// 9: ```{python}
// 10: x = 1 + 1 <- inside python block
// 11: ```
// 13: More markdown text. <- between blocks
// 15: ```{r}
// 16: y <- 1 + 1 <- inside r block
// 17: ```
// 19: Final line. <- after all blocks
// 7: Some regular text here. <- before all blocks

suite("Language from cursor context", function () {
test("Uses language of block when cursor is inside a Python block", async function () {
const { doc, editor } = await openAndShowExamplesOutTextDocument("format/basics.qmd");
const before = countOccurrences(doc.getText(), "```{python}");

editor.selection = new vscode.Selection(10, 0, 10, 0);
await vscode.commands.executeCommand("quarto.insertCodeCell");

assert.strictEqual(countOccurrences(doc.getText(), "```{python}"), before + 1);
});

test("Uses language of block when cursor is inside an R block", async function () {
const { doc, editor } = await openAndShowExamplesOutTextDocument("format/basics.qmd");
const before = countOccurrences(doc.getText(), "```{r}");

editor.selection = new vscode.Selection(16, 0, 16, 0);
await vscode.commands.executeCommand("quarto.insertCodeCell");

assert.strictEqual(countOccurrences(doc.getText(), "```{r}"), before + 1);
});

test("Uses language of nearest preceding block when cursor is between blocks", async function () {
const { doc, editor } = await openAndShowExamplesOutTextDocument("format/basics.qmd");
const before = countOccurrences(doc.getText(), "```{python}");

editor.selection = new vscode.Selection(13, 0, 13, 0);
await vscode.commands.executeCommand("quarto.insertCodeCell");

assert.strictEqual(countOccurrences(doc.getText(), "```{python}"), before + 1);
});

test("Uses language of first following block when cursor precedes all blocks", async function () {
const { doc, editor } = await openAndShowExamplesOutTextDocument("format/basics.qmd");
const before = countOccurrences(doc.getText(), "```{python}");

editor.selection = new vscode.Selection(7, 0, 7, 0);
await vscode.commands.executeCommand("quarto.insertCodeCell");

assert.strictEqual(countOccurrences(doc.getText(), "```{python}"), before + 1);
});

test("Uses language of last block when cursor follows all blocks", async function () {
const { doc, editor } = await openAndShowExamplesOutTextDocument("format/basics.qmd");
const before = countOccurrences(doc.getText(), "```{r}");

editor.selection = new vscode.Selection(19, 0, 19, 0);
await vscode.commands.executeCommand("quarto.insertCodeCell");

assert.strictEqual(countOccurrences(doc.getText(), "```{r}"), before + 1);
});
});

suite("Language picker fallback", function () {
test("Inserts a code fence when document has no executable code cells", async function () {
const content = "---\ntitle: Test\n---\n\nJust some markdown.\n";
const uri = examplesOutUri("insert-test-empty.qmd");
await vscode.workspace.fs.writeFile(uri, Buffer.from(content, "utf8") as Uint8Array);

const doc = await vscode.workspace.openTextDocument(uri);
const editor = await vscode.window.showTextDocument(doc);
editor.selection = new vscode.Selection(4, 0, 4, 0);
await vscode.commands.executeCommand("quarto.insertCodeCell");

assert.ok(doc.getText().includes("```{"));
});
});
});

function countOccurrences(text: string, substring: string): number {
return text.split(substring).length - 1;
}
Loading