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

Provide R session-based editor functionalities #143

Closed
renkun-ken opened this issue Nov 4, 2019 · 65 comments
Closed

Provide R session-based editor functionalities #143

renkun-ken opened this issue Nov 4, 2019 · 65 comments

Comments

@renkun-ken
Copy link
Member

renkun-ken commented Nov 4, 2019

As I commented, using a file-based mechanism may support a wide range of scenarios.

I refined the task handler and write necessary information of symbols in the global environment as following:

~/.vscode-R/init.R:

if (interactive()) {
  local({
    pid <- Sys.getpid()
    dir <- file.path(Sys.getenv("HOME"), ".vscode-R")
    dir_session <- file.path(dir, "session", pid)
    dir.create(dir_session, showWarnings = FALSE, recursive = TRUE, mode = "0700")
    reg.finalizer(.GlobalEnv, function(e) {
      unlink(dir_session, recursive = TRUE, force = TRUE)
    }, onexit = TRUE)

    session_file <- file.path(dir_session, "session.json")
    globalenv_file <- file.path(dir_session, "globalenv.json")
    plot_file <- file.path(dir_session, "plot.png")
    viewer_file <- file.path(dir_session, "viewer.log")
    
    options(device = function(...) {
      png(filename = plot_file, ...)
    })

    options(viewer = function(url, ...) {
      cat(url, "\n", file = viewer_file, append = TRUE)
    })

    update <- function(...) {
      session <- list(
        pid = pid,
        args = commandArgs(),
        wd = getwd(),
        time = Sys.time()
      )
      jsonlite::write_json(session, session_file, auto_unbox = TRUE, pretty = TRUE)
      objs <- eapply(.GlobalEnv, function(obj) {
        list(
          class = class(obj),
          type = typeof(obj),
          length = length(obj),
          names = names(obj),
          str = utils::capture.output(str(obj, max.level = 0, give.attr = FALSE))
        )
      }, all.names = FALSE, USE.NAMES = TRUE)
      jsonlite::write_json(objs, globalenv_file, auto_unbox = TRUE, pretty = TRUE)
      TRUE
    }

    update()
    addTaskCallback(update, name = "vscode-R")

    invisible()
  })
}

Each time user evaluates an expression in top-level, the ~/.vscode-R/[pid]/globalenv.json will be updated to reflect the latest information of global objects. With this information, we can provide some session-based functionalities that languageserver not designed to provide. For example,

  1. Environment pane
  2. Symbol value on hover (string representations are provided in globalenv.json)
  3. Completion for session objects (symbols are provided in globalenv.json)
  4. Completion for session list objects, e.g. data$ (names are provided in globalenv.json)
@renkun-ken
Copy link
Member Author

renkun-ken commented Nov 4, 2019

For example, the following code produces an example globalenv.json:

x <- rnorm(1000)
y <- rnorm(1000)
m <- lm(y ~ x)
ms <- summary(m)
b <- coef(m)
p <- mtcars
{
  "ms": {
    "class": "summary.lm",
    "type": "list",
    "length": 11,
    "names": ["call", "terms", "residuals", "coefficients", "aliased", "sigma", "df", "r.squared", "adj.r.squared", "fstatistic", "cov.unscaled"],
    "str": "List of 11"
  },
  "x": {
    "class": "numeric",
    "type": "double",
    "length": 1000,
    "names": {},
    "str": " num [1:1000] 1.731 -0.792 -1.667 -1.012 1.312 ..."
  },
  "y": {
    "class": "numeric",
    "type": "double",
    "length": 1000,
    "names": {},
    "str": " num [1:1000] 0.289 -0.856 1.005 1.053 -0.656 ..."
  },
  "b": {
    "class": "numeric",
    "type": "double",
    "length": 2,
    "names": ["(Intercept)", "x"],
    "str": " Named num [1:2] 0.0517 -0.0339"
  },
  "m": {
    "class": "lm",
    "type": "list",
    "length": 12,
    "names": ["coefficients", "residuals", "effects", "rank", "fitted.values", "assign", "qr", "df.residual", "xlevels", "call", "terms", "model"],
    "str": "List of 12"
  },
  "p": {
    "class": "data.frame",
    "type": "list",
    "length": 11,
    "names": ["mpg", "cyl", "disp", "hp", "drat", "wt", "qsec", "vs", "am", "gear", "carb"],
    "str": "'data.frame':\t32 obs. of  11 variables:"
  }
}

@andycraig
Copy link
Collaborator

@renkun-ken This is fantastic!

These especially sound great to me:

  1. Symbol value on hover (string representations are provided in globalenv.json)
  2. Completion for recursive objects such as data$ (names are provided in globalenv.json)

An automatically updating environment pane has been requested previously (#47). I had basically thought it would be impossible without a socket server or similar, but this would absolutely do it.

@renkun-ken
Copy link
Member Author

Autocompletion can be extended to session objects since all entries in globalenv.json can be added to the completion list.

@renkun-ken
Copy link
Member Author

renkun-ken commented Nov 5, 2019

@andycraig Here's a rough road map in my mind of how it may appear.

  • Initially, vscode-R provides some scripts in ~/.vscode-R.
  • ~/.vscode-R/init.R is the script I post above that should appear in user's .Rprofile (source("~/.vscode-R/init.R") to allow the interaction between R sessions and vscode-R extension.
  • ~/.vscode-R/browser is the script for BROWSER environment variable. (which at the moment only works remotely but not locally, I'll figure it out for local case later).
  • Each interactive R session started by this user will create a subfolder ~/.vscode-R/session/[pid] and vscode-R should provide a command to attach to a session.
  • The attach command should pop out a list of subfolders detected in ~/.vscode-R/session and show each entry along with its pid, pwd and last active time or something to make it easier for user to checkout which session (if there are multiple sessions) should be attached.
  • R session created by vscode-R can be automatically attached.
  • Once an R session is attached, i.e. its session directory (~/.vscode-R/session/[pid]) is under watch.
  • If globalenv.json has changed, the file can be read and completion list of session symbols and symbol definition (symbol value on hover) can be updated according to its content.
  • If browser.log viewer.log has changed, the newly appended line of file is the latest file produced and a webview can be open to show that page.

@renkun-ken
Copy link
Member Author

renkun-ken commented Nov 5, 2019

Attach to R session may look like Debug: Attach to Node Process:

image

where process name, pid, working directory, last active time (all provided in session.json) can be shown.

@andycraig
Copy link
Collaborator

@renkun-ken That sounds like a good process to me.

I think displaying variables on hover is probably the most straightforward feature to implement (mainly because it doesn't involve a webview), so I can have a try at implementing the vscode-R side of that to start with.

@renkun-ken
Copy link
Member Author

@andycraig Looking forward to it. If you need anything to change in the R side, I'd be happy to refine the R code here.

@renkun-ken
Copy link
Member Author

@andycraig, it looks like options(viewer = function(url, ...) {}) fully controls the viewer behavior, used in htmlwidgets:::print.htmlwidget. At the moment, I refine the init code so that the viewer function simply append the latest file produced to viewer.log. I'm not sure if there's a better way to tell vscode-R a message that a new htmlwidget html page is produced.

@andycraig
Copy link
Collaborator

@renkun-ken I’m not sure if there’s a better way either. That should be fine for now.

@andycraig
Copy link
Collaborator

andycraig commented Nov 7, 2019

Quick update: I have hover working for session variables. At the moment it just works for one hard-coded session name, so next I will add the 'quick pick' for choosing a session.

@renkun-ken A couple of questions:

  1. I had to copy the contents of init.R to .Rprofile to get it to work. I feel like maybe I missed a step? EDIT: Yes, I just found the step that says I should source it in .Rprofile.
  2. I am using createFileSystemWatcher to monitor globalenv.json. According to the docs, it can only watch files in the current workspace. Would there be any problem with having globalenv.json etc. in .vscode/.vscode-R/ in the workspace rather than in ~/.vscode-R/?

Thank you.

@andycraig
Copy link
Collaborator

@renkun-ken Ignore question 1.

@renkun-ken
Copy link
Member Author

renkun-ken commented Nov 8, 2019

@andycraig Excited to hear that! I also notice the limitation of FileSystemWatcher too reading the docs. I'm totally okay with putting the session data in .vscode/vscode-R/ (I bet there's no need to have . if the folder has to appear in the workspace). A number of issues come up with this:

  1. The file contents in the folder is constantly changing due to user input in R sessions, which will always produce git diffs if user choose to commit .vscode folder if the workspace is a git repo, or otherwise user has to git ignore .vscode as a whole or ignore .vscode/vscode-R or .vscode/.vscode-R (in this case, .vscode-R makes some sense). Personally, I don't commit .vscode so it is probably not an issue to me. I'm not sure if it is the case for other users.
  2. The session data will be limited to local, i.e., vscode-R can only pick sessions started in the workspace folder rather than globally. Personally, I don't have a use case where I have to pick a session to provide functionality started from a folder other than workspace folder. So I'm perfectly fine with this.
  3. If the session data has to appear in .vscode/vscode-R, then init.R should not appear in this same folder or it is too much duplication. I guess vscode-R should create ~/.vscode-R automatically and put a init.R there for .Rprofile to source.
  4. In the same workspace, sessions will mostly have the same working directory. This makes it harder for user to choose which one is to attach if user has started multiple sessions in the same workspace. But let's leave it as it is at the moment.

@renkun-ken
Copy link
Member Author

renkun-ken commented Nov 8, 2019

@andycraig I update init.R according to your needs.

if (interactive()) {
  local({
    pid <- Sys.getpid()
    dir <- normalizePath(file.path(".vscode", "vscode-R", Sys.getpid()),
      mustWork = FALSE)
    if (dir.create(dir, showWarnings = FALSE, recursive = TRUE) || dir.exists(dir)) {
      reg.finalizer(.GlobalEnv, function(e) {
        unlink(dir, recursive = TRUE, force = TRUE)
      }, onexit = TRUE)
      
      session_file <- file.path(dir, "session.json")
      globalenv_file <- file.path(dir, "globalenv.json")
      plot_file <- file.path(dir, "plot.png")
      viewer_file <- file.path(dir, "viewer.log")
      
      options(device = function(...) {
        png(filename = plot_file, ...)
      })
      
      options(viewer = function(url, ...) {
        cat(url, "\n", file = viewer_file, append = TRUE)
      })
      
      update <- function(...) {
        session <- list(
          pid = pid,
          args = commandArgs(),
          wd = getwd(),
          time = Sys.time()
        )
        jsonlite::write_json(session, session_file, auto_unbox = TRUE, pretty = TRUE)
        objs <- eapply(.GlobalEnv, function(obj) {
          list(
            class = class(obj),
            type = typeof(obj),
            length = length(obj),
            names = names(obj),
            str = utils::capture.output(str(obj, max.level = 0, give.attr = FALSE))
          )
        }, all.names = FALSE, USE.NAMES = TRUE)
        jsonlite::write_json(objs, globalenv_file, auto_unbox = TRUE, pretty = TRUE)
        TRUE
      }
      
      update()
      addTaskCallback(update, name = "vscode-R")
    }
    invisible()
  })
}

@renkun-ken
Copy link
Member Author

renkun-ken commented Nov 8, 2019

As for the pick session interface, I'm not sure if we can add a button to the status bar which shows the current session, and clicking the button will pop up the list of sessions. Since multiple sessions are started from the same workspace, it's a bit tricky how to label these sessions meaningfully for user to know which to pick.

@renkun-ken
Copy link
Member Author

renkun-ken commented Nov 8, 2019

More issues with .vscode/vscode-R:

  1. If the user started an R session in a read-only file system, then the folders cannot be created and all subsequent operations will not succeed. vscode-R cannot provide session functionalities in this case. I refined init.R so that the subsequent operations are executed only if the folder is successfully created.
  2. If user randomly started R somewhere in the system, it will create an .vscode/vscode-R/[pid] in the working directory. There will be more footprints in the file system.

Just for thinking, let's ignore them at the moment.

@andycraig
Copy link
Collaborator

@renkun-ken Thank you for updating init.R, I’ll switch to that version.

I agree with all the potential issues you’ve identified with having the session files in the workspace. They wouldn’t be a problem for me either given my workflow but it’s conceivable that they might cause problems for some people. It looks like it might be possible to implement a custom file watcher using the fs Node package, so let’s carry on with having the files in the workspace for now, and if we ultimately decide that they should be in the user’s home directory then we can look at a custom file watcher.

For choosing sessions, how about having a command like ‘Follow session in Active Terminal’? This could send a line of code to the active terminal that calls an R function (defined in init.R) to append that session’s ID to a log file. VSCode would detect that log file change and switch to following that session. It’s a bit of a clunky approach though.

@renkun-ken
Copy link
Member Author

renkun-ken commented Nov 9, 2019

@andycraig, I agree with sticking to the current file watcher for now.

As for the Follow session in Active Terminal command, the approach you suggest can be quite general as a way to send request to the active R session and get feedback, in this case, to request the session pid, which is similar to the way we expect to trigger the viewer.

We can define a set of requests and responses, as a file-based communication protocol. For each type of request, we define a function so that vscode-R sends the command to active R terminal as a request, the function handles the request and writes the response to a file in workspace as a response.

For example, we can define a function request_attach which writes a response to the response file (e.g .vscode/response.json) under watch. A very simple format as I imagine is as follows:

> vscodeR::request_attach()

It appends a new line to .vscode/response.log:

{"pid":1,"command":"attach"}

The same approach is also applicable to htmlwidget viewer, data frame viewer, etc.

If an htmlwidget is produced, request_webview(url) can be called so that a new line of json is appended to response file:

{"pid":1,"command":"webview","file":"/tmp/Rtmp12345678/index.html"}

request_dataview(data) can be defined to write the data to disk and respond:

{"pid":1,"command":"dataview","file":"/tmp/Rtmp54321/mtcars.csv"}

We can put the response file in .vscode/vscode-R i.e. all sessions started from the same workspace share it, so that vscode-R can act on all these sessions. As a result, I can switch my tmux window to multiple sessions, each can trigger the webview without it being necessary to switch session on the vscode-R side.

@renkun-ken
Copy link
Member Author

renkun-ken commented Nov 9, 2019

I refined init.R with the response file mechanism I posted. To minimize footprint, no function will be explicitly defined in the global environment, but the local environment will be stored as an option.

You may send the following code to active terminal to attach:

getOption("vscodeR")$attach()

which will append the following one-line json in .vscode/vscode-R/response.log:

{"pid":12345,"command":"attach"} 

And data previewer can be replaced with sending:

getOption("vscodeR")$dataview("mtcars")

which will append the following json in the response file:

{"pid":12345,"command":"dataview","type":"csv","file":"/tmp/RtmprmLoej/mtcars.csv"} 

I put the latest init.R in a gist here for easier tracking. If anything is changed, I'll notice in this issue.

@andycraig
Copy link
Collaborator

@renkun-ken Nice! I like this. Good solution keeping the functions out of the global namespace too.

@renkun-ken
Copy link
Member Author

Another approach is using later::later to run a periodic check of vscode-R requests in the background event loop, as described here.

I'm not sure if this helps, but we may stick to the file-based mechanism first.

@andycraig
Copy link
Collaborator

@renkun-ken I have attaching sessions working now with hover. I thought I'd try to get a webview working next. When I plot with init.R sourced, I get a file plot.png as expected, but if I try to view it in Chromium nothing appears. Trying to view it with ImageMagick display gives me a result like this:

$ display plot.png
display-im6.q16: improper image header `plot.png' @ error/png.c/ReadPNGImage/3954.

Creating a PNG using the R function png works normally when init.R is not sourced.

Is plot.png appearing correctly for you?

@renkun-ken
Copy link
Member Author

@andycraig Glad to know that you make it work!

The R plot only writes to file when dev.off() is called, which is a known limitation at the moment. Do you still cannot view the png after calling dev.off()? This works well here and I can view the file directly in VSCode.

plot(rnorm(100))
dev.off()

Also, would you like to push to a branch in this repo or your own fork so that I can help test and maybe find out how R side can be improved and may discover more functionalities we can implement.

@andycraig
Copy link
Collaborator

@renkun-ken That was it, I hadn't been calling dev.off(). Good guess and thank you!

I've pushed the changes to https://github.com/andycraig/vscode-R/tree/session

It's horrible in-progress code (global variables, duplicated code, very brittle, nothing is async etc.) and would need big changes before being ready for a PR. So, feel very free to add/remove/change as much as you like :)

It doesn't attach anything by default: you have to run the R: Attach Active Terminal command to get it to start monitoring the session and providing hover. It should detect changes to the plot file as well, but won't actually do anything as a result. If you have any trouble getting it running let me know.

@renkun-ken
Copy link
Member Author

@andycraig Thanks! I played with your branch and it looks amazing! I'll take a closer look at your code and see where I can help.

@renkun-ken
Copy link
Member Author

renkun-ken commented Nov 15, 2019

@andycraig I played with the WebView API and it seems that it limits the resource file to workspace folder and extension folder while the htmlwidgets are usually produced in a temp dir in the form of a index.html and a lib folder consisting of external javascript libraries it uses. Also, WebView seems to only allow in-file script and disallow the use of external scripts, which makes it unlikely to work directly with produced html. I tried to make WebView directly show the index.html but it is always blank, and the developer tools says those js libs in lib are all blocked.

WebView API does not say how it handles external scripts so I'm not sure if it can be used in some way.

To walk-around, I tried htmlwidget::saveWidget (introduced at https://plotly-r.com/saving.html), for example,

p <- DT::datatable(mtcars)
htmlwidgets::saveWidget(p, "mtcars.html", selfcontained = TRUE, title = "mtcars")

and the bundled html file can be shown in the webview:

image

It seems that we have to do what saveWidget does in viewer function so that a bundled file is produced for WebView to show.

@renkun-ken
Copy link
Member Author

@andycraig The WebView works now. When vscode-R gets a response of webview, it has to transform the paths of all resources (js libs and css) used in index.html into vscode-resource:// with absolute paths, and the folder in which index.html is located also has to be specified in localResourceRoots on creation of the webview.

For example:

<script src="lib/htmlwidgets-1.5.1/htmlwidgets.js"></script>
<script src="lib/jquery-1.12.4/jquery.min.js"></script>
<link href="lib/datatables-css-0.0.0/datatables-crosstalk.css" rel="stylesheet" />
<script src="lib/datatables-binding-0.9/datatables.js"></script>
<link href="lib/dt-core-1.10.19/css/jquery.dataTables.min.css" rel="stylesheet" />
<link href="lib/dt-core-1.10.19/css/jquery.dataTables.extra.css" rel="stylesheet" />
<script src="lib/dt-core-1.10.19/js/jquery.dataTables.min.js"></script>
<link href="lib/crosstalk-1.0.0/css/crosstalk.css" rel="stylesheet" />
<script src="lib/crosstalk-1.0.0/js/crosstalk.min.js"></script>

has to be transformed into something like

<script
    src="vscode-resource:///var/folders/dh/6nhw4l5x0sq0wqgwh346fwjm0000gn/T//RtmpFl8gBC/viewhtml1057e37ebb549/lib/htmlwidgets-1.5.1/htmlwidgets.js"></script>
  <script
    src="vscode-resource:///var/folders/dh/6nhw4l5x0sq0wqgwh346fwjm0000gn/T//RtmpFl8gBC/viewhtml1057e37ebb549/lib/jquery-1.12.4/jquery.min.js"></script>
  <link
    href="vscode-resource:///var/folders/dh/6nhw4l5x0sq0wqgwh346fwjm0000gn/T//RtmpFl8gBC/viewhtml1057e37ebb549/lib/datatables-css-0.0.0/datatables-crosstalk.css"
    rel="stylesheet" />
  <script
    src="vscode-resource:///var/folders/dh/6nhw4l5x0sq0wqgwh346fwjm0000gn/T//RtmpFl8gBC/viewhtml1057e37ebb549/lib/datatables-binding-0.9/datatables.js"></script>
  <link
    href="vscode-resource:///var/folders/dh/6nhw4l5x0sq0wqgwh346fwjm0000gn/T//RtmpFl8gBC/viewhtml1057e37ebb549/lib/dt-core-1.10.19/css/jquery.dataTables.min.css"
    rel="stylesheet" />
  <link
    href="vscode-resource:///var/folders/dh/6nhw4l5x0sq0wqgwh346fwjm0000gn/T//RtmpFl8gBC/viewhtml1057e37ebb549/lib/dt-core-1.10.19/css/jquery.dataTables.extra.css"
    rel="stylesheet" />
  <script
    src="vscode-resource:///var/folders/dh/6nhw4l5x0sq0wqgwh346fwjm0000gn/T//RtmpFl8gBC/viewhtml1057e37ebb549/lib/dt-core-1.10.19/js/jquery.dataTables.min.js"></script>
  <link
    href="vscode-resource:///var/folders/dh/6nhw4l5x0sq0wqgwh346fwjm0000gn/T//RtmpFl8gBC/viewhtml1057e37ebb549/lib/crosstalk-1.0.0/css/crosstalk.css"
    rel="stylesheet" />
  <script
    src="vscode-resource:///var/folders/dh/6nhw4l5x0sq0wqgwh346fwjm0000gn/T//RtmpFl8gBC/viewhtml1057e37ebb549/lib/crosstalk-1.0.0/js/crosstalk.min.js"></script>

@renkun-ken
Copy link
Member Author

renkun-ken commented Nov 16, 2019

@andycraig, 170524f is an initial attempt to implement showWebView, and it works for me. Please feel free to use/modify the code.

image

@renkun-ken
Copy link
Member Author

renkun-ken commented Nov 16, 2019

As for the plot, maybe we can execute vscode.open the plot file on plot update as a starting point, which is easiest to implement. I tried the following in _updatePlot():

commands.executeCommand("vscode.open", Uri.file(plotPath));

but it does not work as the developer tools say command: 'vscode.open' failed. Illegal argument 'resource' - Resource to open, not sure what's missing.

@renkun-ken
Copy link
Member Author

I updated init.R:

  • Both viewer and page_viewer create response of webview. page_viewer is used by suppress_viewer htmlwidgets, e.g. profvis. It works for most htmlwidgets now.
  • Auto attach for new session (attach() is called in the end of init.R)

@renkun-ken
Copy link
Member Author

renkun-ken commented Nov 28, 2019

@andycraig I'm using session functionalities in my work for several days and finds it works well for me. Following are my thoughts on what you point out:

  • First we may add an vscode opt-in option Enable R session watcher (experimental). The entire session features are enabled only when this option is enabled.
  • Clean up on VSCode deactivate. Do you mean closing the terminal or vscode? I think if it is an R session started by vscode-R, a cleanup would be nice. If it is not, then I'm not sure if it is a good idea to do anything, since user started R session could be in tmux or screen window, closing the terminal or vscode does not (and should not) kill the session.
  • As for automatically deploying init.R, I think vscode-R extension could contain the file and automatically copy the file into the place (~/.vscode-R) on extension activation.
  • One way to add source("~/.vscode-R/init.R") to .Rprofile is to add a command (something like Install R session watcher) which checks if .Rprofile contains a line of this source command, if not, append it to the file. On extension activation, we may check if init.R is installed into .Rprofile, if not we may ask the user whether to install it (by executing the command).
  • For the async reading, I don't observe any delay in my work. Its performance looks good even when I'm editing multiple R scripts (each has 1000+ lines of code), and multiple R sessions in a tmux session. For typical use, globalenv.json shouldn't be too big, but if there's any global object that has tens of thousands of names, the current implementation of init.R would write those names each time user evaluates at top level, which could be slow. However, before we extend the completion provider for lists, we may well not write the names at all. Even we implement it, we may set a limit of number of objects and number of names to write to control the overhead. Anyway, we should tackle it in init.R by avoiding writing too much data to disk, rather than leave it to vscode-R extension.

@andycraig
Copy link
Collaborator

andycraig commented Dec 1, 2019

@renkun-ken

Clean up on VSCode deactivate. Do you mean closing the terminal or vscode?

I meant VSCode, but you are quite right - user-started sessions should not be killed.

These approaches all sound good. I think having it as an opt-in option to start with is a safe way to do it.

I like your vision for this, it's progressing fast, and you seem to be having no trouble with the vscode-R TypeScript side of things :) How about if I take a bit of a back seat on development for this feature and just work on tasks you want to allocate to me?

@renkun-ken
Copy link
Member Author

@andycraig Thanks for your input and encouragement! I only have extremely limited knowledge and experience (if not zero) in TypeScript but it would be interesting for me to give it a shot on both R and TypeScript sides.

@andycraig
Copy link
Collaborator

@renkun-ken Great! Do let me know whenever you want me to work on something or if there's any vscode-R TypeScript questions I can help with etc. 👍

@renkun-ken
Copy link
Member Author

@andycraig, I tried to remove session files in deleteTerminal if the terminal created by vscode-R is closed. However, it does not seem to work. Not sure if I'm missing something. Would you please take a look at my latest commit at session branch? I think it might be easy for you if I create a PR since other things are working well now and you can edit my PR easily.

@andycraig
Copy link
Collaborator

@renkun-ken Sure, happy to take a look! If you want to create a WIP PR I’ll work from that.

@renkun-ken
Copy link
Member Author

@andycraig Please feel free to edit the WIP PR. If you think there's anything I could improve, please feel free to comment.

@renkun-ken
Copy link
Member Author

renkun-ken commented Dec 6, 2019

Next, I'll try to bring in dataview feature using WebView so that data frames can be easily viewed.
After some review, does it look good to use https://github.com/derekeder/csv-to-html-table? It looks simple and functional and it does not require a server to provide the table viewer in contrast with the Excel Viewer (which does not work under Remote Development). If this works well, we don't have to depend on it to provide data frame viewer.

On the R side, I can replace utils::View() to a vscode-R friendly version, and r.previewDataframe sends a command to active terminal to append a response in response.log so that vscode-R could capture the path of the CSV file written down to disk.

Does it sound good to you, @Ikuyadeu @andycraig?

@renkun-ken
Copy link
Member Author

A better way to do this is to directly generate html table using knitr::kable(data, format = "html") so that there's no need a parse csv and convert it to html table in WebView JS. A simple jQuery call to load the html table file into a div and convert it to DataTable object that that's it.

@renkun-ken
Copy link
Member Author

renkun-ken commented Dec 6, 2019

5998f86 Implements dataview for data frame and list objects, and utils::View() is replaced so that it triggers a WebView to view the object. Then previewDataframe could be replaced with this.

@andycraig
Copy link
Collaborator

@renkun-ken It sounds like this would remove the need for dependency on another extension in order to view data frames - sounds good to me!

@renkun-ken
Copy link
Member Author

02169cc implements browser command to show shiny apps or other server-side apps.

@jacob-long
Copy link
Contributor

As an FYI, I installed the test version linked here and it definitely did not copy a init.R to ~/.vscode-R/init.R as was discussed. Is that no longer the intended behavior or has it just not been implemented? Using Windows 10, FWIW.

@renkun-ken
Copy link
Member Author

@jacob-long Copying init.R to ~/.vscode-R/init.R is intended behavior and I can confirm it works on macOS and Ubuntu, but I don't have Windows machine to test this. Help needed on this.

@andycraig
Copy link
Collaborator

andycraig commented Dec 14, 2019

@jacob-long Thank you for testing this too and reporting the results!

@renkun-ken I did my test just now on Ubuntu only so may well be Windows-related. I can look into this on Windows 10.

@andycraig
Copy link
Collaborator

@renkun-ken @jacob-long Problem is differing definitions of the home directory:

  • VSCode's os.homedir(): C:\Users\[USERNAME]
  • R's Sys.getenv("HOME"): C:\Users\[USERNAME]\Documents

If I look in C:\Users\[USERNAME]\.vscode-R, init.R does exist.

I will hopefully have some more time this afternoon to look into a solution to this.

@jacob-long
Copy link
Contributor

Interesting. It isn't in either location for me.

@renkun-ken
Copy link
Member Author

@andycraig @jacob-long I'm curious about does the path ~/.vscode-R/init.R work in Windows or does normalizePath("~/.vscode-R/init.R") work?

@jacob-long
Copy link
Contributor

Okay, now it's in C:\Users\[USERNAME] after I reinstalled and re-enabled. This is where I expected to find it, FWIW.

@andycraig
Copy link
Collaborator

@renkun-ken
Copy link
Member Author

@jacob-long @andycraig I'm a bit confused here. So do we have to do anything about it then?

@andycraig
Copy link
Collaborator

@renkun-ken Yes, at the moment it’s broken on Windows because R doesn’t find init.R. I’m going out soon but I’m going to spend a bit more time now to see if I can solve it.

@andycraig
Copy link
Collaborator

@renkun-ken In Windows:

  • VSCode's os.homedir(): C:\Users\[USERNAME]
  • R's Sys.getenv("HOME"): C:\Users\[USERNAME]\Documents
  • R's Sys.getenv("HOMEPATH"): C:\Users\[USERNAME]

So on Windows changing the .Rprofile line to use Sys.getenv("HOMEPATH") should fix the issue.

I was about to check whether it would still work on Linux but when I tried to restart my PC Windows decided to do a whole bunch of updates and I don’t know when they will finish. (I’m writing this from my phone.) Would you be able to check if Sys.getenv("HOMEPATH") in .Rprofile works on Linux? If so, changing the README should be a sufficient fix for the Windows issue.

Thank you!

@renkun-ken
Copy link
Member Author

In Ubuntu and macOS, there's no HOMEPATH. So the source code should be

source(file.path(Sys.getenv(if (.Platform$OS.type == "windows") "HOMEPATH" else "HOME"), ".vscode-R", "init.R"))

@andycraig
Copy link
Collaborator

@renkun-ken Sounds good!

BTW these both looked in C:\Users\[USERNAME]\Documents too:

  • ~/.vscode-R/init.R
  • normalizePath("~/.vscode-R/init.R")

@renkun-ken
Copy link
Member Author

Close this as we have #150 merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants