From 8f47c14927e87ca8900a50129d7a1085095989ea Mon Sep 17 00:00:00 2001 From: Davis Vaughan Date: Wed, 18 Feb 2026 11:57:59 -0500 Subject: [PATCH 1/6] Add `'positron'` as "external" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To resolve ``` quarto:dev: ✘ [ERROR] Could not resolve "positron" quarto:dev: quarto:dev: src/host/hooks.ts:19:23: quarto:dev: 19 │ import * as hooks from 'positron'; ``` --- apps/vscode/build.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/vscode/build.ts b/apps/vscode/build.ts index 8595c118..7d4a3a5c 100644 --- a/apps/vscode/build.ts +++ b/apps/vscode/build.ts @@ -24,14 +24,14 @@ const testFiles = glob.sync("src/test/*.ts"); const testBuildOptions = { entryPoints: testFiles, outdir: 'test-out', - external: ['vscode', 'mocha', 'glob'], + external: ['vscode', 'positron', 'mocha', 'glob'], sourcemap: true, }; const defaultBuildOptions = { entryPoints: ['./src/main.ts'], outfile: './out/main.js', - external: ['vscode'], + external: ['vscode', 'positron'], minify: !dev, dev }; From 0cfe86a76424b8f3030475a20a8ed771c15817c4 Mon Sep 17 00:00:00 2001 From: Davis Vaughan Date: Wed, 18 Feb 2026 12:00:49 -0500 Subject: [PATCH 2/6] Rethrow new `StatementRangeSyntaxError` with unadjusted line number --- apps/vscode/src/@types/hooks.d.ts | 5 +++++ apps/vscode/src/host/hooks.ts | 24 +++++++++++++++++------- apps/vscode/src/vdoc/vdoc.ts | 14 +++++++++++--- 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/apps/vscode/src/@types/hooks.d.ts b/apps/vscode/src/@types/hooks.d.ts index 32bf8a12..932e09a2 100644 --- a/apps/vscode/src/@types/hooks.d.ts +++ b/apps/vscode/src/@types/hooks.d.ts @@ -58,6 +58,11 @@ declare module 'positron' { readonly code?: string; } + export class StatementRangeSyntaxError extends Error { + readonly line?: number; + constructor(line?: number); + } + export interface PositronWindow { createPreviewPanel( viewType: string, diff --git a/apps/vscode/src/host/hooks.ts b/apps/vscode/src/host/hooks.ts index afcd8b34..ea2c89f4 100644 --- a/apps/vscode/src/host/hooks.ts +++ b/apps/vscode/src/host/hooks.ts @@ -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, VirtualDocStyle } from "../vdoc/vdoc"; +import { virtualDoc, adjustedPosition, unadjustedRange, withVirtualDocUri, VirtualDocStyle, unadjustedLine } from "../vdoc/vdoc"; import { Position, Range } from 'vscode'; import { Uri } from 'vscode'; @@ -200,18 +200,28 @@ class EmbeddedStatementRangeProvider implements HostStatementRangeProvider { position: vscode.Position, token: vscode.CancellationToken): Promise { const vdoc = await virtualDoc(document, position, this._engine, VirtualDocStyle.Block); - if (vdoc) { - return await withVirtualDocUri(vdoc, document.uri, "statementRange", async (uri: vscode.Uri) => { + + if (!vdoc) { + return undefined; + } + + return await withVirtualDocUri(vdoc, document.uri, "statementRange", async (uri: vscode.Uri) => { + try { const result = await vscode.commands.executeCommand( "vscode.executeStatementRangeProvider", uri, adjustedPosition(vdoc.language, position) ); return { range: unadjustedRange(vdoc.language, result.range), code: result.code }; - }); - } else { - return undefined; - } + } catch (err) { + if (err instanceof hooks.StatementRangeSyntaxError) { + // Rethrow syntax error with unadjusted line number, so Positron's notification will + // jump to the correct line + throw new hooks.StatementRangeSyntaxError(err.line ? unadjustedLine(vdoc.language, err.line) : undefined); + } + throw err; + } + }); }; } diff --git a/apps/vscode/src/vdoc/vdoc.ts b/apps/vscode/src/vdoc/vdoc.ts index 29a8c178..c17720e4 100644 --- a/apps/vscode/src/vdoc/vdoc.ts +++ b/apps/vscode/src/vdoc/vdoc.ts @@ -240,13 +240,21 @@ export function isBlockOfLanguage(language: EmbeddedLanguage) { }; } -// adjust position for inject +// adjust line for inject +export function adjustedLine(language: EmbeddedLanguage, line: number): number { + return line + (language.inject?.length || 0); +} + +export function unadjustedLine(language: EmbeddedLanguage, line: number): number { + return line - (language.inject?.length || 0); +} + export function adjustedPosition(language: EmbeddedLanguage, pos: Position) { - return new Position(pos.line + (language.inject?.length || 0), pos.character); + return new Position(adjustedLine(language, pos.line), pos.character); } export function unadjustedPosition(language: EmbeddedLanguage, pos: Position) { - return new Position(pos.line - (language.inject?.length || 0), pos.character); + return new Position(unadjustedLine(language, pos.line), pos.character); } export function unadjustedRange(language: EmbeddedLanguage, range: Range) { From a819d10f7a67bae47d609ce1a1b3dea00e6227b6 Mon Sep 17 00:00:00 2001 From: Davis Vaughan Date: Wed, 18 Feb 2026 13:12:43 -0500 Subject: [PATCH 3/6] Guard `StatementRangeSyntaxError` usage with version requirement --- apps/vscode/src/host/hooks.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/apps/vscode/src/host/hooks.ts b/apps/vscode/src/host/hooks.ts index ea2c89f4..21cc1b02 100644 --- a/apps/vscode/src/host/hooks.ts +++ b/apps/vscode/src/host/hooks.ts @@ -18,6 +18,7 @@ import * as vscode from 'vscode'; import * as hooks from 'positron'; +import semver from "semver"; import { ExtensionHost, HostWebviewPanel, HostStatementRangeProvider, HostHelpTopicProvider } from '.'; import { CellExecutor, cellExecutorForLanguage, executableLanguages, isKnitrDocument, pythonWithReticulate } from './executors'; import { ExecuteQueue } from './execute-queue'; @@ -214,10 +215,14 @@ class EmbeddedStatementRangeProvider implements HostStatementRangeProvider { ); return { range: unadjustedRange(vdoc.language, result.range), code: result.code }; } catch (err) { - if (err instanceof hooks.StatementRangeSyntaxError) { - // Rethrow syntax error with unadjusted line number, so Positron's notification will - // jump to the correct line - throw new hooks.StatementRangeSyntaxError(err.line ? unadjustedLine(vdoc.language, err.line) : undefined); + // TODO: Remove this once `apps/vscode/package.json` bumps to `"positron": "^2026.03.0"` or higher. + // For now we avoid aggressive bumping due to https://github.com/posit-dev/positron/issues/11321. + if (semver.gte(hooks.version, "2026.03.0")) { + if (err instanceof hooks.StatementRangeSyntaxError) { + // Rethrow syntax error with unadjusted line number, so Positron's notification will + // jump to the correct line + throw new hooks.StatementRangeSyntaxError(err.line ? unadjustedLine(vdoc.language, err.line) : undefined); + } } throw err; } From b0642b362c375c928514be4875563ccaf0d6caa1 Mon Sep 17 00:00:00 2001 From: Davis Vaughan Date: Wed, 11 Mar 2026 13:06:39 -0400 Subject: [PATCH 4/6] Remove `'positron'` from list of externals I think this is causing test failures because in CI we run the tests as vscode, but in these externals we are trying to assert that positron will be available, and it's not It would be nice to figure this out though, because this was required for me to run dev quarto with dev positron. --- apps/vscode/build.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/vscode/build.ts b/apps/vscode/build.ts index 7d4a3a5c..8595c118 100644 --- a/apps/vscode/build.ts +++ b/apps/vscode/build.ts @@ -24,14 +24,14 @@ const testFiles = glob.sync("src/test/*.ts"); const testBuildOptions = { entryPoints: testFiles, outdir: 'test-out', - external: ['vscode', 'positron', 'mocha', 'glob'], + external: ['vscode', 'mocha', 'glob'], sourcemap: true, }; const defaultBuildOptions = { entryPoints: ['./src/main.ts'], outfile: './out/main.js', - external: ['vscode', 'positron'], + external: ['vscode'], minify: !dev, dev }; From 3f0a1186686f648a883583e91ef59da195cd4f11 Mon Sep 17 00:00:00 2001 From: Davis Vaughan Date: Wed, 11 Mar 2026 14:17:22 -0400 Subject: [PATCH 5/6] Expose `StatementRangeSyntaxError` via `hooksAPI()` This is why I was needing `'positron'` as an "external" before. We've never had to expose a class that we need to construct before, and it requires using it as a "value" rather than just as a "type" so it needs to show up in `PositronApi` and be called via `hooksApi()` --- apps/vscode/src/@types/hooks.d.ts | 1 + apps/vscode/src/host/hooks.ts | 24 +++++++++++++++++------- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/apps/vscode/src/@types/hooks.d.ts b/apps/vscode/src/@types/hooks.d.ts index 932e09a2..c8b0c2b7 100644 --- a/apps/vscode/src/@types/hooks.d.ts +++ b/apps/vscode/src/@types/hooks.d.ts @@ -10,6 +10,7 @@ declare module 'positron' { runtime: PositronRuntime; languages: PositronLanguages; window: PositronWindow; + StatementRangeSyntaxError: typeof StatementRangeSyntaxError; } export interface PositronRuntime { diff --git a/apps/vscode/src/host/hooks.ts b/apps/vscode/src/host/hooks.ts index 21cc1b02..5f72bd98 100644 --- a/apps/vscode/src/host/hooks.ts +++ b/apps/vscode/src/host/hooks.ts @@ -215,16 +215,26 @@ class EmbeddedStatementRangeProvider implements HostStatementRangeProvider { ); return { range: unadjustedRange(vdoc.language, result.range), code: result.code }; } catch (err) { + let hooks = hooksApi(); + + if (!hooks) { + throw err; + } + // TODO: Remove this once `apps/vscode/package.json` bumps to `"positron": "^2026.03.0"` or higher. // For now we avoid aggressive bumping due to https://github.com/posit-dev/positron/issues/11321. - if (semver.gte(hooks.version, "2026.03.0")) { - if (err instanceof hooks.StatementRangeSyntaxError) { - // Rethrow syntax error with unadjusted line number, so Positron's notification will - // jump to the correct line - throw new hooks.StatementRangeSyntaxError(err.line ? unadjustedLine(vdoc.language, err.line) : undefined); - } + if (semver.lt(hooks.version, "2026.03.0")) { + throw err; + } + + if (err instanceof hooks.StatementRangeSyntaxError) { + // Rethrow syntax error with unadjusted line number, so Positron's notification will + // jump to the correct line + throw new hooks.StatementRangeSyntaxError(err.line ? unadjustedLine(vdoc.language, err.line) : undefined); + } else { + // Rethrow unrecognized error + throw err; } - throw err; } }); }; From 463f380f8fe8c0798cba8e80bac86a982fd4cfe4 Mon Sep 17 00:00:00 2001 From: Davis Vaughan Date: Wed, 11 Mar 2026 14:18:04 -0400 Subject: [PATCH 6/6] Use lexicographic comparison for calendar versions --- apps/vscode/src/host/hooks.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/vscode/src/host/hooks.ts b/apps/vscode/src/host/hooks.ts index 5f72bd98..bd16a8bd 100644 --- a/apps/vscode/src/host/hooks.ts +++ b/apps/vscode/src/host/hooks.ts @@ -223,7 +223,10 @@ class EmbeddedStatementRangeProvider implements HostStatementRangeProvider { // TODO: Remove this once `apps/vscode/package.json` bumps to `"positron": "^2026.03.0"` or higher. // For now we avoid aggressive bumping due to https://github.com/posit-dev/positron/issues/11321. - if (semver.lt(hooks.version, "2026.03.0")) { + // We can't use `semver.lt()` because calendar versioning isn't compatible with semver due to the + // leading `0` in `03`. Instead, we use lexicographic string comparison and rely on the year and + // month to be zero padded so sorting always works correctly. + if (hooks.version < "2026.03.0") { throw err; }