Skip to content

Commit 1bd7b33

Browse files
committed
Working Positron VE statement range execution in r and python
1 parent c75cd1d commit 1bd7b33

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
@@ -263,7 +263,19 @@ class RunPreviousCellCommand extends RunCommand implements Command {
263263
}
264264
}
265265

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

278+
// Run the code at the cursor
267279
class RunCurrentCommand extends RunCommand implements Command {
268280
constructor(
269281
host: ExtensionHost,
@@ -340,95 +352,85 @@ class RunCurrentCommand extends RunCommand implements Command {
340352
context: CodeViewActiveBlockContext
341353
): Promise<void> {
342354
// get selection and active block
343-
let selection = context.selectedText;
355+
const selection = context.selectedText;
344356
const activeBlock = context.blocks.find(block => block.active);
345357

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

361-
// if the selection is empty and we are in Positron:
362-
// try to get the statement's range and use that as the selection
363-
if (selection.length <= 0 && activeBlock && hasHooks()) {
366+
// if in Positron
367+
if (hasHooks()) {
368+
if (activeBlock && selection.length <= 0) {
364369
const codeLines = lines(activeBlock.code)
365370
const vdoc = virtualDocForCode(codeLines, embeddedLanguage(activeBlock.language)!);
366371
if (vdoc) {
367372
const parentUri = Uri.file(editor.document.fileName);
368373
const injectedLines = (vdoc.language?.inject?.length ?? 0)
369374

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

445447
}
446448

447-
449+
// Run Cell and Advance
448450
class RunCurrentAdvanceCommand extends RunCommand implements Command {
449451
constructor(host: ExtensionHost, engine: MarkdownEngine) {
450452
super(host, engine);

0 commit comments

Comments
 (0)