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 }; 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..21cc1b02 100644 --- a/apps/vscode/src/host/hooks.ts +++ b/apps/vscode/src/host/hooks.ts @@ -18,11 +18,12 @@ 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'; 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 +201,32 @@ 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) { + // 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; + } + }); }; } 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) {