Path: blob/main/src/vs/workbench/contrib/notebook/browser/controller/editActions.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 { KeyChord, KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js';6import { Mimes } from '../../../../../base/common/mime.js';7import { URI } from '../../../../../base/common/uri.js';8import { ICodeEditor } from '../../../../../editor/browser/editorBrowser.js';9import { Selection } from '../../../../../editor/common/core/selection.js';10import { CommandExecutor } from '../../../../../editor/common/cursor/cursor.js';11import { EditorContextKeys } from '../../../../../editor/common/editorContextKeys.js';12import { ILanguageService } from '../../../../../editor/common/languages/language.js';13import { ILanguageConfigurationService } from '../../../../../editor/common/languages/languageConfigurationRegistry.js';14import { TrackedRangeStickiness } from '../../../../../editor/common/model.js';15import { getIconClasses } from '../../../../../editor/common/services/getIconClasses.js';16import { IModelService } from '../../../../../editor/common/services/model.js';17import { LineCommentCommand, Type } from '../../../../../editor/contrib/comment/browser/lineCommentCommand.js';18import { localize, localize2 } from '../../../../../nls.js';19import { MenuId, registerAction2 } from '../../../../../platform/actions/common/actions.js';20import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';21import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js';22import { InputFocusedContext, InputFocusedContextKey } from '../../../../../platform/contextkey/common/contextkeys.js';23import { IConfirmationResult, IDialogService } from '../../../../../platform/dialogs/common/dialogs.js';24import { IInstantiationService, ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js';25import { KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js';26import { INotificationService } from '../../../../../platform/notification/common/notification.js';27import { IQuickInputService, IQuickPickItem, QuickPickInput } from '../../../../../platform/quickinput/common/quickInput.js';28import { InlineChatController } from '../../../inlineChat/browser/inlineChatController.js';29import { CTX_INLINE_CHAT_FOCUSED } from '../../../inlineChat/common/inlineChat.js';30import { changeCellToKind, runDeleteAction } from './cellOperations.js';31import { CELL_TITLE_CELL_GROUP_ID, CELL_TITLE_OUTPUT_GROUP_ID, CellToolbarOrder, INotebookActionContext, INotebookCellActionContext, INotebookCommandContext, NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT, NotebookAction, NotebookCellAction, NotebookMultiCellAction, executeNotebookCondition, findTargetCellEditor } from './coreActions.js';32import { NotebookChangeTabDisplaySize, NotebookIndentUsingSpaces, NotebookIndentUsingTabs, NotebookIndentationToSpacesAction, NotebookIndentationToTabsAction } from './notebookIndentationActions.js';33import { CHANGE_CELL_LANGUAGE, CellEditState, DETECT_CELL_LANGUAGE, QUIT_EDIT_CELL_COMMAND_ID, getNotebookEditorFromEditorPane } from '../notebookBrowser.js';34import * as icons from '../notebookIcons.js';35import { CellEditType, CellKind, ICellEditOperation, NotebookCellExecutionState, NotebookSetting } from '../../common/notebookCommon.js';36import { NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_IS_FIRST_OUTPUT, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_HAS_OUTPUTS, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_OUTPUT_FOCUSED, NOTEBOOK_OUTPUT_INPUT_FOCUSED, NOTEBOOK_USE_CONSOLIDATED_OUTPUT_BUTTON } from '../../common/notebookContextKeys.js';37import { INotebookExecutionStateService } from '../../common/notebookExecutionStateService.js';38import { INotebookKernelService } from '../../common/notebookKernelService.js';39import { ICellRange } from '../../common/notebookRange.js';40import { IEditorService } from '../../../../services/editor/common/editorService.js';41import { ILanguageDetectionService } from '../../../../services/languageDetection/common/languageDetectionWorkerService.js';42import { NotebookInlineVariablesController } from '../contrib/notebookVariables/notebookInlineVariables.js';4344const CLEAR_ALL_CELLS_OUTPUTS_COMMAND_ID = 'notebook.clearAllCellsOutputs';45const EDIT_CELL_COMMAND_ID = 'notebook.cell.edit';46const DELETE_CELL_COMMAND_ID = 'notebook.cell.delete';47const QUIT_EDIT_ALL_CELLS_COMMAND_ID = 'notebook.quitEditAllCells';48export const CLEAR_CELL_OUTPUTS_COMMAND_ID = 'notebook.cell.clearOutputs';49export const SELECT_NOTEBOOK_INDENTATION_ID = 'notebook.selectIndentation';50export const COMMENT_SELECTED_CELLS_ID = 'notebook.commentSelectedCells';5152registerAction2(class EditCellAction extends NotebookCellAction {53constructor() {54super(55{56id: EDIT_CELL_COMMAND_ID,57title: localize('notebookActions.editCell', "Edit Cell"),58keybinding: {59when: ContextKeyExpr.and(60NOTEBOOK_CELL_LIST_FOCUSED,61ContextKeyExpr.not(InputFocusedContextKey),62EditorContextKeys.hoverFocused.toNegated(),63NOTEBOOK_OUTPUT_INPUT_FOCUSED.toNegated()64),65primary: KeyCode.Enter,66weight: KeybindingWeight.WorkbenchContrib67},68menu: {69id: MenuId.NotebookCellTitle,70when: ContextKeyExpr.and(71NOTEBOOK_EDITOR_EDITABLE.isEqualTo(true),72NOTEBOOK_CELL_TYPE.isEqualTo('markup'),73NOTEBOOK_CELL_MARKDOWN_EDIT_MODE.toNegated(),74NOTEBOOK_CELL_EDITABLE),75order: CellToolbarOrder.EditCell,76group: CELL_TITLE_CELL_GROUP_ID77},78icon: icons.editIcon,79});80}8182async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise<void> {83if (!context.notebookEditor.hasModel()) {84return;85}8687await context.notebookEditor.focusNotebookCell(context.cell, 'editor');88const foundEditor: ICodeEditor | undefined = context.cell ? findTargetCellEditor(context, context.cell) : undefined;89if (foundEditor && foundEditor.hasTextFocus() && InlineChatController.get(foundEditor)?.getWidgetPosition()?.lineNumber === foundEditor.getPosition()?.lineNumber) {90InlineChatController.get(foundEditor)?.focus();91}92}93});9495const quitEditCondition = ContextKeyExpr.and(96NOTEBOOK_EDITOR_FOCUSED,97InputFocusedContext,98CTX_INLINE_CHAT_FOCUSED.toNegated()99);100registerAction2(class QuitEditCellAction extends NotebookCellAction {101constructor() {102super(103{104id: QUIT_EDIT_CELL_COMMAND_ID,105title: localize('notebookActions.quitEdit', "Stop Editing Cell"),106menu: {107id: MenuId.NotebookCellTitle,108when: ContextKeyExpr.and(109NOTEBOOK_CELL_TYPE.isEqualTo('markup'),110NOTEBOOK_CELL_MARKDOWN_EDIT_MODE,111NOTEBOOK_CELL_EDITABLE),112order: CellToolbarOrder.SaveCell,113group: CELL_TITLE_CELL_GROUP_ID114},115icon: icons.stopEditIcon,116keybinding: [117{118when: ContextKeyExpr.and(quitEditCondition,119EditorContextKeys.hoverVisible.toNegated(),120EditorContextKeys.hasNonEmptySelection.toNegated(),121EditorContextKeys.hasMultipleSelections.toNegated()),122primary: KeyCode.Escape,123weight: NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT - 5124},125{126when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED,127NOTEBOOK_OUTPUT_FOCUSED),128primary: KeyCode.Escape,129weight: KeybindingWeight.WorkbenchContrib + 5130},131{132when: ContextKeyExpr.and(133quitEditCondition,134NOTEBOOK_CELL_TYPE.isEqualTo('markup')),135primary: KeyMod.WinCtrl | KeyCode.Enter,136win: {137primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Enter138},139weight: NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT - 5140},141]142});143}144145async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) {146if (context.cell.cellKind === CellKind.Markup) {147context.cell.updateEditState(CellEditState.Preview, QUIT_EDIT_CELL_COMMAND_ID);148}149150await context.notebookEditor.focusNotebookCell(context.cell, 'container', { skipReveal: true });151}152});153154registerAction2(class QuitEditAllCellsAction extends NotebookAction {155constructor() {156super(157{158id: QUIT_EDIT_ALL_CELLS_COMMAND_ID,159title: localize('notebookActions.quitEditAllCells', "Stop Editing All Cells")160});161}162163async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext) {164if (!context.notebookEditor.hasModel()) {165return;166}167168const viewModel = context.notebookEditor.getViewModel();169if (!viewModel) {170return;171}172173const activeCell = context.notebookEditor.getActiveCell();174175const editingCells = viewModel.viewCells.filter(cell =>176cell.cellKind === CellKind.Markup && cell.getEditState() === CellEditState.Editing177);178179editingCells.forEach(cell => {180cell.updateEditState(CellEditState.Preview, QUIT_EDIT_ALL_CELLS_COMMAND_ID);181});182183if (activeCell) {184await context.notebookEditor.focusNotebookCell(activeCell, 'container', { skipReveal: true });185}186}187});188189registerAction2(class DeleteCellAction extends NotebookCellAction {190constructor() {191super(192{193id: DELETE_CELL_COMMAND_ID,194title: localize('notebookActions.deleteCell', "Delete Cell"),195keybinding: {196primary: KeyCode.Delete,197mac: {198primary: KeyMod.CtrlCmd | KeyCode.Backspace199},200when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey), NOTEBOOK_OUTPUT_INPUT_FOCUSED.toNegated()),201weight: KeybindingWeight.WorkbenchContrib202},203menu: [204{205id: MenuId.NotebookCellDelete,206when: NOTEBOOK_EDITOR_EDITABLE,207group: CELL_TITLE_CELL_GROUP_ID208},209{210id: MenuId.InteractiveCellDelete,211group: CELL_TITLE_CELL_GROUP_ID212}213],214icon: icons.deleteCellIcon215});216}217218async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext) {219if (!context.notebookEditor.hasModel()) {220return;221}222223let confirmation: IConfirmationResult;224const notebookExecutionStateService = accessor.get(INotebookExecutionStateService);225const runState = notebookExecutionStateService.getCellExecution(context.cell.uri)?.state;226const configService = accessor.get(IConfigurationService);227228if (runState === NotebookCellExecutionState.Executing && configService.getValue(NotebookSetting.confirmDeleteRunningCell)) {229const dialogService = accessor.get(IDialogService);230const primaryButton = localize('confirmDeleteButton', "Delete");231232confirmation = await dialogService.confirm({233type: 'question',234message: localize('confirmDeleteButtonMessage', "This cell is running, are you sure you want to delete it?"),235primaryButton: primaryButton,236checkbox: {237label: localize('doNotAskAgain', "Do not ask me again")238}239});240241} else {242confirmation = { confirmed: true };243}244245if (!confirmation.confirmed) {246return;247}248249if (confirmation.checkboxChecked === true) {250await configService.updateValue(NotebookSetting.confirmDeleteRunningCell, false);251}252253runDeleteAction(context.notebookEditor, context.cell);254}255});256257registerAction2(class ClearCellOutputsAction extends NotebookCellAction {258constructor() {259super({260id: CLEAR_CELL_OUTPUTS_COMMAND_ID,261title: localize('clearCellOutputs', 'Clear Cell Outputs'),262menu: [263{264id: MenuId.NotebookCellTitle,265when: ContextKeyExpr.and(NOTEBOOK_CELL_TYPE.isEqualTo('code'), executeNotebookCondition, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_USE_CONSOLIDATED_OUTPUT_BUTTON.toNegated()),266order: CellToolbarOrder.ClearCellOutput,267group: CELL_TITLE_OUTPUT_GROUP_ID268},269{270id: MenuId.NotebookOutputToolbar,271when: ContextKeyExpr.and(NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_IS_FIRST_OUTPUT, NOTEBOOK_USE_CONSOLIDATED_OUTPUT_BUTTON)272},273],274keybinding: {275when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey), NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE),276primary: KeyMod.Alt | KeyCode.Delete,277weight: KeybindingWeight.WorkbenchContrib278},279icon: icons.clearIcon280});281}282283async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise<void> {284const notebookExecutionStateService = accessor.get(INotebookExecutionStateService);285const editor = context.notebookEditor;286if (!editor.hasModel() || !editor.textModel.length) {287return;288}289290const cell = context.cell;291const index = editor.textModel.cells.indexOf(cell.model);292293if (index < 0) {294return;295}296297const computeUndoRedo = !editor.isReadOnly;298editor.textModel.applyEdits([{ editType: CellEditType.Output, index, outputs: [] }], true, undefined, () => undefined, undefined, computeUndoRedo);299300const runState = notebookExecutionStateService.getCellExecution(context.cell.uri)?.state;301if (runState !== NotebookCellExecutionState.Executing) {302context.notebookEditor.textModel.applyEdits([{303editType: CellEditType.PartialInternalMetadata, index, internalMetadata: {304runStartTime: null,305runStartTimeAdjustment: null,306runEndTime: null,307executionOrder: null,308lastRunSuccess: null309}310}], true, undefined, () => undefined, undefined, computeUndoRedo);311}312}313});314315registerAction2(class ClearAllCellOutputsAction extends NotebookAction {316constructor() {317super({318id: CLEAR_ALL_CELLS_OUTPUTS_COMMAND_ID,319title: localize('clearAllCellsOutputs', 'Clear All Outputs'),320precondition: NOTEBOOK_HAS_OUTPUTS,321menu: [322{323id: MenuId.EditorTitle,324when: ContextKeyExpr.and(325NOTEBOOK_IS_ACTIVE_EDITOR,326ContextKeyExpr.notEquals('config.notebook.globalToolbar', true)327),328group: 'navigation',329order: 0330},331{332id: MenuId.NotebookToolbar,333when: ContextKeyExpr.and(334executeNotebookCondition,335ContextKeyExpr.equals('config.notebook.globalToolbar', true)336),337group: 'navigation/execute',338order: 10339}340],341icon: icons.clearIcon342});343}344345async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise<void> {346const notebookExecutionStateService = accessor.get(INotebookExecutionStateService);347const editor = context.notebookEditor;348if (!editor.hasModel() || !editor.textModel.length) {349return;350}351352const computeUndoRedo = !editor.isReadOnly;353editor.textModel.applyEdits(354editor.textModel.cells.map((cell, index) => ({355editType: CellEditType.Output, index, outputs: []356})), true, undefined, () => undefined, undefined, computeUndoRedo);357358const clearExecutionMetadataEdits = editor.textModel.cells.map((cell, index) => {359const runState = notebookExecutionStateService.getCellExecution(cell.uri)?.state;360if (runState !== NotebookCellExecutionState.Executing) {361return {362editType: CellEditType.PartialInternalMetadata, index, internalMetadata: {363runStartTime: null,364runStartTimeAdjustment: null,365runEndTime: null,366executionOrder: null,367lastRunSuccess: null368}369};370} else {371return undefined;372}373}).filter(edit => !!edit) as ICellEditOperation[];374if (clearExecutionMetadataEdits.length) {375context.notebookEditor.textModel.applyEdits(clearExecutionMetadataEdits, true, undefined, () => undefined, undefined, computeUndoRedo);376}377378const controller = editor.getContribution<NotebookInlineVariablesController>(NotebookInlineVariablesController.id);379controller.clearNotebookInlineDecorations();380}381});382383interface ILanguagePickInput extends IQuickPickItem {384languageId: string;385description: string;386}387388interface IChangeCellContext extends INotebookCellActionContext {389// TODO@rebornix : `cells`390// range: ICellRange;391language?: string;392}393394registerAction2(class ChangeCellLanguageAction extends NotebookCellAction<ICellRange> {395constructor() {396super({397id: CHANGE_CELL_LANGUAGE,398title: localize('changeLanguage', 'Change Cell Language'),399keybinding: {400weight: KeybindingWeight.WorkbenchContrib,401primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyCode.KeyM),402when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE)403},404metadata: {405description: localize('changeLanguage', 'Change Cell Language'),406args: [407{408name: 'range',409description: 'The cell range',410schema: {411'type': 'object',412'required': ['start', 'end'],413'properties': {414'start': {415'type': 'number'416},417'end': {418'type': 'number'419}420}421}422},423{424name: 'language',425description: 'The target cell language',426schema: {427'type': 'string'428}429}430]431}432});433}434435protected override getCellContextFromArgs(accessor: ServicesAccessor, context?: ICellRange, ...additionalArgs: any[]): IChangeCellContext | undefined {436if (!context || typeof context.start !== 'number' || typeof context.end !== 'number' || context.start >= context.end) {437return;438}439440const language = additionalArgs.length && typeof additionalArgs[0] === 'string' ? additionalArgs[0] : undefined;441const activeEditorContext = this.getEditorContextFromArgsOrActive(accessor);442443if (!activeEditorContext || !activeEditorContext.notebookEditor.hasModel() || context.start >= activeEditorContext.notebookEditor.getLength()) {444return;445}446447// TODO@rebornix, support multiple cells448return {449notebookEditor: activeEditorContext.notebookEditor,450cell: activeEditorContext.notebookEditor.cellAt(context.start)!,451language452};453}454455456async runWithContext(accessor: ServicesAccessor, context: IChangeCellContext): Promise<void> {457if (context.language) {458await this.setLanguage(context, context.language);459} else {460await this.showLanguagePicker(accessor, context);461}462}463464private async showLanguagePicker(accessor: ServicesAccessor, context: IChangeCellContext) {465const topItems: ILanguagePickInput[] = [];466const mainItems: ILanguagePickInput[] = [];467468const languageService = accessor.get(ILanguageService);469const modelService = accessor.get(IModelService);470const quickInputService = accessor.get(IQuickInputService);471const languageDetectionService = accessor.get(ILanguageDetectionService);472const kernelService = accessor.get(INotebookKernelService);473474let languages = context.notebookEditor.activeKernel?.supportedLanguages;475if (!languages) {476const matchResult = kernelService.getMatchingKernel(context.notebookEditor.textModel);477const allSupportedLanguages = matchResult.all.flatMap(kernel => kernel.supportedLanguages);478languages = allSupportedLanguages.length > 0 ? allSupportedLanguages : languageService.getRegisteredLanguageIds();479}480481const providerLanguages = new Set([482...languages,483'markdown'484]);485486providerLanguages.forEach(languageId => {487let description: string;488if (context.cell.cellKind === CellKind.Markup ? (languageId === 'markdown') : (languageId === context.cell.language)) {489description = localize('languageDescription', "({0}) - Current Language", languageId);490} else {491description = localize('languageDescriptionConfigured', "({0})", languageId);492}493494const languageName = languageService.getLanguageName(languageId);495if (!languageName) {496// Notebook has unrecognized language497return;498}499500const item: ILanguagePickInput = {501label: languageName,502iconClasses: getIconClasses(modelService, languageService, this.getFakeResource(languageName, languageService)),503description,504languageId505};506507if (languageId === 'markdown' || languageId === context.cell.language) {508topItems.push(item);509} else {510mainItems.push(item);511}512});513514mainItems.sort((a, b) => {515return a.description.localeCompare(b.description);516});517518// Offer to "Auto Detect"519const autoDetectMode: IQuickPickItem = {520label: localize('autoDetect', "Auto Detect")521};522523const picks: QuickPickInput[] = [524autoDetectMode,525{ type: 'separator', label: localize('languagesPicks', "languages (identifier)") },526...topItems,527{ type: 'separator' },528...mainItems529];530531const selection = await quickInputService.pick(picks, { placeHolder: localize('pickLanguageToConfigure', "Select Language Mode") });532const languageId = selection === autoDetectMode533? await languageDetectionService.detectLanguage(context.cell.uri)534: (selection as ILanguagePickInput)?.languageId;535536if (languageId) {537await this.setLanguage(context, languageId);538}539}540541private async setLanguage(context: IChangeCellContext, languageId: string) {542await setCellToLanguage(languageId, context);543}544545/**546* Copied from editorStatus.ts547*/548private getFakeResource(lang: string, languageService: ILanguageService): URI | undefined {549let fakeResource: URI | undefined;550551const languageId = languageService.getLanguageIdByLanguageName(lang);552if (languageId) {553const extensions = languageService.getExtensions(languageId);554if (extensions.length) {555fakeResource = URI.file(extensions[0]);556} else {557const filenames = languageService.getFilenames(languageId);558if (filenames.length) {559fakeResource = URI.file(filenames[0]);560}561}562}563564return fakeResource;565}566});567568registerAction2(class DetectCellLanguageAction extends NotebookCellAction {569constructor() {570super({571id: DETECT_CELL_LANGUAGE,572title: localize2('detectLanguage', "Accept Detected Language for Cell"),573f1: true,574precondition: ContextKeyExpr.and(NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE),575keybinding: { primary: KeyCode.KeyD | KeyMod.Alt | KeyMod.Shift, weight: KeybindingWeight.WorkbenchContrib }576});577}578579async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise<void> {580const languageDetectionService = accessor.get(ILanguageDetectionService);581const notificationService = accessor.get(INotificationService);582const kernelService = accessor.get(INotebookKernelService);583const kernel = kernelService.getSelectedOrSuggestedKernel(context.notebookEditor.textModel);584const providerLanguages = [...kernel?.supportedLanguages ?? []];585providerLanguages.push('markdown');586const detection = await languageDetectionService.detectLanguage(context.cell.uri, providerLanguages);587if (detection) {588setCellToLanguage(detection, context);589} else {590notificationService.warn(localize('noDetection', "Unable to detect cell language"));591}592}593});594595async function setCellToLanguage(languageId: string, context: IChangeCellContext) {596if (languageId === 'markdown' && context.cell?.language !== 'markdown') {597const idx = context.notebookEditor.getCellIndex(context.cell);598await changeCellToKind(CellKind.Markup, { cell: context.cell, notebookEditor: context.notebookEditor, ui: true }, 'markdown', Mimes.markdown);599const newCell = context.notebookEditor.cellAt(idx);600601if (newCell) {602await context.notebookEditor.focusNotebookCell(newCell, 'editor');603}604} else if (languageId !== 'markdown' && context.cell?.cellKind === CellKind.Markup) {605await changeCellToKind(CellKind.Code, { cell: context.cell, notebookEditor: context.notebookEditor, ui: true }, languageId);606} else {607const index = context.notebookEditor.textModel.cells.indexOf(context.cell.model);608context.notebookEditor.textModel.applyEdits(609[{ editType: CellEditType.CellLanguage, index, language: languageId }],610true, undefined, () => undefined, undefined, !context.notebookEditor.isReadOnly611);612}613}614615registerAction2(class SelectNotebookIndentation extends NotebookAction {616constructor() {617super({618id: SELECT_NOTEBOOK_INDENTATION_ID,619title: localize2('selectNotebookIndentation', 'Select Indentation'),620f1: true,621precondition: ContextKeyExpr.and(NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE),622});623}624625async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise<void> {626await this.showNotebookIndentationPicker(accessor, context);627}628629private async showNotebookIndentationPicker(accessor: ServicesAccessor, context: INotebookActionContext) {630const quickInputService = accessor.get(IQuickInputService);631const editorService = accessor.get(IEditorService);632const instantiationService = accessor.get(IInstantiationService);633634const activeNotebook = getNotebookEditorFromEditorPane(editorService.activeEditorPane);635if (!activeNotebook || activeNotebook.isDisposed) {636return quickInputService.pick([{ label: localize('noNotebookEditor', "No notebook editor active at this time") }]);637}638639if (activeNotebook.isReadOnly) {640return quickInputService.pick([{ label: localize('noWritableCodeEditor', "The active notebook editor is read-only.") }]);641}642643const picks: QuickPickInput<IQuickPickItem & { run(): void }>[] = [644new NotebookIndentUsingTabs(), // indent using tabs645new NotebookIndentUsingSpaces(), // indent using spaces646new NotebookChangeTabDisplaySize(), // change tab size647new NotebookIndentationToTabsAction(), // convert indentation to tabs648new NotebookIndentationToSpacesAction() // convert indentation to spaces649].map(item => {650return {651id: item.desc.id,652label: item.desc.title.toString(),653run: () => {654instantiationService.invokeFunction(item.run);655}656};657});658659picks.splice(3, 0, { type: 'separator', label: localize('indentConvert', "convert file") });660picks.unshift({ type: 'separator', label: localize('indentView', "change view") });661662const action = await quickInputService.pick(picks, { placeHolder: localize('pickAction', "Select Action"), matchOnDetail: true });663if (!action) {664return;665}666action.run();667context.notebookEditor.focus();668return;669}670});671672registerAction2(class CommentSelectedCellsAction extends NotebookMultiCellAction {673constructor() {674super({675id: COMMENT_SELECTED_CELLS_ID,676title: localize('commentSelectedCells', "Comment Selected Cells"),677keybinding: {678when: ContextKeyExpr.and(679NOTEBOOK_EDITOR_FOCUSED,680NOTEBOOK_EDITOR_EDITABLE,681ContextKeyExpr.not(InputFocusedContextKey),682),683primary: KeyMod.CtrlCmd | KeyCode.Slash,684weight: KeybindingWeight.WorkbenchContrib685}686});687}688689async runWithContext(accessor: ServicesAccessor, context: INotebookCommandContext): Promise<void> {690const languageConfigurationService = accessor.get(ILanguageConfigurationService);691692context.selectedCells.forEach(async cellViewModel => {693const textModel = await cellViewModel.resolveTextModel();694695const commentsOptions = cellViewModel.commentOptions;696const cellCommentCommand = new LineCommentCommand(697languageConfigurationService,698new Selection(1, 1, textModel.getLineCount(), textModel.getLineMaxColumn(textModel.getLineCount())), // comment the entire cell699textModel.getOptions().tabSize,700Type.Toggle,701commentsOptions.insertSpace ?? true,702commentsOptions.ignoreEmptyLines ?? true,703false704);705706// store any selections that are in the cell, allows them to be shifted by comments and preserved707const cellEditorSelections = cellViewModel.getSelections();708const initialTrackedRangesIDs: string[] = cellEditorSelections.map(selection => {709return textModel._setTrackedRange(null, selection, TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges);710});711712CommandExecutor.executeCommands(textModel, cellEditorSelections, [cellCommentCommand]);713714const newTrackedSelections = initialTrackedRangesIDs.map(i => {715return textModel._getTrackedRange(i);716}).filter(r => !!r).map((range,) => {717return new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn);718});719cellViewModel.setSelections(newTrackedSelections ?? []);720}); // end of cells forEach721}722723});724725726