Skip to content

Commit 3e39dce

Browse files
committed
Working Positron VE statement range execution in r and python
1 parent a0d5086 commit 3e39dce

File tree

1 file changed

+70
-68
lines changed

1 file changed

+70
-68
lines changed

apps/vscode/src/providers/cell/commands.ts

Lines changed: 70 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,19 @@ class RunPreviousCellCommand extends RunCommand implements Command {
260260
}
261261
}
262262

263+
// More permissive type than `Position` so its easier to construct via a literal
264+
type LineAndCharPos = { line: number, character: number }
265+
// More permissive type than `Range` so its easier to construct via a literal
266+
type LineAndCharRange = { start: LineAndCharPos, end: LineAndCharPos }
267+
268+
function extractRangeFromCode(code: string, range: LineAndCharRange): string {
269+
const extractedRange = lines(code).slice(range.start.line, range.end.line + 1)
270+
extractedRange[0] = extractedRange[0].slice(range.start.character)
271+
extractedRange[extractedRange.length - 1] = extractedRange[extractedRange.length - 1].slice(0, range.end.character)
272+
return extractedRange.join('\n')
273+
}
263274

275+
// Run the code at the cursor
264276
class RunCurrentCommand extends RunCommand implements Command {
265277
constructor(
266278
host: ExtensionHost,
@@ -337,95 +349,85 @@ class RunCurrentCommand extends RunCommand implements Command {
337349
context: CodeViewActiveBlockContext
338350
): Promise<void> {
339351
// get selection and active block
340-
let selection = context.selectedText;
352+
const selection = context.selectedText;
341353
const activeBlock = context.blocks.find(block => block.active);
342354

343-
// idea: first check that we are in Positron
344-
// and leave the others untouched
345-
346-
// if the selection is empty and this isn't a knitr document then it resolves to run cell
347-
if (false) {
348-
if (activeBlock) {
349-
const executor = await this.cellExecutorForLanguage(activeBlock.language, editor.document, this.engine_);
350-
if (executor) {
351-
await executeInteractive(executor, [activeBlock.code], editor.document);
352-
await activateIfRequired(editor);
353-
}
355+
const exec = async (action: CodeViewSelectionAction, selection: string) => {
356+
const executor = await this.cellExecutorForLanguage(context.activeLanguage, editor.document, this.engine_);
357+
if (executor) {
358+
await executeInteractive(executor, [selection], editor.document);
359+
await editor.setBlockSelection(context, action);
354360
}
355-
} else {
356-
let action: CodeViewSelectionAction | undefined;
361+
}
357362

358-
// if the selection is empty and we are in Positron:
359-
// try to get the statement's range and use that as the selection
360-
if (selection.length <= 0 && activeBlock && hasHooks()) {
363+
// if in Positron
364+
if (hasHooks()) {
365+
if (activeBlock && selection.length <= 0) {
361366
const codeLines = lines(activeBlock.code)
362367
const vdoc = virtualDocForCode(codeLines, embeddedLanguage(activeBlock.language)!);
363368
if (vdoc) {
364369
const parentUri = Uri.file(editor.document.fileName);
365370
const injectedLines = (vdoc.language?.inject?.length ?? 0)
366371

367-
const positionIntoVdoc = (p: { line: number, character: number }) =>
372+
const positionIntoVdoc = (p: LineAndCharPos) =>
368373
new Position(p.line + injectedLines, p.character)
369-
const positionOutOfVdoc = (p: { line: number, character: number }) =>
374+
const positionOutOfVdoc = (p: LineAndCharPos) =>
370375
new Position(p.line - injectedLines, p.character)
371-
372-
const result = await withVirtualDocUri(vdoc, parentUri, "statementRange", async (uri) => {
373-
return await commands.executeCommand<StatementRange>(
374-
"vscode.executeStatementRangeProvider",
375-
uri,
376-
positionIntoVdoc(context.selection.start)
377-
);
378-
});
379-
const { range, code } = result
380-
if (code === undefined) return
381-
const adjustedEnd = positionOutOfVdoc(range.end)
382-
383-
selection = code
384-
action = "nextline";
385-
386-
// BEGIN ref: https://github.com/posit-dev/positron/blob/main/src/vs/workbench/contrib/positronConsole/browser/positronConsoleActions.ts#L428
387-
// strategy from Positron using `StatementRangeProvider` to find range of next statement
388-
// and move cursor based on that.
389-
if (adjustedEnd.line + 1 <= codeLines.length) {
390-
const nextStatementRange = await withVirtualDocUri(vdoc, parentUri, "statementRange", async (uri) => {
376+
const rangeOutOfVdoc = (r: Range): LineAndCharRange => ({
377+
start: positionOutOfVdoc(r.start),
378+
end: positionOutOfVdoc(r.end)
379+
})
380+
const getStatementRange = async (pos: LineAndCharPos) => {
381+
const result = await withVirtualDocUri(vdoc, parentUri, "statementRange", async (uri) => {
391382
return await commands.executeCommand<StatementRange>(
392383
"vscode.executeStatementRangeProvider",
393384
uri,
394-
positionIntoVdoc(new Position(adjustedEnd.line + 1, 1)) // look for statement at line after current statement
385+
positionIntoVdoc(pos)
395386
);
396387
});
397-
const nextStatement = {
398-
start: positionOutOfVdoc(nextStatementRange.range.start),
399-
end: positionOutOfVdoc(nextStatementRange.range.end)
400-
};
401-
if (nextStatement.start.line > adjustedEnd.line) {
402-
action = nextStatement.start
403-
// the nextStatement may start before & end after the current statement if e.g. inside a function:
404-
} else if (nextStatement.end.line > adjustedEnd.line) {
405-
action = nextStatement.end
388+
return rangeOutOfVdoc(result.range)
389+
}
390+
391+
const range = await getStatementRange(context.selection.start)
392+
const code = extractRangeFromCode(activeBlock.code, range)
393+
394+
// BEGIN ref: https://github.com/posit-dev/positron/blob/main/src/vs/workbench/contrib/positronConsole/browser/positronConsoleActions.ts#L428
395+
// strategy from Positron using `StatementRangeProvider` to find range of next statement
396+
// and move cursor based on that.
397+
if (range.end.line + 1 <= codeLines.length) {
398+
// get range of statement at line after current statement)
399+
const nextRange = await getStatementRange(new Position(range.end.line + 1, 1))
400+
401+
if (nextRange.start.line > range.end.line) {
402+
exec(nextRange.start, code)
403+
// the next statement range may start before & end after the current statement if e.g. inside a function:
404+
} else if (nextRange.end.line > range.end.line) {
405+
exec(nextRange.end, code)
406+
} else {
407+
exec("nextline", code)
406408
}
409+
} else {
410+
exec("nextline", code)
407411
}
408412
// END ref.
409413
}
410414
}
411-
412-
// if the selection is still empty:
413-
// take the whole line as the selection
414-
if (selection.length <= 0 && activeBlock) {
415-
selection = lines(activeBlock.code)[context.selection.start.line];
416-
action = "nextline";
417-
}
418-
419-
// run code
420-
const executor = await this.cellExecutorForLanguage(context.activeLanguage, editor.document, this.engine_);
421-
if (executor) {
422-
await executeInteractive(executor, [selection], editor.document);
423-
424-
// advance cursor if necessary
425-
//
426-
if (action) {
427-
console.log('action!!!', action, this.id)
428-
await editor.setBlockSelection(context, action);
415+
// if not in Positron
416+
} else {
417+
// if the selection is empty and this isn't a knitr document then it resolves to run cell
418+
if (selection.length <= 0 && !isKnitrDocument(editor.document, this.engine_)) {
419+
if (activeBlock) {
420+
const executor = await this.cellExecutorForLanguage(activeBlock.language, editor.document, this.engine_);
421+
if (executor) {
422+
await executeInteractive(executor, [activeBlock.code], editor.document);
423+
await activateIfRequired(editor);
424+
}
425+
}
426+
} else {
427+
if (selection.length > 0) {
428+
exec("nextline", selection)
429+
} else if (activeBlock) { // if the selection is empty take the whole line as the selection
430+
exec("nextline", lines(activeBlock.code)[context.selection.start.line])
429431
}
430432
}
431433
}
@@ -441,7 +443,7 @@ class RunSelectionCommand extends RunCurrentCommand implements Command {
441443

442444
}
443445

444-
446+
// Run Cell and Advance
445447
class RunCurrentAdvanceCommand extends RunCommand implements Command {
446448
constructor(host: ExtensionHost, engine: MarkdownEngine) {
447449
super(host, engine);

0 commit comments

Comments
 (0)