-
Notifications
You must be signed in to change notification settings - Fork 139
Implement public facing StatementRangeSyntaxError
#11907
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
d111ca9
Implement `StatementRangeError` handling
DavisVaughan 59c065e
Rework as `StatementRangeRejection`
DavisVaughan 146e035
Align LSP interface too
DavisVaughan f5179c6
tmp
DavisVaughan 834643d
`message` is not required
DavisVaughan c19d4ea
Switch to rejection model
DavisVaughan 0ba73fc
Lowercase names
DavisVaughan 939103c
One more lowercase
DavisVaughan d24a205
Use single `unknown` `data` field
DavisVaughan 6dd2fc8
Add `getParseErrorLine()`
DavisVaughan 962e366
Move towards simpler `StatementRangeSyntaxError`
DavisVaughan a147fd1
Change parse to syntax
DavisVaughan 4b8e889
Temporarily require ark PR
DavisVaughan fc1065d
Oh it looks to be a full github ref
DavisVaughan d90423a
Update src/vs/workbench/contrib/positronConsole/browser/positronConso…
DavisVaughan 1f91c2e
Add Positron fences
DavisVaughan 60b8dd6
Tweak comment
DavisVaughan 7c6cd43
Add `default`s to all `switch` statements
DavisVaughan eddf95b
Step to pre-computed `newPosition` if `nextStatementRange` rejects
DavisVaughan 94086ce
Add basic language agnostic `provideStatementRange()` tests
DavisVaughan 077f0dd
Add tests for `RStatementRangeProvider`
DavisVaughan d2a5097
Use ark 0.1.229
DavisVaughan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,163 @@ | ||
| /*--------------------------------------------------------------------------------------------- | ||
| * Copyright (C) 2026 Posit Software, PBC. All rights reserved. | ||
| * Licensed under the Elastic License 2.0. See LICENSE.txt for license information. | ||
| *--------------------------------------------------------------------------------------------*/ | ||
|
|
||
| import './mocha-setup'; | ||
|
|
||
| import * as assert from 'assert'; | ||
| import * as positron from 'positron'; | ||
| import * as vscode from 'vscode'; | ||
| import * as testKit from './kit'; | ||
| import { createRandomFile, deleteFile } from './editor-utils'; | ||
|
|
||
| suite('RStatementRangeProvider', () => { | ||
| let sessionDisposable: vscode.Disposable; | ||
|
|
||
| suiteSetup(async function () { | ||
| const [, disposable] = await testKit.startR(); | ||
| sessionDisposable = disposable; | ||
| }); | ||
|
|
||
| suiteTeardown(async () => { | ||
| await sessionDisposable?.dispose(); | ||
| }); | ||
|
|
||
| test('single-line expression', async function () { | ||
| const code = ` | ||
| 1 + 1 | ||
| `.trimStart(); | ||
|
|
||
| await testKit.withDisposables(async (disposables) => { | ||
| const result = await getStatementRange( | ||
| disposables, | ||
| code, | ||
| new vscode.Position(0, 0) | ||
| ); | ||
| assert.ok(result, 'Expected a statement range result'); | ||
| assert.strictEqual(result.range.start.line, 0); | ||
| assert.strictEqual(result.range.start.character, 0); | ||
| assert.strictEqual(result.range.end.line, 0); | ||
| assert.strictEqual(result.range.end.character, 5); | ||
| }); | ||
| }); | ||
|
|
||
| test('multi-line block from first line', async function () { | ||
| const code = ` | ||
| for (i in 1:3) { | ||
| print(i) | ||
| } | ||
| `.trimStart(); | ||
|
|
||
| await testKit.withDisposables(async (disposables) => { | ||
| const result = await getStatementRange( | ||
| disposables, | ||
| code, | ||
| new vscode.Position(0, 0) | ||
| ); | ||
|
|
||
| assert.ok(result, 'Expected a statement range result'); | ||
| assert.strictEqual(result.range.start.line, 0); | ||
| assert.strictEqual(result.range.start.character, 0); | ||
| assert.strictEqual(result.range.end.line, 2); | ||
| assert.strictEqual(result.range.end.character, 1); | ||
| }); | ||
| }); | ||
|
|
||
| test('in a pipe chain', async function () { | ||
| const code = ` | ||
| 1 + 1 | ||
|
|
||
| df |> | ||
| mutate(y = x + 1) |> | ||
| mutate(z = x + y) | ||
| `.trimStart(); | ||
|
|
||
| await testKit.withDisposables(async (disposables) => { | ||
| const result = await getStatementRange( | ||
| disposables, | ||
| code, | ||
| new vscode.Position(3, 3) | ||
| ); | ||
|
|
||
| assert.ok(result, 'Expected a statement range result'); | ||
| assert.strictEqual(result.range.start.line, 2); | ||
| assert.strictEqual(result.range.start.character, 0); | ||
| assert.strictEqual(result.range.end.line, 4); | ||
| assert.strictEqual(result.range.end.character, 18); | ||
| }); | ||
| }); | ||
|
|
||
| test('cursor before syntax error works fine', async function () { | ||
| const code = ` | ||
| df |> | ||
| summarise(foo = mean(x)) | ||
|
|
||
| df |> | ||
| mutate(y = x \ 1 |> | ||
| mutate(z = x + y) | ||
| `.trimStart(); | ||
|
|
||
| await testKit.withDisposables(async (disposables) => { | ||
| const result = await getStatementRange( | ||
| disposables, | ||
| code, | ||
| new vscode.Position(1, 3) | ||
| ); | ||
|
|
||
| assert.ok(result, 'Expected a statement range result'); | ||
| assert.strictEqual(result.range.start.line, 0); | ||
| assert.strictEqual(result.range.start.character, 0); | ||
| assert.strictEqual(result.range.end.line, 1); | ||
| assert.strictEqual(result.range.end.character, 25); | ||
| }); | ||
| }); | ||
|
|
||
| test('cursor in syntax error throws StatementRangeSyntaxError', async function () { | ||
| const code = ` | ||
| df |> | ||
| summarise(foo = mean(x)) | ||
|
|
||
| df |> | ||
| mutate(y = x \ 1 |> | ||
| mutate(z = x + y) | ||
| `.trimStart(); | ||
|
|
||
| await testKit.withDisposables(async (disposables) => { | ||
| await assert.rejects( | ||
| () => getStatementRange( | ||
| disposables, | ||
| code, | ||
| new vscode.Position(4, 0) | ||
| ), | ||
| (err) => { | ||
| assert.ok(err instanceof positron.StatementRangeSyntaxError); | ||
| assert.strictEqual(err.line, 3); | ||
| return true; | ||
| } | ||
| ); | ||
| }); | ||
| }); | ||
| }); | ||
|
|
||
| /** | ||
| * Executes the statement range provider for an R file with the given contents | ||
| * at the given position. | ||
| */ | ||
| async function getStatementRange( | ||
| disposables: vscode.Disposable[], | ||
| contents: string, | ||
| position: vscode.Position | ||
| ): Promise<positron.StatementRange | undefined> { | ||
| const fileUri = await createRandomFile(contents, 'R'); | ||
| disposables.push({ dispose: () => deleteFile(fileUri) }); | ||
|
|
||
| const doc = await vscode.workspace.openTextDocument(fileUri); | ||
| await vscode.window.showTextDocument(doc); | ||
|
|
||
| return await vscode.commands.executeCommand<positron.StatementRange | undefined>( | ||
| 'vscode.executeStatementRangeProvider', | ||
| doc.uri, | ||
| position | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ark's custom LSP Request types for StatementRange
Mirrors the "error as data" model used by the main thread, and feels more correct from an LSP perspective compared to going through a jsonrpc error.