Skip to content

refactor: Replace temp-file + setTimeout pattern with positron.runtime.evaluateCode() #14

@atsyplenkov

Description

@atsyplenkov

Summary

After exploring the Positron source code (posit-dev/positron), I identified a significant architectural improvement for how the extension communicates with the R runtime. The current approach of writing to temporary files and polling with setTimeout is fragile and race-condition prone. Positron provides a purpose-built API for this exact use case: positron.runtime.evaluateCode().

Current Pattern (Anti-pattern)

Multiple files (src/makeExplicit.js, src/fixLint.js, src/checkR.js) follow this flow:

  1. Write data to a temp file.
  2. Call positron.runtime.executeCode('r', ...).
  3. await new Promise(resolve => setTimeout(resolve, 100)).
  4. Read the temp file back.
  5. Parse string prefixes like Error_001, Error_002.

This is unreliable because:

  • 100ms may not be enough on slow systems.
  • It leaves orphaned temp files if an error occurs before cleanup.
  • It requires string-based error signaling instead of proper exception handling.

Recommended Pattern

Positron exposes evaluateCode() specifically for silent, promise-based evaluation:

export function evaluateCode(
    languageId: string,
    code: string,
    cancellationToken?: vscode.CancellationToken,
    sessionId?: string
): Thenable<EvalResult>;

export interface EvalResult {
    result: any;
    output: string;
}

Example: checkR.js

Before:

const r_check_command = `writeLines(as.character(...), con = '${normalizedPath}')`;
await positron.runtime.executeCode('r', r_check_command, false, false, positron.RuntimeCodeExecutionMode.Silent);
await new Promise(resolve => setTimeout(resolve, 100));
const output = await fs.readFile(tempFilePath, { encoding: 'utf8' });

After:

const evalResult = await positron.runtime.evaluateCode(
    'r',
    'as.character(c(nchar(system.file(package = "pedant")) != 0, nchar(system.file(package = "remotes")) != 0, nchar(system.file(package = "pak")) != 0))'
);
const [pedant, remotes, pak] = evalResult.result;

Other Improvements

  1. Use finally blocks for temp file cleanup to prevent orphaned files.
  2. Add try/catch around evaluateCode to handle R execution failures gracefully.
  3. Use vscode.window.withProgress when running pedant::explicitize() on large selections.
  4. Add @types/positron reference in jsconfig.json for better IntelliSense.

References

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions