import * as vscode from 'vscode'; import { platform } from 'os'; import { RStartupArguments } from './debugProtocolModifications'; import * as net from 'net'; import path = require('path'); import fs = require('fs'); import winreg = require('winreg'); const packageJson = <{[key: string]: any}>(require('../package.json')); export function config(onlyDebugger: boolean = true): vscode.WorkspaceConfiguration { if(onlyDebugger){ return vscode.workspace.getConfiguration('r.debugger'); } else{ return vscode.workspace.getConfiguration('r'); } } function getRfromEnvPath(platform: string) { let splitChar: string = ':'; let fileExtension: string = ''; if (platform === 'win32') { splitChar = ';'; fileExtension = '.exe'; } const os_paths: string[] = process.env.PATH?.split(splitChar) || []; for (const os_path of os_paths) { const os_r_path: string = path.join(os_path, 'R' + fileExtension); if (fs.existsSync(os_r_path)) { return os_r_path; } } return ''; } export async function getRpathFromSystem(): Promise<string> { let rpath = ''; const platform: string = process.platform; rpath ||= getRfromEnvPath(platform); if ( !rpath && platform === 'win32') { // Find path from registry try { const key = new winreg({ hive: winreg.HKLM, key: '\\Software\\R-Core\\R', }); const item: winreg.RegistryItem = await new Promise((c, e) => key.get('InstallPath', (err, result) => err === null ? c(result) : e(err))); rpath = path.join(item.value, 'bin', 'R.exe'); } catch (e) { rpath = ''; } } return rpath; } export async function getRpath(quote: boolean=false, overwriteConfig?: string): Promise<string> { let rpath: string | undefined = undefined; const configEntry = ( process.platform === 'win32' ? 'rpath.windows' : process.platform === 'darwin' ? 'rpath.mac' : 'rpath.linux' ); // try the config entry specified in the function arg: if(overwriteConfig){ rpath = config().get<string>(overwriteConfig); } // try the os-specific config entry for the rpath: rpath ||= config(false).get<string>(configEntry); // read from path/registry: rpath ||= await getRpathFromSystem(); // represent all invalid paths (undefined, '', null) as '': rpath ||= ''; if(!rpath){ // inform user about missing R path: void vscode.window.showErrorMessage(`${process.platform} can't use R`); } else if(quote && /^[^'"].* .*[^'"]$/.exec(rpath)){ // if requested and rpath contains spaces, add quotes: rpath = `"${rpath}"`; } else if(process.platform === 'win32' && /^'.* .*'$/.exec(rpath)){ // replace single quotes with double quotes on windows rpath = rpath.replace(/^'(.*)'$/, '"$1"'); } return rpath; } export function getPortNumber(server?: net.Server): number { const address = server?.address(); if (typeof address === 'string' || !address) { return -1; } else { return address.port; } } export function timeout(ms: number): Promise<unknown> { return new Promise(resolve => setTimeout(resolve, ms)); } export async function getRStartupArguments(launchConfig: { env?: {[key: string]: string}; commandLineArgs?: string[]; launchDirectory?: string; } = {}): Promise<RStartupArguments> { const platform: string = process.platform; const rpath = await getRpath(true); const rArgs: string[] = [ '--quiet', '--no-save', (platform === 'win32' ? '--ess' : '--interactive') ]; // add user specified args const customArgs = config().get<Array<string>>('commandLineArgs', []); rArgs.push(...customArgs); rArgs.push(...(launchConfig.commandLineArgs || [])); const ret: RStartupArguments = { path: rpath, args: rArgs, cwd: launchConfig.launchDirectory, env: launchConfig.env }; if(rpath === ''){ void vscode.window.showErrorMessage(`${process.platform} can't find R`); } return ret; } export function getRDownloadLink(packageName: string): string{ let url: string = config().get<string>('packageURL', ''); if(url === ''){ const platform: string = process.platform; const version: string = String(packageJson.version); // e.g. "0.1.2" const urlBase = 'https://github.com/ManuelHentschel/VSCode-R-Debugger/releases/download/v' + version + '/' + packageName + '_' + version; if(platform === 'win32'){ url = urlBase + '.zip'; } else if(platform === 'darwin'){ url = urlBase + '.tgz'; } else{ url = urlBase + '.tar.gz'; } } return url; } export function getVSCodePackageVersion(): string { return String(packageJson.version); } export function escapeForRegex(text: string): string { return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string } export interface RequiredRPackageVersion { name?: string, required?: string, recommended?: string, warnIfNewer?: string } export function getRequiredRPackageVersion(): RequiredRPackageVersion { if(typeof packageJson.rPackageInfo === 'object'){ return <RequiredRPackageVersion>packageJson.rPackageInfo; } else{ return {}; } } export function escapeStringForR(s: string, quote: string='"'): string { if (s === undefined) { return 'NULL'; } else { return( quote + s.replace(/\\/g, '\\\\') .replace(RegExp(quote, 'g'), `\\${quote}`) .replace(/\n/g, '\\n') // .replace(/\r/g, "\\r") .replace(/\r/g, '') .replace(/\t/g, '\\t') .replace(/\f/g, '\\f') .replace(/\v/g, '\\v') + quote); } } export async function checkSettings(): Promise<boolean> { const config0 = vscode.workspace.getConfiguration('rdebugger'); const keys = Object.getOwnPropertyNames(config0); const deprecated = [ 'rterm', 'timeouts', 'checkVersion', 'trackTerminals' ]; const foundDeprecated = deprecated.filter((v) => checkDeprecated(config0, v)); console.log(keys); console.log(foundDeprecated); if(foundDeprecated.length === 0){ return false; } const ret1 = 'Open Settings'; const ret2 = 'Don\'t show again'; const ret = await vscode.window.showInformationMessage( `Configuration for R-Debugger has moved (affects: ${foundDeprecated.map(v => 'rdebugger.' + v).join(', ')}). Open settings?`, ret1, ret2 ); if(ret === ret1){ void vscode.commands.executeCommand('workbench.action.openSettings', '@ext:rdebugger.r-debugger'); } return ret === ret2; } function checkDeprecated(config: vscode.WorkspaceConfiguration, entry: string): boolean { const info = config.inspect(entry); const changed: boolean = !!(info && ( info.globalLanguageValue || info.globalValue || info.workspaceFolderLanguageValue || info.workspaceFolderValue || info.workspaceLanguageValue || info.workspaceValue )); return changed; }