Skip to content

Commit

Permalink
move kernel spec to its own file; add xdg-portable
Browse files Browse the repository at this point in the history
  • Loading branch information
jmcphers committed Nov 27, 2024
1 parent cff7ec6 commit 783ab70
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 122 deletions.
3 changes: 2 additions & 1 deletion extensions/positron-r/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
4 changes: 2 additions & 2 deletions extensions/positron-r/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
125 changes: 125 additions & 0 deletions extensions/positron-r/src/kernel-spec.ts
Original file line number Diff line number Diff line change
@@ -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<string>('kernel.logLevel') ?? 'warn';
const logLevelForeign = config.get<string>('kernel.logLevelExternal') ?? 'warn';
const userEnv = config.get<object>('kernel.env') ?? {};
const profile = config.get<string>('kernel.profile');


/* eslint-disable */
const env = <Record<string, string>>{
'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<boolean>('restoreWorkspace')) {
kernelSpec.argv.push('--no-restore-data');
}

// If the user has supplied extra arguments to R, pass them along.
const extraArgs = config.get<Array<string>>('extraArguments');
const quietMode = config.get<boolean>('quietMode');
if (quietMode && extraArgs?.indexOf('--quiet') === -1) {
extraArgs?.push('--quiet');
}
if (extraArgs) {
kernelSpec.argv.push(...extraArgs);
}

return kernelSpec;
}
2 changes: 1 addition & 1 deletion extensions/positron-r/src/kernel.ts
Original file line number Diff line number Diff line change
@@ -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.
*--------------------------------------------------------------------------------------------*/

Expand Down
3 changes: 2 additions & 1 deletion extensions/positron-r/src/runtime-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down
117 changes: 0 additions & 117 deletions extensions/positron-r/src/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,16 @@

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';
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;
Expand Down Expand Up @@ -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<string>('kernel.logLevel') ?? 'warn';
const logLevelForeign = config.get<string>('kernel.logLevelExternal') ?? 'warn';
const userEnv = config.get<object>('kernel.env') ?? {};
const profile = config.get<string>('kernel.profile');


/* eslint-disable */
const env = <Record<string, string>>{
'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<boolean>('restoreWorkspace')) {
kernelSpec.argv.push('--no-restore-data');
}

// If the user has supplied extra arguments to R, pass them along.
const extraArgs = config.get<Array<string>>('extraArguments');
const quietMode = config.get<boolean>('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<boolean> {
Expand Down
21 changes: 21 additions & 0 deletions extensions/positron-r/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down

0 comments on commit 783ab70

Please sign in to comment.