diff --git a/R/.Rprofile b/R/.Rprofile new file mode 100644 index 000000000..324e475a5 --- /dev/null +++ b/R/.Rprofile @@ -0,0 +1,11 @@ +if (nzchar(Sys.getenv("R_PROFILE_USER_OLD"))) { + source(Sys.getenv("R_PROFILE_USER_OLD")) +} else if (file.exists(".Rprofile")) { + source(".Rprofile") +} else if (file.exists("~/.Rprofile")) { + source("~/.Rprofile") +} + +if (is.null(getOption("vscodeR"))) { + source(file.path(Sys.getenv(if (.Platform$OS.type == "windows") "USERPROFILE" else "HOME"), ".vscode-R", "init.R")) +} diff --git a/R/init.R b/R/init.R index 852d4416d..329789bbf 100644 --- a/R/init.R +++ b/R/init.R @@ -1,4 +1,4 @@ -if (interactive() && !identical(Sys.getenv("RSTUDIO"), "1")) { +if (interactive() && Sys.getenv("TERM_PROGRAM") == "vscode") { if (requireNamespace("jsonlite", quietly = TRUE)) { local({ pid <- Sys.getpid() diff --git a/README.md b/README.md index 96165f4af..386bf1832 100644 --- a/README.md +++ b/README.md @@ -69,17 +69,53 @@ This extension contributes the following settings: An opt-in experimental R session watcher is implemented to support the following features: * Watch any R session -* Show value of session symbol on hover -* Provide completion for session symbol -* `View()` data frames and list objects -* Show plot output on update -* Show htmlwidgets and shiny apps +* Show value of session symbols on hover +* Provide completion for session symbols +* `View()` any objects including data frames and list objects +* Show plot output on update and plot history +* Show htmlwidgets, documentation and shiny apps in WebView -To enable this feature, turn on `r.sessionWatcher` and append the following code to your `.Rprofile` (in your home directory): +### Basic usage -```r -source(file.path(if (.Platform$OS.type == "windows") file.path(Sys.getenv("HOMEDRIVE"), Sys.getenv("HOMEPATH")) else Sys.getenv("HOME"), ".vscode-R", "init.R")) -``` +To enable this feature, turn on `r.sessionWatcher` in VSCode settings, reload or restart VSCode, and the session watcher will be activated automatically +on R sessions launched by vscode-R via `R: Create R Terminal` command. + +*If you previously appended the `source(...)` line to `~/.Rprofile`, you may safely remove it since the configuration for basic usage is automated. It is +now only necessary for advanced usage described below.* + +### Advanced usage (for self-managed R sessions) + +For advanced users to work with self-managed R sessions (e.g. manually launched R terminal or started in `tmux` or `screen` window), some extra +configuration is needed. Follow the steps below to make R session watcher work with any external R session: + +1. Turn on `r.sessionWatcher` in VSCode settings. +2. Edit `.Rprofile` in your home directory by running the following code in R: + + ```r + file.edit("~/.Rprofile") + ``` + +3. Append the following code to the file: + + ```r + source(file.path(Sys.getenv(if (.Platform$OS.type == "windows") "USERPROFILE" else "HOME"), ".vscode-R", "init.R")) + ``` + +4. Restart or Reload Window in VSCode + +If the workspace folder you open in VSCode already has a `.Rprofile`, you need to append the code above in this file too because `~/.Rprofile` will not +be executed when a local `.Rprofile` is found. + +The script only works with environment variable `TERM_PROGRAM=vscode`. the script will not take effect with R sessions started in a `tmux` or `screen` window that does not have it, unless this environment variable is manually set before sourcing `init.R`, for example, you may insert a line `Sys.setenv(TERM_PROGRAM="vscode")` before it. + +### How to disable it + +For the case of basic usage, turning off `r.sessionWatcher` in VSCode settings is sufficient +to disable R session watcher. + +For the case of advanced usage, user should, in addition, comment out or remove the `source(...)` line appended to `~/.Rprofile`. + +### How it works This script writes the metadata of symbols in the global environment and plot file to `${workspaceFolder}/.vscode/vscode-R/PID` where `PID` is the R process ID. It also captures user input and append command lines to `${workspaceFolder}/.vscode/vscode-R/response.log`, which enables the communication between vscode-R and a live R sesson. @@ -91,7 +127,7 @@ R sessions started from the workspace root folder will be automatically attached * R session started by vscode-R or user * R session in a `tmux` or `screen` window * Switch between multiple running R sessions -* [Remote Development](https://code.visualstudio.com/docs/remote/remote-overview) +* [Remote Development](https://code.visualstudio.com/docs/remote/remote-overview) via SSH, WSL and Docker The status bar item shows the process id of the attached R session. Click the status bar item and it will attach to currently active session. @@ -100,6 +136,9 @@ attach to currently active session. ![R session watcher](https://user-images.githubusercontent.com/4662568/70815935-65391480-1e09-11ea-9ad6-7ebbebf9a9c8.gif) +*The R terminal used in the screenshot is [radian](https://github.com/randy3k/radian) which is cross-platform and +supports syntax highlighting, auto-completion and many other features.* + ## TODO * Debug diff --git a/src/rTerminal.ts b/src/rTerminal.ts index f492c0843..c59a5cef3 100644 --- a/src/rTerminal.ts +++ b/src/rTerminal.ts @@ -1,8 +1,11 @@ "use strict"; +import os = require("os"); +import path = require("path"); + import { pathExists } from "fs-extra"; import { isDeepStrictEqual } from "util"; -import { commands, Terminal, window } from "vscode"; +import { commands, Terminal, window, TerminalOptions } from "vscode"; import { getSelection } from "./selection"; import { removeSessionFiles } from "./session"; @@ -18,7 +21,18 @@ export function createRTerm(preserveshow?: boolean): boolean { const termOpt: string[] = config.get("rterm.option"); pathExists(termPath, (err, exists) => { if (exists) { - rTerm = window.createTerminal(termName, termPath, termOpt); + let termOptions: TerminalOptions = { + name: termName, + shellPath: termPath, + shellArgs: termOpt + }; + if (config.get("sessionWatcher")) { + termOptions.env = { + R_PROFILE_USER_OLD: process.env.R_PROFILE_USER, + R_PROFILE_USER: path.join(os.homedir(), ".vscode-R", ".Rprofile") + }; + } + rTerm = window.createTerminal(termOptions); rTerm.show(preserveshow); return true; } diff --git a/src/session.ts b/src/session.ts index 5571d34ba..97a53b639 100644 --- a/src/session.ts +++ b/src/session.ts @@ -20,13 +20,13 @@ const sessionDir = path.join(".vscode", "vscode-R"); export function deploySessionWatcher(extensionPath: string) { resDir = path.join(extensionPath, "dist", "resources"); - const srcPath = path.join(extensionPath, "R", "init.R"); const targetDir = path.join(os.homedir(), ".vscode-R"); if (!fs.existsSync(targetDir)) { fs.mkdirSync(targetDir); } - const targetPath = path.join(targetDir, "init.R"); - fs.copySync(srcPath, targetPath); + + fs.copySync(path.join(extensionPath, "R", "init.R"), path.join(targetDir, "init.R")); + fs.copySync(path.join(extensionPath, "R", ".Rprofile"), path.join(targetDir, ".Rprofile")); } export function startResponseWatcher(sessionStatusBarItem: StatusBarItem) {