Skip to content

Commit daea7f4

Browse files
committed
Experiment with callr session
1 parent 23c7a99 commit daea7f4

File tree

4 files changed

+391
-53
lines changed

4 files changed

+391
-53
lines changed

R/notebook2.R

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
requireNamespace("jsonlite")
2+
requireNamespace("callr")
3+
4+
args <- commandArgs(trailingOnly = TRUE)
5+
exprs <- parse(text = args, keep.source = FALSE)
6+
env <- new.env()
7+
for (expr in exprs) {
8+
eval(expr, env)
9+
}
10+
11+
# r <- callr::r_session$new(
12+
# callr::r_session_options(
13+
# system_profile = TRUE, user_profile = TRUE, supervise = TRUE),
14+
# wait = TRUE
15+
# )
16+
17+
# r$run(function() {
18+
# requireNamespace("jsonlite")
19+
# requireNamespace("svglite")
20+
21+
# .vscNotebook <- local({
22+
# null_dev_id <- c(pdf = 2L)
23+
# null_dev_size <- c(7 + pi, 7 + pi)
24+
# viewer_file <- NULL
25+
# browser_url <- NULL
26+
27+
# options(
28+
# device = function(...) {
29+
# pdf(NULL,
30+
# width = null_dev_size[[1L]],
31+
# height = null_dev_size[[2L]],
32+
# bg = "white")
33+
# dev.control(displaylist = "enable")
34+
# },
35+
# viewer = function(url, ...) {
36+
# write_log("viewer: ", url)
37+
# viewer_file <<- url
38+
# },
39+
# page_viewer = function(url, ...) {
40+
# write_log("page_viewer: ", url)
41+
# viewer_file <<- url
42+
# },
43+
# browser = function(url, ...) {
44+
# write_log("browser: ", url)
45+
# browser_url <<- url
46+
# }
47+
# )
48+
49+
# check_null_dev <- function() {
50+
# identical(dev.cur(), null_dev_id) &&
51+
# identical(dev.size(), null_dev_size)
52+
# }
53+
54+
# evaluate <- function(expr) {
55+
# tryCatch({
56+
# out <- withVisible(eval(expr, globalenv()))
57+
# text <- utils::capture.output(print(out$value, view = TRUE))
58+
# if (check_null_dev()) {
59+
# record <- recordPlot()
60+
# plot_file <- tempfile(fileext = ".svg")
61+
# svglite::svglite(plot_file, width = 12, height = 8)
62+
# replayPlot(record)
63+
# graphics.off()
64+
# res <- list(
65+
# type = "plot",
66+
# result = plot_file
67+
# )
68+
# } else if (!is.null(viewer_file)) {
69+
# res <- list(
70+
# type = "viewer",
71+
# result = viewer_file
72+
# )
73+
# } else if (!is.null(browser_url)) {
74+
# res <- list(
75+
# type = "browser",
76+
# result = browser_url
77+
# )
78+
# } else if (out$visible) {
79+
# res <- list(
80+
# type = "text",
81+
# result = paste0(text, collapse = "\n")
82+
# )
83+
# } else {
84+
# res <- list(
85+
# type = "text",
86+
# result = ""
87+
# )
88+
# }
89+
# })
90+
91+
# res
92+
# }
93+
94+
# environment()
95+
# })
96+
97+
# attach(environment(), name = "tools:vscNotebook")
98+
# NULL
99+
# })
100+
101+
con <- socketConnection(host = "127.0.0.1", port = env$port, open = "r+b")
102+
103+
while (TRUE) {
104+
if (socketSelect(list(con), timeout = 0)) {
105+
header <- readLines(con, 1, encoding = "UTF-8")
106+
n <- as.integer(gsub("^Content-Length: (\\d+)$", "\\1", header))
107+
content <- readChar(con, n, useBytes = TRUE)
108+
Encoding(content) <- "UTF-8"
109+
print(content)
110+
}
111+
Sys.sleep(0.1)
112+
}
113+
114+
# while (TRUE) {
115+
# write_log("Listening on port: ", env$port)
116+
# con <- try(socketConnection(host = "127.0.0.1", port = env$port,
117+
# blocking = TRUE, server = TRUE,
118+
# open = "r+"), silent = TRUE)
119+
# if (inherits(con, "try-error")) {
120+
# message(con)
121+
# } else {
122+
# tryCatch({
123+
# line <- readLines(con, n = 1)
124+
# write_log(line)
125+
# request <- jsonlite::fromJSON(line)
126+
127+
# str <- tryCatch({
128+
# expr <- parse(text = request$expr)
129+
# }, error = function(e) {
130+
# list(
131+
# type = "error",
132+
# result = conditionMessage(e)
133+
# )
134+
# }
135+
# )
136+
# response <- jsonlite::toJSON(str, auto_unbox = TRUE, force = TRUE)
137+
# writeLines(response, con)
138+
# }, error = function(e) {
139+
# message(e)
140+
# }, finally = close(con))
141+
# }
142+
# }

src/extension.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ export function activate(context: ExtensionContext) {
207207
commands.registerCommand('r.runCommand', runCommand),
208208
window.onDidCloseTerminal(deleteTerminal),
209209
notebook.registerNotebookContentProvider('r-notebook',
210-
new RNotebookProvider(path.join(context.extensionPath, 'R', 'notebook.R'))),
210+
new RNotebookProvider(path.join(context.extensionPath, 'R', 'notebook2.R'))),
211211
);
212212

213213
if (config().get<boolean>('sessionWatcher')) {

src/notebook.ts

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import * as vscode from 'vscode';
22
import net = require('net');
33
import { spawn, ChildProcess } from 'child_process';
44
import { dirname } from 'path';
5-
import getPort = require('get-port');
65
import * as fs from 'fs';
76

87
interface REvalOutput {
@@ -12,13 +11,23 @@ interface REvalOutput {
1211

1312
class RKernel {
1413
private kernelScript: string;
14+
private doc: vscode.NotebookDocument;
1515
private cwd: string;
1616
private process: ChildProcess;
1717
private port: number;
18+
private socket: net.Socket;
1819

1920
constructor(kernelScript: string, doc: vscode.NotebookDocument) {
2021
this.kernelScript = kernelScript;
2122
this.cwd = dirname(doc.uri.fsPath);
23+
this.doc = doc;
24+
}
25+
26+
private request(obj: any) {
27+
if (this.socket) {
28+
const json = JSON.stringify(obj);
29+
this.socket.write(`Content-Length: ${json.length}\n${json}\n`);
30+
}
2231
}
2332

2433
public async start() {
@@ -29,21 +38,38 @@ class RKernel {
2938
const env = Object.create(process.env);
3039
env.LANG = 'en_US.UTF-8';
3140

32-
this.port = await getPort();
33-
const childProcess = spawn('R', ['--quiet', '--slave', '-f', this.kernelScript, '--args', `port=${this.port}`],
34-
{ cwd: this.cwd, env: env });
35-
childProcess.stderr.on('data', (chunk: Buffer) => {
36-
const str = chunk.toString();
37-
console.log(`R stderr (${childProcess.pid}): ${str}`);
38-
});
39-
childProcess.stdout.on('data', (chunk: Buffer) => {
40-
const str = chunk.toString();
41-
console.log(`R stdout (${childProcess.pid}): ${str}`);
41+
const server = net.createServer(socket => {
42+
console.log('socket started');
43+
this.socket = socket;
44+
socket.on('data', (chunk: Buffer) => {
45+
const str = chunk.toString();
46+
console.log(`socket (${socket.localAddress}:${socket.localPort}): ${str}`);
47+
});
48+
socket.on('end', () => {
49+
console.log('socket disconnected');
50+
this.socket = undefined;
51+
});
52+
server.close();
4253
});
43-
childProcess.on('exit', (code, signal) => {
44-
console.log(`R exited with code ${code}`);
54+
55+
server.listen(0, '127.0.0.1', () => {
56+
this.port = (server.address() as net.AddressInfo).port;
57+
const childProcess = spawn('R', ['--quiet', '--slave', '-f', this.kernelScript, '--args', `port=${this.port}`],
58+
{ cwd: this.cwd, env: env });
59+
childProcess.stderr.on('data', (chunk: Buffer) => {
60+
const str = chunk.toString();
61+
console.log(`R stderr (${childProcess.pid}): ${str}`);
62+
});
63+
childProcess.stdout.on('data', (chunk: Buffer) => {
64+
const str = chunk.toString();
65+
console.log(`R stdout (${childProcess.pid}): ${str}`);
66+
});
67+
childProcess.on('exit', (code, signal) => {
68+
console.log(`R exited with code ${code}`);
69+
});
70+
this.process = childProcess;
71+
return childProcess;
4572
});
46-
this.process = childProcess;
4773

4874
return new Promise((resolve) => setTimeout(resolve, 500));
4975
}
@@ -61,6 +87,13 @@ class RKernel {
6187
}
6288

6389
public async eval(cell: vscode.NotebookCell): Promise<REvalOutput> {
90+
if (this.socket) {
91+
this.request({
92+
uri: cell.uri,
93+
time: Date.now(),
94+
expr: cell.document.getText(),
95+
});
96+
}
6497
if (this.process) {
6598
const client = net.createConnection({ host: '127.0.0.1', port: this.port }, () => {
6699
console.log(`uri: ${cell.uri}, connect`);

0 commit comments

Comments
 (0)