Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve session watcher initialization #184

Merged
merged 11 commits into from
Jan 24, 2020
Merged
11 changes: 11 additions & 0 deletions R/.Rprofile
Original file line number Diff line number Diff line change
@@ -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"))
}
2 changes: 1 addition & 1 deletion R/init.R
Original file line number Diff line number Diff line change
@@ -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()
Expand Down
59 changes: 49 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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.
Expand All @@ -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
Expand Down
18 changes: 16 additions & 2 deletions src/rTerminal.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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")
renkun-ken marked this conversation as resolved.
Show resolved Hide resolved
};
}
rTerm = window.createTerminal(termOptions);
rTerm.show(preserveshow);
return true;
}
Expand Down
6 changes: 3 additions & 3 deletions src/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down