Path: blob/main/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts
5240 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { Iterable } from '../../../../../base/common/iterator.js';6import { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js';7import { isEqual } from '../../../../../base/common/resources.js';8import { ThemeIcon } from '../../../../../base/common/themables.js';9import { URI, UriComponents } from '../../../../../base/common/uri.js';10import { ICodeEditor } from '../../../../../editor/browser/editorBrowser.js';11import { ILanguageService } from '../../../../../editor/common/languages/language.js';12import { localize, localize2 } from '../../../../../nls.js';13import { MenuId, MenuRegistry, registerAction2 } from '../../../../../platform/actions/common/actions.js';14import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';15import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js';16import { ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js';17import { IDebugService } from '../../../debug/common/debug.js';18import { CTX_INLINE_CHAT_FOCUSED } from '../../../inlineChat/common/inlineChat.js';19import { insertCell } from './cellOperations.js';20import { 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';21import { CellEditState, CellFocusMode, EXECUTE_CELL_COMMAND_ID, IActiveNotebookEditor, ICellViewModel, IFocusNotebookCellOptions, ScrollToRevealBehavior } from '../notebookBrowser.js';22import * as icons from '../notebookIcons.js';23import { CellKind, CellUri, NotebookSetting } from '../../common/notebookCommon.js';24import { 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';25import { NotebookEditorInput } from '../../common/notebookEditorInput.js';26import { INotebookExecutionStateService } from '../../common/notebookExecutionStateService.js';27import { IEditorGroupsService } from '../../../../services/editor/common/editorGroupsService.js';28import { IEditorService } from '../../../../services/editor/common/editorService.js';29import { CodeCellViewModel } from '../viewModel/codeCellViewModel.js';3031const EXECUTE_NOTEBOOK_COMMAND_ID = 'notebook.execute';32const CANCEL_NOTEBOOK_COMMAND_ID = 'notebook.cancelExecution';33const INTERRUPT_NOTEBOOK_COMMAND_ID = 'notebook.interruptExecution';34const CANCEL_CELL_COMMAND_ID = 'notebook.cell.cancelExecution';35const EXECUTE_CELL_FOCUS_CONTAINER_COMMAND_ID = 'notebook.cell.executeAndFocusContainer';36const EXECUTE_CELL_SELECT_BELOW = 'notebook.cell.executeAndSelectBelow';37const EXECUTE_CELL_INSERT_BELOW = 'notebook.cell.executeAndInsertBelow';38const EXECUTE_CELL_AND_BELOW = 'notebook.cell.executeCellAndBelow';39const EXECUTE_CELLS_ABOVE = 'notebook.cell.executeCellsAbove';40const RENDER_ALL_MARKDOWN_CELLS = 'notebook.renderAllMarkdownCells';41const REVEAL_RUNNING_CELL = 'notebook.revealRunningCell';42const REVEAL_LAST_FAILED_CELL = 'notebook.revealLastFailedCell';4344// If this changes, update getCodeCellExecutionContextKeyService to match45export const executeCondition = ContextKeyExpr.and(46NOTEBOOK_CELL_TYPE.isEqualTo('code'),47ContextKeyExpr.or(48ContextKeyExpr.greater(NOTEBOOK_KERNEL_COUNT.key, 0),49ContextKeyExpr.greater(NOTEBOOK_KERNEL_SOURCE_COUNT.key, 0),50NOTEBOOK_MISSING_KERNEL_EXTENSION51));5253export const executeThisCellCondition = ContextKeyExpr.and(54executeCondition,55NOTEBOOK_CELL_EXECUTING.toNegated());5657export const executeSectionCondition = ContextKeyExpr.and(58NOTEBOOK_CELL_TYPE.isEqualTo('markup'),59);6061function renderAllMarkdownCells(context: INotebookActionContext): void {62for (let i = 0; i < context.notebookEditor.getLength(); i++) {63const cell = context.notebookEditor.cellAt(i);6465if (cell.cellKind === CellKind.Markup) {66cell.updateEditState(CellEditState.Preview, 'renderAllMarkdownCells');67}68}69}7071async function runCell(editorGroupsService: IEditorGroupsService, context: INotebookActionContext, editorService?: IEditorService): Promise<void> {72const group = editorGroupsService.activeGroup;7374if (group) {75if (group.activeEditor) {76group.pinEditor(group.activeEditor);77}78}7980// If auto-reveal is enabled, ensure the notebook editor is visible before revealing cells81if (context.autoReveal && (context.cell || context.selectedCells?.length) && editorService) {82editorService.openEditor({ resource: context.notebookEditor.textModel.uri, options: { revealIfOpened: true } });83}8485if (context.ui && context.cell) {86if (context.autoReveal) {87handleAutoReveal(context.cell, context.notebookEditor);88}89await context.notebookEditor.executeNotebookCells(Iterable.single(context.cell));90} else if (context.selectedCells?.length || context.cell) {91const selectedCells = context.selectedCells?.length ? context.selectedCells : [context.cell!];92const firstCell = selectedCells[0];9394if (firstCell && context.autoReveal) {95handleAutoReveal(firstCell, context.notebookEditor);96}97await context.notebookEditor.executeNotebookCells(selectedCells);98}99100let foundEditor: ICodeEditor | undefined = undefined;101for (const [, codeEditor] of context.notebookEditor.codeEditors) {102if (isEqual(codeEditor.getModel()?.uri, (context.cell ?? context.selectedCells?.[0])?.uri)) {103foundEditor = codeEditor;104break;105}106}107108if (!foundEditor) {109return;110}111}112113const SMART_VIEWPORT_TOP_REVEAL_PADDING = 20; // enough to not cut off top of cell toolbar114const SMART_VIEWPORT_BOTTOM_REVEAL_PADDING = 60; // enough to show full bottom of output element + tiny buffer below that vertical bar115function handleAutoReveal(cell: ICellViewModel, notebookEditor: IActiveNotebookEditor): void {116// always focus the container, blue bar is a good visual aid in tracking what's happening117notebookEditor.focusNotebookCell(cell, 'container', { skipReveal: true });118119// Handle markup cells with simple reveal120if (cell.cellKind === CellKind.Markup) {121const cellIndex = notebookEditor.getCellIndex(cell);122notebookEditor.revealCellRangeInView({ start: cellIndex, end: cellIndex + 1 });123return;124}125126// Ensure we're working with a code cell - we need the CodeCellViewModel type for accessing layout properties like outputTotalHeight127if (!(cell instanceof CodeCellViewModel)) {128return;129}130131// Get all dimensions132const cellEditorScrollTop = notebookEditor.getAbsoluteTopOfElement(cell);133const cellEditorScrollBottom = cellEditorScrollTop + cell.layoutInfo.outputContainerOffset;134135const cellOutputHeight = cell.layoutInfo.outputTotalHeight;136const cellOutputScrollBottom = notebookEditor.getAbsoluteBottomOfElement(cell);137138const viewportHeight = notebookEditor.getLayoutInfo().height;139const viewportHeight34 = viewportHeight * 0.34;140const viewportHeight66 = viewportHeight * 0.66;141142const totalHeight = cell.layoutInfo.totalHeight;143144const isFullyVisible = cellEditorScrollTop >= notebookEditor.scrollTop && cellOutputScrollBottom <= notebookEditor.scrollBottom;145const isEditorBottomVisible = ((cellEditorScrollBottom - 25 /* padding for the cell status bar */) >= notebookEditor.scrollTop) &&146((cellEditorScrollBottom + 25 /* padding to see a sliver of the beginning of outputs */) <= notebookEditor.scrollBottom);147148// Common scrolling functions149const revealWithTopPadding = (position: number) => { notebookEditor.setScrollTop(position - SMART_VIEWPORT_TOP_REVEAL_PADDING); };150const revealWithNoPadding = (position: number) => { notebookEditor.setScrollTop(position); };151const revealWithBottomPadding = (position: number) => { notebookEditor.setScrollTop(position + SMART_VIEWPORT_BOTTOM_REVEAL_PADDING); };152153// CASE 0: Total is already visible154if (isFullyVisible) {155return;156}157158// CASE 1: Total fits within viewport159if (totalHeight <= viewportHeight && !isEditorBottomVisible) {160revealWithTopPadding(cellEditorScrollTop);161return;162}163164// CASE 2: Total doesn't fit in the viewport165if (totalHeight > viewportHeight && !isEditorBottomVisible) {166if (cellOutputHeight > 0 && cellOutputHeight >= viewportHeight66) {167// has large outputs -- Show 34% editor, 66% output168revealWithNoPadding(cellEditorScrollBottom - viewportHeight34);169} else if (cellOutputHeight > 0) {170// has small outputs -- Show output at viewport bottom171revealWithBottomPadding(cellOutputScrollBottom - viewportHeight);172} else {173// No outputs, just big cell -- put editor bottom @ 2/3 of viewport height174revealWithNoPadding(cellEditorScrollBottom - viewportHeight66);175}176}177}178179registerAction2(class RenderAllMarkdownCellsAction extends NotebookAction {180constructor() {181super({182id: RENDER_ALL_MARKDOWN_CELLS,183title: localize('notebookActions.renderMarkdown', "Render All Markdown Cells"),184});185}186187async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise<void> {188renderAllMarkdownCells(context);189}190});191192registerAction2(class ExecuteNotebookAction extends NotebookAction {193constructor() {194super({195id: EXECUTE_NOTEBOOK_COMMAND_ID,196title: localize('notebookActions.executeNotebook', "Run All"),197icon: icons.executeAllIcon,198metadata: {199description: localize('notebookActions.executeNotebook', "Run All"),200args: [201{202name: 'uri',203description: 'The document uri'204}205]206},207menu: [208{209id: MenuId.EditorTitle,210order: -1,211group: 'navigation',212when: ContextKeyExpr.and(213NOTEBOOK_IS_ACTIVE_EDITOR,214ContextKeyExpr.or(NOTEBOOK_INTERRUPTIBLE_KERNEL.toNegated(), NOTEBOOK_HAS_SOMETHING_RUNNING.toNegated()),215ContextKeyExpr.notEquals('config.notebook.globalToolbar', true)216)217},218{219id: MenuId.NotebookToolbar,220order: -1,221group: 'navigation/execute',222when: ContextKeyExpr.and(223ContextKeyExpr.or(224NOTEBOOK_INTERRUPTIBLE_KERNEL.toNegated(),225NOTEBOOK_HAS_SOMETHING_RUNNING.toNegated(),226),227ContextKeyExpr.and(NOTEBOOK_HAS_SOMETHING_RUNNING, NOTEBOOK_INTERRUPTIBLE_KERNEL.toNegated())?.negate(),228ContextKeyExpr.equals('config.notebook.globalToolbar', true)229)230}231]232});233}234235override getEditorContextFromArgsOrActive(accessor: ServicesAccessor, context?: UriComponents): INotebookActionContext | undefined {236return getContextFromUri(accessor, context) ?? getContextFromActiveEditor(accessor.get(IEditorService));237}238239async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise<void> {240renderAllMarkdownCells(context);241242const editorService = accessor.get(IEditorService);243const editor = editorService.findEditors({244resource: context.notebookEditor.textModel.uri,245typeId: NotebookEditorInput.ID,246editorId: context.notebookEditor.textModel.viewType247}).at(0);248const editorGroupService = accessor.get(IEditorGroupsService);249250if (editor) {251const group = editorGroupService.getGroup(editor.groupId);252group?.pinEditor(editor.editor);253}254255return context.notebookEditor.executeNotebookCells();256}257});258259registerAction2(class ExecuteCell extends NotebookMultiCellAction {260constructor() {261super({262id: EXECUTE_CELL_COMMAND_ID,263precondition: executeThisCellCondition,264title: localize('notebookActions.execute', "Execute Cell"),265keybinding: {266when: NOTEBOOK_CELL_LIST_FOCUSED,267primary: KeyMod.WinCtrl | KeyCode.Enter,268win: {269primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Enter270},271weight: NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT272},273menu: {274id: MenuId.NotebookCellExecutePrimary,275when: executeThisCellCondition,276group: 'inline'277},278metadata: {279description: localize('notebookActions.execute', "Execute Cell"),280args: cellExecutionArgs281},282icon: icons.executeIcon283});284}285286override parseArgs(accessor: ServicesAccessor, ...args: unknown[]): INotebookCommandContext | undefined {287return parseMultiCellExecutionArgs(accessor, ...args);288}289290async runWithContext(accessor: ServicesAccessor, context: INotebookCommandContext | INotebookCellToolbarActionContext): Promise<void> {291const editorGroupsService = accessor.get(IEditorGroupsService);292const editorService = accessor.get(IEditorService);293294if (context.ui) {295await context.notebookEditor.focusNotebookCell(context.cell, 'container', { skipReveal: true });296}297298await runCell(editorGroupsService, context, editorService);299}300});301302registerAction2(class ExecuteAboveCells extends NotebookMultiCellAction {303constructor() {304super({305id: EXECUTE_CELLS_ABOVE,306precondition: executeCondition,307title: localize('notebookActions.executeAbove', "Execute Above Cells"),308menu: [309{310id: MenuId.NotebookCellExecute,311when: ContextKeyExpr.and(312executeCondition,313ContextKeyExpr.equals(`config.${NotebookSetting.consolidatedRunButton}`, true))314},315{316id: MenuId.NotebookCellTitle,317order: CellToolbarOrder.ExecuteAboveCells,318group: CELL_TITLE_CELL_GROUP_ID,319when: ContextKeyExpr.and(320executeCondition,321ContextKeyExpr.equals(`config.${NotebookSetting.consolidatedRunButton}`, false))322}323],324icon: icons.executeAboveIcon325});326}327328override parseArgs(accessor: ServicesAccessor, ...args: unknown[]): INotebookCommandContext | undefined {329return parseMultiCellExecutionArgs(accessor, ...args);330}331332async runWithContext(accessor: ServicesAccessor, context: INotebookCommandContext | INotebookCellToolbarActionContext): Promise<void> {333let endCellIdx: number | undefined = undefined;334if (context.ui) {335endCellIdx = context.notebookEditor.getCellIndex(context.cell);336await context.notebookEditor.focusNotebookCell(context.cell, 'container', { skipReveal: true });337} else {338endCellIdx = Math.min(...context.selectedCells.map(cell => context.notebookEditor.getCellIndex(cell)));339}340341if (typeof endCellIdx === 'number') {342const range = { start: 0, end: endCellIdx };343const cells = context.notebookEditor.getCellsInRange(range);344context.notebookEditor.executeNotebookCells(cells);345}346}347});348349registerAction2(class ExecuteCellAndBelow extends NotebookMultiCellAction {350constructor() {351super({352id: EXECUTE_CELL_AND_BELOW,353precondition: executeCondition,354title: localize('notebookActions.executeBelow', "Execute Cell and Below"),355menu: [356{357id: MenuId.NotebookCellExecute,358when: ContextKeyExpr.and(359executeCondition,360ContextKeyExpr.equals(`config.${NotebookSetting.consolidatedRunButton}`, true))361},362{363id: MenuId.NotebookCellTitle,364order: CellToolbarOrder.ExecuteCellAndBelow,365group: CELL_TITLE_CELL_GROUP_ID,366when: ContextKeyExpr.and(367executeCondition,368ContextKeyExpr.equals(`config.${NotebookSetting.consolidatedRunButton}`, false))369}370],371icon: icons.executeBelowIcon372});373}374375override parseArgs(accessor: ServicesAccessor, ...args: unknown[]): INotebookCommandContext | undefined {376return parseMultiCellExecutionArgs(accessor, ...args);377}378379async runWithContext(accessor: ServicesAccessor, context: INotebookCommandContext | INotebookCellToolbarActionContext): Promise<void> {380let startCellIdx: number | undefined = undefined;381if (context.ui) {382startCellIdx = context.notebookEditor.getCellIndex(context.cell);383await context.notebookEditor.focusNotebookCell(context.cell, 'container', { skipReveal: true });384} else {385startCellIdx = Math.min(...context.selectedCells.map(cell => context.notebookEditor.getCellIndex(cell)));386}387388if (typeof startCellIdx === 'number') {389const range = { start: startCellIdx, end: context.notebookEditor.getLength() };390const cells = context.notebookEditor.getCellsInRange(range);391context.notebookEditor.executeNotebookCells(cells);392}393}394});395396registerAction2(class ExecuteCellFocusContainer extends NotebookMultiCellAction {397constructor() {398super({399id: EXECUTE_CELL_FOCUS_CONTAINER_COMMAND_ID,400precondition: executeThisCellCondition,401title: localize('notebookActions.executeAndFocusContainer', "Execute Cell and Focus Container"),402metadata: {403description: localize('notebookActions.executeAndFocusContainer', "Execute Cell and Focus Container"),404args: cellExecutionArgs405},406icon: icons.executeIcon407});408}409410override parseArgs(accessor: ServicesAccessor, ...args: unknown[]): INotebookCommandContext | undefined {411return parseMultiCellExecutionArgs(accessor, ...args);412}413414async runWithContext(accessor: ServicesAccessor, context: INotebookCommandContext | INotebookCellToolbarActionContext): Promise<void> {415const editorGroupsService = accessor.get(IEditorGroupsService);416const editorService = accessor.get(IEditorService);417418if (context.ui) {419await context.notebookEditor.focusNotebookCell(context.cell, 'container', { skipReveal: true });420} else {421const firstCell = context.selectedCells[0];422423if (firstCell) {424await context.notebookEditor.focusNotebookCell(firstCell, 'container', { skipReveal: true });425}426}427428await runCell(editorGroupsService, context, editorService);429}430});431432const cellCancelCondition = ContextKeyExpr.or(433ContextKeyExpr.equals(NOTEBOOK_CELL_EXECUTION_STATE.key, 'executing'),434ContextKeyExpr.equals(NOTEBOOK_CELL_EXECUTION_STATE.key, 'pending'),435);436437registerAction2(class CancelExecuteCell extends NotebookMultiCellAction {438constructor() {439super({440id: CANCEL_CELL_COMMAND_ID,441precondition: cellCancelCondition,442title: localize('notebookActions.cancel', "Stop Cell Execution"),443icon: icons.stopIcon,444menu: {445id: MenuId.NotebookCellExecutePrimary,446when: cellCancelCondition,447group: 'inline'448},449metadata: {450description: localize('notebookActions.cancel', "Stop Cell Execution"),451args: [452{453name: 'options',454description: 'The cell range options',455schema: {456'type': 'object',457'required': ['ranges'],458'properties': {459'ranges': {460'type': 'array',461items: [462{463'type': 'object',464'required': ['start', 'end'],465'properties': {466'start': {467'type': 'number'468},469'end': {470'type': 'number'471}472}473}474]475},476'document': {477'type': 'object',478'description': 'The document uri',479}480}481}482}483]484},485});486}487488override parseArgs(accessor: ServicesAccessor, ...args: unknown[]): INotebookCommandContext | undefined {489return parseMultiCellExecutionArgs(accessor, ...args);490}491492async runWithContext(accessor: ServicesAccessor, context: INotebookCommandContext | INotebookCellToolbarActionContext): Promise<void> {493if (context.ui) {494await context.notebookEditor.focusNotebookCell(context.cell, 'container', { skipReveal: true });495return context.notebookEditor.cancelNotebookCells(Iterable.single(context.cell));496} else {497return context.notebookEditor.cancelNotebookCells(context.selectedCells);498}499}500});501502registerAction2(class ExecuteCellSelectBelow extends NotebookCellAction {503constructor() {504super({505id: EXECUTE_CELL_SELECT_BELOW,506precondition: ContextKeyExpr.or(executeThisCellCondition, NOTEBOOK_CELL_TYPE.isEqualTo('markup')),507title: localize('notebookActions.executeAndSelectBelow', "Execute Notebook Cell and Select Below"),508keybinding: {509when: ContextKeyExpr.and(510NOTEBOOK_CELL_LIST_FOCUSED,511CTX_INLINE_CHAT_FOCUSED.negate()512),513primary: KeyMod.Shift | KeyCode.Enter,514weight: NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT515},516});517}518519async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise<void> {520const editorGroupsService = accessor.get(IEditorGroupsService);521const editorService = accessor.get(IEditorService);522const idx = context.notebookEditor.getCellIndex(context.cell);523if (typeof idx !== 'number') {524return;525}526const languageService = accessor.get(ILanguageService);527528const config = accessor.get(IConfigurationService);529const scrollBehavior = config.getValue(NotebookSetting.scrollToRevealCell);530let focusOptions: IFocusNotebookCellOptions;531if (scrollBehavior === 'none') {532focusOptions = { skipReveal: true };533} else {534focusOptions = {535revealBehavior: scrollBehavior === 'fullCell' ? ScrollToRevealBehavior.fullCell : ScrollToRevealBehavior.firstLine536};537}538539if (context.cell.cellKind === CellKind.Markup) {540const nextCell = context.notebookEditor.cellAt(idx + 1);541context.cell.updateEditState(CellEditState.Preview, EXECUTE_CELL_SELECT_BELOW);542if (nextCell) {543await context.notebookEditor.focusNotebookCell(nextCell, 'container', focusOptions);544} else {545const newCell = insertCell(languageService, context.notebookEditor, idx, CellKind.Markup, 'below');546547if (newCell) {548await context.notebookEditor.focusNotebookCell(newCell, 'editor', focusOptions);549}550}551return;552} else {553const nextCell = context.notebookEditor.cellAt(idx + 1);554if (nextCell) {555await context.notebookEditor.focusNotebookCell(nextCell, 'container', focusOptions);556} else {557const newCell = insertCell(languageService, context.notebookEditor, idx, CellKind.Code, 'below');558559if (newCell) {560await context.notebookEditor.focusNotebookCell(newCell, 'editor', focusOptions);561}562}563564return runCell(editorGroupsService, context, editorService);565}566}567});568569registerAction2(class ExecuteCellInsertBelow extends NotebookCellAction {570constructor() {571super({572id: EXECUTE_CELL_INSERT_BELOW,573precondition: ContextKeyExpr.or(executeThisCellCondition, NOTEBOOK_CELL_TYPE.isEqualTo('markup')),574title: localize('notebookActions.executeAndInsertBelow', "Execute Notebook Cell and Insert Below"),575keybinding: {576when: NOTEBOOK_CELL_LIST_FOCUSED,577primary: KeyMod.Alt | KeyCode.Enter,578weight: NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT579},580});581}582583async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise<void> {584const editorGroupsService = accessor.get(IEditorGroupsService);585const editorService = accessor.get(IEditorService);586const idx = context.notebookEditor.getCellIndex(context.cell);587const languageService = accessor.get(ILanguageService);588const newFocusMode = context.cell.focusMode === CellFocusMode.Editor ? 'editor' : 'container';589590const newCell = insertCell(languageService, context.notebookEditor, idx, context.cell.cellKind, 'below');591if (newCell) {592await context.notebookEditor.focusNotebookCell(newCell, newFocusMode);593}594595if (context.cell.cellKind === CellKind.Markup) {596context.cell.updateEditState(CellEditState.Preview, EXECUTE_CELL_INSERT_BELOW);597} else {598runCell(editorGroupsService, context, editorService);599}600}601});602603class CancelNotebook extends NotebookAction {604override getEditorContextFromArgsOrActive(accessor: ServicesAccessor, context?: UriComponents): INotebookActionContext | undefined {605return getContextFromUri(accessor, context) ?? getContextFromActiveEditor(accessor.get(IEditorService));606}607608async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise<void> {609return context.notebookEditor.cancelNotebookCells();610}611}612613registerAction2(class CancelAllNotebook extends CancelNotebook {614constructor() {615super({616id: CANCEL_NOTEBOOK_COMMAND_ID,617title: localize2('notebookActions.cancelNotebook', "Stop Execution"),618icon: icons.stopIcon,619menu: [620{621id: MenuId.EditorTitle,622order: -1,623group: 'navigation',624when: ContextKeyExpr.and(625NOTEBOOK_IS_ACTIVE_EDITOR,626NOTEBOOK_HAS_SOMETHING_RUNNING,627NOTEBOOK_INTERRUPTIBLE_KERNEL.toNegated(),628ContextKeyExpr.notEquals('config.notebook.globalToolbar', true)629)630},631{632id: MenuId.NotebookToolbar,633order: -1,634group: 'navigation/execute',635when: ContextKeyExpr.and(636NOTEBOOK_HAS_SOMETHING_RUNNING,637NOTEBOOK_INTERRUPTIBLE_KERNEL.toNegated(),638ContextKeyExpr.equals('config.notebook.globalToolbar', true)639)640}641]642});643}644});645646registerAction2(class InterruptNotebook extends CancelNotebook {647constructor() {648super({649id: INTERRUPT_NOTEBOOK_COMMAND_ID,650title: localize2('notebookActions.interruptNotebook', "Interrupt"),651precondition: ContextKeyExpr.and(652NOTEBOOK_HAS_SOMETHING_RUNNING,653NOTEBOOK_INTERRUPTIBLE_KERNEL654),655icon: icons.stopIcon,656menu: [657{658id: MenuId.EditorTitle,659order: -1,660group: 'navigation',661when: ContextKeyExpr.and(662NOTEBOOK_IS_ACTIVE_EDITOR,663NOTEBOOK_HAS_SOMETHING_RUNNING,664NOTEBOOK_INTERRUPTIBLE_KERNEL,665ContextKeyExpr.notEquals('config.notebook.globalToolbar', true)666)667},668{669id: MenuId.NotebookToolbar,670order: -1,671group: 'navigation/execute',672when: ContextKeyExpr.and(673NOTEBOOK_HAS_SOMETHING_RUNNING,674NOTEBOOK_INTERRUPTIBLE_KERNEL,675ContextKeyExpr.equals('config.notebook.globalToolbar', true)676)677},678{679id: MenuId.InteractiveToolbar,680group: 'navigation/execute'681}682]683});684}685});686687688MenuRegistry.appendMenuItem(MenuId.NotebookToolbar, {689title: localize('revealRunningCellShort', "Go To"),690submenu: MenuId.NotebookCellExecuteGoTo,691group: 'navigation/execute',692order: 20,693icon: ThemeIcon.modify(icons.executingStateIcon, 'spin')694});695696registerAction2(class RevealRunningCellAction extends NotebookAction {697constructor() {698super({699id: REVEAL_RUNNING_CELL,700title: localize('revealRunningCell', "Go to Running Cell"),701tooltip: localize('revealRunningCell', "Go to Running Cell"),702shortTitle: localize('revealRunningCell', "Go to Running Cell"),703precondition: NOTEBOOK_HAS_RUNNING_CELL,704menu: [705{706id: MenuId.EditorTitle,707when: ContextKeyExpr.and(708NOTEBOOK_IS_ACTIVE_EDITOR,709NOTEBOOK_HAS_RUNNING_CELL,710ContextKeyExpr.notEquals('config.notebook.globalToolbar', true)711),712group: 'navigation',713order: 0714},715{716id: MenuId.NotebookCellExecuteGoTo,717when: ContextKeyExpr.and(718NOTEBOOK_IS_ACTIVE_EDITOR,719NOTEBOOK_HAS_RUNNING_CELL,720ContextKeyExpr.equals('config.notebook.globalToolbar', true)721),722group: 'navigation/execute',723order: 20724},725{726id: MenuId.InteractiveToolbar,727when: ContextKeyExpr.and(728NOTEBOOK_HAS_RUNNING_CELL,729ContextKeyExpr.equals('activeEditor', 'workbench.editor.interactive')730),731group: 'navigation',732order: 10733}734],735icon: ThemeIcon.modify(icons.executingStateIcon, 'spin')736});737}738739async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise<void> {740const notebookExecutionStateService = accessor.get(INotebookExecutionStateService);741const notebook = context.notebookEditor.textModel.uri;742const executingCells = notebookExecutionStateService.getCellExecutionsForNotebook(notebook);743if (executingCells[0]) {744const topStackFrameCell = this.findCellAtTopFrame(accessor, notebook);745const focusHandle = topStackFrameCell ?? executingCells[0].cellHandle;746const cell = context.notebookEditor.getCellByHandle(focusHandle);747if (cell) {748context.notebookEditor.focusNotebookCell(cell, 'container');749}750}751}752753private findCellAtTopFrame(accessor: ServicesAccessor, notebook: URI): number | undefined {754const debugService = accessor.get(IDebugService);755for (const session of debugService.getModel().getSessions()) {756for (const thread of session.getAllThreads()) {757const sf = thread.getTopStackFrame();758if (sf) {759const parsed = CellUri.parse(sf.source.uri);760if (parsed && parsed.notebook.toString() === notebook.toString()) {761return parsed.handle;762}763}764}765}766767return undefined;768}769});770771registerAction2(class RevealLastFailedCellAction extends NotebookAction {772constructor() {773super({774id: REVEAL_LAST_FAILED_CELL,775title: localize('revealLastFailedCell', "Go to Most Recently Failed Cell"),776tooltip: localize('revealLastFailedCell', "Go to Most Recently Failed Cell"),777shortTitle: localize('revealLastFailedCellShort', "Go to Most Recently Failed Cell"),778precondition: NOTEBOOK_LAST_CELL_FAILED,779menu: [780{781id: MenuId.EditorTitle,782when: ContextKeyExpr.and(783NOTEBOOK_IS_ACTIVE_EDITOR,784NOTEBOOK_LAST_CELL_FAILED,785NOTEBOOK_HAS_RUNNING_CELL.toNegated(),786ContextKeyExpr.notEquals('config.notebook.globalToolbar', true)787),788group: 'navigation',789order: 0790},791{792id: MenuId.NotebookCellExecuteGoTo,793when: ContextKeyExpr.and(794NOTEBOOK_IS_ACTIVE_EDITOR,795NOTEBOOK_LAST_CELL_FAILED,796NOTEBOOK_HAS_RUNNING_CELL.toNegated(),797ContextKeyExpr.equals('config.notebook.globalToolbar', true)798),799group: 'navigation/execute',800order: 20801},802],803icon: icons.errorStateIcon,804});805}806807async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise<void> {808const notebookExecutionStateService = accessor.get(INotebookExecutionStateService);809const notebook = context.notebookEditor.textModel.uri;810const lastFailedCellHandle = notebookExecutionStateService.getLastFailedCellForNotebook(notebook);811if (lastFailedCellHandle !== undefined) {812const lastFailedCell = context.notebookEditor.getCellByHandle(lastFailedCellHandle);813if (lastFailedCell) {814context.notebookEditor.focusNotebookCell(lastFailedCell, 'container');815}816}817}818});819820821