UNPKG

97.8 kBJavaScriptView Raw
1// Copyright (c) Jupyter Development Team.
2// Distributed under the terms of the Modified BSD License.
3import { Clipboard, Dialog, showDialog } from '@jupyterlab/apputils';
4import { CodeCell, isMarkdownCellModel, isRawCellModel, MarkdownCell } from '@jupyterlab/cells';
5import { Notification } from '@jupyterlab/apputils';
6import { signalToPromise } from '@jupyterlab/coreutils';
7import { nullTranslator } from '@jupyterlab/translation';
8import { every, findIndex } from '@lumino/algorithm';
9import { JSONExt } from '@lumino/coreutils';
10import { Signal } from '@lumino/signaling';
11import * as React from 'react';
12/**
13 * The mimetype used for Jupyter cell data.
14 */
15const JUPYTER_CELL_MIME = 'application/vnd.jupyter.cells';
16export class KernelError extends Error {
17 /**
18 * Construct the kernel error.
19 */
20 constructor(content) {
21 const errorContent = content;
22 const errorName = errorContent.ename;
23 const errorValue = errorContent.evalue;
24 super(`KernelReplyNotOK: ${errorName} ${errorValue}`);
25 this.errorName = errorName;
26 this.errorValue = errorValue;
27 this.traceback = errorContent.traceback;
28 Object.setPrototypeOf(this, KernelError.prototype);
29 }
30}
31/**
32 * A collection of actions that run against notebooks.
33 *
34 * #### Notes
35 * All of the actions are a no-op if there is no model on the notebook.
36 * The actions set the widget `mode` to `'command'` unless otherwise specified.
37 * The actions will preserve the selection on the notebook widget unless
38 * otherwise specified.
39 */
40export class NotebookActions {
41 /**
42 * A signal that emits whenever a cell completes execution.
43 */
44 static get executed() {
45 return Private.executed;
46 }
47 /**
48 * A signal that emits whenever a cell execution is scheduled.
49 */
50 static get executionScheduled() {
51 return Private.executionScheduled;
52 }
53 /**
54 * A signal that emits when one notebook's cells are all executed.
55 */
56 static get selectionExecuted() {
57 return Private.selectionExecuted;
58 }
59 /**
60 * A signal that emits when a cell's output is cleared.
61 */
62 static get outputCleared() {
63 return Private.outputCleared;
64 }
65 /**
66 * A private constructor for the `NotebookActions` class.
67 *
68 * #### Notes
69 * This class can never be instantiated. Its static member `executed` will be
70 * merged with the `NotebookActions` namespace. The reason it exists as a
71 * standalone class is because at run time, the `Private.executed` variable
72 * does not yet exist, so it needs to be referenced via a getter.
73 */
74 constructor() {
75 // Intentionally empty.
76 }
77}
78/**
79 * A namespace for `NotebookActions` static methods.
80 */
81(function (NotebookActions) {
82 /**
83 * Split the active cell into two or more cells.
84 *
85 * @param notebook The target notebook widget.
86 *
87 * #### Notes
88 * It will preserve the existing mode.
89 * The last cell will be activated if no selection is found.
90 * If text was selected, the cell containing the selection will
91 * be activated.
92 * The existing selection will be cleared.
93 * The activated cell will have focus and the cursor will
94 * remain in the initial position.
95 * The leading whitespace in the second cell will be removed.
96 * If there is no content, two empty cells will be created.
97 * Both cells will have the same type as the original cell.
98 * This action can be undone.
99 */
100 function splitCell(notebook) {
101 if (!notebook.model || !notebook.activeCell) {
102 return;
103 }
104 const state = Private.getState(notebook);
105 // We force the notebook back in edit mode as splitting a cell
106 // requires using the cursor position within a cell (aka it was recently in edit mode)
107 // However the focus may be stolen if the action is triggered
108 // from the menu entry; switching the notebook in command mode.
109 notebook.mode = 'edit';
110 notebook.deselectAll();
111 const nbModel = notebook.model;
112 const index = notebook.activeCellIndex;
113 const child = notebook.widgets[index];
114 const editor = child.editor;
115 if (!editor) {
116 // TODO
117 return;
118 }
119 const selections = editor.getSelections();
120 const orig = child.model.sharedModel.getSource();
121 const offsets = [0];
122 let start = -1;
123 let end = -1;
124 for (let i = 0; i < selections.length; i++) {
125 // append start and end to handle selections
126 // cursors will have same start and end
127 start = editor.getOffsetAt(selections[i].start);
128 end = editor.getOffsetAt(selections[i].end);
129 if (start < end) {
130 offsets.push(start);
131 offsets.push(end);
132 }
133 else if (end < start) {
134 offsets.push(end);
135 offsets.push(start);
136 }
137 else {
138 offsets.push(start);
139 }
140 }
141 offsets.push(orig.length);
142 const cellCountAfterSplit = offsets.length - 1;
143 const clones = offsets.slice(0, -1).map((offset, offsetIdx) => {
144 const { cell_type, metadata, outputs } = child.model.sharedModel.toJSON();
145 return {
146 cell_type,
147 metadata,
148 source: orig
149 .slice(offset, offsets[offsetIdx + 1])
150 .replace(/^\n+/, '')
151 .replace(/\n+$/, ''),
152 outputs: offsetIdx === cellCountAfterSplit - 1 && cell_type === 'code'
153 ? outputs
154 : undefined
155 };
156 });
157 nbModel.sharedModel.transact(() => {
158 nbModel.sharedModel.deleteCell(index);
159 nbModel.sharedModel.insertCells(index, clones);
160 });
161 // If there is a selection the selected cell will be activated
162 const activeCellDelta = start !== end ? 2 : 1;
163 notebook.activeCellIndex = index + clones.length - activeCellDelta;
164 notebook
165 .scrollToItem(notebook.activeCellIndex)
166 .then(() => {
167 var _a;
168 (_a = notebook.activeCell) === null || _a === void 0 ? void 0 : _a.editor.focus();
169 })
170 .catch(reason => {
171 // no-op
172 });
173 void Private.handleState(notebook, state);
174 }
175 NotebookActions.splitCell = splitCell;
176 /**
177 * Merge the selected cells.
178 *
179 * @param notebook - The target notebook widget.
180 *
181 * @param mergeAbove - If only one cell is selected, indicates whether to merge it
182 * with the cell above (true) or below (false, default).
183 *
184 * #### Notes
185 * The widget mode will be preserved.
186 * If only one cell is selected and `mergeAbove` is true, the above cell will be selected.
187 * If only one cell is selected and `mergeAbove` is false, the below cell will be selected.
188 * If the active cell is a code cell, its outputs will be cleared.
189 * This action can be undone.
190 * The final cell will have the same type as the active cell.
191 * If the active cell is a markdown cell, it will be unrendered.
192 */
193 function mergeCells(notebook, mergeAbove = false) {
194 if (!notebook.model || !notebook.activeCell) {
195 return;
196 }
197 const state = Private.getState(notebook);
198 const toMerge = [];
199 const toDelete = [];
200 const model = notebook.model;
201 const cells = model.cells;
202 const primary = notebook.activeCell;
203 const active = notebook.activeCellIndex;
204 const attachments = {};
205 // Get the cells to merge.
206 notebook.widgets.forEach((child, index) => {
207 if (notebook.isSelectedOrActive(child)) {
208 toMerge.push(child.model.sharedModel.getSource());
209 if (index !== active) {
210 toDelete.push(index);
211 }
212 // Collect attachments if the cell is a markdown cell or a raw cell
213 const model = child.model;
214 if (isRawCellModel(model) || isMarkdownCellModel(model)) {
215 for (const key of model.attachments.keys) {
216 attachments[key] = model.attachments.get(key).toJSON();
217 }
218 }
219 }
220 });
221 // Check for only a single cell selected.
222 if (toMerge.length === 1) {
223 // Merge with the cell above when mergeAbove is true
224 if (mergeAbove === true) {
225 // Bail if it is the first cell.
226 if (active === 0) {
227 return;
228 }
229 // Otherwise merge with the previous cell.
230 const cellModel = cells.get(active - 1);
231 toMerge.unshift(cellModel.sharedModel.getSource());
232 toDelete.push(active - 1);
233 }
234 else if (mergeAbove === false) {
235 // Bail if it is the last cell.
236 if (active === cells.length - 1) {
237 return;
238 }
239 // Otherwise merge with the next cell.
240 const cellModel = cells.get(active + 1);
241 toMerge.push(cellModel.sharedModel.getSource());
242 toDelete.push(active + 1);
243 }
244 }
245 notebook.deselectAll();
246 const primaryModel = primary.model.sharedModel;
247 const { cell_type, metadata } = primaryModel.toJSON();
248 if (primaryModel.cell_type === 'code') {
249 // We can trust this cell because the outputs will be removed.
250 metadata.trusted = true;
251 }
252 const newModel = {
253 cell_type,
254 metadata,
255 source: toMerge.join('\n\n'),
256 attachments: primaryModel.cell_type === 'markdown' ||
257 primaryModel.cell_type === 'raw'
258 ? attachments
259 : undefined
260 };
261 // Make the changes while preserving history.
262 model.sharedModel.transact(() => {
263 model.sharedModel.deleteCell(active);
264 model.sharedModel.insertCell(active, newModel);
265 toDelete
266 .sort((a, b) => b - a)
267 .forEach(index => {
268 model.sharedModel.deleteCell(index);
269 });
270 });
271 // If the original cell is a markdown cell, make sure
272 // the new cell is unrendered.
273 if (primary instanceof MarkdownCell) {
274 notebook.activeCell.rendered = false;
275 }
276 void Private.handleState(notebook, state);
277 }
278 NotebookActions.mergeCells = mergeCells;
279 /**
280 * Delete the selected cells.
281 *
282 * @param notebook - The target notebook widget.
283 *
284 * #### Notes
285 * The cell after the last selected cell will be activated.
286 * It will add a code cell if all cells are deleted.
287 * This action can be undone.
288 */
289 function deleteCells(notebook) {
290 if (!notebook.model || !notebook.activeCell) {
291 return;
292 }
293 const state = Private.getState(notebook);
294 Private.deleteCells(notebook);
295 void Private.handleState(notebook, state, true);
296 }
297 NotebookActions.deleteCells = deleteCells;
298 /**
299 * Insert a new code cell above the active cell or in index 0 if the notebook is empty.
300 *
301 * @param notebook - The target notebook widget.
302 *
303 * #### Notes
304 * The widget mode will be preserved.
305 * This action can be undone.
306 * The existing selection will be cleared.
307 * The new cell will the active cell.
308 */
309 function insertAbove(notebook) {
310 if (!notebook.model) {
311 return;
312 }
313 const state = Private.getState(notebook);
314 const model = notebook.model;
315 const newIndex = notebook.activeCell ? notebook.activeCellIndex : 0;
316 model.sharedModel.insertCell(newIndex, {
317 cell_type: notebook.notebookConfig.defaultCell,
318 metadata: notebook.notebookConfig.defaultCell === 'code'
319 ? {
320 // This is an empty cell created by user, thus is trusted
321 trusted: true
322 }
323 : {}
324 });
325 // Make the newly inserted cell active.
326 notebook.activeCellIndex = newIndex;
327 notebook.deselectAll();
328 void Private.handleState(notebook, state, true);
329 }
330 NotebookActions.insertAbove = insertAbove;
331 /**
332 * Insert a new code cell below the active cell or in index 0 if the notebook is empty.
333 *
334 * @param notebook - The target notebook widget.
335 *
336 * #### Notes
337 * The widget mode will be preserved.
338 * This action can be undone.
339 * The existing selection will be cleared.
340 * The new cell will be the active cell.
341 */
342 function insertBelow(notebook) {
343 if (!notebook.model) {
344 return;
345 }
346 const state = Private.getState(notebook);
347 const model = notebook.model;
348 const newIndex = notebook.activeCell ? notebook.activeCellIndex + 1 : 0;
349 model.sharedModel.insertCell(newIndex, {
350 cell_type: notebook.notebookConfig.defaultCell,
351 metadata: notebook.notebookConfig.defaultCell === 'code'
352 ? {
353 // This is an empty cell created by user, thus is trusted
354 trusted: true
355 }
356 : {}
357 });
358 // Make the newly inserted cell active.
359 notebook.activeCellIndex = newIndex;
360 notebook.deselectAll();
361 void Private.handleState(notebook, state, true);
362 }
363 NotebookActions.insertBelow = insertBelow;
364 function move(notebook, shift) {
365 if (!notebook.model || !notebook.activeCell) {
366 return;
367 }
368 const state = Private.getState(notebook);
369 const firstIndex = notebook.widgets.findIndex(w => notebook.isSelectedOrActive(w));
370 let lastIndex = notebook.widgets
371 .slice(firstIndex + 1)
372 .findIndex(w => !notebook.isSelectedOrActive(w));
373 if (lastIndex >= 0) {
374 lastIndex += firstIndex + 1;
375 }
376 else {
377 lastIndex = notebook.model.cells.length;
378 }
379 if (shift > 0) {
380 notebook.moveCell(firstIndex, lastIndex, lastIndex - firstIndex);
381 }
382 else {
383 notebook.moveCell(firstIndex, firstIndex + shift, lastIndex - firstIndex);
384 }
385 void Private.handleState(notebook, state, true);
386 }
387 /**
388 * Move the selected cell(s) down.
389 *
390 * @param notebook = The target notebook widget.
391 */
392 function moveDown(notebook) {
393 move(notebook, 1);
394 }
395 NotebookActions.moveDown = moveDown;
396 /**
397 * Move the selected cell(s) up.
398 *
399 * @param notebook - The target notebook widget.
400 */
401 function moveUp(notebook) {
402 move(notebook, -1);
403 }
404 NotebookActions.moveUp = moveUp;
405 /**
406 * Change the selected cell type(s).
407 *
408 * @param notebook - The target notebook widget.
409 *
410 * @param value - The target cell type.
411 *
412 * #### Notes
413 * It should preserve the widget mode.
414 * This action can be undone.
415 * The existing selection will be cleared.
416 * Any cells converted to markdown will be unrendered.
417 */
418 function changeCellType(notebook, value) {
419 if (!notebook.model || !notebook.activeCell) {
420 return;
421 }
422 const state = Private.getState(notebook);
423 Private.changeCellType(notebook, value);
424 void Private.handleState(notebook, state);
425 }
426 NotebookActions.changeCellType = changeCellType;
427 /**
428 * Run the selected cell(s).
429 *
430 * @param notebook - The target notebook widget.
431 * @param sessionContext - The client session object.
432 * @param sessionDialogs - The session dialogs.
433 * @param translator - The application translator.
434 *
435 * #### Notes
436 * The last selected cell will be activated, but not scrolled into view.
437 * The existing selection will be cleared.
438 * An execution error will prevent the remaining code cells from executing.
439 * All markdown cells will be rendered.
440 */
441 function run(notebook, sessionContext, sessionDialogs, translator) {
442 if (!notebook.model || !notebook.activeCell) {
443 return Promise.resolve(false);
444 }
445 const state = Private.getState(notebook);
446 const promise = Private.runSelected(notebook, sessionContext, sessionDialogs, translator);
447 void Private.handleRunState(notebook, state);
448 return promise;
449 }
450 NotebookActions.run = run;
451 /**
452 * Run specified cells.
453 *
454 * @param notebook - The target notebook widget.
455 * @param cells - The cells to run.
456 * @param sessionContext - The client session object.
457 * @param sessionDialogs - The session dialogs.
458 * @param translator - The application translator.
459 *
460 * #### Notes
461 * The existing selection will be preserved.
462 * The mode will be changed to command.
463 * An execution error will prevent the remaining code cells from executing.
464 * All markdown cells will be rendered.
465 */
466 function runCells(notebook, cells, sessionContext, sessionDialogs, translator) {
467 if (!notebook.model) {
468 return Promise.resolve(false);
469 }
470 const state = Private.getState(notebook);
471 const promise = Private.runCells(notebook, cells, sessionContext, sessionDialogs, translator);
472 void Private.handleRunState(notebook, state);
473 return promise;
474 }
475 NotebookActions.runCells = runCells;
476 /**
477 * Run the selected cell(s) and advance to the next cell.
478 *
479 * @param notebook - The target notebook widget.
480 * @param sessionContext - The client session object.
481 * @param sessionDialogs - The session dialogs.
482 * @param translator - The application translator.
483 *
484 * #### Notes
485 * The existing selection will be cleared.
486 * The cell after the last selected cell will be activated and scrolled into view.
487 * An execution error will prevent the remaining code cells from executing.
488 * All markdown cells will be rendered.
489 * If the last selected cell is the last cell, a new code cell
490 * will be created in `'edit'` mode. The new cell creation can be undone.
491 */
492 async function runAndAdvance(notebook, sessionContext, sessionDialogs, translator) {
493 var _a;
494 if (!notebook.model || !notebook.activeCell) {
495 return Promise.resolve(false);
496 }
497 const state = Private.getState(notebook);
498 const promise = Private.runSelected(notebook, sessionContext, sessionDialogs, translator);
499 const model = notebook.model;
500 if (notebook.activeCellIndex === notebook.widgets.length - 1) {
501 // Do not use push here, as we want an widget insertion
502 // to make sure no placeholder widget is rendered.
503 model.sharedModel.insertCell(notebook.widgets.length, {
504 cell_type: notebook.notebookConfig.defaultCell,
505 metadata: notebook.notebookConfig.defaultCell === 'code'
506 ? {
507 // This is an empty cell created by user, thus is trusted
508 trusted: true
509 }
510 : {}
511 });
512 notebook.activeCellIndex++;
513 if (((_a = notebook.activeCell) === null || _a === void 0 ? void 0 : _a.inViewport) === false) {
514 await signalToPromise(notebook.activeCell.inViewportChanged, 200).catch(() => {
515 // no-op
516 });
517 }
518 notebook.mode = 'edit';
519 }
520 else {
521 notebook.activeCellIndex++;
522 }
523 // If a cell is outside of viewport and scrolling is needed, the `smart`
524 // logic in `handleRunState` will choose appropriate alignment, except
525 // for the case of a small cell less than one viewport away for which it
526 // would use the `auto` heuristic, for which we set the preferred alignment
527 // to `center` as in most cases there will be space below and above a cell
528 // that is smaller than (or approximately equal to) the viewport size.
529 void Private.handleRunState(notebook, state, 'center');
530 return promise;
531 }
532 NotebookActions.runAndAdvance = runAndAdvance;
533 /**
534 * Run the selected cell(s) and insert a new code cell.
535 *
536 * @param notebook - The target notebook widget.
537 * @param sessionContext - The client session object.
538 * @param sessionDialogs - The session dialogs.
539 * @param translator - The application translator.
540 *
541 * #### Notes
542 * An execution error will prevent the remaining code cells from executing.
543 * All markdown cells will be rendered.
544 * The widget mode will be set to `'edit'` after running.
545 * The existing selection will be cleared.
546 * The cell insert can be undone.
547 * The new cell will be scrolled into view.
548 */
549 async function runAndInsert(notebook, sessionContext, sessionDialogs, translator) {
550 var _a;
551 if (!notebook.model || !notebook.activeCell) {
552 return Promise.resolve(false);
553 }
554 const state = Private.getState(notebook);
555 const promise = Private.runSelected(notebook, sessionContext, sessionDialogs, translator);
556 const model = notebook.model;
557 model.sharedModel.insertCell(notebook.activeCellIndex + 1, {
558 cell_type: notebook.notebookConfig.defaultCell,
559 metadata: notebook.notebookConfig.defaultCell === 'code'
560 ? {
561 // This is an empty cell created by user, thus is trusted
562 trusted: true
563 }
564 : {}
565 });
566 notebook.activeCellIndex++;
567 if (((_a = notebook.activeCell) === null || _a === void 0 ? void 0 : _a.inViewport) === false) {
568 await signalToPromise(notebook.activeCell.inViewportChanged, 200).catch(() => {
569 // no-op
570 });
571 }
572 notebook.mode = 'edit';
573 void Private.handleRunState(notebook, state, 'center');
574 return promise;
575 }
576 NotebookActions.runAndInsert = runAndInsert;
577 /**
578 * Run all of the cells in the notebook.
579 *
580 * @param notebook - The target notebook widget.
581 * @param sessionContext - The client session object.
582 * @param sessionDialogs - The session dialogs.
583 * @param translator - The application translator.
584 *
585 * #### Notes
586 * The existing selection will be cleared.
587 * An execution error will prevent the remaining code cells from executing.
588 * All markdown cells will be rendered.
589 * The last cell in the notebook will be activated and scrolled into view.
590 */
591 function runAll(notebook, sessionContext, sessionDialogs, translator) {
592 if (!notebook.model || !notebook.activeCell) {
593 return Promise.resolve(false);
594 }
595 const state = Private.getState(notebook);
596 const lastIndex = notebook.widgets.length;
597 const promise = Private.runCells(notebook, notebook.widgets, sessionContext, sessionDialogs, translator);
598 notebook.activeCellIndex = lastIndex;
599 notebook.deselectAll();
600 void Private.handleRunState(notebook, state);
601 return promise;
602 }
603 NotebookActions.runAll = runAll;
604 function renderAllMarkdown(notebook) {
605 if (!notebook.model || !notebook.activeCell) {
606 return Promise.resolve(false);
607 }
608 const previousIndex = notebook.activeCellIndex;
609 const state = Private.getState(notebook);
610 notebook.widgets.forEach((child, index) => {
611 if (child.model.type === 'markdown') {
612 notebook.select(child);
613 // This is to make sure that the activeCell
614 // does not get executed
615 notebook.activeCellIndex = index;
616 }
617 });
618 if (notebook.activeCell.model.type !== 'markdown') {
619 return Promise.resolve(true);
620 }
621 const promise = Private.runSelected(notebook);
622 notebook.activeCellIndex = previousIndex;
623 void Private.handleRunState(notebook, state);
624 return promise;
625 }
626 NotebookActions.renderAllMarkdown = renderAllMarkdown;
627 /**
628 * Run all of the cells before the currently active cell (exclusive).
629 *
630 * @param notebook - The target notebook widget.
631 * @param sessionContext - The client session object.
632 * @param sessionDialogs - The session dialogs.
633 * @param translator - The application translator.
634 *
635 * #### Notes
636 * The existing selection will be cleared.
637 * An execution error will prevent the remaining code cells from executing.
638 * All markdown cells will be rendered.
639 * The currently active cell will remain selected.
640 */
641 function runAllAbove(notebook, sessionContext, sessionDialogs, translator) {
642 const { activeCell, activeCellIndex, model } = notebook;
643 if (!model || !activeCell || activeCellIndex < 1) {
644 return Promise.resolve(false);
645 }
646 const state = Private.getState(notebook);
647 const promise = Private.runCells(notebook, notebook.widgets.slice(0, notebook.activeCellIndex), sessionContext, sessionDialogs, translator);
648 notebook.deselectAll();
649 void Private.handleRunState(notebook, state);
650 return promise;
651 }
652 NotebookActions.runAllAbove = runAllAbove;
653 /**
654 * Run all of the cells after the currently active cell (inclusive).
655 *
656 * @param notebook - The target notebook widget.
657 * @param sessionContext - The client session object.
658 * @param sessionDialogs - The session dialogs.
659 * @param translator - The application translator.
660 *
661 * #### Notes
662 * The existing selection will be cleared.
663 * An execution error will prevent the remaining code cells from executing.
664 * All markdown cells will be rendered.
665 * The last cell in the notebook will be activated and scrolled into view.
666 */
667 function runAllBelow(notebook, sessionContext, sessionDialogs, translator) {
668 if (!notebook.model || !notebook.activeCell) {
669 return Promise.resolve(false);
670 }
671 const state = Private.getState(notebook);
672 const lastIndex = notebook.widgets.length;
673 const promise = Private.runCells(notebook, notebook.widgets.slice(notebook.activeCellIndex), sessionContext, sessionDialogs, translator);
674 notebook.activeCellIndex = lastIndex;
675 notebook.deselectAll();
676 void Private.handleRunState(notebook, state);
677 return promise;
678 }
679 NotebookActions.runAllBelow = runAllBelow;
680 /**
681 * Replaces the selection in the active cell of the notebook.
682 *
683 * @param notebook - The target notebook widget.
684 * @param text - The text to replace the selection.
685 */
686 function replaceSelection(notebook, text) {
687 var _a, _b, _c;
688 if (!notebook.model || !((_a = notebook.activeCell) === null || _a === void 0 ? void 0 : _a.editor)) {
689 return;
690 }
691 (_c = (_b = notebook.activeCell.editor).replaceSelection) === null || _c === void 0 ? void 0 : _c.call(_b, text);
692 }
693 NotebookActions.replaceSelection = replaceSelection;
694 /**
695 * Select the above the active cell.
696 *
697 * @param notebook - The target notebook widget.
698 *
699 * #### Notes
700 * The widget mode will be preserved.
701 * This is a no-op if the first cell is the active cell.
702 * This will skip any collapsed cells.
703 * The existing selection will be cleared.
704 */
705 function selectAbove(notebook) {
706 if (!notebook.model || !notebook.activeCell) {
707 return;
708 }
709 const footer = notebook.layout.footer;
710 if (footer && document.activeElement === footer.node) {
711 footer.node.blur();
712 notebook.mode = 'command';
713 return;
714 }
715 if (notebook.activeCellIndex === 0) {
716 return;
717 }
718 let possibleNextCellIndex = notebook.activeCellIndex - 1;
719 // find first non hidden cell above current cell
720 while (possibleNextCellIndex >= 0) {
721 const possibleNextCell = notebook.widgets[possibleNextCellIndex];
722 if (!possibleNextCell.inputHidden && !possibleNextCell.isHidden) {
723 break;
724 }
725 possibleNextCellIndex -= 1;
726 }
727 const state = Private.getState(notebook);
728 notebook.activeCellIndex = possibleNextCellIndex;
729 notebook.deselectAll();
730 void Private.handleState(notebook, state, true);
731 }
732 NotebookActions.selectAbove = selectAbove;
733 /**
734 * Select the cell below the active cell.
735 *
736 * @param notebook - The target notebook widget.
737 *
738 * #### Notes
739 * The widget mode will be preserved.
740 * This is a no-op if the last cell is the active cell.
741 * This will skip any collapsed cells.
742 * The existing selection will be cleared.
743 */
744 function selectBelow(notebook) {
745 if (!notebook.model || !notebook.activeCell) {
746 return;
747 }
748 let maxCellIndex = notebook.widgets.length - 1;
749 // Find last non-hidden cell
750 while (notebook.widgets[maxCellIndex].isHidden ||
751 notebook.widgets[maxCellIndex].inputHidden) {
752 maxCellIndex -= 1;
753 }
754 if (notebook.activeCellIndex === maxCellIndex) {
755 const footer = notebook.layout.footer;
756 footer === null || footer === void 0 ? void 0 : footer.node.focus();
757 return;
758 }
759 let possibleNextCellIndex = notebook.activeCellIndex + 1;
760 // find first non hidden cell below current cell
761 while (possibleNextCellIndex < maxCellIndex) {
762 let possibleNextCell = notebook.widgets[possibleNextCellIndex];
763 if (!possibleNextCell.inputHidden && !possibleNextCell.isHidden) {
764 break;
765 }
766 possibleNextCellIndex += 1;
767 }
768 const state = Private.getState(notebook);
769 notebook.activeCellIndex = possibleNextCellIndex;
770 notebook.deselectAll();
771 void Private.handleState(notebook, state, true);
772 }
773 NotebookActions.selectBelow = selectBelow;
774 /** Insert new heading of same level above active cell.
775 *
776 * @param notebook - The target notebook widget
777 */
778 async function insertSameLevelHeadingAbove(notebook) {
779 if (!notebook.model || !notebook.activeCell) {
780 return;
781 }
782 let headingLevel = Private.Headings.determineHeadingLevel(notebook.activeCell, notebook);
783 if (headingLevel == -1) {
784 await Private.Headings.insertHeadingAboveCellIndex(0, 1, notebook);
785 }
786 else {
787 await Private.Headings.insertHeadingAboveCellIndex(notebook.activeCellIndex, headingLevel, notebook);
788 }
789 }
790 NotebookActions.insertSameLevelHeadingAbove = insertSameLevelHeadingAbove;
791 /** Insert new heading of same level at end of current section.
792 *
793 * @param notebook - The target notebook widget
794 */
795 async function insertSameLevelHeadingBelow(notebook) {
796 if (!notebook.model || !notebook.activeCell) {
797 return;
798 }
799 let headingLevel = Private.Headings.determineHeadingLevel(notebook.activeCell, notebook);
800 headingLevel = headingLevel > -1 ? headingLevel : 1;
801 let cellIdxOfHeadingBelow = Private.Headings.findLowerEqualLevelHeadingBelow(notebook.activeCell, notebook, true);
802 await Private.Headings.insertHeadingAboveCellIndex(cellIdxOfHeadingBelow == -1
803 ? notebook.model.cells.length
804 : cellIdxOfHeadingBelow, headingLevel, notebook);
805 }
806 NotebookActions.insertSameLevelHeadingBelow = insertSameLevelHeadingBelow;
807 /**
808 * Select the heading above the active cell or, if already at heading, collapse it.
809 *
810 * @param notebook - The target notebook widget.
811 *
812 * #### Notes
813 * The widget mode will be preserved.
814 * This is a no-op if the active cell is the topmost heading in collapsed state
815 * The existing selection will be cleared.
816 */
817 function selectHeadingAboveOrCollapseHeading(notebook) {
818 if (!notebook.model || !notebook.activeCell) {
819 return;
820 }
821 const state = Private.getState(notebook);
822 let hInfoActiveCell = getHeadingInfo(notebook.activeCell);
823 // either collapse or find the right heading to jump to
824 if (hInfoActiveCell.isHeading && !hInfoActiveCell.collapsed) {
825 setHeadingCollapse(notebook.activeCell, true, notebook);
826 }
827 else {
828 let targetHeadingCellIdx = Private.Headings.findLowerEqualLevelParentHeadingAbove(notebook.activeCell, notebook, true);
829 if (targetHeadingCellIdx > -1) {
830 notebook.activeCellIndex = targetHeadingCellIdx;
831 }
832 }
833 // clear selection and handle state
834 notebook.deselectAll();
835 void Private.handleState(notebook, state, true);
836 }
837 NotebookActions.selectHeadingAboveOrCollapseHeading = selectHeadingAboveOrCollapseHeading;
838 /**
839 * Select the heading below the active cell or, if already at heading, expand it.
840 *
841 * @param notebook - The target notebook widget.
842 *
843 * #### Notes
844 * The widget mode will be preserved.
845 * This is a no-op if the active cell is the last heading in expanded state
846 * The existing selection will be cleared.
847 */
848 function selectHeadingBelowOrExpandHeading(notebook) {
849 if (!notebook.model || !notebook.activeCell) {
850 return;
851 }
852 const state = Private.getState(notebook);
853 let hInfo = getHeadingInfo(notebook.activeCell);
854 if (hInfo.isHeading && hInfo.collapsed) {
855 setHeadingCollapse(notebook.activeCell, false, notebook);
856 }
857 else {
858 let targetHeadingCellIdx = Private.Headings.findHeadingBelow(notebook.activeCell, notebook, true // return index of heading cell
859 );
860 if (targetHeadingCellIdx > -1) {
861 notebook.activeCellIndex = targetHeadingCellIdx;
862 }
863 }
864 notebook.deselectAll();
865 void Private.handleState(notebook, state, true);
866 }
867 NotebookActions.selectHeadingBelowOrExpandHeading = selectHeadingBelowOrExpandHeading;
868 /**
869 * Extend the selection to the cell above.
870 *
871 * @param notebook - The target notebook widget.
872 * @param toTop - If true, denotes selection to extend to the top.
873 *
874 * #### Notes
875 * This is a no-op if the first cell is the active cell.
876 * The new cell will be activated.
877 */
878 function extendSelectionAbove(notebook, toTop = false) {
879 if (!notebook.model || !notebook.activeCell) {
880 return;
881 }
882 // Do not wrap around.
883 if (notebook.activeCellIndex === 0) {
884 return;
885 }
886 const state = Private.getState(notebook);
887 notebook.mode = 'command';
888 // Check if toTop is true, if yes, selection is made to the top.
889 if (toTop) {
890 notebook.extendContiguousSelectionTo(0);
891 }
892 else {
893 notebook.extendContiguousSelectionTo(notebook.activeCellIndex - 1);
894 }
895 void Private.handleState(notebook, state, true);
896 }
897 NotebookActions.extendSelectionAbove = extendSelectionAbove;
898 /**
899 * Extend the selection to the cell below.
900 *
901 * @param notebook - The target notebook widget.
902 * @param toBottom - If true, denotes selection to extend to the bottom.
903 *
904 * #### Notes
905 * This is a no-op if the last cell is the active cell.
906 * The new cell will be activated.
907 */
908 function extendSelectionBelow(notebook, toBottom = false) {
909 if (!notebook.model || !notebook.activeCell) {
910 return;
911 }
912 // Do not wrap around.
913 if (notebook.activeCellIndex === notebook.widgets.length - 1) {
914 return;
915 }
916 const state = Private.getState(notebook);
917 notebook.mode = 'command';
918 // Check if toBottom is true, if yes selection is made to the bottom.
919 if (toBottom) {
920 notebook.extendContiguousSelectionTo(notebook.widgets.length - 1);
921 }
922 else {
923 notebook.extendContiguousSelectionTo(notebook.activeCellIndex + 1);
924 }
925 void Private.handleState(notebook, state, true);
926 }
927 NotebookActions.extendSelectionBelow = extendSelectionBelow;
928 /**
929 * Select all of the cells of the notebook.
930 *
931 * @param notebook - the target notebook widget.
932 */
933 function selectAll(notebook) {
934 if (!notebook.model || !notebook.activeCell) {
935 return;
936 }
937 notebook.widgets.forEach(child => {
938 notebook.select(child);
939 });
940 }
941 NotebookActions.selectAll = selectAll;
942 /**
943 * Deselect all of the cells of the notebook.
944 *
945 * @param notebook - the target notebook widget.
946 */
947 function deselectAll(notebook) {
948 if (!notebook.model || !notebook.activeCell) {
949 return;
950 }
951 notebook.deselectAll();
952 }
953 NotebookActions.deselectAll = deselectAll;
954 /**
955 * Copy the selected cell(s) data to a clipboard.
956 *
957 * @param notebook - The target notebook widget.
958 */
959 function copy(notebook) {
960 Private.copyOrCut(notebook, false);
961 }
962 NotebookActions.copy = copy;
963 /**
964 * Cut the selected cell data to a clipboard.
965 *
966 * @param notebook - The target notebook widget.
967 *
968 * #### Notes
969 * This action can be undone.
970 * A new code cell is added if all cells are cut.
971 */
972 function cut(notebook) {
973 Private.copyOrCut(notebook, true);
974 }
975 NotebookActions.cut = cut;
976 /**
977 * Paste cells from the application clipboard.
978 *
979 * @param notebook - The target notebook widget.
980 *
981 * @param mode - the mode of adding cells:
982 * 'below' (default) adds cells below the active cell,
983 * 'belowSelected' adds cells below all selected cells,
984 * 'above' adds cells above the active cell, and
985 * 'replace' removes the currently selected cells and adds cells in their place.
986 *
987 * #### Notes
988 * The last pasted cell becomes the active cell.
989 * This is a no-op if there is no cell data on the clipboard.
990 * This action can be undone.
991 */
992 function paste(notebook, mode = 'below') {
993 const clipboard = Clipboard.getInstance();
994 if (!clipboard.hasData(JUPYTER_CELL_MIME)) {
995 return;
996 }
997 const values = clipboard.getData(JUPYTER_CELL_MIME);
998 addCells(notebook, mode, values, true);
999 void focusActiveCell(notebook);
1000 }
1001 NotebookActions.paste = paste;
1002 /**
1003 * Duplicate selected cells in the notebook without using the application clipboard.
1004 *
1005 * @param notebook - The target notebook widget.
1006 *
1007 * @param mode - the mode of adding cells:
1008 * 'below' (default) adds cells below the active cell,
1009 * 'belowSelected' adds cells below all selected cells,
1010 * 'above' adds cells above the active cell, and
1011 * 'replace' removes the currently selected cells and adds cells in their place.
1012 *
1013 * #### Notes
1014 * The last pasted cell becomes the active cell.
1015 * This is a no-op if there is no cell data on the clipboard.
1016 * This action can be undone.
1017 */
1018 function duplicate(notebook, mode = 'below') {
1019 const values = Private.selectedCells(notebook);
1020 if (!values || values.length === 0) {
1021 return;
1022 }
1023 addCells(notebook, mode, values, false); // Cells not from the clipboard
1024 }
1025 NotebookActions.duplicate = duplicate;
1026 /**
1027 * Adds cells to the notebook.
1028 *
1029 * @param notebook - The target notebook widget.
1030 *
1031 * @param mode - the mode of adding cells:
1032 * 'below' (default) adds cells below the active cell,
1033 * 'belowSelected' adds cells below all selected cells,
1034 * 'above' adds cells above the active cell, and
1035 * 'replace' removes the currently selected cells and adds cells in their place.
1036 *
1037 * @param values — The cells to add to the notebook.
1038 *
1039 * @param cellsFromClipboard — True if the cells were sourced from the clipboard.
1040 *
1041 * #### Notes
1042 * The last added cell becomes the active cell.
1043 * This is a no-op if values is an empty array.
1044 * This action can be undone.
1045 */
1046 function addCells(notebook, mode = 'below', values, cellsFromClipboard = false) {
1047 if (!notebook.model || !notebook.activeCell) {
1048 return;
1049 }
1050 const state = Private.getState(notebook);
1051 const model = notebook.model;
1052 notebook.mode = 'command';
1053 let index = 0;
1054 const prevActiveCellIndex = notebook.activeCellIndex;
1055 model.sharedModel.transact(() => {
1056 // Set the starting index of the paste operation depending upon the mode.
1057 switch (mode) {
1058 case 'below':
1059 index = notebook.activeCellIndex + 1;
1060 break;
1061 case 'belowSelected':
1062 notebook.widgets.forEach((child, childIndex) => {
1063 if (notebook.isSelectedOrActive(child)) {
1064 index = childIndex + 1;
1065 }
1066 });
1067 break;
1068 case 'above':
1069 index = notebook.activeCellIndex;
1070 break;
1071 case 'replace': {
1072 // Find the cells to delete.
1073 const toDelete = [];
1074 notebook.widgets.forEach((child, index) => {
1075 const deletable = child.model.sharedModel.getMetadata('deletable') !== false;
1076 if (notebook.isSelectedOrActive(child) && deletable) {
1077 toDelete.push(index);
1078 }
1079 });
1080 // If cells are not deletable, we may not have anything to delete.
1081 if (toDelete.length > 0) {
1082 // Delete the cells as one undo event.
1083 toDelete.reverse().forEach(i => {
1084 model.sharedModel.deleteCell(i);
1085 });
1086 }
1087 index = toDelete[0];
1088 break;
1089 }
1090 default:
1091 break;
1092 }
1093 model.sharedModel.insertCells(index, values.map(cell => {
1094 cell.id =
1095 cell.cell_type === 'code' &&
1096 notebook.lastClipboardInteraction === 'cut' &&
1097 typeof cell.id === 'string'
1098 ? cell.id
1099 : undefined;
1100 return cell;
1101 }));
1102 });
1103 notebook.activeCellIndex = prevActiveCellIndex + values.length;
1104 notebook.deselectAll();
1105 if (cellsFromClipboard) {
1106 notebook.lastClipboardInteraction = 'paste';
1107 }
1108 void Private.handleState(notebook, state, true);
1109 }
1110 /**
1111 * Undo a cell action.
1112 *
1113 * @param notebook - The target notebook widget.
1114 *
1115 * #### Notes
1116 * This is a no-op if there are no cell actions to undo.
1117 */
1118 function undo(notebook) {
1119 if (!notebook.model) {
1120 return;
1121 }
1122 const state = Private.getState(notebook);
1123 notebook.mode = 'command';
1124 notebook.model.sharedModel.undo();
1125 notebook.deselectAll();
1126 void Private.handleState(notebook, state);
1127 }
1128 NotebookActions.undo = undo;
1129 /**
1130 * Redo a cell action.
1131 *
1132 * @param notebook - The target notebook widget.
1133 *
1134 * #### Notes
1135 * This is a no-op if there are no cell actions to redo.
1136 */
1137 function redo(notebook) {
1138 if (!notebook.model || !notebook.activeCell) {
1139 return;
1140 }
1141 const state = Private.getState(notebook);
1142 notebook.mode = 'command';
1143 notebook.model.sharedModel.redo();
1144 notebook.deselectAll();
1145 void Private.handleState(notebook, state);
1146 }
1147 NotebookActions.redo = redo;
1148 /**
1149 * Toggle the line number of all cells.
1150 *
1151 * @param notebook - The target notebook widget.
1152 *
1153 * #### Notes
1154 * The original state is based on the state of the active cell.
1155 * The `mode` of the widget will be preserved.
1156 */
1157 function toggleAllLineNumbers(notebook) {
1158 if (!notebook.model || !notebook.activeCell) {
1159 return;
1160 }
1161 const state = Private.getState(notebook);
1162 const config = notebook.editorConfig;
1163 const lineNumbers = !(config.code.lineNumbers &&
1164 config.markdown.lineNumbers &&
1165 config.raw.lineNumbers);
1166 const newConfig = {
1167 code: { ...config.code, lineNumbers },
1168 markdown: { ...config.markdown, lineNumbers },
1169 raw: { ...config.raw, lineNumbers }
1170 };
1171 notebook.editorConfig = newConfig;
1172 void Private.handleState(notebook, state);
1173 }
1174 NotebookActions.toggleAllLineNumbers = toggleAllLineNumbers;
1175 /**
1176 * Clear the code outputs of the selected cells.
1177 *
1178 * @param notebook - The target notebook widget.
1179 *
1180 * #### Notes
1181 * The widget `mode` will be preserved.
1182 */
1183 function clearOutputs(notebook) {
1184 if (!notebook.model || !notebook.activeCell) {
1185 return;
1186 }
1187 const state = Private.getState(notebook);
1188 let index = -1;
1189 for (const cell of notebook.model.cells) {
1190 const child = notebook.widgets[++index];
1191 if (notebook.isSelectedOrActive(child) && cell.type === 'code') {
1192 cell.sharedModel.transact(() => {
1193 cell.clearExecution();
1194 child.outputHidden = false;
1195 }, false);
1196 Private.outputCleared.emit({ notebook, cell: child });
1197 }
1198 }
1199 void Private.handleState(notebook, state, true);
1200 }
1201 NotebookActions.clearOutputs = clearOutputs;
1202 /**
1203 * Clear all the code outputs on the widget.
1204 *
1205 * @param notebook - The target notebook widget.
1206 *
1207 * #### Notes
1208 * The widget `mode` will be preserved.
1209 */
1210 function clearAllOutputs(notebook) {
1211 if (!notebook.model || !notebook.activeCell) {
1212 return;
1213 }
1214 const state = Private.getState(notebook);
1215 let index = -1;
1216 for (const cell of notebook.model.cells) {
1217 const child = notebook.widgets[++index];
1218 if (cell.type === 'code') {
1219 cell.sharedModel.transact(() => {
1220 cell.clearExecution();
1221 child.outputHidden = false;
1222 }, false);
1223 Private.outputCleared.emit({ notebook, cell: child });
1224 }
1225 }
1226 void Private.handleState(notebook, state, true);
1227 }
1228 NotebookActions.clearAllOutputs = clearAllOutputs;
1229 /**
1230 * Hide the code on selected code cells.
1231 *
1232 * @param notebook - The target notebook widget.
1233 */
1234 function hideCode(notebook) {
1235 if (!notebook.model || !notebook.activeCell) {
1236 return;
1237 }
1238 const state = Private.getState(notebook);
1239 notebook.widgets.forEach(cell => {
1240 if (notebook.isSelectedOrActive(cell) && cell.model.type === 'code') {
1241 cell.inputHidden = true;
1242 }
1243 });
1244 void Private.handleState(notebook, state);
1245 }
1246 NotebookActions.hideCode = hideCode;
1247 /**
1248 * Show the code on selected code cells.
1249 *
1250 * @param notebook - The target notebook widget.
1251 */
1252 function showCode(notebook) {
1253 if (!notebook.model || !notebook.activeCell) {
1254 return;
1255 }
1256 const state = Private.getState(notebook);
1257 notebook.widgets.forEach(cell => {
1258 if (notebook.isSelectedOrActive(cell) && cell.model.type === 'code') {
1259 cell.inputHidden = false;
1260 }
1261 });
1262 void Private.handleState(notebook, state);
1263 }
1264 NotebookActions.showCode = showCode;
1265 /**
1266 * Hide the code on all code cells.
1267 *
1268 * @param notebook - The target notebook widget.
1269 */
1270 function hideAllCode(notebook) {
1271 if (!notebook.model || !notebook.activeCell) {
1272 return;
1273 }
1274 const state = Private.getState(notebook);
1275 notebook.widgets.forEach(cell => {
1276 if (cell.model.type === 'code') {
1277 cell.inputHidden = true;
1278 }
1279 });
1280 void Private.handleState(notebook, state);
1281 }
1282 NotebookActions.hideAllCode = hideAllCode;
1283 /**
1284 * Show the code on all code cells.
1285 *
1286 * @param widget - The target notebook widget.
1287 */
1288 function showAllCode(notebook) {
1289 if (!notebook.model || !notebook.activeCell) {
1290 return;
1291 }
1292 const state = Private.getState(notebook);
1293 notebook.widgets.forEach(cell => {
1294 if (cell.model.type === 'code') {
1295 cell.inputHidden = false;
1296 }
1297 });
1298 void Private.handleState(notebook, state);
1299 }
1300 NotebookActions.showAllCode = showAllCode;
1301 /**
1302 * Hide the output on selected code cells.
1303 *
1304 * @param notebook - The target notebook widget.
1305 */
1306 function hideOutput(notebook) {
1307 if (!notebook.model || !notebook.activeCell) {
1308 return;
1309 }
1310 const state = Private.getState(notebook);
1311 notebook.widgets.forEach(cell => {
1312 if (notebook.isSelectedOrActive(cell) && cell.model.type === 'code') {
1313 cell.outputHidden = true;
1314 }
1315 });
1316 void Private.handleState(notebook, state, true);
1317 }
1318 NotebookActions.hideOutput = hideOutput;
1319 /**
1320 * Show the output on selected code cells.
1321 *
1322 * @param notebook - The target notebook widget.
1323 */
1324 function showOutput(notebook) {
1325 if (!notebook.model || !notebook.activeCell) {
1326 return;
1327 }
1328 const state = Private.getState(notebook);
1329 notebook.widgets.forEach(cell => {
1330 if (notebook.isSelectedOrActive(cell) && cell.model.type === 'code') {
1331 cell.outputHidden = false;
1332 }
1333 });
1334 void Private.handleState(notebook, state);
1335 }
1336 NotebookActions.showOutput = showOutput;
1337 /**
1338 * Hide the output on all code cells.
1339 *
1340 * @param notebook - The target notebook widget.
1341 */
1342 function hideAllOutputs(notebook) {
1343 if (!notebook.model || !notebook.activeCell) {
1344 return;
1345 }
1346 const state = Private.getState(notebook);
1347 notebook.widgets.forEach(cell => {
1348 if (cell.model.type === 'code') {
1349 cell.outputHidden = true;
1350 }
1351 });
1352 void Private.handleState(notebook, state, true);
1353 }
1354 NotebookActions.hideAllOutputs = hideAllOutputs;
1355 /**
1356 * Render side-by-side.
1357 *
1358 * @param notebook - The target notebook widget.
1359 */
1360 function renderSideBySide(notebook) {
1361 notebook.renderingLayout = 'side-by-side';
1362 }
1363 NotebookActions.renderSideBySide = renderSideBySide;
1364 /**
1365 * Render not side-by-side.
1366 *
1367 * @param notebook - The target notebook widget.
1368 */
1369 function renderDefault(notebook) {
1370 notebook.renderingLayout = 'default';
1371 }
1372 NotebookActions.renderDefault = renderDefault;
1373 /**
1374 * Show the output on all code cells.
1375 *
1376 * @param notebook - The target notebook widget.
1377 */
1378 function showAllOutputs(notebook) {
1379 if (!notebook.model || !notebook.activeCell) {
1380 return;
1381 }
1382 const state = Private.getState(notebook);
1383 notebook.widgets.forEach(cell => {
1384 if (cell.model.type === 'code') {
1385 cell.outputHidden = false;
1386 }
1387 });
1388 void Private.handleState(notebook, state);
1389 }
1390 NotebookActions.showAllOutputs = showAllOutputs;
1391 /**
1392 * Enable output scrolling for all selected cells.
1393 *
1394 * @param notebook - The target notebook widget.
1395 */
1396 function enableOutputScrolling(notebook) {
1397 if (!notebook.model || !notebook.activeCell) {
1398 return;
1399 }
1400 const state = Private.getState(notebook);
1401 notebook.widgets.forEach(cell => {
1402 if (notebook.isSelectedOrActive(cell) && cell.model.type === 'code') {
1403 cell.outputsScrolled = true;
1404 }
1405 });
1406 void Private.handleState(notebook, state, true);
1407 }
1408 NotebookActions.enableOutputScrolling = enableOutputScrolling;
1409 /**
1410 * Disable output scrolling for all selected cells.
1411 *
1412 * @param notebook - The target notebook widget.
1413 */
1414 function disableOutputScrolling(notebook) {
1415 if (!notebook.model || !notebook.activeCell) {
1416 return;
1417 }
1418 const state = Private.getState(notebook);
1419 notebook.widgets.forEach(cell => {
1420 if (notebook.isSelectedOrActive(cell) && cell.model.type === 'code') {
1421 cell.outputsScrolled = false;
1422 }
1423 });
1424 void Private.handleState(notebook, state);
1425 }
1426 NotebookActions.disableOutputScrolling = disableOutputScrolling;
1427 /**
1428 * Go to the last cell that is run or current if it is running.
1429 *
1430 * Note: This requires execution timing to be toggled on or this will have
1431 * no effect.
1432 *
1433 * @param notebook - The target notebook widget.
1434 */
1435 function selectLastRunCell(notebook) {
1436 let latestTime = null;
1437 let latestCellIdx = null;
1438 notebook.widgets.forEach((cell, cellIndx) => {
1439 if (cell.model.type === 'code') {
1440 const execution = cell.model.getMetadata('execution');
1441 if (execution &&
1442 JSONExt.isObject(execution) &&
1443 execution['iopub.status.busy'] !== undefined) {
1444 // The busy status is used as soon as a request is received:
1445 // https://jupyter-client.readthedocs.io/en/stable/messaging.html
1446 const timestamp = execution['iopub.status.busy'].toString();
1447 if (timestamp) {
1448 const startTime = new Date(timestamp);
1449 if (!latestTime || startTime >= latestTime) {
1450 latestTime = startTime;
1451 latestCellIdx = cellIndx;
1452 }
1453 }
1454 }
1455 }
1456 });
1457 if (latestCellIdx !== null) {
1458 notebook.activeCellIndex = latestCellIdx;
1459 }
1460 }
1461 NotebookActions.selectLastRunCell = selectLastRunCell;
1462 /**
1463 * Set the markdown header level.
1464 *
1465 * @param notebook - The target notebook widget.
1466 *
1467 * @param level - The header level.
1468 *
1469 * #### Notes
1470 * All selected cells will be switched to markdown.
1471 * The level will be clamped between 1 and 6.
1472 * If there is an existing header, it will be replaced.
1473 * There will always be one blank space after the header.
1474 * The cells will be unrendered.
1475 */
1476 function setMarkdownHeader(notebook, level) {
1477 if (!notebook.model || !notebook.activeCell) {
1478 return;
1479 }
1480 const state = Private.getState(notebook);
1481 const cells = notebook.model.cells;
1482 level = Math.min(Math.max(level, 1), 6);
1483 notebook.widgets.forEach((child, index) => {
1484 if (notebook.isSelectedOrActive(child)) {
1485 Private.setMarkdownHeader(cells.get(index), level);
1486 }
1487 });
1488 Private.changeCellType(notebook, 'markdown');
1489 void Private.handleState(notebook, state);
1490 }
1491 NotebookActions.setMarkdownHeader = setMarkdownHeader;
1492 /**
1493 * Collapse all cells in given notebook.
1494 *
1495 * @param notebook - The target notebook widget.
1496 */
1497 function collapseAllHeadings(notebook) {
1498 const state = Private.getState(notebook);
1499 for (const cell of notebook.widgets) {
1500 if (NotebookActions.getHeadingInfo(cell).isHeading) {
1501 NotebookActions.setHeadingCollapse(cell, true, notebook);
1502 NotebookActions.setCellCollapse(cell, true);
1503 }
1504 }
1505 notebook.activeCellIndex = 0;
1506 void Private.handleState(notebook, state, true);
1507 }
1508 NotebookActions.collapseAllHeadings = collapseAllHeadings;
1509 /**
1510 * Un-collapse all cells in given notebook.
1511 *
1512 * @param notebook - The target notebook widget.
1513 */
1514 function expandAllHeadings(notebook) {
1515 for (const cell of notebook.widgets) {
1516 if (NotebookActions.getHeadingInfo(cell).isHeading) {
1517 NotebookActions.setHeadingCollapse(cell, false, notebook);
1518 // similar to collapseAll.
1519 NotebookActions.setCellCollapse(cell, false);
1520 }
1521 }
1522 }
1523 NotebookActions.expandAllHeadings = expandAllHeadings;
1524 function findNearestParentHeader(cell, notebook) {
1525 const index = findIndex(notebook.widgets, (possibleCell, index) => {
1526 return cell.model.id === possibleCell.model.id;
1527 });
1528 if (index === -1) {
1529 return;
1530 }
1531 // Finds the nearest header above the given cell. If the cell is a header itself, it does not return itself;
1532 // this can be checked directly by calling functions.
1533 if (index >= notebook.widgets.length) {
1534 return;
1535 }
1536 let childHeaderInfo = getHeadingInfo(notebook.widgets[index]);
1537 for (let cellN = index - 1; cellN >= 0; cellN--) {
1538 if (cellN < notebook.widgets.length) {
1539 let hInfo = getHeadingInfo(notebook.widgets[cellN]);
1540 if (hInfo.isHeading &&
1541 hInfo.headingLevel < childHeaderInfo.headingLevel) {
1542 return notebook.widgets[cellN];
1543 }
1544 }
1545 }
1546 // else no parent header found.
1547 return;
1548 }
1549 /**
1550 * Finds the "parent" heading of the given cell and expands.
1551 * Used for the case that a cell becomes active that is within a collapsed heading.
1552 * @param cell - "Child" cell that has become the active cell
1553 * @param notebook - The target notebook widget.
1554 */
1555 function expandParent(cell, notebook) {
1556 let nearestParentCell = findNearestParentHeader(cell, notebook);
1557 if (!nearestParentCell) {
1558 return;
1559 }
1560 if (!getHeadingInfo(nearestParentCell).collapsed &&
1561 !nearestParentCell.isHidden) {
1562 return;
1563 }
1564 if (nearestParentCell.isHidden) {
1565 expandParent(nearestParentCell, notebook);
1566 }
1567 if (getHeadingInfo(nearestParentCell).collapsed) {
1568 setHeadingCollapse(nearestParentCell, false, notebook);
1569 }
1570 }
1571 NotebookActions.expandParent = expandParent;
1572 /**
1573 * Finds the next heading that isn't a child of the given markdown heading.
1574 * @param cell - "Child" cell that has become the active cell
1575 * @param notebook - The target notebook widget.
1576 */
1577 function findNextParentHeading(cell, notebook) {
1578 let index = findIndex(notebook.widgets, (possibleCell, index) => {
1579 return cell.model.id === possibleCell.model.id;
1580 });
1581 if (index === -1) {
1582 return -1;
1583 }
1584 let childHeaderInfo = getHeadingInfo(cell);
1585 for (index = index + 1; index < notebook.widgets.length; index++) {
1586 let hInfo = getHeadingInfo(notebook.widgets[index]);
1587 if (hInfo.isHeading &&
1588 hInfo.headingLevel <= childHeaderInfo.headingLevel) {
1589 return index;
1590 }
1591 }
1592 // else no parent header found. return the index of the last cell
1593 return notebook.widgets.length;
1594 }
1595 NotebookActions.findNextParentHeading = findNextParentHeading;
1596 /**
1597 * Set the given cell and ** all "child" cells **
1598 * to the given collapse / expand if cell is
1599 * a markdown header.
1600 *
1601 * @param cell - The cell
1602 * @param collapsing - Whether to collapse or expand the cell
1603 * @param notebook - The target notebook widget.
1604 */
1605 function setHeadingCollapse(cell, collapsing, notebook) {
1606 const which = findIndex(notebook.widgets, (possibleCell, index) => {
1607 return cell.model.id === possibleCell.model.id;
1608 });
1609 if (which === -1) {
1610 return -1;
1611 }
1612 if (!notebook.widgets.length) {
1613 return which + 1;
1614 }
1615 let selectedHeadingInfo = NotebookActions.getHeadingInfo(cell);
1616 if (cell.isHidden ||
1617 !(cell instanceof MarkdownCell) ||
1618 !selectedHeadingInfo.isHeading) {
1619 // otherwise collapsing and uncollapsing already hidden stuff can
1620 // cause some funny looking bugs.
1621 return which + 1;
1622 }
1623 let localCollapsed = false;
1624 let localCollapsedLevel = 0;
1625 // iterate through all cells after the active cell.
1626 let cellNum;
1627 for (cellNum = which + 1; cellNum < notebook.widgets.length; cellNum++) {
1628 let subCell = notebook.widgets[cellNum];
1629 let subCellHeadingInfo = NotebookActions.getHeadingInfo(subCell);
1630 if (subCellHeadingInfo.isHeading &&
1631 subCellHeadingInfo.headingLevel <= selectedHeadingInfo.headingLevel) {
1632 // then reached an equivalent or higher heading level than the
1633 // original the end of the collapse.
1634 cellNum -= 1;
1635 break;
1636 }
1637 if (localCollapsed &&
1638 subCellHeadingInfo.isHeading &&
1639 subCellHeadingInfo.headingLevel <= localCollapsedLevel) {
1640 // then reached the end of the local collapsed, so unset NotebookActions.
1641 localCollapsed = false;
1642 }
1643 if (collapsing || localCollapsed) {
1644 // then no extra handling is needed for further locally collapsed
1645 // headings.
1646 subCell.setHidden(true);
1647 continue;
1648 }
1649 if (subCellHeadingInfo.collapsed && subCellHeadingInfo.isHeading) {
1650 localCollapsed = true;
1651 localCollapsedLevel = subCellHeadingInfo.headingLevel;
1652 // but don't collapse the locally collapsed heading, so continue to
1653 // expand the heading. This will get noticed in the next round.
1654 }
1655 subCell.setHidden(false);
1656 }
1657 if (cellNum === notebook.widgets.length) {
1658 cell.numberChildNodes = cellNum - which - 1;
1659 }
1660 else {
1661 cell.numberChildNodes = cellNum - which;
1662 }
1663 NotebookActions.setCellCollapse(cell, collapsing);
1664 return cellNum + 1;
1665 }
1666 NotebookActions.setHeadingCollapse = setHeadingCollapse;
1667 /**
1668 * Toggles the collapse state of the active cell of the given notebook
1669 * and ** all of its "child" cells ** if the cell is a heading.
1670 *
1671 * @param notebook - The target notebook widget.
1672 */
1673 function toggleCurrentHeadingCollapse(notebook) {
1674 if (!notebook.activeCell || notebook.activeCellIndex === undefined) {
1675 return;
1676 }
1677 let headingInfo = NotebookActions.getHeadingInfo(notebook.activeCell);
1678 if (headingInfo.isHeading) {
1679 // Then toggle!
1680 NotebookActions.setHeadingCollapse(notebook.activeCell, !headingInfo.collapsed, notebook);
1681 }
1682 notebook.scrollToItem(notebook.activeCellIndex).catch(reason => {
1683 // no-op
1684 });
1685 }
1686 NotebookActions.toggleCurrentHeadingCollapse = toggleCurrentHeadingCollapse;
1687 /**
1688 * If cell is a markdown heading, sets the headingCollapsed field,
1689 * and otherwise hides the cell.
1690 *
1691 * @param cell - The cell to collapse / expand
1692 * @param collapsing - Whether to collapse or expand the given cell
1693 */
1694 function setCellCollapse(cell, collapsing) {
1695 if (cell instanceof MarkdownCell) {
1696 cell.headingCollapsed = collapsing;
1697 }
1698 else {
1699 cell.setHidden(collapsing);
1700 }
1701 }
1702 NotebookActions.setCellCollapse = setCellCollapse;
1703 /**
1704 * If given cell is a markdown heading, returns the heading level.
1705 * If given cell is not markdown, returns 7 (there are only 6 levels of markdown headings)
1706 *
1707 * @param cell - The target cell widget.
1708 */
1709 function getHeadingInfo(cell) {
1710 if (!(cell instanceof MarkdownCell)) {
1711 return { isHeading: false, headingLevel: 7 };
1712 }
1713 let level = cell.headingInfo.level;
1714 let collapsed = cell.headingCollapsed;
1715 return { isHeading: level > 0, headingLevel: level, collapsed: collapsed };
1716 }
1717 NotebookActions.getHeadingInfo = getHeadingInfo;
1718 /**
1719 * Trust the notebook after prompting the user.
1720 *
1721 * @param notebook - The target notebook widget.
1722 *
1723 * @returns a promise that resolves when the transaction is finished.
1724 *
1725 * #### Notes
1726 * No dialog will be presented if the notebook is already trusted.
1727 */
1728 function trust(notebook, translator) {
1729 translator = translator || nullTranslator;
1730 const trans = translator.load('jupyterlab');
1731 if (!notebook.model) {
1732 return Promise.resolve();
1733 }
1734 // Do nothing if already trusted.
1735 const trusted = every(notebook.model.cells, cell => cell.trusted);
1736 // FIXME
1737 const trustMessage = (React.createElement("p", null,
1738 trans.__('A trusted Jupyter notebook may execute hidden malicious code when you open it.'),
1739 React.createElement("br", null),
1740 trans.__('Selecting "Trust" will re-render this notebook in a trusted state.'),
1741 React.createElement("br", null),
1742 trans.__('For more information, see'),
1743 ' ',
1744 React.createElement("a", { href: "https://jupyter-server.readthedocs.io/en/stable/operators/security.html", target: "_blank", rel: "noopener noreferrer" }, trans.__('the Jupyter security documentation')),
1745 "."));
1746 if (trusted) {
1747 return showDialog({
1748 body: trans.__('Notebook is already trusted'),
1749 buttons: [Dialog.okButton()]
1750 }).then(() => undefined);
1751 }
1752 return showDialog({
1753 body: trustMessage,
1754 title: trans.__('Trust this notebook?'),
1755 buttons: [
1756 Dialog.cancelButton(),
1757 Dialog.warnButton({
1758 label: trans.__('Trust'),
1759 ariaLabel: trans.__('Confirm Trusting this notebook')
1760 })
1761 ] // FIXME?
1762 }).then(result => {
1763 if (result.button.accept) {
1764 if (notebook.model) {
1765 for (const cell of notebook.model.cells) {
1766 cell.trusted = true;
1767 }
1768 }
1769 }
1770 });
1771 }
1772 NotebookActions.trust = trust;
1773 /**
1774 * If the notebook has an active cell, focus it.
1775 *
1776 * @param notebook The target notebook widget
1777 * @param options Optional options to change the behavior of this function
1778 * @param options.waitUntilReady If true, do not call focus until activeCell.ready is resolved
1779 * @param options.preventScroll If true, do not scroll the active cell into view
1780 *
1781 * @returns a promise that resolves when focus has been called on the active
1782 * cell's node.
1783 *
1784 * #### Notes
1785 * By default, waits until after the active cell has been attached unless
1786 * called with { waitUntilReady: false }
1787 */
1788 async function focusActiveCell(notebook, options = { waitUntilReady: true, preventScroll: false }) {
1789 const { activeCell } = notebook;
1790 const { waitUntilReady, preventScroll } = options;
1791 if (!activeCell) {
1792 return;
1793 }
1794 if (waitUntilReady) {
1795 await activeCell.ready;
1796 }
1797 if (notebook.isDisposed || activeCell.isDisposed) {
1798 return;
1799 }
1800 activeCell.node.focus({
1801 preventScroll
1802 });
1803 }
1804 NotebookActions.focusActiveCell = focusActiveCell;
1805 /*
1806 * Access last notebook history.
1807 *
1808 * @param notebook - The target notebook widget.
1809 */
1810 async function accessPreviousHistory(notebook) {
1811 if (!notebook.notebookConfig.accessKernelHistory) {
1812 return;
1813 }
1814 const activeCell = notebook.activeCell;
1815 if (activeCell) {
1816 if (notebook.kernelHistory) {
1817 const previousHistory = await notebook.kernelHistory.back(activeCell);
1818 notebook.kernelHistory.updateEditor(activeCell, previousHistory);
1819 }
1820 }
1821 }
1822 NotebookActions.accessPreviousHistory = accessPreviousHistory;
1823 /**
1824 * Access next notebook history.
1825 *
1826 * @param notebook - The target notebook widget.
1827 */
1828 async function accessNextHistory(notebook) {
1829 if (!notebook.notebookConfig.accessKernelHistory) {
1830 return;
1831 }
1832 const activeCell = notebook.activeCell;
1833 if (activeCell) {
1834 if (notebook.kernelHistory) {
1835 const nextHistory = await notebook.kernelHistory.forward(activeCell);
1836 notebook.kernelHistory.updateEditor(activeCell, nextHistory);
1837 }
1838 }
1839 }
1840 NotebookActions.accessNextHistory = accessNextHistory;
1841})(NotebookActions || (NotebookActions = {}));
1842/**
1843 * A namespace for private data.
1844 */
1845var Private;
1846(function (Private) {
1847 /**
1848 * A signal that emits whenever a cell completes execution.
1849 */
1850 Private.executed = new Signal({});
1851 /**
1852 * A signal that emits whenever a cell execution is scheduled.
1853 */
1854 Private.executionScheduled = new Signal({});
1855 /**
1856 * A signal that emits when one notebook's cells are all executed.
1857 */
1858 Private.selectionExecuted = new Signal({});
1859 /**
1860 * A signal that emits when one notebook's cells are all executed.
1861 */
1862 Private.outputCleared = new Signal({});
1863 /**
1864 * Get the state of a widget before running an action.
1865 */
1866 function getState(notebook) {
1867 var _a, _b;
1868 return {
1869 wasFocused: notebook.node.contains(document.activeElement),
1870 activeCellId: (_b = (_a = notebook.activeCell) === null || _a === void 0 ? void 0 : _a.model.id) !== null && _b !== void 0 ? _b : null
1871 };
1872 }
1873 Private.getState = getState;
1874 /**
1875 * Handle the state of a widget after running an action.
1876 */
1877 async function handleState(notebook, state, scrollIfNeeded = false) {
1878 const { activeCell, activeCellIndex } = notebook;
1879 if (scrollIfNeeded && activeCell) {
1880 await notebook.scrollToItem(activeCellIndex, 'auto', 0).catch(reason => {
1881 // no-op
1882 });
1883 }
1884 if (state.wasFocused || notebook.mode === 'edit') {
1885 notebook.activate();
1886 }
1887 }
1888 Private.handleState = handleState;
1889 /**
1890 * Handle the state of a widget after running a run action.
1891 */
1892 async function handleRunState(notebook, state, alignPreference) {
1893 const { activeCell, activeCellIndex } = notebook;
1894 if (activeCell) {
1895 await notebook
1896 .scrollToItem(activeCellIndex, 'smart', 0, alignPreference)
1897 .catch(reason => {
1898 // no-op
1899 });
1900 }
1901 if (state.wasFocused || notebook.mode === 'edit') {
1902 notebook.activate();
1903 }
1904 }
1905 Private.handleRunState = handleRunState;
1906 /**
1907 * Run the selected cells.
1908 *
1909 * @param notebook Notebook
1910 * @param cells Cells to run
1911 * @param sessionContext Notebook session context
1912 * @param sessionDialogs Session dialogs
1913 * @param translator Application translator
1914 */
1915 function runCells(notebook, cells, sessionContext, sessionDialogs, translator) {
1916 const lastCell = cells[-1];
1917 notebook.mode = 'command';
1918 let initializingDialogShown = false;
1919 return Promise.all(cells.map(cell => {
1920 if (cell.model.type === 'code' &&
1921 notebook.notebookConfig.enableKernelInitNotification &&
1922 sessionContext &&
1923 sessionContext.kernelDisplayStatus === 'initializing' &&
1924 !initializingDialogShown) {
1925 initializingDialogShown = true;
1926 translator = translator || nullTranslator;
1927 const trans = translator.load('jupyterlab');
1928 Notification.emit(trans.__(`Kernel '${sessionContext.kernelDisplayName}' for '${sessionContext.path}' is still initializing. You can run code cells when the kernel has initialized.`), 'warning', {
1929 autoClose: false
1930 });
1931 return Promise.resolve(false);
1932 }
1933 if (cell.model.type === 'code' &&
1934 notebook.notebookConfig.enableKernelInitNotification &&
1935 initializingDialogShown) {
1936 return Promise.resolve(false);
1937 }
1938 return runCell(notebook, cell, sessionContext, sessionDialogs, translator);
1939 }))
1940 .then(results => {
1941 if (notebook.isDisposed) {
1942 return false;
1943 }
1944 Private.selectionExecuted.emit({
1945 notebook,
1946 lastCell
1947 });
1948 // Post an update request.
1949 notebook.update();
1950 return results.every(result => result);
1951 })
1952 .catch(reason => {
1953 if (reason.message.startsWith('KernelReplyNotOK')) {
1954 cells.map(cell => {
1955 // Remove '*' prompt from cells that didn't execute
1956 if (cell.model.type === 'code' &&
1957 cell.model.executionCount == null) {
1958 cell.setPrompt('');
1959 }
1960 });
1961 }
1962 else {
1963 throw reason;
1964 }
1965 Private.selectionExecuted.emit({
1966 notebook,
1967 lastCell
1968 });
1969 notebook.update();
1970 return false;
1971 });
1972 }
1973 Private.runCells = runCells;
1974 /**
1975 * Run the selected cells.
1976 *
1977 * @param notebook Notebook
1978 * @param sessionContext Notebook session context
1979 * @param sessionDialogs Session dialogs
1980 * @param translator Application translator
1981 */
1982 function runSelected(notebook, sessionContext, sessionDialogs, translator) {
1983 notebook.mode = 'command';
1984 let lastIndex = notebook.activeCellIndex;
1985 const selected = notebook.widgets.filter((child, index) => {
1986 const active = notebook.isSelectedOrActive(child);
1987 if (active) {
1988 lastIndex = index;
1989 }
1990 return active;
1991 });
1992 notebook.activeCellIndex = lastIndex;
1993 notebook.deselectAll();
1994 return runCells(notebook, selected, sessionContext, sessionDialogs, translator);
1995 }
1996 Private.runSelected = runSelected;
1997 /**
1998 * Run a cell.
1999 */
2000 async function runCell(notebook, cell, sessionContext, sessionDialogs, translator) {
2001 var _a, _b, _c;
2002 translator = translator || nullTranslator;
2003 const trans = translator.load('jupyterlab');
2004 switch (cell.model.type) {
2005 case 'markdown':
2006 cell.rendered = true;
2007 cell.inputHidden = false;
2008 Private.executed.emit({ notebook, cell, success: true });
2009 break;
2010 case 'code':
2011 if (sessionContext) {
2012 if (sessionContext.isTerminating) {
2013 await showDialog({
2014 title: trans.__('Kernel Terminating'),
2015 body: trans.__('The kernel for %1 appears to be terminating. You can not run any cell for now.', (_a = sessionContext.session) === null || _a === void 0 ? void 0 : _a.path),
2016 buttons: [Dialog.okButton()]
2017 });
2018 break;
2019 }
2020 if (sessionContext.pendingInput) {
2021 await showDialog({
2022 title: trans.__('Cell not executed due to pending input'),
2023 body: trans.__('The cell has not been executed to avoid kernel deadlock as there is another pending input! Submit your pending input and try again.'),
2024 buttons: [Dialog.okButton()]
2025 });
2026 return false;
2027 }
2028 if (sessionContext.hasNoKernel) {
2029 const shouldSelect = await sessionContext.startKernel();
2030 if (shouldSelect && sessionDialogs) {
2031 await sessionDialogs.selectKernel(sessionContext);
2032 }
2033 }
2034 if (sessionContext.hasNoKernel) {
2035 cell.model.sharedModel.transact(() => {
2036 cell.model.clearExecution();
2037 });
2038 return true;
2039 }
2040 const deletedCells = (_c = (_b = notebook.model) === null || _b === void 0 ? void 0 : _b.deletedCells) !== null && _c !== void 0 ? _c : [];
2041 Private.executionScheduled.emit({ notebook, cell });
2042 let ran = false;
2043 try {
2044 const reply = await CodeCell.execute(cell, sessionContext, {
2045 deletedCells,
2046 recordTiming: notebook.notebookConfig.recordTiming
2047 });
2048 deletedCells.splice(0, deletedCells.length);
2049 ran = (() => {
2050 if (cell.isDisposed) {
2051 return false;
2052 }
2053 if (!reply) {
2054 return true;
2055 }
2056 if (reply.content.status === 'ok') {
2057 const content = reply.content;
2058 if (content.payload && content.payload.length) {
2059 handlePayload(content, notebook, cell);
2060 }
2061 return true;
2062 }
2063 else {
2064 throw new KernelError(reply.content);
2065 }
2066 })();
2067 }
2068 catch (reason) {
2069 if (cell.isDisposed || reason.message.startsWith('Canceled')) {
2070 ran = false;
2071 }
2072 else {
2073 Private.executed.emit({
2074 notebook,
2075 cell,
2076 success: false,
2077 error: reason
2078 });
2079 throw reason;
2080 }
2081 }
2082 if (ran) {
2083 Private.executed.emit({ notebook, cell, success: true });
2084 }
2085 return ran;
2086 }
2087 cell.model.sharedModel.transact(() => {
2088 cell.model.clearExecution();
2089 }, false);
2090 break;
2091 default:
2092 break;
2093 }
2094 return Promise.resolve(true);
2095 }
2096 /**
2097 * Handle payloads from an execute reply.
2098 *
2099 * #### Notes
2100 * Payloads are deprecated and there are no official interfaces for them in
2101 * the kernel type definitions.
2102 * See [Payloads (DEPRECATED)](https://jupyter-client.readthedocs.io/en/latest/messaging.html#payloads-deprecated).
2103 */
2104 function handlePayload(content, notebook, cell) {
2105 var _a;
2106 const setNextInput = (_a = content.payload) === null || _a === void 0 ? void 0 : _a.filter(i => {
2107 return i.source === 'set_next_input';
2108 })[0];
2109 if (!setNextInput) {
2110 return;
2111 }
2112 const text = setNextInput.text;
2113 const replace = setNextInput.replace;
2114 if (replace) {
2115 cell.model.sharedModel.setSource(text);
2116 return;
2117 }
2118 // Create a new code cell and add as the next cell.
2119 const notebookModel = notebook.model.sharedModel;
2120 const cells = notebook.model.cells;
2121 const index = findIndex(cells, model => model === cell.model);
2122 // While this cell has no outputs and could be trusted following the letter
2123 // of Jupyter trust model, its content comes from kernel and hence is not
2124 // necessarily controlled by the user; if we set it as trusted, a user
2125 // executing cells in succession could end up with unwanted trusted output.
2126 if (index === -1) {
2127 notebookModel.insertCell(notebookModel.cells.length, {
2128 cell_type: 'code',
2129 source: text,
2130 metadata: {
2131 trusted: false
2132 }
2133 });
2134 }
2135 else {
2136 notebookModel.insertCell(index + 1, {
2137 cell_type: 'code',
2138 source: text,
2139 metadata: {
2140 trusted: false
2141 }
2142 });
2143 }
2144 }
2145 /**
2146 * Get the selected cell(s) without affecting the clipboard.
2147 *
2148 * @param notebook - The target notebook widget.
2149 *
2150 * @returns A list of 0 or more selected cells
2151 */
2152 function selectedCells(notebook) {
2153 return notebook.widgets
2154 .filter(cell => notebook.isSelectedOrActive(cell))
2155 .map(cell => cell.model.toJSON())
2156 .map(cellJSON => {
2157 if (cellJSON.metadata.deletable !== undefined) {
2158 delete cellJSON.metadata.deletable;
2159 }
2160 return cellJSON;
2161 });
2162 }
2163 Private.selectedCells = selectedCells;
2164 /**
2165 * Copy or cut the selected cell data to the application clipboard.
2166 *
2167 * @param notebook - The target notebook widget.
2168 *
2169 * @param cut - True if the cells should be cut, false if they should be copied.
2170 */
2171 function copyOrCut(notebook, cut) {
2172 if (!notebook.model || !notebook.activeCell) {
2173 return;
2174 }
2175 const state = getState(notebook);
2176 const clipboard = Clipboard.getInstance();
2177 notebook.mode = 'command';
2178 clipboard.clear();
2179 const data = Private.selectedCells(notebook);
2180 clipboard.setData(JUPYTER_CELL_MIME, data);
2181 if (cut) {
2182 deleteCells(notebook);
2183 }
2184 else {
2185 notebook.deselectAll();
2186 }
2187 if (cut) {
2188 notebook.lastClipboardInteraction = 'cut';
2189 }
2190 else {
2191 notebook.lastClipboardInteraction = 'copy';
2192 }
2193 void handleState(notebook, state);
2194 }
2195 Private.copyOrCut = copyOrCut;
2196 /**
2197 * Change the selected cell type(s).
2198 *
2199 * @param notebook - The target notebook widget.
2200 *
2201 * @param value - The target cell type.
2202 *
2203 * #### Notes
2204 * It should preserve the widget mode.
2205 * This action can be undone.
2206 * The existing selection will be cleared.
2207 * Any cells converted to markdown will be unrendered.
2208 */
2209 function changeCellType(notebook, value) {
2210 const notebookSharedModel = notebook.model.sharedModel;
2211 notebook.widgets.forEach((child, index) => {
2212 if (!notebook.isSelectedOrActive(child)) {
2213 return;
2214 }
2215 if (child.model.type !== value) {
2216 const raw = child.model.toJSON();
2217 notebookSharedModel.transact(() => {
2218 notebookSharedModel.deleteCell(index);
2219 if (value === 'code') {
2220 // After change of type outputs are deleted so cell can be trusted.
2221 raw.metadata.trusted = true;
2222 }
2223 else {
2224 // Otherwise clear the metadata as trusted is only "valid" on code
2225 // cells (since other cell types cannot have outputs).
2226 raw.metadata.trusted = undefined;
2227 }
2228 const newCell = notebookSharedModel.insertCell(index, {
2229 cell_type: value,
2230 source: raw.source,
2231 metadata: raw.metadata
2232 });
2233 if (raw.attachments && ['markdown', 'raw'].includes(value)) {
2234 newCell.attachments =
2235 raw.attachments;
2236 }
2237 });
2238 }
2239 if (value === 'markdown') {
2240 // Fetch the new widget and unrender it.
2241 child = notebook.widgets[index];
2242 child.rendered = false;
2243 }
2244 });
2245 notebook.deselectAll();
2246 }
2247 Private.changeCellType = changeCellType;
2248 /**
2249 * Delete the selected cells.
2250 *
2251 * @param notebook - The target notebook widget.
2252 *
2253 * #### Notes
2254 * The cell after the last selected cell will be activated.
2255 * If the last cell is deleted, then the previous one will be activated.
2256 * It will add a code cell if all cells are deleted.
2257 * This action can be undone.
2258 */
2259 function deleteCells(notebook) {
2260 const model = notebook.model;
2261 const sharedModel = model.sharedModel;
2262 const toDelete = [];
2263 notebook.mode = 'command';
2264 // Find the cells to delete.
2265 notebook.widgets.forEach((child, index) => {
2266 var _a;
2267 const deletable = child.model.getMetadata('deletable') !== false;
2268 if (notebook.isSelectedOrActive(child) && deletable) {
2269 toDelete.push(index);
2270 (_a = notebook.model) === null || _a === void 0 ? void 0 : _a.deletedCells.push(child.model.id);
2271 }
2272 });
2273 // If cells are not deletable, we may not have anything to delete.
2274 if (toDelete.length > 0) {
2275 // Delete the cells as one undo event.
2276 sharedModel.transact(() => {
2277 // Delete cells in reverse order to maintain the correct indices.
2278 toDelete.reverse().forEach(index => {
2279 sharedModel.deleteCell(index);
2280 });
2281 // Add a new cell if the notebook is empty. This is done
2282 // within the compound operation to make the deletion of
2283 // a notebook's last cell undoable.
2284 if (sharedModel.cells.length == toDelete.length) {
2285 sharedModel.insertCell(0, {
2286 cell_type: notebook.notebookConfig.defaultCell,
2287 metadata: notebook.notebookConfig.defaultCell === 'code'
2288 ? {
2289 // This is an empty cell created in empty notebook, thus is trusted
2290 trusted: true
2291 }
2292 : {}
2293 });
2294 }
2295 });
2296 // Select the *first* interior cell not deleted or the cell
2297 // *after* the last selected cell.
2298 // Note: The activeCellIndex is clamped to the available cells,
2299 // so if the last cell is deleted the previous cell will be activated.
2300 // The *first* index is the index of the last cell in the initial
2301 // toDelete list due to the `reverse` operation above.
2302 notebook.activeCellIndex = toDelete[0] - toDelete.length + 1;
2303 }
2304 // Deselect any remaining, undeletable cells. Do this even if we don't
2305 // delete anything so that users are aware *something* happened.
2306 notebook.deselectAll();
2307 }
2308 Private.deleteCells = deleteCells;
2309 /**
2310 * Set the markdown header level of a cell.
2311 */
2312 function setMarkdownHeader(cell, level) {
2313 // Remove existing header or leading white space.
2314 let source = cell.sharedModel.getSource();
2315 const regex = /^(#+\s*)|^(\s*)/;
2316 const newHeader = Array(level + 1).join('#') + ' ';
2317 const matches = regex.exec(source);
2318 if (matches) {
2319 source = source.slice(matches[0].length);
2320 }
2321 cell.sharedModel.setSource(newHeader + source);
2322 }
2323 Private.setMarkdownHeader = setMarkdownHeader;
2324 /** Functionality related to collapsible headings */
2325 let Headings;
2326 (function (Headings) {
2327 /** Find the heading that is parent to cell.
2328 *
2329 * @param childCell - The cell that is child to the sought heading
2330 * @param notebook - The target notebook widget
2331 * @param includeChildCell [default=false] - if set to true and childCell is a heading itself, the childCell will be returned
2332 * @param returnIndex [default=false] - if set to true, the cell index is returned rather than the cell object.
2333 *
2334 * @returns the (index | Cell object) of the parent heading or (-1 | null) if there is no parent heading.
2335 */
2336 function findParentHeading(childCell, notebook, includeChildCell = false, returnIndex = false) {
2337 let cellIdx = notebook.widgets.indexOf(childCell) - (includeChildCell ? 1 : 0);
2338 while (cellIdx >= 0) {
2339 let headingInfo = NotebookActions.getHeadingInfo(notebook.widgets[cellIdx]);
2340 if (headingInfo.isHeading) {
2341 return returnIndex ? cellIdx : notebook.widgets[cellIdx];
2342 }
2343 cellIdx--;
2344 }
2345 return returnIndex ? -1 : null;
2346 }
2347 Headings.findParentHeading = findParentHeading;
2348 /** Find heading above with leq level than baseCell heading level.
2349 *
2350 * @param baseCell - cell relative to which so search
2351 * @param notebook - target notebook widget
2352 * @param returnIndex [default=false] - if set to true, the cell index is returned rather than the cell object.
2353 *
2354 * @returns the (index | Cell object) of the found heading or (-1 | null) if no heading found.
2355 */
2356 function findLowerEqualLevelParentHeadingAbove(baseCell, notebook, returnIndex = false) {
2357 let baseHeadingLevel = Private.Headings.determineHeadingLevel(baseCell, notebook);
2358 if (baseHeadingLevel == -1) {
2359 baseHeadingLevel = 1; // if no heading level can be determined, assume we're on level 1
2360 }
2361 // find the heading above with heading level <= baseHeadingLevel and return its index
2362 let cellIdx = notebook.widgets.indexOf(baseCell) - 1;
2363 while (cellIdx >= 0) {
2364 let cell = notebook.widgets[cellIdx];
2365 let headingInfo = NotebookActions.getHeadingInfo(cell);
2366 if (headingInfo.isHeading &&
2367 headingInfo.headingLevel <= baseHeadingLevel) {
2368 return returnIndex ? cellIdx : cell;
2369 }
2370 cellIdx--;
2371 }
2372 return returnIndex ? -1 : null; // no heading found
2373 }
2374 Headings.findLowerEqualLevelParentHeadingAbove = findLowerEqualLevelParentHeadingAbove;
2375 /** Find next heading with equal or lower level.
2376 *
2377 * @param baseCell - cell relative to which so search
2378 * @param notebook - target notebook widget
2379 * @param returnIndex [default=false] - if set to true, the cell index is returned rather than the cell object.
2380 *
2381 * @returns the (index | Cell object) of the found heading or (-1 | null) if no heading found.
2382 */
2383 function findLowerEqualLevelHeadingBelow(baseCell, notebook, returnIndex = false) {
2384 let baseHeadingLevel = Private.Headings.determineHeadingLevel(baseCell, notebook);
2385 if (baseHeadingLevel == -1) {
2386 baseHeadingLevel = 1; // if no heading level can be determined, assume we're on level 1
2387 }
2388 let cellIdx = notebook.widgets.indexOf(baseCell) + 1;
2389 while (cellIdx < notebook.widgets.length) {
2390 let cell = notebook.widgets[cellIdx];
2391 let headingInfo = NotebookActions.getHeadingInfo(cell);
2392 if (headingInfo.isHeading &&
2393 headingInfo.headingLevel <= baseHeadingLevel) {
2394 return returnIndex ? cellIdx : cell;
2395 }
2396 cellIdx++;
2397 }
2398 return returnIndex ? -1 : null;
2399 }
2400 Headings.findLowerEqualLevelHeadingBelow = findLowerEqualLevelHeadingBelow;
2401 /** Find next heading.
2402 *
2403 * @param baseCell - cell relative to which so search
2404 * @param notebook - target notebook widget
2405 * @param returnIndex [default=false] - if set to true, the cell index is returned rather than the cell object.
2406 *
2407 * @returns the (index | Cell object) of the found heading or (-1 | null) if no heading found.
2408 */
2409 function findHeadingBelow(baseCell, notebook, returnIndex = false) {
2410 let cellIdx = notebook.widgets.indexOf(baseCell) + 1;
2411 while (cellIdx < notebook.widgets.length) {
2412 let cell = notebook.widgets[cellIdx];
2413 let headingInfo = NotebookActions.getHeadingInfo(cell);
2414 if (headingInfo.isHeading) {
2415 return returnIndex ? cellIdx : cell;
2416 }
2417 cellIdx++;
2418 }
2419 return returnIndex ? -1 : null;
2420 }
2421 Headings.findHeadingBelow = findHeadingBelow;
2422 /** Determine the heading level of a cell.
2423 *
2424 * @param baseCell - The cell of which the heading level shall be determined
2425 * @param notebook - The target notebook widget
2426 *
2427 * @returns the heading level or -1 if there is no parent heading
2428 *
2429 * #### Notes
2430 * If the baseCell is a heading itself, the heading level of baseCell is returned.
2431 * If the baseCell is not a heading itself, the level of the parent heading is returned.
2432 * If there is no parent heading, -1 is returned.
2433 */
2434 function determineHeadingLevel(baseCell, notebook) {
2435 let headingInfoBaseCell = NotebookActions.getHeadingInfo(baseCell);
2436 // fill baseHeadingLevel or return null if there is no heading at or above baseCell
2437 if (headingInfoBaseCell.isHeading) {
2438 return headingInfoBaseCell.headingLevel;
2439 }
2440 else {
2441 let parentHeading = findParentHeading(baseCell, notebook, true);
2442 if (parentHeading == null) {
2443 return -1;
2444 }
2445 return NotebookActions.getHeadingInfo(parentHeading).headingLevel;
2446 }
2447 }
2448 Headings.determineHeadingLevel = determineHeadingLevel;
2449 /** Insert a new heading cell at given position.
2450 *
2451 * @param cellIndex - where to insert
2452 * @param headingLevel - level of the new heading
2453 * @param notebook - target notebook
2454 *
2455 * #### Notes
2456 * Enters edit mode after insert.
2457 */
2458 async function insertHeadingAboveCellIndex(cellIndex, headingLevel, notebook) {
2459 var _a;
2460 headingLevel = Math.min(Math.max(headingLevel, 1), 6);
2461 const state = Private.getState(notebook);
2462 const model = notebook.model;
2463 const sharedModel = model.sharedModel;
2464 sharedModel.insertCell(cellIndex, {
2465 cell_type: 'markdown',
2466 source: '#'.repeat(headingLevel) + ' '
2467 });
2468 notebook.activeCellIndex = cellIndex;
2469 if (((_a = notebook.activeCell) === null || _a === void 0 ? void 0 : _a.inViewport) === false) {
2470 await signalToPromise(notebook.activeCell.inViewportChanged, 200).catch(() => {
2471 // no-op
2472 });
2473 }
2474 notebook.deselectAll();
2475 void Private.handleState(notebook, state, true);
2476 notebook.mode = 'edit';
2477 notebook.widgets[cellIndex].setHidden(false);
2478 }
2479 Headings.insertHeadingAboveCellIndex = insertHeadingAboveCellIndex;
2480 })(Headings = Private.Headings || (Private.Headings = {}));
2481})(Private || (Private = {}));
2482//# sourceMappingURL=actions.js.map
\No newline at end of file