import { assert } from "@fluidframework/common-utils" ; import { TrackingGroup , MergeTreeDeltaOperationType , MergeTreeDeltaType } from "@fluidframework/merge-tree" ; import { MatrixItem , SharedMatrix } from "./matrix" ; import { Handle , isHandleValid } from "./handletable" ; import { PermutationSegment , PermutationVector } from "./permutationvector" ; import { IUndoConsumer } from "./types" ; export class VectorUndoProvider { private currentGroup ? : TrackingGroup ; private currentOp ? : MergeTreeDeltaType ; constructor ( private readonly manager , private readonly undoInsert , private readonly undoRemove ) { } public record ( operation , ranges ) { if ( ranges . length > 0 ) { const trackingGroup = this . currentGroup ?? new TrackingGroup ( ) ; for ( const range of ranges ) { trackingGroup . link ( range . segment ) ; } assert ( this . currentOp === undefined || this . currentOp === operation , 0x02a ) ; switch ( operation ) { case MergeTreeDeltaType . INSERT : if ( this . currentOp !== MergeTreeDeltaType . INSERT ) { this . pushRevertible ( trackingGroup , this . undoInsert ) ; } break ; case MergeTreeDeltaType . REMOVE : { if ( this . currentOp !== MergeTreeDeltaType . REMOVE ) { this . pushRevertible ( trackingGroup , this . undoRemove ) ; } break ; } default : throw new Error ( "operation type not revertible" ) ; } if ( this . currentGroup !== undefined ) { this . currentOp = operation ; } } } private pushRevertible ( trackingGroup , callback ) { const revertible = { revert : ( ) => { assert ( this . currentGroup === undefined && this . currentOp === undefined , 0x02b ) ; this . currentGroup = new TrackingGroup ( ) ; try { while ( trackingGroup . size > 0 ) { const segment = trackingGroup . segments [ 0 ] as PermutationSegment ; segment . trackingCollection . unlink ( trackingGroup ) ; callback ( segment ) ; } } finally { this . currentOp = undefined ; this . currentGroup = undefined ; } } , discard : ( ) => { while ( trackingGroup . size > 0 ) { trackingGroup . unlink ( trackingGroup . segments [ 0 ] ) ; } } } ; this . manager . pushToCurrentOperation ( revertible ) ; return revertible ; } } export class MatrixUndoProvider < T > { constructor ( private readonly consumer , private readonly matrix , private readonly rows , private readonly cols ) { rows . undo = new VectorUndoProvider ( consumer , ( segment ) => { const start = this . rows . getPosition ( segment ) ; this . matrix . removeRows ( start , segment . cachedLength ) ; } , ( segment ) => { this . matrix . _undoRemoveRows ( segment ) ; } ) ; cols . undo = new VectorUndoProvider ( consumer , ( segment ) => { const start = this . cols . getPosition ( segment ) ; this . matrix . removeCols ( start , segment . cachedLength ) ; } , ( segment ) => { this . matrix . _undoRemoveCols ( segment ) ; } ) ; } cellSet ( rowHandle , colHandle , oldValue ) { assert ( isHandleValid ( rowHandle ) && isHandleValid ( colHandle ) , 0x02c ) ; if ( this . consumer !== undefined ) { this . consumer . pushToCurrentOperation ( { revert : ( ) => { this . matrix . setCell ( this . rows . handleToPosition ( rowHandle ) , this . cols . handleToPosition ( colHandle ) , oldValue ) ; } , discard : ( ) => { } } ) ; } } }