diff --git a/R/getAliases.R b/R/help/getAliases.R similarity index 100% rename from R/getAliases.R rename to R/help/getAliases.R diff --git a/R/help/helpServer.R b/R/help/helpServer.R new file mode 100644 index 000000000..7b6d264b5 --- /dev/null +++ b/R/help/helpServer.R @@ -0,0 +1,11 @@ +# get values from extension-set env values +lim <- Sys.getenv("VSCR_LIM") + +cat( + lim, + tools::startDynamicHelp(), + lim, + sep = "" +) + +while (TRUE) Sys.sleep(1) diff --git a/R/rmarkdown/knit.R b/R/rmarkdown/knit.R new file mode 100644 index 000000000..895fa27b5 --- /dev/null +++ b/R/rmarkdown/knit.R @@ -0,0 +1,22 @@ +# requires rmarkdown package to run (and knitr) +if (!requireNamespace(rmarkdown, quietly = TRUE)) { + stop("Knitting requires the {rmarkdown} package.") +} + +# get values from extension-set env values + +knit_dir <- Sys.getenv("VSCR_KNIT_DIR") +lim <- Sys.getenv("VSCR_LIM") +knit_command <- Sys.getenv("VSCR_KNIT_COMMAND") + +# set the knitr chunk eval directory +# mainly affects source calls +knitr::opts_knit[["set"]](root.dir = knit_dir) + +# render and get file output location for use in extension +cat( + lim, + eval(parse(text = knit_command)), + lim, + sep = "" +) \ No newline at end of file diff --git a/R/rmarkdown/preview.R b/R/rmarkdown/preview.R new file mode 100644 index 000000000..ecf5e6cc4 --- /dev/null +++ b/R/rmarkdown/preview.R @@ -0,0 +1,29 @@ +# requires rmarkdown package to run (and knitr) +if (!requireNamespace(rmarkdown, quietly = TRUE)) { + stop("Previewing documents requires the {rmarkdown} package.") +} + +# get values from extension-set env values + +knit_dir <- Sys.getenv("VSCR_KNIT_DIR") +lim <- Sys.getenv("VSCR_LIM") +file_path <- Sys.getenv("VSCR_FILE_PATH") +output_file_loc <- Sys.getenv("VSCR_OUTPUT_FILE") +tmp_dir <- Sys.getenv("VSCR_TMP_DIR") + +# set the knitr chunk eval directory +# mainly affects source calls +knitr::opts_knit[["set"]](root.dir = knit_dir) + +# render and get file output location for use in extension +cat( + lim, + rmarkdown::render( + file_path, + output_format = rmarkdown::html_document(), + output_file = output_file_loc, + intermediates_dir = tmp_dir + ), + lim, + sep = "" +) \ No newline at end of file diff --git a/R/.Rprofile b/R/session/.Rprofile similarity index 100% rename from R/.Rprofile rename to R/session/.Rprofile diff --git a/R/init.R b/R/session/init.R similarity index 100% rename from R/init.R rename to R/session/init.R diff --git a/R/rstudioapi.R b/R/session/rstudioapi.R similarity index 100% rename from R/rstudioapi.R rename to R/session/rstudioapi.R diff --git a/R/rstudioapi_util.R b/R/session/rstudioapi_util.R similarity index 100% rename from R/rstudioapi_util.R rename to R/session/rstudioapi_util.R diff --git a/R/vsc.R b/R/session/vsc.R similarity index 100% rename from R/vsc.R rename to R/session/vsc.R diff --git a/src/helpViewer/helpProvider.ts b/src/helpViewer/helpProvider.ts index 510893e03..12f9888e3 100644 --- a/src/helpViewer/helpProvider.ts +++ b/src/helpViewer/helpProvider.ts @@ -8,6 +8,7 @@ import * as fs from 'fs'; import * as os from 'os'; import * as rHelp from '.'; +import { extensionContext } from '../extension'; export interface RHelpProviderOptions { // path of the R executable @@ -39,12 +40,14 @@ export class HelpProvider { const re = new RegExp(`.*${lim}(.*)${lim}.*`, 'ms'); // starts the background help server and waits forever to keep the R process running + const scriptPath = extensionContext.asAbsolutePath('R/help/helpServer.R'); const cmd = ( - `${this.rPath} --silent --slave --no-save --no-restore -e ` + - `"cat('${lim}', tools::startDynamicHelp(), '${lim}', sep=''); while(TRUE) Sys.sleep(1)" ` + `${this.rPath} --silent --slave --no-save --no-restore -f ` + + `${scriptPath}` ); const cpOptions = { - cwd: this.cwd + cwd: this.cwd, + env: { ...process.env, 'VSCR_LIM': lim } }; this.cp = cp.exec(cmd, cpOptions); @@ -53,7 +56,7 @@ export class HelpProvider { const outputPromise = new Promise((resolve) => { this.cp.stdout?.on('data', (data) => { try{ - // eslint-disable-next-line + // eslint-disable-next-line str += data.toString(); } catch(e){ resolve(''); @@ -92,7 +95,7 @@ export class HelpProvider { content?: string, redirect?: string } - + // forward request to R instance // below is just a complicated way of getting a http response from the help server let url = `http://localhost:${this.port}/${requestPath}`; @@ -159,7 +162,7 @@ export interface AliasProviderArgs { cwd?: string; // getAliases.R rScriptFile: string; - + persistentState: Memento; } @@ -283,7 +286,7 @@ export class AliasProvider { } // get from R - this.allPackageAliases = null; + this.allPackageAliases = null; const lim = '---vsc---'; // must match the lim used in R! const re = new RegExp(`^.*?${lim}(.*)${lim}.*$`, 'ms'); const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'vscode-R-aliases-')); diff --git a/src/helpViewer/index.ts b/src/helpViewer/index.ts index 59a64fade..321bdce8b 100644 --- a/src/helpViewer/index.ts +++ b/src/helpViewer/index.ts @@ -41,7 +41,7 @@ export async function initializeHelp(context: vscode.ExtensionContext, rExtensio const rHelpOptions: HelpOptions = { webviewScriptPath: context.asAbsolutePath('/html/help/script.js'), webviewStylePath: context.asAbsolutePath('html/help/theme.css'), - rScriptFile: context.asAbsolutePath('R/getAliases.R'), + rScriptFile: context.asAbsolutePath('R/help/getAliases.R'), rPath: rPath, cwd: cwd, persistentState: persistentState diff --git a/src/rTerminal.ts b/src/rTerminal.ts index f1bba02b6..2fe936769 100644 --- a/src/rTerminal.ts +++ b/src/rTerminal.ts @@ -98,8 +98,8 @@ export async function createRTerm(preserveshow?: boolean): Promise { shellPath: termPath, shellArgs: termOpt, }; - const newRprofile = extensionContext.asAbsolutePath(path.join('R', '.Rprofile')); - const initR = extensionContext.asAbsolutePath(path.join('R', 'init.R')); + const newRprofile = extensionContext.asAbsolutePath(path.join('R', 'session', '.Rprofile')); + const initR = extensionContext.asAbsolutePath(path.join('R', 'session','init.R')); if (config().get('sessionWatcher')) { termOptions.env = { R_PROFILE_USER_OLD: process.env.R_PROFILE_USER, diff --git a/src/rmarkdown/knit.ts b/src/rmarkdown/knit.ts index 25a5afbd0..149d39290 100644 --- a/src/rmarkdown/knit.ts +++ b/src/rmarkdown/knit.ts @@ -6,7 +6,7 @@ import yaml = require('js-yaml'); import { RMarkdownManager, KnitWorkingDirectory, DisposableProcess } from './manager'; import { runTextInTerm } from '../rTerminal'; -import { rmdPreviewManager } from '../extension'; +import { extensionContext, rmdPreviewManager } from '../extension'; export let knitDir: KnitWorkingDirectory = util.config().get('rmarkdown.knit.defaults.knitWorkingDirectory') ?? undefined; @@ -29,18 +29,17 @@ export class RMarkdownKnitManager extends RMarkdownManager { private async renderDocument(rDocumentPath: string, docPath: string, docName: string, yamlParams: IYamlFrontmatter, outputFormat?: string): Promise { const openOutfile: boolean = util.config().get('rmarkdown.knit.openOutputFile') ?? false; const knitWorkingDir = this.getKnitDir(knitDir, docPath); - const knitWorkingDirText = knitWorkingDir ? `'${knitWorkingDir}'` : `NULL`; + const knitWorkingDirText = knitWorkingDir ? `${knitWorkingDir}` : `NULL`; const knitCommand = await this.getKnitCommand(yamlParams, rDocumentPath, outputFormat); this.rPath = await util.getRpath(); const lim = '---vsc---'; const re = new RegExp(`.*${lim}(.*)${lim}.*`, 'gms'); - const cmd = - `knitr::opts_knit[['set']](root.dir = ${knitWorkingDirText});` + - `cat('${lim}', ` + - `${knitCommand}, ` + - `'${lim}',` + - `sep='')`; + const scriptValues = { + 'VSCR_KNIT_DIR': knitWorkingDirText, + 'VSCR_LIM': lim, + 'VSCR_KNIT_COMMAND': knitCommand + }; const callback = (dat: string) => { const outputUrl = re.exec(dat)?.[0]?.replace(re, '$1'); @@ -67,7 +66,8 @@ export class RMarkdownKnitManager extends RMarkdownManager { { fileName: docName, filePath: rDocumentPath, - cmd: cmd, + scriptArgs: scriptValues, + scriptPath: extensionContext.asAbsolutePath('R/rmarkdown/knit.R'), rCmd: knitCommand, rOutputFormat: outputFormat, callback: callback diff --git a/src/rmarkdown/manager.ts b/src/rmarkdown/manager.ts index 0ef0a45a1..6bd98113c 100644 --- a/src/rmarkdown/manager.ts +++ b/src/rmarkdown/manager.ts @@ -20,7 +20,8 @@ const rMarkdownOutput: vscode.OutputChannel = vscode.window.createOutputChannel( interface IKnitArgs { filePath: string; fileName: string; - cmd: string; + scriptArgs: Record; + scriptPath: string; rCmd?: string; rOutputFormat?: string; callback: (...args: unknown[]) => boolean; @@ -61,18 +62,23 @@ export abstract class RMarkdownManager { return await new Promise( (resolve, reject) => { - const cmd = args.cmd; + const scriptArgs = args.scriptArgs; + const scriptPath = args.scriptPath; const fileName = args.fileName; + const processArgs = [ `--silent`, `--slave`, `--no-save`, `--no-restore`, - `-e`, - cmd + `-f`, + `${scriptPath}` ]; const processOptions = { - env: process.env + env: { + ...process.env, + ...scriptArgs + } }; let childProcess: DisposableProcess; diff --git a/src/rmarkdown/preview.ts b/src/rmarkdown/preview.ts index 4f72ecd8b..ca4d13f77 100644 --- a/src/rmarkdown/preview.ts +++ b/src/rmarkdown/preview.ts @@ -283,21 +283,20 @@ export class RMarkdownPreviewManager extends RMarkdownManager { private async previewDocument(filePath: string, fileName?: string, viewer?: vscode.ViewColumn, currentViewColumn?: vscode.ViewColumn): Promise { const knitWorkingDir = this.getKnitDir(knitDir, filePath); - const knitWorkingDirText = knitWorkingDir ? `'${knitWorkingDir}'` : `NULL`; + const knitWorkingDirText = knitWorkingDir ? `${knitWorkingDir}` : `NULL`; this.rPath = await getRpath(); const lim = '---vsc---'; const re = new RegExp(`.*${lim}(.*)${lim}.*`, 'ms'); const outputFile = path.join(tmpDir(), crypto.createHash('sha256').update(filePath).digest('hex') + '.html'); - const cmd = ( - `knitr::opts_knit[['set']](root.dir = ${knitWorkingDirText});` + - `cat('${lim}', rmarkdown::render(` + - `'${filePath.replace(/\\/g, '/')}',` + - `output_format = rmarkdown::html_document(),` + - `output_file = '${outputFile.replace(/\\/g, '/')}',` + - `intermediates_dir = '${tmpDir().replace(/\\/g, '/')}'), '${lim}',` + - `sep='')` - ); + const scriptValues = { + 'VSCR_KNIT_DIR' : knitWorkingDirText, + 'VSCR_LIM': lim, + 'VSCR_FILE_PATH': filePath.replace(/\\/g, '/'), + 'VSCR_OUTPUT_FILE': outputFile.replace(/\\/g, '/'), + 'VSCR_TMP_DIR': tmpDir().replace(/\\/g, '/') + }; + const callback = (dat: string, childProcess: DisposableProcess) => { const outputUrl = re.exec(dat)?.[0]?.replace(re, '$1'); @@ -329,7 +328,8 @@ export class RMarkdownPreviewManager extends RMarkdownManager { { fileName: fileName, filePath: filePath, - cmd: cmd, + scriptPath: extensionContext.asAbsolutePath('R/rmarkdown/preview.R'), + scriptArgs: scriptValues, rOutputFormat: 'html preview', callback: callback, onRejection: onRejected diff --git a/src/session.ts b/src/session.ts index 2bcc9a7c9..67f0f8515 100644 --- a/src/session.ts +++ b/src/session.ts @@ -43,7 +43,7 @@ export function deploySessionWatcher(extensionPath: string): void { console.info(`[deploySessionWatcher] extensionPath: ${extensionPath}`); resDir = path.join(extensionPath, 'dist', 'resources'); - const initPath = path.join(extensionPath, 'R', 'init.R'); + const initPath = path.join(extensionPath, 'R', 'session', 'init.R'); const linkPath = path.join(homeExtDir(), 'init.R'); fs.writeFileSync(linkPath, `local(source("${initPath.replace(/\\/g, '\\\\')}", chdir = TRUE, local = TRUE))\n`);