From 27d33bc0e55290df9f688ddc84ea604d5c8cea2f Mon Sep 17 00:00:00 2001 From: Kun Ren Date: Wed, 15 Dec 2021 16:35:19 +0800 Subject: [PATCH 01/11] Use cp.spawn --- src/helpViewer/helpProvider.ts | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/helpViewer/helpProvider.ts b/src/helpViewer/helpProvider.ts index 5ca646085..516099cff 100644 --- a/src/helpViewer/helpProvider.ts +++ b/src/helpViewer/helpProvider.ts @@ -2,7 +2,7 @@ import { Memento, window } from 'vscode'; import * as http from 'http'; import * as cp from 'child_process'; -import * as kill from 'tree-kill'; +// import * as kill from 'tree-kill'; import * as path from 'path'; import * as fs from 'fs'; import * as os from 'os'; @@ -40,8 +40,8 @@ export class HelpProvider { public async refresh(): Promise { if (this.cp.running) { - // this.cp.kill('SIGKILL'); - kill(this.cp.pid, 'SIGKILL'); // more reliable than cp.kill (?) + this.cp.kill('SIGKILL'); + // kill(this.cp.pid, 'SIGKILL'); // more reliable than cp.kill (?) } this.cp = this.launchRHelpServer(); await this.cp.port; @@ -55,16 +55,20 @@ export class HelpProvider { // 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 -f ` + - `${scriptPath}` - ); + const args: string[] = [ + '--silent', + '--slave', + '--no-save', + '--no-restore', + '-f', + scriptPath + ]; const cpOptions = { cwd: this.cwd, env: { ...process.env, 'VSCR_LIM': lim } }; - const childProcess: ChildProcessWithPort = cp.exec(cmd, cpOptions); + const childProcess: ChildProcessWithPort = cp.spawn(this.rPath, args, cpOptions); childProcess.running = true; let str = ''; @@ -175,8 +179,8 @@ export class HelpProvider { dispose(): void { if (this.cp.running) { - // this.cp.kill('SIGKILL'); - kill(this.cp.pid, 'SIGKILL'); + this.cp.kill('SIGKILL'); + // kill(this.cp.pid, 'SIGKILL'); } } } From 16b58264f907847fa9a0cce1b10feed5b20da73b Mon Sep 17 00:00:00 2001 From: Kun Ren Date: Thu, 16 Dec 2021 07:22:15 +0000 Subject: [PATCH 02/11] Use unquoted rPath in cp.spawn --- src/helpViewer/helpProvider.ts | 4 ++-- src/helpViewer/index.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/helpViewer/helpProvider.ts b/src/helpViewer/helpProvider.ts index 516099cff..ecf203709 100644 --- a/src/helpViewer/helpProvider.ts +++ b/src/helpViewer/helpProvider.ts @@ -285,13 +285,13 @@ export class AliasProvider { const re = new RegExp(`^.*?${lim}(.*)${lim}.*$`, 'ms'); const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'vscode-R-aliases')); const tempFile = path.join(tempDir, 'aliases.json'); - const cmd = `${this.rPath} --silent --no-save --no-restore --slave -f "${this.rScriptFile}" > "${tempFile}"`; + const cmd = `"${this.rPath}" --silent --no-save --no-restore --slave -f "${this.rScriptFile}" > "${tempFile}"`; let allPackageAliases: undefined | AllPackageAliases = undefined; try{ // execute R script 'getAliases.R' // aliases will be written to tempFile - cp.execSync(cmd, {cwd: this.cwd}); + cp.execSync(cmd, { cwd: this.cwd }); // read and parse aliases const txt = fs.readFileSync(tempFile, 'utf-8'); diff --git a/src/helpViewer/index.ts b/src/helpViewer/index.ts index fd2cfec7d..0f4913e98 100644 --- a/src/helpViewer/index.ts +++ b/src/helpViewer/index.ts @@ -29,7 +29,7 @@ export async function initializeHelp( void vscode.commands.executeCommand('setContext', 'r.helpViewer.show', true); // get the "vanilla" R path from config - const rPath = await getRpath(true); + const rPath = await getRpath(false); // get the current working directory from vscode const cwd = vscode.workspace.workspaceFolders?.length From a2462b242a20a5e58868d412403d4d5132af005d Mon Sep 17 00:00:00 2001 From: Kun Ren Date: Thu, 16 Dec 2021 08:54:45 +0000 Subject: [PATCH 03/11] Specify shell and detached --- src/helpViewer/helpProvider.ts | 4 +++- src/languageService.ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/helpViewer/helpProvider.ts b/src/helpViewer/helpProvider.ts index ecf203709..47f52a39a 100644 --- a/src/helpViewer/helpProvider.ts +++ b/src/helpViewer/helpProvider.ts @@ -65,7 +65,9 @@ export class HelpProvider { ]; const cpOptions = { cwd: this.cwd, - env: { ...process.env, 'VSCR_LIM': lim } + env: { ...process.env, 'VSCR_LIM': lim }, + shell: false, + detached: false, }; const childProcess: ChildProcessWithPort = cp.spawn(this.rPath, args, cpOptions); diff --git a/src/languageService.ts b/src/languageService.ts index 3ce3ec151..8ea947d28 100644 --- a/src/languageService.ts +++ b/src/languageService.ts @@ -45,7 +45,7 @@ export class LanguageService implements Disposable { console.log(`LANG: ${env.LANG}`); } - const options = { cwd: cwd, env: env }; + const options = { cwd: cwd, env: env, shell: false, detached: false }; const initArgs: string[] = config.get('lsp.args').concat('--quiet', '--slave'); const tcpServerOptions = () => new Promise((resolve, reject) => { From 79036f873cbda743c1b915cd43382459be5e8d39 Mon Sep 17 00:00:00 2001 From: Kun Ren Date: Fri, 17 Dec 2021 21:15:41 +0800 Subject: [PATCH 04/11] Update code --- src/helpViewer/helpProvider.ts | 32 ++++++++------------------------ src/languageService.ts | 26 ++++++++++---------------- src/rmarkdown/manager.ts | 31 ++++++------------------------- src/rmarkdown/preview.ts | 8 ++++---- src/util.ts | 25 +++++++++++++++++++++++++ 5 files changed, 53 insertions(+), 69 deletions(-) diff --git a/src/helpViewer/helpProvider.ts b/src/helpViewer/helpProvider.ts index 47f52a39a..8deda91ee 100644 --- a/src/helpViewer/helpProvider.ts +++ b/src/helpViewer/helpProvider.ts @@ -2,13 +2,13 @@ import { Memento, window } from 'vscode'; import * as http from 'http'; import * as cp from 'child_process'; -// import * as kill from 'tree-kill'; import * as path from 'path'; import * as fs from 'fs'; import * as os from 'os'; import * as rHelp from '.'; import { extensionContext } from '../extension'; +import { DisposableProcess, execCommand } from '../util'; export interface RHelpProviderOptions { // path of the R executable @@ -19,9 +19,8 @@ export interface RHelpProviderOptions { pkgListener?: () => void; } -type ChildProcessWithPort = cp.ChildProcess & { +type ChildProcessWithPort = DisposableProcess & { port?: number | Promise; - running?: boolean; }; // Class to forward help requests to a backgorund R instance that is running a help server @@ -39,10 +38,7 @@ export class HelpProvider { } public async refresh(): Promise { - if (this.cp.running) { - this.cp.kill('SIGKILL'); - // kill(this.cp.pid, 'SIGKILL'); // more reliable than cp.kill (?) - } + this.cp.dispose(); this.cp = this.launchRHelpServer(); await this.cp.port; } @@ -55,23 +51,15 @@ export class HelpProvider { // starts the background help server and waits forever to keep the R process running const scriptPath = extensionContext.asAbsolutePath('R/help/helpServer.R'); - const args: string[] = [ - '--silent', - '--slave', - '--no-save', - '--no-restore', - '-f', - scriptPath - ]; + const cmd = ( + `"${this.rPath}" --silent --slave --no-save --no-restore -f "${scriptPath}"` + ); const cpOptions = { cwd: this.cwd, env: { ...process.env, 'VSCR_LIM': lim }, - shell: false, - detached: false, }; - const childProcess: ChildProcessWithPort = cp.spawn(this.rPath, args, cpOptions); - childProcess.running = true; + const childProcess: ChildProcessWithPort = execCommand(cmd, cpOptions); let str = ''; // promise containing the port number of the process (or 0) @@ -99,7 +87,6 @@ export class HelpProvider { const exitHandler = () => { childProcess.port = 0; - childProcess.running = false; }; childProcess.on('exit', exitHandler); childProcess.on('error', exitHandler); @@ -180,10 +167,7 @@ export class HelpProvider { dispose(): void { - if (this.cp.running) { - this.cp.kill('SIGKILL'); - // kill(this.cp.pid, 'SIGKILL'); - } + this.cp.dispose(); } } diff --git a/src/languageService.ts b/src/languageService.ts index 8ea947d28..8cdd1becd 100644 --- a/src/languageService.ts +++ b/src/languageService.ts @@ -7,10 +7,9 @@ import os = require('os'); import path = require('path'); import net = require('net'); import url = require('url'); -import { spawn, ChildProcess } from 'child_process'; import { LanguageClient, LanguageClientOptions, StreamInfo, DocumentFilter, ErrorAction, CloseAction, RevealOutputChannelOn } from 'vscode-languageclient/node'; import { Disposable, workspace, Uri, TextDocument, WorkspaceConfiguration, OutputChannel, window, WorkspaceFolder } from 'vscode'; -import { getRpath } from './util'; +import { DisposableProcess, getRpath, execCommand } from './util'; export class LanguageService implements Disposable { private readonly clients: Map = new Map(); @@ -29,9 +28,9 @@ export class LanguageService implements Disposable { let client: LanguageClient; const debug = config.get('lsp.debug'); - const path = await getRpath(); + const rPath = await getRpath(); if (debug) { - console.log(`R binary: ${path}`); + console.log(`R path: ${rPath}`); } const use_stdio = config.get('lsp.use_stdio'); const env = Object.create(process.env); @@ -45,10 +44,10 @@ export class LanguageService implements Disposable { console.log(`LANG: ${env.LANG}`); } - const options = { cwd: cwd, env: env, shell: false, detached: false }; - const initArgs: string[] = config.get('lsp.args').concat('--quiet', '--slave'); + const options = { cwd: cwd, env: env }; + const initArgs: string[] = config.get('lsp.args').concat('--silent', '--slave'); - const tcpServerOptions = () => new Promise((resolve, reject) => { + const tcpServerOptions = () => new Promise((resolve, reject) => { // Use a TCP socket because of problems with blocking STDIO const server = net.createServer(socket => { // 'connection' listener @@ -66,14 +65,9 @@ export class LanguageService implements Disposable { // Listen on random port server.listen(0, '127.0.0.1', () => { const port = (server.address() as net.AddressInfo).port; - let args: string[]; - // The server is implemented in R - if (debug) { - args = initArgs.concat(['-e', `languageserver::run(port=${port},debug=TRUE)`]); - } else { - args = initArgs.concat(['-e', `languageserver::run(port=${port})`]); - } - const childProcess = spawn(path, args, options); + const expr = debug ? `languageserver::run(port=${port},debug=TRUE)` : `languageserver::run(port=${port})`; + const cmd = `"{path}" ${initArgs.join(' ')} -e ${expr}`; + const childProcess = execCommand(cmd, options); client.outputChannel.appendLine(`R Language Server (${childProcess.pid}) started`); childProcess.stderr.on('data', (chunk: Buffer) => { client.outputChannel.appendLine(chunk.toString()); @@ -122,7 +116,7 @@ export class LanguageService implements Disposable { } else { args = initArgs.concat(['-e', `languageserver::run()`]); } - client = new LanguageClient('r', 'R Language Server', { command: path, args: args, options: options }, clientOptions); + client = new LanguageClient('r', 'R Language Server', { command: rPath, args: args, options: options }, clientOptions); } else { client = new LanguageClient('r', 'R Language Server', tcpServerOptions, clientOptions); } diff --git a/src/rmarkdown/manager.ts b/src/rmarkdown/manager.ts index de4f761e8..b6fb63ed6 100644 --- a/src/rmarkdown/manager.ts +++ b/src/rmarkdown/manager.ts @@ -2,14 +2,13 @@ import * as util from '../util'; import * as vscode from 'vscode'; import * as cp from 'child_process'; import path = require('path'); +import { DisposableProcess, execCommand } from '../util'; export enum KnitWorkingDirectory { documentDirectory = 'document directory', workspaceRoot = 'workspace root', } -export type DisposableProcess = cp.ChildProcessWithoutNullStreams & vscode.Disposable; - export interface IKnitRejection { cp: DisposableProcess; wasCancelled: boolean; @@ -66,15 +65,7 @@ export abstract class RMarkdownManager { const scriptArgs = args.scriptArgs; const scriptPath = args.scriptPath; const fileName = args.fileName; - - const processArgs = [ - `--silent`, - `--slave`, - `--no-save`, - `--no-restore`, - `-f`, - `${scriptPath}` - ]; + const cmd = `"${this.rPath}" --silent --slave --no-save --no-restore -f "${scriptPath}"`; const processOptions: cp.SpawnOptions = { env: { ...process.env, @@ -84,21 +75,11 @@ export abstract class RMarkdownManager { }; let childProcess: DisposableProcess; - try { - childProcess = util.asDisposable( - cp.spawn( - `${this.rPath}`, - processArgs, - processOptions - ), - () => { - if (childProcess.kill('SIGKILL')) { - rMarkdownOutput.appendLine('[VSC-R] terminating R process'); - printOutput = false; - } - } - ); + childProcess = execCommand(cmd, processOptions, () => { + rMarkdownOutput.appendLine('[VSC-R] terminating R process'); + printOutput = false; + }); progress.report({ increment: 0, message: '0%' diff --git a/src/rmarkdown/preview.ts b/src/rmarkdown/preview.ts index 8d972334d..265bad40d 100644 --- a/src/rmarkdown/preview.ts +++ b/src/rmarkdown/preview.ts @@ -6,10 +6,10 @@ import path = require('path'); import crypto = require('crypto'); -import { config, readContent, setContext, escapeHtml, UriIcon, saveDocument, getRpath } from '../util'; +import { config, readContent, setContext, escapeHtml, UriIcon, saveDocument, getRpath, DisposableProcess } from '../util'; import { extensionContext, tmpDir } from '../extension'; import { knitDir } from './knit'; -import { DisposableProcess, RMarkdownManager } from './manager'; +import { RMarkdownManager } from './manager'; class RMarkdownPreview extends vscode.Disposable { title: string; @@ -27,7 +27,7 @@ class RMarkdownPreview extends vscode.Disposable { resourceViewColumn: vscode.ViewColumn, outputUri: vscode.Uri, filePath: string, RMarkdownPreviewManager: RMarkdownPreviewManager, useCodeTheme: boolean, autoRefresh: boolean) { super(() => { - this.cp?.kill('SIGKILL'); + this.cp?.dispose(); this.panel?.dispose(); this.fileWatcher?.close(); fs.removeSync(this.outputUri.fsPath); @@ -271,7 +271,7 @@ export class RMarkdownPreviewManager extends RMarkdownManager { public async updatePreview(preview?: RMarkdownPreview): Promise { const toUpdate = preview ?? this.activePreview?.preview; const previewUri = this.previewStore?.getFilePath(toUpdate); - toUpdate?.cp?.kill('SIGKILL'); + toUpdate?.cp?.dispose(); if (toUpdate) { const childProcess: DisposableProcess | void = await this.previewDocument(previewUri, toUpdate.title).catch(() => { diff --git a/src/util.ts b/src/util.ts index 8348ae58c..d3617ab8d 100644 --- a/src/util.ts +++ b/src/util.ts @@ -8,6 +8,7 @@ import * as vscode from 'vscode'; import * as cp from 'child_process'; import { rGuestService, isGuestSession } from './liveShare'; import { extensionContext } from './extension'; +import { kill } from 'process'; export function config(): vscode.WorkspaceConfiguration { return vscode.workspace.getConfiguration('r'); @@ -407,3 +408,27 @@ export function asDisposable(toDispose: T, disposeFunction: (...args: unknown extensionContext.subscriptions.push(toDispose as disposeType); return toDispose as disposeType; } + +export type DisposableProcess = cp.ChildProcessWithoutNullStreams & vscode.Disposable; +export function execCommand(command: string, options?: cp.CommonOptions, onDisposed?: () => unknown): DisposableProcess { + const proc = cp.exec(command, options); + let running = true; + const exitHandler = () => { + running = false; + }; + proc.on('exit', exitHandler); + proc.on('error', exitHandler); + const disposable = asDisposable(proc, () => { + if (running) { + if (process.platform === 'win32') { + kill(proc.pid); + } else { + proc.kill('SIGKILL'); + } + } + if (onDisposed) { + onDisposed(); + } + }); + return disposable; +} From 920f28fe0676b14d42a44640b842e0d22269e3da Mon Sep 17 00:00:00 2001 From: Kun Ren Date: Fri, 17 Dec 2021 21:16:43 +0800 Subject: [PATCH 05/11] Fix import --- src/rmarkdown/knit.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rmarkdown/knit.ts b/src/rmarkdown/knit.ts index 5c28ebc65..5b8c2c78b 100644 --- a/src/rmarkdown/knit.ts +++ b/src/rmarkdown/knit.ts @@ -4,9 +4,10 @@ import * as fs from 'fs-extra'; import path = require('path'); import yaml = require('js-yaml'); -import { RMarkdownManager, KnitWorkingDirectory, DisposableProcess } from './manager'; +import { RMarkdownManager, KnitWorkingDirectory } from './manager'; import { runTextInTerm } from '../rTerminal'; import { extensionContext, rmdPreviewManager } from '../extension'; +import { DisposableProcess } from '../util'; export let knitDir: KnitWorkingDirectory = util.config().get('rmarkdown.knit.defaults.knitWorkingDirectory') ?? undefined; From df0fc7a24a5bef23ebde895e55f422f254224ae1 Mon Sep 17 00:00:00 2001 From: Kun Ren Date: Fri, 17 Dec 2021 21:17:30 +0800 Subject: [PATCH 06/11] Fix path --- src/languageService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languageService.ts b/src/languageService.ts index 8cdd1becd..259c4d6da 100644 --- a/src/languageService.ts +++ b/src/languageService.ts @@ -66,7 +66,7 @@ export class LanguageService implements Disposable { server.listen(0, '127.0.0.1', () => { const port = (server.address() as net.AddressInfo).port; const expr = debug ? `languageserver::run(port=${port},debug=TRUE)` : `languageserver::run(port=${port})`; - const cmd = `"{path}" ${initArgs.join(' ')} -e ${expr}`; + const cmd = `"${rPath}" ${initArgs.join(' ')} -e ${expr}`; const childProcess = execCommand(cmd, options); client.outputChannel.appendLine(`R Language Server (${childProcess.pid}) started`); childProcess.stderr.on('data', (chunk: Buffer) => { From 3dd3cc50532dfea0df887062b040b824b0d47fd3 Mon Sep 17 00:00:00 2001 From: Kun Ren Date: Fri, 17 Dec 2021 21:21:37 +0800 Subject: [PATCH 07/11] Fix args --- src/languageService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languageService.ts b/src/languageService.ts index 259c4d6da..c88cdad94 100644 --- a/src/languageService.ts +++ b/src/languageService.ts @@ -66,7 +66,7 @@ export class LanguageService implements Disposable { server.listen(0, '127.0.0.1', () => { const port = (server.address() as net.AddressInfo).port; const expr = debug ? `languageserver::run(port=${port},debug=TRUE)` : `languageserver::run(port=${port})`; - const cmd = `"${rPath}" ${initArgs.join(' ')} -e ${expr}`; + const cmd = `"${rPath}" ${initArgs.join(' ')} -e "${expr}"`; const childProcess = execCommand(cmd, options); client.outputChannel.appendLine(`R Language Server (${childProcess.pid}) started`); childProcess.stderr.on('data', (chunk: Buffer) => { From 31db66a2657287ff3375b52dbd9b4046eda935f0 Mon Sep 17 00:00:00 2001 From: Kun Ren Date: Fri, 17 Dec 2021 22:44:40 +0800 Subject: [PATCH 08/11] Fix import --- src/util.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util.ts b/src/util.ts index d3617ab8d..dcd1b9f8a 100644 --- a/src/util.ts +++ b/src/util.ts @@ -8,7 +8,7 @@ import * as vscode from 'vscode'; import * as cp from 'child_process'; import { rGuestService, isGuestSession } from './liveShare'; import { extensionContext } from './extension'; -import { kill } from 'process'; +import * as kill from 'tree-kill'; export function config(): vscode.WorkspaceConfiguration { return vscode.workspace.getConfiguration('r'); From 1a806bc0596af9f878df7cc5001142dcc97d3d91 Mon Sep 17 00:00:00 2001 From: Kun Ren Date: Fri, 17 Dec 2021 23:12:01 +0800 Subject: [PATCH 09/11] Rename to exec --- src/helpViewer/helpProvider.ts | 4 ++-- src/languageService.ts | 4 ++-- src/rmarkdown/manager.ts | 4 ++-- src/util.ts | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/helpViewer/helpProvider.ts b/src/helpViewer/helpProvider.ts index 8deda91ee..29d7fb94e 100644 --- a/src/helpViewer/helpProvider.ts +++ b/src/helpViewer/helpProvider.ts @@ -8,7 +8,7 @@ import * as os from 'os'; import * as rHelp from '.'; import { extensionContext } from '../extension'; -import { DisposableProcess, execCommand } from '../util'; +import { DisposableProcess, exec } from '../util'; export interface RHelpProviderOptions { // path of the R executable @@ -59,7 +59,7 @@ export class HelpProvider { env: { ...process.env, 'VSCR_LIM': lim }, }; - const childProcess: ChildProcessWithPort = execCommand(cmd, cpOptions); + const childProcess: ChildProcessWithPort = exec(cmd, cpOptions); let str = ''; // promise containing the port number of the process (or 0) diff --git a/src/languageService.ts b/src/languageService.ts index c88cdad94..a6472886d 100644 --- a/src/languageService.ts +++ b/src/languageService.ts @@ -9,7 +9,7 @@ import net = require('net'); import url = require('url'); import { LanguageClient, LanguageClientOptions, StreamInfo, DocumentFilter, ErrorAction, CloseAction, RevealOutputChannelOn } from 'vscode-languageclient/node'; import { Disposable, workspace, Uri, TextDocument, WorkspaceConfiguration, OutputChannel, window, WorkspaceFolder } from 'vscode'; -import { DisposableProcess, getRpath, execCommand } from './util'; +import { DisposableProcess, getRpath, exec } from './util'; export class LanguageService implements Disposable { private readonly clients: Map = new Map(); @@ -67,7 +67,7 @@ export class LanguageService implements Disposable { const port = (server.address() as net.AddressInfo).port; const expr = debug ? `languageserver::run(port=${port},debug=TRUE)` : `languageserver::run(port=${port})`; const cmd = `"${rPath}" ${initArgs.join(' ')} -e "${expr}"`; - const childProcess = execCommand(cmd, options); + const childProcess = exec(cmd, options); client.outputChannel.appendLine(`R Language Server (${childProcess.pid}) started`); childProcess.stderr.on('data', (chunk: Buffer) => { client.outputChannel.appendLine(chunk.toString()); diff --git a/src/rmarkdown/manager.ts b/src/rmarkdown/manager.ts index b6fb63ed6..586c02607 100644 --- a/src/rmarkdown/manager.ts +++ b/src/rmarkdown/manager.ts @@ -2,7 +2,7 @@ import * as util from '../util'; import * as vscode from 'vscode'; import * as cp from 'child_process'; import path = require('path'); -import { DisposableProcess, execCommand } from '../util'; +import { DisposableProcess, exec } from '../util'; export enum KnitWorkingDirectory { documentDirectory = 'document directory', @@ -76,7 +76,7 @@ export abstract class RMarkdownManager { let childProcess: DisposableProcess; try { - childProcess = execCommand(cmd, processOptions, () => { + childProcess = exec(cmd, processOptions, () => { rMarkdownOutput.appendLine('[VSC-R] terminating R process'); printOutput = false; }); diff --git a/src/util.ts b/src/util.ts index dcd1b9f8a..abc6c14eb 100644 --- a/src/util.ts +++ b/src/util.ts @@ -410,7 +410,7 @@ export function asDisposable(toDispose: T, disposeFunction: (...args: unknown } export type DisposableProcess = cp.ChildProcessWithoutNullStreams & vscode.Disposable; -export function execCommand(command: string, options?: cp.CommonOptions, onDisposed?: () => unknown): DisposableProcess { +export function exec(command: string, options?: cp.CommonOptions, onDisposed?: () => unknown): DisposableProcess { const proc = cp.exec(command, options); let running = true; const exitHandler = () => { From 70e75adf57633ae3fcdf4094711d9db37d285ebe Mon Sep 17 00:00:00 2001 From: Kun Ren Date: Sat, 18 Dec 2021 00:33:24 +0800 Subject: [PATCH 10/11] Use cp.spawn on linux --- src/helpViewer/helpProvider.ts | 14 ++++++++++---- src/languageService.ts | 5 +++-- src/rmarkdown/manager.ts | 12 ++++++++++-- src/util.ts | 10 ++++++++-- 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/helpViewer/helpProvider.ts b/src/helpViewer/helpProvider.ts index 29d7fb94e..8a9531df9 100644 --- a/src/helpViewer/helpProvider.ts +++ b/src/helpViewer/helpProvider.ts @@ -51,15 +51,21 @@ export class HelpProvider { // 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 -f "${scriptPath}"` - ); + // const cmd = `${this.rPath} --silent --slave --no-save --no-restore -f "${scriptPath}"`; + const args = [ + '--slient', + '--slave', + '--no-save', + '--no-restore', + '-f', + scriptPath + ]; const cpOptions = { cwd: this.cwd, env: { ...process.env, 'VSCR_LIM': lim }, }; - const childProcess: ChildProcessWithPort = exec(cmd, cpOptions); + const childProcess: ChildProcessWithPort = exec(this.rPath, args, cpOptions); let str = ''; // promise containing the port number of the process (or 0) diff --git a/src/languageService.ts b/src/languageService.ts index a6472886d..8e3114529 100644 --- a/src/languageService.ts +++ b/src/languageService.ts @@ -66,8 +66,9 @@ export class LanguageService implements Disposable { server.listen(0, '127.0.0.1', () => { const port = (server.address() as net.AddressInfo).port; const expr = debug ? `languageserver::run(port=${port},debug=TRUE)` : `languageserver::run(port=${port})`; - const cmd = `"${rPath}" ${initArgs.join(' ')} -e "${expr}"`; - const childProcess = exec(cmd, options); + // const cmd = `${rPath} ${initArgs.join(' ')} -e "${expr}"`; + const args = initArgs.concat(['-e', expr]); + const childProcess = exec(rPath, args, options); client.outputChannel.appendLine(`R Language Server (${childProcess.pid}) started`); childProcess.stderr.on('data', (chunk: Buffer) => { client.outputChannel.appendLine(chunk.toString()); diff --git a/src/rmarkdown/manager.ts b/src/rmarkdown/manager.ts index 586c02607..102b26237 100644 --- a/src/rmarkdown/manager.ts +++ b/src/rmarkdown/manager.ts @@ -65,7 +65,15 @@ export abstract class RMarkdownManager { const scriptArgs = args.scriptArgs; const scriptPath = args.scriptPath; const fileName = args.fileName; - const cmd = `"${this.rPath}" --silent --slave --no-save --no-restore -f "${scriptPath}"`; + // const cmd = `${this.rPath} --silent --slave --no-save --no-restore -f "${scriptPath}"`; + const cpArgs = [ + '--slient', + '--slave', + '--no-save', + '--no-restore', + '-f', + scriptPath + ]; const processOptions: cp.SpawnOptions = { env: { ...process.env, @@ -76,7 +84,7 @@ export abstract class RMarkdownManager { let childProcess: DisposableProcess; try { - childProcess = exec(cmd, processOptions, () => { + childProcess = exec(this.rPath, cpArgs, processOptions, () => { rMarkdownOutput.appendLine('[VSC-R] terminating R process'); printOutput = false; }); diff --git a/src/util.ts b/src/util.ts index abc6c14eb..338902c71 100644 --- a/src/util.ts +++ b/src/util.ts @@ -410,8 +410,14 @@ export function asDisposable(toDispose: T, disposeFunction: (...args: unknown } export type DisposableProcess = cp.ChildProcessWithoutNullStreams & vscode.Disposable; -export function exec(command: string, options?: cp.CommonOptions, onDisposed?: () => unknown): DisposableProcess { - const proc = cp.exec(command, options); +export function exec(command: string, args?: ReadonlyArray, options?: cp.CommonOptions, onDisposed?: () => unknown): DisposableProcess { + let proc: cp.ChildProcess; + if (process.platform === 'linux') { + proc = cp.spawn(command, args, options); + } else { + const cmd = `"${command}" ${args.map(s => `"${s}"`).join(' ')}`; + proc = cp.exec(cmd, options); + } let running = true; const exitHandler = () => { running = false; From a1200f30e71fa881fbdd1106d6f3a3f041016a09 Mon Sep 17 00:00:00 2001 From: Kun Ren Date: Sat, 18 Dec 2021 00:38:44 +0800 Subject: [PATCH 11/11] Only use cp.exec on win32 --- src/util.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/util.ts b/src/util.ts index 338902c71..c5e568984 100644 --- a/src/util.ts +++ b/src/util.ts @@ -412,11 +412,11 @@ export function asDisposable(toDispose: T, disposeFunction: (...args: unknown export type DisposableProcess = cp.ChildProcessWithoutNullStreams & vscode.Disposable; export function exec(command: string, args?: ReadonlyArray, options?: cp.CommonOptions, onDisposed?: () => unknown): DisposableProcess { let proc: cp.ChildProcess; - if (process.platform === 'linux') { - proc = cp.spawn(command, args, options); - } else { + if (process.platform === 'win32') { const cmd = `"${command}" ${args.map(s => `"${s}"`).join(' ')}`; proc = cp.exec(cmd, options); + } else { + proc = cp.spawn(command, args, options); } let running = true; const exitHandler = () => {