Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts
5240 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { Iterable } from '../../../../../base/common/iterator.js';
7
import { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js';
8
import { isEqual } from '../../../../../base/common/resources.js';
9
import { ThemeIcon } from '../../../../../base/common/themables.js';
10
import { URI, UriComponents } from '../../../../../base/common/uri.js';
11
import { ICodeEditor } from '../../../../../editor/browser/editorBrowser.js';
12
import { ILanguageService } from '../../../../../editor/common/languages/language.js';
13
import { localize, localize2 } from '../../../../../nls.js';
14
import { MenuId, MenuRegistry, registerAction2 } from '../../../../../platform/actions/common/actions.js';
15
import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';
16
import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js';
17
import { ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js';
18
import { IDebugService } from '../../../debug/common/debug.js';
19
import { CTX_INLINE_CHAT_FOCUSED } from '../../../inlineChat/common/inlineChat.js';
20
import { insertCell } from './cellOperations.js';
21
import { CELL_TITLE_CELL_GROUP_ID, CellToolbarOrder, INotebookActionContext, INotebookCellActionContext, INotebookCellToolbarActionContext, INotebookCommandContext, NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT, NotebookAction, NotebookCellAction, NotebookMultiCellAction, cellExecutionArgs, getContextFromActiveEditor, getContextFromUri, parseMultiCellExecutionArgs } from './coreActions.js';
22
import { CellEditState, CellFocusMode, EXECUTE_CELL_COMMAND_ID, IActiveNotebookEditor, ICellViewModel, IFocusNotebookCellOptions, ScrollToRevealBehavior } from '../notebookBrowser.js';
23
import * as icons from '../notebookIcons.js';
24
import { CellKind, CellUri, NotebookSetting } from '../../common/notebookCommon.js';
25
import { NOTEBOOK_CELL_EXECUTING, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_HAS_RUNNING_CELL, NOTEBOOK_HAS_SOMETHING_RUNNING, NOTEBOOK_INTERRUPTIBLE_KERNEL, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_KERNEL_COUNT, NOTEBOOK_KERNEL_SOURCE_COUNT, NOTEBOOK_LAST_CELL_FAILED, NOTEBOOK_MISSING_KERNEL_EXTENSION } from '../../common/notebookContextKeys.js';
26
import { NotebookEditorInput } from '../../common/notebookEditorInput.js';
27
import { INotebookExecutionStateService } from '../../common/notebookExecutionStateService.js';
28
import { IEditorGroupsService } from '../../../../services/editor/common/editorGroupsService.js';
29
import { IEditorService } from '../../../../services/editor/common/editorService.js';
30
import { CodeCellViewModel } from '../viewModel/codeCellViewModel.js';
31
32
const EXECUTE_NOTEBOOK_COMMAND_ID = 'notebook.execute';
33
const CANCEL_NOTEBOOK_COMMAND_ID = 'notebook.cancelExecution';
34
const INTERRUPT_NOTEBOOK_COMMAND_ID = 'notebook.interruptExecution';
35
const CANCEL_CELL_COMMAND_ID = 'notebook.cell.cancelExecution';
36
const EXECUTE_CELL_FOCUS_CONTAINER_COMMAND_ID = 'notebook.cell.executeAndFocusContainer';
37
const EXECUTE_CELL_SELECT_BELOW = 'notebook.cell.executeAndSelectBelow';
38
const EXECUTE_CELL_INSERT_BELOW = 'notebook.cell.executeAndInsertBelow';
39
const EXECUTE_CELL_AND_BELOW = 'notebook.cell.executeCellAndBelow';
40
const EXECUTE_CELLS_ABOVE = 'notebook.cell.executeCellsAbove';
41
const RENDER_ALL_MARKDOWN_CELLS = 'notebook.renderAllMarkdownCells';
42
const REVEAL_RUNNING_CELL = 'notebook.revealRunningCell';
43
const REVEAL_LAST_FAILED_CELL = 'notebook.revealLastFailedCell';
44
45
// If this changes, update getCodeCellExecutionContextKeyService to match
46
export const executeCondition = ContextKeyExpr.and(
47
NOTEBOOK_CELL_TYPE.isEqualTo('code'),
48
ContextKeyExpr.or(
49
ContextKeyExpr.greater(NOTEBOOK_KERNEL_COUNT.key, 0),
50
ContextKeyExpr.greater(NOTEBOOK_KERNEL_SOURCE_COUNT.key, 0),
51
NOTEBOOK_MISSING_KERNEL_EXTENSION
52
));
53
54
export const executeThisCellCondition = ContextKeyExpr.and(
55
executeCondition,
56
NOTEBOOK_CELL_EXECUTING.toNegated());
57
58
export const executeSectionCondition = ContextKeyExpr.and(
59
NOTEBOOK_CELL_TYPE.isEqualTo('markup'),
60
);
61
62
function renderAllMarkdownCells(context: INotebookActionContext): void {
63
for (let i = 0; i < context.notebookEditor.getLength(); i++) {
64
const cell = context.notebookEditor.cellAt(i);
65
66
if (cell.cellKind === CellKind.Markup) {
67
cell.updateEditState(CellEditState.Preview, 'renderAllMarkdownCells');
68
}
69
}
70
}
71
72
async function runCell(editorGroupsService: IEditorGroupsService, context: INotebookActionContext, editorService?: IEditorService): Promise<void> {
73
const group = editorGroupsService.activeGroup;
74
75
if (group) {
76
if (group.activeEditor) {
77
group.pinEditor(group.activeEditor);
78
}
79
}
80
81
// If auto-reveal is enabled, ensure the notebook editor is visible before revealing cells
82
if (context.autoReveal && (context.cell || context.selectedCells?.length) && editorService) {
83
editorService.openEditor({ resource: context.notebookEditor.textModel.uri, options: { revealIfOpened: true } });
84
}
85
86
if (context.ui && context.cell) {
87
if (context.autoReveal) {
88
handleAutoReveal(context.cell, context.notebookEditor);
89
}
90
await context.notebookEditor.executeNotebookCells(Iterable.single(context.cell));
91
} else if (context.selectedCells?.length || context.cell) {
92
const selectedCells = context.selectedCells?.length ? context.selectedCells : [context.cell!];
93
const firstCell = selectedCells[0];
94
95
if (firstCell && context.autoReveal) {
96
handleAutoReveal(firstCell, context.notebookEditor);
97
}
98
await context.notebookEditor.executeNotebookCells(selectedCells);
99
}
100
101
let foundEditor: ICodeEditor | undefined = undefined;
102
for (const [, codeEditor] of context.notebookEditor.codeEditors) {
103
if (isEqual(codeEditor.getModel()?.uri, (context.cell ?? context.selectedCells?.[0])?.uri)) {
104
foundEditor = codeEditor;
105
break;
106
}
107
}
108
109
if (!foundEditor) {
110
return;
111
}
112
}
113
114
const SMART_VIEWPORT_TOP_REVEAL_PADDING = 20; // enough to not cut off top of cell toolbar
115
const SMART_VIEWPORT_BOTTOM_REVEAL_PADDING = 60; // enough to show full bottom of output element + tiny buffer below that vertical bar
116
function handleAutoReveal(cell: ICellViewModel, notebookEditor: IActiveNotebookEditor): void {
117
// always focus the container, blue bar is a good visual aid in tracking what's happening
118
notebookEditor.focusNotebookCell(cell, 'container', { skipReveal: true });
119
120
// Handle markup cells with simple reveal
121
if (cell.cellKind === CellKind.Markup) {
122
const cellIndex = notebookEditor.getCellIndex(cell);
123
notebookEditor.revealCellRangeInView({ start: cellIndex, end: cellIndex + 1 });
124
return;
125
}
126
127
// Ensure we're working with a code cell - we need the CodeCellViewModel type for accessing layout properties like outputTotalHeight
128
if (!(cell instanceof CodeCellViewModel)) {
129
return;
130
}
131
132
// Get all dimensions
133
const cellEditorScrollTop = notebookEditor.getAbsoluteTopOfElement(cell);
134
const cellEditorScrollBottom = cellEditorScrollTop + cell.layoutInfo.outputContainerOffset;
135
136
const cellOutputHeight = cell.layoutInfo.outputTotalHeight;
137
const cellOutputScrollBottom = notebookEditor.getAbsoluteBottomOfElement(cell);
138
139
const viewportHeight = notebookEditor.getLayoutInfo().height;
140
const viewportHeight34 = viewportHeight * 0.34;
141
const viewportHeight66 = viewportHeight * 0.66;
142
143
const totalHeight = cell.layoutInfo.totalHeight;
144
145
const isFullyVisible = cellEditorScrollTop >= notebookEditor.scrollTop && cellOutputScrollBottom <= notebookEditor.scrollBottom;
146
const isEditorBottomVisible = ((cellEditorScrollBottom - 25 /* padding for the cell status bar */) >= notebookEditor.scrollTop) &&
147
((cellEditorScrollBottom + 25 /* padding to see a sliver of the beginning of outputs */) <= notebookEditor.scrollBottom);
148
149
// Common scrolling functions
150
const revealWithTopPadding = (position: number) => { notebookEditor.setScrollTop(position - SMART_VIEWPORT_TOP_REVEAL_PADDING); };
151
const revealWithNoPadding = (position: number) => { notebookEditor.setScrollTop(position); };
152
const revealWithBottomPadding = (position: number) => { notebookEditor.setScrollTop(position + SMART_VIEWPORT_BOTTOM_REVEAL_PADDING); };
153
154
// CASE 0: Total is already visible
155
if (isFullyVisible) {
156
return;
157
}
158
159
// CASE 1: Total fits within viewport
160
if (totalHeight <= viewportHeight && !isEditorBottomVisible) {
161
revealWithTopPadding(cellEditorScrollTop);
162
return;
163
}
164
165
// CASE 2: Total doesn't fit in the viewport
166
if (totalHeight > viewportHeight && !isEditorBottomVisible) {
167
if (cellOutputHeight > 0 && cellOutputHeight >= viewportHeight66) {
168
// has large outputs -- Show 34% editor, 66% output
169
revealWithNoPadding(cellEditorScrollBottom - viewportHeight34);
170
} else if (cellOutputHeight > 0) {
171
// has small outputs -- Show output at viewport bottom
172
revealWithBottomPadding(cellOutputScrollBottom - viewportHeight);
173
} else {
174
// No outputs, just big cell -- put editor bottom @ 2/3 of viewport height
175
revealWithNoPadding(cellEditorScrollBottom - viewportHeight66);
176
}
177
}
178
}
179
180
registerAction2(class RenderAllMarkdownCellsAction extends NotebookAction {
181
constructor() {
182
super({
183
id: RENDER_ALL_MARKDOWN_CELLS,
184
title: localize('notebookActions.renderMarkdown', "Render All Markdown Cells"),
185
});
186
}
187
188
async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise<void> {
189
renderAllMarkdownCells(context);
190
}
191
});
192
193
registerAction2(class ExecuteNotebookAction extends NotebookAction {
194
constructor() {
195
super({
196
id: EXECUTE_NOTEBOOK_COMMAND_ID,
197
title: localize('notebookActions.executeNotebook', "Run All"),
198
icon: icons.executeAllIcon,
199
metadata: {
200
description: localize('notebookActions.executeNotebook', "Run All"),
201
args: [
202
{
203
name: 'uri',
204
description: 'The document uri'
205
}
206
]
207
},
208
menu: [
209
{
210
id: MenuId.EditorTitle,
211
order: -1,
212
group: 'navigation',
213
when: ContextKeyExpr.and(
214
NOTEBOOK_IS_ACTIVE_EDITOR,
215
ContextKeyExpr.or(NOTEBOOK_INTERRUPTIBLE_KERNEL.toNegated(), NOTEBOOK_HAS_SOMETHING_RUNNING.toNegated()),
216
ContextKeyExpr.notEquals('config.notebook.globalToolbar', true)
217
)
218
},
219
{
220
id: MenuId.NotebookToolbar,
221
order: -1,
222
group: 'navigation/execute',
223
when: ContextKeyExpr.and(
224
ContextKeyExpr.or(
225
NOTEBOOK_INTERRUPTIBLE_KERNEL.toNegated(),
226
NOTEBOOK_HAS_SOMETHING_RUNNING.toNegated(),
227
),
228
ContextKeyExpr.and(NOTEBOOK_HAS_SOMETHING_RUNNING, NOTEBOOK_INTERRUPTIBLE_KERNEL.toNegated())?.negate(),
229
ContextKeyExpr.equals('config.notebook.globalToolbar', true)
230
)
231
}
232
]
233
});
234
}
235
236
override getEditorContextFromArgsOrActive(accessor: ServicesAccessor, context?: UriComponents): INotebookActionContext | undefined {
237
return getContextFromUri(accessor, context) ?? getContextFromActiveEditor(accessor.get(IEditorService));
238
}
239
240
async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise<void> {
241
renderAllMarkdownCells(context);
242
243
const editorService = accessor.get(IEditorService);
244
const editor = editorService.findEditors({
245
resource: context.notebookEditor.textModel.uri,
246
typeId: NotebookEditorInput.ID,
247
editorId: context.notebookEditor.textModel.viewType
248
}).at(0);
249
const editorGroupService = accessor.get(IEditorGroupsService);
250
251
if (editor) {
252
const group = editorGroupService.getGroup(editor.groupId);
253
group?.pinEditor(editor.editor);
254
}
255
256
return context.notebookEditor.executeNotebookCells();
257
}
258
});
259
260
registerAction2(class ExecuteCell extends NotebookMultiCellAction {
261
constructor() {
262
super({
263
id: EXECUTE_CELL_COMMAND_ID,
264
precondition: executeThisCellCondition,
265
title: localize('notebookActions.execute', "Execute Cell"),
266
keybinding: {
267
when: NOTEBOOK_CELL_LIST_FOCUSED,
268
primary: KeyMod.WinCtrl | KeyCode.Enter,
269
win: {
270
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Enter
271
},
272
weight: NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT
273
},
274
menu: {
275
id: MenuId.NotebookCellExecutePrimary,
276
when: executeThisCellCondition,
277
group: 'inline'
278
},
279
metadata: {
280
description: localize('notebookActions.execute', "Execute Cell"),
281
args: cellExecutionArgs
282
},
283
icon: icons.executeIcon
284
});
285
}
286
287
override parseArgs(accessor: ServicesAccessor, ...args: unknown[]): INotebookCommandContext | undefined {
288
return parseMultiCellExecutionArgs(accessor, ...args);
289
}
290
291
async runWithContext(accessor: ServicesAccessor, context: INotebookCommandContext | INotebookCellToolbarActionContext): Promise<void> {
292
const editorGroupsService = accessor.get(IEditorGroupsService);
293
const editorService = accessor.get(IEditorService);
294
295
if (context.ui) {
296
await context.notebookEditor.focusNotebookCell(context.cell, 'container', { skipReveal: true });
297
}
298
299
await runCell(editorGroupsService, context, editorService);
300
}
301
});
302
303
registerAction2(class ExecuteAboveCells extends NotebookMultiCellAction {
304
constructor() {
305
super({
306
id: EXECUTE_CELLS_ABOVE,
307
precondition: executeCondition,
308
title: localize('notebookActions.executeAbove', "Execute Above Cells"),
309
menu: [
310
{
311
id: MenuId.NotebookCellExecute,
312
when: ContextKeyExpr.and(
313
executeCondition,
314
ContextKeyExpr.equals(`config.${NotebookSetting.consolidatedRunButton}`, true))
315
},
316
{
317
id: MenuId.NotebookCellTitle,
318
order: CellToolbarOrder.ExecuteAboveCells,
319
group: CELL_TITLE_CELL_GROUP_ID,
320
when: ContextKeyExpr.and(
321
executeCondition,
322
ContextKeyExpr.equals(`config.${NotebookSetting.consolidatedRunButton}`, false))
323
}
324
],
325
icon: icons.executeAboveIcon
326
});
327
}
328
329
override parseArgs(accessor: ServicesAccessor, ...args: unknown[]): INotebookCommandContext | undefined {
330
return parseMultiCellExecutionArgs(accessor, ...args);
331
}
332
333
async runWithContext(accessor: ServicesAccessor, context: INotebookCommandContext | INotebookCellToolbarActionContext): Promise<void> {
334
let endCellIdx: number | undefined = undefined;
335
if (context.ui) {
336
endCellIdx = context.notebookEditor.getCellIndex(context.cell);
337
await context.notebookEditor.focusNotebookCell(context.cell, 'container', { skipReveal: true });
338
} else {
339
endCellIdx = Math.min(...context.selectedCells.map(cell => context.notebookEditor.getCellIndex(cell)));
340
}
341
342
if (typeof endCellIdx === 'number') {
343
const range = { start: 0, end: endCellIdx };
344
const cells = context.notebookEditor.getCellsInRange(range);
345
context.notebookEditor.executeNotebookCells(cells);
346
}
347
}
348
});
349
350
registerAction2(class ExecuteCellAndBelow extends NotebookMultiCellAction {
351
constructor() {
352
super({
353
id: EXECUTE_CELL_AND_BELOW,
354
precondition: executeCondition,
355
title: localize('notebookActions.executeBelow', "Execute Cell and Below"),
356
menu: [
357
{
358
id: MenuId.NotebookCellExecute,
359
when: ContextKeyExpr.and(
360
executeCondition,
361
ContextKeyExpr.equals(`config.${NotebookSetting.consolidatedRunButton}`, true))
362
},
363
{
364
id: MenuId.NotebookCellTitle,
365
order: CellToolbarOrder.ExecuteCellAndBelow,
366
group: CELL_TITLE_CELL_GROUP_ID,
367
when: ContextKeyExpr.and(
368
executeCondition,
369
ContextKeyExpr.equals(`config.${NotebookSetting.consolidatedRunButton}`, false))
370
}
371
],
372
icon: icons.executeBelowIcon
373
});
374
}
375
376
override parseArgs(accessor: ServicesAccessor, ...args: unknown[]): INotebookCommandContext | undefined {
377
return parseMultiCellExecutionArgs(accessor, ...args);
378
}
379
380
async runWithContext(accessor: ServicesAccessor, context: INotebookCommandContext | INotebookCellToolbarActionContext): Promise<void> {
381
let startCellIdx: number | undefined = undefined;
382
if (context.ui) {
383
startCellIdx = context.notebookEditor.getCellIndex(context.cell);
384
await context.notebookEditor.focusNotebookCell(context.cell, 'container', { skipReveal: true });
385
} else {
386
startCellIdx = Math.min(...context.selectedCells.map(cell => context.notebookEditor.getCellIndex(cell)));
387
}
388
389
if (typeof startCellIdx === 'number') {
390
const range = { start: startCellIdx, end: context.notebookEditor.getLength() };
391
const cells = context.notebookEditor.getCellsInRange(range);
392
context.notebookEditor.executeNotebookCells(cells);
393
}
394
}
395
});
396
397
registerAction2(class ExecuteCellFocusContainer extends NotebookMultiCellAction {
398
constructor() {
399
super({
400
id: EXECUTE_CELL_FOCUS_CONTAINER_COMMAND_ID,
401
precondition: executeThisCellCondition,
402
title: localize('notebookActions.executeAndFocusContainer', "Execute Cell and Focus Container"),
403
metadata: {
404
description: localize('notebookActions.executeAndFocusContainer', "Execute Cell and Focus Container"),
405
args: cellExecutionArgs
406
},
407
icon: icons.executeIcon
408
});
409
}
410
411
override parseArgs(accessor: ServicesAccessor, ...args: unknown[]): INotebookCommandContext | undefined {
412
return parseMultiCellExecutionArgs(accessor, ...args);
413
}
414
415
async runWithContext(accessor: ServicesAccessor, context: INotebookCommandContext | INotebookCellToolbarActionContext): Promise<void> {
416
const editorGroupsService = accessor.get(IEditorGroupsService);
417
const editorService = accessor.get(IEditorService);
418
419
if (context.ui) {
420
await context.notebookEditor.focusNotebookCell(context.cell, 'container', { skipReveal: true });
421
} else {
422
const firstCell = context.selectedCells[0];
423
424
if (firstCell) {
425
await context.notebookEditor.focusNotebookCell(firstCell, 'container', { skipReveal: true });
426
}
427
}
428
429
await runCell(editorGroupsService, context, editorService);
430
}
431
});
432
433
const cellCancelCondition = ContextKeyExpr.or(
434
ContextKeyExpr.equals(NOTEBOOK_CELL_EXECUTION_STATE.key, 'executing'),
435
ContextKeyExpr.equals(NOTEBOOK_CELL_EXECUTION_STATE.key, 'pending'),
436
);
437
438
registerAction2(class CancelExecuteCell extends NotebookMultiCellAction {
439
constructor() {
440
super({
441
id: CANCEL_CELL_COMMAND_ID,
442
precondition: cellCancelCondition,
443
title: localize('notebookActions.cancel', "Stop Cell Execution"),
444
icon: icons.stopIcon,
445
menu: {
446
id: MenuId.NotebookCellExecutePrimary,
447
when: cellCancelCondition,
448
group: 'inline'
449
},
450
metadata: {
451
description: localize('notebookActions.cancel', "Stop Cell Execution"),
452
args: [
453
{
454
name: 'options',
455
description: 'The cell range options',
456
schema: {
457
'type': 'object',
458
'required': ['ranges'],
459
'properties': {
460
'ranges': {
461
'type': 'array',
462
items: [
463
{
464
'type': 'object',
465
'required': ['start', 'end'],
466
'properties': {
467
'start': {
468
'type': 'number'
469
},
470
'end': {
471
'type': 'number'
472
}
473
}
474
}
475
]
476
},
477
'document': {
478
'type': 'object',
479
'description': 'The document uri',
480
}
481
}
482
}
483
}
484
]
485
},
486
});
487
}
488
489
override parseArgs(accessor: ServicesAccessor, ...args: unknown[]): INotebookCommandContext | undefined {
490
return parseMultiCellExecutionArgs(accessor, ...args);
491
}
492
493
async runWithContext(accessor: ServicesAccessor, context: INotebookCommandContext | INotebookCellToolbarActionContext): Promise<void> {
494
if (context.ui) {
495
await context.notebookEditor.focusNotebookCell(context.cell, 'container', { skipReveal: true });
496
return context.notebookEditor.cancelNotebookCells(Iterable.single(context.cell));
497
} else {
498
return context.notebookEditor.cancelNotebookCells(context.selectedCells);
499
}
500
}
501
});
502
503
registerAction2(class ExecuteCellSelectBelow extends NotebookCellAction {
504
constructor() {
505
super({
506
id: EXECUTE_CELL_SELECT_BELOW,
507
precondition: ContextKeyExpr.or(executeThisCellCondition, NOTEBOOK_CELL_TYPE.isEqualTo('markup')),
508
title: localize('notebookActions.executeAndSelectBelow', "Execute Notebook Cell and Select Below"),
509
keybinding: {
510
when: ContextKeyExpr.and(
511
NOTEBOOK_CELL_LIST_FOCUSED,
512
CTX_INLINE_CHAT_FOCUSED.negate()
513
),
514
primary: KeyMod.Shift | KeyCode.Enter,
515
weight: NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT
516
},
517
});
518
}
519
520
async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise<void> {
521
const editorGroupsService = accessor.get(IEditorGroupsService);
522
const editorService = accessor.get(IEditorService);
523
const idx = context.notebookEditor.getCellIndex(context.cell);
524
if (typeof idx !== 'number') {
525
return;
526
}
527
const languageService = accessor.get(ILanguageService);
528
529
const config = accessor.get(IConfigurationService);
530
const scrollBehavior = config.getValue(NotebookSetting.scrollToRevealCell);
531
let focusOptions: IFocusNotebookCellOptions;
532
if (scrollBehavior === 'none') {
533
focusOptions = { skipReveal: true };
534
} else {
535
focusOptions = {
536
revealBehavior: scrollBehavior === 'fullCell' ? ScrollToRevealBehavior.fullCell : ScrollToRevealBehavior.firstLine
537
};
538
}
539
540
if (context.cell.cellKind === CellKind.Markup) {
541
const nextCell = context.notebookEditor.cellAt(idx + 1);
542
context.cell.updateEditState(CellEditState.Preview, EXECUTE_CELL_SELECT_BELOW);
543
if (nextCell) {
544
await context.notebookEditor.focusNotebookCell(nextCell, 'container', focusOptions);
545
} else {
546
const newCell = insertCell(languageService, context.notebookEditor, idx, CellKind.Markup, 'below');
547
548
if (newCell) {
549
await context.notebookEditor.focusNotebookCell(newCell, 'editor', focusOptions);
550
}
551
}
552
return;
553
} else {
554
const nextCell = context.notebookEditor.cellAt(idx + 1);
555
if (nextCell) {
556
await context.notebookEditor.focusNotebookCell(nextCell, 'container', focusOptions);
557
} else {
558
const newCell = insertCell(languageService, context.notebookEditor, idx, CellKind.Code, 'below');
559
560
if (newCell) {
561
await context.notebookEditor.focusNotebookCell(newCell, 'editor', focusOptions);
562
}
563
}
564
565
return runCell(editorGroupsService, context, editorService);
566
}
567
}
568
});
569
570
registerAction2(class ExecuteCellInsertBelow extends NotebookCellAction {
571
constructor() {
572
super({
573
id: EXECUTE_CELL_INSERT_BELOW,
574
precondition: ContextKeyExpr.or(executeThisCellCondition, NOTEBOOK_CELL_TYPE.isEqualTo('markup')),
575
title: localize('notebookActions.executeAndInsertBelow', "Execute Notebook Cell and Insert Below"),
576
keybinding: {
577
when: NOTEBOOK_CELL_LIST_FOCUSED,
578
primary: KeyMod.Alt | KeyCode.Enter,
579
weight: NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT
580
},
581
});
582
}
583
584
async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise<void> {
585
const editorGroupsService = accessor.get(IEditorGroupsService);
586
const editorService = accessor.get(IEditorService);
587
const idx = context.notebookEditor.getCellIndex(context.cell);
588
const languageService = accessor.get(ILanguageService);
589
const newFocusMode = context.cell.focusMode === CellFocusMode.Editor ? 'editor' : 'container';
590
591
const newCell = insertCell(languageService, context.notebookEditor, idx, context.cell.cellKind, 'below');
592
if (newCell) {
593
await context.notebookEditor.focusNotebookCell(newCell, newFocusMode);
594
}
595
596
if (context.cell.cellKind === CellKind.Markup) {
597
context.cell.updateEditState(CellEditState.Preview, EXECUTE_CELL_INSERT_BELOW);
598
} else {
599
runCell(editorGroupsService, context, editorService);
600
}
601
}
602
});
603
604
class CancelNotebook extends NotebookAction {
605
override getEditorContextFromArgsOrActive(accessor: ServicesAccessor, context?: UriComponents): INotebookActionContext | undefined {
606
return getContextFromUri(accessor, context) ?? getContextFromActiveEditor(accessor.get(IEditorService));
607
}
608
609
async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise<void> {
610
return context.notebookEditor.cancelNotebookCells();
611
}
612
}
613
614
registerAction2(class CancelAllNotebook extends CancelNotebook {
615
constructor() {
616
super({
617
id: CANCEL_NOTEBOOK_COMMAND_ID,
618
title: localize2('notebookActions.cancelNotebook', "Stop Execution"),
619
icon: icons.stopIcon,
620
menu: [
621
{
622
id: MenuId.EditorTitle,
623
order: -1,
624
group: 'navigation',
625
when: ContextKeyExpr.and(
626
NOTEBOOK_IS_ACTIVE_EDITOR,
627
NOTEBOOK_HAS_SOMETHING_RUNNING,
628
NOTEBOOK_INTERRUPTIBLE_KERNEL.toNegated(),
629
ContextKeyExpr.notEquals('config.notebook.globalToolbar', true)
630
)
631
},
632
{
633
id: MenuId.NotebookToolbar,
634
order: -1,
635
group: 'navigation/execute',
636
when: ContextKeyExpr.and(
637
NOTEBOOK_HAS_SOMETHING_RUNNING,
638
NOTEBOOK_INTERRUPTIBLE_KERNEL.toNegated(),
639
ContextKeyExpr.equals('config.notebook.globalToolbar', true)
640
)
641
}
642
]
643
});
644
}
645
});
646
647
registerAction2(class InterruptNotebook extends CancelNotebook {
648
constructor() {
649
super({
650
id: INTERRUPT_NOTEBOOK_COMMAND_ID,
651
title: localize2('notebookActions.interruptNotebook', "Interrupt"),
652
precondition: ContextKeyExpr.and(
653
NOTEBOOK_HAS_SOMETHING_RUNNING,
654
NOTEBOOK_INTERRUPTIBLE_KERNEL
655
),
656
icon: icons.stopIcon,
657
menu: [
658
{
659
id: MenuId.EditorTitle,
660
order: -1,
661
group: 'navigation',
662
when: ContextKeyExpr.and(
663
NOTEBOOK_IS_ACTIVE_EDITOR,
664
NOTEBOOK_HAS_SOMETHING_RUNNING,
665
NOTEBOOK_INTERRUPTIBLE_KERNEL,
666
ContextKeyExpr.notEquals('config.notebook.globalToolbar', true)
667
)
668
},
669
{
670
id: MenuId.NotebookToolbar,
671
order: -1,
672
group: 'navigation/execute',
673
when: ContextKeyExpr.and(
674
NOTEBOOK_HAS_SOMETHING_RUNNING,
675
NOTEBOOK_INTERRUPTIBLE_KERNEL,
676
ContextKeyExpr.equals('config.notebook.globalToolbar', true)
677
)
678
},
679
{
680
id: MenuId.InteractiveToolbar,
681
group: 'navigation/execute'
682
}
683
]
684
});
685
}
686
});
687
688
689
MenuRegistry.appendMenuItem(MenuId.NotebookToolbar, {
690
title: localize('revealRunningCellShort', "Go To"),
691
submenu: MenuId.NotebookCellExecuteGoTo,
692
group: 'navigation/execute',
693
order: 20,
694
icon: ThemeIcon.modify(icons.executingStateIcon, 'spin')
695
});
696
697
registerAction2(class RevealRunningCellAction extends NotebookAction {
698
constructor() {
699
super({
700
id: REVEAL_RUNNING_CELL,
701
title: localize('revealRunningCell', "Go to Running Cell"),
702
tooltip: localize('revealRunningCell', "Go to Running Cell"),
703
shortTitle: localize('revealRunningCell', "Go to Running Cell"),
704
precondition: NOTEBOOK_HAS_RUNNING_CELL,
705
menu: [
706
{
707
id: MenuId.EditorTitle,
708
when: ContextKeyExpr.and(
709
NOTEBOOK_IS_ACTIVE_EDITOR,
710
NOTEBOOK_HAS_RUNNING_CELL,
711
ContextKeyExpr.notEquals('config.notebook.globalToolbar', true)
712
),
713
group: 'navigation',
714
order: 0
715
},
716
{
717
id: MenuId.NotebookCellExecuteGoTo,
718
when: ContextKeyExpr.and(
719
NOTEBOOK_IS_ACTIVE_EDITOR,
720
NOTEBOOK_HAS_RUNNING_CELL,
721
ContextKeyExpr.equals('config.notebook.globalToolbar', true)
722
),
723
group: 'navigation/execute',
724
order: 20
725
},
726
{
727
id: MenuId.InteractiveToolbar,
728
when: ContextKeyExpr.and(
729
NOTEBOOK_HAS_RUNNING_CELL,
730
ContextKeyExpr.equals('activeEditor', 'workbench.editor.interactive')
731
),
732
group: 'navigation',
733
order: 10
734
}
735
],
736
icon: ThemeIcon.modify(icons.executingStateIcon, 'spin')
737
});
738
}
739
740
async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise<void> {
741
const notebookExecutionStateService = accessor.get(INotebookExecutionStateService);
742
const notebook = context.notebookEditor.textModel.uri;
743
const executingCells = notebookExecutionStateService.getCellExecutionsForNotebook(notebook);
744
if (executingCells[0]) {
745
const topStackFrameCell = this.findCellAtTopFrame(accessor, notebook);
746
const focusHandle = topStackFrameCell ?? executingCells[0].cellHandle;
747
const cell = context.notebookEditor.getCellByHandle(focusHandle);
748
if (cell) {
749
context.notebookEditor.focusNotebookCell(cell, 'container');
750
}
751
}
752
}
753
754
private findCellAtTopFrame(accessor: ServicesAccessor, notebook: URI): number | undefined {
755
const debugService = accessor.get(IDebugService);
756
for (const session of debugService.getModel().getSessions()) {
757
for (const thread of session.getAllThreads()) {
758
const sf = thread.getTopStackFrame();
759
if (sf) {
760
const parsed = CellUri.parse(sf.source.uri);
761
if (parsed && parsed.notebook.toString() === notebook.toString()) {
762
return parsed.handle;
763
}
764
}
765
}
766
}
767
768
return undefined;
769
}
770
});
771
772
registerAction2(class RevealLastFailedCellAction extends NotebookAction {
773
constructor() {
774
super({
775
id: REVEAL_LAST_FAILED_CELL,
776
title: localize('revealLastFailedCell', "Go to Most Recently Failed Cell"),
777
tooltip: localize('revealLastFailedCell', "Go to Most Recently Failed Cell"),
778
shortTitle: localize('revealLastFailedCellShort', "Go to Most Recently Failed Cell"),
779
precondition: NOTEBOOK_LAST_CELL_FAILED,
780
menu: [
781
{
782
id: MenuId.EditorTitle,
783
when: ContextKeyExpr.and(
784
NOTEBOOK_IS_ACTIVE_EDITOR,
785
NOTEBOOK_LAST_CELL_FAILED,
786
NOTEBOOK_HAS_RUNNING_CELL.toNegated(),
787
ContextKeyExpr.notEquals('config.notebook.globalToolbar', true)
788
),
789
group: 'navigation',
790
order: 0
791
},
792
{
793
id: MenuId.NotebookCellExecuteGoTo,
794
when: ContextKeyExpr.and(
795
NOTEBOOK_IS_ACTIVE_EDITOR,
796
NOTEBOOK_LAST_CELL_FAILED,
797
NOTEBOOK_HAS_RUNNING_CELL.toNegated(),
798
ContextKeyExpr.equals('config.notebook.globalToolbar', true)
799
),
800
group: 'navigation/execute',
801
order: 20
802
},
803
],
804
icon: icons.errorStateIcon,
805
});
806
}
807
808
async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise<void> {
809
const notebookExecutionStateService = accessor.get(INotebookExecutionStateService);
810
const notebook = context.notebookEditor.textModel.uri;
811
const lastFailedCellHandle = notebookExecutionStateService.getLastFailedCellForNotebook(notebook);
812
if (lastFailedCellHandle !== undefined) {
813
const lastFailedCell = context.notebookEditor.getCellByHandle(lastFailedCellHandle);
814
if (lastFailedCell) {
815
context.notebookEditor.focusNotebookCell(lastFailedCell, 'container');
816
}
817
}
818
}
819
});
820
821