Skip to content

Commit e8a7426

Browse files
authored
Use StatementRangeSyntaxError (#919)
* Add `'positron'` as "external" 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'; ``` * Rethrow new `StatementRangeSyntaxError` with unadjusted line number * Guard `StatementRangeSyntaxError` usage with version requirement * 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. * 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()` * Use lexicographic comparison for calendar versions * CHANGELOG
1 parent 0be30fc commit e8a7426

4 files changed

Lines changed: 53 additions & 10 deletions

File tree

apps/vscode/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## 1.131.0 (Unreleased)
44

5+
- Added support for Positron's statement execution feature that reports the approximate line number of the parse error (<https://github.com/quarto-dev/quarto/pull/919>).
56
- Fixed a bug where `Quarto: Format Cell` would notify you that no formatter was available for code cells that were already formatted (<https://github.com/quarto-dev/quarto/pull/933>).
67

78
## 1.130.0 (Release on 2026-02-18)

apps/vscode/src/@types/hooks.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ declare module 'positron' {
1010
runtime: PositronRuntime;
1111
languages: PositronLanguages;
1212
window: PositronWindow;
13+
StatementRangeSyntaxError: typeof StatementRangeSyntaxError;
1314
}
1415

1516
export interface PositronRuntime {
@@ -58,6 +59,11 @@ declare module 'positron' {
5859
readonly code?: string;
5960
}
6061

62+
export class StatementRangeSyntaxError extends Error {
63+
readonly line?: number;
64+
constructor(line?: number);
65+
}
66+
6167
export interface PositronWindow {
6268
createPreviewPanel(
6369
viewType: string,

apps/vscode/src/host/hooks.ts

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@
1818
import * as vscode from 'vscode';
1919
import * as hooks from 'positron';
2020

21+
import semver from "semver";
2122
import { ExtensionHost, HostWebviewPanel, HostStatementRangeProvider, HostHelpTopicProvider } from '.';
2223
import { CellExecutor, cellExecutorForLanguage, executableLanguages, isKnitrDocument, pythonWithReticulate } from './executors';
2324
import { ExecuteQueue } from './execute-queue';
2425
import { MarkdownEngine } from '../markdown/engine';
25-
import { virtualDoc, adjustedPosition, unadjustedRange, withVirtualDocUri, VirtualDocStyle } from "../vdoc/vdoc";
26+
import { virtualDoc, adjustedPosition, unadjustedRange, withVirtualDocUri, VirtualDocStyle, unadjustedLine } from "../vdoc/vdoc";
2627
import { Position, Range } from 'vscode';
2728
import { Uri } from 'vscode';
2829

@@ -200,18 +201,45 @@ class EmbeddedStatementRangeProvider implements HostStatementRangeProvider {
200201
position: vscode.Position,
201202
token: vscode.CancellationToken): Promise<hooks.StatementRange | undefined> {
202203
const vdoc = await virtualDoc(document, position, this._engine, VirtualDocStyle.Block);
203-
if (vdoc) {
204-
return await withVirtualDocUri(vdoc, document.uri, "statementRange", async (uri: vscode.Uri) => {
204+
205+
if (!vdoc) {
206+
return undefined;
207+
}
208+
209+
return await withVirtualDocUri(vdoc, document.uri, "statementRange", async (uri: vscode.Uri) => {
210+
try {
205211
const result = await vscode.commands.executeCommand<hooks.StatementRange>(
206212
"vscode.executeStatementRangeProvider",
207213
uri,
208214
adjustedPosition(vdoc.language, position)
209215
);
210216
return { range: unadjustedRange(vdoc.language, result.range), code: result.code };
211-
});
212-
} else {
213-
return undefined;
214-
}
217+
} catch (err) {
218+
let hooks = hooksApi();
219+
220+
if (!hooks) {
221+
throw err;
222+
}
223+
224+
// TODO: Remove this once `apps/vscode/package.json` bumps to `"positron": "^2026.03.0"` or higher.
225+
// For now we avoid aggressive bumping due to https://github.com/posit-dev/positron/issues/11321.
226+
// We can't use `semver.lt()` because calendar versioning isn't compatible with semver due to the
227+
// leading `0` in `03`. Instead, we use lexicographic string comparison and rely on the year and
228+
// month to be zero padded so sorting always works correctly.
229+
if (hooks.version < "2026.03.0") {
230+
throw err;
231+
}
232+
233+
if (err instanceof hooks.StatementRangeSyntaxError) {
234+
// Rethrow syntax error with unadjusted line number, so Positron's notification will
235+
// jump to the correct line
236+
throw new hooks.StatementRangeSyntaxError(err.line ? unadjustedLine(vdoc.language, err.line) : undefined);
237+
} else {
238+
// Rethrow unrecognized error
239+
throw err;
240+
}
241+
}
242+
});
215243
};
216244
}
217245

apps/vscode/src/vdoc/vdoc.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -240,13 +240,21 @@ export function isBlockOfLanguage(language: EmbeddedLanguage) {
240240
};
241241
}
242242

243-
// adjust position for inject
243+
// adjust line for inject
244+
export function adjustedLine(language: EmbeddedLanguage, line: number): number {
245+
return line + (language.inject?.length || 0);
246+
}
247+
248+
export function unadjustedLine(language: EmbeddedLanguage, line: number): number {
249+
return line - (language.inject?.length || 0);
250+
}
251+
244252
export function adjustedPosition(language: EmbeddedLanguage, pos: Position) {
245-
return new Position(pos.line + (language.inject?.length || 0), pos.character);
253+
return new Position(adjustedLine(language, pos.line), pos.character);
246254
}
247255

248256
export function unadjustedPosition(language: EmbeddedLanguage, pos: Position) {
249-
return new Position(pos.line - (language.inject?.length || 0), pos.character);
257+
return new Position(unadjustedLine(language, pos.line), pos.character);
250258
}
251259

252260
export function unadjustedRange(language: EmbeddedLanguage, range: Range) {

0 commit comments

Comments
 (0)