提交 4f94d116 编写于 作者: R rebornix

hook up undo redo with edits from extension host

上级 ccffe896
......@@ -6,7 +6,7 @@
import { EditorInput, EditorModel, IEditorInput, GroupIdentifier, ISaveOptions, IRevertOptions } from 'vs/workbench/common/editor';
import { Emitter, Event } from 'vs/base/common/event';
import { INotebookService } from 'vs/workbench/contrib/notebook/browser/notebookService';
import { ICell, NotebookCellsSplice } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { ICell, NotebookCellTextModelSplice } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { URI } from 'vs/base/common/uri';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
......@@ -18,8 +18,8 @@ export class NotebookEditorModel extends EditorModel {
protected readonly _onDidChangeDirty = this._register(new Emitter<void>());
readonly onDidChangeDirty = this._onDidChangeDirty.event;
private readonly _onDidChangeCells = new Emitter<NotebookCellsSplice[]>();
get onDidChangeCells(): Event<NotebookCellsSplice[]> { return this._onDidChangeCells.event; }
private readonly _onDidChangeCells = new Emitter<NotebookCellTextModelSplice[]>();
get onDidChangeCells(): Event<NotebookCellTextModelSplice[]> { return this._onDidChangeCells.event; }
get notebook() {
......
......@@ -7,13 +7,14 @@ import { ICell } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { IResourceUndoRedoElement, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo';
import { URI } from 'vs/base/common/uri';
import { BaseCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel';
import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
/**
* It should not modify Undo/Redo stack
*/
export interface ICellEditingDelegate {
insertCell?(index: number, viewCell: BaseCellViewModel): void;
deleteCell?(index: number, cell: ICell): void;
deleteCell?(index: number): void;
moveCell?(fromIndex: number, toIndex: number): void;
createCellViewModel?(cell: ICell): BaseCellViewModel;
}
......@@ -34,7 +35,7 @@ export class InsertCellEdit implements IResourceUndoRedoElement {
throw new Error('Notebook Delete Cell not implemented for Undo/Redo');
}
this.editingDelegate.deleteCell(this.insertIndex, this.cell.model);
this.editingDelegate.deleteCell(this.insertIndex);
}
redo(): void | Promise<void> {
if (!this.editingDelegate.insertCell) {
......@@ -76,7 +77,7 @@ export class DeleteCellEdit implements IResourceUndoRedoElement {
throw new Error('Notebook Delete Cell not implemented for Undo/Redo');
}
this.editingDelegate.deleteCell(this.insertIndex, this._rawCell);
this.editingDelegate.deleteCell(this.insertIndex);
}
}
......@@ -108,3 +109,47 @@ export class MoveCellEdit implements IResourceUndoRedoElement {
this.editingDelegate.moveCell(this.fromIndex, this.toIndex);
}
}
export class SpliceCellsEdit implements IResourceUndoRedoElement {
type: UndoRedoElementType.Resource = UndoRedoElementType.Resource;
label: string = 'Insert Cell';
constructor(
public resource: URI,
private diffs: [number, CellViewModel[], CellViewModel[]][],
private editingDelegate: ICellEditingDelegate
) {
}
undo(): void | Promise<void> {
if (!this.editingDelegate.deleteCell || !this.editingDelegate.insertCell) {
throw new Error('Notebook Insert/Delete Cell not implemented for Undo/Redo');
}
this.diffs.forEach(diff => {
for (let i = 0; i < diff[2].length; i++) {
this.editingDelegate.deleteCell!(diff[0]);
}
diff[1].reverse().forEach(cell => {
this.editingDelegate.insertCell!(diff[0], cell);
});
});
}
redo(): void | Promise<void> {
if (!this.editingDelegate.deleteCell || !this.editingDelegate.insertCell) {
throw new Error('Notebook Insert/Delete Cell not implemented for Undo/Redo');
}
this.diffs.reverse().forEach(diff => {
for (let i = 0; i < diff[1].length; i++) {
this.editingDelegate.deleteCell!(diff[0]);
}
diff[2].reverse().forEach(cell => {
this.editingDelegate.insertCell!(diff[0], cell);
});
});
}
}
......@@ -20,13 +20,13 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
import { CellEditState, CellFindMatch, ICellRange, ICellViewModel, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { NotebookEditorModel } from 'vs/workbench/contrib/notebook/browser/notebookEditorInput';
import { DeleteCellEdit, InsertCellEdit, MoveCellEdit } from 'vs/workbench/contrib/notebook/browser/viewModel/cellEdit';
import { DeleteCellEdit, InsertCellEdit, MoveCellEdit, SpliceCellsEdit } from 'vs/workbench/contrib/notebook/browser/viewModel/cellEdit';
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
import { NotebookEventDispatcher, NotebookMetadataChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher';
import { CellFoldingState, EditorFoldingStateDelegate } from 'vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel';
import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { CellKind, ICell } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { FoldingRegions } from 'vs/editor/contrib/folding/foldingRanges';
export interface INotebookEditorViewState {
......@@ -227,6 +227,12 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD
})] as [number, number, CellViewModel[]];
});
const undoDiff = diffs.map(diff => {
const deletedCells = this.viewCells.slice(diff[0], diff[0] + diff[1]);
return [diff[0], deletedCells, diff[2]] as [number, CellViewModel[], CellViewModel[]];
});
diffs.reverse().forEach(diff => {
this._viewCells.splice(diff[0], diff[1], ...diff[2]);
diff[2].forEach(cell => {
......@@ -239,6 +245,11 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD
synchronous: true,
splices: diffs
});
this.undoService.pushElement(new SpliceCellsEdit(this.uri, undoDiff, {
insertCell: this._insertCellDelegate.bind(this),
deleteCell: this._deleteCellDelegate.bind(this)
}));
}));
this._register(this._model.notebook.onDidChangeMetadata(e => {
......@@ -486,7 +497,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD
this._onDidChangeViewCells.fire({ synchronous: true, splices: [[insertIndex, 0, [insertCell]]] });
}
private _deleteCellDelegate(deleteIndex: number, cell: ICell) {
private _deleteCellDelegate(deleteIndex: number) {
const deleteCell = this._viewCells[deleteIndex];
this._viewCells.splice(deleteIndex, 1);
this._handleToViewCellMapping.delete(deleteCell.handle);
......
......@@ -7,15 +7,15 @@ import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { INotebookTextModel, NotebookCellOutputsSplice, NotebookCellsSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, CellEditType, CellUri, ICellInsertEdit, NotebookCellsChangedEvent, CellKind, IOutput, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookTextModel, NotebookCellOutputsSplice, NotebookCellTextModelSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, CellEditType, CellUri, ICellInsertEdit, NotebookCellsChangedEvent, CellKind, IOutput, notebookDocumentMetadataDefaults, diff } from 'vs/workbench/contrib/notebook/common/notebookCommon';
export class NotebookTextModel extends Disposable implements INotebookTextModel {
private static _cellhandlePool: number = 0;
private readonly _onWillDispose: Emitter<void> = this._register(new Emitter<void>());
readonly onWillDispose: Event<void> = this._onWillDispose.event;
private readonly _onDidChangeCells = new Emitter<NotebookCellsSplice[]>();
get onDidChangeCells(): Event<NotebookCellsSplice[]> { return this._onDidChangeCells.event; }
private readonly _onDidChangeCells = new Emitter<NotebookCellTextModelSplice[]>();
get onDidChangeCells(): Event<NotebookCellTextModelSplice[]> { return this._onDidChangeCells.event; }
private _onDidModelChangeProxy = new Emitter<NotebookCellsChangedEvent>();
get onDidModelChange(): Event<NotebookCellsChangedEvent> { return this._onDidModelChangeProxy.event; }
private _onDidChangeContent = new Emitter<void>();
......@@ -61,6 +61,10 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
return false;
}
const oldViewCells = this.cells.slice(0);
const oldMap = new Map(this._mapping);
edits = edits.reverse();
for (let i = 0; i < edits.length; i++) {
switch (edits[i].editType) {
case CellEditType.Insert:
......@@ -70,14 +74,21 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
const cellUri = CellUri.generate(this.uri, cellHandle);
return new NotebookCellTextModel(URI.revive(cellUri), cellHandle, cell.source, cell.language, cell.cellKind, cell.outputs || [], cell.metadata);
});
this.insertNewCell(insertEdit.index, mainCells, true);
this.insertNewCell(insertEdit.index, mainCells);
break;
case CellEditType.Delete:
this.removeCell(edits[i].index, true);
this.removeCell(edits[i].index);
break;
}
}
const diffs = diff(oldViewCells, this.cells, cell => {
return oldMap.has(cell.handle);
}).map(diff => {
return [diff.start, diff.deleteCount, diff.toInsert] as [number, number, NotebookCellTextModel[]];
});
this._onDidChangeCells.fire(diffs);
return true;
}
......@@ -147,12 +158,11 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
]
]
});
this._onDidChangeCells.fire([[0, 0, [cell]]]);
return;
}
insertNewCell(index: number, cells: NotebookCellTextModel[], emitModelChangeToView: boolean = false): void {
insertNewCell(index: number, cells: NotebookCellTextModel[]): void {
this._isUntitled = false;
for (let i = 0; i < cells.length; i++) {
......@@ -185,14 +195,10 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
]
});
if (emitModelChangeToView) {
this._onDidChangeCells.fire([[index, 0, cells]]);
}
return;
}
removeCell(index: number, emitModelChangeToView: boolean = false) {
removeCell(index: number) {
this._isUntitled = false;
let cell = this.cells[index];
......@@ -203,9 +209,6 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
this._increaseVersionId();
this._onDidModelChangeProxy.fire({ versionId: this._versionId, changes: [[index, 1, []]] });
if (emitModelChangeToView) {
this._onDidChangeCells.fire([[index, 1, []]]);
}
}
// TODO@rebornix should this trigger content change event?
......
......@@ -177,7 +177,7 @@ export interface INotebookTextModel {
languages: string[];
cells: ICell[];
renderers: Set<number>;
onDidChangeCells?: Event<NotebookCellsSplice[]>;
onDidChangeCells?: Event<NotebookCellTextModelSplice[]>;
onDidChangeContent: Event<void>;
onWillDispose(listener: () => void): IDisposable;
}
......@@ -187,9 +187,9 @@ export interface IRenderOutput {
hasDynamicHeight: boolean;
}
export type NotebookCellsSplice = [
export type NotebookCellTextModelSplice = [
number /* start */,
number /* delete count */,
number,
ICell[]
];
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册