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.
Files changed (2) hide show
  1. package/lib/index.js +168 -79
  2. 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, NotebookActions, } from '@jupyterlab/notebook';
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
- ipyflowState[session_id] = new IpyflowSessionState();
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
- const settings = state.settings;
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
- let closureCellIds = state.executionScheduledCells;
368
- state.executionScheduledCells = [];
369
- if (closureCellIds.length === 0) {
370
- closureCellIds = [state.activeCell.model.id];
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
- NotebookActions.executionScheduled.connect((_, args) => {
385
- const notebook = notebooks === null || notebooks === void 0 ? void 0 : notebooks.currentWidget;
386
- if ((notebook === null || notebook === void 0 ? void 0 : notebook.content) !== args.notebook) {
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
@@ -67,5 +67,5 @@
67
67
  "extension": true,
68
68
  "outputDir": "../../core/ipyflow/resources/labextension/"
69
69
  },
70
- "version": "0.0.192"
70
+ "version": "0.0.193"
71
71
  }