Skip to content

Commit 7c256ae

Browse files
committed
Add support for input
1 parent dac0e82 commit 7c256ae

File tree

1 file changed

+98
-93
lines changed

1 file changed

+98
-93
lines changed

packages/docprovider/src/notebookCellExecutor.ts

Lines changed: 98 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,104 @@ async function requestServer(
194194
ServerConnection.makeRequest(url, init, settings)
195195
.then(async response => {
196196
if (!response.ok) {
197-
promise.reject(await ServerConnection.ResponseError.create(response));
197+
if (response.status === 300) {
198+
let replyUrl = response.headers.get('Location') || '';
199+
200+
if (!replyUrl.startsWith(settings.baseUrl)) {
201+
replyUrl = URLExt.join(settings.baseUrl, replyUrl);
202+
}
203+
const { parent_header, input_request } = await response.json();
204+
// TODO only the client sending the snippet will be prompted for the input
205+
// we can have a deadlock if its connection is lost.
206+
const panel = new Panel();
207+
panel.addClass('jp-OutputArea-child');
208+
panel.addClass('jp-OutputArea-stdin-item');
209+
210+
const prompt = new OutputPrompt();
211+
prompt.addClass('jp-OutputArea-prompt');
212+
panel.addWidget(prompt);
213+
214+
const input = new Stdin({
215+
future: Object.freeze({
216+
sendInputReply: (
217+
content: KernelMessage.IInputReply,
218+
parent_header: KernelMessage.IHeader<'input_request'>
219+
) => {
220+
ServerConnection.makeRequest(
221+
replyUrl,
222+
{
223+
method: 'POST',
224+
body: JSON.stringify({ input: content.value })
225+
},
226+
settings
227+
).catch(error => {
228+
console.error(
229+
`Failed to set input to ${JSON.stringify(content)}.`,
230+
error
231+
);
232+
});
233+
}
234+
}) as any,
235+
parent_header,
236+
password: input_request.password,
237+
prompt: input_request.prompt,
238+
translator
239+
});
240+
input.addClass('jp-OutputArea-output');
241+
panel.addWidget(input);
242+
243+
// Get the input node to ensure focus after updating the model upon user reply.
244+
const inputNode = input.node.getElementsByTagName('input')[0];
245+
246+
void input.value.then(value => {
247+
panel.addClass('jp-OutputArea-stdin-hiding');
248+
249+
// FIXME this is not great as the model should not be modified on the client.
250+
// Use stdin as the stream so it does not get combined with stdout.
251+
// Note: because it modifies DOM it may (will) shift focus away from the input node.
252+
cell.outputArea.model.add({
253+
output_type: 'stream',
254+
name: 'stdin',
255+
text: value + '\n'
256+
});
257+
// Refocus the input node after it lost focus due to update of the model.
258+
inputNode.focus();
259+
260+
// Keep the input in view for a little while; this (along refocusing)
261+
// ensures that we can avoid the cell editor stealing the focus, and
262+
// leading to user inadvertently modifying editor content when executing
263+
// consecutive commands in short succession.
264+
window.setTimeout(async () => {
265+
// Tack currently focused element to ensure that it remains on it
266+
// after disposal of the panel with the old input
267+
// (which modifies DOM and can lead to focus jump).
268+
const focusedElement = document.activeElement;
269+
// Dispose the old panel with no longer needed input box.
270+
panel.dispose();
271+
// Refocus the element that was focused before.
272+
if (focusedElement && focusedElement instanceof HTMLElement) {
273+
focusedElement.focus();
274+
}
275+
276+
try {
277+
const response = await requestServer(
278+
cell,
279+
url,
280+
init,
281+
settings,
282+
translator
283+
);
284+
promise.resolve(response);
285+
} catch (error) {
286+
promise.reject(error);
287+
}
288+
}, 500);
289+
});
290+
291+
cell.outputArea.layout.addWidget(panel);
292+
} else {
293+
promise.reject(await ServerConnection.ResponseError.create(response));
294+
}
198295
} else if (response.status === 202) {
199296
let redirectUrl = response.headers.get('Location') || url;
200297

@@ -234,98 +331,6 @@ async function requestServer(
234331
// Evanescent interval
235332
Math.min(MAX_POLLING_INTERVAL, interval * 2)
236333
);
237-
} else if (response.status === 300) {
238-
let replyUrl = response.headers.get('Location') || '';
239-
240-
if (!replyUrl.startsWith(settings.baseUrl)) {
241-
replyUrl = URLExt.join(settings.baseUrl, replyUrl);
242-
}
243-
const { parent_header, input_request } = await response.json();
244-
// TODO only the client sending the snippet will be prompted for the input
245-
// we can have a deadlock if its connection is lost.
246-
const panel = new Panel();
247-
panel.addClass('jp-OutputArea-child');
248-
panel.addClass('jp-OutputArea-stdin-item');
249-
250-
const prompt = new OutputPrompt();
251-
prompt.addClass('jp-OutputArea-prompt');
252-
panel.addWidget(prompt);
253-
254-
const input = new Stdin({
255-
future: Object.freeze({
256-
sendInputReply: (
257-
content: KernelMessage.IInputReply,
258-
parent_header: KernelMessage.IHeader<'input_request'>
259-
) => {
260-
ServerConnection.makeRequest(
261-
replyUrl,
262-
{ method: 'POST' },
263-
settings
264-
).catch(error => {
265-
console.error(
266-
`Failed to set input to ${JSON.stringify(content)}.`,
267-
error
268-
);
269-
});
270-
}
271-
}) as any,
272-
parent_header,
273-
password: input_request.password,
274-
prompt: input_request.prompt,
275-
translator
276-
});
277-
input.addClass('jp-OutputArea-output');
278-
panel.addWidget(input);
279-
280-
// Get the input node to ensure focus after updating the model upon user reply.
281-
const inputNode = input.node.getElementsByTagName('input')[0];
282-
283-
void input.value.then(value => {
284-
panel.addClass('jp-OutputArea-stdin-hiding');
285-
286-
// FIXME this is not great as the model should not be modified on the client.
287-
// Use stdin as the stream so it does not get combined with stdout.
288-
// Note: because it modifies DOM it may (will) shift focus away from the input node.
289-
cell.outputArea.model.add({
290-
output_type: 'stream',
291-
name: 'stdin',
292-
text: value + '\n'
293-
});
294-
// Refocus the input node after it lost focus due to update of the model.
295-
inputNode.focus();
296-
297-
// Keep the input in view for a little while; this (along refocusing)
298-
// ensures that we can avoid the cell editor stealing the focus, and
299-
// leading to user inadvertently modifying editor content when executing
300-
// consecutive commands in short succession.
301-
window.setTimeout(async () => {
302-
// Tack currently focused element to ensure that it remains on it
303-
// after disposal of the panel with the old input
304-
// (which modifies DOM and can lead to focus jump).
305-
const focusedElement = document.activeElement;
306-
// Dispose the old panel with no longer needed input box.
307-
panel.dispose();
308-
// Refocus the element that was focused before.
309-
if (focusedElement && focusedElement instanceof HTMLElement) {
310-
focusedElement.focus();
311-
}
312-
313-
try {
314-
const response = await requestServer(
315-
cell,
316-
url,
317-
init,
318-
settings,
319-
translator
320-
);
321-
promise.resolve(response);
322-
} catch (error) {
323-
promise.reject(error);
324-
}
325-
}, 500);
326-
});
327-
328-
cell.outputArea.layout.addWidget(panel);
329334
} else {
330335
promise.resolve(response);
331336
}

0 commit comments

Comments
 (0)