diff --git a/CHANGELOG.md b/CHANGELOG.md index 68e36c7..166ebb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## [UNRELEASED] + +* In Positron, the extension now uses the selected R runtime for Shiny for R apps. In VS Code, the extension also now consults the `r.rpath.mac`, `r.rpath.windows` or `r.rpath.linux` settings to find the R executable, before falling back to system settings. These settings are part of the [R Debugger extension](https://marketplace.visualstudio.com/items?itemName=RDebugger.r-debugger) ([#64](https://github.com/posit-dev/shiny-vscode/pull/64)) + ## 1.0.0 The Shiny extension for VS Code now has a new extension ID: `Posit.shiny`! New Shiny users should install the Shiny extension from [the VS Code marketplace](https://marketplace.visualstudio.com/items?itemName=Posit.shiny) or [https://open-vsx.org/extension/posit/shiny](https://open-vsx.org/extension/posit/shiny). diff --git a/src/extension-api-utils/extensionHost.ts b/src/extension-api-utils/extensionHost.ts new file mode 100644 index 0000000..d824c3f --- /dev/null +++ b/src/extension-api-utils/extensionHost.ts @@ -0,0 +1,52 @@ +import * as vscode from "vscode"; + +declare const globalThis: { + [key: string]: any; +}; + +export interface HostWebviewPanel extends vscode.Disposable { + readonly webview: vscode.Webview; + readonly visible: boolean; + reveal(viewColumn?: vscode.ViewColumn, preserveFocus?: boolean): void; + readonly onDidChangeViewState: vscode.Event; + readonly onDidDispose: vscode.Event; +} + +type LanguageRuntimeMetadata = Partial<{ + // https://github.com/posit-dev/positron/blob/39a01b71/src/positron-dts/positron.d.ts#L357 + /** The path to the runtime. */ + runtimePath: string; + + /** + * The fully qualified name of the runtime displayed to the user; e.g. "R 4.2 (64-bit)". + * Should be unique across languages. + */ + runtimeName: string; + + /** + * A language specific runtime name displayed to the user; e.g. "4.2 (64-bit)". + * Should be unique within a single language. + */ + runtimeShortName: string; + + /** The version of the runtime itself (e.g. kernel or extension version) as a string; e.g. "0.1" */ + runtimeVersion: string; +}>; + +export function getExtensionHostPreview(): void | ((url: string) => HostWebviewPanel) { + const pst = getPositronAPI(); + if (!pst) { return; } + return (url: string) => pst.window.previewUrl(vscode.Uri.parse(url)); +} + +export async function getPositronPreferredRuntime(languageId: string): Promise { + const pst = getPositronAPI(); + if (!pst) { return; } + return await pst.runtime.getPreferredRuntime(languageId); +} + +function getPositronAPI(): undefined | any { + if (typeof globalThis?.acquirePositronApi !== "function") { return; } + + return globalThis.acquirePositronApi(); +} \ No newline at end of file diff --git a/src/extension-api-utils/extensionHostPreview.ts b/src/extension-api-utils/extensionHostPreview.ts deleted file mode 100644 index 21fce72..0000000 --- a/src/extension-api-utils/extensionHostPreview.ts +++ /dev/null @@ -1,24 +0,0 @@ -import * as vscode from "vscode"; - -declare const globalThis: { - [key: string]: any; -}; - -export interface HostWebviewPanel extends vscode.Disposable { - readonly webview: vscode.Webview; - readonly visible: boolean; - reveal(viewColumn?: vscode.ViewColumn, preserveFocus?: boolean): void; - readonly onDidChangeViewState: vscode.Event; - readonly onDidDispose: vscode.Event; -} - -export function getExtensionHostPreview(): void | ((url: string) => HostWebviewPanel) { - if ( - "acquirePositronApi" in globalThis && - typeof globalThis.acquirePositronApi === "function" - ) { - const pst = globalThis.acquirePositronApi(); - if (!pst) { return; } - return (url: string) => pst.window.previewUrl(vscode.Uri.parse(url)); - } -} diff --git a/src/net-utils.ts b/src/net-utils.ts index 7bb6980..a808112 100644 --- a/src/net-utils.ts +++ b/src/net-utils.ts @@ -4,7 +4,7 @@ import { AddressInfo } from "net"; import * as vscode from "vscode"; import { getRemoteSafeUrl } from "./extension-api-utils/getRemoteSafeUrl"; import { retryUntilTimeout } from "./retry-utils"; -import { getExtensionHostPreview } from "./extension-api-utils/extensionHostPreview"; +import { getExtensionHostPreview } from "./extension-api-utils/extensionHost"; /** * Tests if a port is open on a host, by trying to connect to it with a TCP diff --git a/src/run.ts b/src/run.ts index 8ca857e..11743c2 100644 --- a/src/run.ts +++ b/src/run.ts @@ -10,6 +10,7 @@ import { } from "./shell-utils"; import { getAppPort, getAutoreloadPort } from "./port-settings"; import * as winreg from "winreg"; +import { getPositronPreferredRuntime } from "./extension-api-utils/extensionHost"; const DEBUG_NAME = "Debug Shiny app"; @@ -344,7 +345,56 @@ function getExtensionPath(): string | undefined { } async function getRBinPath(bin: string): Promise { - return getRPathFromEnv(bin) || (await getRPathFromWindowsReg(bin)) || ""; + return ( + (await getRPathFromPositron(bin)) || + getRPathFromConfig(bin) || + getRPathFromEnv(bin) || + (await getRPathFromWindowsReg(bin)) || + "" + ); +} + +async function getRPathFromPositron(bin: string): Promise { + const runtimeMetadata = await getPositronPreferredRuntime("r"); + if (!runtimeMetadata) { + return ""; + } + + console.log(`[shiny] runtimeMetadata: ${JSON.stringify(runtimeMetadata)}`) + + const runtimePath = runtimeMetadata.runtimePath; + if (!runtimePath) { + return ""; + } + + const { platform } = process; + const fileExt = platform === "win32" ? ".exe" : ""; + return path_join(path_dirname(runtimePath), bin + fileExt); +} + +function getRPathFromConfig(bin: string): string { + const { platform } = process; + const fileExt = platform === "win32" ? ".exe" : ""; + let osType: string; + + switch (platform) { + case "win32": + osType = "windows"; + break; + case "darwin": + osType = "mac"; + break; + default: + osType = "linux"; + } + + const rPath = vscode.workspace + .getConfiguration("r.rpath") + .get(osType, undefined); + + console.log(`[shiny] rPath: ${rPath}`); + + return rPath ? path_join(path_dirname(rPath), bin + fileExt) : ""; } function getRPathFromEnv(bin: string = "R"): string {