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
Comments
For example, the following code produces an example 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:"
}
} |
@renkun-ken This is fantastic! These especially sound great to me:
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. |
Autocompletion can be extended to session objects since all entries in |
@andycraig Here's a rough road map in my mind of how it may appear.
|
@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. |
@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. |
@andycraig, it looks like |
@renkun-ken I’m not sure if there’s a better way either. That should be fine for now. |
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:
Thank you. |
@renkun-ken Ignore question 1. |
@andycraig Excited to hear that! I also notice the limitation of
|
@andycraig I update 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()
})
} |
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. |
More issues with
Just for thinking, let's ignore them at the moment. |
@renkun-ken Thank you for updating 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 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 |
@andycraig, I agree with sticking to the current file watcher for now. As for the 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
It appends a new line to {"pid":1,"command":"attach"} The same approach is also applicable to htmlwidget viewer, data frame viewer, etc. If an htmlwidget is produced, {"pid":1,"command":"webview","file":"/tmp/Rtmp12345678/index.html"}
{"pid":1,"command":"dataview","file":"/tmp/Rtmp54321/mtcars.csv"} We can put the response file in |
I refined You may send the following code to active terminal to attach: getOption("vscodeR")$attach() which will append the following one-line json in {"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 |
@renkun-ken Nice! I like this. Good solution keeping the functions out of the global namespace too. |
Another approach is using I'm not sure if this helps, but we may stick to the file-based mechanism first. |
@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
Creating a PNG using the R function Is |
@andycraig Glad to know that you make it work! The R plot only writes to file when 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. |
@renkun-ken That was it, I hadn't been calling 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 |
@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. |
@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 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 p <- DT::datatable(mtcars)
htmlwidgets::saveWidget(p, "mtcars.html", selfcontained = TRUE, title = "mtcars") and the bundled html file can be shown in the webview: It seems that we have to do what |
@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 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> |
@andycraig, 170524f is an initial attempt to implement |
As for the plot, maybe we can execute commands.executeCommand("vscode.open", Uri.file(plotPath)); but it does not work as the developer tools say |
I updated
|
@andycraig I'm using
|
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? |
@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. |
@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. 👍 |
@andycraig, I tried to remove session files in |
@renkun-ken Sure, happy to take a look! If you want to create a WIP PR I’ll work from that. |
@andycraig Please feel free to edit the WIP PR. If you think there's anything I could improve, please feel free to comment. |
Next, I'll try to bring in On the R side, I can replace Does it sound good to you, @Ikuyadeu @andycraig? |
A better way to do this is to directly generate html table using |
5998f86 Implements |
@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! |
02169cc implements |
As an FYI, I installed the test version linked here and it definitely did not copy a |
@jacob-long Copying |
@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. |
@renkun-ken @jacob-long Problem is differing definitions of the home directory:
If I look in I will hopefully have some more time this afternoon to look into a solution to this. |
Interesting. It isn't in either location for me. |
@andycraig @jacob-long I'm curious about does the path |
Okay, now it's in |
@jacob-long @andycraig I'm a bit confused here. So do we have to do anything about it then? |
@renkun-ken Yes, at the moment it’s broken on Windows because R doesn’t find |
@renkun-ken In Windows:
So on Windows changing the 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 Thank you! |
In Ubuntu and macOS, there's no source(file.path(Sys.getenv(if (.Platform$OS.type == "windows") "HOMEPATH" else "HOME"), ".vscode-R", "init.R")) |
@renkun-ken Sounds good! BTW these both looked in
|
Close this as we have #150 merged. |
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
: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,globalenv.json
)globalenv.json
)data$
(names are provided inglobalenv.json
)The text was updated successfully, but these errors were encountered: