jupyterlab-ipyflow 0.0.192 → 0.0.193
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.js +168 -79
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
2
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
3
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
4
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
|
+
};
|
|
6
|
+
var _IpyflowSessionState_instances, _IpyflowSessionState_computeTopoOrderIdxHelper, _IpyflowSessionState_computeTopoOrderIdx;
|
|
1
7
|
import { ICommandPalette } from '@jupyterlab/apputils';
|
|
2
8
|
import { CodeCell } from '@jupyterlab/cells';
|
|
3
|
-
import { INotebookTracker
|
|
9
|
+
import { INotebookTracker } from '@jupyterlab/notebook';
|
|
4
10
|
import _ from 'lodash';
|
|
5
11
|
const waitingClass = 'waiting-cell';
|
|
6
12
|
const readyClass = 'ready-cell';
|
|
@@ -15,11 +21,11 @@ const cleanup = new Event('cleanup');
|
|
|
15
21
|
// ipyflow frontend state
|
|
16
22
|
class IpyflowSessionState {
|
|
17
23
|
constructor() {
|
|
24
|
+
_IpyflowSessionState_instances.add(this);
|
|
18
25
|
this.comm = null;
|
|
19
26
|
this.notebook = null;
|
|
20
27
|
this.session = null;
|
|
21
28
|
this.isIpyflowCommConnected = false;
|
|
22
|
-
this.executionScheduledCells = [];
|
|
23
29
|
this.selectedCells = [];
|
|
24
30
|
this.executedCells = new Set();
|
|
25
31
|
this.dirtyCells = new Set();
|
|
@@ -65,6 +71,12 @@ class IpyflowSessionState {
|
|
|
65
71
|
is_reactively_executing: this.isReactivelyExecuting,
|
|
66
72
|
});
|
|
67
73
|
}
|
|
74
|
+
isBatchReactive() {
|
|
75
|
+
var _a, _b, _c;
|
|
76
|
+
return (((_a = this.isIpyflowCommConnected) !== null && _a !== void 0 ? _a : false) &&
|
|
77
|
+
((_b = this.settings) === null || _b === void 0 ? void 0 : _b.exec_mode) === 'reactive' &&
|
|
78
|
+
((_c = this.settings) === null || _c === void 0 ? void 0 : _c.reactivity_mode) === 'batch');
|
|
79
|
+
}
|
|
68
80
|
executeCells(cells) {
|
|
69
81
|
if (cells.length === 0) {
|
|
70
82
|
return;
|
|
@@ -83,11 +95,20 @@ class IpyflowSessionState {
|
|
|
83
95
|
}
|
|
84
96
|
if (++numFinished === cells.length) {
|
|
85
97
|
// wait a tick first to allow the disk changes to propagate up
|
|
98
|
+
this.isReactivelyExecuting = false;
|
|
86
99
|
setTimeout(() => this.requestComputeExecSchedule(), 0);
|
|
87
100
|
}
|
|
88
101
|
});
|
|
89
102
|
}
|
|
90
103
|
}
|
|
104
|
+
executeClosure(cells) {
|
|
105
|
+
if (cells.length === 0) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const cellIds = cells.map((cell) => cell.model.id);
|
|
109
|
+
const closureCells = this.computeTransitiveClosure(cellIds);
|
|
110
|
+
this.executeCells(closureCells);
|
|
111
|
+
}
|
|
91
112
|
toggleReactivity() {
|
|
92
113
|
if (this.settings.exec_mode === 'reactive') {
|
|
93
114
|
this.settings.exec_mode = 'normal';
|
|
@@ -126,10 +147,21 @@ class IpyflowSessionState {
|
|
|
126
147
|
(closure.size > prevClosureSize ||
|
|
127
148
|
!this.executedCells.has(cellId) ||
|
|
128
149
|
this.readyCells.has(cellId) ||
|
|
129
|
-
this.waitingCells.has(cellId)
|
|
150
|
+
this.waitingCells.has(cellId) ||
|
|
151
|
+
this.dirtyCells.has(cellId))) {
|
|
130
152
|
closure.add(cellId);
|
|
131
153
|
}
|
|
132
154
|
}
|
|
155
|
+
cellIdsToCells(cellIds) {
|
|
156
|
+
const orderIdxById = this.settings.flow_order === 'any_order'
|
|
157
|
+
? __classPrivateFieldGet(this, _IpyflowSessionState_instances, "m", _IpyflowSessionState_computeTopoOrderIdx).call(this)
|
|
158
|
+
: this.orderIdxById;
|
|
159
|
+
return cellIds
|
|
160
|
+
.filter((id) => this.cellsById[id] !== undefined)
|
|
161
|
+
.filter((id) => this.orderIdxById[id] !== undefined)
|
|
162
|
+
.sort((a, b) => orderIdxById[a] - orderIdxById[b])
|
|
163
|
+
.map((id) => this.cellsById[id]);
|
|
164
|
+
}
|
|
133
165
|
computeTransitiveClosure(cellIds, inclusive = true, parents = false) {
|
|
134
166
|
const closure = new Set();
|
|
135
167
|
for (const cellId of cellIds) {
|
|
@@ -144,35 +176,43 @@ class IpyflowSessionState {
|
|
|
144
176
|
for (const cellId of closure) {
|
|
145
177
|
this.computeTransitiveClosureHelper(closure, cellId, this.cellParents, true, true);
|
|
146
178
|
}
|
|
147
|
-
// if (this.settings.flow_order === 'in_order') {
|
|
148
|
-
// const minSeedPosition = Math.min(
|
|
149
|
-
// ...cellIds.map((id) => this.orderIdxById[id])
|
|
150
|
-
// );
|
|
151
|
-
// for (const cellId of Array.from(closure)) {
|
|
152
|
-
// const pos = this.orderIdxById[cellId];
|
|
153
|
-
// if (pos === undefined) {
|
|
154
|
-
// closure.delete(cellId);
|
|
155
|
-
// } else if (pos < minSeedPosition) {
|
|
156
|
-
// closure.delete(cellId);
|
|
157
|
-
// }
|
|
158
|
-
// }
|
|
159
|
-
// }
|
|
160
179
|
}
|
|
161
180
|
if (!inclusive) {
|
|
162
181
|
for (const cellId of cellIds) {
|
|
163
182
|
closure.delete(cellId);
|
|
164
183
|
}
|
|
165
184
|
}
|
|
166
|
-
return Array.from(closure)
|
|
167
|
-
.filter((id) => this.cellsById[id] !== undefined)
|
|
168
|
-
.filter((id) => this.orderIdxById[id] !== undefined)
|
|
169
|
-
.sort((a, b) => this.orderIdxById[a] - this.orderIdxById[b])
|
|
170
|
-
.map((id) => this.cellsById[id]);
|
|
185
|
+
return this.cellIdsToCells(Array.from(closure));
|
|
171
186
|
}
|
|
172
187
|
}
|
|
188
|
+
_IpyflowSessionState_instances = new WeakSet(), _IpyflowSessionState_computeTopoOrderIdxHelper = function _IpyflowSessionState_computeTopoOrderIdxHelper(cellId, orderedCellIds, seen) {
|
|
189
|
+
var _a, _b, _c;
|
|
190
|
+
if (seen.has(cellId) || ((_b = (_a = this.cellsById[cellId]) === null || _a === void 0 ? void 0 : _a.model) === null || _b === void 0 ? void 0 : _b.type) !== 'code') {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
seen.add(cellId);
|
|
194
|
+
for (const child of (_c = this.cellChildren[cellId]) !== null && _c !== void 0 ? _c : []) {
|
|
195
|
+
__classPrivateFieldGet(this, _IpyflowSessionState_instances, "m", _IpyflowSessionState_computeTopoOrderIdxHelper).call(this, child, orderedCellIds, seen);
|
|
196
|
+
}
|
|
197
|
+
orderedCellIds.unshift(cellId);
|
|
198
|
+
}, _IpyflowSessionState_computeTopoOrderIdx = function _IpyflowSessionState_computeTopoOrderIdx() {
|
|
199
|
+
const orderedCellIds = [];
|
|
200
|
+
const seen = new Set();
|
|
201
|
+
for (const cellId of Object.keys(this.cellsById)) {
|
|
202
|
+
__classPrivateFieldGet(this, _IpyflowSessionState_instances, "m", _IpyflowSessionState_computeTopoOrderIdxHelper).call(this, cellId, orderedCellIds, seen);
|
|
203
|
+
}
|
|
204
|
+
const topoOrderIdx = {};
|
|
205
|
+
orderedCellIds.forEach((cellId, idx) => {
|
|
206
|
+
topoOrderIdx[cellId] = idx;
|
|
207
|
+
});
|
|
208
|
+
return topoOrderIdx;
|
|
209
|
+
};
|
|
173
210
|
const ipyflowState = {};
|
|
211
|
+
const deferredCells = [];
|
|
174
212
|
function initSessionState(session_id) {
|
|
175
|
-
|
|
213
|
+
const ipyflowSessionState = new IpyflowSessionState();
|
|
214
|
+
ipyflowState[session_id] = ipyflowSessionState;
|
|
215
|
+
window.ipyflow = ipyflowSessionState;
|
|
176
216
|
}
|
|
177
217
|
function resetSessionState(session_id) {
|
|
178
218
|
delete ipyflowState[session_id];
|
|
@@ -195,6 +235,32 @@ const extension = {
|
|
|
195
235
|
requires: [INotebookTracker, ICommandPalette],
|
|
196
236
|
autoStart: true,
|
|
197
237
|
activate: (app, notebooks, palette) => {
|
|
238
|
+
app.commands.addCommand('execute-stale', {
|
|
239
|
+
label: 'Execute Ready Cells',
|
|
240
|
+
isEnabled: () => true,
|
|
241
|
+
isVisible: () => true,
|
|
242
|
+
isToggled: () => false,
|
|
243
|
+
execute: () => {
|
|
244
|
+
var _a, _b;
|
|
245
|
+
const session = notebooks.currentWidget.sessionContext;
|
|
246
|
+
if (!session.isReady) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
const state = ((_a = ipyflowState[session.session.id]) !== null && _a !== void 0 ? _a : {});
|
|
250
|
+
if (!((_b = state.isIpyflowCommConnected) !== null && _b !== void 0 ? _b : false)) {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
const cellIdsToExecute = Array.from(new Set([...state.dirtyCells, ...state.readyCells]));
|
|
254
|
+
let cellsToExecute;
|
|
255
|
+
if (state.settings.reactivity_mode === 'batch') {
|
|
256
|
+
cellsToExecute = state.computeTransitiveClosure(cellIdsToExecute);
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
cellsToExecute = state.cellIdsToCells(cellIdsToExecute);
|
|
260
|
+
}
|
|
261
|
+
state.executeCells(cellsToExecute);
|
|
262
|
+
},
|
|
263
|
+
});
|
|
198
264
|
app.commands.addCommand('alt-mode-execute', {
|
|
199
265
|
label: 'Alt Mode Execute',
|
|
200
266
|
isEnabled: () => true,
|
|
@@ -267,6 +333,11 @@ const extension = {
|
|
|
267
333
|
keys: ['Ctrl Shift Enter'],
|
|
268
334
|
selector: '.jp-Notebook',
|
|
269
335
|
});
|
|
336
|
+
app.commands.addKeyBinding({
|
|
337
|
+
command: 'execute-stale',
|
|
338
|
+
keys: ['Space'],
|
|
339
|
+
selector: '.jp-Notebook.jp-mod-commandMode',
|
|
340
|
+
});
|
|
270
341
|
palette.addItem({
|
|
271
342
|
command: 'alt-mode-execute',
|
|
272
343
|
category: 'execution',
|
|
@@ -328,13 +399,21 @@ const extension = {
|
|
|
328
399
|
selector: '.jp-Notebook',
|
|
329
400
|
});
|
|
330
401
|
let runCellCommand;
|
|
402
|
+
let runCellAndSelectNextCommand;
|
|
403
|
+
let runMenuRunCommand;
|
|
331
404
|
try {
|
|
332
405
|
runCellCommand = app.commands._commands.get('notebook:run-cell');
|
|
406
|
+
runCellAndSelectNextCommand = app.commands._commands.get('notebook:run-cell-and-select-next');
|
|
407
|
+
runMenuRunCommand = app.commands._commands.get('runmenu:run');
|
|
333
408
|
}
|
|
334
409
|
catch (e) {
|
|
335
410
|
runCellCommand = app.commands._commands['notebook:run-cell'];
|
|
411
|
+
runCellAndSelectNextCommand = app.commands._commands['notebook:run-cell-and-select-next'];
|
|
412
|
+
runMenuRunCommand = app.commands._commands['runmenu:run'];
|
|
336
413
|
}
|
|
337
414
|
const runCellCommandExecute = runCellCommand.execute;
|
|
415
|
+
const runCellAndSelectNextCommandExecute = runCellAndSelectNextCommand.execute;
|
|
416
|
+
const runMenuRunCommandExecute = runMenuRunCommand.execute;
|
|
338
417
|
const getIpyflowState = () => {
|
|
339
418
|
var _a;
|
|
340
419
|
const session = notebooks.currentWidget.sessionContext;
|
|
@@ -344,30 +423,62 @@ const extension = {
|
|
|
344
423
|
return ((_a = ipyflowState[session.session.id]) !== null && _a !== void 0 ? _a : {});
|
|
345
424
|
};
|
|
346
425
|
const isBatchReactive = () => {
|
|
347
|
-
var _a;
|
|
348
426
|
const state = getIpyflowState();
|
|
349
|
-
|
|
350
|
-
return (((_a = state.isIpyflowCommConnected) !== null && _a !== void 0 ? _a : false) &&
|
|
351
|
-
(settings === null || settings === void 0 ? void 0 : settings.exec_mode) === 'reactive' &&
|
|
352
|
-
(settings === null || settings === void 0 ? void 0 : settings.reactivity_mode) === 'batch');
|
|
353
|
-
};
|
|
354
|
-
runCellCommand.execute = (...args) => {
|
|
355
|
-
if (isBatchReactive() &&
|
|
356
|
-
getIpyflowState().activeCell.model.type === 'code') {
|
|
357
|
-
app.commands.execute('notebook:enter-command-mode');
|
|
358
|
-
}
|
|
359
|
-
else {
|
|
360
|
-
runCellCommandExecute.call(runCellCommand, args);
|
|
361
|
-
}
|
|
427
|
+
return state.isBatchReactive();
|
|
362
428
|
};
|
|
429
|
+
[
|
|
430
|
+
[runCellCommand, runCellCommandExecute, 'notebook:run-cell'],
|
|
431
|
+
[
|
|
432
|
+
runCellAndSelectNextCommand,
|
|
433
|
+
runCellAndSelectNextCommandExecute,
|
|
434
|
+
'notebook:run-cell-and-select-next',
|
|
435
|
+
],
|
|
436
|
+
[runMenuRunCommand, runMenuRunCommandExecute, 'runmenu:run'],
|
|
437
|
+
].forEach(([command, exec, commandId]) => {
|
|
438
|
+
command.execute = (...args) => {
|
|
439
|
+
var _a, _b, _c;
|
|
440
|
+
const state = getIpyflowState();
|
|
441
|
+
const nbpanel = notebooks.currentWidget;
|
|
442
|
+
const notebook = nbpanel.content;
|
|
443
|
+
const kernel = nbpanel.sessionContext.session.kernel.name;
|
|
444
|
+
if (kernel === 'ipyflow' && !((_a = state === null || state === void 0 ? void 0 : state.isIpyflowCommConnected) !== null && _a !== void 0 ? _a : false)) {
|
|
445
|
+
for (const cell of notebook.widgets) {
|
|
446
|
+
if (notebook.isSelectedOrActive(cell)) {
|
|
447
|
+
cell.setPrompt('*');
|
|
448
|
+
deferredCells.push(cell);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
else if (isBatchReactive() &&
|
|
453
|
+
((_c = (_b = state === null || state === void 0 ? void 0 : state.activeCell) === null || _b === void 0 ? void 0 : _b.model) === null || _c === void 0 ? void 0 : _c.type) === 'code') {
|
|
454
|
+
app.commands.execute('notebook:enter-command-mode');
|
|
455
|
+
const lastCell = state.notebook.widgets[state.notebook.widgets.length - 1];
|
|
456
|
+
const isExecutingLastCell = state.activeCell.model.id === lastCell.model.id;
|
|
457
|
+
executeBatchReactive();
|
|
458
|
+
if (['notebook:run-cell-and-select-next', 'runmenu:run'].includes(commandId)) {
|
|
459
|
+
if (isExecutingLastCell) {
|
|
460
|
+
app.commands.execute('notebook:insert-cell-below');
|
|
461
|
+
}
|
|
462
|
+
else {
|
|
463
|
+
app.commands.execute('notebook:move-cursor-down');
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
else {
|
|
468
|
+
exec.call(command, args);
|
|
469
|
+
state.requestComputeExecSchedule();
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
});
|
|
363
473
|
const executeBatchReactive = (skipFirst = false) => {
|
|
364
474
|
var _a;
|
|
365
475
|
const state = getIpyflowState();
|
|
366
476
|
if ((_a = state.isIpyflowCommConnected) !== null && _a !== void 0 ? _a : false) {
|
|
367
|
-
|
|
368
|
-
state.
|
|
369
|
-
|
|
370
|
-
|
|
477
|
+
const closureCellIds = [];
|
|
478
|
+
for (const cell of state.notebook.widgets) {
|
|
479
|
+
if (state.notebook.isSelectedOrActive(cell)) {
|
|
480
|
+
closureCellIds.push(cell.model.id);
|
|
481
|
+
}
|
|
371
482
|
}
|
|
372
483
|
let closure = state.computeTransitiveClosure(closureCellIds, true);
|
|
373
484
|
if (skipFirst) {
|
|
@@ -381,45 +492,9 @@ const extension = {
|
|
|
381
492
|
}
|
|
382
493
|
}
|
|
383
494
|
};
|
|
384
|
-
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
return;
|
|
388
|
-
}
|
|
389
|
-
if (!isBatchReactive()) {
|
|
390
|
-
return;
|
|
391
|
-
}
|
|
392
|
-
if (args.cell.model.type === 'code') {
|
|
393
|
-
const state = getIpyflowState();
|
|
394
|
-
state.executionScheduledCells.push(args.cell.model.id);
|
|
395
|
-
}
|
|
396
|
-
});
|
|
397
|
-
app.commands.commandExecuted.connect((_, args) => {
|
|
398
|
-
var _a;
|
|
399
|
-
if (args.id === 'notebook:run-cell') {
|
|
400
|
-
if (isBatchReactive()) {
|
|
401
|
-
executeBatchReactive();
|
|
402
|
-
}
|
|
403
|
-
else {
|
|
404
|
-
(_a = getIpyflowState()) === null || _a === void 0 ? void 0 : _a.requestComputeExecSchedule();
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
else if (['notebook:run-cell-and-select-next', 'runmenu:run'].includes(args.id)) {
|
|
408
|
-
const state = getIpyflowState();
|
|
409
|
-
const origActiveCell = state.activeCell;
|
|
410
|
-
try {
|
|
411
|
-
state.activeCell = state.prevActiveCell;
|
|
412
|
-
if (isBatchReactive()) {
|
|
413
|
-
executeBatchReactive(true);
|
|
414
|
-
}
|
|
415
|
-
else {
|
|
416
|
-
state === null || state === void 0 ? void 0 : state.requestComputeExecSchedule();
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
finally {
|
|
420
|
-
state.activeCell = origActiveCell;
|
|
421
|
-
}
|
|
422
|
-
}
|
|
495
|
+
notebooks.currentChanged.connect((_, nbPanel) => {
|
|
496
|
+
const session = nbPanel.sessionContext;
|
|
497
|
+
window.ipyflow = ipyflowState[session.session.id];
|
|
423
498
|
});
|
|
424
499
|
notebooks.widgetAdded.connect((sender, nbPanel) => {
|
|
425
500
|
const session = nbPanel.sessionContext;
|
|
@@ -517,6 +592,7 @@ const clearCellState = (notebook) => {
|
|
|
517
592
|
cell.node.classList.remove(readyClass);
|
|
518
593
|
cell.node.classList.remove(readyMakingInputClass);
|
|
519
594
|
cell.node.classList.remove(sliceClass);
|
|
595
|
+
cell.node.classList.remove(executeSliceClass);
|
|
520
596
|
// clear any old event listeners
|
|
521
597
|
const inputCollapser = getJpInputCollapser(cell.node);
|
|
522
598
|
if (inputCollapser !== null) {
|
|
@@ -575,6 +651,7 @@ const connectToComm = (session, notebooks, notebook) => {
|
|
|
575
651
|
const onContentChanged = _.debounce(() => {
|
|
576
652
|
if (disconnected) {
|
|
577
653
|
notebook.model.contentChanged.disconnect(onContentChanged);
|
|
654
|
+
notebook.model.cells.changed.disconnect(onContentChanged);
|
|
578
655
|
return;
|
|
579
656
|
}
|
|
580
657
|
const cell_metadata_by_id = state.gatherCellMetadataAndContent();
|
|
@@ -796,10 +873,12 @@ const connectToComm = (session, notebooks, notebook) => {
|
|
|
796
873
|
}
|
|
797
874
|
if (payload.type === 'establish') {
|
|
798
875
|
state.isIpyflowCommConnected = true;
|
|
876
|
+
refreshNodeMapping(notebook);
|
|
799
877
|
notebook.activeCellChanged.connect(onActiveCellChange);
|
|
800
878
|
notebook.activeCell.model.stateChanged.connect(onExecution);
|
|
801
879
|
notifyActiveCell(notebook.activeCell.model);
|
|
802
880
|
notebook.model.contentChanged.connect(onContentChanged);
|
|
881
|
+
notebook.model.cells.changed.connect(onContentChanged);
|
|
803
882
|
state.requestComputeExecSchedule();
|
|
804
883
|
}
|
|
805
884
|
else if (payload.type === 'set_exec_mode') {
|
|
@@ -857,6 +936,16 @@ const connectToComm = (session, notebooks, notebook) => {
|
|
|
857
936
|
state.lastExecutionHighlights = payload.highlights;
|
|
858
937
|
const lastExecutedCellId = payload.last_executed_cell_id;
|
|
859
938
|
state.executedReactiveReadyCells.add(lastExecutedCellId);
|
|
939
|
+
if (deferredCells.length > 0) {
|
|
940
|
+
const cells = deferredCells.splice(0, deferredCells.length);
|
|
941
|
+
if (state.isBatchReactive()) {
|
|
942
|
+
state.executeClosure(cells);
|
|
943
|
+
}
|
|
944
|
+
else {
|
|
945
|
+
state.executeCells(cells);
|
|
946
|
+
}
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
860
949
|
const last_execution_was_error = payload.last_execution_was_error;
|
|
861
950
|
let doneReactivelyExecuting = false;
|
|
862
951
|
if (last_execution_was_error) {
|
package/package.json
CHANGED