Skip to content

Commit c102e3e

Browse files
committed
Support cancel
1 parent 5352b9c commit c102e3e

File tree

2 files changed

+103
-107
lines changed

2 files changed

+103
-107
lines changed

R/notebook2.R

Lines changed: 34 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,6 @@ r <- callr::r_session$new(
1414
wait = TRUE
1515
)
1616

17-
r$call(function() {
18-
for (i in 1:1000) {
19-
x <- rnorm(1000000)
20-
y <- rnorm(1000000)
21-
m <- lm(y ~ x)
22-
summary(m)
23-
}
24-
})
25-
r$interrupt()
26-
27-
while (TRUE) {
28-
res <- r$read()
29-
if (!is.null(res)) {
30-
print(res)
31-
break
32-
}
33-
}
34-
35-
36-
r$call(function() print(1))
37-
r$read()
38-
r$get_state()
39-
r$get_status()
40-
4117
r$run(function() {
4218
requireNamespace("jsonlite")
4319
requireNamespace("svglite")
@@ -128,6 +104,7 @@ r$run(function() {
128104
})
129105

130106
con <- socketConnection(host = "127.0.0.1", port = env$port, open = "r+b")
107+
running_request <- NULL
131108

132109
while (TRUE) {
133110
response <- NULL
@@ -143,7 +120,8 @@ while (TRUE) {
143120
response <- tryCatch({
144121
r$call(function(id, uri, expr) {
145122
.vscNotebook$evaluate(id, uri, expr)
146-
}, request)
123+
}, list(id = request$id, uri = request$uri, expr = request$expr))
124+
running_request <- request
147125
NULL
148126
}, error = function(e) {
149127
list(
@@ -154,32 +132,43 @@ while (TRUE) {
154132
)
155133
})
156134
} else if (request$type == "cancel") {
157-
if (r$interrupt()) {
158-
response <- list(
159-
id = request$id,
160-
uri = request$uri,
161-
type = "cancel",
162-
result = "cancelled"
163-
)
164-
} else {
135+
r$interrupt()
136+
}
137+
}
165138

139+
if (!is.null(running_request)) {
140+
result <- r$read()
141+
if (!is.null(result)) {
142+
print(result)
143+
if (is.list(result$result)) {
144+
response <- result$result
145+
} else {
146+
if (is.null(result$error)) {
147+
response <- list(
148+
id = running_request$id,
149+
uri = running_request$uri,
150+
type = "text",
151+
result = result$message
152+
)
153+
} else {
154+
response <- list(
155+
id = running_request$id,
156+
uri = running_request$uri,
157+
type = "error",
158+
result = conditionMessage(result$error)
159+
)
160+
}
166161
}
162+
running_request <- NULL
167163
}
168-
}
169164

170-
result <- r$read()
171-
if (!is.null(result)) {
172-
if (is.list(result$result)) {
173-
response <- result$result
174-
} else if (!is.null(result$error)) {
175-
message(result$error)
165+
if (!is.null(response)) {
166+
response <- jsonlite::toJSON(response,
167+
auto_unbox = TRUE, force = TRUE)
168+
cat("response: ", response, "\n")
169+
writeLines(response, con)
176170
}
177171
}
178172

179-
if (!is.null(response)) {
180-
response <- jsonlite::toJSON(result$result,
181-
auto_unbox = TRUE, force = TRUE)
182-
writeLines(response, con)
183-
}
184173
Sys.sleep(0.1)
185174
}

src/notebook.ts

Lines changed: 69 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,18 @@ import * as vscode from 'vscode';
22
import net = require('net');
33
import { spawn, ChildProcess } from 'child_process';
44
import { dirname } from 'path';
5-
import * as fs from 'fs';
65

76
interface RSessionRequest {
87
id: number;
98
uri: string;
109
type: 'eval' | 'cancel';
11-
args: any;
10+
expr?: any;
1211
}
1312

1413
interface RSessionResponse {
1514
id: number;
1615
uri: string;
17-
type: 'text' | 'plot' | 'viewer' | 'browser' | 'error' | 'cancel';
16+
type: 'text' | 'plot' | 'viewer' | 'browser' | 'error';
1817
result: string;
1918
}
2019

@@ -32,63 +31,13 @@ class RKernel {
3231
this.doc = doc;
3332
}
3433

35-
private request(obj: any) {
34+
private request(request: RSessionRequest) {
3635
if (this.socket) {
37-
const json = JSON.stringify(obj);
36+
const json = JSON.stringify(request);
3837
this.socket.write(`Content-Length: ${json.length}\n${json}`);
3938
}
4039
}
4140

42-
private async handleResponse(response: RSessionResponse) {
43-
const cell = this.doc.cells.find((cell) => cell.metadata.executionOrder == response.id);
44-
if (cell) {
45-
cell.metadata.runState = vscode.NotebookCellRunState.Success;
46-
cell.metadata.lastRunDuration = +new Date() - cell.metadata.runStartTime;
47-
48-
console.log(`uri: ${cell.uri}, response.type: ${response.type}, response.result: ${response.result}`);
49-
switch (response.type) {
50-
case 'text':
51-
cell.outputs = [{
52-
outputKind: vscode.CellOutputKind.Text,
53-
text: response.result,
54-
}];
55-
break;
56-
case 'plot':
57-
cell.outputs = [{
58-
outputKind: vscode.CellOutputKind.Rich,
59-
data: {
60-
'image/svg+xml': (await vscode.workspace.fs.readFile(vscode.Uri.parse(response.result))).toString(),
61-
},
62-
}];
63-
break;
64-
case 'viewer':
65-
cell.outputs = [{
66-
outputKind: vscode.CellOutputKind.Rich,
67-
data: {
68-
'application/json': response.result,
69-
},
70-
}];
71-
break;
72-
case 'browser':
73-
cell.outputs = [{
74-
outputKind: vscode.CellOutputKind.Rich,
75-
data: {
76-
'application/json': response.result,
77-
},
78-
}];
79-
break;
80-
case 'error':
81-
cell.outputs = [{
82-
outputKind: vscode.CellOutputKind.Error,
83-
evalue: response.result,
84-
ename: '',
85-
traceback: [],
86-
}];
87-
break;
88-
}
89-
}
90-
}
91-
9241
public async start() {
9342
if (this.process) {
9443
return;
@@ -103,6 +52,58 @@ class RKernel {
10352
this.socket = socket;
10453
resolve(undefined);
10554

55+
socket.on('data', async (data) => {
56+
const response: RSessionResponse = JSON.parse(data.toString());
57+
const cell = this.doc.cells.find(cell => cell.metadata.executionOrder === response.id);
58+
if (cell) {
59+
cell.metadata.runState = vscode.NotebookCellRunState.Success;
60+
cell.metadata.lastRunDuration = +new Date() - cell.metadata.runStartTime;
61+
62+
console.log(`id: ${response.id}, uri: ${response.uri}, type: ${response.type}, result: ${response.result}`);
63+
switch (response.type) {
64+
case 'text':
65+
cell.outputs = [{
66+
outputKind: vscode.CellOutputKind.Text,
67+
text: response.result,
68+
}];
69+
break;
70+
case 'plot':
71+
cell.outputs = [{
72+
outputKind: vscode.CellOutputKind.Rich,
73+
data: {
74+
'image/svg+xml': (await vscode.workspace.fs.readFile(vscode.Uri.parse(response.result))).toString(),
75+
},
76+
}];
77+
break;
78+
case 'viewer':
79+
cell.outputs = [{
80+
outputKind: vscode.CellOutputKind.Rich,
81+
data: {
82+
'application/json': response.result,
83+
},
84+
}];
85+
break;
86+
case 'browser':
87+
cell.outputs = [{
88+
outputKind: vscode.CellOutputKind.Rich,
89+
data: {
90+
'application/json': response.result,
91+
},
92+
}];
93+
break;
94+
case 'error':
95+
cell.metadata.runState = vscode.NotebookCellRunState.Error;
96+
cell.outputs = [{
97+
outputKind: vscode.CellOutputKind.Error,
98+
evalue: response.result,
99+
ename: '',
100+
traceback: [],
101+
}];
102+
break;
103+
}
104+
}
105+
});
106+
106107
socket.on('end', () => {
107108
console.log('socket disconnected');
108109
this.socket = undefined;
@@ -125,6 +126,7 @@ class RKernel {
125126
});
126127
childProcess.on('exit', (code, signal) => {
127128
console.log(`R exited with code ${code}`);
129+
reject(undefined);
128130
});
129131
this.process = childProcess;
130132
return childProcess;
@@ -144,7 +146,7 @@ class RKernel {
144146
await this.start();
145147
}
146148

147-
public async eval(cell: vscode.NotebookCell): Promise<void> {
149+
public eval(cell: vscode.NotebookCell) {
148150
if (this.socket) {
149151
this.request({
150152
id: cell.metadata.executionOrder,
@@ -155,13 +157,13 @@ class RKernel {
155157
}
156158
}
157159

158-
public async cancel(cell: vscode.NotebookCell): Promise<void> {
159-
if (this.socket && cell.metadata.runState === vscode.NotebookCellRunState.Running) {
160+
public cancel(cell: vscode.NotebookCell) {
161+
if (this.socket) {
160162
this.request({
161163
id: cell.metadata.executionOrder,
162164
uri: cell.uri.toString(),
163165
type: 'cancel',
164-
})
166+
});
165167
}
166168
}
167169
}
@@ -187,12 +189,14 @@ class RNotebook implements vscode.Disposable {
187189
await this.kernel.start();
188190
return this.kernel.eval(cell);
189191
}
192+
193+
public async cancel(cell: vscode.NotebookCell): Promise<void> {
194+
return this.kernel.cancel(cell);
195+
}
190196
}
191197

192198
export class RNotebookProvider implements vscode.NotebookContentProvider, vscode.NotebookKernel {
193199
public label = 'R Kernel';
194-
// public kernel = this;
195-
196200
private kernelScript: string;
197201
private disposables: vscode.Disposable[] = [];
198202
private readonly notebooks = new Map<string, RNotebook>();
@@ -406,7 +410,10 @@ export class RNotebookProvider implements vscode.NotebookContentProvider, vscode
406410
}
407411

408412
async cancelCellExecution(document: vscode.NotebookDocument, cell: vscode.NotebookCell) {
409-
413+
if (cell.metadata.runState === vscode.NotebookCellRunState.Running) {
414+
const notebook = this.notebooks.get(document.uri.toString());
415+
await notebook.cancel(cell);
416+
}
410417
}
411418

412419
async cancelAllCellsExecution(document: vscode.NotebookDocument) {

0 commit comments

Comments
 (0)