From 783ab70d88db7e3a56ea4b10f89bcf2032900615 Mon Sep 17 00:00:00 2001 From: Jonathan McPherson Date: Wed, 27 Nov 2024 09:30:07 -0800 Subject: [PATCH] move kernel spec to its own file; add xdg-portable --- extensions/positron-r/package.json | 3 +- extensions/positron-r/package.nls.json | 4 +- extensions/positron-r/src/kernel-spec.ts | 125 +++++++++++++++++++ extensions/positron-r/src/kernel.ts | 2 +- extensions/positron-r/src/runtime-manager.ts | 3 +- extensions/positron-r/src/session.ts | 117 ----------------- extensions/positron-r/yarn.lock | 21 ++++ 7 files changed, 153 insertions(+), 122 deletions(-) create mode 100644 extensions/positron-r/src/kernel-spec.ts diff --git a/extensions/positron-r/package.json b/extensions/positron-r/package.json index f28f316b3f7..686c78ff4c3 100644 --- a/extensions/positron-r/package.json +++ b/extensions/positron-r/package.json @@ -674,7 +674,8 @@ "split2": "^4.2.0", "vscode-languageclient": "^9.0.1", "web-tree-sitter": "^0.20.8", - "which": "^3.0.0" + "which": "^3.0.0", + "xdg-portable": "^10.6.0" }, "peerDependencies": { "@vscode/windows-registry": "^1.0.0" diff --git a/extensions/positron-r/package.nls.json b/extensions/positron-r/package.nls.json index f85d9ee515e..258e7f4ba15 100644 --- a/extensions/positron-r/package.nls.json +++ b/extensions/positron-r/package.nls.json @@ -61,8 +61,8 @@ "r.configuration.diagnostics.enable.description": "Enable R diagnostics globally", "r.configuration.taskHyperlinks.description": "Turn on experimental support for hyperlinks in package development tasks", "r.configuration.defaultRepositories.description": "The default repositories to use for R package installation, if no repository is otherwise specified in R startup scripts (restart Positron to apply).\n\nThe default repositories will be set as the `repos` option in R.", - "r.configuration.defaultRepositories.auto.description": "Automatically choose a default repository, or use a .conf file if it exists.", + "r.configuration.defaultRepositories.auto.description": "Automatically choose a default repository, or use a repos.conf file if it exists.", "r.configuration.defaultRepositories.rstudio.description": "Use the RStudio CRAN mirror (cran.rstudio.com)", - "r.configuration.defaultRepositories.posit-ppm.description": "Use the RStudio public package manager (packagemanager.rstudio.com)", + "r.configuration.defaultRepositories.posit-ppm.description": "Use the Posit public package manager (packagemanager.posit.co)", "r.configuration.defaultRepositories.none.description": "Do not set a default repository or change the value of the 'repos' option" } diff --git a/extensions/positron-r/src/kernel-spec.ts b/extensions/positron-r/src/kernel-spec.ts new file mode 100644 index 00000000000..9566e0d9515 --- /dev/null +++ b/extensions/positron-r/src/kernel-spec.ts @@ -0,0 +1,125 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (C) 2024 Posit Software, PBC. All rights reserved. + * Licensed under the Elastic License 2.0. See LICENSE.txt for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as positron from 'positron'; +import * as vscode from 'vscode'; +import * as path from 'path'; + +import { JupyterKernelSpec } from './jupyter-adapter'; +import { getArkKernelPath } from './kernel'; +import { getPandocPath } from './pandoc'; + +/** + * Create a new Jupyter kernel spec. + * + * @param rHomePath The R_HOME path for the R version + * @param runtimeName The (display) name of the runtime + * @param sessionMode The mode in which to create the session + * + * @returns A JupyterKernelSpec definining the kernel's path, arguments, and + * metadata. + */ +export function createJupyterKernelSpec( + rHomePath: string, + runtimeName: string, + sessionMode: positron.LanguageRuntimeSessionMode): JupyterKernelSpec { + + // Path to the kernel executable + const kernelPath = getArkKernelPath(); + if (!kernelPath) { + throw new Error('Unable to find R kernel'); + } + + // Check the R kernel log level setting + const config = vscode.workspace.getConfiguration('positron.r'); + const logLevel = config.get('kernel.logLevel') ?? 'warn'; + const logLevelForeign = config.get('kernel.logLevelExternal') ?? 'warn'; + const userEnv = config.get('kernel.env') ?? {}; + const profile = config.get('kernel.profile'); + + + /* eslint-disable */ + const env = >{ + 'RUST_BACKTRACE': '1', + 'RUST_LOG': logLevelForeign + ',ark=' + logLevel, + 'R_HOME': rHomePath, + ...userEnv + }; + /* eslint-enable */ + + if (profile) { + env['ARK_PROFILE'] = profile; + } + + if (process.platform === 'linux') { + // Workaround for + // https://github.com/posit-dev/positron/issues/1619#issuecomment-1971552522 + env['LD_LIBRARY_PATH'] = rHomePath + '/lib'; + } else if (process.platform === 'darwin') { + // Workaround for + // https://github.com/posit-dev/positron/issues/3732 + env['DYLD_LIBRARY_PATH'] = rHomePath + '/lib'; + } + + // Inject the path to the Pandoc executable into the environment; R packages + // that use Pandoc for rendering will need this. + // + // On MacOS, the binary path lives alongside the app bundle; on other + // platforms, it's a couple of directories up from the app root. + const pandocPath = getPandocPath(); + if (pandocPath) { + env['RSTUDIO_PANDOC'] = pandocPath; + } + + // R script to run on session startup + const startupFile = path.join(EXTENSION_ROOT_DIR, 'resources', 'scripts', 'startup.R'); + + const argv = [ + kernelPath, + '--connection_file', '{connection_file}', + '--log', '{log_file}', + '--startup-file', `${startupFile}`, + '--session-mode', `${sessionMode}`, + ]; + + // Only create profile if requested in configuration + if (profile) { + argv.push(...[ + '--profile', '{profile_file}', + ]); + } + + argv.push(...[ + // The arguments after `--` are passed verbatim to R + '--', + '--interactive', + ]); + + // Create a kernel spec for this R installation + const kernelSpec: JupyterKernelSpec = { + 'argv': argv, + 'display_name': runtimeName, // eslint-disable-line + 'language': 'R', + 'env': env, + }; + + // Unless the user has chosen to restore the workspace, pass the + // `--no-restore-data` flag to R. + if (!config.get('restoreWorkspace')) { + kernelSpec.argv.push('--no-restore-data'); + } + + // If the user has supplied extra arguments to R, pass them along. + const extraArgs = config.get>('extraArguments'); + const quietMode = config.get('quietMode'); + if (quietMode && extraArgs?.indexOf('--quiet') === -1) { + extraArgs?.push('--quiet'); + } + if (extraArgs) { + kernelSpec.argv.push(...extraArgs); + } + + return kernelSpec; +} diff --git a/extensions/positron-r/src/kernel.ts b/extensions/positron-r/src/kernel.ts index f5be4b2aa9c..469fd67d4f7 100644 --- a/extensions/positron-r/src/kernel.ts +++ b/extensions/positron-r/src/kernel.ts @@ -1,5 +1,5 @@ /*--------------------------------------------------------------------------------------------- - * Copyright (C) 2023 Posit Software, PBC. All rights reserved. + * Copyright (C) 2023-2024 Posit Software, PBC. All rights reserved. * Licensed under the Elastic License 2.0. See LICENSE.txt for license information. *--------------------------------------------------------------------------------------------*/ diff --git a/extensions/positron-r/src/runtime-manager.ts b/extensions/positron-r/src/runtime-manager.ts index f9143651518..4553bfe6531 100644 --- a/extensions/positron-r/src/runtime-manager.ts +++ b/extensions/positron-r/src/runtime-manager.ts @@ -7,7 +7,8 @@ import * as positron from 'positron'; import * as vscode from 'vscode'; import { findCurrentRBinary, makeMetadata, rRuntimeDiscoverer } from './provider'; import { RInstallation, RMetadataExtra } from './r-installation'; -import { RSession, createJupyterKernelExtra, createJupyterKernelSpec } from './session'; +import { RSession, createJupyterKernelExtra } from './session'; +import { createJupyterKernelSpec } from './kernel-spec'; export class RRuntimeManager implements positron.LanguageRuntimeManager { diff --git a/extensions/positron-r/src/session.ts b/extensions/positron-r/src/session.ts index 5e475e9ef29..c39423d4521 100644 --- a/extensions/positron-r/src/session.ts +++ b/extensions/positron-r/src/session.ts @@ -5,7 +5,6 @@ import * as positron from 'positron'; import * as vscode from 'vscode'; -import * as path from 'path'; import PQueue from 'p-queue'; import { JupyterAdapterApi, JupyterKernelSpec, JupyterLanguageRuntimeSession, JupyterKernelExtra } from './jupyter-adapter'; @@ -13,12 +12,9 @@ import { ArkLsp, LspState } from './lsp'; import { delay, whenTimeout, timeout } from './util'; import { ArkAttachOnStartup, ArkDelayStartup } from './startup'; import { RHtmlWidget, getResourceRoots } from './htmlwidgets'; -import { getArkKernelPath } from './kernel'; import { randomUUID } from 'crypto'; import { handleRCode } from './hyperlink'; import { RSessionManager } from './session-manager'; -import { EXTENSION_ROOT_DIR } from './constants'; -import { getPandocPath } from './pandoc'; interface RPackageInstallation { packageName: string; @@ -793,119 +789,6 @@ export function createJupyterKernelExtra(): JupyterKernelExtra { }; } -/** - * Create a new Jupyter kernel spec. - * - * @param rHomePath The R_HOME path for the R version - * @param runtimeName The (display) name of the runtime - * @param sessionMode The mode in which to create the session - * - * @returns A JupyterKernelSpec definining the kernel's path, arguments, and - * metadata. - */ -export function createJupyterKernelSpec( - rHomePath: string, - runtimeName: string, - sessionMode: positron.LanguageRuntimeSessionMode): JupyterKernelSpec { - - // Path to the kernel executable - const kernelPath = getArkKernelPath(); - if (!kernelPath) { - throw new Error('Unable to find R kernel'); - } - - // Check the R kernel log level setting - const config = vscode.workspace.getConfiguration('positron.r'); - const logLevel = config.get('kernel.logLevel') ?? 'warn'; - const logLevelForeign = config.get('kernel.logLevelExternal') ?? 'warn'; - const userEnv = config.get('kernel.env') ?? {}; - const profile = config.get('kernel.profile'); - - - /* eslint-disable */ - const env = >{ - 'RUST_BACKTRACE': '1', - 'RUST_LOG': logLevelForeign + ',ark=' + logLevel, - 'R_HOME': rHomePath, - ...userEnv - }; - /* eslint-enable */ - - if (profile) { - env['ARK_PROFILE'] = profile; - } - - if (process.platform === 'linux') { - // Workaround for - // https://github.com/posit-dev/positron/issues/1619#issuecomment-1971552522 - env['LD_LIBRARY_PATH'] = rHomePath + '/lib'; - } else if (process.platform === 'darwin') { - // Workaround for - // https://github.com/posit-dev/positron/issues/3732 - env['DYLD_LIBRARY_PATH'] = rHomePath + '/lib'; - } - - // Inject the path to the Pandoc executable into the environment; R packages - // that use Pandoc for rendering will need this. - // - // On MacOS, the binary path lives alongside the app bundle; on other - // platforms, it's a couple of directories up from the app root. - const pandocPath = getPandocPath(); - if (pandocPath) { - env['RSTUDIO_PANDOC'] = pandocPath; - } - - // R script to run on session startup - const startupFile = path.join(EXTENSION_ROOT_DIR, 'resources', 'scripts', 'startup.R'); - - const argv = [ - kernelPath, - '--connection_file', '{connection_file}', - '--log', '{log_file}', - '--startup-file', `${startupFile}`, - '--session-mode', `${sessionMode}`, - ]; - - // Only create profile if requested in configuration - if (profile) { - argv.push(...[ - '--profile', '{profile_file}', - ]); - } - - argv.push(...[ - // The arguments after `--` are passed verbatim to R - '--', - '--interactive', - ]); - - // Create a kernel spec for this R installation - const kernelSpec: JupyterKernelSpec = { - 'argv': argv, - 'display_name': runtimeName, // eslint-disable-line - 'language': 'R', - 'env': env, - }; - - // Unless the user has chosen to restore the workspace, pass the - // `--no-restore-data` flag to R. - if (!config.get('restoreWorkspace')) { - kernelSpec.argv.push('--no-restore-data'); - } - - // If the user has supplied extra arguments to R, pass them along. - const extraArgs = config.get>('extraArguments'); - const quietMode = config.get('quietMode'); - if (quietMode && extraArgs?.indexOf('--quiet') === -1) { - extraArgs?.push('--quiet'); - } - if (extraArgs) { - kernelSpec.argv.push(...extraArgs); - } - - return kernelSpec; -} - export async function checkInstalled(pkgName: string, pkgVersion?: string, session?: RSession): Promise { diff --git a/extensions/positron-r/yarn.lock b/extensions/positron-r/yarn.lock index 4cbb2e01711..b6bee192f61 100644 --- a/extensions/positron-r/yarn.lock +++ b/extensions/positron-r/yarn.lock @@ -1059,6 +1059,11 @@ fs.realpath@^1.0.0: resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= +fsevents@*: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + fsevents@~2.3.2: version "2.3.2" resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" @@ -1620,6 +1625,13 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" +os-paths@^7.4.0: + version "7.4.0" + resolved "https://registry.yarnpkg.com/os-paths/-/os-paths-7.4.0.tgz#3354f1814425c232b6f42138a90e4000af6f9692" + integrity sha512-Ux1J4NUqC6tZayBqLN1kUlDAEvLiQlli/53sSddU4IN+h+3xxnv2HmRSMpVSvr1hvJzotfMs3ERvETGK+f4OwA== + optionalDependencies: + fsevents "*" + p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" @@ -2346,6 +2358,15 @@ wrappy@1: resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= +xdg-portable@^10.6.0: + version "10.6.0" + resolved "https://registry.yarnpkg.com/xdg-portable/-/xdg-portable-10.6.0.tgz#879ef439ace6a95ac5a49eea96c30f6a7819857c" + integrity sha512-xrcqhWDvtZ7WLmt8G4f3hHy37iK7D2idtosRgkeiSPZEPmBShp0VfmRBLWAPC6zLF48APJ21yfea+RfQMF4/Aw== + dependencies: + os-paths "^7.4.0" + optionalDependencies: + fsevents "*" + xml2js@^0.4.23: version "0.4.23" resolved "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz"