From 1651f3f8bc7b36818caf2c39239c4d1c93815c04 Mon Sep 17 00:00:00 2001 From: Barbara Valdez <34872381+barbaravaldez@users.noreply.github.com> Date: Tue, 7 Dec 2021 15:51:33 -0800 Subject: [PATCH] Add undo/redo to convert cells (#17835) * add undo/redo to convert cells --- .../test/browser/cellToolbarActions.test.ts | 21 ++++++++++++++++++- .../notebook/browser/models/cellEdit.ts | 20 ++++++++++++++++++ .../notebook/browser/models/notebookModel.ts | 7 +++++-- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/sql/workbench/contrib/notebook/test/browser/cellToolbarActions.test.ts b/src/sql/workbench/contrib/notebook/test/browser/cellToolbarActions.test.ts index c9820150c5..da3dab4ce0 100644 --- a/src/sql/workbench/contrib/notebook/test/browser/cellToolbarActions.test.ts +++ b/src/sql/workbench/contrib/notebook/test/browser/cellToolbarActions.test.ts @@ -11,6 +11,8 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { TestLifecycleService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService'; +import { NullAdsTelemetryService } from 'sql/platform/telemetry/common/adsTelemetryService'; import { CellContext } from 'sql/workbench/contrib/notebook/browser/cellViews/codeActions'; import { INotebookService } from 'sql/workbench/services/notebook/browser/notebookService'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; @@ -30,6 +32,8 @@ import { nb } from 'azdata'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { ExecuteManagerStub, SerializationManagerStub } from 'sql/workbench/contrib/notebook/test/stubs'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { UndoRedoService } from 'vs/platform/undoRedo/common/undoRedoService'; suite('CellToolbarActions', function (): void { suite('removeDuplicatedAndStartingSeparators', function (): void { @@ -178,6 +182,17 @@ suite('CellToolbarActions', function (): void { await convertCellAction.doRun({ model: notebookModel, cell: notebookModel.cells[0] }); assert.strictEqual(notebookModel.cells[0].cellType, 'markdown', 'Cell was not converted correctly second time'); }); + + test('Undo/redo convert cell', async function (): Promise { + await notebookModel.loadContents(); + notebookModel.cells[0].cellType = 'markdown'; + await convertCellAction.doRun({ model: notebookModel, cell: notebookModel.cells[0] }); + assert.strictEqual(notebookModel.cells[0].cellType, 'code', 'Cell was not converted correctly'); + notebookModel.undo(); + assert.strictEqual(notebookModel.cells[0].cellType, 'markdown', 'Undo not converting cell correctly'); + notebookModel.redo(); + assert.strictEqual(notebookModel.cells[0].cellType, 'code', 'Redo not converting cell correctly'); + }); }); }); @@ -203,6 +218,10 @@ export async function createandLoadNotebookModel(codeContent?: nb.INotebookConte let serviceCollection = new ServiceCollection(); let instantiationService = new InstantiationService(serviceCollection, true); let mockContentManager = TypeMoq.Mock.ofType(NotebookEditorContentLoader); + let dialogService = TypeMoq.Mock.ofType(TestDialogService, TypeMoq.MockBehavior.Loose); + let notificationService = TypeMoq.Mock.ofType(TestNotificationService, TypeMoq.MockBehavior.Loose); + let undoRedoService = new UndoRedoService(dialogService.object, notificationService.object); + mockContentManager.setup(c => c.loadContent()).returns(() => Promise.resolve(codeContent ? codeContent : defaultCodeContent)); let defaultModelOptions: INotebookModelOptions = { notebookUri: URI.file('/some/path.ipynb'), @@ -218,5 +237,5 @@ export async function createandLoadNotebookModel(codeContent?: nb.INotebookConte layoutChanged: undefined, capabilitiesService: undefined }; - return new NotebookModel(defaultModelOptions, undefined, undefined, undefined, undefined, undefined, undefined, undefined); + return new NotebookModel(defaultModelOptions, undefined, undefined, undefined, new NullAdsTelemetryService(), undefined, undefined, undoRedoService); } diff --git a/src/sql/workbench/services/notebook/browser/models/cellEdit.ts b/src/sql/workbench/services/notebook/browser/models/cellEdit.ts index 437258c4a8..25e6aa61c2 100644 --- a/src/sql/workbench/services/notebook/browser/models/cellEdit.ts +++ b/src/sql/workbench/services/notebook/browser/models/cellEdit.ts @@ -88,3 +88,23 @@ export class AddCellEdit implements IResourceUndoRedoElement { this.model.sendNotebookTelemetryActionEvent(TelemetryKeys.NbTelemetryAction.RedoCell, this.cellOperation); } } + +export class ConvertCellTypeEdit implements IResourceUndoRedoElement { + type: UndoRedoElementType.Resource = UndoRedoElementType.Resource; + label: string = localize('convertCellTypeEdit', "Convert Cell Type"); + resource = this.model.notebookUri; + private readonly cellOperation = { cell_operation: 'convert_cell_type' }; + + constructor(private model: NotebookModel, private cell: ICellModel) { + } + + undo(): void { + this.model.convertCellType(this.cell, false); + this.model.sendNotebookTelemetryActionEvent(TelemetryKeys.NbTelemetryAction.UndoCell, this.cellOperation); + } + + redo(): void { + this.model.convertCellType(this.cell, false); + this.model.sendNotebookTelemetryActionEvent(TelemetryKeys.NbTelemetryAction.RedoCell, this.cellOperation); + } +} diff --git a/src/sql/workbench/services/notebook/browser/models/notebookModel.ts b/src/sql/workbench/services/notebook/browser/models/notebookModel.ts index ed175fba9c..a5568a620d 100644 --- a/src/sql/workbench/services/notebook/browser/models/notebookModel.ts +++ b/src/sql/workbench/services/notebook/browser/models/notebookModel.ts @@ -35,7 +35,7 @@ import { isUUID } from 'vs/base/common/uuid'; import { TextModel } from 'vs/editor/common/model/textModel'; import { QueryTextEditor } from 'sql/workbench/browser/modelComponents/queryTextEditor'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; -import { AddCellEdit, DeleteCellEdit, MoveCellEdit, SplitCellEdit } from 'sql/workbench/services/notebook/browser/models/cellEdit'; +import { AddCellEdit, ConvertCellTypeEdit, DeleteCellEdit, MoveCellEdit, SplitCellEdit } from 'sql/workbench/services/notebook/browser/models/cellEdit'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { deepClone } from 'vs/base/common/objects'; @@ -796,10 +796,13 @@ export class NotebookModel extends Disposable implements INotebookModel { this._onActiveCellChanged.fire(cell); } - public convertCellType(cell: ICellModel): void { + public convertCellType(cell: ICellModel, addToUndoStack: boolean = true): void { if (cell) { let index = this.findCellIndex(cell); if (index > -1) { + if (addToUndoStack) { + this.undoService.pushElement(new ConvertCellTypeEdit(this, cell)); + } // Ensure override language is reset cell.setOverrideLanguage(''); cell.cellType = cell.cellType === CellTypes.Markdown ? CellTypes.Code : CellTypes.Markdown;