Path: blob/main/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts
3296 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 { NotebookChatController } from './chat/notebookChatController.js';21import { 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';22import { CellEditState, CellFocusMode, EXECUTE_CELL_COMMAND_ID, IActiveNotebookEditor, ICellViewModel, IFocusNotebookCellOptions, ScrollToRevealBehavior } from '../notebookBrowser.js';23import * as icons from '../notebookIcons.js';24import { CellKind, CellUri, NotebookSetting } from '../../common/notebookCommon.js';25import { 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';26import { NotebookEditorInput } from '../../common/notebookEditorInput.js';27import { INotebookExecutionStateService } from '../../common/notebookExecutionStateService.js';28import { IEditorGroupsService } from '../../../../services/editor/common/editorGroupsService.js';29import { IEditorService } from '../../../../services/editor/common/editorService.js';30import { CodeCellViewModel } from '../viewModel/codeCellViewModel.js';3132const EXECUTE_NOTEBOOK_COMMAND_ID = 'notebook.execute';33const CANCEL_NOTEBOOK_COMMAND_ID = 'notebook.cancelExecution';34const INTERRUPT_NOTEBOOK_COMMAND_ID = 'notebook.interruptExecution';35const CANCEL_CELL_COMMAND_ID = 'notebook.cell.cancelExecution';36const EXECUTE_CELL_FOCUS_CONTAINER_COMMAND_ID = 'notebook.cell.executeAndFocusContainer';37const EXECUTE_CELL_SELECT_BELOW = 'notebook.cell.executeAndSelectBelow';38const EXECUTE_CELL_INSERT_BELOW = 'notebook.cell.executeAndInsertBelow';39const EXECUTE_CELL_AND_BELOW = 'notebook.cell.executeCellAndBelow';40const EXECUTE_CELLS_ABOVE = 'notebook.cell.executeCellsAbove';41const RENDER_ALL_MARKDOWN_CELLS = 'notebook.renderAllMarkdownCells';42const REVEAL_RUNNING_CELL = 'notebook.revealRunningCell';43const REVEAL_LAST_FAILED_CELL = 'notebook.revealLastFailedCell';4445// If this changes, update getCodeCellExecutionContextKeyService to match46export const executeCondition = ContextKeyExpr.and(47NOTEBOOK_CELL_TYPE.isEqualTo('code'),48ContextKeyExpr.or(49ContextKeyExpr.greater(NOTEBOOK_KERNEL_COUNT.key, 0),50ContextKeyExpr.greater(NOTEBOOK_KERNEL_SOURCE_COUNT.key, 0),51NOTEBOOK_MISSING_KERNEL_EXTENSION52));5354export const executeThisCellCondition = ContextKeyExpr.and(55executeCondition,56NOTEBOOK_CELL_EXECUTING.toNegated());5758export const executeSectionCondition = ContextKeyExpr.and(59NOTEBOOK_CELL_TYPE.isEqualTo('markup'),60);6162function renderAllMarkdownCells(context: INotebookActionContext): void {63for (let i = 0; i < context.notebookEditor.getLength(); i++) {64const cell = context.notebookEditor.cellAt(i);6566if (cell.cellKind === CellKind.Markup) {67cell.updateEditState(CellEditState.Preview, 'renderAllMarkdownCells');68}69}70}7172async function runCell(editorGroupsService: IEditorGroupsService, context: INotebookActionContext, editorService?: IEditorService): Promise<void> {73const group = editorGroupsService.activeGroup;7475if (group) {76if (group.activeEditor) {77group.pinEditor(group.activeEditor);78}79}8081// If auto-reveal is enabled, ensure the notebook editor is visible before revealing cells82if (context.autoReveal && (context.cell || context.selectedCells?.length) && editorService) {83editorService.openEditor({ resource: context.notebookEditor.textModel.uri, options: { revealIfOpened: true } });84}8586if (context.ui && context.cell) {87if (context.autoReveal) {88handleAutoReveal(context.cell, context.notebookEditor);89}90await context.notebookEditor.executeNotebookCells(Iterable.single(context.cell));91} else if (context.selectedCells?.length || context.cell) {92const selectedCells = context.selectedCells?.length ? context.selectedCells : [context.cell!];93const firstCell = selectedCells[0];9495if (firstCell && context.autoReveal) {96handleAutoReveal(firstCell, context.notebookEditor);97}98await context.notebookEditor.executeNotebookCells(selectedCells);99}100101let foundEditor: ICodeEditor | undefined = undefined;102for (const [, codeEditor] of context.notebookEditor.codeEditors) {103if (isEqual(codeEditor.getModel()?.uri, (context.cell ?? context.selectedCells?.[0])?.uri)) {104foundEditor = codeEditor;105break;106}107}108109if (!foundEditor) {110return;111}112}113114const SMART_VIEWPORT_TOP_REVEAL_PADDING = 20; // enough to not cut off top of cell toolbar115const SMART_VIEWPORT_BOTTOM_REVEAL_PADDING = 60; // enough to show full bottom of output element + tiny buffer below that vertical bar116function handleAutoReveal(cell: ICellViewModel, notebookEditor: IActiveNotebookEditor): void {117// always focus the container, blue bar is a good visual aid in tracking what's happening118notebookEditor.focusNotebookCell(cell, 'container', { skipReveal: true });119120// Handle markup cells with simple reveal121if (cell.cellKind === CellKind.Markup) {122const cellIndex = notebookEditor.getCellIndex(cell);123notebookEditor.revealCellRangeInView({ start: cellIndex, end: cellIndex + 1 });124return;125}126127// Ensure we're working with a code cell - we need the CodeCellViewModel type for accessing layout properties like outputTotalHeight128if (!(cell instanceof CodeCellViewModel)) {129return;130}131132// Get all dimensions133const cellEditorScrollTop = notebookEditor.getAbsoluteTopOfElement(cell);134const cellEditorScrollBottom = cellEditorScrollTop + cell.layoutInfo.outputContainerOffset;135136const cellOutputHeight = cell.layoutInfo.outputTotalHeight;137const cellOutputScrollBottom = notebookEditor.getAbsoluteBottomOfElement(cell);138139const viewportHeight = notebookEditor.getLayoutInfo().height;140const viewportHeight34 = viewportHeight * 0.34;141const viewportHeight66 = viewportHeight * 0.66;142143const totalHeight = cell.layoutInfo.totalHeight;144145const isFullyVisible = cellEditorScrollTop >= notebookEditor.scrollTop && cellOutputScrollBottom <= notebookEditor.scrollBottom;146const 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);148149// Common scrolling functions150const revealWithTopPadding = (position: number) => { notebookEditor.setScrollTop(position - SMART_VIEWPORT_TOP_REVEAL_PADDING); };151const revealWithNoPadding = (position: number) => { notebookEditor.setScrollTop(position); };152const revealWithBottomPadding = (position: number) => { notebookEditor.setScrollTop(position + SMART_VIEWPORT_BOTTOM_REVEAL_PADDING); };153154// CASE 0: Total is already visible155if (isFullyVisible) {156return;157}158159// CASE 1: Total fits within viewport160if (totalHeight <= viewportHeight && !isEditorBottomVisible) {161revealWithTopPadding(cellEditorScrollTop);162return;163}164165// CASE 2: Total doesn't fit in the viewport166if (totalHeight > viewportHeight && !isEditorBottomVisible) {167if (cellOutputHeight > 0 && cellOutputHeight >= viewportHeight66) {168// has large outputs -- Show 34% editor, 66% output169revealWithNoPadding(cellEditorScrollBottom - viewportHeight34);170} else if (cellOutputHeight > 0) {171// has small outputs -- Show output at viewport bottom172revealWithBottomPadding(cellOutputScrollBottom - viewportHeight);173} else {174// No outputs, just big cell -- put editor bottom @ 2/3 of viewport height175revealWithNoPadding(cellEditorScrollBottom - viewportHeight66);176}177}178}179180registerAction2(class RenderAllMarkdownCellsAction extends NotebookAction {181constructor() {182super({183id: RENDER_ALL_MARKDOWN_CELLS,184title: localize('notebookActions.renderMarkdown', "Render All Markdown Cells"),185});186}187188async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise<void> {189renderAllMarkdownCells(context);190}191});192193registerAction2(class ExecuteNotebookAction extends NotebookAction {194constructor() {195super({196id: EXECUTE_NOTEBOOK_COMMAND_ID,197title: localize('notebookActions.executeNotebook', "Run All"),198icon: icons.executeAllIcon,199metadata: {200description: localize('notebookActions.executeNotebook', "Run All"),201args: [202{203name: 'uri',204description: 'The document uri'205}206]207},208menu: [209{210id: MenuId.EditorTitle,211order: -1,212group: 'navigation',213when: ContextKeyExpr.and(214NOTEBOOK_IS_ACTIVE_EDITOR,215ContextKeyExpr.or(NOTEBOOK_INTERRUPTIBLE_KERNEL.toNegated(), NOTEBOOK_HAS_SOMETHING_RUNNING.toNegated()),216ContextKeyExpr.notEquals('config.notebook.globalToolbar', true)217)218},219{220id: MenuId.NotebookToolbar,221order: -1,222group: 'navigation/execute',223when: ContextKeyExpr.and(224ContextKeyExpr.or(225NOTEBOOK_INTERRUPTIBLE_KERNEL.toNegated(),226NOTEBOOK_HAS_SOMETHING_RUNNING.toNegated(),227),228ContextKeyExpr.and(NOTEBOOK_HAS_SOMETHING_RUNNING, NOTEBOOK_INTERRUPTIBLE_KERNEL.toNegated())?.negate(),229ContextKeyExpr.equals('config.notebook.globalToolbar', true)230)231}232]233});234}235236override getEditorContextFromArgsOrActive(accessor: ServicesAccessor, context?: UriComponents): INotebookActionContext | undefined {237return getContextFromUri(accessor, context) ?? getContextFromActiveEditor(accessor.get(IEditorService));238}239240async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise<void> {241renderAllMarkdownCells(context);242243const editorService = accessor.get(IEditorService);244const editor = editorService.findEditors({245resource: context.notebookEditor.textModel.uri,246typeId: NotebookEditorInput.ID,247editorId: context.notebookEditor.textModel.viewType248}).at(0);249const editorGroupService = accessor.get(IEditorGroupsService);250251if (editor) {252const group = editorGroupService.getGroup(editor.groupId);253group?.pinEditor(editor.editor);254}255256return context.notebookEditor.executeNotebookCells();257}258});259260registerAction2(class ExecuteCell extends NotebookMultiCellAction {261constructor() {262super({263id: EXECUTE_CELL_COMMAND_ID,264precondition: executeThisCellCondition,265title: localize('notebookActions.execute', "Execute Cell"),266keybinding: {267when: NOTEBOOK_CELL_LIST_FOCUSED,268primary: KeyMod.WinCtrl | KeyCode.Enter,269win: {270primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Enter271},272weight: NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT273},274menu: {275id: MenuId.NotebookCellExecutePrimary,276when: executeThisCellCondition,277group: 'inline'278},279metadata: {280description: localize('notebookActions.execute', "Execute Cell"),281args: cellExecutionArgs282},283icon: icons.executeIcon284});285}286287override parseArgs(accessor: ServicesAccessor, ...args: any[]): INotebookCommandContext | undefined {288return parseMultiCellExecutionArgs(accessor, ...args);289}290291async runWithContext(accessor: ServicesAccessor, context: INotebookCommandContext | INotebookCellToolbarActionContext): Promise<void> {292const editorGroupsService = accessor.get(IEditorGroupsService);293const editorService = accessor.get(IEditorService);294295if (context.ui) {296await context.notebookEditor.focusNotebookCell(context.cell, 'container', { skipReveal: true });297}298299const chatController = NotebookChatController.get(context.notebookEditor);300const editingCell = chatController?.getEditingCell();301if (chatController?.hasFocus() && editingCell) {302const group = editorGroupsService.activeGroup;303304if (group) {305if (group.activeEditor) {306group.pinEditor(group.activeEditor);307}308}309310await context.notebookEditor.executeNotebookCells([editingCell]);311return;312}313314await runCell(editorGroupsService, context, editorService);315}316});317318registerAction2(class ExecuteAboveCells extends NotebookMultiCellAction {319constructor() {320super({321id: EXECUTE_CELLS_ABOVE,322precondition: executeCondition,323title: localize('notebookActions.executeAbove', "Execute Above Cells"),324menu: [325{326id: MenuId.NotebookCellExecute,327when: ContextKeyExpr.and(328executeCondition,329ContextKeyExpr.equals(`config.${NotebookSetting.consolidatedRunButton}`, true))330},331{332id: MenuId.NotebookCellTitle,333order: CellToolbarOrder.ExecuteAboveCells,334group: CELL_TITLE_CELL_GROUP_ID,335when: ContextKeyExpr.and(336executeCondition,337ContextKeyExpr.equals(`config.${NotebookSetting.consolidatedRunButton}`, false))338}339],340icon: icons.executeAboveIcon341});342}343344override parseArgs(accessor: ServicesAccessor, ...args: any[]): INotebookCommandContext | undefined {345return parseMultiCellExecutionArgs(accessor, ...args);346}347348async runWithContext(accessor: ServicesAccessor, context: INotebookCommandContext | INotebookCellToolbarActionContext): Promise<void> {349let endCellIdx: number | undefined = undefined;350if (context.ui) {351endCellIdx = context.notebookEditor.getCellIndex(context.cell);352await context.notebookEditor.focusNotebookCell(context.cell, 'container', { skipReveal: true });353} else {354endCellIdx = Math.min(...context.selectedCells.map(cell => context.notebookEditor.getCellIndex(cell)));355}356357if (typeof endCellIdx === 'number') {358const range = { start: 0, end: endCellIdx };359const cells = context.notebookEditor.getCellsInRange(range);360context.notebookEditor.executeNotebookCells(cells);361}362}363});364365registerAction2(class ExecuteCellAndBelow extends NotebookMultiCellAction {366constructor() {367super({368id: EXECUTE_CELL_AND_BELOW,369precondition: executeCondition,370title: localize('notebookActions.executeBelow', "Execute Cell and Below"),371menu: [372{373id: MenuId.NotebookCellExecute,374when: ContextKeyExpr.and(375executeCondition,376ContextKeyExpr.equals(`config.${NotebookSetting.consolidatedRunButton}`, true))377},378{379id: MenuId.NotebookCellTitle,380order: CellToolbarOrder.ExecuteCellAndBelow,381group: CELL_TITLE_CELL_GROUP_ID,382when: ContextKeyExpr.and(383executeCondition,384ContextKeyExpr.equals(`config.${NotebookSetting.consolidatedRunButton}`, false))385}386],387icon: icons.executeBelowIcon388});389}390391override parseArgs(accessor: ServicesAccessor, ...args: any[]): INotebookCommandContext | undefined {392return parseMultiCellExecutionArgs(accessor, ...args);393}394395async runWithContext(accessor: ServicesAccessor, context: INotebookCommandContext | INotebookCellToolbarActionContext): Promise<void> {396let startCellIdx: number | undefined = undefined;397if (context.ui) {398startCellIdx = context.notebookEditor.getCellIndex(context.cell);399await context.notebookEditor.focusNotebookCell(context.cell, 'container', { skipReveal: true });400} else {401startCellIdx = Math.min(...context.selectedCells.map(cell => context.notebookEditor.getCellIndex(cell)));402}403404if (typeof startCellIdx === 'number') {405const range = { start: startCellIdx, end: context.notebookEditor.getLength() };406const cells = context.notebookEditor.getCellsInRange(range);407context.notebookEditor.executeNotebookCells(cells);408}409}410});411412registerAction2(class ExecuteCellFocusContainer extends NotebookMultiCellAction {413constructor() {414super({415id: EXECUTE_CELL_FOCUS_CONTAINER_COMMAND_ID,416precondition: executeThisCellCondition,417title: localize('notebookActions.executeAndFocusContainer', "Execute Cell and Focus Container"),418metadata: {419description: localize('notebookActions.executeAndFocusContainer', "Execute Cell and Focus Container"),420args: cellExecutionArgs421},422icon: icons.executeIcon423});424}425426override parseArgs(accessor: ServicesAccessor, ...args: any[]): INotebookCommandContext | undefined {427return parseMultiCellExecutionArgs(accessor, ...args);428}429430async runWithContext(accessor: ServicesAccessor, context: INotebookCommandContext | INotebookCellToolbarActionContext): Promise<void> {431const editorGroupsService = accessor.get(IEditorGroupsService);432const editorService = accessor.get(IEditorService);433434if (context.ui) {435await context.notebookEditor.focusNotebookCell(context.cell, 'container', { skipReveal: true });436} else {437const firstCell = context.selectedCells[0];438439if (firstCell) {440await context.notebookEditor.focusNotebookCell(firstCell, 'container', { skipReveal: true });441}442}443444await runCell(editorGroupsService, context, editorService);445}446});447448const cellCancelCondition = ContextKeyExpr.or(449ContextKeyExpr.equals(NOTEBOOK_CELL_EXECUTION_STATE.key, 'executing'),450ContextKeyExpr.equals(NOTEBOOK_CELL_EXECUTION_STATE.key, 'pending'),451);452453registerAction2(class CancelExecuteCell extends NotebookMultiCellAction {454constructor() {455super({456id: CANCEL_CELL_COMMAND_ID,457precondition: cellCancelCondition,458title: localize('notebookActions.cancel', "Stop Cell Execution"),459icon: icons.stopIcon,460menu: {461id: MenuId.NotebookCellExecutePrimary,462when: cellCancelCondition,463group: 'inline'464},465metadata: {466description: localize('notebookActions.cancel', "Stop Cell Execution"),467args: [468{469name: 'options',470description: 'The cell range options',471schema: {472'type': 'object',473'required': ['ranges'],474'properties': {475'ranges': {476'type': 'array',477items: [478{479'type': 'object',480'required': ['start', 'end'],481'properties': {482'start': {483'type': 'number'484},485'end': {486'type': 'number'487}488}489}490]491},492'document': {493'type': 'object',494'description': 'The document uri',495}496}497}498}499]500},501});502}503504override parseArgs(accessor: ServicesAccessor, ...args: any[]): INotebookCommandContext | undefined {505return parseMultiCellExecutionArgs(accessor, ...args);506}507508async runWithContext(accessor: ServicesAccessor, context: INotebookCommandContext | INotebookCellToolbarActionContext): Promise<void> {509if (context.ui) {510await context.notebookEditor.focusNotebookCell(context.cell, 'container', { skipReveal: true });511return context.notebookEditor.cancelNotebookCells(Iterable.single(context.cell));512} else {513return context.notebookEditor.cancelNotebookCells(context.selectedCells);514}515}516});517518registerAction2(class ExecuteCellSelectBelow extends NotebookCellAction {519constructor() {520super({521id: EXECUTE_CELL_SELECT_BELOW,522precondition: ContextKeyExpr.or(executeThisCellCondition, NOTEBOOK_CELL_TYPE.isEqualTo('markup')),523title: localize('notebookActions.executeAndSelectBelow', "Execute Notebook Cell and Select Below"),524keybinding: {525when: ContextKeyExpr.and(526NOTEBOOK_CELL_LIST_FOCUSED,527CTX_INLINE_CHAT_FOCUSED.negate()528),529primary: KeyMod.Shift | KeyCode.Enter,530weight: NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT531},532});533}534535async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise<void> {536const editorGroupsService = accessor.get(IEditorGroupsService);537const editorService = accessor.get(IEditorService);538const idx = context.notebookEditor.getCellIndex(context.cell);539if (typeof idx !== 'number') {540return;541}542const languageService = accessor.get(ILanguageService);543544const config = accessor.get(IConfigurationService);545const scrollBehavior = config.getValue(NotebookSetting.scrollToRevealCell);546let focusOptions: IFocusNotebookCellOptions;547if (scrollBehavior === 'none') {548focusOptions = { skipReveal: true };549} else {550focusOptions = {551revealBehavior: scrollBehavior === 'fullCell' ? ScrollToRevealBehavior.fullCell : ScrollToRevealBehavior.firstLine552};553}554555if (context.cell.cellKind === CellKind.Markup) {556const nextCell = context.notebookEditor.cellAt(idx + 1);557context.cell.updateEditState(CellEditState.Preview, EXECUTE_CELL_SELECT_BELOW);558if (nextCell) {559await context.notebookEditor.focusNotebookCell(nextCell, 'container', focusOptions);560} else {561const newCell = insertCell(languageService, context.notebookEditor, idx, CellKind.Markup, 'below');562563if (newCell) {564await context.notebookEditor.focusNotebookCell(newCell, 'editor', focusOptions);565}566}567return;568} else {569const nextCell = context.notebookEditor.cellAt(idx + 1);570if (nextCell) {571await context.notebookEditor.focusNotebookCell(nextCell, 'container', focusOptions);572} else {573const newCell = insertCell(languageService, context.notebookEditor, idx, CellKind.Code, 'below');574575if (newCell) {576await context.notebookEditor.focusNotebookCell(newCell, 'editor', focusOptions);577}578}579580return runCell(editorGroupsService, context, editorService);581}582}583});584585registerAction2(class ExecuteCellInsertBelow extends NotebookCellAction {586constructor() {587super({588id: EXECUTE_CELL_INSERT_BELOW,589precondition: ContextKeyExpr.or(executeThisCellCondition, NOTEBOOK_CELL_TYPE.isEqualTo('markup')),590title: localize('notebookActions.executeAndInsertBelow', "Execute Notebook Cell and Insert Below"),591keybinding: {592when: NOTEBOOK_CELL_LIST_FOCUSED,593primary: KeyMod.Alt | KeyCode.Enter,594weight: NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT595},596});597}598599async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise<void> {600const editorGroupsService = accessor.get(IEditorGroupsService);601const editorService = accessor.get(IEditorService);602const idx = context.notebookEditor.getCellIndex(context.cell);603const languageService = accessor.get(ILanguageService);604const newFocusMode = context.cell.focusMode === CellFocusMode.Editor ? 'editor' : 'container';605606const newCell = insertCell(languageService, context.notebookEditor, idx, context.cell.cellKind, 'below');607if (newCell) {608await context.notebookEditor.focusNotebookCell(newCell, newFocusMode);609}610611if (context.cell.cellKind === CellKind.Markup) {612context.cell.updateEditState(CellEditState.Preview, EXECUTE_CELL_INSERT_BELOW);613} else {614runCell(editorGroupsService, context, editorService);615}616}617});618619class CancelNotebook extends NotebookAction {620override getEditorContextFromArgsOrActive(accessor: ServicesAccessor, context?: UriComponents): INotebookActionContext | undefined {621return getContextFromUri(accessor, context) ?? getContextFromActiveEditor(accessor.get(IEditorService));622}623624async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise<void> {625return context.notebookEditor.cancelNotebookCells();626}627}628629registerAction2(class CancelAllNotebook extends CancelNotebook {630constructor() {631super({632id: CANCEL_NOTEBOOK_COMMAND_ID,633title: localize2('notebookActions.cancelNotebook', "Stop Execution"),634icon: icons.stopIcon,635menu: [636{637id: MenuId.EditorTitle,638order: -1,639group: 'navigation',640when: ContextKeyExpr.and(641NOTEBOOK_IS_ACTIVE_EDITOR,642NOTEBOOK_HAS_SOMETHING_RUNNING,643NOTEBOOK_INTERRUPTIBLE_KERNEL.toNegated(),644ContextKeyExpr.notEquals('config.notebook.globalToolbar', true)645)646},647{648id: MenuId.NotebookToolbar,649order: -1,650group: 'navigation/execute',651when: ContextKeyExpr.and(652NOTEBOOK_HAS_SOMETHING_RUNNING,653NOTEBOOK_INTERRUPTIBLE_KERNEL.toNegated(),654ContextKeyExpr.equals('config.notebook.globalToolbar', true)655)656}657]658});659}660});661662registerAction2(class InterruptNotebook extends CancelNotebook {663constructor() {664super({665id: INTERRUPT_NOTEBOOK_COMMAND_ID,666title: localize2('notebookActions.interruptNotebook', "Interrupt"),667precondition: ContextKeyExpr.and(668NOTEBOOK_HAS_SOMETHING_RUNNING,669NOTEBOOK_INTERRUPTIBLE_KERNEL670),671icon: icons.stopIcon,672menu: [673{674id: MenuId.EditorTitle,675order: -1,676group: 'navigation',677when: ContextKeyExpr.and(678NOTEBOOK_IS_ACTIVE_EDITOR,679NOTEBOOK_HAS_SOMETHING_RUNNING,680NOTEBOOK_INTERRUPTIBLE_KERNEL,681ContextKeyExpr.notEquals('config.notebook.globalToolbar', true)682)683},684{685id: MenuId.NotebookToolbar,686order: -1,687group: 'navigation/execute',688when: ContextKeyExpr.and(689NOTEBOOK_HAS_SOMETHING_RUNNING,690NOTEBOOK_INTERRUPTIBLE_KERNEL,691ContextKeyExpr.equals('config.notebook.globalToolbar', true)692)693},694{695id: MenuId.InteractiveToolbar,696group: 'navigation/execute'697}698]699});700}701});702703704MenuRegistry.appendMenuItem(MenuId.NotebookToolbar, {705title: localize('revealRunningCellShort', "Go To"),706submenu: MenuId.NotebookCellExecuteGoTo,707group: 'navigation/execute',708order: 20,709icon: ThemeIcon.modify(icons.executingStateIcon, 'spin')710});711712registerAction2(class RevealRunningCellAction extends NotebookAction {713constructor() {714super({715id: REVEAL_RUNNING_CELL,716title: localize('revealRunningCell', "Go to Running Cell"),717tooltip: localize('revealRunningCell', "Go to Running Cell"),718shortTitle: localize('revealRunningCell', "Go to Running Cell"),719precondition: NOTEBOOK_HAS_RUNNING_CELL,720menu: [721{722id: MenuId.EditorTitle,723when: ContextKeyExpr.and(724NOTEBOOK_IS_ACTIVE_EDITOR,725NOTEBOOK_HAS_RUNNING_CELL,726ContextKeyExpr.notEquals('config.notebook.globalToolbar', true)727),728group: 'navigation',729order: 0730},731{732id: MenuId.NotebookCellExecuteGoTo,733when: ContextKeyExpr.and(734NOTEBOOK_IS_ACTIVE_EDITOR,735NOTEBOOK_HAS_RUNNING_CELL,736ContextKeyExpr.equals('config.notebook.globalToolbar', true)737),738group: 'navigation/execute',739order: 20740},741{742id: MenuId.InteractiveToolbar,743when: ContextKeyExpr.and(744NOTEBOOK_HAS_RUNNING_CELL,745ContextKeyExpr.equals('activeEditor', 'workbench.editor.interactive')746),747group: 'navigation',748order: 10749}750],751icon: ThemeIcon.modify(icons.executingStateIcon, 'spin')752});753}754755async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise<void> {756const notebookExecutionStateService = accessor.get(INotebookExecutionStateService);757const notebook = context.notebookEditor.textModel.uri;758const executingCells = notebookExecutionStateService.getCellExecutionsForNotebook(notebook);759if (executingCells[0]) {760const topStackFrameCell = this.findCellAtTopFrame(accessor, notebook);761const focusHandle = topStackFrameCell ?? executingCells[0].cellHandle;762const cell = context.notebookEditor.getCellByHandle(focusHandle);763if (cell) {764context.notebookEditor.focusNotebookCell(cell, 'container');765}766}767}768769private findCellAtTopFrame(accessor: ServicesAccessor, notebook: URI): number | undefined {770const debugService = accessor.get(IDebugService);771for (const session of debugService.getModel().getSessions()) {772for (const thread of session.getAllThreads()) {773const sf = thread.getTopStackFrame();774if (sf) {775const parsed = CellUri.parse(sf.source.uri);776if (parsed && parsed.notebook.toString() === notebook.toString()) {777return parsed.handle;778}779}780}781}782783return undefined;784}785});786787registerAction2(class RevealLastFailedCellAction extends NotebookAction {788constructor() {789super({790id: REVEAL_LAST_FAILED_CELL,791title: localize('revealLastFailedCell', "Go to Most Recently Failed Cell"),792tooltip: localize('revealLastFailedCell', "Go to Most Recently Failed Cell"),793shortTitle: localize('revealLastFailedCellShort', "Go to Most Recently Failed Cell"),794precondition: NOTEBOOK_LAST_CELL_FAILED,795menu: [796{797id: MenuId.EditorTitle,798when: ContextKeyExpr.and(799NOTEBOOK_IS_ACTIVE_EDITOR,800NOTEBOOK_LAST_CELL_FAILED,801NOTEBOOK_HAS_RUNNING_CELL.toNegated(),802ContextKeyExpr.notEquals('config.notebook.globalToolbar', true)803),804group: 'navigation',805order: 0806},807{808id: MenuId.NotebookCellExecuteGoTo,809when: ContextKeyExpr.and(810NOTEBOOK_IS_ACTIVE_EDITOR,811NOTEBOOK_LAST_CELL_FAILED,812NOTEBOOK_HAS_RUNNING_CELL.toNegated(),813ContextKeyExpr.equals('config.notebook.globalToolbar', true)814),815group: 'navigation/execute',816order: 20817},818],819icon: icons.errorStateIcon,820});821}822823async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise<void> {824const notebookExecutionStateService = accessor.get(INotebookExecutionStateService);825const notebook = context.notebookEditor.textModel.uri;826const lastFailedCellHandle = notebookExecutionStateService.getLastFailedCellForNotebook(notebook);827if (lastFailedCellHandle !== undefined) {828const lastFailedCell = context.notebookEditor.getCellByHandle(lastFailedCellHandle);829if (lastFailedCell) {830context.notebookEditor.focusNotebookCell(lastFailedCell, 'container');831}832}833}834});835836837